diff --git a/src/assemble/distribution.xml b/src/assemble/distribution.xml deleted file mode 100644 index 231e9979e..000000000 --- a/src/assemble/distribution.xml +++ /dev/null @@ -1,19 +0,0 @@ - - agent - - jar - - false - - - target/classes - / - - com/avaje/ebean/enhance/** - - - - diff --git a/src/jdk_1.5/java/com/avaje/ebeaninternal/jdbc/ConnectionDelegator.java b/src/jdk_1.5/java/com/avaje/ebeaninternal/jdbc/ConnectionDelegator.java deleted file mode 100644 index efcc387bb..000000000 --- a/src/jdk_1.5/java/com/avaje/ebeaninternal/jdbc/ConnectionDelegator.java +++ /dev/null @@ -1,254 +0,0 @@ -/** - * 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.jdbc; - -import java.sql.CallableStatement; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.Savepoint; -import java.sql.Statement; -import java.util.Map; - -public class ConnectionDelegator implements Connection -{ - private final Connection delegate; - - public ConnectionDelegator(Connection delegate) - { - this.delegate = delegate; - } - - public Statement createStatement() - throws SQLException - { - return delegate.createStatement(); - } - - public PreparedStatement prepareStatement(String sql) - throws SQLException - { - return delegate.prepareStatement(sql); - } - - public CallableStatement prepareCall(String sql) - throws SQLException - { - return delegate.prepareCall(sql); - } - - public String nativeSQL(String sql) - throws SQLException - { - return delegate.nativeSQL(sql); - } - - public void setAutoCommit(boolean autoCommit) - throws SQLException - { - delegate.setAutoCommit(autoCommit); - } - - public boolean getAutoCommit() - throws SQLException - { - return delegate.getAutoCommit(); - } - - public void commit() - throws SQLException - { - delegate.commit(); - } - - public void rollback() - throws SQLException - { - delegate.rollback(); - } - - public void close() - throws SQLException - { - delegate.close(); - } - - public boolean isClosed() - throws SQLException - { - return delegate.isClosed(); - } - - public DatabaseMetaData getMetaData() - throws SQLException - { - return delegate.getMetaData(); - } - - public void setReadOnly(boolean readOnly) - throws SQLException - { - delegate.setReadOnly(readOnly); - } - - public boolean isReadOnly() - throws SQLException - { - return delegate.isReadOnly(); - } - - public void setCatalog(String catalog) - throws SQLException - { - delegate.setCatalog(catalog); - } - - public String getCatalog() - throws SQLException - { - return delegate.getCatalog(); - } - - public void setTransactionIsolation(int level) - throws SQLException - { - delegate.setTransactionIsolation(level); - } - - public int getTransactionIsolation() - throws SQLException - { - return delegate.getTransactionIsolation(); - } - - public SQLWarning getWarnings() - throws SQLException - { - return delegate.getWarnings(); - } - - public void clearWarnings() - throws SQLException - { - delegate.clearWarnings(); - } - - public Statement createStatement(int resultSetType, int resultSetConcurrency) - throws SQLException - { - return delegate.createStatement(resultSetType, resultSetConcurrency); - } - - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) - throws SQLException - { - return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency); - } - - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) - throws SQLException - { - return delegate.prepareCall(sql, resultSetType, resultSetConcurrency); - } - - public Map> getTypeMap() - throws SQLException - { - return delegate.getTypeMap(); - } - - public void setTypeMap(Map> map) - throws SQLException - { - delegate.setTypeMap(map); - } - - public void setHoldability(int holdability) - throws SQLException - { - delegate.setHoldability(holdability); - } - - public int getHoldability() - throws SQLException - { - return delegate.getHoldability(); - } - - public Savepoint setSavepoint() - throws SQLException - { - return delegate.setSavepoint(); - } - - public Savepoint setSavepoint(String name) - throws SQLException - { - return delegate.setSavepoint(name); - } - - public void rollback(Savepoint savepoint) - throws SQLException - { - delegate.rollback(savepoint); - } - - public void releaseSavepoint(Savepoint savepoint) - throws SQLException - { - delegate.releaseSavepoint(savepoint); - } - - public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) - throws SQLException - { - return delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); - } - - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) - throws SQLException - { - return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); - } - - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) - throws SQLException - { - return delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); - } - - public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) - throws SQLException - { - return delegate.prepareStatement(sql, autoGeneratedKeys); - } - - public PreparedStatement prepareStatement(String sql, int[] columnIndexes) - throws SQLException - { - return delegate.prepareStatement(sql, columnIndexes); - } - - public PreparedStatement prepareStatement(String sql, String[] columnNames) - throws SQLException - { - return delegate.prepareStatement(sql, columnNames); - } -} diff --git a/src/jdk_1.5/java/com/avaje/ebeaninternal/jdbc/PreparedStatementDelegator.java b/src/jdk_1.5/java/com/avaje/ebeaninternal/jdbc/PreparedStatementDelegator.java deleted file mode 100644 index e3a401332..000000000 --- a/src/jdk_1.5/java/com/avaje/ebeaninternal/jdbc/PreparedStatementDelegator.java +++ /dev/null @@ -1,492 +0,0 @@ -/** - * 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.jdbc; - -import java.io.InputStream; -import java.io.Reader; -import java.math.BigDecimal; -import java.net.URL; -import java.sql.Array; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.Connection; -import java.sql.Date; -import java.sql.ParameterMetaData; -import java.sql.PreparedStatement; -import java.sql.Ref; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.Time; -import java.sql.Timestamp; -import java.util.Calendar; - -public class PreparedStatementDelegator implements PreparedStatement -{ - private final PreparedStatement delegate; - - public PreparedStatementDelegator(PreparedStatement delegate) - { - this.delegate = delegate; - } - - public ResultSet executeQuery() - throws SQLException - { - return delegate.executeQuery(); - } - - public int executeUpdate() - throws SQLException - { - return delegate.executeUpdate(); - } - - public void setNull(int parameterIndex, int sqlType) - throws SQLException - { - delegate.setNull(parameterIndex, sqlType); - } - - public void setBoolean(int parameterIndex, boolean x) - throws SQLException - { - delegate.setBoolean(parameterIndex, x); - } - - public void setByte(int parameterIndex, byte x) - throws SQLException - { - delegate.setByte(parameterIndex, x); - } - - public void setShort(int parameterIndex, short x) - throws SQLException - { - delegate.setShort(parameterIndex, x); - } - - public void setInt(int parameterIndex, int x) - throws SQLException - { - delegate.setInt(parameterIndex, x); - } - - public void setLong(int parameterIndex, long x) - throws SQLException - { - delegate.setLong(parameterIndex, x); - } - - public void setFloat(int parameterIndex, float x) - throws SQLException - { - delegate.setFloat(parameterIndex, x); - } - - public void setDouble(int parameterIndex, double x) - throws SQLException - { - delegate.setDouble(parameterIndex, x); - } - - public void setBigDecimal(int parameterIndex, BigDecimal x) - throws SQLException - { - delegate.setBigDecimal(parameterIndex, x); - } - - public void setString(int parameterIndex, String x) - throws SQLException - { - delegate.setString(parameterIndex, x); - } - - public void setBytes(int parameterIndex, byte[] x) - throws SQLException - { - delegate.setBytes(parameterIndex, x); - } - - public void setDate(int parameterIndex, Date x) - throws SQLException - { - delegate.setDate(parameterIndex, x); - } - - public void setTime(int parameterIndex, Time x) - throws SQLException - { - delegate.setTime(parameterIndex, x); - } - - public void setTimestamp(int parameterIndex, Timestamp x) - throws SQLException - { - delegate.setTimestamp(parameterIndex, x); - } - - public void setAsciiStream(int parameterIndex, InputStream x, int length) - throws SQLException - { - delegate.setAsciiStream(parameterIndex, x, length); - } - - @SuppressWarnings("deprecation") - public void setUnicodeStream(int parameterIndex, InputStream x, int length) - throws SQLException - { - delegate.setUnicodeStream(parameterIndex, x, length); - } - - public void setBinaryStream(int parameterIndex, InputStream x, int length) - throws SQLException - { - delegate.setBinaryStream(parameterIndex, x, length); - } - - public void clearParameters() - throws SQLException - { - delegate.clearParameters(); - } - - public void setObject(int i, Object o, int i1, int i2) throws SQLException - { - delegate.setObject(i, o, i1, i2); - } - - public void setObject(int parameterIndex, Object x, int targetSqlType) - throws SQLException - { - delegate.setObject(parameterIndex, x, targetSqlType); - } - - public void setObject(int parameterIndex, Object x) - throws SQLException - { - delegate.setObject(parameterIndex, x); - } - - public boolean execute() - throws SQLException - { - return delegate.execute(); - } - - public void addBatch() - throws SQLException - { - delegate.addBatch(); - } - - public void setCharacterStream(int parameterIndex, Reader reader, int length) - throws SQLException - { - delegate.setCharacterStream(parameterIndex, reader, length); - } - - public void setRef(int parameterIndex, Ref x) - throws SQLException - { - delegate.setRef(parameterIndex, x); - } - - public void setBlob(int parameterIndex, Blob x) - throws SQLException - { - delegate.setBlob(parameterIndex, x); - } - - public void setClob(int parameterIndex, Clob x) - throws SQLException - { - delegate.setClob(parameterIndex, x); - } - - public void setArray(int parameterIndex, Array x) - throws SQLException - { - delegate.setArray(parameterIndex, x); - } - - public ResultSetMetaData getMetaData() - throws SQLException - { - return delegate.getMetaData(); - } - - public void setDate(int parameterIndex, Date x, Calendar cal) - throws SQLException - { - delegate.setDate(parameterIndex, x, cal); - } - - public void setTime(int parameterIndex, Time x, Calendar cal) - throws SQLException - { - delegate.setTime(parameterIndex, x, cal); - } - - public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) - throws SQLException - { - delegate.setTimestamp(parameterIndex, x, cal); - } - - public void setNull(int parameterIndex, int sqlType, String typeName) - throws SQLException - { - delegate.setNull(parameterIndex, sqlType, typeName); - } - - public void setURL(int parameterIndex, URL x) - throws SQLException - { - delegate.setURL(parameterIndex, x); - } - - public ParameterMetaData getParameterMetaData() - throws SQLException - { - return delegate.getParameterMetaData(); - } - - public ResultSet executeQuery(String sql) - throws SQLException - { - return delegate.executeQuery(sql); - } - - public int executeUpdate(String sql) - throws SQLException - { - return delegate.executeUpdate(sql); - } - - public void close() - throws SQLException - { - delegate.close(); - } - - public int getMaxFieldSize() - throws SQLException - { - return delegate.getMaxFieldSize(); - } - - public void setMaxFieldSize(int max) - throws SQLException - { - delegate.setMaxFieldSize(max); - } - - public int getMaxRows() - throws SQLException - { - return delegate.getMaxRows(); - } - - public void setMaxRows(int max) - throws SQLException - { - delegate.setMaxRows(max); - } - - public void setEscapeProcessing(boolean enable) - throws SQLException - { - delegate.setEscapeProcessing(enable); - } - - public int getQueryTimeout() - throws SQLException - { - return delegate.getQueryTimeout(); - } - - public void setQueryTimeout(int seconds) - throws SQLException - { - delegate.setQueryTimeout(seconds); - } - - public void cancel() - throws SQLException - { - delegate.cancel(); - } - - public SQLWarning getWarnings() - throws SQLException - { - return delegate.getWarnings(); - } - - public void clearWarnings() - throws SQLException - { - delegate.clearWarnings(); - } - - public void setCursorName(String name) - throws SQLException - { - delegate.setCursorName(name); - } - - public boolean execute(String sql) - throws SQLException - { - return delegate.execute(sql); - } - - public ResultSet getResultSet() - throws SQLException - { - return delegate.getResultSet(); - } - - public int getUpdateCount() - throws SQLException - { - return delegate.getUpdateCount(); - } - - public boolean getMoreResults() - throws SQLException - { - return delegate.getMoreResults(); - } - - public void setFetchDirection(int direction) - throws SQLException - { - delegate.setFetchDirection(direction); - } - - public int getFetchDirection() - throws SQLException - { - return delegate.getFetchDirection(); - } - - public void setFetchSize(int rows) - throws SQLException - { - delegate.setFetchSize(rows); - } - - public int getFetchSize() - throws SQLException - { - return delegate.getFetchSize(); - } - - public int getResultSetConcurrency() - throws SQLException - { - return delegate.getResultSetConcurrency(); - } - - public int getResultSetType() - throws SQLException - { - return delegate.getResultSetType(); - } - - public void addBatch(String sql) - throws SQLException - { - delegate.addBatch(sql); - } - - public void clearBatch() - throws SQLException - { - delegate.clearBatch(); - } - - public int[] executeBatch() - throws SQLException - { - return delegate.executeBatch(); - } - - public Connection getConnection() - throws SQLException - { - return delegate.getConnection(); - } - - public boolean getMoreResults(int current) - throws SQLException - { - return delegate.getMoreResults(current); - } - - public ResultSet getGeneratedKeys() - throws SQLException - { - return delegate.getGeneratedKeys(); - } - - public int executeUpdate(String sql, int autoGeneratedKeys) - throws SQLException - { - return delegate.executeUpdate(sql, autoGeneratedKeys); - } - - public int executeUpdate(String sql, int[] columnIndexes) - throws SQLException - { - return delegate.executeUpdate(sql, columnIndexes); - } - - public int executeUpdate(String sql, String[] columnNames) - throws SQLException - { - return delegate.executeUpdate(sql, columnNames); - } - - public boolean execute(String sql, int autoGeneratedKeys) - throws SQLException - { - return delegate.execute(sql, autoGeneratedKeys); - } - - public boolean execute(String sql, int[] columnIndexes) - throws SQLException - { - return delegate.execute(sql, columnIndexes); - } - - public boolean execute(String sql, String[] columnNames) - throws SQLException - { - return delegate.execute(sql, columnNames); - } - - public int getResultSetHoldability() - throws SQLException - { - return delegate.getResultSetHoldability(); - } -} diff --git a/src/jdk_1.6/java/com/avaje/ebeaninternal/jdbc/ConnectionDelegator.java b/src/jdk_1.6/java/com/avaje/ebeaninternal/jdbc/ConnectionDelegator.java deleted file mode 100644 index 51c6e47e0..000000000 --- a/src/jdk_1.6/java/com/avaje/ebeaninternal/jdbc/ConnectionDelegator.java +++ /dev/null @@ -1,340 +0,0 @@ -/** - * 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.jdbc; - -import java.sql.Array; -import java.sql.Blob; -import java.sql.CallableStatement; -import java.sql.Clob; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.NClob; -import java.sql.PreparedStatement; -import java.sql.SQLClientInfoException; -import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.SQLXML; -import java.sql.Savepoint; -import java.sql.Statement; -import java.sql.Struct; -import java.util.Map; -import java.util.Properties; - -public class ConnectionDelegator implements Connection -{ - private final Connection delegate; - - public ConnectionDelegator(Connection delegate) - { - this.delegate = delegate; - } - - public Statement createStatement() - throws SQLException - { - return delegate.createStatement(); - } - - public PreparedStatement prepareStatement(String sql) - throws SQLException - { - return delegate.prepareStatement(sql); - } - - public CallableStatement prepareCall(String sql) - throws SQLException - { - return delegate.prepareCall(sql); - } - - public String nativeSQL(String sql) - throws SQLException - { - return delegate.nativeSQL(sql); - } - - public void setAutoCommit(boolean autoCommit) - throws SQLException - { - delegate.setAutoCommit(autoCommit); - } - - public boolean getAutoCommit() - throws SQLException - { - return delegate.getAutoCommit(); - } - - public void commit() - throws SQLException - { - delegate.commit(); - } - - public void rollback() - throws SQLException - { - delegate.rollback(); - } - - public void close() - throws SQLException - { - delegate.close(); - } - - public boolean isClosed() - throws SQLException - { - return delegate.isClosed(); - } - - public DatabaseMetaData getMetaData() - throws SQLException - { - return delegate.getMetaData(); - } - - public void setReadOnly(boolean readOnly) - throws SQLException - { - delegate.setReadOnly(readOnly); - } - - public boolean isReadOnly() - throws SQLException - { - return delegate.isReadOnly(); - } - - public void setCatalog(String catalog) - throws SQLException - { - delegate.setCatalog(catalog); - } - - public String getCatalog() - throws SQLException - { - return delegate.getCatalog(); - } - - public void setTransactionIsolation(int level) - throws SQLException - { - delegate.setTransactionIsolation(level); - } - - public int getTransactionIsolation() - throws SQLException - { - return delegate.getTransactionIsolation(); - } - - public SQLWarning getWarnings() - throws SQLException - { - return delegate.getWarnings(); - } - - public void clearWarnings() - throws SQLException - { - delegate.clearWarnings(); - } - - public Statement createStatement(int resultSetType, int resultSetConcurrency) - throws SQLException - { - return delegate.createStatement(resultSetType, resultSetConcurrency); - } - - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) - throws SQLException - { - return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency); - } - - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) - throws SQLException - { - return delegate.prepareCall(sql, resultSetType, resultSetConcurrency); - } - - public Map> getTypeMap() - throws SQLException - { - return delegate.getTypeMap(); - } - - public void setTypeMap(Map> map) - throws SQLException - { - delegate.setTypeMap(map); - } - - public void setHoldability(int holdability) - throws SQLException - { - delegate.setHoldability(holdability); - } - - public int getHoldability() - throws SQLException - { - return delegate.getHoldability(); - } - - public Savepoint setSavepoint() - throws SQLException - { - return delegate.setSavepoint(); - } - - public Savepoint setSavepoint(String name) - throws SQLException - { - return delegate.setSavepoint(name); - } - - public void rollback(Savepoint savepoint) - throws SQLException - { - delegate.rollback(savepoint); - } - - public void releaseSavepoint(Savepoint savepoint) - throws SQLException - { - delegate.releaseSavepoint(savepoint); - } - - public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) - throws SQLException - { - return delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); - } - - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) - throws SQLException - { - return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); - } - - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) - throws SQLException - { - return delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); - } - - public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) - throws SQLException - { - return delegate.prepareStatement(sql, autoGeneratedKeys); - } - - public PreparedStatement prepareStatement(String sql, int[] columnIndexes) - throws SQLException - { - return delegate.prepareStatement(sql, columnIndexes); - } - - public PreparedStatement prepareStatement(String sql, String[] columnNames) - throws SQLException - { - return delegate.prepareStatement(sql, columnNames); - } - - public Clob createClob() - throws SQLException - { - return delegate.createClob(); - } - - public Blob createBlob() - throws SQLException - { - return delegate.createBlob(); - } - - public NClob createNClob() - throws SQLException - { - return delegate.createNClob(); - } - - public SQLXML createSQLXML() - throws SQLException - { - return delegate.createSQLXML(); - } - - public boolean isValid(int timeout) - throws SQLException - { - return delegate.isValid(timeout); - } - - public void setClientInfo(String name, String value) - throws SQLClientInfoException - { - delegate.setClientInfo(name, value); - } - - public void setClientInfo(Properties properties) - throws SQLClientInfoException - { - delegate.setClientInfo(properties); - } - - public String getClientInfo(String name) - throws SQLException - { - return delegate.getClientInfo(name); - } - - public Properties getClientInfo() - throws SQLException - { - return delegate.getClientInfo(); - } - - public Array createArrayOf(String typeName, Object[] elements) - throws SQLException - { - return delegate.createArrayOf(typeName, elements); - } - - public Struct createStruct(String typeName, Object[] attributes) - throws SQLException - { - return delegate.createStruct(typeName, attributes); - } - - public T unwrap(Class iface) - throws SQLException - { - return delegate.unwrap(iface); - } - - public boolean isWrapperFor(Class iface) - throws SQLException - { - return delegate.isWrapperFor(iface); - } -} diff --git a/src/jdk_1.6/java/com/avaje/ebeaninternal/jdbc/PreparedStatementDelegator.java b/src/jdk_1.6/java/com/avaje/ebeaninternal/jdbc/PreparedStatementDelegator.java deleted file mode 100644 index dba32b4d0..000000000 --- a/src/jdk_1.6/java/com/avaje/ebeaninternal/jdbc/PreparedStatementDelegator.java +++ /dev/null @@ -1,634 +0,0 @@ -/** - * 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.jdbc; - -import java.io.InputStream; -import java.io.Reader; -import java.math.BigDecimal; -import java.net.URL; -import java.sql.Array; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.Connection; -import java.sql.Date; -import java.sql.NClob; -import java.sql.ParameterMetaData; -import java.sql.PreparedStatement; -import java.sql.Ref; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.RowId; -import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.SQLXML; -import java.sql.Time; -import java.sql.Timestamp; -import java.util.Calendar; - -public class PreparedStatementDelegator implements PreparedStatement -{ - private final PreparedStatement delegate; - - public PreparedStatementDelegator(PreparedStatement delegate) - { - this.delegate = delegate; - } - - public ResultSet executeQuery() - throws SQLException - { - return delegate.executeQuery(); - } - - public int executeUpdate() - throws SQLException - { - return delegate.executeUpdate(); - } - - public void setNull(int parameterIndex, int sqlType) - throws SQLException - { - delegate.setNull(parameterIndex, sqlType); - } - - public void setBoolean(int parameterIndex, boolean x) - throws SQLException - { - delegate.setBoolean(parameterIndex, x); - } - - public void setByte(int parameterIndex, byte x) - throws SQLException - { - delegate.setByte(parameterIndex, x); - } - - public void setShort(int parameterIndex, short x) - throws SQLException - { - delegate.setShort(parameterIndex, x); - } - - public void setInt(int parameterIndex, int x) - throws SQLException - { - delegate.setInt(parameterIndex, x); - } - - public void setLong(int parameterIndex, long x) - throws SQLException - { - delegate.setLong(parameterIndex, x); - } - - public void setFloat(int parameterIndex, float x) - throws SQLException - { - delegate.setFloat(parameterIndex, x); - } - - public void setDouble(int parameterIndex, double x) - throws SQLException - { - delegate.setDouble(parameterIndex, x); - } - - public void setBigDecimal(int parameterIndex, BigDecimal x) - throws SQLException - { - delegate.setBigDecimal(parameterIndex, x); - } - - public void setString(int parameterIndex, String x) - throws SQLException - { - delegate.setString(parameterIndex, x); - } - - public void setBytes(int parameterIndex, byte[] x) - throws SQLException - { - delegate.setBytes(parameterIndex, x); - } - - public void setDate(int parameterIndex, Date x) - throws SQLException - { - delegate.setDate(parameterIndex, x); - } - - public void setTime(int parameterIndex, Time x) - throws SQLException - { - delegate.setTime(parameterIndex, x); - } - - public void setTimestamp(int parameterIndex, Timestamp x) - throws SQLException - { - delegate.setTimestamp(parameterIndex, x); - } - - public void setAsciiStream(int parameterIndex, InputStream x, int length) - throws SQLException - { - delegate.setAsciiStream(parameterIndex, x, length); - } - - @SuppressWarnings("deprecation") - public void setUnicodeStream(int parameterIndex, InputStream x, int length) - throws SQLException - { - delegate.setUnicodeStream(parameterIndex, x, length); - } - - public void setBinaryStream(int parameterIndex, InputStream x, int length) - throws SQLException - { - delegate.setBinaryStream(parameterIndex, x, length); - } - - public void clearParameters() - throws SQLException - { - delegate.clearParameters(); - } - - public void setObject(int parameterIndex, Object x, int targetSqlType) - throws SQLException - { - delegate.setObject(parameterIndex, x, targetSqlType); - } - - public void setObject(int parameterIndex, Object x) - throws SQLException - { - delegate.setObject(parameterIndex, x); - } - - public boolean execute() - throws SQLException - { - return delegate.execute(); - } - - public void addBatch() - throws SQLException - { - delegate.addBatch(); - } - - public void setCharacterStream(int parameterIndex, Reader reader, int length) - throws SQLException - { - delegate.setCharacterStream(parameterIndex, reader, length); - } - - public void setRef(int parameterIndex, Ref x) - throws SQLException - { - delegate.setRef(parameterIndex, x); - } - - public void setBlob(int parameterIndex, Blob x) - throws SQLException - { - delegate.setBlob(parameterIndex, x); - } - - public void setClob(int parameterIndex, Clob x) - throws SQLException - { - delegate.setClob(parameterIndex, x); - } - - public void setArray(int parameterIndex, Array x) - throws SQLException - { - delegate.setArray(parameterIndex, x); - } - - public ResultSetMetaData getMetaData() - throws SQLException - { - return delegate.getMetaData(); - } - - public void setDate(int parameterIndex, Date x, Calendar cal) - throws SQLException - { - delegate.setDate(parameterIndex, x, cal); - } - - public void setTime(int parameterIndex, Time x, Calendar cal) - throws SQLException - { - delegate.setTime(parameterIndex, x, cal); - } - - public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) - throws SQLException - { - delegate.setTimestamp(parameterIndex, x, cal); - } - - public void setNull(int parameterIndex, int sqlType, String typeName) - throws SQLException - { - delegate.setNull(parameterIndex, sqlType, typeName); - } - - public void setURL(int parameterIndex, URL x) - throws SQLException - { - delegate.setURL(parameterIndex, x); - } - - public ParameterMetaData getParameterMetaData() - throws SQLException - { - return delegate.getParameterMetaData(); - } - - public void setRowId(int parameterIndex, RowId x) - throws SQLException - { - delegate.setRowId(parameterIndex, x); - } - - public void setNString(int parameterIndex, String value) - throws SQLException - { - delegate.setNString(parameterIndex, value); - } - - public void setNCharacterStream(int parameterIndex, Reader value, long length) - throws SQLException - { - delegate.setNCharacterStream(parameterIndex, value, length); - } - - public void setNClob(int parameterIndex, NClob value) - throws SQLException - { - delegate.setNClob(parameterIndex, value); - } - - public void setClob(int parameterIndex, Reader reader, long length) - throws SQLException - { - delegate.setClob(parameterIndex, reader, length); - } - - public void setBlob(int parameterIndex, InputStream inputStream, long length) - throws SQLException - { - delegate.setBlob(parameterIndex, inputStream, length); - } - - public void setNClob(int parameterIndex, Reader reader, long length) - throws SQLException - { - delegate.setNClob(parameterIndex, reader, length); - } - - public void setSQLXML(int parameterIndex, SQLXML xmlObject) - throws SQLException - { - delegate.setSQLXML(parameterIndex, xmlObject); - } - - public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) - throws SQLException - { - delegate.setObject(parameterIndex, x, targetSqlType, scaleOrLength); - } - - public void setAsciiStream(int parameterIndex, InputStream x, long length) - throws SQLException - { - delegate.setAsciiStream(parameterIndex, x, length); - } - - public void setBinaryStream(int parameterIndex, InputStream x, long length) - throws SQLException - { - delegate.setBinaryStream(parameterIndex, x, length); - } - - public void setCharacterStream(int parameterIndex, Reader reader, long length) - throws SQLException - { - delegate.setCharacterStream(parameterIndex, reader, length); - } - - public void setAsciiStream(int parameterIndex, InputStream x) - throws SQLException - { - delegate.setAsciiStream(parameterIndex, x); - } - - public void setBinaryStream(int parameterIndex, InputStream x) - throws SQLException - { - delegate.setBinaryStream(parameterIndex, x); - } - - public void setCharacterStream(int parameterIndex, Reader reader) - throws SQLException - { - delegate.setCharacterStream(parameterIndex, reader); - } - - public void setNCharacterStream(int parameterIndex, Reader value) - throws SQLException - { - delegate.setNCharacterStream(parameterIndex, value); - } - - public void setClob(int parameterIndex, Reader reader) - throws SQLException - { - delegate.setClob(parameterIndex, reader); - } - - public void setBlob(int parameterIndex, InputStream inputStream) - throws SQLException - { - delegate.setBlob(parameterIndex, inputStream); - } - - public void setNClob(int parameterIndex, Reader reader) - throws SQLException - { - delegate.setNClob(parameterIndex, reader); - } - - public ResultSet executeQuery(String sql) - throws SQLException - { - return delegate.executeQuery(sql); - } - - public int executeUpdate(String sql) - throws SQLException - { - return delegate.executeUpdate(sql); - } - - public void close() - throws SQLException - { - delegate.close(); - } - - public int getMaxFieldSize() - throws SQLException - { - return delegate.getMaxFieldSize(); - } - - public void setMaxFieldSize(int max) - throws SQLException - { - delegate.setMaxFieldSize(max); - } - - public int getMaxRows() - throws SQLException - { - return delegate.getMaxRows(); - } - - public void setMaxRows(int max) - throws SQLException - { - delegate.setMaxRows(max); - } - - public void setEscapeProcessing(boolean enable) - throws SQLException - { - delegate.setEscapeProcessing(enable); - } - - public int getQueryTimeout() - throws SQLException - { - return delegate.getQueryTimeout(); - } - - public void setQueryTimeout(int seconds) - throws SQLException - { - delegate.setQueryTimeout(seconds); - } - - public void cancel() - throws SQLException - { - delegate.cancel(); - } - - public SQLWarning getWarnings() - throws SQLException - { - return delegate.getWarnings(); - } - - public void clearWarnings() - throws SQLException - { - delegate.clearWarnings(); - } - - public void setCursorName(String name) - throws SQLException - { - delegate.setCursorName(name); - } - - public boolean execute(String sql) - throws SQLException - { - return delegate.execute(sql); - } - - public ResultSet getResultSet() - throws SQLException - { - return delegate.getResultSet(); - } - - public int getUpdateCount() - throws SQLException - { - return delegate.getUpdateCount(); - } - - public boolean getMoreResults() - throws SQLException - { - return delegate.getMoreResults(); - } - - public void setFetchDirection(int direction) - throws SQLException - { - delegate.setFetchDirection(direction); - } - - public int getFetchDirection() - throws SQLException - { - return delegate.getFetchDirection(); - } - - public void setFetchSize(int rows) - throws SQLException - { - delegate.setFetchSize(rows); - } - - public int getFetchSize() - throws SQLException - { - return delegate.getFetchSize(); - } - - public int getResultSetConcurrency() - throws SQLException - { - return delegate.getResultSetConcurrency(); - } - - public int getResultSetType() - throws SQLException - { - return delegate.getResultSetType(); - } - - public void addBatch(String sql) - throws SQLException - { - delegate.addBatch(sql); - } - - public void clearBatch() - throws SQLException - { - delegate.clearBatch(); - } - - public int[] executeBatch() - throws SQLException - { - return delegate.executeBatch(); - } - - public Connection getConnection() - throws SQLException - { - return delegate.getConnection(); - } - - public boolean getMoreResults(int current) - throws SQLException - { - return delegate.getMoreResults(current); - } - - public ResultSet getGeneratedKeys() - throws SQLException - { - return delegate.getGeneratedKeys(); - } - - public int executeUpdate(String sql, int autoGeneratedKeys) - throws SQLException - { - return delegate.executeUpdate(sql, autoGeneratedKeys); - } - - public int executeUpdate(String sql, int[] columnIndexes) - throws SQLException - { - return delegate.executeUpdate(sql, columnIndexes); - } - - public int executeUpdate(String sql, String[] columnNames) - throws SQLException - { - return delegate.executeUpdate(sql, columnNames); - } - - public boolean execute(String sql, int autoGeneratedKeys) - throws SQLException - { - return delegate.execute(sql, autoGeneratedKeys); - } - - public boolean execute(String sql, int[] columnIndexes) - throws SQLException - { - return delegate.execute(sql, columnIndexes); - } - - public boolean execute(String sql, String[] columnNames) - throws SQLException - { - return delegate.execute(sql, columnNames); - } - - public int getResultSetHoldability() - throws SQLException - { - return delegate.getResultSetHoldability(); - } - - public boolean isClosed() - throws SQLException - { - return delegate.isClosed(); - } - - public void setPoolable(boolean poolable) - throws SQLException - { - delegate.setPoolable(poolable); - } - - public boolean isPoolable() - throws SQLException - { - return delegate.isPoolable(); - } - - public T unwrap(Class iface) - throws SQLException - { - return delegate.unwrap(iface); - } - - public boolean isWrapperFor(Class iface) - throws SQLException - { - return delegate.isWrapperFor(iface); - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/api/BeanIdList.java b/src/main/java/com/avaje/ebeaninternal/api/BeanIdList.java index 7d3df1d4e..f46215a11 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/BeanIdList.java +++ b/src/main/java/com/avaje/ebeaninternal/api/BeanIdList.java @@ -1,114 +1,95 @@ -/** - * 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.api; - -import java.util.List; -import java.util.concurrent.FutureTask; -import java.util.concurrent.TimeUnit; - -import javax.persistence.PersistenceException; - -/** - * Wrapper of the list of Id's adding support for background fetching - * future object. - * - * @author rbygrave - */ -public class BeanIdList { - - private final List idList; - - private boolean hasMore = true; - - private FutureTask fetchFuture; - - public BeanIdList(List idList) { - this.idList = idList; - } - - /** - * Return true if the fetch is continuing in a background thread. - */ - public boolean isFetchingInBackground() { - return fetchFuture != null; - } - - /** - * Set the FutureTask that is continuing the fetch in a background thread. - */ - public void setBackgroundFetch(FutureTask fetchFuture) { - this.fetchFuture = fetchFuture; - } - - /** - * Wait for the background fetching to complete with a timeout. - */ - public void backgroundFetchWait(long wait, TimeUnit timeUnit) { - if (fetchFuture != null){ - try { - fetchFuture.get(wait, timeUnit); - } catch (Exception e) { - throw new PersistenceException(e); - } - } - } - - /** - * Wait for the background fetching to complete. - */ - public void backgroundFetchWait() { - if (fetchFuture != null){ - try { - fetchFuture.get(); - } catch (Exception e) { - throw new PersistenceException(e); - } - } - } - - /** - * Add an Id to the list. - */ - public void add(Object id){ - idList.add(id); - } - - /** - * Return the list of Id's. - */ - public List getIdList() { - return idList; - } - - /** - * Return true if max rows was hit and there is more rows to fetch. - */ - public boolean isHasMore() { - return hasMore; - } - - /** - * Set to true when max rows is hit and there are more rows to fetch. - */ - public void setHasMore(boolean hasMore) { - this.hasMore = hasMore; - } - -} +package com.avaje.ebeaninternal.api; + +import java.util.List; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; + +import javax.persistence.PersistenceException; + +/** + * Wrapper of the list of Id's adding support for background fetching + * future object. + * + * @author rbygrave + */ +public class BeanIdList { + + private final List idList; + + private boolean hasMore = true; + + private FutureTask fetchFuture; + + public BeanIdList(List idList) { + this.idList = idList; + } + + /** + * Return true if the fetch is continuing in a background thread. + */ + public boolean isFetchingInBackground() { + return fetchFuture != null; + } + + /** + * Set the FutureTask that is continuing the fetch in a background thread. + */ + public void setBackgroundFetch(FutureTask fetchFuture) { + this.fetchFuture = fetchFuture; + } + + /** + * Wait for the background fetching to complete with a timeout. + */ + public void backgroundFetchWait(long wait, TimeUnit timeUnit) { + if (fetchFuture != null){ + try { + fetchFuture.get(wait, timeUnit); + } catch (Exception e) { + throw new PersistenceException(e); + } + } + } + + /** + * Wait for the background fetching to complete. + */ + public void backgroundFetchWait() { + if (fetchFuture != null){ + try { + fetchFuture.get(); + } catch (Exception e) { + throw new PersistenceException(e); + } + } + } + + /** + * Add an Id to the list. + */ + public void add(Object id){ + idList.add(id); + } + + /** + * Return the list of Id's. + */ + public List getIdList() { + return idList; + } + + /** + * Return true if max rows was hit and there is more rows to fetch. + */ + public boolean isHasMore() { + return hasMore; + } + + /** + * Set to true when max rows is hit and there are more rows to fetch. + */ + public void setHasMore(boolean hasMore) { + this.hasMore = hasMore; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/BindParams.java b/src/main/java/com/avaje/ebeaninternal/api/BindParams.java index 95bfc75e8..2b68aa947 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/BindParams.java +++ b/src/main/java/com/avaje/ebeaninternal/api/BindParams.java @@ -1,536 +1,517 @@ -/** - * 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.api; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam; - -/** - * Parameters used for binding to a statement. - *

- * Used by FindByNativeSql and UpdateSql to support ordered and named - * parameters. Note that you can use either ordered OR named parameters. - *

- */ -public class BindParams implements Serializable { - - private static final long serialVersionUID = 4541081933302086285L; - - private ArrayList positionedParameters = new ArrayList(); - - private HashMap namedParameters = new HashMap(); - - /** - * Need to create a hash when binding collection values (for in clauses). - */ - private int queryPlanHash = 1; - - /** - * This is the sql. For named parameters this is the sql after the named - * parameters have been replaced with question mark place holders and the - * parameters have been ordered by addNamedParamInOrder(). - */ - private String preparedSql; - - /** - * Return a deep copy of the BindParams. - */ - public BindParams copy() { - BindParams copy = new BindParams(); - for (Param p : positionedParameters) { - copy.positionedParameters.add(p.copy()); - } - Iterator> it = namedParameters.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry entry = (Map.Entry) it.next(); - copy.namedParameters.put(entry.getKey(), entry.getValue().copy()); - } - return copy; - } - - public int queryBindHash() { - int hc = namedParameters.hashCode(); - for (int i = 0; i < positionedParameters.size(); i++) { - hc = hc * 31 + positionedParameters.get(i).hashCode(); - } - return hc; - } - - public int hashCode() { - int hc = getClass().hashCode(); - hc = hc * 31 + namedParameters.hashCode(); - for (int i = 0; i < positionedParameters.size(); i++) { - hc = hc * 31 + positionedParameters.get(i).hashCode(); - } - hc = hc * 31 + (preparedSql == null ? 0 : preparedSql.hashCode()); - return hc; - } - - public boolean equals(Object o) { - if (o == null) { - return false; - } - if (o == this) { - return true; - } - if (o instanceof BindParams) { - return hashCode() == o.hashCode(); - } - return false; - } - - /** - * Return true if there are no bind parameters. - */ - public boolean isEmpty() { - return positionedParameters.isEmpty() && namedParameters.isEmpty(); - } - - /** - * Return a Natural Key bind param if supported. - */ - public NaturalKeyBindParam getNaturalKeyBindParam() { - if (positionedParameters != null){ - return null; - } - if (namedParameters != null && namedParameters.size() == 1){ - Entry e = namedParameters.entrySet().iterator().next(); - return new NaturalKeyBindParam(e.getKey(), e.getValue().getInValue()); - } - return null; - } - - public int size() { - return positionedParameters.size(); - } - - /** - * Return true if named parameters are being used and they have not yet been - * ordered. The sql needs to be prepared (named replaced with ?) and the - * parameters ordered. - */ - public boolean requiresNamedParamsPrepare() { - return !namedParameters.isEmpty() && positionedParameters.isEmpty(); - } - - /** - * Set a null parameter using position. - */ - public void setNullParameter(int position, int jdbcType) { - Param p = getParam(position); - p.setInNullType(jdbcType); - } - - /** - * Set an In Out parameter using position. - */ - public void setParameter(int position, Object value, int outType) { - - addToQueryPlanHash(String.valueOf(position), value); - - Param p = getParam(position); - p.setInValue(value); - p.setOutType(outType); - } - - /** - * Using position set the In value of a parameter. Note that for nulls you - * must use setNullParameter. - */ - public void setParameter(int position, Object value) { - - addToQueryPlanHash(String.valueOf(position), value); - - Param p = getParam(position); - p.setInValue(value); - } - - /** - * Register the parameter as an Out parameter using position. - */ - public void registerOut(int position, int outType) { - Param p = getParam(position); - p.setOutType(outType); - } - - private Param getParam(String name) { - Param p = (Param) namedParameters.get(name); - if (p == null) { - p = new Param(); - namedParameters.put(name, p); - } - return p; - } - - private Param getParam(int position) { - int more = position - positionedParameters.size(); - if (more > 0) { - for (int i = 0; i < more; i++) { - positionedParameters.add(new Param()); - } - } - return (Param) positionedParameters.get(position - 1); - } - - /** - * Set a named In Out parameter. - */ - public void setParameter(String name, Object value, int outType) { - - addToQueryPlanHash(name, value); - - Param p = getParam(name); - p.setInValue(value); - p.setOutType(outType); - } - - /** - * Set a named In parameter that is null. - */ - public void setNullParameter(String name, int jdbcType) { - Param p = getParam(name); - p.setInNullType(jdbcType); - } - - /** - * Set a named In parameter that is not null. - */ - public Param setParameter(String name, Object value) { - - addToQueryPlanHash(name, value); - - Param p = getParam(name); - p.setInValue(value); - return p; - } - - /** - * For binding collections calculate a hash to be used for the query plan. - */ - private void addToQueryPlanHash(String name, Object value){ - if (value != null){ - if (value instanceof Collection){ - queryPlanHash = queryPlanHash * 31 + name.hashCode(); - queryPlanHash = queryPlanHash * 31 + ((Collection)value).size(); - } - } - } - - /** - * Return the hash that should be included with the query plan. - *

- * This is to handle binding collections to in clauses. The number - * of values in the collection effects the query (number of bind values) - * and so must be taken into account when calculating the query hash. - *

- */ - public int getQueryPlanHash() { - return queryPlanHash; - } - - /** - * Set an encryption key as a bind value. - *

- * Needs special treatment as the value should not be included in a log. - *

- */ - public Param setEncryptionKey(String name, Object value) { - Param p = getParam(name); - p.setEncryptionKey(value); - return p; - } - - /** - * Register the named parameter as an Out parameter. - */ - public void registerOut(String name, int outType) { - Param p = getParam(name); - p.setOutType(outType); - } - - /** - * Return the Parameter for a given position. - */ - public Param getParameter(int position) { - // Used to read Out value by CallableSql - return getParam(position); - } - - /** - * Return the named parameter. - */ - public Param getParameter(String name) { - return getParam(name); - } - - /** - * Return the values of ordered parameters. - */ - public List positionedParameters() { - return positionedParameters; - } - - /** - * Set the sql with named parameters replaced with place holder ?. - */ - public void setPreparedSql(String preparedSql) { - this.preparedSql = preparedSql; - } - - /** - * Return the sql with ? place holders (named parameters have been processed - * and ordered). - */ - public String getPreparedSql() { - return preparedSql; - } - - /** - * The bind parameters in the correct binding order. - *

- * This is the result of converting sql with named parameters - * into sql with ? and ordered parameters. - *

- */ - public static final class OrderedList { - - final List paramList; - - final StringBuilder preparedSql; - - public OrderedList() { - this(new ArrayList()); - } - - public OrderedList(List paramList) { - this.paramList = paramList; - this.preparedSql = new StringBuilder(); - } - - /** - * Add a parameter in the correct binding order. - */ - public void add(Param param) { - paramList.add(param); - } - - /** - * Return the number of bind parameters in this list. - */ - public int size() { - return paramList.size(); - } - - /** - * Returns the ordered list of bind parameters. - */ - public List list() { - return paramList; - } - - /** - * Append parsedSql that has named parameters converted into ?. - */ - public void appendSql(String parsedSql) { - preparedSql.append(parsedSql); - } - - public String getPreparedSql() { - return preparedSql.toString(); - } - } - - /** - * A In Out capable parameter for the CallableStatement. - */ - public static final class Param implements Serializable { - - private static final long serialVersionUID = 1L; - - private boolean encryptionKey; - - private boolean isInParam; - - private boolean isOutParam; - - private int type; - - private Object inValue; - - private Object outValue; - - private int textLocation; - - /** - * Construct a Parameter. - */ - public Param() { - } - - /** - * Create a deep copy of the Param. - */ - public Param copy() { - Param copy = new Param(); - copy.isInParam = isInParam; - copy.isOutParam = isOutParam; - copy.type = type; - copy.inValue = inValue; - copy.outValue = outValue; - return copy; - } - - public int hashCode() { - int hc = getClass().hashCode(); - hc = hc * 31 + (isInParam ? 0 : 1); - hc = hc * 31 + (isOutParam ? 0 : 1); - hc = hc * 31 + (type); - hc = hc * 31 + (inValue == null ? 0 : inValue.hashCode()); - return hc; - } - - public boolean equals(Object o) { - if (o == null) { - return false; - } - if (o == this) { - return true; - } - if (o instanceof Param) { - return hashCode() == o.hashCode(); - } - return false; - } - - /** - * Return true if this is an In parameter that needs to be bound before - * execution. - */ - public boolean isInParam() { - return isInParam; - } - - /** - * Return true if this is an out parameter that needs to be registered - * before execution. - */ - public boolean isOutParam() { - return isOutParam; - } - - /** - * Return the jdbc type of this parameter. Used for registering Out - * parameters and setting NULL In parameters. - */ - public int getType() { - return type; - } - - /** - * Set the Out parameter type. - */ - public void setOutType(int type) { - this.type = type; - this.isOutParam = true; - } - - /** - * Set the In value. - */ - public void setInValue(Object in) { - this.inValue = in; - this.isInParam = true; - } - - /** - * Set an encryption key (which can not be logged). - */ - public void setEncryptionKey(Object in) { - this.inValue = in; - this.isInParam = true; - this.encryptionKey = true; - } - - /** - * Specify that the In parameter is NULL and the specific type that it - * is. - */ - public void setInNullType(int type) { - this.type = type; - this.inValue = null; - this.isInParam = true; - } - - /** - * Return the OUT value that was retrieved. This value is set after - * CallableStatement was executed. - */ - public Object getOutValue() { - return outValue; - } - - /** - * Return the In value. If this is null, then the type should be used to - * specify the type of the null. - */ - public Object getInValue() { - return inValue; - } - - /** - * Set the OUT value returned by a CallableStatement after it has - * executed. - */ - public void setOutValue(Object out) { - this.outValue = out; - } - - /** - * Return the location this parameter was found in the sql text. - */ - public int getTextLocation() { - return textLocation; - } - - /** - * Set the location in the sql text this parameter was located. This is - * used to control order for named parameters. - */ - public void setTextLocation(int textLocation) { - this.textLocation = textLocation; - } - - /** - * If true do not include this value in a transaction log. - */ - public boolean isEncryptionKey() { - return encryptionKey; - } - - } -} +package com.avaje.ebeaninternal.api; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam; + +/** + * Parameters used for binding to a statement. + *

+ * Used by FindByNativeSql and UpdateSql to support ordered and named + * parameters. Note that you can use either ordered OR named parameters. + *

+ */ +public class BindParams implements Serializable { + + private static final long serialVersionUID = 4541081933302086285L; + + private ArrayList positionedParameters = new ArrayList(); + + private HashMap namedParameters = new HashMap(); + + /** + * Need to create a hash when binding collection values (for in clauses). + */ + private int queryPlanHash = 1; + + /** + * This is the sql. For named parameters this is the sql after the named + * parameters have been replaced with question mark place holders and the + * parameters have been ordered by addNamedParamInOrder(). + */ + private String preparedSql; + + /** + * Return a deep copy of the BindParams. + */ + public BindParams copy() { + BindParams copy = new BindParams(); + for (Param p : positionedParameters) { + copy.positionedParameters.add(p.copy()); + } + Iterator> it = namedParameters.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = (Map.Entry) it.next(); + copy.namedParameters.put(entry.getKey(), entry.getValue().copy()); + } + return copy; + } + + public int queryBindHash() { + int hc = namedParameters.hashCode(); + for (int i = 0; i < positionedParameters.size(); i++) { + hc = hc * 31 + positionedParameters.get(i).hashCode(); + } + return hc; + } + + public int hashCode() { + int hc = getClass().hashCode(); + hc = hc * 31 + namedParameters.hashCode(); + for (int i = 0; i < positionedParameters.size(); i++) { + hc = hc * 31 + positionedParameters.get(i).hashCode(); + } + hc = hc * 31 + (preparedSql == null ? 0 : preparedSql.hashCode()); + return hc; + } + + public boolean equals(Object o) { + if (o == null) { + return false; + } + if (o == this) { + return true; + } + if (o instanceof BindParams) { + return hashCode() == o.hashCode(); + } + return false; + } + + /** + * Return true if there are no bind parameters. + */ + public boolean isEmpty() { + return positionedParameters.isEmpty() && namedParameters.isEmpty(); + } + + /** + * Return a Natural Key bind param if supported. + */ + public NaturalKeyBindParam getNaturalKeyBindParam() { + if (positionedParameters != null){ + return null; + } + if (namedParameters != null && namedParameters.size() == 1){ + Entry e = namedParameters.entrySet().iterator().next(); + return new NaturalKeyBindParam(e.getKey(), e.getValue().getInValue()); + } + return null; + } + + public int size() { + return positionedParameters.size(); + } + + /** + * Return true if named parameters are being used and they have not yet been + * ordered. The sql needs to be prepared (named replaced with ?) and the + * parameters ordered. + */ + public boolean requiresNamedParamsPrepare() { + return !namedParameters.isEmpty() && positionedParameters.isEmpty(); + } + + /** + * Set a null parameter using position. + */ + public void setNullParameter(int position, int jdbcType) { + Param p = getParam(position); + p.setInNullType(jdbcType); + } + + /** + * Set an In Out parameter using position. + */ + public void setParameter(int position, Object value, int outType) { + + addToQueryPlanHash(String.valueOf(position), value); + + Param p = getParam(position); + p.setInValue(value); + p.setOutType(outType); + } + + /** + * Using position set the In value of a parameter. Note that for nulls you + * must use setNullParameter. + */ + public void setParameter(int position, Object value) { + + addToQueryPlanHash(String.valueOf(position), value); + + Param p = getParam(position); + p.setInValue(value); + } + + /** + * Register the parameter as an Out parameter using position. + */ + public void registerOut(int position, int outType) { + Param p = getParam(position); + p.setOutType(outType); + } + + private Param getParam(String name) { + Param p = (Param) namedParameters.get(name); + if (p == null) { + p = new Param(); + namedParameters.put(name, p); + } + return p; + } + + private Param getParam(int position) { + int more = position - positionedParameters.size(); + if (more > 0) { + for (int i = 0; i < more; i++) { + positionedParameters.add(new Param()); + } + } + return (Param) positionedParameters.get(position - 1); + } + + /** + * Set a named In Out parameter. + */ + public void setParameter(String name, Object value, int outType) { + + addToQueryPlanHash(name, value); + + Param p = getParam(name); + p.setInValue(value); + p.setOutType(outType); + } + + /** + * Set a named In parameter that is null. + */ + public void setNullParameter(String name, int jdbcType) { + Param p = getParam(name); + p.setInNullType(jdbcType); + } + + /** + * Set a named In parameter that is not null. + */ + public Param setParameter(String name, Object value) { + + addToQueryPlanHash(name, value); + + Param p = getParam(name); + p.setInValue(value); + return p; + } + + /** + * For binding collections calculate a hash to be used for the query plan. + */ + private void addToQueryPlanHash(String name, Object value){ + if (value != null){ + if (value instanceof Collection){ + queryPlanHash = queryPlanHash * 31 + name.hashCode(); + queryPlanHash = queryPlanHash * 31 + ((Collection)value).size(); + } + } + } + + /** + * Return the hash that should be included with the query plan. + *

+ * This is to handle binding collections to in clauses. The number + * of values in the collection effects the query (number of bind values) + * and so must be taken into account when calculating the query hash. + *

+ */ + public int getQueryPlanHash() { + return queryPlanHash; + } + + /** + * Set an encryption key as a bind value. + *

+ * Needs special treatment as the value should not be included in a log. + *

+ */ + public Param setEncryptionKey(String name, Object value) { + Param p = getParam(name); + p.setEncryptionKey(value); + return p; + } + + /** + * Register the named parameter as an Out parameter. + */ + public void registerOut(String name, int outType) { + Param p = getParam(name); + p.setOutType(outType); + } + + /** + * Return the Parameter for a given position. + */ + public Param getParameter(int position) { + // Used to read Out value by CallableSql + return getParam(position); + } + + /** + * Return the named parameter. + */ + public Param getParameter(String name) { + return getParam(name); + } + + /** + * Return the values of ordered parameters. + */ + public List positionedParameters() { + return positionedParameters; + } + + /** + * Set the sql with named parameters replaced with place holder ?. + */ + public void setPreparedSql(String preparedSql) { + this.preparedSql = preparedSql; + } + + /** + * Return the sql with ? place holders (named parameters have been processed + * and ordered). + */ + public String getPreparedSql() { + return preparedSql; + } + + /** + * The bind parameters in the correct binding order. + *

+ * This is the result of converting sql with named parameters + * into sql with ? and ordered parameters. + *

+ */ + public static final class OrderedList { + + final List paramList; + + final StringBuilder preparedSql; + + public OrderedList() { + this(new ArrayList()); + } + + public OrderedList(List paramList) { + this.paramList = paramList; + this.preparedSql = new StringBuilder(); + } + + /** + * Add a parameter in the correct binding order. + */ + public void add(Param param) { + paramList.add(param); + } + + /** + * Return the number of bind parameters in this list. + */ + public int size() { + return paramList.size(); + } + + /** + * Returns the ordered list of bind parameters. + */ + public List list() { + return paramList; + } + + /** + * Append parsedSql that has named parameters converted into ?. + */ + public void appendSql(String parsedSql) { + preparedSql.append(parsedSql); + } + + public String getPreparedSql() { + return preparedSql.toString(); + } + } + + /** + * A In Out capable parameter for the CallableStatement. + */ + public static final class Param implements Serializable { + + private static final long serialVersionUID = 1L; + + private boolean encryptionKey; + + private boolean isInParam; + + private boolean isOutParam; + + private int type; + + private Object inValue; + + private Object outValue; + + private int textLocation; + + /** + * Construct a Parameter. + */ + public Param() { + } + + /** + * Create a deep copy of the Param. + */ + public Param copy() { + Param copy = new Param(); + copy.isInParam = isInParam; + copy.isOutParam = isOutParam; + copy.type = type; + copy.inValue = inValue; + copy.outValue = outValue; + return copy; + } + + public int hashCode() { + int hc = getClass().hashCode(); + hc = hc * 31 + (isInParam ? 0 : 1); + hc = hc * 31 + (isOutParam ? 0 : 1); + hc = hc * 31 + (type); + hc = hc * 31 + (inValue == null ? 0 : inValue.hashCode()); + return hc; + } + + public boolean equals(Object o) { + if (o == null) { + return false; + } + if (o == this) { + return true; + } + if (o instanceof Param) { + return hashCode() == o.hashCode(); + } + return false; + } + + /** + * Return true if this is an In parameter that needs to be bound before + * execution. + */ + public boolean isInParam() { + return isInParam; + } + + /** + * Return true if this is an out parameter that needs to be registered + * before execution. + */ + public boolean isOutParam() { + return isOutParam; + } + + /** + * Return the jdbc type of this parameter. Used for registering Out + * parameters and setting NULL In parameters. + */ + public int getType() { + return type; + } + + /** + * Set the Out parameter type. + */ + public void setOutType(int type) { + this.type = type; + this.isOutParam = true; + } + + /** + * Set the In value. + */ + public void setInValue(Object in) { + this.inValue = in; + this.isInParam = true; + } + + /** + * Set an encryption key (which can not be logged). + */ + public void setEncryptionKey(Object in) { + this.inValue = in; + this.isInParam = true; + this.encryptionKey = true; + } + + /** + * Specify that the In parameter is NULL and the specific type that it + * is. + */ + public void setInNullType(int type) { + this.type = type; + this.inValue = null; + this.isInParam = true; + } + + /** + * Return the OUT value that was retrieved. This value is set after + * CallableStatement was executed. + */ + public Object getOutValue() { + return outValue; + } + + /** + * Return the In value. If this is null, then the type should be used to + * specify the type of the null. + */ + public Object getInValue() { + return inValue; + } + + /** + * Set the OUT value returned by a CallableStatement after it has + * executed. + */ + public void setOutValue(Object out) { + this.outValue = out; + } + + /** + * Return the location this parameter was found in the sql text. + */ + public int getTextLocation() { + return textLocation; + } + + /** + * Set the location in the sql text this parameter was located. This is + * used to control order for named parameters. + */ + public void setTextLocation(int textLocation) { + this.textLocation = textLocation; + } + + /** + * If true do not include this value in a transaction log. + */ + public boolean isEncryptionKey() { + return encryptionKey; + } + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/ClassLoadContext.java b/src/main/java/com/avaje/ebeaninternal/api/ClassLoadContext.java index efd469b23..6a77fd0d4 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/ClassLoadContext.java +++ b/src/main/java/com/avaje/ebeaninternal/api/ClassLoadContext.java @@ -1,22 +1,3 @@ -/** - * Copyright (C) 2010 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.api; import java.util.logging.Level; diff --git a/src/main/java/com/avaje/ebeaninternal/api/ClassUtil.java b/src/main/java/com/avaje/ebeaninternal/api/ClassUtil.java index 53fc3bd5f..85ab3be68 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/ClassUtil.java +++ b/src/main/java/com/avaje/ebeaninternal/api/ClassUtil.java @@ -1,111 +1,92 @@ -/** - * Copyright (C) 2010 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.api; - -import java.util.logging.Logger; - - -/** - * Helper to find classes taking into account the context class loader. - * - * @author rbygrave - */ -public class ClassUtil { - - private static final Logger logger = Logger.getLogger(ClassUtil.class.getName()); - - private static boolean preferContext = true; - - /** - * Load a class taking into account a context class loader (if present). - */ - public static Class forName(String name) throws ClassNotFoundException { - return forName(name, null); - } - - /** - * Load a class taking into account a context class loader (if present). - */ - public static Class forName(String name, Class caller) throws ClassNotFoundException { - - if (caller == null){ - caller = ClassUtil.class; - } - ClassLoadContext ctx = ClassLoadContext.of(caller, preferContext); - - return ctx.forName(name); - } - - - public static ClassLoader getClassLoader(Class caller, boolean preferContext) { - - if (caller == null){ - caller = ClassUtil.class; - } - ClassLoadContext ctx = ClassLoadContext.of(caller, preferContext); - ClassLoader classLoader = ctx.getDefault(preferContext); - if (ctx.isAmbiguous()){ - logger.info("Ambigous ClassLoader (Context vs Caller) chosen "+classLoader); - } - return classLoader; - } - - /** - * Return true if the given class is present. - */ - public static boolean isPresent(String className) { - return isPresent(className, null); - } - - /** - * Return true if the given class is present. - */ - public static boolean isPresent(String className, Class caller) { - try { - forName(className, caller); - return true; - } catch (Throwable ex) { - // Class or one of its dependencies is not present... - return false; - } - } - - /** - * Return a new instance of the class using the default constructor. - */ - public static Object newInstance(String className) { - return newInstance(className,null); - } - - /** - * Return a new instance of the class using the default constructor. - */ - public static Object newInstance(String className, Class caller) { - - try { - Class cls = forName(className, caller); - return cls.newInstance(); - } catch (Exception e){ - String msg = "Error constructing "+className; - throw new IllegalArgumentException(msg, e); - } - } -} - +package com.avaje.ebeaninternal.api; + +import java.util.logging.Logger; + + +/** + * Helper to find classes taking into account the context class loader. + * + * @author rbygrave + */ +public class ClassUtil { + + private static final Logger logger = Logger.getLogger(ClassUtil.class.getName()); + + private static boolean preferContext = true; + + /** + * Load a class taking into account a context class loader (if present). + */ + public static Class forName(String name) throws ClassNotFoundException { + return forName(name, null); + } + + /** + * Load a class taking into account a context class loader (if present). + */ + public static Class forName(String name, Class caller) throws ClassNotFoundException { + + if (caller == null){ + caller = ClassUtil.class; + } + ClassLoadContext ctx = ClassLoadContext.of(caller, preferContext); + + return ctx.forName(name); + } + + + public static ClassLoader getClassLoader(Class caller, boolean preferContext) { + + if (caller == null){ + caller = ClassUtil.class; + } + ClassLoadContext ctx = ClassLoadContext.of(caller, preferContext); + ClassLoader classLoader = ctx.getDefault(preferContext); + if (ctx.isAmbiguous()){ + logger.info("Ambigous ClassLoader (Context vs Caller) chosen "+classLoader); + } + return classLoader; + } + + /** + * Return true if the given class is present. + */ + public static boolean isPresent(String className) { + return isPresent(className, null); + } + + /** + * Return true if the given class is present. + */ + public static boolean isPresent(String className, Class caller) { + try { + forName(className, caller); + return true; + } catch (Throwable ex) { + // Class or one of its dependencies is not present... + return false; + } + } + + /** + * Return a new instance of the class using the default constructor. + */ + public static Object newInstance(String className) { + return newInstance(className,null); + } + + /** + * Return a new instance of the class using the default constructor. + */ + public static Object newInstance(String className, Class caller) { + + try { + Class cls = forName(className, caller); + return cls.newInstance(); + } catch (Exception e){ + String msg = "Error constructing "+className; + throw new IllegalArgumentException(msg, e); + } + } +} + diff --git a/src/main/java/com/avaje/ebeaninternal/api/HelpScopeTrans.java b/src/main/java/com/avaje/ebeaninternal/api/HelpScopeTrans.java index 7d01952a9..5a73c7063 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/HelpScopeTrans.java +++ b/src/main/java/com/avaje/ebeaninternal/api/HelpScopeTrans.java @@ -1,55 +1,36 @@ -/** - * 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.api; - -import com.avaje.ebean.Ebean; -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.TxScope; - -/** - * Helper object to make AOP generated code simpler. - */ -public class HelpScopeTrans { - - /** - * Create a ScopeTrans for a given methods TxScope. - */ - public static ScopeTrans createScopeTrans(TxScope txScope) { - - EbeanServer server = Ebean.getServer(txScope.getServerName()); - SpiEbeanServer iserver = (SpiEbeanServer)server; - return iserver.createScopeTrans(txScope); - } - - /** - * Exiting the method in an expected fashion. - *

- * That is returning successfully or via a caught exception. - * Unexpected exceptions are caught via the Thread uncaughtExceptionHandler. - *

- * @param returnOrThrowable the return or throwable object - * @param opCode the opcode for ATHROW or ARETURN etc - * @param scopeTrans the scoped transaction the method was run with. - */ - public static void onExitScopeTrans(Object returnOrThrowable, int opCode, ScopeTrans scopeTrans){ - - scopeTrans.onExit(returnOrThrowable, opCode); - } -} +package com.avaje.ebeaninternal.api; + +import com.avaje.ebean.Ebean; +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.TxScope; + +/** + * Helper object to make AOP generated code simpler. + */ +public class HelpScopeTrans { + + /** + * Create a ScopeTrans for a given methods TxScope. + */ + public static ScopeTrans createScopeTrans(TxScope txScope) { + + EbeanServer server = Ebean.getServer(txScope.getServerName()); + SpiEbeanServer iserver = (SpiEbeanServer)server; + return iserver.createScopeTrans(txScope); + } + + /** + * Exiting the method in an expected fashion. + *

+ * That is returning successfully or via a caught exception. + * Unexpected exceptions are caught via the Thread uncaughtExceptionHandler. + *

+ * @param returnOrThrowable the return or throwable object + * @param opCode the opcode for ATHROW or ARETURN etc + * @param scopeTrans the scoped transaction the method was run with. + */ + public static void onExitScopeTrans(Object returnOrThrowable, int opCode, ScopeTrans scopeTrans){ + + scopeTrans.onExit(returnOrThrowable, opCode); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadBeanContext.java b/src/main/java/com/avaje/ebeaninternal/api/LoadBeanContext.java index b09af7086..e5d711260 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/LoadBeanContext.java +++ b/src/main/java/com/avaje/ebeaninternal/api/LoadBeanContext.java @@ -1,58 +1,39 @@ -/** - * 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.api; - -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; - -/** - * Controls the loading of ManyToOne and OneToOne relationships. - * - * @author rbygrave - */ -public interface LoadBeanContext extends LoadSecondaryQuery { - - /** - * Configure the query to load beans for this node/path. - */ - public void configureQuery(SpiQuery query, String lazyLoadProperty); - - /** - * Return the full path of this node from the root object. - */ - public String getFullPath(); - - /** - * Return the persistence context used for all queries - * related to this object graph. - */ - public PersistenceContext getPersistenceContext(); - - /** - * Return the BeanDescriptor for beans for this node. - */ - public BeanDescriptor getBeanDescriptor(); - - /** - * Return the batchSize used for lazy loading beans. - */ - public int getBatchSize(); - -} +package com.avaje.ebeaninternal.api; + +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Controls the loading of ManyToOne and OneToOne relationships. + * + * @author rbygrave + */ +public interface LoadBeanContext extends LoadSecondaryQuery { + + /** + * Configure the query to load beans for this node/path. + */ + public void configureQuery(SpiQuery query, String lazyLoadProperty); + + /** + * Return the full path of this node from the root object. + */ + public String getFullPath(); + + /** + * Return the persistence context used for all queries + * related to this object graph. + */ + public PersistenceContext getPersistenceContext(); + + /** + * Return the BeanDescriptor for beans for this node. + */ + public BeanDescriptor getBeanDescriptor(); + + /** + * Return the batchSize used for lazy loading beans. + */ + public int getBatchSize(); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java b/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java index 9f8d33ab1..ab4c94c2b 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java @@ -1,82 +1,63 @@ -/** - * 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.api; - -import java.util.List; - -import com.avaje.ebean.Transaction; -import com.avaje.ebean.bean.EntityBeanIntercept; - -/** - * Request for loading ManyToOne and OneToOne relationships. - */ -public class LoadBeanRequest extends LoadRequest { - - private final List batch; - - private final LoadBeanContext loadContext; - - private final String lazyLoadProperty; - - private final boolean loadCache; - - public LoadBeanRequest(LoadBeanContext loadContext, List batch, - Transaction transaction, int batchSize, boolean lazy, String lazyLoadProperty, boolean loadCache) { - - super(transaction, batchSize, lazy); - this.loadContext = loadContext; - this.batch = batch; - this.lazyLoadProperty = lazyLoadProperty; - this.loadCache = loadCache; - } - - public boolean isLoadCache() { - return loadCache; - } - - public String getDescription() { - String fullPath = loadContext.getFullPath(); - String s = "path:" + fullPath + " batch:" + batchSize + " actual:" - + batch.size(); - return s; - } - - /** - * Return the batch of beans to actually load. - */ - public List getBatch() { - return batch; - } - - /** - * Return the load context. - */ - public LoadBeanContext getLoadContext() { - return loadContext; - } - - /** - * Return the property that invoked the lazy loading. - */ - public String getLazyLoadProperty() { - return lazyLoadProperty; - } - -} +package com.avaje.ebeaninternal.api; + +import java.util.List; + +import com.avaje.ebean.Transaction; +import com.avaje.ebean.bean.EntityBeanIntercept; + +/** + * Request for loading ManyToOne and OneToOne relationships. + */ +public class LoadBeanRequest extends LoadRequest { + + private final List batch; + + private final LoadBeanContext loadContext; + + private final String lazyLoadProperty; + + private final boolean loadCache; + + public LoadBeanRequest(LoadBeanContext loadContext, List batch, + Transaction transaction, int batchSize, boolean lazy, String lazyLoadProperty, boolean loadCache) { + + super(transaction, batchSize, lazy); + this.loadContext = loadContext; + this.batch = batch; + this.lazyLoadProperty = lazyLoadProperty; + this.loadCache = loadCache; + } + + public boolean isLoadCache() { + return loadCache; + } + + public String getDescription() { + String fullPath = loadContext.getFullPath(); + String s = "path:" + fullPath + " batch:" + batchSize + " actual:" + + batch.size(); + return s; + } + + /** + * Return the batch of beans to actually load. + */ + public List getBatch() { + return batch; + } + + /** + * Return the load context. + */ + public LoadBeanContext getLoadContext() { + return loadContext; + } + + /** + * Return the property that invoked the lazy loading. + */ + public String getLazyLoadProperty() { + return lazyLoadProperty; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadContext.java b/src/main/java/com/avaje/ebeaninternal/api/LoadContext.java index a811f59bf..2af1aa939 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/LoadContext.java +++ b/src/main/java/com/avaje/ebeaninternal/api/LoadContext.java @@ -1,79 +1,60 @@ -/** - * 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.api; - -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; - -/** - * Controls the loading of reference objects for a query instance. - */ -public interface LoadContext { - - /** - * Return the minimum batch size when using QueryIterator with query joins. - */ - public int getSecondaryQueriesMinBatchSize(OrmQueryRequest parentRequest, int defaultQueryBatch); - - /** - * Execute any secondary (+query) queries if there are any defined. - * @param parentRequest the originating query request - */ - public void executeSecondaryQueries(OrmQueryRequest parentRequest, int defaultQueryBatch); - - /** - * Register any secondary queries (+query or +lazy) with their - * appropriate LoadBeanContext or LoadManyContext. - *

- * This is so the LoadBeanContext or LoadManyContext use the - * defined query for +query and +lazy execution. - *

- */ - public void registerSecondaryQueries(SpiQuery query); - - /** - * Return the node for a given path which is used by autofetch profiling. - */ - public ObjectGraphNode getObjectGraphNode(String path); - - /** - * Return the persistence context used by this query and future lazy loading. - */ - public PersistenceContext getPersistenceContext(); - - /** - * Set the persistence context used by this query and future lazy loading. - */ - public void setPersistenceContext(PersistenceContext persistenceContext); - - /** - * Register a Bean for lazy loading. - */ - public void register(String path, EntityBeanIntercept ebi); - - /** - * Register a collection for lazy loading. - */ - public void register(String path, BeanCollection bc); - -} +package com.avaje.ebeaninternal.api; + +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; + +/** + * Controls the loading of reference objects for a query instance. + */ +public interface LoadContext { + + /** + * Return the minimum batch size when using QueryIterator with query joins. + */ + public int getSecondaryQueriesMinBatchSize(OrmQueryRequest parentRequest, int defaultQueryBatch); + + /** + * Execute any secondary (+query) queries if there are any defined. + * @param parentRequest the originating query request + */ + public void executeSecondaryQueries(OrmQueryRequest parentRequest, int defaultQueryBatch); + + /** + * Register any secondary queries (+query or +lazy) with their + * appropriate LoadBeanContext or LoadManyContext. + *

+ * This is so the LoadBeanContext or LoadManyContext use the + * defined query for +query and +lazy execution. + *

+ */ + public void registerSecondaryQueries(SpiQuery query); + + /** + * Return the node for a given path which is used by autofetch profiling. + */ + public ObjectGraphNode getObjectGraphNode(String path); + + /** + * Return the persistence context used by this query and future lazy loading. + */ + public PersistenceContext getPersistenceContext(); + + /** + * Set the persistence context used by this query and future lazy loading. + */ + public void setPersistenceContext(PersistenceContext persistenceContext); + + /** + * Register a Bean for lazy loading. + */ + public void register(String path, EntityBeanIntercept ebi); + + /** + * Register a collection for lazy loading. + */ + public void register(String path, BeanCollection bc); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadManyContext.java b/src/main/java/com/avaje/ebeaninternal/api/LoadManyContext.java index 0fbd1b466..543bde12c 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/LoadManyContext.java +++ b/src/main/java/com/avaje/ebeaninternal/api/LoadManyContext.java @@ -1,73 +1,54 @@ -/** - * 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.api; - -import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; - -/** - * Controls the loading of OneToMany and ManyToMany relationships. - * - * @author rbygrave - */ -public interface LoadManyContext extends LoadSecondaryQuery { - - /** - * Configure the query to load beans for this node/path. - */ - public void configureQuery(SpiQuery query); - - /** - * Return the full path of this node from the root object. - */ - public String getFullPath(); - - /** - * Return the node location for this node/path. - */ - public ObjectGraphNode getObjectGraphNode(); - - - /** - * Return the persistence context used for all queries - * related to this object graph. - */ - public PersistenceContext getPersistenceContext(); - - /** - * Return the batchSize used for lazy loading beans. - */ - public int getBatchSize(); - - /** - * Return the BeanDescriptor for beans for this node. - */ - public BeanDescriptor getBeanDescriptor(); - - /** - * Return the associated Many bean property. - */ - public BeanPropertyAssocMany getBeanProperty(); - - - -} +package com.avaje.ebeaninternal.api; + +import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; + +/** + * Controls the loading of OneToMany and ManyToMany relationships. + * + * @author rbygrave + */ +public interface LoadManyContext extends LoadSecondaryQuery { + + /** + * Configure the query to load beans for this node/path. + */ + public void configureQuery(SpiQuery query); + + /** + * Return the full path of this node from the root object. + */ + public String getFullPath(); + + /** + * Return the node location for this node/path. + */ + public ObjectGraphNode getObjectGraphNode(); + + + /** + * Return the persistence context used for all queries + * related to this object graph. + */ + public PersistenceContext getPersistenceContext(); + + /** + * Return the batchSize used for lazy loading beans. + */ + public int getBatchSize(); + + /** + * Return the BeanDescriptor for beans for this node. + */ + public BeanDescriptor getBeanDescriptor(); + + /** + * Return the associated Many bean property. + */ + public BeanPropertyAssocMany getBeanProperty(); + + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadManyRequest.java b/src/main/java/com/avaje/ebeaninternal/api/LoadManyRequest.java index 72f14bb07..1bbbaf51b 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/LoadManyRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/api/LoadManyRequest.java @@ -1,93 +1,74 @@ -/** - * 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.api; - -import java.util.List; - -import com.avaje.ebean.Transaction; -import com.avaje.ebean.bean.BeanCollection; - -/** - * Request for loading Associated One Beans. - */ -public class LoadManyRequest extends LoadRequest { - - - private final List> batch; - - private final LoadManyContext loadContext; - - private final boolean onlyIds; - - private final boolean loadCache; - - public LoadManyRequest(LoadManyContext loadContext, - List> batch, Transaction transaction, - int batchSize, boolean lazy, boolean onlyIds, boolean loadCache) { - - super(transaction, batchSize, lazy); - this.loadContext = loadContext; - this.batch = batch; - this.onlyIds = onlyIds; - this.loadCache = loadCache; - } - - public String getDescription() { - String fullPath = loadContext.getFullPath(); - String s = "path:" + fullPath + " batch:" + batchSize + " actual:" - + batch.size(); - return s; - } - - /** - * Return the batch of collections to actually load. - */ - public List> getBatch() { - return batch; - } - - /** - * Return the load context. - */ - public LoadManyContext getLoadContext() { - return loadContext; - } - - /** - * Return true if lazy loading should only load the id values. - *

- * This for use when lazy loading is invoked on methods such - * as clear() and removeAll() where it generally makes sense to - * only fetch the Id values as the other property information is - * not used. - *

- */ - public boolean isOnlyIds() { - return onlyIds; - } - - /** - * Return true if we should load the Collection ids into the cache. - */ - public boolean isLoadCache() { - return loadCache; - } - -} +package com.avaje.ebeaninternal.api; + +import java.util.List; + +import com.avaje.ebean.Transaction; +import com.avaje.ebean.bean.BeanCollection; + +/** + * Request for loading Associated One Beans. + */ +public class LoadManyRequest extends LoadRequest { + + + private final List> batch; + + private final LoadManyContext loadContext; + + private final boolean onlyIds; + + private final boolean loadCache; + + public LoadManyRequest(LoadManyContext loadContext, + List> batch, Transaction transaction, + int batchSize, boolean lazy, boolean onlyIds, boolean loadCache) { + + super(transaction, batchSize, lazy); + this.loadContext = loadContext; + this.batch = batch; + this.onlyIds = onlyIds; + this.loadCache = loadCache; + } + + public String getDescription() { + String fullPath = loadContext.getFullPath(); + String s = "path:" + fullPath + " batch:" + batchSize + " actual:" + + batch.size(); + return s; + } + + /** + * Return the batch of collections to actually load. + */ + public List> getBatch() { + return batch; + } + + /** + * Return the load context. + */ + public LoadManyContext getLoadContext() { + return loadContext; + } + + /** + * Return true if lazy loading should only load the id values. + *

+ * This for use when lazy loading is invoked on methods such + * as clear() and removeAll() where it generally makes sense to + * only fetch the Id values as the other property information is + * not used. + *

+ */ + public boolean isOnlyIds() { + return onlyIds; + } + + /** + * Return true if we should load the Collection ids into the cache. + */ + public boolean isLoadCache() { + return loadCache; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadRequest.java b/src/main/java/com/avaje/ebeaninternal/api/LoadRequest.java index 2a9148955..23dc372e5 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/LoadRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/api/LoadRequest.java @@ -1,67 +1,48 @@ -/** - * 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.api; - -import com.avaje.ebean.Transaction; - -/** - * Request for loading Associated One Beans. - */ -public abstract class LoadRequest { - - protected final boolean lazy; - - protected final int batchSize; - - protected final Transaction transaction; - - public LoadRequest(Transaction transaction, int batchSize, boolean lazy) { - - this.transaction = transaction; - this.batchSize = batchSize; - this.lazy = lazy; - } - - - /** - * Return true if this is a lazy load and false if it is a secondary query. - */ - public boolean isLazy() { - return lazy; - } - - /** - * Return the requested batch size. - */ - public int getBatchSize() { - return batchSize; - } - - /** - * Return the transaction to use if this is a secondary query. - *

- * Lazy loading queries run in their own transaction. - *

- */ - public Transaction getTransaction() { - return transaction; - } - -} +package com.avaje.ebeaninternal.api; + +import com.avaje.ebean.Transaction; + +/** + * Request for loading Associated One Beans. + */ +public abstract class LoadRequest { + + protected final boolean lazy; + + protected final int batchSize; + + protected final Transaction transaction; + + public LoadRequest(Transaction transaction, int batchSize, boolean lazy) { + + this.transaction = transaction; + this.batchSize = batchSize; + this.lazy = lazy; + } + + + /** + * Return true if this is a lazy load and false if it is a secondary query. + */ + public boolean isLazy() { + return lazy; + } + + /** + * Return the requested batch size. + */ + public int getBatchSize() { + return batchSize; + } + + /** + * Return the transaction to use if this is a secondary query. + *

+ * Lazy loading queries run in their own transaction. + *

+ */ + public Transaction getTransaction() { + return transaction; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadSecondaryQuery.java b/src/main/java/com/avaje/ebeaninternal/api/LoadSecondaryQuery.java index 33a93abc6..572746e57 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/LoadSecondaryQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/api/LoadSecondaryQuery.java @@ -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.api; - -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; - -/** - * Defines the method for executing secondary queries. - *

- * That is +query nodes in a orm query get executed after - * the initial query as 'secondary' queries. - *

- */ -public interface LoadSecondaryQuery { - - /** - * Execute the secondary query with a given batch size. - * - * @param parentRequest - * the originating query request - */ - public void loadSecondaryQuery(OrmQueryRequest parentRequest, int requestedBatchSize, boolean all); -} +package com.avaje.ebeaninternal.api; + +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; + +/** + * Defines the method for executing secondary queries. + *

+ * That is +query nodes in a orm query get executed after + * the initial query as 'secondary' queries. + *

+ */ +public interface LoadSecondaryQuery { + + /** + * Execute the secondary query with a given batch size. + * + * @param parentRequest + * the originating query request + */ + public void loadSecondaryQuery(OrmQueryRequest parentRequest, int requestedBatchSize, boolean all); +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/ManyWhereJoins.java b/src/main/java/com/avaje/ebeaninternal/api/ManyWhereJoins.java index a9fb76ea4..4c27f49be 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/ManyWhereJoins.java +++ b/src/main/java/com/avaje/ebeaninternal/api/ManyWhereJoins.java @@ -1,95 +1,76 @@ -/** - * 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.api; - -import java.io.Serializable; -import java.util.Set; -import java.util.TreeSet; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.el.ElPropertyDeploy; -import com.avaje.ebeaninternal.server.query.SplitName; - -/** - * Holds the joins needs to support the many where predicates. - * These joins are independent of any 'fetch' joins on the many. - */ -public class ManyWhereJoins implements Serializable { - - private static final long serialVersionUID = -6490181101871795417L; - - private final TreeSet joins = new TreeSet(); - - /** - * Add a many where join. - */ - public void add(ElPropertyDeploy elProp) { - - String join = elProp.getElPrefix(); - BeanProperty p = elProp.getBeanProperty(); - if (p instanceof BeanPropertyAssocMany){ - join = addManyToJoin(join, p.getName()); - } - if (join != null){ - joins.add(join); - String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix(); - if (secondaryTableJoinPrefix != null) { - joins.add(join+"."+secondaryTableJoinPrefix); - } - addParentJoins(join); - } - } - - /** - * For 'many' properties we also need to add the name of the - * many property to get the full logical name of the join. - */ - private String addManyToJoin(String join, String manyPropName){ - if (join == null){ - return manyPropName; - } else { - return join+"."+manyPropName; - } - } - - private void addParentJoins(String join) { - String[] split = SplitName.split(join); - if (split[0] != null){ - joins.add(split[0]); - addParentJoins(split[0]); - } - } - - /** - * Return true if there are no extra many where joins. - */ - public boolean isEmpty() { - return joins.isEmpty(); - } - - /** - * Return the set of many where joins. - */ - public Set getJoins() { - return joins; - } - -} +package com.avaje.ebeaninternal.api; + +import java.io.Serializable; +import java.util.Set; +import java.util.TreeSet; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.el.ElPropertyDeploy; +import com.avaje.ebeaninternal.server.query.SplitName; + +/** + * Holds the joins needs to support the many where predicates. + * These joins are independent of any 'fetch' joins on the many. + */ +public class ManyWhereJoins implements Serializable { + + private static final long serialVersionUID = -6490181101871795417L; + + private final TreeSet joins = new TreeSet(); + + /** + * Add a many where join. + */ + public void add(ElPropertyDeploy elProp) { + + String join = elProp.getElPrefix(); + BeanProperty p = elProp.getBeanProperty(); + if (p instanceof BeanPropertyAssocMany){ + join = addManyToJoin(join, p.getName()); + } + if (join != null){ + joins.add(join); + String secondaryTableJoinPrefix = p.getSecondaryTableJoinPrefix(); + if (secondaryTableJoinPrefix != null) { + joins.add(join+"."+secondaryTableJoinPrefix); + } + addParentJoins(join); + } + } + + /** + * For 'many' properties we also need to add the name of the + * many property to get the full logical name of the join. + */ + private String addManyToJoin(String join, String manyPropName){ + if (join == null){ + return manyPropName; + } else { + return join+"."+manyPropName; + } + } + + private void addParentJoins(String join) { + String[] split = SplitName.split(join); + if (split[0] != null){ + joins.add(split[0]); + addParentJoins(split[0]); + } + } + + /** + * Return true if there are no extra many where joins. + */ + public boolean isEmpty() { + return joins.isEmpty(); + } + + /** + * Return the set of many where joins. + */ + public Set getJoins() { + return joins; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/Monitor.java b/src/main/java/com/avaje/ebeaninternal/api/Monitor.java index c5f77f557..ea9a8454d 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/Monitor.java +++ b/src/main/java/com/avaje/ebeaninternal/api/Monitor.java @@ -1,31 +1,12 @@ -/** - * 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.api; - -import java.io.Serializable; - -/** - * Object used as a synchronization monitor that is serializable. - */ -public class Monitor implements Serializable { - - private static final long serialVersionUID = -2741687226680981940L; - -} +package com.avaje.ebeaninternal.api; + +import java.io.Serializable; + +/** + * Object used as a synchronization monitor that is serializable. + */ +public class Monitor implements Serializable { + + private static final long serialVersionUID = -2741687226680981940L; + +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/ScopeTrans.java b/src/main/java/com/avaje/ebeaninternal/api/ScopeTrans.java index 33cd1f8cf..9cb8e9522 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/ScopeTrans.java +++ b/src/main/java/com/avaje/ebeaninternal/api/ScopeTrans.java @@ -1,221 +1,202 @@ -/** - * 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.api; - -import java.lang.Thread.UncaughtExceptionHandler; -import java.util.ArrayList; - -import com.avaje.ebean.TxScope; - -/** - * Used internally to handle the scoping of transactions for methods. - */ -public class ScopeTrans implements Thread.UncaughtExceptionHandler { - - private static final int OPCODE_ATHROW = 191; - //private static final int OPCODE_ATHROW = com.avaje.ebean.enhance.asm.Opcodes.ATHROW; - - private final SpiTransactionScopeManager scopeMgr; - - /** - * The suspended transaction (can be null). - */ - private final SpiTransaction suspendedTransaction; - /** - * The transaction in scope (can be null). - */ - private final SpiTransaction transaction; - - /** - * If true by default rollback on Checked exceptions. - */ - private final boolean rollbackOnChecked; - - /** - * True if the transaction was created and hence should be committed - * on finally if it hasn't already been rolled back. - */ - private final boolean created; - - /** - * Explicit set of Exceptions that DO NOT cause a rollback to occur. - */ - private final ArrayList> noRollbackFor; - - /** - * Explicit set of Exceptions that DO cause a rollback to occur. - */ - private final ArrayList> rollbackFor; - - - private final UncaughtExceptionHandler originalUncaughtHandler; - - /** - * Flag set when a rollback has occurred. - */ - private boolean rolledBack; - - - public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope, - SpiTransaction suspendedTransaction, SpiTransactionScopeManager scopeMgr) { - - this.rollbackOnChecked = rollbackOnChecked; - this.created = created; - this.transaction = transaction; - this.suspendedTransaction = suspendedTransaction; - this.scopeMgr = scopeMgr; - - this.noRollbackFor = txScope.getNoRollbackFor(); - this.rollbackFor = txScope.getRollbackFor(); - - Thread t = Thread.currentThread(); - originalUncaughtHandler = t.getUncaughtExceptionHandler(); - - t.setUncaughtExceptionHandler(this); - } - - /** - * Called when the Thread catches any uncaught exception. - * For example, an unexpected NullPointerException or Error. - */ - public void uncaughtException(Thread thread, Throwable e) { - - // rollback transaction if required - caughtThrowable(e); - - // reinstate suspended transaction and - // original uncaughtExceptionHandler if required - onFinally(); - - if (originalUncaughtHandler != null){ - originalUncaughtHandler.uncaughtException(thread, e); - } - } - - /** - * Returned via RETURN or expected Exception from the method. - * @param returnOrThrowable the return value or Throwable - * @param opCode indicates - */ - public void onExit(Object returnOrThrowable, int opCode) { - - if (opCode == OPCODE_ATHROW){ - // exited with a Throwable - caughtThrowable((Throwable)returnOrThrowable); - } - onFinally(); - } - - - /** - * Commit if the transaction exists and has not already been rolled back. - * Also reinstate the suspended transaction if there was one. - */ - public void onFinally() { - try { - if (originalUncaughtHandler != null){ - Thread.currentThread().setUncaughtExceptionHandler(originalUncaughtHandler); - } - - if (!rolledBack && created) { - transaction.commit(); - } - - } finally { - if (suspendedTransaction != null){ - // put the previously suspended transaction - // back onto the ThreadLocal or equivalent - scopeMgr.replace(suspendedTransaction); - } - } - } - - /** - * An Error was caught and this ALWAYS causes a rollback to occur. - * Returns the error and this should be thrown by the calling code. - */ - public Error caughtError(Error e) { - rollback(e); - return e; - } - - /** - * An Exception was caught and may or may not cause a rollback to occur. - * Returns the exception and this should be thrown by the calling code. - */ - public T caughtThrowable(T e) { - - if (isRollbackThrowable(e)) { - rollback(e); - } - return e; - } - - private void rollback(Throwable e) { - if (transaction != null && transaction.isActive()) { - // transaction is null for NOT_SUPPORTED and sometimes SUPPORTS - // and Inactive (already rolled back) if nested REQUIRED - transaction.rollback(e); - } - rolledBack = true; - } - - /** - * Return true if this throwable should cause a rollback to occur. - */ - private boolean isRollbackThrowable(Throwable e) { - - if (e instanceof Error){ - return true; - } - - if (noRollbackFor != null){ - for (int i = 0; i < noRollbackFor.size(); i++) { - if (noRollbackFor.get(i).equals(e.getClass())) { - - // explicit no rollback for this one - return false; - } - } - } - - if (rollbackFor != null){ - for (int i = 0; i < rollbackFor.size(); i++) { - if (rollbackFor.get(i).equals(e.getClass())) { - // explicit rollback for this one - return true; - } - } - } - - - if (e instanceof RuntimeException) { - return true; - - } else { - // checked exceptions... - // EJB defaults this to false which is not intuitive IMO - // Ebean makes this configurable (default to true) - return rollbackOnChecked; - } - } - - -} +package com.avaje.ebeaninternal.api; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.ArrayList; + +import com.avaje.ebean.TxScope; + +/** + * Used internally to handle the scoping of transactions for methods. + */ +public class ScopeTrans implements Thread.UncaughtExceptionHandler { + + private static final int OPCODE_ATHROW = 191; + //private static final int OPCODE_ATHROW = com.avaje.ebean.enhance.asm.Opcodes.ATHROW; + + private final SpiTransactionScopeManager scopeMgr; + + /** + * The suspended transaction (can be null). + */ + private final SpiTransaction suspendedTransaction; + /** + * The transaction in scope (can be null). + */ + private final SpiTransaction transaction; + + /** + * If true by default rollback on Checked exceptions. + */ + private final boolean rollbackOnChecked; + + /** + * True if the transaction was created and hence should be committed + * on finally if it hasn't already been rolled back. + */ + private final boolean created; + + /** + * Explicit set of Exceptions that DO NOT cause a rollback to occur. + */ + private final ArrayList> noRollbackFor; + + /** + * Explicit set of Exceptions that DO cause a rollback to occur. + */ + private final ArrayList> rollbackFor; + + + private final UncaughtExceptionHandler originalUncaughtHandler; + + /** + * Flag set when a rollback has occurred. + */ + private boolean rolledBack; + + + public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope, + SpiTransaction suspendedTransaction, SpiTransactionScopeManager scopeMgr) { + + this.rollbackOnChecked = rollbackOnChecked; + this.created = created; + this.transaction = transaction; + this.suspendedTransaction = suspendedTransaction; + this.scopeMgr = scopeMgr; + + this.noRollbackFor = txScope.getNoRollbackFor(); + this.rollbackFor = txScope.getRollbackFor(); + + Thread t = Thread.currentThread(); + originalUncaughtHandler = t.getUncaughtExceptionHandler(); + + t.setUncaughtExceptionHandler(this); + } + + /** + * Called when the Thread catches any uncaught exception. + * For example, an unexpected NullPointerException or Error. + */ + public void uncaughtException(Thread thread, Throwable e) { + + // rollback transaction if required + caughtThrowable(e); + + // reinstate suspended transaction and + // original uncaughtExceptionHandler if required + onFinally(); + + if (originalUncaughtHandler != null){ + originalUncaughtHandler.uncaughtException(thread, e); + } + } + + /** + * Returned via RETURN or expected Exception from the method. + * @param returnOrThrowable the return value or Throwable + * @param opCode indicates + */ + public void onExit(Object returnOrThrowable, int opCode) { + + if (opCode == OPCODE_ATHROW){ + // exited with a Throwable + caughtThrowable((Throwable)returnOrThrowable); + } + onFinally(); + } + + + /** + * Commit if the transaction exists and has not already been rolled back. + * Also reinstate the suspended transaction if there was one. + */ + public void onFinally() { + try { + if (originalUncaughtHandler != null){ + Thread.currentThread().setUncaughtExceptionHandler(originalUncaughtHandler); + } + + if (!rolledBack && created) { + transaction.commit(); + } + + } finally { + if (suspendedTransaction != null){ + // put the previously suspended transaction + // back onto the ThreadLocal or equivalent + scopeMgr.replace(suspendedTransaction); + } + } + } + + /** + * An Error was caught and this ALWAYS causes a rollback to occur. + * Returns the error and this should be thrown by the calling code. + */ + public Error caughtError(Error e) { + rollback(e); + return e; + } + + /** + * An Exception was caught and may or may not cause a rollback to occur. + * Returns the exception and this should be thrown by the calling code. + */ + public T caughtThrowable(T e) { + + if (isRollbackThrowable(e)) { + rollback(e); + } + return e; + } + + private void rollback(Throwable e) { + if (transaction != null && transaction.isActive()) { + // transaction is null for NOT_SUPPORTED and sometimes SUPPORTS + // and Inactive (already rolled back) if nested REQUIRED + transaction.rollback(e); + } + rolledBack = true; + } + + /** + * Return true if this throwable should cause a rollback to occur. + */ + private boolean isRollbackThrowable(Throwable e) { + + if (e instanceof Error){ + return true; + } + + if (noRollbackFor != null){ + for (int i = 0; i < noRollbackFor.size(); i++) { + if (noRollbackFor.get(i).equals(e.getClass())) { + + // explicit no rollback for this one + return false; + } + } + } + + if (rollbackFor != null){ + for (int i = 0; i < rollbackFor.size(); i++) { + if (rollbackFor.get(i).equals(e.getClass())) { + // explicit rollback for this one + return true; + } + } + } + + + if (e instanceof RuntimeException) { + return true; + + } else { + // checked exceptions... + // EJB defaults this to false which is not intuitive IMO + // Ebean makes this configurable (default to true) + return rollbackOnChecked; + } + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java b/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java index eb06553aa..6c4ea84c9 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java @@ -1,210 +1,191 @@ -/** - * 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.api; - -import java.util.List; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.Query; -import com.avaje.ebean.Transaction; -import com.avaje.ebean.TxScope; -import com.avaje.ebean.bean.BeanCollectionLoader; -import com.avaje.ebean.bean.BeanLoader; -import com.avaje.ebean.bean.CallStack; -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; -import com.avaje.ebeaninternal.server.core.PstmtBatch; -import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest; -import com.avaje.ebeaninternal.server.ddl.DdlGenerator; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.query.CQuery; -import com.avaje.ebeaninternal.server.query.CQueryEngine; -import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; - -/** - * Service Provider extension to EbeanServer. - */ -public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionLoader { - - /** - * Return true if DeleteMissingChildren defaults to true for stateless updates. - */ - public boolean isDefaultDeleteMissingChildren(); - - /** - * Return true if UpdateNullProperties defaults to true for stateless updates. - */ - public boolean isDefaultUpdateNullProperties(); - - /** - * Return true if vanilla beans should be returned by queries by default. - */ - public boolean isVanillaMode(); - - /** - * Return the DatabasePlatform for this server. - */ - public DatabasePlatform getDatabasePlatform(); - - /** - * Return a JDBC driver specific handler for batching. - *

- * Required for Oracle specific batch handling. - *

- */ - public PstmtBatch getPstmtBatch(); - - /** - * Create an object to represent the current CallStack. - *

- * Typically used to identify the origin of queries for Autofetch - * and object graph costing. - *

- */ - public CallStack createCallStack(); - - /** - * Return the DDL generator. - */ - public DdlGenerator getDdlGenerator(); - - /** - * Return the AutoFetchListener. - */ - public AutoFetchManager getAutoFetchManager(); - - /** - * Clear the query execution statistics. - */ - public void clearQueryStatistics(); - - /** - * Return all the descriptors. - */ - public List> getBeanDescriptors(); - - /** - * Return the BeanDescriptor for a given type of bean. - */ - public BeanDescriptor getBeanDescriptor(Class type); - - /** - * Return BeanDescriptor using it's unique id. - */ - public BeanDescriptor getBeanDescriptorById(String descriptorId); - - /** - * Return BeanDescriptors mapped to this table. - */ - public List> getBeanDescriptors(String tableName); - - /** - * Process committed changes from another framework. - *

- * This notifies this instance of the framework that beans have been - * committed externally to it. Either by another framework or clustered - * server. It uses this to maintain its cache and text indexes - * appropriately. - *

- */ - public void externalModification(TransactionEventTable event); - - /** - * Create a ServerTransaction. - *

- * To specify to use the default transaction isolation use a value of -1. - *

- */ - public SpiTransaction createServerTransaction(boolean isExplicit, int isolationLevel); - - /** - * Return the current transaction or null if there is no current - * transaction. - */ - public SpiTransaction getCurrentServerTransaction(); - - /** - * Create a ScopeTrans for a method for the given scope definition. - */ - public ScopeTrans createScopeTrans(TxScope txScope); - - /** - * Create a ServerTransaction for query purposes. - */ - public SpiTransaction createQueryTransaction(); - - /** - * An event from another server in the cluster used to notify local - * BeanListeners of remote inserts updates and deletes. - */ - public void remoteTransactionEvent(RemoteTransactionEvent event); - - - /** - * Create a query request object. - */ - public SpiOrmQueryRequest createQueryRequest(BeanDescriptor desc, SpiQuery q, Transaction t); - - /** - * Compile a query. - */ - public CQuery compileQuery(Query query, Transaction t); - - /** - * Return the queryEngine for this server. - */ - public CQueryEngine getQueryEngine(); - - /** - * Execute the findId's query but without copying the query. - *

- * Used so that the list of Id's can be made accessible to client code - * before the query has finished (if executing in a background thread). - *

- */ - public List findIdsWithCopy(Query query, Transaction t); - - /** - * Execute the findRowCount query but without copying the query. - */ - public int findRowCountWithCopy(Query query, Transaction t); - - /** - * Load a batch of Associated One Beans. - */ - public void loadBean(LoadBeanRequest loadRequest); - - /** - * Lazy load a batch of Many's. - */ - public void loadMany(LoadManyRequest loadRequest); - - /** - * Return the default batch size for lazy loading. - */ - public int getLazyLoadBatchSize(); - - /** - * Return true if the type is known as an Entity or Xml type - * or a List Set or Map of known bean types. - */ - public boolean isSupportedType(java.lang.reflect.Type genericType); - -} +package com.avaje.ebeaninternal.api; + +import java.util.List; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.Query; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.TxScope; +import com.avaje.ebean.bean.BeanCollectionLoader; +import com.avaje.ebean.bean.BeanLoader; +import com.avaje.ebean.bean.CallStack; +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; +import com.avaje.ebeaninternal.server.core.PstmtBatch; +import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest; +import com.avaje.ebeaninternal.server.ddl.DdlGenerator; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.query.CQuery; +import com.avaje.ebeaninternal.server.query.CQueryEngine; +import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; + +/** + * Service Provider extension to EbeanServer. + */ +public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionLoader { + + /** + * Return true if DeleteMissingChildren defaults to true for stateless updates. + */ + public boolean isDefaultDeleteMissingChildren(); + + /** + * Return true if UpdateNullProperties defaults to true for stateless updates. + */ + public boolean isDefaultUpdateNullProperties(); + + /** + * Return true if vanilla beans should be returned by queries by default. + */ + public boolean isVanillaMode(); + + /** + * Return the DatabasePlatform for this server. + */ + public DatabasePlatform getDatabasePlatform(); + + /** + * Return a JDBC driver specific handler for batching. + *

+ * Required for Oracle specific batch handling. + *

+ */ + public PstmtBatch getPstmtBatch(); + + /** + * Create an object to represent the current CallStack. + *

+ * Typically used to identify the origin of queries for Autofetch + * and object graph costing. + *

+ */ + public CallStack createCallStack(); + + /** + * Return the DDL generator. + */ + public DdlGenerator getDdlGenerator(); + + /** + * Return the AutoFetchListener. + */ + public AutoFetchManager getAutoFetchManager(); + + /** + * Clear the query execution statistics. + */ + public void clearQueryStatistics(); + + /** + * Return all the descriptors. + */ + public List> getBeanDescriptors(); + + /** + * Return the BeanDescriptor for a given type of bean. + */ + public BeanDescriptor getBeanDescriptor(Class type); + + /** + * Return BeanDescriptor using it's unique id. + */ + public BeanDescriptor getBeanDescriptorById(String descriptorId); + + /** + * Return BeanDescriptors mapped to this table. + */ + public List> getBeanDescriptors(String tableName); + + /** + * Process committed changes from another framework. + *

+ * This notifies this instance of the framework that beans have been + * committed externally to it. Either by another framework or clustered + * server. It uses this to maintain its cache and text indexes + * appropriately. + *

+ */ + public void externalModification(TransactionEventTable event); + + /** + * Create a ServerTransaction. + *

+ * To specify to use the default transaction isolation use a value of -1. + *

+ */ + public SpiTransaction createServerTransaction(boolean isExplicit, int isolationLevel); + + /** + * Return the current transaction or null if there is no current + * transaction. + */ + public SpiTransaction getCurrentServerTransaction(); + + /** + * Create a ScopeTrans for a method for the given scope definition. + */ + public ScopeTrans createScopeTrans(TxScope txScope); + + /** + * Create a ServerTransaction for query purposes. + */ + public SpiTransaction createQueryTransaction(); + + /** + * An event from another server in the cluster used to notify local + * BeanListeners of remote inserts updates and deletes. + */ + public void remoteTransactionEvent(RemoteTransactionEvent event); + + + /** + * Create a query request object. + */ + public SpiOrmQueryRequest createQueryRequest(BeanDescriptor desc, SpiQuery q, Transaction t); + + /** + * Compile a query. + */ + public CQuery compileQuery(Query query, Transaction t); + + /** + * Return the queryEngine for this server. + */ + public CQueryEngine getQueryEngine(); + + /** + * Execute the findId's query but without copying the query. + *

+ * Used so that the list of Id's can be made accessible to client code + * before the query has finished (if executing in a background thread). + *

+ */ + public List findIdsWithCopy(Query query, Transaction t); + + /** + * Execute the findRowCount query but without copying the query. + */ + public int findRowCountWithCopy(Query query, Transaction t); + + /** + * Load a batch of Associated One Beans. + */ + public void loadBean(LoadBeanRequest loadRequest); + + /** + * Lazy load a batch of Many's. + */ + public void loadMany(LoadManyRequest loadRequest); + + /** + * Return the default batch size for lazy loading. + */ + public int getLazyLoadBatchSize(); + + /** + * Return true if the type is known as an Entity or Xml type + * or a List Set or Map of known bean types. + */ + public boolean isSupportedType(java.lang.reflect.Type genericType); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionFactory.java b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionFactory.java index 4f8a88ff5..f0bfc101a 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionFactory.java @@ -1,32 +1,13 @@ -/** - * 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.api; - -import com.avaje.ebean.ExpressionFactory; -import com.avaje.ebeaninternal.server.expression.FilterExprPath; - -public interface SpiExpressionFactory extends ExpressionFactory { - - /** - * Create another expression factory with a given sub path. - */ - public ExpressionFactory createExpressionFactory(FilterExprPath prefix); - -} +package com.avaje.ebeaninternal.api; + +import com.avaje.ebean.ExpressionFactory; +import com.avaje.ebeaninternal.server.expression.FilterExprPath; + +public interface SpiExpressionFactory extends ExpressionFactory { + + /** + * Create another expression factory with a given sub path. + */ + public ExpressionFactory createExpressionFactory(FilterExprPath prefix); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java index b13bc7df1..c00bf5514 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java @@ -1,609 +1,590 @@ -/** - * 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.api; - -import com.avaje.ebean.ExpressionList; -import com.avaje.ebean.OrderBy; -import com.avaje.ebean.Query; -import com.avaje.ebean.QueryListener; -import com.avaje.ebean.bean.BeanCollectionTouched; -import com.avaje.ebean.bean.CallStack; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebean.event.BeanQueryRequest; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.TableJoin; -import com.avaje.ebeaninternal.server.query.CancelableQuery; -import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties; - -import java.util.ArrayList; -import java.util.List; - -/** - * Object Relational query - Internal extension to Query object. - */ -public interface SpiQuery extends Query { - - public enum Mode { - NORMAL(false), LAZYLOAD_MANY(false), LAZYLOAD_BEAN(true), REFRESH_BEAN(true); - Mode(boolean loadContextBean) { - this.loadContextBean = loadContextBean; - } - - private final boolean loadContextBean; - - public boolean isLoadContextBean() { - return loadContextBean; - } - } - - /** - * The type of query result. - */ - public enum Type { - - /** - * Find by Id or unique returning a single bean. - */ - BEAN, - - /** - * Find returning a List. - */ - LIST, - - /** - * Find returning a Set. - */ - SET, - - /** - * Find returning a Map. - */ - MAP, - - /** - * Find the Id's. - */ - ID_LIST, - - /** - * Find rowCount. - */ - ROWCOUNT, - - /** - * A subquery used as part of a where clause. - */ - SUBQUERY - } - - /** - * Set total hits when querying against lucene. - */ - public void setTotalHits(int totalHits); - - /** - * Return true if select all properties was used to ensure the property - * invoking a lazy load was included in the query. - */ - public boolean selectAllForLazyLoadProperty(); - - /** - * Set the query mode. - */ - public void setMode(Mode m); - - /** - * Return the query mode. - */ - public Mode getMode(); - - /** - * Return a listener that wants to be notified when the bean collection is - * first used. - */ - public BeanCollectionTouched getBeanCollectionTouched(); - - /** - * Set a listener to be notified when the bean collection has been touched - * (when the list/set/map is first used). - */ - public void setBeanCollectionTouched(BeanCollectionTouched notify); - - /** - * Set the list of Id's that is being populated. - *

- * This is a mutating list of id's and we are setting this so that other - * threads have access to the id's before the id query has finished. - *

- */ - public void setIdList(List ids); - - /** - * Return the list of Id's that is currently being fetched by a background - * thread. - */ - public List getIdList(); - - /** - * Return a copy of the query. - */ - public SpiQuery copy(); - - /** - * Return the type of query (List, Set, Map, Bean, rowCount etc). - */ - public Type getType(); - - /** - * Set the query type (List, Set etc). - */ - public void setType(Type type); - - /** - * Return a more detailed description of the lazy or query load. - */ - public String getLoadDescription(); - - /** - * Return the load mode (+lazy or +query). - */ - public String getLoadMode(); - - /** - * Set the load mode (+lazy or +query) and the load description. - * - * @param loadMode - * @param loadDescription - */ - public void setLoadDescription(String loadMode, String loadDescription); - - /** - * Set the BeanDescriptor for the root type of this query. - */ - public void setBeanDescriptor(BeanDescriptor desc); - - /** - * Initialise/determine the joins required to support 'many' where clause predicates. - */ - public boolean initManyWhereJoins(); - - /** - * Return the joins required to support predicates on the many properties. - */ - public ManyWhereJoins getManyWhereJoins(); - - /** - * Convert this natural key query into a find by id query. - */ - public void convertWhereNaturalKeyToId(Object idValue); - - /** - * Return a Natural Key bind parameter if supported by this query. - */ - public NaturalKeyBindParam getNaturalKeyBindParam(); - - /** - * Set the query to select the id property only. - */ - public void setSelectId(); - - /** - * Set a filter to a join path. - */ - public void setFilterMany(String prop, ExpressionList filterMany); - - /** - * Remove the query joins from query detail. - *

- * These are registered with the Load Context. - *

- */ - public List removeQueryJoins(); - - /** - * Remove the lazy joins from query detail. - *

- * These are registered with the Load Context. - *

- */ - public List removeLazyJoins(); - - /** - * Set the path of the many when +query/+lazy loading query is executed. - */ - public void setLazyLoadManyPath(String lazyLoadManyPath); - - /** - * Convert any many joins fetch joins to query joins. - */ - public void convertManyFetchJoinsToQueryJoins(boolean allowOne, int queryBatch); - - /** - * Return the TransactionContext. - *

- * If no TransactionContext is present on the query then the - * TransactionContext from the Transaction is used (transaction scoped - * persistence context). - *

- */ - public PersistenceContext getPersistenceContext(); - - /** - * Set an explicit TransactionContext (typically for a refresh query). - *

- * If no TransactionContext is present on the query then the - * TransactionContext from the Transaction is used (transaction scoped - * persistence context). - *

- */ - public void setPersistenceContext(PersistenceContext transactionContext); - - /** - * Return true if the query detail has neither select or joins specified. - */ - public boolean isDetailEmpty(); - - /** - * Return explicit autoFetch setting or null. If null then not explicitly - * set so we use the default behaviour. - */ - public Boolean isAutofetch(); - -// /** -// * Return explicit forUpdate setting or null. -// */ -// public boolean isForUpdate(); - - /** - * If return null then no autoFetch profiling for this query. If a - * AutoFetchManager is returned this implies that profiling is turned on for - * this query (and all the objects this query creates). - */ - public AutoFetchManager getAutoFetchManager(); - - /** - * This has the effect of turning on autoFetch profiling for this query. - */ - public void setAutoFetchManager(AutoFetchManager manager); - - /** - * Return the origin point for the query. - *

- * This MUST be call prior to a query being changed via tuning. This is - * because the queryPlanHash is used to identify the query point. - *

- */ - public ObjectGraphNode setOrigin(CallStack callStack); - - /** - * Set the profile point of the bean or collection that is lazy loading. - *

- * This enables use to hook this back to the original 'root' query by the - * queryPlanHash and stackPoint. - *

- */ - public void setParentNode(ObjectGraphNode node); - - /** - * Set the property that invoked the lazy load and MUST be included in the - * lazy loading query. - */ - public void setLazyLoadProperty(String lazyLoadProperty); - - /** - * Return the property that invoked lazy load. - */ - public String getLazyLoadProperty(); - - /** - * Return the lazy load path. - */ - public String getLazyLoadManyPath(); - - /** - * Used to hook back a lazy loading query to the original query (query - * point). - *

- * This will return null or an "original" query. - *

- */ - public ObjectGraphNode getParentNode(); - - /** - * Return false when this is a lazy load or refresh query for a bean. - *

- * We just take/copy the data from those beans and don't collect autoFetch - * usage profiling on those lazy load or refresh beans. - *

- */ - public boolean isUsageProfiling(); - - /** - * Set to false if this query should not be included in the autoFetch usage - * profiling information. - */ - public void setUsageProfiling(boolean usageProfiling); - - /** - * Return the query name. - */ - public String getName(); - - /** - * Calculate a hash used by AutoFetch to identify when a query has changed - * (and hence potentially needs a new tuned query plan to be developed). - *

- * Excludes bind values and occurs prior to AutoFetch potentially - * tuning/modifying the query. - *

- */ - public int queryAutofetchHash(); - - /** - * Identifies queries that are the same bar the bind variables. - *

- * This is used AFTER AutoFetch has potentially tuned the query. This is - * used to identify and reused query plans (the final SQL string and - * associated SqlTree object). - *

- *

- * Excludes the actual bind values (as they don't effect the query plan). - *

- */ - public int queryPlanHash(BeanQueryRequest request); - - /** - * Calculate a hash based on the bind values used in the query. - *

- * Combined with queryPlanHash() to return getQueryHash (a unique hash for a - * query). - *

- */ - public int queryBindHash(); - - /** - * Identifies queries that are exactly the same including bind variables. - */ - public int queryHash(); - - /** - * Return true if this is a query based on a SqlSelect rather than - * generated. - */ - public boolean isSqlSelect(); - - /** - * Return true if this is a RawSql query. - */ - public boolean isRawSql(); - - /** - * Return the Order By clause or null if there is none defined. - */ - public OrderBy getOrderBy(); - - /** - * Return additional where clause. This should be added to any where clause - * that was part of the original query. - */ - public String getAdditionalWhere(); - - /** - * Can return null if no expressions where added to the where clause. - */ - public SpiExpressionList getWhereExpressions(); - - /** - * Can return null if no expressions where added to the having clause. - */ - public SpiExpressionList getHavingExpressions(); - - /** - * Return additional having clause. Where raw String expressions are added - * to having clause rather than Expression objects. - */ - public String getAdditionalHaving(); - - /** - * Returns true if either firstRow or maxRows has been set. - */ - public boolean hasMaxRowsOrFirstRow(); - - /** - * Return true if this query should use/check the bean cache. - */ - public Boolean isUseBeanCache(); - - /** - * Return true if this query should use/check the query cache. - */ - public boolean isUseQueryCache(); - - /** - * Return true if the beans from this query should be loaded into the bean - * cache. - */ - public boolean isLoadBeanCache(); - - /** - * Return true if the beans returned by this query should be read only. - */ - public Boolean isReadOnly(); - - /** - * Adds this bean to the persistence context prior to executing the query. - */ - public void contextAdd(EntityBean bean); - - /** - * Return the type of beans queries. - */ - public Class getBeanType(); - - /** - * Return the query timeout. - */ - public int getTimeout(); - - /** - * Return the objects that should be added to the persistence context prior - * to executing the query. - */ - public ArrayList getContextAdditions(); - - /** - * Return the bind parameters. - */ - public BindParams getBindParams(); - - /** - * Get the orm query as a String. Only available if the query was built from - * a string. - */ - public String getQuery(); - - /** - * Replace the query detail. This is used by the autoFetch feature to as a - * fast way to set the query properties and joins. - *

- * Note care must be taken to keep the where, orderBy, firstRows and maxRows - * held in the detail attributes. - *

- */ - public void setDetail(OrmQueryDetail detail); - - /** - * Autofetch tune the detail specifying properties to select on already defined joins - * and adding extra joins where they are missing. - */ - public boolean tuneFetchProperties(OrmQueryDetail detail); - - /** - * Set to true if this query has been tuned by autoFetch. - */ - public void setAutoFetchTuned(boolean autoFetchTuned); - - /** - * Return the query detail. - */ - public OrmQueryDetail getDetail(); - - public TableJoin getIncludeTableJoin(); - - public void setIncludeTableJoin(TableJoin includeTableJoin); - - /** - * Return the property used to specify keys for a map. - */ - public String getMapKey(); - - /** - * Return the number of rows after which fetching should occur in a - * background thread. - */ - public int getBackgroundFetchAfter(); - - /** - * Return the maximum number of rows to return in the query. - */ - public int getMaxRows(); - - /** - * Return the index of the first row to return in the query. - */ - public int getFirstRow(); - - /** - * return true if this query uses DISTINCT. - */ - public boolean isDistinct(); - - /** - * Return true if this query should build and return vanilla objects. - */ - public boolean isVanillaMode(boolean serverDefaultVanillaMode); - - /** - * Set default select clauses where none have been explicitly defined. - */ - public void setDefaultSelectClause(); - - /** - * Return the where clause from a parsed string query. - */ - public String getRawWhereClause(); - - /** - * Return the Id value. - */ - public Object getId(); - - /** - * Return the queryListener. - */ - public QueryListener getListener(); - - /** - * Return true if this query should use its own transaction. - *

- * This is true for background fetching and when using QueryListener. - *

- */ - public boolean createOwnTransaction(); - - /** - * Set the generated sql for debug purposes. - * - * @param generatedSql - */ - public void setGeneratedSql(String generatedSql); - - /** - * Return the hint for Statement.setFetchSize(). - */ - public int getBufferFetchSizeHint(); - - /** - * Return true if this is a query executing in the background. - */ - public boolean isFutureFetch(); - - /** - * Set to true to indicate the query is executing in a background thread - * asynchronously. - */ - public void setFutureFetch(boolean futureFetch); - - /** - * Set the underlying cancelable query (with the PreparedStatement). - */ - public void setCancelableQuery(CancelableQuery cancelableQuery); - - /** - * Return true if this query has been cancelled. - */ - public boolean isCancelled(); -} +package com.avaje.ebeaninternal.api; + +import com.avaje.ebean.ExpressionList; +import com.avaje.ebean.OrderBy; +import com.avaje.ebean.Query; +import com.avaje.ebean.QueryListener; +import com.avaje.ebean.bean.BeanCollectionTouched; +import com.avaje.ebean.bean.CallStack; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebean.event.BeanQueryRequest; +import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.TableJoin; +import com.avaje.ebeaninternal.server.query.CancelableQuery; +import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties; + +import java.util.ArrayList; +import java.util.List; + +/** + * Object Relational query - Internal extension to Query object. + */ +public interface SpiQuery extends Query { + + public enum Mode { + NORMAL(false), LAZYLOAD_MANY(false), LAZYLOAD_BEAN(true), REFRESH_BEAN(true); + Mode(boolean loadContextBean) { + this.loadContextBean = loadContextBean; + } + + private final boolean loadContextBean; + + public boolean isLoadContextBean() { + return loadContextBean; + } + } + + /** + * The type of query result. + */ + public enum Type { + + /** + * Find by Id or unique returning a single bean. + */ + BEAN, + + /** + * Find returning a List. + */ + LIST, + + /** + * Find returning a Set. + */ + SET, + + /** + * Find returning a Map. + */ + MAP, + + /** + * Find the Id's. + */ + ID_LIST, + + /** + * Find rowCount. + */ + ROWCOUNT, + + /** + * A subquery used as part of a where clause. + */ + SUBQUERY + } + + /** + * Set total hits when querying against lucene. + */ + public void setTotalHits(int totalHits); + + /** + * Return true if select all properties was used to ensure the property + * invoking a lazy load was included in the query. + */ + public boolean selectAllForLazyLoadProperty(); + + /** + * Set the query mode. + */ + public void setMode(Mode m); + + /** + * Return the query mode. + */ + public Mode getMode(); + + /** + * Return a listener that wants to be notified when the bean collection is + * first used. + */ + public BeanCollectionTouched getBeanCollectionTouched(); + + /** + * Set a listener to be notified when the bean collection has been touched + * (when the list/set/map is first used). + */ + public void setBeanCollectionTouched(BeanCollectionTouched notify); + + /** + * Set the list of Id's that is being populated. + *

+ * This is a mutating list of id's and we are setting this so that other + * threads have access to the id's before the id query has finished. + *

+ */ + public void setIdList(List ids); + + /** + * Return the list of Id's that is currently being fetched by a background + * thread. + */ + public List getIdList(); + + /** + * Return a copy of the query. + */ + public SpiQuery copy(); + + /** + * Return the type of query (List, Set, Map, Bean, rowCount etc). + */ + public Type getType(); + + /** + * Set the query type (List, Set etc). + */ + public void setType(Type type); + + /** + * Return a more detailed description of the lazy or query load. + */ + public String getLoadDescription(); + + /** + * Return the load mode (+lazy or +query). + */ + public String getLoadMode(); + + /** + * Set the load mode (+lazy or +query) and the load description. + * + * @param loadMode + * @param loadDescription + */ + public void setLoadDescription(String loadMode, String loadDescription); + + /** + * Set the BeanDescriptor for the root type of this query. + */ + public void setBeanDescriptor(BeanDescriptor desc); + + /** + * Initialise/determine the joins required to support 'many' where clause predicates. + */ + public boolean initManyWhereJoins(); + + /** + * Return the joins required to support predicates on the many properties. + */ + public ManyWhereJoins getManyWhereJoins(); + + /** + * Convert this natural key query into a find by id query. + */ + public void convertWhereNaturalKeyToId(Object idValue); + + /** + * Return a Natural Key bind parameter if supported by this query. + */ + public NaturalKeyBindParam getNaturalKeyBindParam(); + + /** + * Set the query to select the id property only. + */ + public void setSelectId(); + + /** + * Set a filter to a join path. + */ + public void setFilterMany(String prop, ExpressionList filterMany); + + /** + * Remove the query joins from query detail. + *

+ * These are registered with the Load Context. + *

+ */ + public List removeQueryJoins(); + + /** + * Remove the lazy joins from query detail. + *

+ * These are registered with the Load Context. + *

+ */ + public List removeLazyJoins(); + + /** + * Set the path of the many when +query/+lazy loading query is executed. + */ + public void setLazyLoadManyPath(String lazyLoadManyPath); + + /** + * Convert any many joins fetch joins to query joins. + */ + public void convertManyFetchJoinsToQueryJoins(boolean allowOne, int queryBatch); + + /** + * Return the TransactionContext. + *

+ * If no TransactionContext is present on the query then the + * TransactionContext from the Transaction is used (transaction scoped + * persistence context). + *

+ */ + public PersistenceContext getPersistenceContext(); + + /** + * Set an explicit TransactionContext (typically for a refresh query). + *

+ * If no TransactionContext is present on the query then the + * TransactionContext from the Transaction is used (transaction scoped + * persistence context). + *

+ */ + public void setPersistenceContext(PersistenceContext transactionContext); + + /** + * Return true if the query detail has neither select or joins specified. + */ + public boolean isDetailEmpty(); + + /** + * Return explicit autoFetch setting or null. If null then not explicitly + * set so we use the default behaviour. + */ + public Boolean isAutofetch(); + +// /** +// * Return explicit forUpdate setting or null. +// */ +// public boolean isForUpdate(); + + /** + * If return null then no autoFetch profiling for this query. If a + * AutoFetchManager is returned this implies that profiling is turned on for + * this query (and all the objects this query creates). + */ + public AutoFetchManager getAutoFetchManager(); + + /** + * This has the effect of turning on autoFetch profiling for this query. + */ + public void setAutoFetchManager(AutoFetchManager manager); + + /** + * Return the origin point for the query. + *

+ * This MUST be call prior to a query being changed via tuning. This is + * because the queryPlanHash is used to identify the query point. + *

+ */ + public ObjectGraphNode setOrigin(CallStack callStack); + + /** + * Set the profile point of the bean or collection that is lazy loading. + *

+ * This enables use to hook this back to the original 'root' query by the + * queryPlanHash and stackPoint. + *

+ */ + public void setParentNode(ObjectGraphNode node); + + /** + * Set the property that invoked the lazy load and MUST be included in the + * lazy loading query. + */ + public void setLazyLoadProperty(String lazyLoadProperty); + + /** + * Return the property that invoked lazy load. + */ + public String getLazyLoadProperty(); + + /** + * Return the lazy load path. + */ + public String getLazyLoadManyPath(); + + /** + * Used to hook back a lazy loading query to the original query (query + * point). + *

+ * This will return null or an "original" query. + *

+ */ + public ObjectGraphNode getParentNode(); + + /** + * Return false when this is a lazy load or refresh query for a bean. + *

+ * We just take/copy the data from those beans and don't collect autoFetch + * usage profiling on those lazy load or refresh beans. + *

+ */ + public boolean isUsageProfiling(); + + /** + * Set to false if this query should not be included in the autoFetch usage + * profiling information. + */ + public void setUsageProfiling(boolean usageProfiling); + + /** + * Return the query name. + */ + public String getName(); + + /** + * Calculate a hash used by AutoFetch to identify when a query has changed + * (and hence potentially needs a new tuned query plan to be developed). + *

+ * Excludes bind values and occurs prior to AutoFetch potentially + * tuning/modifying the query. + *

+ */ + public int queryAutofetchHash(); + + /** + * Identifies queries that are the same bar the bind variables. + *

+ * This is used AFTER AutoFetch has potentially tuned the query. This is + * used to identify and reused query plans (the final SQL string and + * associated SqlTree object). + *

+ *

+ * Excludes the actual bind values (as they don't effect the query plan). + *

+ */ + public int queryPlanHash(BeanQueryRequest request); + + /** + * Calculate a hash based on the bind values used in the query. + *

+ * Combined with queryPlanHash() to return getQueryHash (a unique hash for a + * query). + *

+ */ + public int queryBindHash(); + + /** + * Identifies queries that are exactly the same including bind variables. + */ + public int queryHash(); + + /** + * Return true if this is a query based on a SqlSelect rather than + * generated. + */ + public boolean isSqlSelect(); + + /** + * Return true if this is a RawSql query. + */ + public boolean isRawSql(); + + /** + * Return the Order By clause or null if there is none defined. + */ + public OrderBy getOrderBy(); + + /** + * Return additional where clause. This should be added to any where clause + * that was part of the original query. + */ + public String getAdditionalWhere(); + + /** + * Can return null if no expressions where added to the where clause. + */ + public SpiExpressionList getWhereExpressions(); + + /** + * Can return null if no expressions where added to the having clause. + */ + public SpiExpressionList getHavingExpressions(); + + /** + * Return additional having clause. Where raw String expressions are added + * to having clause rather than Expression objects. + */ + public String getAdditionalHaving(); + + /** + * Returns true if either firstRow or maxRows has been set. + */ + public boolean hasMaxRowsOrFirstRow(); + + /** + * Return true if this query should use/check the bean cache. + */ + public Boolean isUseBeanCache(); + + /** + * Return true if this query should use/check the query cache. + */ + public boolean isUseQueryCache(); + + /** + * Return true if the beans from this query should be loaded into the bean + * cache. + */ + public boolean isLoadBeanCache(); + + /** + * Return true if the beans returned by this query should be read only. + */ + public Boolean isReadOnly(); + + /** + * Adds this bean to the persistence context prior to executing the query. + */ + public void contextAdd(EntityBean bean); + + /** + * Return the type of beans queries. + */ + public Class getBeanType(); + + /** + * Return the query timeout. + */ + public int getTimeout(); + + /** + * Return the objects that should be added to the persistence context prior + * to executing the query. + */ + public ArrayList getContextAdditions(); + + /** + * Return the bind parameters. + */ + public BindParams getBindParams(); + + /** + * Get the orm query as a String. Only available if the query was built from + * a string. + */ + public String getQuery(); + + /** + * Replace the query detail. This is used by the autoFetch feature to as a + * fast way to set the query properties and joins. + *

+ * Note care must be taken to keep the where, orderBy, firstRows and maxRows + * held in the detail attributes. + *

+ */ + public void setDetail(OrmQueryDetail detail); + + /** + * Autofetch tune the detail specifying properties to select on already defined joins + * and adding extra joins where they are missing. + */ + public boolean tuneFetchProperties(OrmQueryDetail detail); + + /** + * Set to true if this query has been tuned by autoFetch. + */ + public void setAutoFetchTuned(boolean autoFetchTuned); + + /** + * Return the query detail. + */ + public OrmQueryDetail getDetail(); + + public TableJoin getIncludeTableJoin(); + + public void setIncludeTableJoin(TableJoin includeTableJoin); + + /** + * Return the property used to specify keys for a map. + */ + public String getMapKey(); + + /** + * Return the number of rows after which fetching should occur in a + * background thread. + */ + public int getBackgroundFetchAfter(); + + /** + * Return the maximum number of rows to return in the query. + */ + public int getMaxRows(); + + /** + * Return the index of the first row to return in the query. + */ + public int getFirstRow(); + + /** + * return true if this query uses DISTINCT. + */ + public boolean isDistinct(); + + /** + * Return true if this query should build and return vanilla objects. + */ + public boolean isVanillaMode(boolean serverDefaultVanillaMode); + + /** + * Set default select clauses where none have been explicitly defined. + */ + public void setDefaultSelectClause(); + + /** + * Return the where clause from a parsed string query. + */ + public String getRawWhereClause(); + + /** + * Return the Id value. + */ + public Object getId(); + + /** + * Return the queryListener. + */ + public QueryListener getListener(); + + /** + * Return true if this query should use its own transaction. + *

+ * This is true for background fetching and when using QueryListener. + *

+ */ + public boolean createOwnTransaction(); + + /** + * Set the generated sql for debug purposes. + * + * @param generatedSql + */ + public void setGeneratedSql(String generatedSql); + + /** + * Return the hint for Statement.setFetchSize(). + */ + public int getBufferFetchSizeHint(); + + /** + * Return true if this is a query executing in the background. + */ + public boolean isFutureFetch(); + + /** + * Set to true to indicate the query is executing in a background thread + * asynchronously. + */ + public void setFutureFetch(boolean futureFetch); + + /** + * Set the underlying cancelable query (with the PreparedStatement). + */ + public void setCancelableQuery(CancelableQuery cancelableQuery); + + /** + * Return true if this query has been cancelled. + */ + public boolean isCancelled(); +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java b/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java index d6e89f668..372789244 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiTransaction.java @@ -1,203 +1,184 @@ -/** - * 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.api; - -import java.sql.Connection; -import java.util.List; - -import com.avaje.ebean.Transaction; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.server.persist.BatchControl; -import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer; - -/** - * Extends Transaction with additional API required on server. - *

- * Provides support for batching and TransactionContext. - *

- */ -public interface SpiTransaction extends Transaction { - - /** - * Return true if generated SQL and Bind values should be logged to the - * transaction log. - */ - public boolean isLogSql(); - - /** - * Return true if summary level events should be logged to the transaction - * log. - */ - public boolean isLogSummary(); - - /** - * Log a comment to the transaction log for Ebean INTERNAL use. There should - * always be an external LogLevel check prior to calling this method. - */ - public void logInternal(String msg); - - /** - * Return the buffer containing transaction log messages. - */ - public TransactionLogBuffer getLogBuffer(); - - /** - * Register a "Derived Relationship" (that requires an additional update). - */ - public void registerDerivedRelationship(DerivedRelationshipData assocBean); - - /** - * Return the list of "Derived Relationships" that must be maintained after - * insert. - */ - public List getDerivedRelationship(Object bean); - - /** - * Add a deleting bean to the registered list. - *

- * This is to handle bi-directional relationships where both sides Cascade. - *

- */ - public void registerDeleteBean(Integer hash); - - /** - * Unregister the hash of the bean. - */ - public void unregisterDeleteBean(Integer hash); - - /** - * Return true if this is a bean that has already been saved/deleted. - */ - public boolean isRegisteredDeleteBean(Integer hash); - - /** - * Unregister the persisted bean. - */ - public void unregisterBean(Object bean); - - /** - * Return true if this is a bean that has already been persisted in the - * current recursive save request. The goal is to stop recursively saving - * the bean when cascade persist is on both sides of a relationship). - *

- * This will register the bean if it is not already. - *

- */ - public boolean isRegisteredBean(Object bean); - - /** - * Returns a String used to identify the transaction. This id is used for - * Transaction logging. - */ - public String getId(); - - /** - * Return the batchSize specifically set for this transaction or 0. - *

- * Returning 0 implies to use the system wide default batch size. - *

- */ - public int getBatchSize(); - - /** - * Modify and return the current 'depth' of the transaction. - *

- * As we cascade save or delete we traverse the object graph tree. Going up - * to Assoc Ones the depth decreases and going down to Assoc Manys the depth - * increases. - *

- *

- * The depth is used for ordering batching statements. The lowest depth get - * executed first during save. - *

- */ - public int depth(int diff); - - /** - * Return true if this transaction was created explicitly via - * Ebean.beginTransaction(). - */ - public boolean isExplicit(); - - /** - * Get the object that holds the event details. - *

- * This information is used maintain the table state, cache and text - * indexes. On commit the Table modifications this generates is broadcast - * around the cluster (if you have a cluster). - *

- */ - public TransactionEvent getEvent(); - - /** - * Whether persistCascade is on for save and delete. - */ - public boolean isPersistCascade(); - - /** - * Return true if this request should be batched. Conversely returns false - * if this request should be executed immediately. - */ - public boolean isBatchThisRequest(); - - /** - * Return the queue used to batch up persist requests. - */ - public BatchControl getBatchControl(); - - /** - * Set the queue used to batch up persist requests. There should only be one - * PersistQueue set per transaction. - */ - public void setBatchControl(BatchControl control); - - /** - * Return the persistence context associated with this transaction. - *

- * You may wish to hold onto this and set it against another transaction - * later. This is along the lines of 'extended persistence context' - * behaviour. - *

- */ - public PersistenceContext getPersistenceContext(); - - /** - * Set the persistence context to this transaction. - *

- * This could be considered similar to 'EJB3 Extended Persistence Context'. - * In that you can get the PersistenceContext from a transaction, hold onto - * it, and then set it back later to a second transaction. In general there - * is one PersistenceContext per Transaction. The getPersistenceContext() - * and setPersistenceContext() enable a developer to reuse a single - * PersistenceContext with multiple transactions. - *

- */ - public void setPersistenceContext(PersistenceContext context); - - /** - * Return the underlying Connection for internal use. - *

- * If the connection is made public from Transaction and the user code calls - * that method we can no longer trust the query only status of a - * Transaction. - *

- */ - public Connection getInternalConnection(); -} +package com.avaje.ebeaninternal.api; + +import java.sql.Connection; +import java.util.List; + +import com.avaje.ebean.Transaction; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebeaninternal.server.persist.BatchControl; +import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer; + +/** + * Extends Transaction with additional API required on server. + *

+ * Provides support for batching and TransactionContext. + *

+ */ +public interface SpiTransaction extends Transaction { + + /** + * Return true if generated SQL and Bind values should be logged to the + * transaction log. + */ + public boolean isLogSql(); + + /** + * Return true if summary level events should be logged to the transaction + * log. + */ + public boolean isLogSummary(); + + /** + * Log a comment to the transaction log for Ebean INTERNAL use. There should + * always be an external LogLevel check prior to calling this method. + */ + public void logInternal(String msg); + + /** + * Return the buffer containing transaction log messages. + */ + public TransactionLogBuffer getLogBuffer(); + + /** + * Register a "Derived Relationship" (that requires an additional update). + */ + public void registerDerivedRelationship(DerivedRelationshipData assocBean); + + /** + * Return the list of "Derived Relationships" that must be maintained after + * insert. + */ + public List getDerivedRelationship(Object bean); + + /** + * Add a deleting bean to the registered list. + *

+ * This is to handle bi-directional relationships where both sides Cascade. + *

+ */ + public void registerDeleteBean(Integer hash); + + /** + * Unregister the hash of the bean. + */ + public void unregisterDeleteBean(Integer hash); + + /** + * Return true if this is a bean that has already been saved/deleted. + */ + public boolean isRegisteredDeleteBean(Integer hash); + + /** + * Unregister the persisted bean. + */ + public void unregisterBean(Object bean); + + /** + * Return true if this is a bean that has already been persisted in the + * current recursive save request. The goal is to stop recursively saving + * the bean when cascade persist is on both sides of a relationship). + *

+ * This will register the bean if it is not already. + *

+ */ + public boolean isRegisteredBean(Object bean); + + /** + * Returns a String used to identify the transaction. This id is used for + * Transaction logging. + */ + public String getId(); + + /** + * Return the batchSize specifically set for this transaction or 0. + *

+ * Returning 0 implies to use the system wide default batch size. + *

+ */ + public int getBatchSize(); + + /** + * Modify and return the current 'depth' of the transaction. + *

+ * As we cascade save or delete we traverse the object graph tree. Going up + * to Assoc Ones the depth decreases and going down to Assoc Manys the depth + * increases. + *

+ *

+ * The depth is used for ordering batching statements. The lowest depth get + * executed first during save. + *

+ */ + public int depth(int diff); + + /** + * Return true if this transaction was created explicitly via + * Ebean.beginTransaction(). + */ + public boolean isExplicit(); + + /** + * Get the object that holds the event details. + *

+ * This information is used maintain the table state, cache and text + * indexes. On commit the Table modifications this generates is broadcast + * around the cluster (if you have a cluster). + *

+ */ + public TransactionEvent getEvent(); + + /** + * Whether persistCascade is on for save and delete. + */ + public boolean isPersistCascade(); + + /** + * Return true if this request should be batched. Conversely returns false + * if this request should be executed immediately. + */ + public boolean isBatchThisRequest(); + + /** + * Return the queue used to batch up persist requests. + */ + public BatchControl getBatchControl(); + + /** + * Set the queue used to batch up persist requests. There should only be one + * PersistQueue set per transaction. + */ + public void setBatchControl(BatchControl control); + + /** + * Return the persistence context associated with this transaction. + *

+ * You may wish to hold onto this and set it against another transaction + * later. This is along the lines of 'extended persistence context' + * behaviour. + *

+ */ + public PersistenceContext getPersistenceContext(); + + /** + * Set the persistence context to this transaction. + *

+ * This could be considered similar to 'EJB3 Extended Persistence Context'. + * In that you can get the PersistenceContext from a transaction, hold onto + * it, and then set it back later to a second transaction. In general there + * is one PersistenceContext per Transaction. The getPersistenceContext() + * and setPersistenceContext() enable a developer to reuse a single + * PersistenceContext with multiple transactions. + *

+ */ + public void setPersistenceContext(PersistenceContext context); + + /** + * Return the underlying Connection for internal use. + *

+ * If the connection is made public from Transaction and the user code calls + * that method we can no longer trust the query only status of a + * Transaction. + *

+ */ + public Connection getInternalConnection(); +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiUpdatePlan.java b/src/main/java/com/avaje/ebeaninternal/api/SpiUpdatePlan.java index 9f24cc86c..47f1eeede 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiUpdatePlan.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiUpdatePlan.java @@ -1,94 +1,75 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.api; - -import java.sql.SQLException; -import java.util.Set; - -import com.avaje.ebeaninternal.server.core.ConcurrencyMode; -import com.avaje.ebeaninternal.server.persist.dml.DmlHandler; -import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable; - -/** - * A plan for executing bean updates for a given set of changed properties. - *

- * This is a cachable plan with the purpose of being being able to skip some - * phases of the update bean processing. - *

- *

- * The plans are cached by the BeanDescriptors. - * - * - * @author rbygrave - */ -public interface SpiUpdatePlan { - - /** - * Return true if the set clause has no columns. - *

- * Can occur when the only columns updated have a updatable=false in their - * deployment. - *

- */ - public boolean isEmptySetClause(); - - /** - * Bind given the request and bean. The bean could be the oldValues bean - * when binding a update or delete where clause with ALL concurrency mode. - */ - public void bindSet(DmlHandler bind, Object bean) throws SQLException; - - /** - * Return the time this plan was created. - */ - public long getTimeCreated(); - - /** - * Return the time this plan was last used. - */ - public Long getTimeLastUsed(); - - /** - * Return the hash key for this plan. - */ - public Integer getKey(); - - /** - * Return the concurrency mode for this plan. - */ - public ConcurrencyMode getMode(); - - /** - * Return the update SQL statement. - */ - public String getSql(); - - /** - * Return the set of bindable update properties. - */ - public Bindable getSet(); - - /** - * Return the properties that where changed and should be included in the - * update statement. - */ - public Set getProperties(); - +package com.avaje.ebeaninternal.api; + +import java.sql.SQLException; +import java.util.Set; + +import com.avaje.ebeaninternal.server.core.ConcurrencyMode; +import com.avaje.ebeaninternal.server.persist.dml.DmlHandler; +import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable; + +/** + * A plan for executing bean updates for a given set of changed properties. + *

+ * This is a cachable plan with the purpose of being being able to skip some + * phases of the update bean processing. + *

+ *

+ * The plans are cached by the BeanDescriptors. + * + * + * @author rbygrave + */ +public interface SpiUpdatePlan { + + /** + * Return true if the set clause has no columns. + *

+ * Can occur when the only columns updated have a updatable=false in their + * deployment. + *

+ */ + public boolean isEmptySetClause(); + + /** + * Bind given the request and bean. The bean could be the oldValues bean + * when binding a update or delete where clause with ALL concurrency mode. + */ + public void bindSet(DmlHandler bind, Object bean) throws SQLException; + + /** + * Return the time this plan was created. + */ + public long getTimeCreated(); + + /** + * Return the time this plan was last used. + */ + public Long getTimeLastUsed(); + + /** + * Return the hash key for this plan. + */ + public Integer getKey(); + + /** + * Return the concurrency mode for this plan. + */ + public ConcurrencyMode getMode(); + + /** + * Return the update SQL statement. + */ + public String getSql(); + + /** + * Return the set of bindable update properties. + */ + public Bindable getSet(); + + /** + * Return the properties that where changed and should be included in the + * update statement. + */ + public Set getProperties(); + } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/api/TransactionEvent.java b/src/main/java/com/avaje/ebeaninternal/api/TransactionEvent.java index 8fc24c37a..9423d65bf 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/TransactionEvent.java +++ b/src/main/java/com/avaje/ebeaninternal/api/TransactionEvent.java @@ -1,221 +1,202 @@ -/** - * 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.api; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Logger; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.transaction.BeanDelta; -import com.avaje.ebeaninternal.server.transaction.DeleteByIdMap; -import com.avaje.ebeaninternal.server.transaction.IndexInvalidate; - -/** - * Holds information for a transaction. There is one TransactionEvent instance - * per Transaction instance. - *

- * When the associated Transaction commits or rollback this information is sent - * to the TransactionEventManager. - *

- */ -public class TransactionEvent implements Serializable { - - private static final Logger logger = Logger.getLogger(TransactionEvent.class.getName()); - - private static final long serialVersionUID = 7230903304106097120L; - - /** - * Flag indicating this is a local transaction (not from another server in - * the cluster). - */ - private transient boolean local; - - private boolean invalidateAll; - - private TransactionEventTable eventTables; - - private transient TransactionEventBeans eventBeans; - - private transient List beanDeltas; - - private transient DeleteByIdMap deleteByIdMap; - - private transient Set indexInvalidations; - - private transient Set pauseIndexInvalidate; - - /** - * Create the TransactionEvent, one per Transaction. - */ - public TransactionEvent() { - this.local = true; - } - - /** - * Set this to true to invalidate all table dependent cached objects. - */ - public void setInvalidateAll(boolean isInvalidateAll) { - this.invalidateAll = isInvalidateAll; - } - - /** - * Return true if all table states should be invalidated. This will cause - * all cached objects to be invalidated. - */ - public boolean isInvalidateAll() { - return invalidateAll; - } - - /** - * Temporarily pause/ignore any index invalidation for this bean type. - */ - public void pauseIndexInvalidate(Class beanType) { - if (pauseIndexInvalidate == null){ - pauseIndexInvalidate = new HashSet(); - } - pauseIndexInvalidate.add(beanType.getName()); - } - - /** - * Resume listening for index invalidation for this bean type. - */ - public void resumeIndexInvalidate(Class beanType) { - if (pauseIndexInvalidate != null){ - pauseIndexInvalidate.remove(beanType.getName()); - } - } - - /** - * Add an IndexInvalidation notices to the transaction. - */ - public void addIndexInvalidate(IndexInvalidate indexEvent){ - if (pauseIndexInvalidate != null && pauseIndexInvalidate.contains(indexEvent.getIndexName())){ - logger.fine("--- IGNORE Invalidate on "+indexEvent.getIndexName()); - return; - } - if (indexInvalidations == null){ - indexInvalidations = new HashSet(); - } - indexInvalidations.add(indexEvent); - } - - public void addDeleteById(BeanDescriptor desc, Object id){ - if (deleteByIdMap == null){ - deleteByIdMap = new DeleteByIdMap(); - } - deleteByIdMap.add(desc, id); - } - - public void addDeleteByIdList(BeanDescriptor desc, List idList) { - if (deleteByIdMap == null) { - deleteByIdMap = new DeleteByIdMap(); - } - deleteByIdMap.addList(desc, idList); - } - - public DeleteByIdMap getDeleteByIdMap() { - return deleteByIdMap; - } - - public void addBeanDelta(BeanDelta delta) { - if (beanDeltas == null) { - beanDeltas = new ArrayList(); - } - beanDeltas.add(delta); - } - - public List getBeanDeltas() { - return beanDeltas; - } - - /** - * Return true if this was a local transaction. Returns false if this - * transaction originated on another server in the cluster. - */ - public boolean isLocal() { - return local; - } - - /** - * For BeanListeners the requests they are interested in. - */ - public TransactionEventBeans getEventBeans() { - return eventBeans; - } - - public TransactionEventTable getEventTables() { - return eventTables; - } - - public Set getIndexInvalidations() { - return indexInvalidations; - } - - public void add(String tableName, boolean inserts, boolean updates, boolean deletes){ - if (eventTables == null){ - eventTables = new TransactionEventTable(); - } - eventTables.add(tableName, inserts, updates, deletes); - } - - public void add(TransactionEventTable table){ - if (eventTables == null){ - eventTables = new TransactionEventTable(); - } - eventTables.add(table); - } - - /** - * Add a inserted updated or deleted bean to the event. - */ - public void add(PersistRequestBean request) { - - if (request.isNotify(this)){ - // either a BeanListener or Cache is interested - if (eventBeans == null) { - eventBeans = new TransactionEventBeans(); - } - eventBeans.add(request); - } - } - - /** - * Notify the cache of bean changes. - *

- * This returns the TransactionEventTable so that if any - * general table changes can also be used to invalidate - * parts of the cache. - *

- */ - public void notifyCache(){ - if (eventBeans != null){ - eventBeans.notifyCache(); - } - if (deleteByIdMap != null) { - deleteByIdMap.notifyCache(); - } - } - -} +package com.avaje.ebeaninternal.api; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Logger; + +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.transaction.BeanDelta; +import com.avaje.ebeaninternal.server.transaction.DeleteByIdMap; +import com.avaje.ebeaninternal.server.transaction.IndexInvalidate; + +/** + * Holds information for a transaction. There is one TransactionEvent instance + * per Transaction instance. + *

+ * When the associated Transaction commits or rollback this information is sent + * to the TransactionEventManager. + *

+ */ +public class TransactionEvent implements Serializable { + + private static final Logger logger = Logger.getLogger(TransactionEvent.class.getName()); + + private static final long serialVersionUID = 7230903304106097120L; + + /** + * Flag indicating this is a local transaction (not from another server in + * the cluster). + */ + private transient boolean local; + + private boolean invalidateAll; + + private TransactionEventTable eventTables; + + private transient TransactionEventBeans eventBeans; + + private transient List beanDeltas; + + private transient DeleteByIdMap deleteByIdMap; + + private transient Set indexInvalidations; + + private transient Set pauseIndexInvalidate; + + /** + * Create the TransactionEvent, one per Transaction. + */ + public TransactionEvent() { + this.local = true; + } + + /** + * Set this to true to invalidate all table dependent cached objects. + */ + public void setInvalidateAll(boolean isInvalidateAll) { + this.invalidateAll = isInvalidateAll; + } + + /** + * Return true if all table states should be invalidated. This will cause + * all cached objects to be invalidated. + */ + public boolean isInvalidateAll() { + return invalidateAll; + } + + /** + * Temporarily pause/ignore any index invalidation for this bean type. + */ + public void pauseIndexInvalidate(Class beanType) { + if (pauseIndexInvalidate == null){ + pauseIndexInvalidate = new HashSet(); + } + pauseIndexInvalidate.add(beanType.getName()); + } + + /** + * Resume listening for index invalidation for this bean type. + */ + public void resumeIndexInvalidate(Class beanType) { + if (pauseIndexInvalidate != null){ + pauseIndexInvalidate.remove(beanType.getName()); + } + } + + /** + * Add an IndexInvalidation notices to the transaction. + */ + public void addIndexInvalidate(IndexInvalidate indexEvent){ + if (pauseIndexInvalidate != null && pauseIndexInvalidate.contains(indexEvent.getIndexName())){ + logger.fine("--- IGNORE Invalidate on "+indexEvent.getIndexName()); + return; + } + if (indexInvalidations == null){ + indexInvalidations = new HashSet(); + } + indexInvalidations.add(indexEvent); + } + + public void addDeleteById(BeanDescriptor desc, Object id){ + if (deleteByIdMap == null){ + deleteByIdMap = new DeleteByIdMap(); + } + deleteByIdMap.add(desc, id); + } + + public void addDeleteByIdList(BeanDescriptor desc, List idList) { + if (deleteByIdMap == null) { + deleteByIdMap = new DeleteByIdMap(); + } + deleteByIdMap.addList(desc, idList); + } + + public DeleteByIdMap getDeleteByIdMap() { + return deleteByIdMap; + } + + public void addBeanDelta(BeanDelta delta) { + if (beanDeltas == null) { + beanDeltas = new ArrayList(); + } + beanDeltas.add(delta); + } + + public List getBeanDeltas() { + return beanDeltas; + } + + /** + * Return true if this was a local transaction. Returns false if this + * transaction originated on another server in the cluster. + */ + public boolean isLocal() { + return local; + } + + /** + * For BeanListeners the requests they are interested in. + */ + public TransactionEventBeans getEventBeans() { + return eventBeans; + } + + public TransactionEventTable getEventTables() { + return eventTables; + } + + public Set getIndexInvalidations() { + return indexInvalidations; + } + + public void add(String tableName, boolean inserts, boolean updates, boolean deletes){ + if (eventTables == null){ + eventTables = new TransactionEventTable(); + } + eventTables.add(tableName, inserts, updates, deletes); + } + + public void add(TransactionEventTable table){ + if (eventTables == null){ + eventTables = new TransactionEventTable(); + } + eventTables.add(table); + } + + /** + * Add a inserted updated or deleted bean to the event. + */ + public void add(PersistRequestBean request) { + + if (request.isNotify(this)){ + // either a BeanListener or Cache is interested + if (eventBeans == null) { + eventBeans = new TransactionEventBeans(); + } + eventBeans.add(request); + } + } + + /** + * Notify the cache of bean changes. + *

+ * This returns the TransactionEventTable so that if any + * general table changes can also be used to invalidate + * parts of the cache. + *

+ */ + public void notifyCache(){ + if (eventBeans != null){ + eventBeans.notifyCache(); + } + if (deleteByIdMap != null) { + deleteByIdMap.notifyCache(); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/TransactionEventBeans.java b/src/main/java/com/avaje/ebeaninternal/api/TransactionEventBeans.java index 2c77545cb..9eb427cc0 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/TransactionEventBeans.java +++ b/src/main/java/com/avaje/ebeaninternal/api/TransactionEventBeans.java @@ -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.api; - -import java.util.ArrayList; -import java.util.List; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; - -/** - * Lists of inserted updated and deleted beans that have a BeanPersistListener. - *

- * These beans will be sent to the appropriate BeanListeners after a successful - * commit of the transaction. - *

- */ -public class TransactionEventBeans { - - ArrayList> requests = new ArrayList>(); - - /** - * Return the list of PersistRequests that BeanListeners are interested in. - */ - public List> getRequests() { - return requests; - } - - /** - * Add a bean for BeanListener notification. - */ - public void add(PersistRequestBean request) { - - requests.add(request); - } - - public void notifyCache() { - for (int i = 0; i < requests.size(); i++) { - requests.get(i).notifyCache(); - } - } - -} +package com.avaje.ebeaninternal.api; + +import java.util.ArrayList; +import java.util.List; + +import com.avaje.ebeaninternal.server.core.PersistRequestBean; + +/** + * Lists of inserted updated and deleted beans that have a BeanPersistListener. + *

+ * These beans will be sent to the appropriate BeanListeners after a successful + * commit of the transaction. + *

+ */ +public class TransactionEventBeans { + + ArrayList> requests = new ArrayList>(); + + /** + * Return the list of PersistRequests that BeanListeners are interested in. + */ + public List> getRequests() { + return requests; + } + + /** + * Add a bean for BeanListener notification. + */ + public void add(PersistRequestBean request) { + + requests.add(request); + } + + public void notifyCache() { + for (int i = 0; i < requests.size(); i++) { + requests.get(i).notifyCache(); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/api/package-info.java b/src/main/java/com/avaje/ebeaninternal/api/package-info.java index 7ecafc143..de61459f3 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/package-info.java +++ b/src/main/java/com/avaje/ebeaninternal/api/package-info.java @@ -1,4 +1 @@ -/** - * Internal service API. - */ package com.avaje.ebeaninternal.api; \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/jdbc/ConnectionDelegator.java b/src/main/java/com/avaje/ebeaninternal/jdbc/ConnectionDelegator.java index 51c6e47e0..6fd318532 100644 --- a/src/main/java/com/avaje/ebeaninternal/jdbc/ConnectionDelegator.java +++ b/src/main/java/com/avaje/ebeaninternal/jdbc/ConnectionDelegator.java @@ -1,340 +1,323 @@ -/** - * 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.jdbc; - -import java.sql.Array; -import java.sql.Blob; -import java.sql.CallableStatement; -import java.sql.Clob; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.NClob; -import java.sql.PreparedStatement; -import java.sql.SQLClientInfoException; -import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.SQLXML; -import java.sql.Savepoint; -import java.sql.Statement; -import java.sql.Struct; -import java.util.Map; -import java.util.Properties; - -public class ConnectionDelegator implements Connection -{ - private final Connection delegate; - - public ConnectionDelegator(Connection delegate) - { - this.delegate = delegate; - } - - public Statement createStatement() - throws SQLException - { - return delegate.createStatement(); - } - - public PreparedStatement prepareStatement(String sql) - throws SQLException - { - return delegate.prepareStatement(sql); - } - - public CallableStatement prepareCall(String sql) - throws SQLException - { - return delegate.prepareCall(sql); - } - - public String nativeSQL(String sql) - throws SQLException - { - return delegate.nativeSQL(sql); - } - - public void setAutoCommit(boolean autoCommit) - throws SQLException - { - delegate.setAutoCommit(autoCommit); - } - - public boolean getAutoCommit() - throws SQLException - { - return delegate.getAutoCommit(); - } - - public void commit() - throws SQLException - { - delegate.commit(); - } - - public void rollback() - throws SQLException - { - delegate.rollback(); - } - - public void close() - throws SQLException - { - delegate.close(); - } - - public boolean isClosed() - throws SQLException - { - return delegate.isClosed(); - } - - public DatabaseMetaData getMetaData() - throws SQLException - { - return delegate.getMetaData(); - } - - public void setReadOnly(boolean readOnly) - throws SQLException - { - delegate.setReadOnly(readOnly); - } - - public boolean isReadOnly() - throws SQLException - { - return delegate.isReadOnly(); - } - - public void setCatalog(String catalog) - throws SQLException - { - delegate.setCatalog(catalog); - } - - public String getCatalog() - throws SQLException - { - return delegate.getCatalog(); - } - - public void setTransactionIsolation(int level) - throws SQLException - { - delegate.setTransactionIsolation(level); - } - - public int getTransactionIsolation() - throws SQLException - { - return delegate.getTransactionIsolation(); - } - - public SQLWarning getWarnings() - throws SQLException - { - return delegate.getWarnings(); - } - - public void clearWarnings() - throws SQLException - { - delegate.clearWarnings(); - } - - public Statement createStatement(int resultSetType, int resultSetConcurrency) - throws SQLException - { - return delegate.createStatement(resultSetType, resultSetConcurrency); - } - - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) - throws SQLException - { - return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency); - } - - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) - throws SQLException - { - return delegate.prepareCall(sql, resultSetType, resultSetConcurrency); - } - - public Map> getTypeMap() - throws SQLException - { - return delegate.getTypeMap(); - } - - public void setTypeMap(Map> map) - throws SQLException - { - delegate.setTypeMap(map); - } - - public void setHoldability(int holdability) - throws SQLException - { - delegate.setHoldability(holdability); - } - - public int getHoldability() - throws SQLException - { - return delegate.getHoldability(); - } - - public Savepoint setSavepoint() - throws SQLException - { - return delegate.setSavepoint(); - } - - public Savepoint setSavepoint(String name) - throws SQLException - { - return delegate.setSavepoint(name); - } - - public void rollback(Savepoint savepoint) - throws SQLException - { - delegate.rollback(savepoint); - } - - public void releaseSavepoint(Savepoint savepoint) - throws SQLException - { - delegate.releaseSavepoint(savepoint); - } - - public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) - throws SQLException - { - return delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); - } - - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) - throws SQLException - { - return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); - } - - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) - throws SQLException - { - return delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); - } - - public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) - throws SQLException - { - return delegate.prepareStatement(sql, autoGeneratedKeys); - } - - public PreparedStatement prepareStatement(String sql, int[] columnIndexes) - throws SQLException - { - return delegate.prepareStatement(sql, columnIndexes); - } - - public PreparedStatement prepareStatement(String sql, String[] columnNames) - throws SQLException - { - return delegate.prepareStatement(sql, columnNames); - } - - public Clob createClob() - throws SQLException - { - return delegate.createClob(); - } - - public Blob createBlob() - throws SQLException - { - return delegate.createBlob(); - } - - public NClob createNClob() - throws SQLException - { - return delegate.createNClob(); - } - - public SQLXML createSQLXML() - throws SQLException - { - return delegate.createSQLXML(); - } - - public boolean isValid(int timeout) - throws SQLException - { - return delegate.isValid(timeout); - } - - public void setClientInfo(String name, String value) - throws SQLClientInfoException - { - delegate.setClientInfo(name, value); - } - - public void setClientInfo(Properties properties) - throws SQLClientInfoException - { - delegate.setClientInfo(properties); - } - - public String getClientInfo(String name) - throws SQLException - { - return delegate.getClientInfo(name); - } - - public Properties getClientInfo() - throws SQLException - { - return delegate.getClientInfo(); - } - - public Array createArrayOf(String typeName, Object[] elements) - throws SQLException - { - return delegate.createArrayOf(typeName, elements); - } - - public Struct createStruct(String typeName, Object[] attributes) - throws SQLException - { - return delegate.createStruct(typeName, attributes); - } - - public T unwrap(Class iface) - throws SQLException - { - return delegate.unwrap(iface); - } - - public boolean isWrapperFor(Class iface) - throws SQLException - { - return delegate.isWrapperFor(iface); - } -} +package com.avaje.ebeaninternal.jdbc; + +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.Statement; +import java.sql.Struct; +import java.util.Map; +import java.util.Properties; + +public class ConnectionDelegator implements Connection +{ + private final Connection delegate; + + public ConnectionDelegator(Connection delegate) + { + this.delegate = delegate; + } + + public Statement createStatement() + throws SQLException + { + return delegate.createStatement(); + } + + public PreparedStatement prepareStatement(String sql) + throws SQLException + { + return delegate.prepareStatement(sql); + } + + public CallableStatement prepareCall(String sql) + throws SQLException + { + return delegate.prepareCall(sql); + } + + public String nativeSQL(String sql) + throws SQLException + { + return delegate.nativeSQL(sql); + } + + public void setAutoCommit(boolean autoCommit) + throws SQLException + { + delegate.setAutoCommit(autoCommit); + } + + public boolean getAutoCommit() + throws SQLException + { + return delegate.getAutoCommit(); + } + + public void commit() + throws SQLException + { + delegate.commit(); + } + + public void rollback() + throws SQLException + { + delegate.rollback(); + } + + public void close() + throws SQLException + { + delegate.close(); + } + + public boolean isClosed() + throws SQLException + { + return delegate.isClosed(); + } + + public DatabaseMetaData getMetaData() + throws SQLException + { + return delegate.getMetaData(); + } + + public void setReadOnly(boolean readOnly) + throws SQLException + { + delegate.setReadOnly(readOnly); + } + + public boolean isReadOnly() + throws SQLException + { + return delegate.isReadOnly(); + } + + public void setCatalog(String catalog) + throws SQLException + { + delegate.setCatalog(catalog); + } + + public String getCatalog() + throws SQLException + { + return delegate.getCatalog(); + } + + public void setTransactionIsolation(int level) + throws SQLException + { + delegate.setTransactionIsolation(level); + } + + public int getTransactionIsolation() + throws SQLException + { + return delegate.getTransactionIsolation(); + } + + public SQLWarning getWarnings() + throws SQLException + { + return delegate.getWarnings(); + } + + public void clearWarnings() + throws SQLException + { + delegate.clearWarnings(); + } + + public Statement createStatement(int resultSetType, int resultSetConcurrency) + throws SQLException + { + return delegate.createStatement(resultSetType, resultSetConcurrency); + } + + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) + throws SQLException + { + return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency); + } + + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) + throws SQLException + { + return delegate.prepareCall(sql, resultSetType, resultSetConcurrency); + } + + public Map> getTypeMap() + throws SQLException + { + return delegate.getTypeMap(); + } + + public void setTypeMap(Map> map) + throws SQLException + { + delegate.setTypeMap(map); + } + + public void setHoldability(int holdability) + throws SQLException + { + delegate.setHoldability(holdability); + } + + public int getHoldability() + throws SQLException + { + return delegate.getHoldability(); + } + + public Savepoint setSavepoint() + throws SQLException + { + return delegate.setSavepoint(); + } + + public Savepoint setSavepoint(String name) + throws SQLException + { + return delegate.setSavepoint(name); + } + + public void rollback(Savepoint savepoint) + throws SQLException + { + delegate.rollback(savepoint); + } + + public void releaseSavepoint(Savepoint savepoint) + throws SQLException + { + delegate.releaseSavepoint(savepoint); + } + + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException + { + return delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException + { + return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException + { + return delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) + throws SQLException + { + return delegate.prepareStatement(sql, autoGeneratedKeys); + } + + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) + throws SQLException + { + return delegate.prepareStatement(sql, columnIndexes); + } + + public PreparedStatement prepareStatement(String sql, String[] columnNames) + throws SQLException + { + return delegate.prepareStatement(sql, columnNames); + } + + public Clob createClob() + throws SQLException + { + return delegate.createClob(); + } + + public Blob createBlob() + throws SQLException + { + return delegate.createBlob(); + } + + public NClob createNClob() + throws SQLException + { + return delegate.createNClob(); + } + + public SQLXML createSQLXML() + throws SQLException + { + return delegate.createSQLXML(); + } + + public boolean isValid(int timeout) + throws SQLException + { + return delegate.isValid(timeout); + } + + public void setClientInfo(String name, String value) + throws SQLClientInfoException + { + delegate.setClientInfo(name, value); + } + + public void setClientInfo(Properties properties) + throws SQLClientInfoException + { + delegate.setClientInfo(properties); + } + + public String getClientInfo(String name) + throws SQLException + { + return delegate.getClientInfo(name); + } + + public Properties getClientInfo() + throws SQLException + { + return delegate.getClientInfo(); + } + + public Array createArrayOf(String typeName, Object[] elements) + throws SQLException + { + return delegate.createArrayOf(typeName, elements); + } + + public Struct createStruct(String typeName, Object[] attributes) + throws SQLException + { + return delegate.createStruct(typeName, attributes); + } + + public T unwrap(Class iface) + throws SQLException + { + return delegate.unwrap(iface); + } + + public boolean isWrapperFor(Class iface) + throws SQLException + { + return delegate.isWrapperFor(iface); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/jdbc/PreparedStatementDelegator.java b/src/main/java/com/avaje/ebeaninternal/jdbc/PreparedStatementDelegator.java index dba32b4d0..007868b7f 100644 --- a/src/main/java/com/avaje/ebeaninternal/jdbc/PreparedStatementDelegator.java +++ b/src/main/java/com/avaje/ebeaninternal/jdbc/PreparedStatementDelegator.java @@ -1,634 +1,617 @@ -/** - * 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.jdbc; - -import java.io.InputStream; -import java.io.Reader; -import java.math.BigDecimal; -import java.net.URL; -import java.sql.Array; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.Connection; -import java.sql.Date; -import java.sql.NClob; -import java.sql.ParameterMetaData; -import java.sql.PreparedStatement; -import java.sql.Ref; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.RowId; -import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.SQLXML; -import java.sql.Time; -import java.sql.Timestamp; -import java.util.Calendar; - -public class PreparedStatementDelegator implements PreparedStatement -{ - private final PreparedStatement delegate; - - public PreparedStatementDelegator(PreparedStatement delegate) - { - this.delegate = delegate; - } - - public ResultSet executeQuery() - throws SQLException - { - return delegate.executeQuery(); - } - - public int executeUpdate() - throws SQLException - { - return delegate.executeUpdate(); - } - - public void setNull(int parameterIndex, int sqlType) - throws SQLException - { - delegate.setNull(parameterIndex, sqlType); - } - - public void setBoolean(int parameterIndex, boolean x) - throws SQLException - { - delegate.setBoolean(parameterIndex, x); - } - - public void setByte(int parameterIndex, byte x) - throws SQLException - { - delegate.setByte(parameterIndex, x); - } - - public void setShort(int parameterIndex, short x) - throws SQLException - { - delegate.setShort(parameterIndex, x); - } - - public void setInt(int parameterIndex, int x) - throws SQLException - { - delegate.setInt(parameterIndex, x); - } - - public void setLong(int parameterIndex, long x) - throws SQLException - { - delegate.setLong(parameterIndex, x); - } - - public void setFloat(int parameterIndex, float x) - throws SQLException - { - delegate.setFloat(parameterIndex, x); - } - - public void setDouble(int parameterIndex, double x) - throws SQLException - { - delegate.setDouble(parameterIndex, x); - } - - public void setBigDecimal(int parameterIndex, BigDecimal x) - throws SQLException - { - delegate.setBigDecimal(parameterIndex, x); - } - - public void setString(int parameterIndex, String x) - throws SQLException - { - delegate.setString(parameterIndex, x); - } - - public void setBytes(int parameterIndex, byte[] x) - throws SQLException - { - delegate.setBytes(parameterIndex, x); - } - - public void setDate(int parameterIndex, Date x) - throws SQLException - { - delegate.setDate(parameterIndex, x); - } - - public void setTime(int parameterIndex, Time x) - throws SQLException - { - delegate.setTime(parameterIndex, x); - } - - public void setTimestamp(int parameterIndex, Timestamp x) - throws SQLException - { - delegate.setTimestamp(parameterIndex, x); - } - - public void setAsciiStream(int parameterIndex, InputStream x, int length) - throws SQLException - { - delegate.setAsciiStream(parameterIndex, x, length); - } - - @SuppressWarnings("deprecation") - public void setUnicodeStream(int parameterIndex, InputStream x, int length) - throws SQLException - { - delegate.setUnicodeStream(parameterIndex, x, length); - } - - public void setBinaryStream(int parameterIndex, InputStream x, int length) - throws SQLException - { - delegate.setBinaryStream(parameterIndex, x, length); - } - - public void clearParameters() - throws SQLException - { - delegate.clearParameters(); - } - - public void setObject(int parameterIndex, Object x, int targetSqlType) - throws SQLException - { - delegate.setObject(parameterIndex, x, targetSqlType); - } - - public void setObject(int parameterIndex, Object x) - throws SQLException - { - delegate.setObject(parameterIndex, x); - } - - public boolean execute() - throws SQLException - { - return delegate.execute(); - } - - public void addBatch() - throws SQLException - { - delegate.addBatch(); - } - - public void setCharacterStream(int parameterIndex, Reader reader, int length) - throws SQLException - { - delegate.setCharacterStream(parameterIndex, reader, length); - } - - public void setRef(int parameterIndex, Ref x) - throws SQLException - { - delegate.setRef(parameterIndex, x); - } - - public void setBlob(int parameterIndex, Blob x) - throws SQLException - { - delegate.setBlob(parameterIndex, x); - } - - public void setClob(int parameterIndex, Clob x) - throws SQLException - { - delegate.setClob(parameterIndex, x); - } - - public void setArray(int parameterIndex, Array x) - throws SQLException - { - delegate.setArray(parameterIndex, x); - } - - public ResultSetMetaData getMetaData() - throws SQLException - { - return delegate.getMetaData(); - } - - public void setDate(int parameterIndex, Date x, Calendar cal) - throws SQLException - { - delegate.setDate(parameterIndex, x, cal); - } - - public void setTime(int parameterIndex, Time x, Calendar cal) - throws SQLException - { - delegate.setTime(parameterIndex, x, cal); - } - - public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) - throws SQLException - { - delegate.setTimestamp(parameterIndex, x, cal); - } - - public void setNull(int parameterIndex, int sqlType, String typeName) - throws SQLException - { - delegate.setNull(parameterIndex, sqlType, typeName); - } - - public void setURL(int parameterIndex, URL x) - throws SQLException - { - delegate.setURL(parameterIndex, x); - } - - public ParameterMetaData getParameterMetaData() - throws SQLException - { - return delegate.getParameterMetaData(); - } - - public void setRowId(int parameterIndex, RowId x) - throws SQLException - { - delegate.setRowId(parameterIndex, x); - } - - public void setNString(int parameterIndex, String value) - throws SQLException - { - delegate.setNString(parameterIndex, value); - } - - public void setNCharacterStream(int parameterIndex, Reader value, long length) - throws SQLException - { - delegate.setNCharacterStream(parameterIndex, value, length); - } - - public void setNClob(int parameterIndex, NClob value) - throws SQLException - { - delegate.setNClob(parameterIndex, value); - } - - public void setClob(int parameterIndex, Reader reader, long length) - throws SQLException - { - delegate.setClob(parameterIndex, reader, length); - } - - public void setBlob(int parameterIndex, InputStream inputStream, long length) - throws SQLException - { - delegate.setBlob(parameterIndex, inputStream, length); - } - - public void setNClob(int parameterIndex, Reader reader, long length) - throws SQLException - { - delegate.setNClob(parameterIndex, reader, length); - } - - public void setSQLXML(int parameterIndex, SQLXML xmlObject) - throws SQLException - { - delegate.setSQLXML(parameterIndex, xmlObject); - } - - public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) - throws SQLException - { - delegate.setObject(parameterIndex, x, targetSqlType, scaleOrLength); - } - - public void setAsciiStream(int parameterIndex, InputStream x, long length) - throws SQLException - { - delegate.setAsciiStream(parameterIndex, x, length); - } - - public void setBinaryStream(int parameterIndex, InputStream x, long length) - throws SQLException - { - delegate.setBinaryStream(parameterIndex, x, length); - } - - public void setCharacterStream(int parameterIndex, Reader reader, long length) - throws SQLException - { - delegate.setCharacterStream(parameterIndex, reader, length); - } - - public void setAsciiStream(int parameterIndex, InputStream x) - throws SQLException - { - delegate.setAsciiStream(parameterIndex, x); - } - - public void setBinaryStream(int parameterIndex, InputStream x) - throws SQLException - { - delegate.setBinaryStream(parameterIndex, x); - } - - public void setCharacterStream(int parameterIndex, Reader reader) - throws SQLException - { - delegate.setCharacterStream(parameterIndex, reader); - } - - public void setNCharacterStream(int parameterIndex, Reader value) - throws SQLException - { - delegate.setNCharacterStream(parameterIndex, value); - } - - public void setClob(int parameterIndex, Reader reader) - throws SQLException - { - delegate.setClob(parameterIndex, reader); - } - - public void setBlob(int parameterIndex, InputStream inputStream) - throws SQLException - { - delegate.setBlob(parameterIndex, inputStream); - } - - public void setNClob(int parameterIndex, Reader reader) - throws SQLException - { - delegate.setNClob(parameterIndex, reader); - } - - public ResultSet executeQuery(String sql) - throws SQLException - { - return delegate.executeQuery(sql); - } - - public int executeUpdate(String sql) - throws SQLException - { - return delegate.executeUpdate(sql); - } - - public void close() - throws SQLException - { - delegate.close(); - } - - public int getMaxFieldSize() - throws SQLException - { - return delegate.getMaxFieldSize(); - } - - public void setMaxFieldSize(int max) - throws SQLException - { - delegate.setMaxFieldSize(max); - } - - public int getMaxRows() - throws SQLException - { - return delegate.getMaxRows(); - } - - public void setMaxRows(int max) - throws SQLException - { - delegate.setMaxRows(max); - } - - public void setEscapeProcessing(boolean enable) - throws SQLException - { - delegate.setEscapeProcessing(enable); - } - - public int getQueryTimeout() - throws SQLException - { - return delegate.getQueryTimeout(); - } - - public void setQueryTimeout(int seconds) - throws SQLException - { - delegate.setQueryTimeout(seconds); - } - - public void cancel() - throws SQLException - { - delegate.cancel(); - } - - public SQLWarning getWarnings() - throws SQLException - { - return delegate.getWarnings(); - } - - public void clearWarnings() - throws SQLException - { - delegate.clearWarnings(); - } - - public void setCursorName(String name) - throws SQLException - { - delegate.setCursorName(name); - } - - public boolean execute(String sql) - throws SQLException - { - return delegate.execute(sql); - } - - public ResultSet getResultSet() - throws SQLException - { - return delegate.getResultSet(); - } - - public int getUpdateCount() - throws SQLException - { - return delegate.getUpdateCount(); - } - - public boolean getMoreResults() - throws SQLException - { - return delegate.getMoreResults(); - } - - public void setFetchDirection(int direction) - throws SQLException - { - delegate.setFetchDirection(direction); - } - - public int getFetchDirection() - throws SQLException - { - return delegate.getFetchDirection(); - } - - public void setFetchSize(int rows) - throws SQLException - { - delegate.setFetchSize(rows); - } - - public int getFetchSize() - throws SQLException - { - return delegate.getFetchSize(); - } - - public int getResultSetConcurrency() - throws SQLException - { - return delegate.getResultSetConcurrency(); - } - - public int getResultSetType() - throws SQLException - { - return delegate.getResultSetType(); - } - - public void addBatch(String sql) - throws SQLException - { - delegate.addBatch(sql); - } - - public void clearBatch() - throws SQLException - { - delegate.clearBatch(); - } - - public int[] executeBatch() - throws SQLException - { - return delegate.executeBatch(); - } - - public Connection getConnection() - throws SQLException - { - return delegate.getConnection(); - } - - public boolean getMoreResults(int current) - throws SQLException - { - return delegate.getMoreResults(current); - } - - public ResultSet getGeneratedKeys() - throws SQLException - { - return delegate.getGeneratedKeys(); - } - - public int executeUpdate(String sql, int autoGeneratedKeys) - throws SQLException - { - return delegate.executeUpdate(sql, autoGeneratedKeys); - } - - public int executeUpdate(String sql, int[] columnIndexes) - throws SQLException - { - return delegate.executeUpdate(sql, columnIndexes); - } - - public int executeUpdate(String sql, String[] columnNames) - throws SQLException - { - return delegate.executeUpdate(sql, columnNames); - } - - public boolean execute(String sql, int autoGeneratedKeys) - throws SQLException - { - return delegate.execute(sql, autoGeneratedKeys); - } - - public boolean execute(String sql, int[] columnIndexes) - throws SQLException - { - return delegate.execute(sql, columnIndexes); - } - - public boolean execute(String sql, String[] columnNames) - throws SQLException - { - return delegate.execute(sql, columnNames); - } - - public int getResultSetHoldability() - throws SQLException - { - return delegate.getResultSetHoldability(); - } - - public boolean isClosed() - throws SQLException - { - return delegate.isClosed(); - } - - public void setPoolable(boolean poolable) - throws SQLException - { - delegate.setPoolable(poolable); - } - - public boolean isPoolable() - throws SQLException - { - return delegate.isPoolable(); - } - - public T unwrap(Class iface) - throws SQLException - { - return delegate.unwrap(iface); - } - - public boolean isWrapperFor(Class iface) - throws SQLException - { - return delegate.isWrapperFor(iface); - } -} +package com.avaje.ebeaninternal.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.Date; +import java.sql.NClob; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; + +public class PreparedStatementDelegator implements PreparedStatement +{ + private final PreparedStatement delegate; + + public PreparedStatementDelegator(PreparedStatement delegate) + { + this.delegate = delegate; + } + + public ResultSet executeQuery() + throws SQLException + { + return delegate.executeQuery(); + } + + public int executeUpdate() + throws SQLException + { + return delegate.executeUpdate(); + } + + public void setNull(int parameterIndex, int sqlType) + throws SQLException + { + delegate.setNull(parameterIndex, sqlType); + } + + public void setBoolean(int parameterIndex, boolean x) + throws SQLException + { + delegate.setBoolean(parameterIndex, x); + } + + public void setByte(int parameterIndex, byte x) + throws SQLException + { + delegate.setByte(parameterIndex, x); + } + + public void setShort(int parameterIndex, short x) + throws SQLException + { + delegate.setShort(parameterIndex, x); + } + + public void setInt(int parameterIndex, int x) + throws SQLException + { + delegate.setInt(parameterIndex, x); + } + + public void setLong(int parameterIndex, long x) + throws SQLException + { + delegate.setLong(parameterIndex, x); + } + + public void setFloat(int parameterIndex, float x) + throws SQLException + { + delegate.setFloat(parameterIndex, x); + } + + public void setDouble(int parameterIndex, double x) + throws SQLException + { + delegate.setDouble(parameterIndex, x); + } + + public void setBigDecimal(int parameterIndex, BigDecimal x) + throws SQLException + { + delegate.setBigDecimal(parameterIndex, x); + } + + public void setString(int parameterIndex, String x) + throws SQLException + { + delegate.setString(parameterIndex, x); + } + + public void setBytes(int parameterIndex, byte[] x) + throws SQLException + { + delegate.setBytes(parameterIndex, x); + } + + public void setDate(int parameterIndex, Date x) + throws SQLException + { + delegate.setDate(parameterIndex, x); + } + + public void setTime(int parameterIndex, Time x) + throws SQLException + { + delegate.setTime(parameterIndex, x); + } + + public void setTimestamp(int parameterIndex, Timestamp x) + throws SQLException + { + delegate.setTimestamp(parameterIndex, x); + } + + public void setAsciiStream(int parameterIndex, InputStream x, int length) + throws SQLException + { + delegate.setAsciiStream(parameterIndex, x, length); + } + + @SuppressWarnings("deprecation") + public void setUnicodeStream(int parameterIndex, InputStream x, int length) + throws SQLException + { + delegate.setUnicodeStream(parameterIndex, x, length); + } + + public void setBinaryStream(int parameterIndex, InputStream x, int length) + throws SQLException + { + delegate.setBinaryStream(parameterIndex, x, length); + } + + public void clearParameters() + throws SQLException + { + delegate.clearParameters(); + } + + public void setObject(int parameterIndex, Object x, int targetSqlType) + throws SQLException + { + delegate.setObject(parameterIndex, x, targetSqlType); + } + + public void setObject(int parameterIndex, Object x) + throws SQLException + { + delegate.setObject(parameterIndex, x); + } + + public boolean execute() + throws SQLException + { + return delegate.execute(); + } + + public void addBatch() + throws SQLException + { + delegate.addBatch(); + } + + public void setCharacterStream(int parameterIndex, Reader reader, int length) + throws SQLException + { + delegate.setCharacterStream(parameterIndex, reader, length); + } + + public void setRef(int parameterIndex, Ref x) + throws SQLException + { + delegate.setRef(parameterIndex, x); + } + + public void setBlob(int parameterIndex, Blob x) + throws SQLException + { + delegate.setBlob(parameterIndex, x); + } + + public void setClob(int parameterIndex, Clob x) + throws SQLException + { + delegate.setClob(parameterIndex, x); + } + + public void setArray(int parameterIndex, Array x) + throws SQLException + { + delegate.setArray(parameterIndex, x); + } + + public ResultSetMetaData getMetaData() + throws SQLException + { + return delegate.getMetaData(); + } + + public void setDate(int parameterIndex, Date x, Calendar cal) + throws SQLException + { + delegate.setDate(parameterIndex, x, cal); + } + + public void setTime(int parameterIndex, Time x, Calendar cal) + throws SQLException + { + delegate.setTime(parameterIndex, x, cal); + } + + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) + throws SQLException + { + delegate.setTimestamp(parameterIndex, x, cal); + } + + public void setNull(int parameterIndex, int sqlType, String typeName) + throws SQLException + { + delegate.setNull(parameterIndex, sqlType, typeName); + } + + public void setURL(int parameterIndex, URL x) + throws SQLException + { + delegate.setURL(parameterIndex, x); + } + + public ParameterMetaData getParameterMetaData() + throws SQLException + { + return delegate.getParameterMetaData(); + } + + public void setRowId(int parameterIndex, RowId x) + throws SQLException + { + delegate.setRowId(parameterIndex, x); + } + + public void setNString(int parameterIndex, String value) + throws SQLException + { + delegate.setNString(parameterIndex, value); + } + + public void setNCharacterStream(int parameterIndex, Reader value, long length) + throws SQLException + { + delegate.setNCharacterStream(parameterIndex, value, length); + } + + public void setNClob(int parameterIndex, NClob value) + throws SQLException + { + delegate.setNClob(parameterIndex, value); + } + + public void setClob(int parameterIndex, Reader reader, long length) + throws SQLException + { + delegate.setClob(parameterIndex, reader, length); + } + + public void setBlob(int parameterIndex, InputStream inputStream, long length) + throws SQLException + { + delegate.setBlob(parameterIndex, inputStream, length); + } + + public void setNClob(int parameterIndex, Reader reader, long length) + throws SQLException + { + delegate.setNClob(parameterIndex, reader, length); + } + + public void setSQLXML(int parameterIndex, SQLXML xmlObject) + throws SQLException + { + delegate.setSQLXML(parameterIndex, xmlObject); + } + + public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) + throws SQLException + { + delegate.setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } + + public void setAsciiStream(int parameterIndex, InputStream x, long length) + throws SQLException + { + delegate.setAsciiStream(parameterIndex, x, length); + } + + public void setBinaryStream(int parameterIndex, InputStream x, long length) + throws SQLException + { + delegate.setBinaryStream(parameterIndex, x, length); + } + + public void setCharacterStream(int parameterIndex, Reader reader, long length) + throws SQLException + { + delegate.setCharacterStream(parameterIndex, reader, length); + } + + public void setAsciiStream(int parameterIndex, InputStream x) + throws SQLException + { + delegate.setAsciiStream(parameterIndex, x); + } + + public void setBinaryStream(int parameterIndex, InputStream x) + throws SQLException + { + delegate.setBinaryStream(parameterIndex, x); + } + + public void setCharacterStream(int parameterIndex, Reader reader) + throws SQLException + { + delegate.setCharacterStream(parameterIndex, reader); + } + + public void setNCharacterStream(int parameterIndex, Reader value) + throws SQLException + { + delegate.setNCharacterStream(parameterIndex, value); + } + + public void setClob(int parameterIndex, Reader reader) + throws SQLException + { + delegate.setClob(parameterIndex, reader); + } + + public void setBlob(int parameterIndex, InputStream inputStream) + throws SQLException + { + delegate.setBlob(parameterIndex, inputStream); + } + + public void setNClob(int parameterIndex, Reader reader) + throws SQLException + { + delegate.setNClob(parameterIndex, reader); + } + + public ResultSet executeQuery(String sql) + throws SQLException + { + return delegate.executeQuery(sql); + } + + public int executeUpdate(String sql) + throws SQLException + { + return delegate.executeUpdate(sql); + } + + public void close() + throws SQLException + { + delegate.close(); + } + + public int getMaxFieldSize() + throws SQLException + { + return delegate.getMaxFieldSize(); + } + + public void setMaxFieldSize(int max) + throws SQLException + { + delegate.setMaxFieldSize(max); + } + + public int getMaxRows() + throws SQLException + { + return delegate.getMaxRows(); + } + + public void setMaxRows(int max) + throws SQLException + { + delegate.setMaxRows(max); + } + + public void setEscapeProcessing(boolean enable) + throws SQLException + { + delegate.setEscapeProcessing(enable); + } + + public int getQueryTimeout() + throws SQLException + { + return delegate.getQueryTimeout(); + } + + public void setQueryTimeout(int seconds) + throws SQLException + { + delegate.setQueryTimeout(seconds); + } + + public void cancel() + throws SQLException + { + delegate.cancel(); + } + + public SQLWarning getWarnings() + throws SQLException + { + return delegate.getWarnings(); + } + + public void clearWarnings() + throws SQLException + { + delegate.clearWarnings(); + } + + public void setCursorName(String name) + throws SQLException + { + delegate.setCursorName(name); + } + + public boolean execute(String sql) + throws SQLException + { + return delegate.execute(sql); + } + + public ResultSet getResultSet() + throws SQLException + { + return delegate.getResultSet(); + } + + public int getUpdateCount() + throws SQLException + { + return delegate.getUpdateCount(); + } + + public boolean getMoreResults() + throws SQLException + { + return delegate.getMoreResults(); + } + + public void setFetchDirection(int direction) + throws SQLException + { + delegate.setFetchDirection(direction); + } + + public int getFetchDirection() + throws SQLException + { + return delegate.getFetchDirection(); + } + + public void setFetchSize(int rows) + throws SQLException + { + delegate.setFetchSize(rows); + } + + public int getFetchSize() + throws SQLException + { + return delegate.getFetchSize(); + } + + public int getResultSetConcurrency() + throws SQLException + { + return delegate.getResultSetConcurrency(); + } + + public int getResultSetType() + throws SQLException + { + return delegate.getResultSetType(); + } + + public void addBatch(String sql) + throws SQLException + { + delegate.addBatch(sql); + } + + public void clearBatch() + throws SQLException + { + delegate.clearBatch(); + } + + public int[] executeBatch() + throws SQLException + { + return delegate.executeBatch(); + } + + public Connection getConnection() + throws SQLException + { + return delegate.getConnection(); + } + + public boolean getMoreResults(int current) + throws SQLException + { + return delegate.getMoreResults(current); + } + + public ResultSet getGeneratedKeys() + throws SQLException + { + return delegate.getGeneratedKeys(); + } + + public int executeUpdate(String sql, int autoGeneratedKeys) + throws SQLException + { + return delegate.executeUpdate(sql, autoGeneratedKeys); + } + + public int executeUpdate(String sql, int[] columnIndexes) + throws SQLException + { + return delegate.executeUpdate(sql, columnIndexes); + } + + public int executeUpdate(String sql, String[] columnNames) + throws SQLException + { + return delegate.executeUpdate(sql, columnNames); + } + + public boolean execute(String sql, int autoGeneratedKeys) + throws SQLException + { + return delegate.execute(sql, autoGeneratedKeys); + } + + public boolean execute(String sql, int[] columnIndexes) + throws SQLException + { + return delegate.execute(sql, columnIndexes); + } + + public boolean execute(String sql, String[] columnNames) + throws SQLException + { + return delegate.execute(sql, columnNames); + } + + public int getResultSetHoldability() + throws SQLException + { + return delegate.getResultSetHoldability(); + } + + public boolean isClosed() + throws SQLException + { + return delegate.isClosed(); + } + + public void setPoolable(boolean poolable) + throws SQLException + { + delegate.setPoolable(poolable); + } + + public boolean isPoolable() + throws SQLException + { + return delegate.isPoolable(); + } + + public T unwrap(Class iface) + throws SQLException + { + return delegate.unwrap(iface); + } + + public boolean isWrapperFor(Class iface) + throws SQLException + { + return delegate.isWrapperFor(iface); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManager.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManager.java index 79929fb62..f2e8cb37f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManager.java @@ -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. - *

- * The profile information is periodically converted into "tuned query details" - - * which is used to automatically tune the queries that use autoFetch. - *

- *

- * The "tuned query details" effectively are part of the query that has the - * select() and join() information (but not the where clause, order by, limits - * etc). These are applied to the query when tuneQuery() is called. - *

- */ -public interface AutoFetchManager extends NodeUsageListener { - - /** - * Set the owning ebean server. - */ - public void setOwner(SpiEbeanServer server, ServerConfig serverConfig); - - /** - * Clear the query execution statistics. - */ - public void clearQueryStatistics(); - - /** - * Clear all the tuned query info. - *

- * Should only need do this for testing and playing around. - *

- */ - public int clearTunedQueryInfo(); - - /** - * Clear all the profiling information. - *

- * This means the profiling information will need to be re-gathered. - *

- *

- * Should only need do this for testing and playing around. - *

- */ - public int clearProfilingInfo(); - - /** - * On shutdown fire garbage collection and collect statistics. Note that - * usually we add a little delay (100 milliseconds) to give the garbage - * collector plenty of time to do its thing and collect the profile - * information. - */ - public void shutdown(); - - /** - * Return the current tuned fetch information for a given queryPoint key. - */ - public TunedQueryInfo getTunedQueryInfo(String queryPointKey); - - /** - * Return the current Statistics for a given queryPoint key. - */ - public Statistics getStatistics(String queryPointKey); - - /** - * Iterate the tuned fetch info. - *

- * This should be a read only iteration. - *

- */ - public Iterator iterateTunedQueryInfo(); - - /** - * Iterate the node usage statistics. - *

- * This should be a read only iteration. - *

- */ - public Iterator iterateStatistics(); - - /** - * Return true if profiling is enabled. - */ - public boolean isProfiling(); - - /** - * Set to true to enable profiling. - *

- * We rely on garbage collection to collect the profiling information. This - * means there is a unknown delay between when a query is executed and when - * we actually collect the usage profile information. - *

- *

- * Due to this garbage collection delay, when turning off profiling while - * the application is running you should consider calling - * collectUsageViaGC() BEFORE setProfiling(false). This hints to - * the JVM to perform garbage collection, and hopefully collects the - * profiling information. - *

- */ - public void setProfiling(boolean enable); - - /** - * Return true if automatic query tuning is enabled. - */ - public boolean isQueryTuning(); - - /** - * Set to true to enable automatic query tuning. - */ - public void setQueryTuning(boolean enable); - - /** - * This controls whether autoFetch is used when it has not been explicitly - * set on a query via {@link Query#setAutoFetch(boolean)}. - */ - public AutofetchMode getMode(); - - /** - * Set the auto fetch mode used when a query has not had - * {@link Query#setAutoFetch(boolean)}. - */ - public void setMode(AutofetchMode Mode); - - /** - * Return the profiling rate (int between 0 and 100). - */ - public double getProfilingRate(); - - /** - * Set the profiling rate (int between 0 and 100). - */ - public void setProfilingRate(double rate); - - /** - * Return the max number of queries profiled (per query point). - *

- * The number of queries profiled is collected per query point. Once a query - * point has profiled this number of queries it does not profile any more. - *

- */ - public int getProfilingBase(); - - /** - * Set a max number of queries to profile per query point. - *

- * This number should provide a level of confidence that no more profiling - * is required for this query point. - *

- */ - public void setProfilingBase(int profilingMax); - - /** - * Return the minimum number of queries profiled before autoFetch will start - * automatically tuning the queries. - *

- * This could be one which means start autoFetch tuning after the first - * profiling information is collected. - *

- */ - public int getProfilingMin(); - - /** - * Set the minimum number of queries profiled per query point before - * autoFetch will automatically tune the queries. - *

- * Increasing this number will mean more profiling is collected before - * autoFetch starts tuning the query. - *

- */ - public void setProfilingMin(int autoFetchMinThreshold); - - /** - * Fire a garbage collection (hint to the JVM). Assuming garbage collection - * fires this will gather the usage profiling information. - */ - public String collectUsageViaGC(long waitMillis); - - /** - * This will take the current profiling information and update the "tuned - * query detail". - *

- * This is done periodically and can also be manually invoked. - *

- *

- * This returns a string summary of the updates that occurred. - *

- */ - public String updateTunedQueryInfo(); - - /** - * Called when a query thinks it should be automatically tuned by autoFetch. - *

- * This internally checks that autoFetch is enabled, there is a "tuned query - * detail" to tune the query with and that the autoFetchMinThreshold has - * been reached. - *

- *

- * This will also determine if the query should be profiled. - *

- */ - public boolean tuneQuery(SpiQuery query); - - /** - * Collect query profiling information. - *

- * This is for the original query as well as any subsequent lazy loading - * queries that are required as the object graph is traversed. - *

- * - * @param node - * the node path in the object graph. - * @param beans - * the number of beans loaded by the query. - * @param micros - * the query executing time in microseconds - */ - public void collectQueryInfo(ObjectGraphNode node, int beans, int micros); - - - /** - * Return the number of queries tuned by AutoFetch. - */ - public int getTotalTunedQueryCount(); - - /** - * Return the size of the TuneQuery map. - */ - public int getTotalTunedQuerySize(); - - /** - * Return the size of the profile map. - */ - public int getTotalProfileSize(); -} +package com.avaje.ebeaninternal.server.autofetch; + +import java.util.Iterator; + +import com.avaje.ebean.Query; +import com.avaje.ebean.bean.NodeUsageListener; +import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebean.config.AutofetchMode; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiQuery; + +/** + * Collects and manages the the profile information. + *

+ * The profile information is periodically converted into "tuned query details" - + * which is used to automatically tune the queries that use autoFetch. + *

+ *

+ * The "tuned query details" effectively are part of the query that has the + * select() and join() information (but not the where clause, order by, limits + * etc). These are applied to the query when tuneQuery() is called. + *

+ */ +public interface AutoFetchManager extends NodeUsageListener { + + /** + * Set the owning ebean server. + */ + public void setOwner(SpiEbeanServer server, ServerConfig serverConfig); + + /** + * Clear the query execution statistics. + */ + public void clearQueryStatistics(); + + /** + * Clear all the tuned query info. + *

+ * Should only need do this for testing and playing around. + *

+ */ + public int clearTunedQueryInfo(); + + /** + * Clear all the profiling information. + *

+ * This means the profiling information will need to be re-gathered. + *

+ *

+ * Should only need do this for testing and playing around. + *

+ */ + public int clearProfilingInfo(); + + /** + * On shutdown fire garbage collection and collect statistics. Note that + * usually we add a little delay (100 milliseconds) to give the garbage + * collector plenty of time to do its thing and collect the profile + * information. + */ + public void shutdown(); + + /** + * Return the current tuned fetch information for a given queryPoint key. + */ + public TunedQueryInfo getTunedQueryInfo(String queryPointKey); + + /** + * Return the current Statistics for a given queryPoint key. + */ + public Statistics getStatistics(String queryPointKey); + + /** + * Iterate the tuned fetch info. + *

+ * This should be a read only iteration. + *

+ */ + public Iterator iterateTunedQueryInfo(); + + /** + * Iterate the node usage statistics. + *

+ * This should be a read only iteration. + *

+ */ + public Iterator iterateStatistics(); + + /** + * Return true if profiling is enabled. + */ + public boolean isProfiling(); + + /** + * Set to true to enable profiling. + *

+ * We rely on garbage collection to collect the profiling information. This + * means there is a unknown delay between when a query is executed and when + * we actually collect the usage profile information. + *

+ *

+ * Due to this garbage collection delay, when turning off profiling while + * the application is running you should consider calling + * collectUsageViaGC() BEFORE setProfiling(false). This hints to + * the JVM to perform garbage collection, and hopefully collects the + * profiling information. + *

+ */ + public void setProfiling(boolean enable); + + /** + * Return true if automatic query tuning is enabled. + */ + public boolean isQueryTuning(); + + /** + * Set to true to enable automatic query tuning. + */ + public void setQueryTuning(boolean enable); + + /** + * This controls whether autoFetch is used when it has not been explicitly + * set on a query via {@link Query#setAutoFetch(boolean)}. + */ + public AutofetchMode getMode(); + + /** + * Set the auto fetch mode used when a query has not had + * {@link Query#setAutoFetch(boolean)}. + */ + public void setMode(AutofetchMode Mode); + + /** + * Return the profiling rate (int between 0 and 100). + */ + public double getProfilingRate(); + + /** + * Set the profiling rate (int between 0 and 100). + */ + public void setProfilingRate(double rate); + + /** + * Return the max number of queries profiled (per query point). + *

+ * The number of queries profiled is collected per query point. Once a query + * point has profiled this number of queries it does not profile any more. + *

+ */ + public int getProfilingBase(); + + /** + * Set a max number of queries to profile per query point. + *

+ * This number should provide a level of confidence that no more profiling + * is required for this query point. + *

+ */ + public void setProfilingBase(int profilingMax); + + /** + * Return the minimum number of queries profiled before autoFetch will start + * automatically tuning the queries. + *

+ * This could be one which means start autoFetch tuning after the first + * profiling information is collected. + *

+ */ + public int getProfilingMin(); + + /** + * Set the minimum number of queries profiled per query point before + * autoFetch will automatically tune the queries. + *

+ * Increasing this number will mean more profiling is collected before + * autoFetch starts tuning the query. + *

+ */ + public void setProfilingMin(int autoFetchMinThreshold); + + /** + * Fire a garbage collection (hint to the JVM). Assuming garbage collection + * fires this will gather the usage profiling information. + */ + public String collectUsageViaGC(long waitMillis); + + /** + * This will take the current profiling information and update the "tuned + * query detail". + *

+ * This is done periodically and can also be manually invoked. + *

+ *

+ * This returns a string summary of the updates that occurred. + *

+ */ + public String updateTunedQueryInfo(); + + /** + * Called when a query thinks it should be automatically tuned by autoFetch. + *

+ * This internally checks that autoFetch is enabled, there is a "tuned query + * detail" to tune the query with and that the autoFetchMinThreshold has + * been reached. + *

+ *

+ * This will also determine if the query should be profiled. + *

+ */ + public boolean tuneQuery(SpiQuery query); + + /** + * Collect query profiling information. + *

+ * This is for the original query as well as any subsequent lazy loading + * queries that are required as the object graph is traversed. + *

+ * + * @param node + * the node path in the object graph. + * @param beans + * the number of beans loaded by the query. + * @param micros + * the query executing time in microseconds + */ + public void collectQueryInfo(ObjectGraphNode node, int beans, int micros); + + + /** + * Return the number of queries tuned by AutoFetch. + */ + public int getTotalTunedQueryCount(); + + /** + * Return the size of the TuneQuery map. + */ + public int getTotalTunedQuerySize(); + + /** + * Return the size of the profile map. + */ + public int getTotalProfileSize(); +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/package-info.java b/src/main/java/com/avaje/ebeaninternal/server/cache/package-info.java index d142dd20d..b72997ba7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cache/package-info.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cache/package-info.java @@ -1,4 +1 @@ -/** - * Default L2 server cache implementation. - */ package com.avaje.ebeaninternal.server.cache; \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessage.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessage.java index 0445adbd7..c82d88655 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessage.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessage.java @@ -1,83 +1,64 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; - -/** - * Represents a relatively small independent message. - *

- * In general terms we break up a potentially large object like - * RemoteTransactionEvent into many smaller BinaryMessages. This is so that if - * they don't all fit on a single Packet we can easily break them up and put - * them on multiple packets. - *

- *

- * Also note that for the Multicast approach a Packet will generally contain - * many messages each directed to different members of the cluster. So it would - * be common for many Ack, Resend and Control messages to all be contained in a - * single packet. - *

- * - * @author rbygrave - * - */ -public class BinaryMessage { - - public static final int TYPE_MSGCONTROL = 0; - public static final int TYPE_BEANIUD = 1; - public static final int TYPE_TABLEIUD = 2; - public static final int TYPE_BEANDELTA = 3; - public static final int TYPE_BEANPATHUPDATE = 4; - public static final int TYPE_INDEX_INVALIDATE = 6; - public static final int TYPE_INDEX = 7; - public static final int TYPE_MSGACK = 8; - public static final int TYPE_MSGRESEND = 9; - - private final ByteArrayOutputStream buffer; - private final DataOutputStream os; - private byte[] bytes; - - /** - * Create with an estimated buffer size. - */ - public BinaryMessage(int bufSize) { - this.buffer = new ByteArrayOutputStream(bufSize); - this.os = new DataOutputStream(buffer); - } - - /** - * Return the DataOutputStream to write content to. - */ - public DataOutputStream getOs() { - return os; - } - - /** - * Return all the content as a byte array. - */ - public byte[] getByteArray() { - if (bytes == null) { - bytes = buffer.toByteArray(); - } - return bytes; - } -} +package com.avaje.ebeaninternal.server.cluster; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; + +/** + * Represents a relatively small independent message. + *

+ * In general terms we break up a potentially large object like + * RemoteTransactionEvent into many smaller BinaryMessages. This is so that if + * they don't all fit on a single Packet we can easily break them up and put + * them on multiple packets. + *

+ *

+ * Also note that for the Multicast approach a Packet will generally contain + * many messages each directed to different members of the cluster. So it would + * be common for many Ack, Resend and Control messages to all be contained in a + * single packet. + *

+ * + * @author rbygrave + * + */ +public class BinaryMessage { + + public static final int TYPE_MSGCONTROL = 0; + public static final int TYPE_BEANIUD = 1; + public static final int TYPE_TABLEIUD = 2; + public static final int TYPE_BEANDELTA = 3; + public static final int TYPE_BEANPATHUPDATE = 4; + public static final int TYPE_INDEX_INVALIDATE = 6; + public static final int TYPE_INDEX = 7; + public static final int TYPE_MSGACK = 8; + public static final int TYPE_MSGRESEND = 9; + + private final ByteArrayOutputStream buffer; + private final DataOutputStream os; + private byte[] bytes; + + /** + * Create with an estimated buffer size. + */ + public BinaryMessage(int bufSize) { + this.buffer = new ByteArrayOutputStream(bufSize); + this.os = new DataOutputStream(buffer); + } + + /** + * Return the DataOutputStream to write content to. + */ + public DataOutputStream getOs() { + return os; + } + + /** + * Return all the content as a byte array. + */ + public byte[] getByteArray() { + if (bytes == null) { + bytes = buffer.toByteArray(); + } + return bytes; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessageList.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessageList.java index 64d7d3168..3c0e36fe0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessageList.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/BinaryMessageList.java @@ -1,42 +1,23 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster; - -import java.util.ArrayList; -import java.util.List; - -/** - * Holds a List of BinaryMessage's. - * - * @author rbygrave - */ -public class BinaryMessageList { - - ArrayList list = new ArrayList(); - - public void add(BinaryMessage msg) { - list.add(msg); - } - - public List getList() { - return list; - } - -} +package com.avaje.ebeaninternal.server.cluster; + +import java.util.ArrayList; +import java.util.List; + +/** + * Holds a List of BinaryMessage's. + * + * @author rbygrave + */ +public class BinaryMessageList { + + ArrayList list = new ArrayList(); + + public void add(BinaryMessage msg) { + list.add(msg); + } + + public List getList() { + return list; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterBroadcast.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterBroadcast.java index c184baf91..de871602c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterBroadcast.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterBroadcast.java @@ -1,45 +1,28 @@ -/** - * Copyright (C) 2006 Robin Bygrave - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster; - -import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; - - -/** - * Sends messages to the cluster members. - */ -public interface ClusterBroadcast { - - /** - * Inform the other cluster members that this instance has come online and - * start any listeners etc. - */ - public void startup(ClusterManager clusterManager); - - /** - * Inform the other cluster members that this instance is leaving and - * shutdown any listeners. - */ - public void shutdown(); - - /** - * Send a transaction event to all the members of the cluster. - */ - public void broadcast(RemoteTransactionEvent remoteTransEvent); - -} +package com.avaje.ebeaninternal.server.cluster; + +import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; + + +/** + * Sends messages to the cluster members. + */ +public interface ClusterBroadcast { + + /** + * Inform the other cluster members that this instance has come online and + * start any listeners etc. + */ + public void startup(ClusterManager clusterManager); + + /** + * Inform the other cluster members that this instance is leaving and + * shutdown any listeners. + */ + public void shutdown(); + + /** + * Send a transaction event to all the members of the cluster. + */ + public void broadcast(RemoteTransactionEvent remoteTransEvent); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterManager.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterManager.java index e595a0bda..d5c8651b3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/ClusterManager.java @@ -1,122 +1,105 @@ -/** - * Copyright (C) 2006 Robin Bygrave - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebeaninternal.api.ClassUtil; -import com.avaje.ebeaninternal.server.cluster.mcast.McastClusterManager; -import com.avaje.ebeaninternal.server.cluster.socket.SocketClusterBroadcast; -import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; - -/** - * Manages the cluster service. - */ -public class ClusterManager { - - private static final Logger logger = Logger.getLogger(ClusterManager.class.getName()); - - private final ConcurrentHashMap serverMap = new ConcurrentHashMap(); - - private final Object monitor = new Object(); - - private final ClusterBroadcast broadcast; - - private boolean started; - - public ClusterManager() { - - String clusterType = GlobalProperties.get("ebean.cluster.type", null); - if (clusterType == null || clusterType.trim().length() == 0) { - // not clustering this instance - this.broadcast = null; - - } else { - - try { - if ("mcast".equalsIgnoreCase(clusterType)) { - this.broadcast = new McastClusterManager(); - - } else if ("socket".equalsIgnoreCase(clusterType)) { - this.broadcast = new SocketClusterBroadcast(); - - } else { - logger.info("Clustering using [" + clusterType + "]"); - this.broadcast = (ClusterBroadcast) ClassUtil.newInstance(clusterType); - } - - } catch (Exception e) { - String msg = "Error initialising ClusterManager type [" + clusterType + "]"; - logger.log(Level.SEVERE, msg, e); - throw new RuntimeException(e); - } - } - } - - public void registerServer(EbeanServer server) { - synchronized (monitor) { - if (!started) { - startup(); - } - serverMap.put(server.getName(), server); - } - } - - public EbeanServer getServer(String name) { - synchronized (monitor) { - return serverMap.get(name); - } - } - - private void startup() { - started = true; - if (broadcast != null) { - broadcast.startup(this); - } - } - - /** - * Return true if clustering is on. - */ - public boolean isClustering() { - return broadcast != null; - } - - /** - * Send the message headers and payload to every server in the cluster. - */ - public void broadcast(RemoteTransactionEvent remoteTransEvent) { - if (broadcast != null) { - broadcast.broadcast(remoteTransEvent); - } - } - - /** - * Shutdown the service and Deregister from the cluster. - */ - public void shutdown() { - if (broadcast != null) { - logger.info("ClusterManager shutdown "); - broadcast.shutdown(); - } - } -} +package com.avaje.ebeaninternal.server.cluster; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebeaninternal.api.ClassUtil; +import com.avaje.ebeaninternal.server.cluster.mcast.McastClusterManager; +import com.avaje.ebeaninternal.server.cluster.socket.SocketClusterBroadcast; +import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; + +/** + * Manages the cluster service. + */ +public class ClusterManager { + + private static final Logger logger = Logger.getLogger(ClusterManager.class.getName()); + + private final ConcurrentHashMap serverMap = new ConcurrentHashMap(); + + private final Object monitor = new Object(); + + private final ClusterBroadcast broadcast; + + private boolean started; + + public ClusterManager() { + + String clusterType = GlobalProperties.get("ebean.cluster.type", null); + if (clusterType == null || clusterType.trim().length() == 0) { + // not clustering this instance + this.broadcast = null; + + } else { + + try { + if ("mcast".equalsIgnoreCase(clusterType)) { + this.broadcast = new McastClusterManager(); + + } else if ("socket".equalsIgnoreCase(clusterType)) { + this.broadcast = new SocketClusterBroadcast(); + + } else { + logger.info("Clustering using [" + clusterType + "]"); + this.broadcast = (ClusterBroadcast) ClassUtil.newInstance(clusterType); + } + + } catch (Exception e) { + String msg = "Error initialising ClusterManager type [" + clusterType + "]"; + logger.log(Level.SEVERE, msg, e); + throw new RuntimeException(e); + } + } + } + + public void registerServer(EbeanServer server) { + synchronized (monitor) { + if (!started) { + startup(); + } + serverMap.put(server.getName(), server); + } + } + + public EbeanServer getServer(String name) { + synchronized (monitor) { + return serverMap.get(name); + } + } + + private void startup() { + started = true; + if (broadcast != null) { + broadcast.startup(this); + } + } + + /** + * Return true if clustering is on. + */ + public boolean isClustering() { + return broadcast != null; + } + + /** + * Send the message headers and payload to every server in the cluster. + */ + public void broadcast(RemoteTransactionEvent remoteTransEvent) { + if (broadcast != null) { + broadcast.broadcast(remoteTransEvent); + } + } + + /** + * Shutdown the service and Deregister from the cluster. + */ + public void shutdown() { + if (broadcast != null) { + logger.info("ClusterManager shutdown "); + broadcast.shutdown(); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/DataHolder.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/DataHolder.java index bfb0da2fe..082ccded8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/DataHolder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/DataHolder.java @@ -1,43 +1,24 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster; - -import java.io.Serializable; - -/** - * Simple holder of binary data. - * Used to use Packet based serialisation of RemoteTransactionEvent - * with simple Java Serialisation of the DataHolder. - */ -public class DataHolder implements Serializable { - - private static final long serialVersionUID = 9090748723571322192L; - - private final byte[] data; - - public DataHolder(byte[] data) { - this.data = data; - } - - public byte[] getData() { - return data; - } - -} +package com.avaje.ebeaninternal.server.cluster; + +import java.io.Serializable; + +/** + * Simple holder of binary data. + * Used to use Packet based serialisation of RemoteTransactionEvent + * with simple Java Serialisation of the DataHolder. + */ +public class DataHolder implements Serializable { + + private static final long serialVersionUID = 9090748723571322192L; + + private final byte[] data; + + public DataHolder(byte[] data) { + this.data = data; + } + + public byte[] getData() { + return data; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/Packet.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/Packet.java index cc33392e1..d8fd083a6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/Packet.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/Packet.java @@ -1,212 +1,193 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster; - -import java.io.ByteArrayOutputStream; -import java.io.DataInput; -import java.io.DataOutputStream; -import java.io.IOException; - -/** - * Represents the contents sent as a single DatagramPacket. - *

- * The contents is typically multiple messages (ACK,PING etc) or all or part of - * a RemoteTransactionEvent. - *

- *

- * Due to the hard limit on the size of UDP packets a RemoteTransactionEvent - * with lots of information could be broken up into multiple packets. - *

- * - * @author rbygrave - */ -public class Packet { - - /** - * A Packet that holds protocol messages like ACK, PING etc. - */ - public static final short TYPE_MESSAGES = 1; - - /** - * A Packet that holds TransactionEvent information such as Bean - * and or Table IUD information. - */ - public static final short TYPE_TRANSEVENT = 2; - - /** - * The type of Packet. - */ - protected short packetType; - - /** - * The PacketId. - */ - protected long packetId; - - /** - * The timestamp the Packet was created. - */ - protected long timestamp; - - /** - * The EbeanServer name this relates to if relevant. - */ - protected String serverName; - - protected ByteArrayOutputStream buffer; - protected DataOutputStream dataOut; - protected byte[] bytes; - - /** - * The number of messages in this Packet. - */ - private int messageCount; - - /** - * The number of times this Packet was resent. - */ - private int resendCount; - - /** - * Create a Packet for writing messages to. - */ - public static Packet forWrite(short packetType, long packetId, long timestamp, String serverName) throws IOException { - return new Packet(true, packetType, packetId, timestamp, serverName); - } - - /** - * Create a Packet just reading the Header information. - */ - public static Packet readHeader(DataInput dataInput) throws IOException { - - short packetType = dataInput.readShort(); - long packetId = dataInput.readLong(); - long timestamp = dataInput.readLong(); - String serverName = dataInput.readUTF(); - - return new Packet(false, packetType, packetId, timestamp, serverName); - } - - protected Packet(boolean write, short packetType, long packetId, long timestamp, String serverName) throws IOException{ - this.packetType = packetType; - this.packetId = packetId; - this.timestamp = timestamp; - this.serverName = serverName; - if (write){ - this.buffer = new ByteArrayOutputStream(); - this.dataOut = new DataOutputStream(buffer); - writeHeader(); - } else { - this.buffer = null; - this.dataOut = null; - } - } - - private void writeHeader() throws IOException { - dataOut.writeShort(packetType); - dataOut.writeLong(packetId); - dataOut.writeLong(timestamp); - dataOut.writeUTF(serverName); - } - - public int incrementResendCount() { - return resendCount++; - } - - public short getPacketType() { - return packetType; - } - - public long getPacketId() { - return packetId; - } - - public long getTimestamp() { - return timestamp; - } - - public String getServerName() { - return serverName; - } - - public void writeEof() throws IOException { - dataOut.writeBoolean(false); - } - - public void read(DataInput dataInput) throws IOException { - boolean more = dataInput.readBoolean(); - while (more){ - int msgType = dataInput.readInt(); - readMessage(dataInput, msgType); - // see if there is more information - more = dataInput.readBoolean(); - } - } - - /** - * Overridden by more specific Packet implementations to read the messages. - */ - protected void readMessage(DataInput dataInput, int msgType) throws IOException { - - } - - /** - * Write a binary message to this packet returning true if there was - * enough room to do so. Return false if the message was too large for - * the remaining space left - in this case another Packet should be - * created to put that message into. - */ - public boolean writeBinaryMessage(BinaryMessage msg, int maxPacketSize) throws IOException { - - byte[] bytes = msg.getByteArray(); - - if (messageCount > 0 && (bytes.length + buffer.size() > maxPacketSize)){ - // we are actually going to ignore the maxPacketSize iff we have one - // large message. - - // false = no more messages - dataOut.writeBoolean(false); - return false; - } - ++messageCount; - // true = another message follows - dataOut.writeBoolean(true); - dataOut.write(bytes); - return true; - } - - public int getSize() { - return getBytes().length; - } - - /** - * Return the Packet as raw bytes. - */ - public byte[] getBytes() { - if (bytes == null){ - bytes = buffer.toByteArray(); - buffer = null; - dataOut = null; - } - return bytes; - } - - -} +package com.avaje.ebeaninternal.server.cluster; + +import java.io.ByteArrayOutputStream; +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Represents the contents sent as a single DatagramPacket. + *

+ * The contents is typically multiple messages (ACK,PING etc) or all or part of + * a RemoteTransactionEvent. + *

+ *

+ * Due to the hard limit on the size of UDP packets a RemoteTransactionEvent + * with lots of information could be broken up into multiple packets. + *

+ * + * @author rbygrave + */ +public class Packet { + + /** + * A Packet that holds protocol messages like ACK, PING etc. + */ + public static final short TYPE_MESSAGES = 1; + + /** + * A Packet that holds TransactionEvent information such as Bean + * and or Table IUD information. + */ + public static final short TYPE_TRANSEVENT = 2; + + /** + * The type of Packet. + */ + protected short packetType; + + /** + * The PacketId. + */ + protected long packetId; + + /** + * The timestamp the Packet was created. + */ + protected long timestamp; + + /** + * The EbeanServer name this relates to if relevant. + */ + protected String serverName; + + protected ByteArrayOutputStream buffer; + protected DataOutputStream dataOut; + protected byte[] bytes; + + /** + * The number of messages in this Packet. + */ + private int messageCount; + + /** + * The number of times this Packet was resent. + */ + private int resendCount; + + /** + * Create a Packet for writing messages to. + */ + public static Packet forWrite(short packetType, long packetId, long timestamp, String serverName) throws IOException { + return new Packet(true, packetType, packetId, timestamp, serverName); + } + + /** + * Create a Packet just reading the Header information. + */ + public static Packet readHeader(DataInput dataInput) throws IOException { + + short packetType = dataInput.readShort(); + long packetId = dataInput.readLong(); + long timestamp = dataInput.readLong(); + String serverName = dataInput.readUTF(); + + return new Packet(false, packetType, packetId, timestamp, serverName); + } + + protected Packet(boolean write, short packetType, long packetId, long timestamp, String serverName) throws IOException{ + this.packetType = packetType; + this.packetId = packetId; + this.timestamp = timestamp; + this.serverName = serverName; + if (write){ + this.buffer = new ByteArrayOutputStream(); + this.dataOut = new DataOutputStream(buffer); + writeHeader(); + } else { + this.buffer = null; + this.dataOut = null; + } + } + + private void writeHeader() throws IOException { + dataOut.writeShort(packetType); + dataOut.writeLong(packetId); + dataOut.writeLong(timestamp); + dataOut.writeUTF(serverName); + } + + public int incrementResendCount() { + return resendCount++; + } + + public short getPacketType() { + return packetType; + } + + public long getPacketId() { + return packetId; + } + + public long getTimestamp() { + return timestamp; + } + + public String getServerName() { + return serverName; + } + + public void writeEof() throws IOException { + dataOut.writeBoolean(false); + } + + public void read(DataInput dataInput) throws IOException { + boolean more = dataInput.readBoolean(); + while (more){ + int msgType = dataInput.readInt(); + readMessage(dataInput, msgType); + // see if there is more information + more = dataInput.readBoolean(); + } + } + + /** + * Overridden by more specific Packet implementations to read the messages. + */ + protected void readMessage(DataInput dataInput, int msgType) throws IOException { + + } + + /** + * Write a binary message to this packet returning true if there was + * enough room to do so. Return false if the message was too large for + * the remaining space left - in this case another Packet should be + * created to put that message into. + */ + public boolean writeBinaryMessage(BinaryMessage msg, int maxPacketSize) throws IOException { + + byte[] bytes = msg.getByteArray(); + + if (messageCount > 0 && (bytes.length + buffer.size() > maxPacketSize)){ + // we are actually going to ignore the maxPacketSize iff we have one + // large message. + + // false = no more messages + dataOut.writeBoolean(false); + return false; + } + ++messageCount; + // true = another message follows + dataOut.writeBoolean(true); + dataOut.write(bytes); + return true; + } + + public int getSize() { + return getBytes().length; + } + + /** + * Return the Packet as raw bytes. + */ + public byte[] getBytes() { + if (bytes == null){ + bytes = buffer.toByteArray(); + buffer = null; + dataOut = null; + } + return bytes; + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketMessages.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketMessages.java index 53a182133..9dac59a44 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketMessages.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketMessages.java @@ -1,88 +1,69 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster; - -import java.io.DataInput; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import com.avaje.ebeaninternal.server.cluster.mcast.Message; -import com.avaje.ebeaninternal.server.cluster.mcast.MessageAck; -import com.avaje.ebeaninternal.server.cluster.mcast.MessageControl; -import com.avaje.ebeaninternal.server.cluster.mcast.MessageResend; - -/** - * A Packet that contains Ack, Resend and Control messages. - * - * @author rbygrave - */ -public class PacketMessages extends Packet { - - private final ArrayList messages; - - public static PacketMessages forWrite(long packetId, long timestamp, String serverName) throws IOException { - return new PacketMessages(true, packetId, timestamp, serverName); - } - - public static PacketMessages forRead(Packet header) throws IOException { - return new PacketMessages(header); - } - - private PacketMessages(boolean write, long packetId, long timestamp, String serverName) throws IOException { - super(write, TYPE_MESSAGES, packetId, timestamp, serverName); - this.messages = null; - } - - private PacketMessages(Packet header) throws IOException { - super(false, TYPE_MESSAGES, header.packetId, header.timestamp, header.serverName); - this.messages = new ArrayList(); - } - - /** - * Return the messages contained in this Packet. - */ - public List getMessages() { - return messages; - } - - /** - * Read the messages (Ack, Resend or Control) contained in this packet. - */ - protected void readMessage(DataInput dataInput, int msgType) throws IOException { - - switch (msgType) { - case BinaryMessage.TYPE_MSGCONTROL: - messages.add(MessageControl.readBinaryMessage(dataInput)); - break; - - case BinaryMessage.TYPE_MSGACK: - messages.add(MessageAck.readBinaryMessage(dataInput)); - break; - - case BinaryMessage.TYPE_MSGRESEND: - messages.add(MessageResend.readBinaryMessage(dataInput)); - break; - - default: - throw new RuntimeException("Invalid Transaction msgType "+msgType); - } - } -} +package com.avaje.ebeaninternal.server.cluster; + +import java.io.DataInput; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import com.avaje.ebeaninternal.server.cluster.mcast.Message; +import com.avaje.ebeaninternal.server.cluster.mcast.MessageAck; +import com.avaje.ebeaninternal.server.cluster.mcast.MessageControl; +import com.avaje.ebeaninternal.server.cluster.mcast.MessageResend; + +/** + * A Packet that contains Ack, Resend and Control messages. + * + * @author rbygrave + */ +public class PacketMessages extends Packet { + + private final ArrayList messages; + + public static PacketMessages forWrite(long packetId, long timestamp, String serverName) throws IOException { + return new PacketMessages(true, packetId, timestamp, serverName); + } + + public static PacketMessages forRead(Packet header) throws IOException { + return new PacketMessages(header); + } + + private PacketMessages(boolean write, long packetId, long timestamp, String serverName) throws IOException { + super(write, TYPE_MESSAGES, packetId, timestamp, serverName); + this.messages = null; + } + + private PacketMessages(Packet header) throws IOException { + super(false, TYPE_MESSAGES, header.packetId, header.timestamp, header.serverName); + this.messages = new ArrayList(); + } + + /** + * Return the messages contained in this Packet. + */ + public List getMessages() { + return messages; + } + + /** + * Read the messages (Ack, Resend or Control) contained in this packet. + */ + protected void readMessage(DataInput dataInput, int msgType) throws IOException { + + switch (msgType) { + case BinaryMessage.TYPE_MSGCONTROL: + messages.add(MessageControl.readBinaryMessage(dataInput)); + break; + + case BinaryMessage.TYPE_MSGACK: + messages.add(MessageAck.readBinaryMessage(dataInput)); + break; + + case BinaryMessage.TYPE_MSGRESEND: + messages.add(MessageResend.readBinaryMessage(dataInput)); + break; + + default: + throw new RuntimeException("Invalid Transaction msgType "+msgType); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketTransactionEvent.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketTransactionEvent.java index a746d28d8..4e1236e40 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketTransactionEvent.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketTransactionEvent.java @@ -1,94 +1,75 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster; - -import java.io.DataInput; -import java.io.IOException; - -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD; -import com.avaje.ebeaninternal.server.transaction.BeanDelta; -import com.avaje.ebeaninternal.server.transaction.BeanPersistIds; -import com.avaje.ebeaninternal.server.transaction.IndexEvent; -import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; - -/** - * A Packet holding TransactionEvent data. - *

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

- * @author rbygrave - */ -public class PacketTransactionEvent extends Packet { - - private final SpiEbeanServer server; - - private final RemoteTransactionEvent event; - - public static PacketTransactionEvent forWrite(long packetId, long timestamp, String serverName) throws IOException { - return new PacketTransactionEvent(true, packetId, timestamp, serverName); - } - - private PacketTransactionEvent(boolean write, long packetId, long timestamp, String serverName) throws IOException { - super(write, TYPE_TRANSEVENT, packetId, timestamp, serverName); - this.server = null; - this.event = null; - } - - private PacketTransactionEvent(Packet header, SpiEbeanServer server) throws IOException { - super(false, TYPE_TRANSEVENT, header.packetId, header.timestamp, header.serverName); - this.server = server; - this.event = new RemoteTransactionEvent(server); - } - - public static PacketTransactionEvent forRead(Packet header, SpiEbeanServer server) throws IOException { - return new PacketTransactionEvent(header, server); - } - - public RemoteTransactionEvent getEvent() { - return event; - } - - protected void readMessage(DataInput dataInput, int msgType) throws IOException { - - switch (msgType) { - case BinaryMessage.TYPE_BEANIUD: - event.addBeanPersistIds(BeanPersistIds.readBinaryMessage(server, dataInput)); - break; - - case BinaryMessage.TYPE_TABLEIUD: - event.addTableIUD(TableIUD.readBinaryMessage(dataInput)); - break; - - case BinaryMessage.TYPE_BEANDELTA: - event.addBeanDelta(BeanDelta.readBinaryMessage(server, dataInput)); - break; - - case BinaryMessage.TYPE_INDEX: - event.addIndexEvent(IndexEvent.readBinaryMessage(dataInput)); - break; - - default: - throw new RuntimeException("Invalid Transaction msgType "+msgType); - } - } - -} +package com.avaje.ebeaninternal.server.cluster; + +import java.io.DataInput; +import java.io.IOException; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD; +import com.avaje.ebeaninternal.server.transaction.BeanDelta; +import com.avaje.ebeaninternal.server.transaction.BeanPersistIds; +import com.avaje.ebeaninternal.server.transaction.IndexEvent; +import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; + +/** + * A Packet holding TransactionEvent data. + *

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

+ * @author rbygrave + */ +public class PacketTransactionEvent extends Packet { + + private final SpiEbeanServer server; + + private final RemoteTransactionEvent event; + + public static PacketTransactionEvent forWrite(long packetId, long timestamp, String serverName) throws IOException { + return new PacketTransactionEvent(true, packetId, timestamp, serverName); + } + + private PacketTransactionEvent(boolean write, long packetId, long timestamp, String serverName) throws IOException { + super(write, TYPE_TRANSEVENT, packetId, timestamp, serverName); + this.server = null; + this.event = null; + } + + private PacketTransactionEvent(Packet header, SpiEbeanServer server) throws IOException { + super(false, TYPE_TRANSEVENT, header.packetId, header.timestamp, header.serverName); + this.server = server; + this.event = new RemoteTransactionEvent(server); + } + + public static PacketTransactionEvent forRead(Packet header, SpiEbeanServer server) throws IOException { + return new PacketTransactionEvent(header, server); + } + + public RemoteTransactionEvent getEvent() { + return event; + } + + protected void readMessage(DataInput dataInput, int msgType) throws IOException { + + switch (msgType) { + case BinaryMessage.TYPE_BEANIUD: + event.addBeanPersistIds(BeanPersistIds.readBinaryMessage(server, dataInput)); + break; + + case BinaryMessage.TYPE_TABLEIUD: + event.addTableIUD(TableIUD.readBinaryMessage(dataInput)); + break; + + case BinaryMessage.TYPE_BEANDELTA: + event.addBeanDelta(BeanDelta.readBinaryMessage(server, dataInput)); + break; + + case BinaryMessage.TYPE_INDEX: + event.addIndexEvent(IndexEvent.readBinaryMessage(dataInput)); + break; + + default: + throw new RuntimeException("Invalid Transaction msgType "+msgType); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketWriter.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketWriter.java index a868fab63..853ec3bc6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketWriter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/PacketWriter.java @@ -1,22 +1,3 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ package com.avaje.ebeaninternal.server.cluster; import java.io.IOException; diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/SerialiseTransactionHelper.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/SerialiseTransactionHelper.java index 9e9b14130..9b3d8f57f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/SerialiseTransactionHelper.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/SerialiseTransactionHelper.java @@ -1,22 +1,3 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ package com.avaje.ebeaninternal.server.cluster; import java.io.ByteArrayInputStream; diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/AckResendMessages.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/AckResendMessages.java index fa9ef1b84..b35d8e6c5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/AckResendMessages.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/AckResendMessages.java @@ -1,62 +1,43 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.mcast; - -import java.util.ArrayList; -import java.util.List; - -/** - * Holds a list of ACK and RESEND messages that should be sent out. - * - * @author rbygrave - */ -public class AckResendMessages { - - ArrayList messages = new ArrayList(); - - public String toString() { - return messages.toString(); - } - - public int size() { - return messages.size(); - } - - /** - * Add a ACK message to send. - */ - public void add(MessageAck ack){ - messages.add(ack); - } - - /** - * Add a RESEND message to send. - */ - public void add(MessageResend resend){ - messages.add(resend); - } - - /** - * Return all the messages to be sent out. - */ - public List getMessages() { - return messages; - } -} +package com.avaje.ebeaninternal.server.cluster.mcast; + +import java.util.ArrayList; +import java.util.List; + +/** + * Holds a list of ACK and RESEND messages that should be sent out. + * + * @author rbygrave + */ +public class AckResendMessages { + + ArrayList messages = new ArrayList(); + + public String toString() { + return messages.toString(); + } + + public int size() { + return messages.size(); + } + + /** + * Add a ACK message to send. + */ + public void add(MessageAck ack){ + messages.add(ack); + } + + /** + * Add a RESEND message to send. + */ + public void add(MessageResend resend){ + messages.add(resend); + } + + /** + * Return all the messages to be sent out. + */ + public List getMessages() { + return messages; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsLastAck.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsLastAck.java index 2c47538cf..882e451cf 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsLastAck.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsLastAck.java @@ -1,72 +1,53 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.mcast; - -import java.util.HashMap; -import java.util.List; - -/** - * For this node this holds the ACK gotAllPoint for each member in the cluster. - *

- * As we receive messages from other members of the cluster periodically we need - * to send them ACK messages to say we got all the packets up to the gotAllPoint. - *

- * Thread Safety note: Object only used by McastClusterBroadcast Manager thread. - * So Single Threaded access. - * - * @author rbygrave - */ -public class IncomingPacketsLastAck { - - private HashMap lastAckMap = new HashMap(); - - public String toString() { - return lastAckMap.values().toString(); - } - - /** - * Remove a member of the cluster who has left. - */ - public void remove(String memberHostPort) { - lastAckMap.remove(memberHostPort); - } - - /** - * Get the last Ack point for a given member of the cluster. - */ - public MessageAck getLastAck(String memberHostPort) { - return lastAckMap.get(memberHostPort); - } - - /** - * For the ACK messages in AckResendMessages update the - * last Ack packetId. - */ - public void updateLastAck(AckResendMessages ackResendMessages) { - List messages = ackResendMessages.getMessages(); - for (int i = 0; i < messages.size(); i++) { - Message msg = messages.get(i); - if (msg instanceof MessageAck){ - MessageAck lastAck = (MessageAck)msg; - lastAckMap.put(lastAck.getToHostPort(), lastAck); - } - } - } -} +package com.avaje.ebeaninternal.server.cluster.mcast; + +import java.util.HashMap; +import java.util.List; + +/** + * For this node this holds the ACK gotAllPoint for each member in the cluster. + *

+ * As we receive messages from other members of the cluster periodically we need + * to send them ACK messages to say we got all the packets up to the gotAllPoint. + *

+ * Thread Safety note: Object only used by McastClusterBroadcast Manager thread. + * So Single Threaded access. + * + * @author rbygrave + */ +public class IncomingPacketsLastAck { + + private HashMap lastAckMap = new HashMap(); + + public String toString() { + return lastAckMap.values().toString(); + } + + /** + * Remove a member of the cluster who has left. + */ + public void remove(String memberHostPort) { + lastAckMap.remove(memberHostPort); + } + + /** + * Get the last Ack point for a given member of the cluster. + */ + public MessageAck getLastAck(String memberHostPort) { + return lastAckMap.get(memberHostPort); + } + + /** + * For the ACK messages in AckResendMessages update the + * last Ack packetId. + */ + public void updateLastAck(AckResendMessages ackResendMessages) { + List messages = ackResendMessages.getMessages(); + for (int i = 0; i < messages.size(); i++) { + Message msg = messages.get(i); + if (msg instanceof MessageAck){ + MessageAck lastAck = (MessageAck)msg; + lastAckMap.put(lastAck.getToHostPort(), lastAck); + } + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsProcessed.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsProcessed.java index 6d1cad167..3605aad18 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsProcessed.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/IncomingPacketsProcessed.java @@ -1,292 +1,273 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.mcast; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; - -/** - * For Incoming Packets remembers the packets we have received and processed. - *

- * This determines the gotAllPoint per cluster member and identifies missing - * packets (gap between gotAllPoint and gotMaxPoint). - *

- *

- * This information is used by the managerThread so send ACK's for messages we - * have received and RESEND messages to fill the missing packets we have - * detected. - *

- * - * @author rbygrave - * - */ -public class IncomingPacketsProcessed { - - private final ConcurrentHashMap mapByMember = new ConcurrentHashMap(); - - private final int maxResendIncoming; - - public IncomingPacketsProcessed(int maxResendIncoming) { - this.maxResendIncoming = maxResendIncoming; - } - - public void removeMember(String memberKey) { - mapByMember.remove(memberKey); - } - - /** - * Return true if we should process this packet. Return false if we have - * already processed the packet. - */ - public boolean isProcessPacket(String memberKey, long packetId) { - - GotAllPoint memberPackets = getMemberPackets(memberKey); - return memberPackets.processPacket(packetId); - } - - /** - * Build the list of ACK and RESEND messages that we should send out - * to the other members of the cluster. - */ - public AckResendMessages getAckResendMessages(IncomingPacketsLastAck lastAck) { - - // Called by the McastClusterBroadcast manager thread - - AckResendMessages response = new AckResendMessages(); - - for (GotAllPoint member : mapByMember.values()) { - - MessageAck lastAckMessage = lastAck.getLastAck(member.getMemberKey()); - - member.addAckResendMessages(response, lastAckMessage); - } - - return response; - } - - private GotAllPoint getMemberPackets(String memberKey) { - - // This method is only called single threaded - // by the listener thread so I'm happy that this - // put into mapByMember is ok. - GotAllPoint memberGotAllPoint = mapByMember.get(memberKey); - if (memberGotAllPoint == null) { - memberGotAllPoint = new GotAllPoint(memberKey, maxResendIncoming); - mapByMember.put(memberKey, memberGotAllPoint); - } - return memberGotAllPoint; - } - - /** - * Keeps track of packets received from a particular member of the cluster. - *

- * It notes the packetIds of the packets received and uses those to maintain - * the 'gotAllPoint'. The 'gotAllPoint' is the packetId which we know we - * received all the previous packets. - *

- */ - public static class GotAllPoint { - - private static final Logger logger = Logger.getLogger(GotAllPoint.class.getName()); - - private final String memberKey; - private final int maxResendIncoming; - - private long gotAllPoint; - - private long gotMaxPoint; - - /** - * Packets received out of order. - */ - private ArrayList outOfOrderList = new ArrayList(); - - private HashMap resendCountMap = new HashMap(); - - public GotAllPoint(String memberKey, int maxResendIncoming) { - this.memberKey = memberKey; - this.maxResendIncoming = maxResendIncoming; - } - - /** - * Add ACK and RESEND messages if required. - */ - public void addAckResendMessages(AckResendMessages response, MessageAck lastAckMessage) { - - synchronized (this) { - if (lastAckMessage != null && lastAckMessage.getGotAllPacketId() >= gotAllPoint) { - // nothing has changed - } else { - // ACK that we have got every packet up to gotAllPoint - response.add(new MessageAck(memberKey, gotAllPoint)); - } - - if (getMissingPacketCount() > 0) { - // Ask for these Packets to be RESENT - List missingPackets = getMissingPackets(); - response.add(new MessageResend(memberKey, missingPackets)); - } - } - } - - public String getMemberKey() { - return memberKey; - } - - public long getGotAllPoint() { - synchronized (this) { - return gotAllPoint; - } - } - - public long getGotMaxPoint() { - synchronized (this) { - return gotMaxPoint; - } - } - - private int getMissingPacketCount() { - if (gotMaxPoint <= gotAllPoint) { - if (!resendCountMap.isEmpty()) { - resendCountMap.clear(); - } - return 0; - } - return (int) (gotMaxPoint - gotAllPoint) - outOfOrderList.size(); - } - - public List getMissingPackets() { - - synchronized (this) { - ArrayList missingList = new ArrayList(); - - // this is not particularly efficient but expecting - // the outOfOrderList to be relatively small - - boolean lostPacket = false; - - for (long i = gotAllPoint + 1; i < gotMaxPoint; i++) { - Long packetId = Long.valueOf(i); - if (!outOfOrderList.contains(packetId)) { - if (incrementResendCount(packetId)) { - // request this packet be resent - missingList.add(packetId); - } else { - lostPacket = true; - } - } - } - - if (lostPacket){ - checkOutOfOrderList(); - } - - return missingList; - } - } - - /** - * Return true if this packet has not yet exceeded the maxResendCount. - */ - private boolean incrementResendCount(Long packetId){ - Integer resendCount = resendCountMap.get(packetId); - if (resendCount != null){ - int i = resendCount.intValue() + 1; - if (i > maxResendIncoming){ - // we are going to give up trying to get this packet now - logger.warning("Exceeded maxResendIncoming["+maxResendIncoming+"] for packet["+packetId+"]. Giving up on requesting it."); - resendCountMap.remove(packetId); - outOfOrderList.add(packetId); - return false; - } - resendCount = Integer.valueOf(i); - resendCountMap.put(packetId, resendCount); - } else { - resendCountMap.put(packetId, ONE); - } - return true; - } - - private static final Integer ONE = Integer.valueOf(1); - - public boolean processPacket(long packetId) { - synchronized (this) { - - if (gotAllPoint == 0) { - gotAllPoint = packetId; - return true; - } - if (packetId <= gotAllPoint) { - // already processed this packet - return false; - } - - if (!resendCountMap.isEmpty()){ - resendCountMap.remove(Long.valueOf(packetId)); - } - - if (packetId == gotAllPoint + 1) { - gotAllPoint = packetId; - } else { - if (packetId > gotMaxPoint) { - gotMaxPoint = packetId; - } - outOfOrderList.add(Long.valueOf(packetId)); - } - checkOutOfOrderList(); - return true; - } - } - - private void checkOutOfOrderList() { - - if (outOfOrderList.size() == 0) { - return; - } - - boolean continueCheck; - do { - continueCheck = false; - long nextPoint = gotAllPoint + 1; - - Iterator it = outOfOrderList.iterator(); - while (it.hasNext()) { - Long id = it.next(); - if (id.longValue() == nextPoint) { - // we found the next one in the outOfOrderList - it.remove(); - gotAllPoint = nextPoint; - continueCheck = true; - break; - } - } - } while (continueCheck); - - } - - } - - - -} +package com.avaje.ebeaninternal.server.cluster.mcast; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + +/** + * For Incoming Packets remembers the packets we have received and processed. + *

+ * This determines the gotAllPoint per cluster member and identifies missing + * packets (gap between gotAllPoint and gotMaxPoint). + *

+ *

+ * This information is used by the managerThread so send ACK's for messages we + * have received and RESEND messages to fill the missing packets we have + * detected. + *

+ * + * @author rbygrave + * + */ +public class IncomingPacketsProcessed { + + private final ConcurrentHashMap mapByMember = new ConcurrentHashMap(); + + private final int maxResendIncoming; + + public IncomingPacketsProcessed(int maxResendIncoming) { + this.maxResendIncoming = maxResendIncoming; + } + + public void removeMember(String memberKey) { + mapByMember.remove(memberKey); + } + + /** + * Return true if we should process this packet. Return false if we have + * already processed the packet. + */ + public boolean isProcessPacket(String memberKey, long packetId) { + + GotAllPoint memberPackets = getMemberPackets(memberKey); + return memberPackets.processPacket(packetId); + } + + /** + * Build the list of ACK and RESEND messages that we should send out + * to the other members of the cluster. + */ + public AckResendMessages getAckResendMessages(IncomingPacketsLastAck lastAck) { + + // Called by the McastClusterBroadcast manager thread + + AckResendMessages response = new AckResendMessages(); + + for (GotAllPoint member : mapByMember.values()) { + + MessageAck lastAckMessage = lastAck.getLastAck(member.getMemberKey()); + + member.addAckResendMessages(response, lastAckMessage); + } + + return response; + } + + private GotAllPoint getMemberPackets(String memberKey) { + + // This method is only called single threaded + // by the listener thread so I'm happy that this + // put into mapByMember is ok. + GotAllPoint memberGotAllPoint = mapByMember.get(memberKey); + if (memberGotAllPoint == null) { + memberGotAllPoint = new GotAllPoint(memberKey, maxResendIncoming); + mapByMember.put(memberKey, memberGotAllPoint); + } + return memberGotAllPoint; + } + + /** + * Keeps track of packets received from a particular member of the cluster. + *

+ * It notes the packetIds of the packets received and uses those to maintain + * the 'gotAllPoint'. The 'gotAllPoint' is the packetId which we know we + * received all the previous packets. + *

+ */ + public static class GotAllPoint { + + private static final Logger logger = Logger.getLogger(GotAllPoint.class.getName()); + + private final String memberKey; + private final int maxResendIncoming; + + private long gotAllPoint; + + private long gotMaxPoint; + + /** + * Packets received out of order. + */ + private ArrayList outOfOrderList = new ArrayList(); + + private HashMap resendCountMap = new HashMap(); + + public GotAllPoint(String memberKey, int maxResendIncoming) { + this.memberKey = memberKey; + this.maxResendIncoming = maxResendIncoming; + } + + /** + * Add ACK and RESEND messages if required. + */ + public void addAckResendMessages(AckResendMessages response, MessageAck lastAckMessage) { + + synchronized (this) { + if (lastAckMessage != null && lastAckMessage.getGotAllPacketId() >= gotAllPoint) { + // nothing has changed + } else { + // ACK that we have got every packet up to gotAllPoint + response.add(new MessageAck(memberKey, gotAllPoint)); + } + + if (getMissingPacketCount() > 0) { + // Ask for these Packets to be RESENT + List missingPackets = getMissingPackets(); + response.add(new MessageResend(memberKey, missingPackets)); + } + } + } + + public String getMemberKey() { + return memberKey; + } + + public long getGotAllPoint() { + synchronized (this) { + return gotAllPoint; + } + } + + public long getGotMaxPoint() { + synchronized (this) { + return gotMaxPoint; + } + } + + private int getMissingPacketCount() { + if (gotMaxPoint <= gotAllPoint) { + if (!resendCountMap.isEmpty()) { + resendCountMap.clear(); + } + return 0; + } + return (int) (gotMaxPoint - gotAllPoint) - outOfOrderList.size(); + } + + public List getMissingPackets() { + + synchronized (this) { + ArrayList missingList = new ArrayList(); + + // this is not particularly efficient but expecting + // the outOfOrderList to be relatively small + + boolean lostPacket = false; + + for (long i = gotAllPoint + 1; i < gotMaxPoint; i++) { + Long packetId = Long.valueOf(i); + if (!outOfOrderList.contains(packetId)) { + if (incrementResendCount(packetId)) { + // request this packet be resent + missingList.add(packetId); + } else { + lostPacket = true; + } + } + } + + if (lostPacket){ + checkOutOfOrderList(); + } + + return missingList; + } + } + + /** + * Return true if this packet has not yet exceeded the maxResendCount. + */ + private boolean incrementResendCount(Long packetId){ + Integer resendCount = resendCountMap.get(packetId); + if (resendCount != null){ + int i = resendCount.intValue() + 1; + if (i > maxResendIncoming){ + // we are going to give up trying to get this packet now + logger.warning("Exceeded maxResendIncoming["+maxResendIncoming+"] for packet["+packetId+"]. Giving up on requesting it."); + resendCountMap.remove(packetId); + outOfOrderList.add(packetId); + return false; + } + resendCount = Integer.valueOf(i); + resendCountMap.put(packetId, resendCount); + } else { + resendCountMap.put(packetId, ONE); + } + return true; + } + + private static final Integer ONE = Integer.valueOf(1); + + public boolean processPacket(long packetId) { + synchronized (this) { + + if (gotAllPoint == 0) { + gotAllPoint = packetId; + return true; + } + if (packetId <= gotAllPoint) { + // already processed this packet + return false; + } + + if (!resendCountMap.isEmpty()){ + resendCountMap.remove(Long.valueOf(packetId)); + } + + if (packetId == gotAllPoint + 1) { + gotAllPoint = packetId; + } else { + if (packetId > gotMaxPoint) { + gotMaxPoint = packetId; + } + outOfOrderList.add(Long.valueOf(packetId)); + } + checkOutOfOrderList(); + return true; + } + } + + private void checkOutOfOrderList() { + + if (outOfOrderList.size() == 0) { + return; + } + + boolean continueCheck; + do { + continueCheck = false; + long nextPoint = gotAllPoint + 1; + + Iterator it = outOfOrderList.iterator(); + while (it.hasNext()) { + Long id = it.next(); + if (id.longValue() == nextPoint) { + // we found the next one in the outOfOrderList + it.remove(); + gotAllPoint = nextPoint; + continueCheck = true; + break; + } + } + } while (continueCheck); + + } + + } + + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastClusterManager.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastClusterManager.java index 4fef7370b..314db9ce4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastClusterManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastClusterManager.java @@ -1,22 +1,3 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ package com.avaje.ebeaninternal.server.cluster.mcast; import java.io.IOException; diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastPacketControl.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastPacketControl.java index ec67b2f4e..5c3122111 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastPacketControl.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastPacketControl.java @@ -1,22 +1,3 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ package com.avaje.ebeaninternal.server.cluster.mcast; import java.io.DataInput; diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastSender.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastSender.java index a9830ecaf..c52adc3c0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastSender.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastSender.java @@ -1,135 +1,116 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.mcast; - -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebeaninternal.server.cluster.Packet; - -/** - * Handles the sending of Packets via DatagramPacket. - * - * @author rbygrave - */ -public class McastSender { - - private static final Logger logger = Logger.getLogger(McastSender.class.getName()); - - private final int port; - - private final InetAddress inetAddress; - - private final DatagramSocket sock; - - private final InetSocketAddress sendAddr; - - private final String senderHostPort; - - - public McastSender(int port, String address, int sendPort, String sendAddress) { - - try { - this.port = port; - this.inetAddress = InetAddress.getByName(address); - - InetAddress sendInetAddress = null; - if (sendAddress != null) { - sendInetAddress = InetAddress.getByName(sendAddress); - } else { - sendInetAddress = InetAddress.getLocalHost(); - } - - if (sendPort > 0) { - this.sock = new DatagramSocket(sendPort, sendInetAddress); - } else { - this.sock = new DatagramSocket(new InetSocketAddress(sendInetAddress, 0)); - } - - String msg = "Cluster Multicast Sender on["+sendInetAddress.getHostAddress()+":"+sock.getLocalPort()+"]"; - logger.info(msg); - - this.sendAddr = new InetSocketAddress(sendInetAddress, sock.getLocalPort()); - this.senderHostPort = sendInetAddress.getHostAddress()+":"+sock.getLocalPort(); - - } catch (Exception e) { - String msg = "McastSender port:" + port + " sendPort:" + sendPort + " " + address; - throw new RuntimeException(msg, e); - } - } - - /** - * Return the send Address so that if we have loopback messages we can - * detect if they where sent by this local sender and hence should be - * ignored. - */ - public InetSocketAddress getAddress() { - return sendAddr; - } - - /** - * Return the Host and Port of the sender. This is used to uniquely identify - * this instance in the cluster. - */ - public String getSenderHostPort() { - return senderHostPort; - } - - /** - * Send the packet. - */ - public int sendPacket(Packet packet) throws IOException { - - byte[] pktBytes = packet.getBytes(); - - if (logger.isLoggable(Level.FINE)){ - logger.fine("OUTGOING packet: " + packet.getPacketId() + " size:" + pktBytes.length); - } - - if (pktBytes.length > 65507){ - logger.warning("OUTGOING packet: " + packet.getPacketId() + " size:" + pktBytes.length - +" likely to be truncated using UDP with a MAXIMUM length of 65507"); - } - - DatagramPacket pack = new DatagramPacket(pktBytes, pktBytes.length, inetAddress, port); - sock.send(pack); - - return pktBytes.length; - } - - /** - * Send the list of Packets. - */ - public int sendPackets(List packets) throws IOException { - - int totalBytes = 0; - for (int i = 0; i < packets.size(); i++) { - totalBytes += sendPacket(packets.get(i)); - } - return totalBytes; - } - -} +package com.avaje.ebeaninternal.server.cluster.mcast; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebeaninternal.server.cluster.Packet; + +/** + * Handles the sending of Packets via DatagramPacket. + * + * @author rbygrave + */ +public class McastSender { + + private static final Logger logger = Logger.getLogger(McastSender.class.getName()); + + private final int port; + + private final InetAddress inetAddress; + + private final DatagramSocket sock; + + private final InetSocketAddress sendAddr; + + private final String senderHostPort; + + + public McastSender(int port, String address, int sendPort, String sendAddress) { + + try { + this.port = port; + this.inetAddress = InetAddress.getByName(address); + + InetAddress sendInetAddress = null; + if (sendAddress != null) { + sendInetAddress = InetAddress.getByName(sendAddress); + } else { + sendInetAddress = InetAddress.getLocalHost(); + } + + if (sendPort > 0) { + this.sock = new DatagramSocket(sendPort, sendInetAddress); + } else { + this.sock = new DatagramSocket(new InetSocketAddress(sendInetAddress, 0)); + } + + String msg = "Cluster Multicast Sender on["+sendInetAddress.getHostAddress()+":"+sock.getLocalPort()+"]"; + logger.info(msg); + + this.sendAddr = new InetSocketAddress(sendInetAddress, sock.getLocalPort()); + this.senderHostPort = sendInetAddress.getHostAddress()+":"+sock.getLocalPort(); + + } catch (Exception e) { + String msg = "McastSender port:" + port + " sendPort:" + sendPort + " " + address; + throw new RuntimeException(msg, e); + } + } + + /** + * Return the send Address so that if we have loopback messages we can + * detect if they where sent by this local sender and hence should be + * ignored. + */ + public InetSocketAddress getAddress() { + return sendAddr; + } + + /** + * Return the Host and Port of the sender. This is used to uniquely identify + * this instance in the cluster. + */ + public String getSenderHostPort() { + return senderHostPort; + } + + /** + * Send the packet. + */ + public int sendPacket(Packet packet) throws IOException { + + byte[] pktBytes = packet.getBytes(); + + if (logger.isLoggable(Level.FINE)){ + logger.fine("OUTGOING packet: " + packet.getPacketId() + " size:" + pktBytes.length); + } + + if (pktBytes.length > 65507){ + logger.warning("OUTGOING packet: " + packet.getPacketId() + " size:" + pktBytes.length + +" likely to be truncated using UDP with a MAXIMUM length of 65507"); + } + + DatagramPacket pack = new DatagramPacket(pktBytes, pktBytes.length, inetAddress, port); + sock.send(pack); + + return pktBytes.length; + } + + /** + * Send the list of Packets. + */ + public int sendPackets(List packets) throws IOException { + + int totalBytes = 0; + for (int i = 0; i < packets.size(); i++) { + totalBytes += sendPacket(packets.get(i)); + } + return totalBytes; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastStatus.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastStatus.java index 472512e59..bc5c8e8db 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastStatus.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/McastStatus.java @@ -1,155 +1,136 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.mcast; - -/** - * Gives an overall status of this Cluster instance. - *

- * Ideally you want to see relatively low Re-send statistics. - *

- * - * @author rbygrave - * - */ -public class McastStatus { - - private final long totalTxnEventsSent; - private final long totalTxnEventsReceived; - - private final long totalPacketsSent; - private final long totalPacketsResent; - private final long totalPacketsReceived; - - private final long totalBytesSent; - private final long totalBytesResent; - private final long totalBytesReceived; - - private final int currentGroupSize; - private final int outgoingPacketsCacheSize; - - private final long currentPacketId; - private final long minAckedPacketId; - private final String lastOutgoingAcks; - - public String getSummary() { - - StringBuilder sb = new StringBuilder(80); - sb.append("txnOut:").append(totalTxnEventsSent).append("; "); - sb.append("txnIn:").append(totalTxnEventsReceived).append("; "); - sb.append("outPackets:").append(totalPacketsSent).append("; "); - sb.append("outBytes:").append(totalBytesSent).append("; "); - sb.append("inPackets:").append(totalPacketsReceived).append("; "); - sb.append("inBytes:").append(totalBytesReceived).append("; "); - sb.append("resentPackets:").append(totalPacketsResent).append("; "); - sb.append("resentBytes:").append(totalBytesResent).append("; "); - sb.append("groupSize:").append(currentGroupSize).append("; "); - sb.append("cache:").append(outgoingPacketsCacheSize).append("; "); - sb.append("currentPacket:").append(currentPacketId).append("; "); - sb.append("minAckedPacket:").append(minAckedPacketId).append("; "); - sb.append("lastAck:").append(lastOutgoingAcks).append("; "); - - return sb.toString(); - } - - public McastStatus(int currentGroupSize, - int outgoingPacketsCacheSize, - long currentPacketId, - long minAckedPacketId, - String lastOutgoingAcks, - long totalTransEventsSent, - long totalTransEventsReceived, - long totalPacketsSent, - long totalPacketsResent, - long totalPacketsReceived, - long totalBytesSent, - long totalBytesResent, - long totalBytesReceived) { - - this.currentGroupSize = currentGroupSize; - this.outgoingPacketsCacheSize = outgoingPacketsCacheSize; - this.currentPacketId = currentPacketId; - this.minAckedPacketId = minAckedPacketId; - this.lastOutgoingAcks = lastOutgoingAcks; - this.totalTxnEventsSent = totalTransEventsSent; - this.totalTxnEventsReceived = totalTransEventsReceived; - this.totalPacketsSent = totalPacketsSent; - this.totalPacketsResent = totalPacketsResent; - this.totalPacketsReceived = totalPacketsReceived; - - this.totalBytesSent = totalBytesSent; - this.totalBytesResent = totalBytesResent; - this.totalBytesReceived = totalBytesReceived; - - } - - - public long getTotalTxnEventsReceived() { - return totalTxnEventsReceived; - } - - public long getTotalPacketsReceived() { - return totalPacketsReceived; - } - - public long getTotalBytesSent() { - return totalBytesSent; - } - - public long getTotalBytesResent() { - return totalBytesResent; - } - - public long getTotalBytesReceived() { - return totalBytesReceived; - } - - public String getLastOutgoingAcks() { - return lastOutgoingAcks; - } - - public int getOutgoingPacketsCacheSize() { - return outgoingPacketsCacheSize; - } - - public long getCurrentPacketId() { - return currentPacketId; - } - - public long getMinAckedPacketId() { - return minAckedPacketId; - } - - public long getTotalTxnEventsSent() { - return totalTxnEventsSent; - } - - public long getTotalPacketsSent() { - return totalPacketsSent; - } - - public long getTotalPacketsResent() { - return totalPacketsResent; - } - - public long getCurrentGroupSize() { - return currentGroupSize; - } - -} +package com.avaje.ebeaninternal.server.cluster.mcast; + +/** + * Gives an overall status of this Cluster instance. + *

+ * Ideally you want to see relatively low Re-send statistics. + *

+ * + * @author rbygrave + * + */ +public class McastStatus { + + private final long totalTxnEventsSent; + private final long totalTxnEventsReceived; + + private final long totalPacketsSent; + private final long totalPacketsResent; + private final long totalPacketsReceived; + + private final long totalBytesSent; + private final long totalBytesResent; + private final long totalBytesReceived; + + private final int currentGroupSize; + private final int outgoingPacketsCacheSize; + + private final long currentPacketId; + private final long minAckedPacketId; + private final String lastOutgoingAcks; + + public String getSummary() { + + StringBuilder sb = new StringBuilder(80); + sb.append("txnOut:").append(totalTxnEventsSent).append("; "); + sb.append("txnIn:").append(totalTxnEventsReceived).append("; "); + sb.append("outPackets:").append(totalPacketsSent).append("; "); + sb.append("outBytes:").append(totalBytesSent).append("; "); + sb.append("inPackets:").append(totalPacketsReceived).append("; "); + sb.append("inBytes:").append(totalBytesReceived).append("; "); + sb.append("resentPackets:").append(totalPacketsResent).append("; "); + sb.append("resentBytes:").append(totalBytesResent).append("; "); + sb.append("groupSize:").append(currentGroupSize).append("; "); + sb.append("cache:").append(outgoingPacketsCacheSize).append("; "); + sb.append("currentPacket:").append(currentPacketId).append("; "); + sb.append("minAckedPacket:").append(minAckedPacketId).append("; "); + sb.append("lastAck:").append(lastOutgoingAcks).append("; "); + + return sb.toString(); + } + + public McastStatus(int currentGroupSize, + int outgoingPacketsCacheSize, + long currentPacketId, + long minAckedPacketId, + String lastOutgoingAcks, + long totalTransEventsSent, + long totalTransEventsReceived, + long totalPacketsSent, + long totalPacketsResent, + long totalPacketsReceived, + long totalBytesSent, + long totalBytesResent, + long totalBytesReceived) { + + this.currentGroupSize = currentGroupSize; + this.outgoingPacketsCacheSize = outgoingPacketsCacheSize; + this.currentPacketId = currentPacketId; + this.minAckedPacketId = minAckedPacketId; + this.lastOutgoingAcks = lastOutgoingAcks; + this.totalTxnEventsSent = totalTransEventsSent; + this.totalTxnEventsReceived = totalTransEventsReceived; + this.totalPacketsSent = totalPacketsSent; + this.totalPacketsResent = totalPacketsResent; + this.totalPacketsReceived = totalPacketsReceived; + + this.totalBytesSent = totalBytesSent; + this.totalBytesResent = totalBytesResent; + this.totalBytesReceived = totalBytesReceived; + + } + + + public long getTotalTxnEventsReceived() { + return totalTxnEventsReceived; + } + + public long getTotalPacketsReceived() { + return totalPacketsReceived; + } + + public long getTotalBytesSent() { + return totalBytesSent; + } + + public long getTotalBytesResent() { + return totalBytesResent; + } + + public long getTotalBytesReceived() { + return totalBytesReceived; + } + + public String getLastOutgoingAcks() { + return lastOutgoingAcks; + } + + public int getOutgoingPacketsCacheSize() { + return outgoingPacketsCacheSize; + } + + public long getCurrentPacketId() { + return currentPacketId; + } + + public long getMinAckedPacketId() { + return minAckedPacketId; + } + + public long getTotalTxnEventsSent() { + return totalTxnEventsSent; + } + + public long getTotalPacketsSent() { + return totalPacketsSent; + } + + public long getTotalPacketsResent() { + return totalPacketsResent; + } + + public long getCurrentGroupSize() { + return currentGroupSize; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/Message.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/Message.java index e44775489..f6a2fc5b6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/Message.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/Message.java @@ -1,33 +1,14 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.mcast; - -import java.io.IOException; - -import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; - -public interface Message { - - public void writeBinaryMessage(BinaryMessageList msgList) throws IOException; - - public boolean isControlMessage(); - - public String getToHostPort(); -} +package com.avaje.ebeaninternal.server.cluster.mcast; + +import java.io.IOException; + +import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; + +public interface Message { + + public void writeBinaryMessage(BinaryMessageList msgList) throws IOException; + + public boolean isControlMessage(); + + public String getToHostPort(); +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageAck.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageAck.java index 1eaca1a7f..6266c0dd5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageAck.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageAck.java @@ -1,76 +1,57 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.mcast; - -import java.io.DataInput; -import java.io.DataOutputStream; -import java.io.IOException; - -import com.avaje.ebeaninternal.server.cluster.BinaryMessage; -import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; - -public class MessageAck implements Message { - - private final String toHostPort; - - private final long gotAllPacketId; - - public MessageAck(String toHostPort, long gotAllPacketId) { - this.toHostPort = toHostPort; - this.gotAllPacketId = gotAllPacketId; - } - - public String toString() { - return "Ack "+toHostPort+" "+gotAllPacketId; - } - - public boolean isControlMessage() { - return false; - } - - public String getToHostPort() { - return toHostPort; - } - - public long getGotAllPacketId() { - return gotAllPacketId; - } - - - public static MessageAck readBinaryMessage(DataInput dataInput) throws IOException { - - String hostPort = dataInput.readUTF(); - long gotAllPacketId = dataInput.readLong(); - return new MessageAck(hostPort, gotAllPacketId); - } - - public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { - - BinaryMessage m = new BinaryMessage(toHostPort.length() * 2 + 20); - - DataOutputStream os = m.getOs(); - os.writeInt(BinaryMessage.TYPE_MSGACK); - os.writeUTF(toHostPort); - os.writeLong(gotAllPacketId); - os.flush(); - - msgList.add(m); - } -} +package com.avaje.ebeaninternal.server.cluster.mcast; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +import com.avaje.ebeaninternal.server.cluster.BinaryMessage; +import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; + +public class MessageAck implements Message { + + private final String toHostPort; + + private final long gotAllPacketId; + + public MessageAck(String toHostPort, long gotAllPacketId) { + this.toHostPort = toHostPort; + this.gotAllPacketId = gotAllPacketId; + } + + public String toString() { + return "Ack "+toHostPort+" "+gotAllPacketId; + } + + public boolean isControlMessage() { + return false; + } + + public String getToHostPort() { + return toHostPort; + } + + public long getGotAllPacketId() { + return gotAllPacketId; + } + + + public static MessageAck readBinaryMessage(DataInput dataInput) throws IOException { + + String hostPort = dataInput.readUTF(); + long gotAllPacketId = dataInput.readLong(); + return new MessageAck(hostPort, gotAllPacketId); + } + + public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { + + BinaryMessage m = new BinaryMessage(toHostPort.length() * 2 + 20); + + DataOutputStream os = m.getOs(); + os.writeInt(BinaryMessage.TYPE_MSGACK); + os.writeUTF(toHostPort); + os.writeLong(gotAllPacketId); + os.flush(); + + msgList.add(m); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageControl.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageControl.java index e455db0ee..d5494d3aa 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageControl.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageControl.java @@ -1,92 +1,73 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.mcast; - -import java.io.DataInput; -import java.io.DataOutputStream; -import java.io.IOException; - -import com.avaje.ebeaninternal.server.cluster.BinaryMessage; -import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; - -public class MessageControl implements Message { - - public static final short TYPE_JOIN = 1; - public static final short TYPE_LEAVE = 2; - public static final short TYPE_PING = 3; - public static final short TYPE_JOINRESPONSE = 7; - public static final short TYPE_PINGRESPONSE = 8; - - private final short controlType; - private final String fromHostPort; - - public static MessageControl readBinaryMessage(DataInput dataInput) throws IOException { - short controlType = dataInput.readShort(); - String hostPort = dataInput.readUTF(); - return new MessageControl(controlType, hostPort); - } - - public MessageControl(short controlType, String helloFromHostPort) { - this.controlType = controlType; - this.fromHostPort = helloFromHostPort; - } - - - public String toString() { - switch (controlType) { - case TYPE_JOIN: return "Join "+fromHostPort; - case TYPE_LEAVE: return "Leave "+fromHostPort; - case TYPE_PING: return "Ping "+fromHostPort; - case TYPE_PINGRESPONSE: return "PingResponse "+fromHostPort; - - default: - throw new RuntimeException("Invalid controlType "+controlType); - } - } - - public boolean isControlMessage() { - return true; - } - - public short getControlType() { - return controlType; - } - - public String getToHostPort() { - return "*"; - } - - public String getFromHostPort() { - return fromHostPort; - } - - public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { - - BinaryMessage m = new BinaryMessage(fromHostPort.length() * 2 + 10); - - DataOutputStream os = m.getOs(); - os.writeInt(BinaryMessage.TYPE_MSGCONTROL); - os.writeShort(controlType); - os.writeUTF(fromHostPort); - os.flush(); - - msgList.add(m); - } -} +package com.avaje.ebeaninternal.server.cluster.mcast; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +import com.avaje.ebeaninternal.server.cluster.BinaryMessage; +import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; + +public class MessageControl implements Message { + + public static final short TYPE_JOIN = 1; + public static final short TYPE_LEAVE = 2; + public static final short TYPE_PING = 3; + public static final short TYPE_JOINRESPONSE = 7; + public static final short TYPE_PINGRESPONSE = 8; + + private final short controlType; + private final String fromHostPort; + + public static MessageControl readBinaryMessage(DataInput dataInput) throws IOException { + short controlType = dataInput.readShort(); + String hostPort = dataInput.readUTF(); + return new MessageControl(controlType, hostPort); + } + + public MessageControl(short controlType, String helloFromHostPort) { + this.controlType = controlType; + this.fromHostPort = helloFromHostPort; + } + + + public String toString() { + switch (controlType) { + case TYPE_JOIN: return "Join "+fromHostPort; + case TYPE_LEAVE: return "Leave "+fromHostPort; + case TYPE_PING: return "Ping "+fromHostPort; + case TYPE_PINGRESPONSE: return "PingResponse "+fromHostPort; + + default: + throw new RuntimeException("Invalid controlType "+controlType); + } + } + + public boolean isControlMessage() { + return true; + } + + public short getControlType() { + return controlType; + } + + public String getToHostPort() { + return "*"; + } + + public String getFromHostPort() { + return fromHostPort; + } + + public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { + + BinaryMessage m = new BinaryMessage(fromHostPort.length() * 2 + 10); + + DataOutputStream os = m.getOs(); + os.writeInt(BinaryMessage.TYPE_MSGCONTROL); + os.writeShort(controlType); + os.writeUTF(fromHostPort); + os.flush(); + + msgList.add(m); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageResend.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageResend.java index 80f8f5f77..700c8a2a3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageResend.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/MessageResend.java @@ -1,96 +1,77 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.mcast; - -import java.io.DataInput; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import com.avaje.ebeaninternal.server.cluster.BinaryMessage; -import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; - -public class MessageResend implements Message { - - private final String toHostPort; - - private final List resendPacketIds; - - public MessageResend(String toHostPort, List resendPacketIds) { - this.toHostPort = toHostPort; - this.resendPacketIds = resendPacketIds; - } - - public MessageResend(String toHostPort) { - this(toHostPort, new ArrayList(4)); - } - - public String toString() { - return "Resend "+toHostPort+" "+resendPacketIds; - } - - public boolean isControlMessage() { - return false; - } - - public String getToHostPort() { - return toHostPort; - } - - public void add(long packetId){ - resendPacketIds.add(Long.valueOf(packetId)); - } - - public List getResendPacketIds() { - return resendPacketIds; - } - - public static MessageResend readBinaryMessage(DataInput dataInput) throws IOException { - - String hostPort = dataInput.readUTF(); - - MessageResend msg = new MessageResend(hostPort); - - int numberOfPacketIds = dataInput.readInt(); - for (int i = 0; i < numberOfPacketIds; i++) { - long packetId = dataInput.readLong(); - msg.add(packetId); - } - - return msg; - } - - public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { - - BinaryMessage m = new BinaryMessage(toHostPort.length() * 2 + 20); - - DataOutputStream os = m.getOs(); - os.writeInt(BinaryMessage.TYPE_MSGRESEND); - os.writeUTF(toHostPort); - os.writeInt(resendPacketIds.size()); - for (int i = 0; i < resendPacketIds.size(); i++) { - Long packetId = resendPacketIds.get(i); - os.writeLong(packetId.longValue()); - } - os.flush(); - msgList.add(m); - } -} +package com.avaje.ebeaninternal.server.cluster.mcast; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import com.avaje.ebeaninternal.server.cluster.BinaryMessage; +import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; + +public class MessageResend implements Message { + + private final String toHostPort; + + private final List resendPacketIds; + + public MessageResend(String toHostPort, List resendPacketIds) { + this.toHostPort = toHostPort; + this.resendPacketIds = resendPacketIds; + } + + public MessageResend(String toHostPort) { + this(toHostPort, new ArrayList(4)); + } + + public String toString() { + return "Resend "+toHostPort+" "+resendPacketIds; + } + + public boolean isControlMessage() { + return false; + } + + public String getToHostPort() { + return toHostPort; + } + + public void add(long packetId){ + resendPacketIds.add(Long.valueOf(packetId)); + } + + public List getResendPacketIds() { + return resendPacketIds; + } + + public static MessageResend readBinaryMessage(DataInput dataInput) throws IOException { + + String hostPort = dataInput.readUTF(); + + MessageResend msg = new MessageResend(hostPort); + + int numberOfPacketIds = dataInput.readInt(); + for (int i = 0; i < numberOfPacketIds; i++) { + long packetId = dataInput.readLong(); + msg.add(packetId); + } + + return msg; + } + + public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { + + BinaryMessage m = new BinaryMessage(toHostPort.length() * 2 + 20); + + DataOutputStream os = m.getOs(); + os.writeInt(BinaryMessage.TYPE_MSGRESEND); + os.writeUTF(toHostPort); + os.writeInt(resendPacketIds.size()); + for (int i = 0; i < resendPacketIds.size(); i++) { + Long packetId = resendPacketIds.get(i); + os.writeLong(packetId.longValue()); + } + os.flush(); + msgList.add(m); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsAcked.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsAcked.java index 28a5f1bd4..f20405e00 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsAcked.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsAcked.java @@ -1,125 +1,106 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.mcast; - -import java.util.HashMap; -import java.util.Map; - -public class OutgoingPacketsAcked { - - private long minimumGotAllPacketId; - - private Map recievedByMap = new HashMap(); - - public int getGroupSize() { - synchronized (this) { - return recievedByMap.size(); - } - } - - public long getMinimumGotAllPacketId() { - synchronized (this) { - return minimumGotAllPacketId; - } - } - - public void removeMember(String groupMember){ - synchronized (this) { - recievedByMap.remove(groupMember); - resetGotAllMin(); - } - } - - private boolean resetGotAllMin() { - - long tempMin; - if (recievedByMap.isEmpty()){ - //System.out.println(" -- -- -- -- "+recievedByMap.isEmpty()); - tempMin = Long.MAX_VALUE; - } else { - tempMin = Long.MAX_VALUE; - } - - for (GroupMemberAck groupMemAck : recievedByMap.values()) { - long memberMin = groupMemAck.getGotAllPacketId(); - if (memberMin < tempMin){ - //System.out.println(" -- new tmpMin "+memberMin); - tempMin = memberMin; - } - } - - if (tempMin != minimumGotAllPacketId) { - minimumGotAllPacketId = tempMin; - return true; - } else { - return false; - } - } - - public long receivedAck(String groupMember, MessageAck ack) { - - synchronized (this) { - - boolean checkMin = false; - - GroupMemberAck groupMemberAck = recievedByMap.get(groupMember); - if (groupMemberAck == null) { - //System.out.println(" -- new groupMemberAck"); - groupMemberAck = new GroupMemberAck(); - groupMemberAck.setIfBigger(ack.getGotAllPacketId()); - recievedByMap.put(groupMember, groupMemberAck); - checkMin = true; - } else { - checkMin = groupMemberAck.getGotAllPacketId() == minimumGotAllPacketId; - //System.out.println(" -- existing groupMemberAck, checkMin:"+checkMin+" "+groupMemberAck.getGotAllPacketId()); - groupMemberAck.setIfBigger(ack.getGotAllPacketId()); - } - - boolean minChanged = false; - - //System.out.println(" -- checkMin:"+checkMin+" minimumGotAllPacketId:"+minimumGotAllPacketId); - if (checkMin || minimumGotAllPacketId == 0){ - - minChanged = resetGotAllMin(); - //System.out.println(" -- minChanged:"+minChanged+" minimumGotAllPacketId:"+minimumGotAllPacketId); - } - - return minChanged ? minimumGotAllPacketId : 0; - } - } - - private static class GroupMemberAck { - - private long gotAllPacketId; - - private GroupMemberAck() { - } - - private long getGotAllPacketId() { - return gotAllPacketId; - } - - private void setIfBigger(long newGotAll) { - if (newGotAll > gotAllPacketId) { - gotAllPacketId = newGotAll; - } - } - } -} +package com.avaje.ebeaninternal.server.cluster.mcast; + +import java.util.HashMap; +import java.util.Map; + +public class OutgoingPacketsAcked { + + private long minimumGotAllPacketId; + + private Map recievedByMap = new HashMap(); + + public int getGroupSize() { + synchronized (this) { + return recievedByMap.size(); + } + } + + public long getMinimumGotAllPacketId() { + synchronized (this) { + return minimumGotAllPacketId; + } + } + + public void removeMember(String groupMember){ + synchronized (this) { + recievedByMap.remove(groupMember); + resetGotAllMin(); + } + } + + private boolean resetGotAllMin() { + + long tempMin; + if (recievedByMap.isEmpty()){ + //System.out.println(" -- -- -- -- "+recievedByMap.isEmpty()); + tempMin = Long.MAX_VALUE; + } else { + tempMin = Long.MAX_VALUE; + } + + for (GroupMemberAck groupMemAck : recievedByMap.values()) { + long memberMin = groupMemAck.getGotAllPacketId(); + if (memberMin < tempMin){ + //System.out.println(" -- new tmpMin "+memberMin); + tempMin = memberMin; + } + } + + if (tempMin != minimumGotAllPacketId) { + minimumGotAllPacketId = tempMin; + return true; + } else { + return false; + } + } + + public long receivedAck(String groupMember, MessageAck ack) { + + synchronized (this) { + + boolean checkMin = false; + + GroupMemberAck groupMemberAck = recievedByMap.get(groupMember); + if (groupMemberAck == null) { + //System.out.println(" -- new groupMemberAck"); + groupMemberAck = new GroupMemberAck(); + groupMemberAck.setIfBigger(ack.getGotAllPacketId()); + recievedByMap.put(groupMember, groupMemberAck); + checkMin = true; + } else { + checkMin = groupMemberAck.getGotAllPacketId() == minimumGotAllPacketId; + //System.out.println(" -- existing groupMemberAck, checkMin:"+checkMin+" "+groupMemberAck.getGotAllPacketId()); + groupMemberAck.setIfBigger(ack.getGotAllPacketId()); + } + + boolean minChanged = false; + + //System.out.println(" -- checkMin:"+checkMin+" minimumGotAllPacketId:"+minimumGotAllPacketId); + if (checkMin || minimumGotAllPacketId == 0){ + + minChanged = resetGotAllMin(); + //System.out.println(" -- minChanged:"+minChanged+" minimumGotAllPacketId:"+minimumGotAllPacketId); + } + + return minChanged ? minimumGotAllPacketId : 0; + } + } + + private static class GroupMemberAck { + + private long gotAllPacketId; + + private GroupMemberAck() { + } + + private long getGotAllPacketId() { + return gotAllPacketId; + } + + private void setIfBigger(long newGotAll) { + if (newGotAll > gotAllPacketId) { + gotAllPacketId = newGotAll; + } + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsCache.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsCache.java index a002f1602..be5c103c3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsCache.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/mcast/OutgoingPacketsCache.java @@ -1,85 +1,66 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.mcast; - -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import com.avaje.ebeaninternal.server.cluster.Packet; - -/** - * Cache of the outgoing packets. - *

- * These are held until we receive ACKs from the other members of the cluster to - * say they have received the packets. - *

- * - * @author rbygrave - * - */ -public class OutgoingPacketsCache { - - private final Map packetMap = new TreeMap(); - - public int size() { - return packetMap.size(); - } - - public Packet getPacket(Long packetId) { - return packetMap.get(packetId); - } - - public String toString() { - return packetMap.keySet().toString(); - } - - /** - * Remove the packet when we give up trying to send it out. - */ - public void remove(Packet packet) { - packetMap.remove(packet.getPacketId()); - } - - public void registerPackets(List packets) { - for (int i = 0; i < packets.size(); i++) { - Packet p = packets.get(i); - packetMap.put(p.getPacketId(), p); - } - } - - public int trimAll() { - int size = packetMap.size(); - packetMap.clear(); - return size; - } - - public void trimAcknowledgedMessages(long minAcked) { - Iterator it = packetMap.keySet().iterator(); - while (it.hasNext()) { - Long pktId = it.next(); - if (minAcked >= pktId.longValue()) { - it.remove(); - } - } - } - -} +package com.avaje.ebeaninternal.server.cluster.mcast; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import com.avaje.ebeaninternal.server.cluster.Packet; + +/** + * Cache of the outgoing packets. + *

+ * These are held until we receive ACKs from the other members of the cluster to + * say they have received the packets. + *

+ * + * @author rbygrave + * + */ +public class OutgoingPacketsCache { + + private final Map packetMap = new TreeMap(); + + public int size() { + return packetMap.size(); + } + + public Packet getPacket(Long packetId) { + return packetMap.get(packetId); + } + + public String toString() { + return packetMap.keySet().toString(); + } + + /** + * Remove the packet when we give up trying to send it out. + */ + public void remove(Packet packet) { + packetMap.remove(packet.getPacketId()); + } + + public void registerPackets(List packets) { + for (int i = 0; i < packets.size(); i++) { + Packet p = packets.get(i); + packetMap.put(p.getPacketId(), p); + } + } + + public int trimAll() { + int size = packetMap.size(); + packetMap.clear(); + return size; + } + + public void trimAcknowledgedMessages(long minAcked) { + Iterator it = packetMap.keySet().iterator(); + while (it.hasNext()) { + Long pktId = it.next(); + if (minAcked >= pktId.longValue()) { + it.remove(); + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/RequestProcessor.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/RequestProcessor.java index d4a286a49..6b9f4ca97 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/RequestProcessor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/RequestProcessor.java @@ -1,76 +1,59 @@ -/** - * Copyright (C) 2006 Robin Bygrave - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.socket; - -import java.io.IOException; -import java.net.Socket; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * This parses and dispatches a request to the appropriate handler. - *

- * Looks up the appropriate RequestHandler - * and then gets it to process the Client request.

- *

- * Note that this is a Runnable because it is assigned to the ThreadPool. - */ -class RequestProcessor implements Runnable { - - private static final Logger logger = Logger.getLogger(RequestProcessor.class.getName()); - - private final Socket clientSocket; - - private final SocketClusterBroadcast owner; - - /** - * Create including the Listener (used to lookup the Request Handler) and - * the socket itself. - */ - public RequestProcessor(SocketClusterBroadcast owner, Socket clientSocket) { - this.clientSocket = clientSocket; - this.owner = owner; - } - - /** - * This will parse out the command. Lookup the appropriate Handler and - * pass the information to the handler for processing. - *

Dev Note: the command parsing is processed here so that it is preformed - * by the assigned thread rather than the listeners thread.

- */ - public void run() { - try { - SocketConnection sc = new SocketConnection(clientSocket); - - while(true){ - if (owner.process(sc)) { - // got the offline message or timeout - break; - } - } - sc.disconnect(); - - } catch (IOException e) { - logger.log(Level.SEVERE, null, e); - } catch (ClassNotFoundException e) { - logger.log(Level.SEVERE, null, e); - } - } - - -}; +package com.avaje.ebeaninternal.server.cluster.socket; + +import java.io.IOException; +import java.net.Socket; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This parses and dispatches a request to the appropriate handler. + *

+ * Looks up the appropriate RequestHandler + * and then gets it to process the Client request.

+ *

+ * Note that this is a Runnable because it is assigned to the ThreadPool. + */ +class RequestProcessor implements Runnable { + + private static final Logger logger = Logger.getLogger(RequestProcessor.class.getName()); + + private final Socket clientSocket; + + private final SocketClusterBroadcast owner; + + /** + * Create including the Listener (used to lookup the Request Handler) and + * the socket itself. + */ + public RequestProcessor(SocketClusterBroadcast owner, Socket clientSocket) { + this.clientSocket = clientSocket; + this.owner = owner; + } + + /** + * This will parse out the command. Lookup the appropriate Handler and + * pass the information to the handler for processing. + *

Dev Note: the command parsing is processed here so that it is preformed + * by the assigned thread rather than the listeners thread.

+ */ + public void run() { + try { + SocketConnection sc = new SocketConnection(clientSocket); + + while(true){ + if (owner.process(sc)) { + // got the offline message or timeout + break; + } + } + sc.disconnect(); + + } catch (IOException e) { + logger.log(Level.SEVERE, null, e); + } catch (ClassNotFoundException e) { + logger.log(Level.SEVERE, null, e); + } + } + + +}; diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClient.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClient.java index f79e10590..7ab8f5764 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClient.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClient.java @@ -1,151 +1,134 @@ -/** - * Copyright (C) 2006 Robin Bygrave - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.socket; - -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.util.logging.Level; -import java.util.logging.Logger; - - -/** - * The client side of the socket clustering. - */ -class SocketClient { - - private static final Logger logger = Logger.getLogger(SocketClient.class.getName()); - - private final InetSocketAddress address; - - private final String hostPort; - - private boolean online; - - private Socket socket; - private OutputStream os; - private ObjectOutputStream oos; - - /** - * Construct with an IP address and port. - */ - public SocketClient(InetSocketAddress address) { - this.address = address; - this.hostPort = address.getHostName()+":"+address.getPort(); - } - - public String getHostPort() { - return hostPort; - } - - public int getPort() { - return address.getPort(); - } - - public boolean isOnline() { - return online; - } - - public void setOnline(boolean online) throws IOException { - if (online){ - setOnline(); - } else { - disconnect(); - } - } - - - /** - * Set whether the client is thought to be online. - */ - private void setOnline() throws IOException { - connect(); - this.online = true; - } - - public void reconnect() throws IOException { - disconnect(); - connect(); - } - - private void connect() throws IOException { - if (socket != null){ - throw new IllegalStateException("Already got a socket connection?"); - } - Socket s = new Socket(); - s.setKeepAlive(true); - s.connect(address); - - this.socket = s; - this.os = socket.getOutputStream(); - } - - public void disconnect() { - this.online = false; - if (socket != null){ - - try { - socket.close(); - } catch (IOException e) { - String msg = "Error disconnecting from Cluster member "+hostPort; - logger.log(Level.INFO, msg, e); - } - - os = null; - oos = null; - socket = null; - } - } - - public boolean register(SocketClusterMessage registerMsg) { - - try { - setOnline(); - send(registerMsg); - return true; - } catch (IOException e) { - disconnect(); - return false; - } - } - - public boolean send(SocketClusterMessage msg) throws IOException { - - if (online){ - writeObject(msg); - return true; - - } else { - return false; - } - - } - - private void writeObject(Object object) throws IOException { - if (oos == null){ - this.oos = new ObjectOutputStream(os); - } - oos.writeObject(object); - oos.flush(); - } - - - -} +package com.avaje.ebeaninternal.server.cluster.socket; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.logging.Level; +import java.util.logging.Logger; + + +/** + * The client side of the socket clustering. + */ +class SocketClient { + + private static final Logger logger = Logger.getLogger(SocketClient.class.getName()); + + private final InetSocketAddress address; + + private final String hostPort; + + private boolean online; + + private Socket socket; + private OutputStream os; + private ObjectOutputStream oos; + + /** + * Construct with an IP address and port. + */ + public SocketClient(InetSocketAddress address) { + this.address = address; + this.hostPort = address.getHostName()+":"+address.getPort(); + } + + public String getHostPort() { + return hostPort; + } + + public int getPort() { + return address.getPort(); + } + + public boolean isOnline() { + return online; + } + + public void setOnline(boolean online) throws IOException { + if (online){ + setOnline(); + } else { + disconnect(); + } + } + + + /** + * Set whether the client is thought to be online. + */ + private void setOnline() throws IOException { + connect(); + this.online = true; + } + + public void reconnect() throws IOException { + disconnect(); + connect(); + } + + private void connect() throws IOException { + if (socket != null){ + throw new IllegalStateException("Already got a socket connection?"); + } + Socket s = new Socket(); + s.setKeepAlive(true); + s.connect(address); + + this.socket = s; + this.os = socket.getOutputStream(); + } + + public void disconnect() { + this.online = false; + if (socket != null){ + + try { + socket.close(); + } catch (IOException e) { + String msg = "Error disconnecting from Cluster member "+hostPort; + logger.log(Level.INFO, msg, e); + } + + os = null; + oos = null; + socket = null; + } + } + + public boolean register(SocketClusterMessage registerMsg) { + + try { + setOnline(); + send(registerMsg); + return true; + } catch (IOException e) { + disconnect(); + return false; + } + } + + public boolean send(SocketClusterMessage msg) throws IOException { + + if (online){ + writeObject(msg); + return true; + + } else { + return false; + } + + } + + private void writeObject(Object object) throws IOException { + if (oos == null){ + this.oos = new ObjectOutputStream(os); + } + oos.writeObject(object); + oos.flush(); + } + + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterBroadcast.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterBroadcast.java index b2f403636..971d217e8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterBroadcast.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterBroadcast.java @@ -1,265 +1,248 @@ -/** - * Copyright (C) 2006 Robin Bygrave - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.socket; - -import java.io.IOException; -import java.io.InterruptedIOException; -import java.net.InetSocketAddress; -import java.util.HashMap; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.server.cluster.ClusterBroadcast; -import com.avaje.ebeaninternal.server.cluster.ClusterManager; -import com.avaje.ebeaninternal.server.cluster.DataHolder; -import com.avaje.ebeaninternal.server.cluster.SerialiseTransactionHelper; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; -import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; - -/** - * Broadcast messages across the cluster using sockets. - */ -public class SocketClusterBroadcast implements ClusterBroadcast { - - private static final Logger logger = Logger.getLogger(SocketClusterBroadcast.class.getName()); - - private final SocketClient local; - - private final HashMap clientMap; - - private final SocketClusterListener listener; - - private SocketClient[] members; - - private ClusterManager clusterManager; - - private final TxnSerialiseHelper txnSerialiseHelper = new TxnSerialiseHelper(); - - private final AtomicInteger txnOutgoing = new AtomicInteger(); - private final AtomicInteger txnIncoming = new AtomicInteger(); - - - public SocketClusterBroadcast( ){ - - String localHostPort = GlobalProperties.get("ebean.cluster.local", null); - String members = GlobalProperties.get("ebean.cluster.members", null); - - logger.info("Clustering using Sockets local["+localHostPort+"] members["+members+"]"); - - this.local = new SocketClient(parseFullName(localHostPort)); - this.clientMap = new HashMap(); - - String[] memArray = StringHelper.delimitedToArray(members, ",", false); - for (int i = 0; i < memArray.length; i++) { - InetSocketAddress member = parseFullName(memArray[i]); - SocketClient client = new SocketClient(member); - if (!local.getHostPort().equalsIgnoreCase(client.getHostPort())) { - // don't add the local one ... - clientMap.put(client.getHostPort(), client); - } - } - - this.members = clientMap.values().toArray(new SocketClient[clientMap.size()]); - this.listener = new SocketClusterListener(this, local.getPort()); - } - - /** - * Return the current status of this instance. - */ - public SocketClusterStatus getStatus() { - - // count of online members - int currentGroupSize = 0; - for (int i = 0; i < members.length; i++) { - if (members[i].isOnline()) { - ++currentGroupSize; - } - } - int txnIn = txnIncoming.get(); - int txnOut = txnOutgoing.get(); - - return new SocketClusterStatus(currentGroupSize, txnIn, txnOut); - } - - public void startup(ClusterManager clusterManager) { - - this.clusterManager = clusterManager; - try { - listener.startListening(); - register(); - - } catch (IOException e) { - throw new PersistenceException(e); - } - } - - public void shutdown() { - deregister(); - listener.shutdown(); - } - - /** - * Register with all the other members of the Cluster. - */ - private void register() { - - SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), true); - - for (int i = 0; i < members.length; i++) { - boolean online = members[i].register(h); - - String msg = "Cluster Member ["+members[i].getHostPort()+"] online["+online+"]"; - logger.info(msg); - } - } - - protected void setMemberOnline(String fullName, boolean online) throws IOException { - synchronized (clientMap) { - String msg = "Cluster Member ["+fullName+"] online["+online+"]"; - logger.info(msg); - SocketClient member = clientMap.get(fullName); - member.setOnline(online); - } - } - - private void send(SocketClient client, SocketClusterMessage msg) { - - try { - // alternative would be to connect/disconnect here - // but prefer to use keepalive - client.send(msg); - - } catch (Exception ex){ - logger.log(Level.SEVERE, "Error sending message", ex); - try { - client.reconnect(); - } catch (IOException e) { - logger.log(Level.SEVERE, "Error trying to reconnect", ex); - } - } - } - - /** - * Send the payload to all the members of the cluster. - */ - public void broadcast(RemoteTransactionEvent remoteTransEvent) { - try { - - txnOutgoing.incrementAndGet(); - DataHolder dataHolder = txnSerialiseHelper.createDataHolder(remoteTransEvent); - SocketClusterMessage msg = SocketClusterMessage.transEvent(dataHolder); - broadcast(msg); - } catch (Exception e){ - String msg = "Error sending RemoteTransactionEvent "+remoteTransEvent+" to cluster members."; - logger.log(Level.SEVERE, msg, e); - } - } - - protected void broadcast(SocketClusterMessage msg) { - - for (int i = 0; i < members.length; i++) { - send(members[i], msg); - } - } - - /** - * Leave the cluster. - */ - private void deregister() { - - SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), false); - broadcast(h); - - for (int i = 0; i < members.length; i++) { - members[i].disconnect(); - } - } - - /** - * Process a Cluster message. - */ - protected boolean process(SocketConnection request) throws IOException, ClassNotFoundException { - - try { - SocketClusterMessage h = (SocketClusterMessage)request.readObject(); - - if (h.isRegisterEvent()){ - setMemberOnline(h.getRegisterHost(), h.isRegister()); - - } else { - txnIncoming.incrementAndGet(); - DataHolder dataHolder = h.getDataHolder(); - RemoteTransactionEvent transEvent = txnSerialiseHelper.read(dataHolder); - transEvent.run(); - } - - if (h.isRegisterEvent() && !h.isRegister()){ - // instance shutting down - return true; - } else { - return false; - } - } catch (InterruptedIOException e) { - String msg = "Timeout waiting for message"; - logger.log(Level.INFO, msg, e); - try { - request.disconnect(); - } catch (IOException ex){ - logger.log(Level.INFO, "Error disconnecting after timeout", ex); - } - return true; - } - } - - - /** - * Parse a host:port into a InetSocketAddress. - */ - private InetSocketAddress parseFullName(String hostAndPort) { - - try { - hostAndPort = hostAndPort.trim(); - int colonPos = hostAndPort.indexOf(":"); - if (colonPos == -1) { - String msg = "No colon \":\" in "+hostAndPort; - throw new IllegalArgumentException(msg); - } - String host = hostAndPort.substring(0, colonPos); - String sPort = hostAndPort.substring(colonPos + 1, hostAndPort.length()); - int port = Integer.parseInt(sPort); - - return new InetSocketAddress(host, port); - - } catch (Exception ex){ - throw new RuntimeException("Error parsing ["+hostAndPort+"] for the form [host:port]", ex); - } - } - - class TxnSerialiseHelper extends SerialiseTransactionHelper { - - @Override - public SpiEbeanServer getEbeanServer(String serverName) { - return (SpiEbeanServer)clusterManager.getServer(serverName); - } - } -} +package com.avaje.ebeaninternal.server.cluster.socket; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.cluster.ClusterBroadcast; +import com.avaje.ebeaninternal.server.cluster.ClusterManager; +import com.avaje.ebeaninternal.server.cluster.DataHolder; +import com.avaje.ebeaninternal.server.cluster.SerialiseTransactionHelper; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; + +/** + * Broadcast messages across the cluster using sockets. + */ +public class SocketClusterBroadcast implements ClusterBroadcast { + + private static final Logger logger = Logger.getLogger(SocketClusterBroadcast.class.getName()); + + private final SocketClient local; + + private final HashMap clientMap; + + private final SocketClusterListener listener; + + private SocketClient[] members; + + private ClusterManager clusterManager; + + private final TxnSerialiseHelper txnSerialiseHelper = new TxnSerialiseHelper(); + + private final AtomicInteger txnOutgoing = new AtomicInteger(); + private final AtomicInteger txnIncoming = new AtomicInteger(); + + + public SocketClusterBroadcast( ){ + + String localHostPort = GlobalProperties.get("ebean.cluster.local", null); + String members = GlobalProperties.get("ebean.cluster.members", null); + + logger.info("Clustering using Sockets local["+localHostPort+"] members["+members+"]"); + + this.local = new SocketClient(parseFullName(localHostPort)); + this.clientMap = new HashMap(); + + String[] memArray = StringHelper.delimitedToArray(members, ",", false); + for (int i = 0; i < memArray.length; i++) { + InetSocketAddress member = parseFullName(memArray[i]); + SocketClient client = new SocketClient(member); + if (!local.getHostPort().equalsIgnoreCase(client.getHostPort())) { + // don't add the local one ... + clientMap.put(client.getHostPort(), client); + } + } + + this.members = clientMap.values().toArray(new SocketClient[clientMap.size()]); + this.listener = new SocketClusterListener(this, local.getPort()); + } + + /** + * Return the current status of this instance. + */ + public SocketClusterStatus getStatus() { + + // count of online members + int currentGroupSize = 0; + for (int i = 0; i < members.length; i++) { + if (members[i].isOnline()) { + ++currentGroupSize; + } + } + int txnIn = txnIncoming.get(); + int txnOut = txnOutgoing.get(); + + return new SocketClusterStatus(currentGroupSize, txnIn, txnOut); + } + + public void startup(ClusterManager clusterManager) { + + this.clusterManager = clusterManager; + try { + listener.startListening(); + register(); + + } catch (IOException e) { + throw new PersistenceException(e); + } + } + + public void shutdown() { + deregister(); + listener.shutdown(); + } + + /** + * Register with all the other members of the Cluster. + */ + private void register() { + + SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), true); + + for (int i = 0; i < members.length; i++) { + boolean online = members[i].register(h); + + String msg = "Cluster Member ["+members[i].getHostPort()+"] online["+online+"]"; + logger.info(msg); + } + } + + protected void setMemberOnline(String fullName, boolean online) throws IOException { + synchronized (clientMap) { + String msg = "Cluster Member ["+fullName+"] online["+online+"]"; + logger.info(msg); + SocketClient member = clientMap.get(fullName); + member.setOnline(online); + } + } + + private void send(SocketClient client, SocketClusterMessage msg) { + + try { + // alternative would be to connect/disconnect here + // but prefer to use keepalive + client.send(msg); + + } catch (Exception ex){ + logger.log(Level.SEVERE, "Error sending message", ex); + try { + client.reconnect(); + } catch (IOException e) { + logger.log(Level.SEVERE, "Error trying to reconnect", ex); + } + } + } + + /** + * Send the payload to all the members of the cluster. + */ + public void broadcast(RemoteTransactionEvent remoteTransEvent) { + try { + + txnOutgoing.incrementAndGet(); + DataHolder dataHolder = txnSerialiseHelper.createDataHolder(remoteTransEvent); + SocketClusterMessage msg = SocketClusterMessage.transEvent(dataHolder); + broadcast(msg); + } catch (Exception e){ + String msg = "Error sending RemoteTransactionEvent "+remoteTransEvent+" to cluster members."; + logger.log(Level.SEVERE, msg, e); + } + } + + protected void broadcast(SocketClusterMessage msg) { + + for (int i = 0; i < members.length; i++) { + send(members[i], msg); + } + } + + /** + * Leave the cluster. + */ + private void deregister() { + + SocketClusterMessage h = SocketClusterMessage.register(local.getHostPort(), false); + broadcast(h); + + for (int i = 0; i < members.length; i++) { + members[i].disconnect(); + } + } + + /** + * Process a Cluster message. + */ + protected boolean process(SocketConnection request) throws IOException, ClassNotFoundException { + + try { + SocketClusterMessage h = (SocketClusterMessage)request.readObject(); + + if (h.isRegisterEvent()){ + setMemberOnline(h.getRegisterHost(), h.isRegister()); + + } else { + txnIncoming.incrementAndGet(); + DataHolder dataHolder = h.getDataHolder(); + RemoteTransactionEvent transEvent = txnSerialiseHelper.read(dataHolder); + transEvent.run(); + } + + if (h.isRegisterEvent() && !h.isRegister()){ + // instance shutting down + return true; + } else { + return false; + } + } catch (InterruptedIOException e) { + String msg = "Timeout waiting for message"; + logger.log(Level.INFO, msg, e); + try { + request.disconnect(); + } catch (IOException ex){ + logger.log(Level.INFO, "Error disconnecting after timeout", ex); + } + return true; + } + } + + + /** + * Parse a host:port into a InetSocketAddress. + */ + private InetSocketAddress parseFullName(String hostAndPort) { + + try { + hostAndPort = hostAndPort.trim(); + int colonPos = hostAndPort.indexOf(":"); + if (colonPos == -1) { + String msg = "No colon \":\" in "+hostAndPort; + throw new IllegalArgumentException(msg); + } + String host = hostAndPort.substring(0, colonPos); + String sPort = hostAndPort.substring(colonPos + 1, hostAndPort.length()); + int port = Integer.parseInt(sPort); + + return new InetSocketAddress(host, port); + + } catch (Exception ex){ + throw new RuntimeException("Error parsing ["+hostAndPort+"] for the form [host:port]", ex); + } + } + + class TxnSerialiseHelper extends SerialiseTransactionHelper { + + @Override + public SpiEbeanServer getEbeanServer(String serverName) { + return (SpiEbeanServer)clusterManager.getServer(serverName); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterListener.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterListener.java index ba3fc6529..c9c049c80 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterListener.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterListener.java @@ -1,181 +1,164 @@ -/** - * Copyright (C) 2006 Robin Bygrave - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.socket; - -import java.io.IOException; -import java.io.InterruptedIOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebeaninternal.server.lib.thread.ThreadPool; -import com.avaje.ebeaninternal.server.lib.thread.ThreadPoolManager; - -/** - * Serverside multithreaded socket listener. Accepts connections and dispatches - * them to an appropriate handler. - *

- * This is designed as a single port listener, where part of the connection - * protocol determines which service the client is requesting (rather than a - * port per service). - *

- *

- * It has its own daemon background thread that handles the accept() loop on the - * ServerSocket. - *

- */ -class SocketClusterListener implements Runnable { - - private static final Logger logger = Logger.getLogger(SocketClusterListener.class.getName()); - - /** - * The port the SocketListener uses. - */ - private final int port; - - /** - * The length of the socket accept timeout. - */ - private final int listenTimeout = 60000; - - /** - * The server socket used to listen for requests. - */ - private final ServerSocket serverListenSocket; - - /** - * The listening thread. - */ - private final Thread listenerThread; - - /** - * The pool of threads that actually do the parsing execution of requests. - */ - private final ThreadPool threadPool; - - private final SocketClusterBroadcast owner; - - /** - * shutting down flag. - */ - boolean doingShutdown; - - /** - * Whether the listening thread is busy assigning a request to a thread. - */ - boolean isActive; - - /** - * Construct with a given thread pool name. - */ - public SocketClusterListener(SocketClusterBroadcast owner, int port) { - this.owner = owner; - this.threadPool = ThreadPoolManager.getThreadPool("EbeanClusterMember"); - this.port = port; - - try { - this.serverListenSocket = new ServerSocket(port); - this.serverListenSocket.setSoTimeout(listenTimeout); - this.listenerThread = new Thread(this, "EbeanClusterListener"); - - } catch (IOException e){ - String msg = "Error starting cluster socket listener on port "+port; - throw new RuntimeException(msg,e); - } - } - - /** - * Returns the port the listener is using. - */ - public int getPort() { - return port; - } - - /** - * Start listening for requests. - */ - public void startListening() throws IOException { - this.listenerThread.setDaemon(true); - this.listenerThread.start(); - } - - /** - * Shutdown this listener. - */ - public void shutdown() { - doingShutdown = true; - try { - if (isActive) { - synchronized (listenerThread) { - try { - listenerThread.wait(1000); - } catch (InterruptedException e) { - // OK to ignore as expected to Interrupt for shutdown. - ; - } - } - } - listenerThread.interrupt(); - serverListenSocket.close(); - } catch (IOException e) { - logger.log(Level.SEVERE, null, e); - } - } - - /** - * This is a runnable and so this must be public. Don't call this externally - * but rather call the startListening() method. - */ - public void run() { - // run in loop until doingShutdown is true... - while (!doingShutdown) { - try { - synchronized (listenerThread) { - Socket clientSocket = serverListenSocket.accept(); - - isActive = true; - - Runnable request = new RequestProcessor(owner, clientSocket); - threadPool.assign(request, true); - - isActive = false; - } - } catch (SocketException e) { - if (doingShutdown) { - String msg = "doingShutdown and accept threw:"+ e.getMessage(); - logger.info(msg); - - } else { - logger.log(Level.SEVERE, null, e); - } - - } catch (InterruptedIOException e) { - // this will happen when the server is very quiet. - // that is, no requests - logger.fine("Possibly expected due to accept timeout?" + e.getMessage()); - - } catch (IOException e) { - // log it and continue in the loop... - logger.log(Level.SEVERE, null, e); - } - } - } - -} +package com.avaje.ebeaninternal.server.cluster.socket; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebeaninternal.server.lib.thread.ThreadPool; +import com.avaje.ebeaninternal.server.lib.thread.ThreadPoolManager; + +/** + * Serverside multithreaded socket listener. Accepts connections and dispatches + * them to an appropriate handler. + *

+ * This is designed as a single port listener, where part of the connection + * protocol determines which service the client is requesting (rather than a + * port per service). + *

+ *

+ * It has its own daemon background thread that handles the accept() loop on the + * ServerSocket. + *

+ */ +class SocketClusterListener implements Runnable { + + private static final Logger logger = Logger.getLogger(SocketClusterListener.class.getName()); + + /** + * The port the SocketListener uses. + */ + private final int port; + + /** + * The length of the socket accept timeout. + */ + private final int listenTimeout = 60000; + + /** + * The server socket used to listen for requests. + */ + private final ServerSocket serverListenSocket; + + /** + * The listening thread. + */ + private final Thread listenerThread; + + /** + * The pool of threads that actually do the parsing execution of requests. + */ + private final ThreadPool threadPool; + + private final SocketClusterBroadcast owner; + + /** + * shutting down flag. + */ + boolean doingShutdown; + + /** + * Whether the listening thread is busy assigning a request to a thread. + */ + boolean isActive; + + /** + * Construct with a given thread pool name. + */ + public SocketClusterListener(SocketClusterBroadcast owner, int port) { + this.owner = owner; + this.threadPool = ThreadPoolManager.getThreadPool("EbeanClusterMember"); + this.port = port; + + try { + this.serverListenSocket = new ServerSocket(port); + this.serverListenSocket.setSoTimeout(listenTimeout); + this.listenerThread = new Thread(this, "EbeanClusterListener"); + + } catch (IOException e){ + String msg = "Error starting cluster socket listener on port "+port; + throw new RuntimeException(msg,e); + } + } + + /** + * Returns the port the listener is using. + */ + public int getPort() { + return port; + } + + /** + * Start listening for requests. + */ + public void startListening() throws IOException { + this.listenerThread.setDaemon(true); + this.listenerThread.start(); + } + + /** + * Shutdown this listener. + */ + public void shutdown() { + doingShutdown = true; + try { + if (isActive) { + synchronized (listenerThread) { + try { + listenerThread.wait(1000); + } catch (InterruptedException e) { + // OK to ignore as expected to Interrupt for shutdown. + ; + } + } + } + listenerThread.interrupt(); + serverListenSocket.close(); + } catch (IOException e) { + logger.log(Level.SEVERE, null, e); + } + } + + /** + * This is a runnable and so this must be public. Don't call this externally + * but rather call the startListening() method. + */ + public void run() { + // run in loop until doingShutdown is true... + while (!doingShutdown) { + try { + synchronized (listenerThread) { + Socket clientSocket = serverListenSocket.accept(); + + isActive = true; + + Runnable request = new RequestProcessor(owner, clientSocket); + threadPool.assign(request, true); + + isActive = false; + } + } catch (SocketException e) { + if (doingShutdown) { + String msg = "doingShutdown and accept threw:"+ e.getMessage(); + logger.info(msg); + + } else { + logger.log(Level.SEVERE, null, e); + } + + } catch (InterruptedIOException e) { + // this will happen when the server is very quiet. + // that is, no requests + logger.fine("Possibly expected due to accept timeout?" + e.getMessage()); + + } catch (IOException e) { + // log it and continue in the loop... + logger.log(Level.SEVERE, null, e); + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterMessage.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterMessage.java index 47015de8b..71bc8b572 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterMessage.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterMessage.java @@ -1,95 +1,78 @@ -/** - * Copyright (C) 2006 Robin Bygrave - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.socket; - -import java.io.Serializable; - -import com.avaje.ebeaninternal.server.cluster.DataHolder; -import com.avaje.ebeaninternal.server.cluster.Packet; - -/** - * The messages broadcast around the cluster. - */ -public class SocketClusterMessage implements Serializable { - - private static final long serialVersionUID = 2993350408394934473L; - - private final String registerHost; - - private final boolean register; - - private final DataHolder dataHolder; - - public static SocketClusterMessage register(String registerHost, boolean register){ - return new SocketClusterMessage(registerHost, register); - } - - public static SocketClusterMessage transEvent(DataHolder transEvent){ - return new SocketClusterMessage(transEvent); - } - - public static SocketClusterMessage packet(Packet packet){ - DataHolder d = new DataHolder(packet.getBytes()); - return new SocketClusterMessage(d); - } - - /** - * Used to construct a Child AttributeMap. - */ - private SocketClusterMessage(String registerHost, boolean register) { - this.registerHost = registerHost; - this.register = register; - this.dataHolder = null; - } - - private SocketClusterMessage(DataHolder dataHolder) { - this.dataHolder = dataHolder; - this.registerHost = null; - this.register = false; - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - if (registerHost != null){ - sb.append("register "); - sb.append(register); - sb.append(" "); - sb.append(registerHost); - } else { - sb.append("transEvent "); - } - return sb.toString(); - } - - public boolean isRegisterEvent() { - return registerHost != null; - } - - public String getRegisterHost() { - return registerHost; - } - - public boolean isRegister() { - return register; - } - - public DataHolder getDataHolder() { - return dataHolder; - } - -} +package com.avaje.ebeaninternal.server.cluster.socket; + +import java.io.Serializable; + +import com.avaje.ebeaninternal.server.cluster.DataHolder; +import com.avaje.ebeaninternal.server.cluster.Packet; + +/** + * The messages broadcast around the cluster. + */ +public class SocketClusterMessage implements Serializable { + + private static final long serialVersionUID = 2993350408394934473L; + + private final String registerHost; + + private final boolean register; + + private final DataHolder dataHolder; + + public static SocketClusterMessage register(String registerHost, boolean register){ + return new SocketClusterMessage(registerHost, register); + } + + public static SocketClusterMessage transEvent(DataHolder transEvent){ + return new SocketClusterMessage(transEvent); + } + + public static SocketClusterMessage packet(Packet packet){ + DataHolder d = new DataHolder(packet.getBytes()); + return new SocketClusterMessage(d); + } + + /** + * Used to construct a Child AttributeMap. + */ + private SocketClusterMessage(String registerHost, boolean register) { + this.registerHost = registerHost; + this.register = register; + this.dataHolder = null; + } + + private SocketClusterMessage(DataHolder dataHolder) { + this.dataHolder = dataHolder; + this.registerHost = null; + this.register = false; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + if (registerHost != null){ + sb.append("register "); + sb.append(register); + sb.append(" "); + sb.append(registerHost); + } else { + sb.append("transEvent "); + } + return sb.toString(); + } + + public boolean isRegisterEvent() { + return registerHost != null; + } + + public String getRegisterHost() { + return registerHost; + } + + public boolean isRegister() { + return register; + } + + public DataHolder getDataHolder() { + return dataHolder; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterStatus.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterStatus.java index ad4c46060..fa06064db 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterStatus.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketClusterStatus.java @@ -1,60 +1,41 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.socket; - -/** - * The current state of this cluster member. - * - * @author rbygrave - */ -public class SocketClusterStatus { - - private final int currentGroupSize; - private final int txnIncoming; - private final int txtOutgoing; - - public SocketClusterStatus(int currentGroupSize, int txnIncoming, int txnOutgoing) { - this.currentGroupSize = currentGroupSize; - this.txnIncoming = txnIncoming; - this.txtOutgoing = txnOutgoing; - } - - /** - * Return the number of members of the cluster currently online. - */ - public int getCurrentGroupSize() { - return currentGroupSize; - } - - /** - * Return the number of Remote transactions received. - */ - public int getTxnIncoming() { - return txnIncoming; - } - - /** - * Return the number of transactions sent to the cluster. - */ - public int getTxtOutgoing() { - return txtOutgoing; - } - -} +package com.avaje.ebeaninternal.server.cluster.socket; + +/** + * The current state of this cluster member. + * + * @author rbygrave + */ +public class SocketClusterStatus { + + private final int currentGroupSize; + private final int txnIncoming; + private final int txtOutgoing; + + public SocketClusterStatus(int currentGroupSize, int txnIncoming, int txnOutgoing) { + this.currentGroupSize = currentGroupSize; + this.txnIncoming = txnIncoming; + this.txtOutgoing = txnOutgoing; + } + + /** + * Return the number of members of the cluster currently online. + */ + public int getCurrentGroupSize() { + return currentGroupSize; + } + + /** + * Return the number of Remote transactions received. + */ + public int getTxnIncoming() { + return txnIncoming; + } + + /** + * Return the number of transactions sent to the cluster. + */ + public int getTxtOutgoing() { + return txtOutgoing; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketConnection.java b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketConnection.java index 7f25412aa..aeef8e95b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketConnection.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cluster/socket/SocketConnection.java @@ -1,146 +1,129 @@ -/** - * Copyright (C) 2006 Robin Bygrave - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.cluster.socket; - -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.OutputStream; -import java.net.Socket; - -/** - * The client side of a TCP Sockect connection. - */ -class SocketConnection { - - /** - * The object underlying objectOutputStream. - */ - ObjectOutputStream oos; - - /** - * The underlying ObjectInputStream. - */ - ObjectInputStream ois; - - /** - * The underlying inputStream. - */ - InputStream is; - - /** - * The underlying outputStream. - */ - OutputStream os; - - /** - * The underlying socket. - */ - Socket socket; - - /** - * Create for a given Socket. - */ - public SocketConnection(Socket socket) throws IOException { - this.is = socket.getInputStream(); - this.os = socket.getOutputStream(); - this.socket = socket; - } - - /** - * Disconnect from the server. - */ - public void disconnect() throws IOException { - os.flush(); - socket.close(); - } - - /** - * Flush the outputStream. - */ - public void flush() throws IOException { - os.flush(); - } - - /** - * Read an object from the object input stream. - */ - public Object readObject() throws IOException, ClassNotFoundException { - return getObjectInputStream().readObject(); - } - - /** - * Write an object to the object output stream. - */ - public ObjectOutputStream writeObject(Object object) throws IOException { - ObjectOutputStream oos = getObjectOutputStream(); - oos.writeObject(object); - return oos; - } - - /** - * Get the object output stream. - */ - public ObjectOutputStream getObjectOutputStream() throws IOException { - if (oos == null){ - oos = new ObjectOutputStream(os); - } - return oos; - } - - /** - * Get the object input stream. - */ - public ObjectInputStream getObjectInputStream() throws IOException { - if (ois == null){ - ois = new ObjectInputStream(is); - } - return ois; - } - - - /** - * Set the ObjectInputStream to use. - */ - public void setObjectInputStream(ObjectInputStream ois) { - this.ois = ois; - } - - /** - * Set the ObjectOutputStream to use. - */ - public void setObjectOutputStream(ObjectOutputStream oos) { - this.oos = oos; - } - - /** - * Return the underlying input stream. - */ - public InputStream getInputStream() throws IOException { - return is; - } - - /** - * Return the underlying output stream. - */ - public OutputStream getOutputStream() throws IOException { - return os; - } - -} +package com.avaje.ebeaninternal.server.cluster.socket; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.net.Socket; + +/** + * The client side of a TCP Sockect connection. + */ +class SocketConnection { + + /** + * The object underlying objectOutputStream. + */ + ObjectOutputStream oos; + + /** + * The underlying ObjectInputStream. + */ + ObjectInputStream ois; + + /** + * The underlying inputStream. + */ + InputStream is; + + /** + * The underlying outputStream. + */ + OutputStream os; + + /** + * The underlying socket. + */ + Socket socket; + + /** + * Create for a given Socket. + */ + public SocketConnection(Socket socket) throws IOException { + this.is = socket.getInputStream(); + this.os = socket.getOutputStream(); + this.socket = socket; + } + + /** + * Disconnect from the server. + */ + public void disconnect() throws IOException { + os.flush(); + socket.close(); + } + + /** + * Flush the outputStream. + */ + public void flush() throws IOException { + os.flush(); + } + + /** + * Read an object from the object input stream. + */ + public Object readObject() throws IOException, ClassNotFoundException { + return getObjectInputStream().readObject(); + } + + /** + * Write an object to the object output stream. + */ + public ObjectOutputStream writeObject(Object object) throws IOException { + ObjectOutputStream oos = getObjectOutputStream(); + oos.writeObject(object); + return oos; + } + + /** + * Get the object output stream. + */ + public ObjectOutputStream getObjectOutputStream() throws IOException { + if (oos == null){ + oos = new ObjectOutputStream(os); + } + return oos; + } + + /** + * Get the object input stream. + */ + public ObjectInputStream getObjectInputStream() throws IOException { + if (ois == null){ + ois = new ObjectInputStream(is); + } + return ois; + } + + + /** + * Set the ObjectInputStream to use. + */ + public void setObjectInputStream(ObjectInputStream ois) { + this.ois = ois; + } + + /** + * Set the ObjectOutputStream to use. + */ + public void setObjectOutputStream(ObjectOutputStream oos) { + this.oos = oos; + } + + /** + * Return the underlying input stream. + */ + public InputStream getInputStream() throws IOException { + return is; + } + + /** + * Return the underlying output stream. + */ + public OutputStream getOutputStream() throws IOException { + return os; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/BasicTypeConverter.java b/src/main/java/com/avaje/ebeaninternal/server/core/BasicTypeConverter.java index 7680ede4d..761291f26 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/BasicTypeConverter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/BasicTypeConverter.java @@ -1,493 +1,474 @@ -/** - * Copyright (C) 2006 Robin Bygrave - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.core; - -import java.io.Serializable; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.sql.Timestamp; -import java.sql.Types; -import java.util.Calendar; -import java.util.UUID; - - -/** - * Default implementation of TypeConverter. - *

- * Converts objects to the required type if required. - *

- */ -public final class BasicTypeConverter implements Serializable { - - private static final long serialVersionUID = 7691463236204070311L; - - /** - * Type code for java.util.Calendar. - */ - public static final int UTIL_CALENDAR = -999998986; - - /** - * Type code for java.util.Date. - */ - public static final int UTIL_DATE = -999998988; - - /** - * Type code for java.math.BigInteger. - */ - public static final int MATH_BIGINTEGER = -999998987; - - /** - * Type code for an Enum type. - */ - public static final int ENUM = -999998989; - - private BasicTypeConverter() { - } - - /** - * Convert the Object to the required data type. - * - * @param value - * the Object value - * @param toDataType - * the dataType as per java.sql.Types. - */ - public static Object convert(Object value, int toDataType) { - - try { - switch (toDataType) { - case UTIL_DATE: { - return toUtilDate(value); - } - case UTIL_CALENDAR: { - return toCalendar(value); - } - case Types.BIGINT: { - return toLong(value); - } - case Types.INTEGER: { - return toInteger(value); - } - case Types.BIT: { - return toBoolean(value); - } - case Types.TINYINT: { - return toByte(value); - } - case Types.SMALLINT: { - return toShort(value); - } - case Types.NUMERIC: { - return toBigDecimal(value); - } - case Types.DECIMAL: { - return toBigDecimal(value); - } - case Types.REAL: { - return toFloat(value); - } - case Types.DOUBLE: { - return toDouble(value); - } - case Types.FLOAT: { - return toDouble(value); - } - case Types.BOOLEAN: { - return toBoolean(value); - } - case Types.TIMESTAMP: { - return toTimestamp(value); - } - case Types.DATE: { - return toDate(value); - } - case Types.VARCHAR: { - return toString(value); - } - case Types.CHAR: { - return toString(value); - } - case Types.OTHER: { - return value; - } - case Types.JAVA_OBJECT: { - return value; - } - case Types.BINARY: - case Types.LONGVARBINARY: - case Types.BLOB: { - return value; - } - case Types.LONGVARCHAR: - case Types.CLOB: { - return value; - } - default: { - String msg = "Unhandled data type [" + toDataType + "] converting [" + value + "]"; - throw new RuntimeException(msg); - } - } - } catch (ClassCastException e) { - String m = "ClassCastException converting to data type [" + toDataType + "] value [" + value + "]"; - throw new RuntimeException(m); - } - } - - /** - * Convert the value to a String. - */ - public static String toString(Object value) { - - if (value == null) { - return null; - } - if (value instanceof String) { - return (String) value; - } - if (value instanceof char[]) { - return String.valueOf((char[]) value); - } - - return value.toString(); - } - - - /** - * Convert the value to a Boolean with an explicit String true value. - */ - public static Boolean toBoolean(Object value, String dbTrueValue) { - - if (value == null) { - return null; - } - if (value instanceof Boolean) { - return (Boolean) value; - } - String s = value.toString(); - return s.equalsIgnoreCase(dbTrueValue); - } - - /** - * Convert the value to a Boolean. Can be a Boolean or the string values - * "true" or "false". - */ - public static Boolean toBoolean(Object value) { - - if (value == null) { - return null; - } - if (value instanceof Boolean) { - return (Boolean) value; - } - - return Boolean.valueOf(value.toString()); - } - - /** - * Convert the value to a UUID. - */ - public static UUID toUUID(Object value) { - - if (value == null) { - return null; - } - if (value instanceof String) { - return UUID.fromString((String) value); - } - return (UUID) value; - } - - /** - * convert the passed in object to a BigDecimal. It should be another - * numeric type. - */ - public static BigDecimal toBigDecimal(Object value) { - - if (value == null) { - return null; - } - if (value instanceof BigDecimal) { - return (BigDecimal) value; - } - return new BigDecimal(value.toString()); - } - - public static Float toFloat(Object value) { - - if (value == null) { - return null; - } - if (value instanceof Float) { - return (Float) value; - } - if (value instanceof Number) { - return Float.valueOf(((Number) value).floatValue()); - } - return Float.valueOf(value.toString()); - } - - public static Short toShort(Object value) { - - if (value == null) { - return null; - } - if (value instanceof Short) { - return (Short) value; - } - if (value instanceof Number) { - return Short.valueOf(((Number) value).shortValue()); - } - return Short.valueOf(value.toString()); - } - - public static Byte toByte(Object value) { - - if (value == null) { - return null; - } - if (value instanceof Byte) { - return (Byte) value; - } - return Byte.valueOf(value.toString()); - } - - /** - * convert the passed in object to a Integer. It should be another numeric - * type. - */ - public static Integer toInteger(Object value) { - - if (value == null) { - return null; - } - if (value instanceof Integer) { - return (Integer) value; - } - if (value instanceof Number) { - return Integer.valueOf(((Number) value).intValue()); - } - return Integer.valueOf(value.toString()); - } - - /** - * Convert the object to a Long. It should be another numeric type. - */ - public static Long toLong(Object value) { - - if (value == null) { - return null; - } - if (value instanceof Long) { - return (Long) value; - } - if (value instanceof String) { - return Long.valueOf((String) value); - } - if (value instanceof Number) { - return Long.valueOf(((Number) value).longValue()); - } - if (value instanceof java.util.Date) { - return Long.valueOf(((java.util.Date) value).getTime()); - } - if (value instanceof Calendar) { - return Long.valueOf(((Calendar) value).getTime().getTime()); - } - return Long.valueOf(value.toString()); - } - - public static BigInteger toMathBigInteger(Object value) { - - if (value == null) { - return null; - } - if (value instanceof BigInteger) { - return (BigInteger) value; - } - return new BigInteger(value.toString()); - } - - /** - * Convert the object to a Double. It should be another numberic type. - */ - public static Double toDouble(Object value) { - - if (value == null) { - return null; - } - if (value instanceof Double) { - return (Double) value; - } - if (value instanceof Number) { - return Double.valueOf(((Number) value).doubleValue()); - } - return Double.valueOf(value.toString()); - } - - /** - * convert the passed in object to a Timestamp. It is expected to be a - * java.sql.Date really. - */ - public static Timestamp toTimestamp(Object value) { - - if (value == null) { - return null; - } - if (value instanceof Timestamp) { - return (Timestamp) value; - - } else if (value instanceof java.util.Date) { - // no nanos here... so hopefully ok - return new Timestamp(((java.util.Date) value).getTime()); - - } else if (value instanceof Calendar) { - return new Timestamp(((Calendar) value).getTime().getTime()); - - } else if (value instanceof String) { - return Timestamp.valueOf((String) value); - - } else if (value instanceof Number) { - return new Timestamp(((Number) value).longValue()); - - } else { - String msg = "Unable to convert [" + value.getClass().getName() + "] into a Timestamp."; - throw new RuntimeException(msg); - } - } - - public static java.sql.Time toTime(Object value) { - - if (value == null) { - return null; - } - if (value instanceof java.sql.Time) { - return (java.sql.Time) value; - - } else if (value instanceof String) { - return java.sql.Time.valueOf((String) value); - - } else { - String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date."; - throw new RuntimeException(m); - } - } - - /** - * convert the passed in object to a java sql Date. - */ - public static java.sql.Date toDate(Object value) { - - if (value == null) { - return null; - } - if (value instanceof java.sql.Date) { - return (java.sql.Date) value; - - } else if (value instanceof java.util.Date) { - return new java.sql.Date(((java.util.Date) value).getTime()); - - } else if (value instanceof Calendar) { - return new java.sql.Date(((Calendar) value).getTime().getTime()); - - } else if (value instanceof String) { - return java.sql.Date.valueOf((String) value); - - } else if (value instanceof Number) { - return new java.sql.Date(((Number) value).longValue()); - - } else { - String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date."; - throw new RuntimeException(m); - } - } - - /** - * convert the passed in object to a java sql Date. - */ - public static java.util.Date toUtilDate(Object value) { - - if (value == null) { - return null; - } - if (value instanceof java.sql.Timestamp) { - // loss of nanos precision - return new java.util.Date(((java.sql.Timestamp) value).getTime()); - } - // DEVNOTE: strictly speaking do I need to convert a java.sql.Date to - // java.util.Date? equals() is symmetrical so perhaps this is not - // really required? - if (value instanceof java.sql.Date) { - return new java.util.Date(((java.sql.Date) value).getTime()); - } - if (value instanceof java.util.Date) { - return (java.util.Date) value; - - } else if (value instanceof Calendar) { - return ((Calendar) value).getTime(); - - } else if (value instanceof String) { - return new java.util.Date(Timestamp.valueOf((String) value).getTime()); - - } else if (value instanceof Number) { - return new java.util.Date(((Number) value).longValue()); - - } else { - throw new RuntimeException("Unable to convert [" + value.getClass().getName() + "] into a java.util.Date"); - } - } - - /** - * convert the passed in object to a java sql Date. - */ - public static Calendar toCalendar(Object value) { - - if (value == null) { - return null; - } - if (value instanceof Calendar) { - return (Calendar) value; - - } else if (value instanceof java.util.Date) { - java.util.Date date = ((java.util.Date) value); - return toCalendarFromDate(date); - - } else if (value instanceof String) { - java.util.Date date = toUtilDate(value); - return toCalendarFromDate(date); - - } else if (value instanceof Number) { - long timeMillis = ((Number) value).longValue(); - java.util.Date date = new java.util.Date(timeMillis); - return toCalendarFromDate(date); - - } else { - String m = "Unable to convert [" + value.getClass().getName() + "] into a java.util.Date"; - throw new RuntimeException(m); - } - } - - private static Calendar toCalendarFromDate(java.util.Date date) { - - Calendar cal = Calendar.getInstance(); - cal.setTime(date); - - return cal; - } - -} +package com.avaje.ebeaninternal.server.core; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Calendar; +import java.util.UUID; + + +/** + * Default implementation of TypeConverter. + *

+ * Converts objects to the required type if required. + *

+ */ +public final class BasicTypeConverter implements Serializable { + + private static final long serialVersionUID = 7691463236204070311L; + + /** + * Type code for java.util.Calendar. + */ + public static final int UTIL_CALENDAR = -999998986; + + /** + * Type code for java.util.Date. + */ + public static final int UTIL_DATE = -999998988; + + /** + * Type code for java.math.BigInteger. + */ + public static final int MATH_BIGINTEGER = -999998987; + + /** + * Type code for an Enum type. + */ + public static final int ENUM = -999998989; + + private BasicTypeConverter() { + } + + /** + * Convert the Object to the required data type. + * + * @param value + * the Object value + * @param toDataType + * the dataType as per java.sql.Types. + */ + public static Object convert(Object value, int toDataType) { + + try { + switch (toDataType) { + case UTIL_DATE: { + return toUtilDate(value); + } + case UTIL_CALENDAR: { + return toCalendar(value); + } + case Types.BIGINT: { + return toLong(value); + } + case Types.INTEGER: { + return toInteger(value); + } + case Types.BIT: { + return toBoolean(value); + } + case Types.TINYINT: { + return toByte(value); + } + case Types.SMALLINT: { + return toShort(value); + } + case Types.NUMERIC: { + return toBigDecimal(value); + } + case Types.DECIMAL: { + return toBigDecimal(value); + } + case Types.REAL: { + return toFloat(value); + } + case Types.DOUBLE: { + return toDouble(value); + } + case Types.FLOAT: { + return toDouble(value); + } + case Types.BOOLEAN: { + return toBoolean(value); + } + case Types.TIMESTAMP: { + return toTimestamp(value); + } + case Types.DATE: { + return toDate(value); + } + case Types.VARCHAR: { + return toString(value); + } + case Types.CHAR: { + return toString(value); + } + case Types.OTHER: { + return value; + } + case Types.JAVA_OBJECT: { + return value; + } + case Types.BINARY: + case Types.LONGVARBINARY: + case Types.BLOB: { + return value; + } + case Types.LONGVARCHAR: + case Types.CLOB: { + return value; + } + default: { + String msg = "Unhandled data type [" + toDataType + "] converting [" + value + "]"; + throw new RuntimeException(msg); + } + } + } catch (ClassCastException e) { + String m = "ClassCastException converting to data type [" + toDataType + "] value [" + value + "]"; + throw new RuntimeException(m); + } + } + + /** + * Convert the value to a String. + */ + public static String toString(Object value) { + + if (value == null) { + return null; + } + if (value instanceof String) { + return (String) value; + } + if (value instanceof char[]) { + return String.valueOf((char[]) value); + } + + return value.toString(); + } + + + /** + * Convert the value to a Boolean with an explicit String true value. + */ + public static Boolean toBoolean(Object value, String dbTrueValue) { + + if (value == null) { + return null; + } + if (value instanceof Boolean) { + return (Boolean) value; + } + String s = value.toString(); + return s.equalsIgnoreCase(dbTrueValue); + } + + /** + * Convert the value to a Boolean. Can be a Boolean or the string values + * "true" or "false". + */ + public static Boolean toBoolean(Object value) { + + if (value == null) { + return null; + } + if (value instanceof Boolean) { + return (Boolean) value; + } + + return Boolean.valueOf(value.toString()); + } + + /** + * Convert the value to a UUID. + */ + public static UUID toUUID(Object value) { + + if (value == null) { + return null; + } + if (value instanceof String) { + return UUID.fromString((String) value); + } + return (UUID) value; + } + + /** + * convert the passed in object to a BigDecimal. It should be another + * numeric type. + */ + public static BigDecimal toBigDecimal(Object value) { + + if (value == null) { + return null; + } + if (value instanceof BigDecimal) { + return (BigDecimal) value; + } + return new BigDecimal(value.toString()); + } + + public static Float toFloat(Object value) { + + if (value == null) { + return null; + } + if (value instanceof Float) { + return (Float) value; + } + if (value instanceof Number) { + return Float.valueOf(((Number) value).floatValue()); + } + return Float.valueOf(value.toString()); + } + + public static Short toShort(Object value) { + + if (value == null) { + return null; + } + if (value instanceof Short) { + return (Short) value; + } + if (value instanceof Number) { + return Short.valueOf(((Number) value).shortValue()); + } + return Short.valueOf(value.toString()); + } + + public static Byte toByte(Object value) { + + if (value == null) { + return null; + } + if (value instanceof Byte) { + return (Byte) value; + } + return Byte.valueOf(value.toString()); + } + + /** + * convert the passed in object to a Integer. It should be another numeric + * type. + */ + public static Integer toInteger(Object value) { + + if (value == null) { + return null; + } + if (value instanceof Integer) { + return (Integer) value; + } + if (value instanceof Number) { + return Integer.valueOf(((Number) value).intValue()); + } + return Integer.valueOf(value.toString()); + } + + /** + * Convert the object to a Long. It should be another numeric type. + */ + public static Long toLong(Object value) { + + if (value == null) { + return null; + } + if (value instanceof Long) { + return (Long) value; + } + if (value instanceof String) { + return Long.valueOf((String) value); + } + if (value instanceof Number) { + return Long.valueOf(((Number) value).longValue()); + } + if (value instanceof java.util.Date) { + return Long.valueOf(((java.util.Date) value).getTime()); + } + if (value instanceof Calendar) { + return Long.valueOf(((Calendar) value).getTime().getTime()); + } + return Long.valueOf(value.toString()); + } + + public static BigInteger toMathBigInteger(Object value) { + + if (value == null) { + return null; + } + if (value instanceof BigInteger) { + return (BigInteger) value; + } + return new BigInteger(value.toString()); + } + + /** + * Convert the object to a Double. It should be another numberic type. + */ + public static Double toDouble(Object value) { + + if (value == null) { + return null; + } + if (value instanceof Double) { + return (Double) value; + } + if (value instanceof Number) { + return Double.valueOf(((Number) value).doubleValue()); + } + return Double.valueOf(value.toString()); + } + + /** + * convert the passed in object to a Timestamp. It is expected to be a + * java.sql.Date really. + */ + public static Timestamp toTimestamp(Object value) { + + if (value == null) { + return null; + } + if (value instanceof Timestamp) { + return (Timestamp) value; + + } else if (value instanceof java.util.Date) { + // no nanos here... so hopefully ok + return new Timestamp(((java.util.Date) value).getTime()); + + } else if (value instanceof Calendar) { + return new Timestamp(((Calendar) value).getTime().getTime()); + + } else if (value instanceof String) { + return Timestamp.valueOf((String) value); + + } else if (value instanceof Number) { + return new Timestamp(((Number) value).longValue()); + + } else { + String msg = "Unable to convert [" + value.getClass().getName() + "] into a Timestamp."; + throw new RuntimeException(msg); + } + } + + public static java.sql.Time toTime(Object value) { + + if (value == null) { + return null; + } + if (value instanceof java.sql.Time) { + return (java.sql.Time) value; + + } else if (value instanceof String) { + return java.sql.Time.valueOf((String) value); + + } else { + String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date."; + throw new RuntimeException(m); + } + } + + /** + * convert the passed in object to a java sql Date. + */ + public static java.sql.Date toDate(Object value) { + + if (value == null) { + return null; + } + if (value instanceof java.sql.Date) { + return (java.sql.Date) value; + + } else if (value instanceof java.util.Date) { + return new java.sql.Date(((java.util.Date) value).getTime()); + + } else if (value instanceof Calendar) { + return new java.sql.Date(((Calendar) value).getTime().getTime()); + + } else if (value instanceof String) { + return java.sql.Date.valueOf((String) value); + + } else if (value instanceof Number) { + return new java.sql.Date(((Number) value).longValue()); + + } else { + String m = "Unable to convert [" + value.getClass().getName() + "] into a java.sql.Date."; + throw new RuntimeException(m); + } + } + + /** + * convert the passed in object to a java sql Date. + */ + public static java.util.Date toUtilDate(Object value) { + + if (value == null) { + return null; + } + if (value instanceof java.sql.Timestamp) { + // loss of nanos precision + return new java.util.Date(((java.sql.Timestamp) value).getTime()); + } + // DEVNOTE: strictly speaking do I need to convert a java.sql.Date to + // java.util.Date? equals() is symmetrical so perhaps this is not + // really required? + if (value instanceof java.sql.Date) { + return new java.util.Date(((java.sql.Date) value).getTime()); + } + if (value instanceof java.util.Date) { + return (java.util.Date) value; + + } else if (value instanceof Calendar) { + return ((Calendar) value).getTime(); + + } else if (value instanceof String) { + return new java.util.Date(Timestamp.valueOf((String) value).getTime()); + + } else if (value instanceof Number) { + return new java.util.Date(((Number) value).longValue()); + + } else { + throw new RuntimeException("Unable to convert [" + value.getClass().getName() + "] into a java.util.Date"); + } + } + + /** + * convert the passed in object to a java sql Date. + */ + public static Calendar toCalendar(Object value) { + + if (value == null) { + return null; + } + if (value instanceof Calendar) { + return (Calendar) value; + + } else if (value instanceof java.util.Date) { + java.util.Date date = ((java.util.Date) value); + return toCalendarFromDate(date); + + } else if (value instanceof String) { + java.util.Date date = toUtilDate(value); + return toCalendarFromDate(date); + + } else if (value instanceof Number) { + long timeMillis = ((Number) value).longValue(); + java.util.Date date = new java.util.Date(timeMillis); + return toCalendarFromDate(date); + + } else { + String m = "Unable to convert [" + value.getClass().getName() + "] into a java.util.Date"; + throw new RuntimeException(m); + } + } + + private static Calendar toCalendarFromDate(java.util.Date date) { + + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + + return cal; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/BeanRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/BeanRequest.java index 3632c7081..f19c5c841 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/BeanRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/BeanRequest.java @@ -1,146 +1,127 @@ -/** - * Copyright (C) 2006 Robin Bygrave - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.core; - -import java.sql.Connection; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.LogLevel; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiTransaction; - -/** - * Base class for find and persist requests. - */ -public abstract class BeanRequest { - - /** - * The server processing the request. - */ - final SpiEbeanServer ebeanServer; - - final String serverName; - - /** - * The transaction this is part of. - */ - SpiTransaction transaction; - - boolean createdTransaction; - - boolean readOnly; - - public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) { - this.ebeanServer = ebeanServer; - this.serverName = ebeanServer.getName(); - this.transaction = t; - } - - /** - * initialise an implicit transaction if one is not currently supplied. - *

- * A transaction may have been passed in or active in the thread local. If - * not then create one implicitly to handle the request. - *

- */ - public abstract void initTransIfRequired(); - - /** - * A helper method for creating an implicit transaction is it is required. - *

- * A transaction may have been passed in or active in the thread local. If - * not then create one implicitly to handle the request. - *

- */ - public void createImplicitTransIfRequired(boolean readOnlyTransaction) { - if (transaction == null) { - transaction = ebeanServer.getCurrentServerTransaction(); - if (transaction == null || !transaction.isActive()) { - // create an implicit transaction to execute this query - transaction = ebeanServer.createServerTransaction(false, -1); - // commented out for performance reasons... - // TODO: review performance of trans.setReadOnly(true) - //if (readOnlyTransaction) { - // readOnly = true; - // transaction.setReadOnly(true); - //} - createdTransaction = true; - } - } - } - - /** - * Commit this transaction if it was created for this request. - */ - public void commitTransIfRequired() { - if (createdTransaction) { - if (readOnly) { - transaction.rollback(); - } else { - transaction.commit(); - } - } - } - - /** - * Rollback the transaction if it was created for this request. - */ - public void rollbackTransIfRequired() { - if (createdTransaction) { - transaction.rollback(); - } - } - - /** - * Return the server processing the request. Made available for - * BeanController and BeanFinder. - */ - public EbeanServer getEbeanServer() { - return ebeanServer; - } - - /** - * Return the Transaction associated with this request. - */ - public SpiTransaction getTransaction() { - return transaction; - } - - /** - * Returns the connection from the Transaction. - */ - public Connection getConnection() { - return transaction.getInternalConnection(); - } - - /** - * Return true if SQL should be logged for this transaction. - */ - public boolean isLogSql() { - return transaction.getLogLevel().ordinal() >= LogLevel.SQL.ordinal(); - } - - /** - * Return true if SUMMARY information should be logged for this transaction. - */ - public boolean isLogSummary() { - return transaction.getLogLevel().ordinal() >= LogLevel.SUMMARY.ordinal(); - } -} +package com.avaje.ebeaninternal.server.core; + +import java.sql.Connection; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.LogLevel; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiTransaction; + +/** + * Base class for find and persist requests. + */ +public abstract class BeanRequest { + + /** + * The server processing the request. + */ + final SpiEbeanServer ebeanServer; + + final String serverName; + + /** + * The transaction this is part of. + */ + SpiTransaction transaction; + + boolean createdTransaction; + + boolean readOnly; + + public BeanRequest(SpiEbeanServer ebeanServer, SpiTransaction t) { + this.ebeanServer = ebeanServer; + this.serverName = ebeanServer.getName(); + this.transaction = t; + } + + /** + * initialise an implicit transaction if one is not currently supplied. + *

+ * A transaction may have been passed in or active in the thread local. If + * not then create one implicitly to handle the request. + *

+ */ + public abstract void initTransIfRequired(); + + /** + * A helper method for creating an implicit transaction is it is required. + *

+ * A transaction may have been passed in or active in the thread local. If + * not then create one implicitly to handle the request. + *

+ */ + public void createImplicitTransIfRequired(boolean readOnlyTransaction) { + if (transaction == null) { + transaction = ebeanServer.getCurrentServerTransaction(); + if (transaction == null || !transaction.isActive()) { + // create an implicit transaction to execute this query + transaction = ebeanServer.createServerTransaction(false, -1); + // commented out for performance reasons... + // TODO: review performance of trans.setReadOnly(true) + //if (readOnlyTransaction) { + // readOnly = true; + // transaction.setReadOnly(true); + //} + createdTransaction = true; + } + } + } + + /** + * Commit this transaction if it was created for this request. + */ + public void commitTransIfRequired() { + if (createdTransaction) { + if (readOnly) { + transaction.rollback(); + } else { + transaction.commit(); + } + } + } + + /** + * Rollback the transaction if it was created for this request. + */ + public void rollbackTransIfRequired() { + if (createdTransaction) { + transaction.rollback(); + } + } + + /** + * Return the server processing the request. Made available for + * BeanController and BeanFinder. + */ + public EbeanServer getEbeanServer() { + return ebeanServer; + } + + /** + * Return the Transaction associated with this request. + */ + public SpiTransaction getTransaction() { + return transaction; + } + + /** + * Returns the connection from the Transaction. + */ + public Connection getConnection() { + return transaction.getInternalConnection(); + } + + /** + * Return true if SQL should be logged for this transaction. + */ + public boolean isLogSql() { + return transaction.getLogLevel().ordinal() >= LogLevel.SQL.ordinal(); + } + + /** + * Return true if SUMMARY information should be logged for this transaction. + */ + public boolean isLogSummary() { + return transaction.getLogLevel().ordinal() >= LogLevel.SUMMARY.ordinal(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClassPathSearch.java b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClassPathSearch.java index 140973c58..6aa927f67 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClassPathSearch.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClassPathSearch.java @@ -1,22 +1,3 @@ -/** - * Copyright (C) 2006 Robin Bygrave - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ package com.avaje.ebeaninternal.server.core; import java.util.List; diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java index 197d6a7c8..e16d161c8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java @@ -1,460 +1,441 @@ -/** - * Copyright (C) 2006 Robin Bygrave - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.core; - -import java.lang.annotation.Annotation; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.Table; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlType; - -import com.avaje.ebean.annotation.LdapDomain; -import com.avaje.ebean.config.CompoundType; -import com.avaje.ebean.config.ScalarTypeConverter; -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebean.event.BeanFinder; -import com.avaje.ebean.event.BeanPersistController; -import com.avaje.ebean.event.BeanPersistListener; -import com.avaje.ebean.event.BeanQueryAdapter; -import com.avaje.ebean.event.ServerConfigStartup; -import com.avaje.ebean.event.TransactionEventListener; -import com.avaje.ebeaninternal.server.type.ScalarType; -import com.avaje.ebeaninternal.server.util.ClassPathSearchMatcher; - -/** - * Interesting classes for a EbeanServer such as Embeddable, Entity, - * ScalarTypes, Finders, Listeners and Controllers. - */ -public class BootupClasses implements ClassPathSearchMatcher { - - private static final Logger logger = Logger.getLogger(BootupClasses.class.getName()); - - private ArrayList> xmlBeanList = new ArrayList>(); - - private ArrayList> embeddableList = new ArrayList>(); - - private ArrayList> entityList = new ArrayList>(); - - private ArrayList> scalarTypeList = new ArrayList>(); - - private ArrayList> scalarConverterList = new ArrayList>(); - - private ArrayList> compoundTypeList = new ArrayList>(); - - private ArrayList> beanControllerList = new ArrayList>(); - - private ArrayList> transactionEventListenerList = new ArrayList>(); - - private ArrayList> beanFinderList = new ArrayList>(); - - private ArrayList> beanListenerList = new ArrayList>(); - - private ArrayList> beanQueryAdapterList = new ArrayList>(); - - private ArrayList> luceneIndexList = new ArrayList>(); - - private ArrayList> serverConfigStartupList = new ArrayList>(); - private ArrayList serverConfigStartupInstances = new ArrayList(); - - private List persistControllerInstances = new ArrayList(); - private List> persistListenerInstances = new ArrayList>(); - private List queryAdapterInstances = new ArrayList(); - private List transactionEventListenerInstances = new ArrayList(); - - public BootupClasses() { - } - - public BootupClasses(List> list) { - if (list != null) { - 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> 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 queryAdapterInstances) { - if (queryAdapterInstances != null) { - for (BeanQueryAdapter a : queryAdapterInstances) { - this.queryAdapterInstances.add(a); - // don't automatically instantiate - this.beanQueryAdapterList.remove(a.getClass()); - } - } - } - - /** - * Add BeanPersistController instances. - */ - public void addPersistControllers(List beanControllerInstances) { - if (beanControllerInstances != null) { - for (BeanPersistController c : beanControllerInstances) { - this.persistControllerInstances.add(c); - // don't automatically instantiate - this.beanControllerList.remove(c.getClass()); - } - } - } - - /** - * Add TransactionEventListeners instances. - */ - public void addTransactionEventListeners(List transactionEventListeners) { - if (transactionEventListeners != null) { - for (TransactionEventListener c : transactionEventListeners) { - this.transactionEventListenerInstances.add(c); - // don't automatically instantiate - this.transactionEventListenerList.remove(c.getClass()); - } - } - } - - public void addPersistListeners(List> listenerInstances) { - if (listenerInstances != null) { - for (BeanPersistListener l : listenerInstances) { - this.persistListenerInstances.add(l); - // don't automatically instantiate - this.beanListenerList.remove(l.getClass()); - } - } - } - - public void addServerConfigStartup(List startupInstances) { - if (startupInstances != null) { - for (ServerConfigStartup l : startupInstances) { - this.serverConfigStartupInstances.add(l); - // don't automatically instantiate - this.serverConfigStartupList.remove(l.getClass()); - } - } - } - - public List getBeanQueryAdapters() { - // add class registered BeanQueryAdapter to the - // already created instances - for (Class cls : beanQueryAdapterList) { - try { - BeanQueryAdapter newInstance = (BeanQueryAdapter) cls.newInstance(); - queryAdapterInstances.add(newInstance); - } catch (Exception e) { - String msg = "Error creating BeanQueryAdapter " + cls; - logger.log(Level.SEVERE, msg, e); - } - } - - return queryAdapterInstances; - } - - public List> getBeanPersistListeners() { - // add class registered BeanPersistController to the - // already created instances - for (Class cls : beanListenerList) { - try { - BeanPersistListener newInstance = (BeanPersistListener) cls.newInstance(); - persistListenerInstances.add(newInstance); - } catch (Exception e) { - String msg = "Error creating BeanPersistController " + cls; - logger.log(Level.SEVERE, msg, e); - } - } - - return persistListenerInstances; - } - - public List getBeanPersistControllers() { - // add class registered BeanPersistController to the - // already created instances - for (Class cls : beanControllerList) { - try { - BeanPersistController newInstance = (BeanPersistController) cls.newInstance(); - persistControllerInstances.add(newInstance); - } catch (Exception e) { - String msg = "Error creating BeanPersistController " + cls; - logger.log(Level.SEVERE, msg, e); - } - } - - return persistControllerInstances; - } - - public List getTransactionEventListeners() { - // add class registered TransactionEventListener to the - // already created instances - for (Class cls : transactionEventListenerList) { - try { - TransactionEventListener newInstance = (TransactionEventListener) cls.newInstance(); - transactionEventListenerInstances.add(newInstance); - } catch (Exception e) { - String msg = "Error creating TransactionEventListener " + cls; - logger.log(Level.SEVERE, msg, e); - } - } - - return transactionEventListenerInstances; - } - - /** - * Return the list of Embeddable classes. - */ - public ArrayList> getEmbeddables() { - return embeddableList; - } - - /** - * Return the list of entity classes. - */ - public ArrayList> getEntities() { - return entityList; - } - - /** - * Return the list of ScalarTypes found. - */ - public ArrayList> getScalarTypes() { - return scalarTypeList; - } - - /** - * Return the list of ScalarConverters found. - */ - public ArrayList> getScalarConverters() { - return scalarConverterList; - } - - /** - * Return the list of ScalarConverters found. - */ - public ArrayList> getCompoundTypes() { - return compoundTypeList; - } - - /** - * Return the list of BeanControllers found. - */ - public ArrayList> getBeanControllers() { - return beanControllerList; - } - - /** - * Return the list of TransactionEventListeners found - */ - public ArrayList> getTransactionEventListenerList() { - return transactionEventListenerList; - } - - /** - * Return the list of BeanFinders found. - */ - public ArrayList> getBeanFinders() { - return beanFinderList; - } - - /** - * Return the list of BeanListeners found. - */ - public ArrayList> getBeanListeners() { - return beanListenerList; - } - - /** - * Return the list of XML Beans. - */ - public ArrayList> getXmlBeanList() { - return xmlBeanList; - } - - public void add(Iterator> 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. - *

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

- */ - private boolean isInterestingInterface(Class cls) { - - boolean interesting = false; - - if (BeanPersistController.class.isAssignableFrom(cls)) { - beanControllerList.add(cls); - interesting = true; - } - - if (TransactionEventListener.class.isAssignableFrom(cls)) { - transactionEventListenerList.add(cls); - interesting = true; - } - - if (ScalarType.class.isAssignableFrom(cls)) { - scalarTypeList.add(cls); - interesting = true; - } - - if (ScalarTypeConverter.class.isAssignableFrom(cls)) { - scalarConverterList.add(cls); - interesting = true; - } - - if (CompoundType.class.isAssignableFrom(cls)) { - compoundTypeList.add(cls); - interesting = true; - } - - if (BeanFinder.class.isAssignableFrom(cls)) { - beanFinderList.add(cls); - interesting = true; - } - - if (BeanPersistListener.class.isAssignableFrom(cls)) { - beanListenerList.add(cls); - interesting = true; - } - - if (BeanQueryAdapter.class.isAssignableFrom(cls)) { - beanQueryAdapterList.add(cls); - interesting = true; - } - - if (ServerConfigStartup.class.isAssignableFrom(cls)){ - serverConfigStartupList.add(cls); - interesting = true; - } - - return interesting; - } - - private boolean isEntity(Class cls) { - - Annotation ann = cls.getAnnotation(Entity.class); - if (ann != null) { - return true; - } - ann = cls.getAnnotation(Table.class); - if (ann != null) { - return true; - } - 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> xmlBeanList = new ArrayList>(); + + private ArrayList> embeddableList = new ArrayList>(); + + private ArrayList> entityList = new ArrayList>(); + + private ArrayList> scalarTypeList = new ArrayList>(); + + private ArrayList> scalarConverterList = new ArrayList>(); + + private ArrayList> compoundTypeList = new ArrayList>(); + + private ArrayList> beanControllerList = new ArrayList>(); + + private ArrayList> transactionEventListenerList = new ArrayList>(); + + private ArrayList> beanFinderList = new ArrayList>(); + + private ArrayList> beanListenerList = new ArrayList>(); + + private ArrayList> beanQueryAdapterList = new ArrayList>(); + + private ArrayList> luceneIndexList = new ArrayList>(); + + private ArrayList> serverConfigStartupList = new ArrayList>(); + private ArrayList serverConfigStartupInstances = new ArrayList(); + + private List persistControllerInstances = new ArrayList(); + private List> persistListenerInstances = new ArrayList>(); + private List queryAdapterInstances = new ArrayList(); + private List transactionEventListenerInstances = new ArrayList(); + + public BootupClasses() { + } + + public BootupClasses(List> list) { + if (list != null) { + 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> 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 queryAdapterInstances) { + if (queryAdapterInstances != null) { + for (BeanQueryAdapter a : queryAdapterInstances) { + this.queryAdapterInstances.add(a); + // don't automatically instantiate + this.beanQueryAdapterList.remove(a.getClass()); + } + } + } + + /** + * Add BeanPersistController instances. + */ + public void addPersistControllers(List beanControllerInstances) { + if (beanControllerInstances != null) { + for (BeanPersistController c : beanControllerInstances) { + this.persistControllerInstances.add(c); + // don't automatically instantiate + this.beanControllerList.remove(c.getClass()); + } + } + } + + /** + * Add TransactionEventListeners instances. + */ + public void addTransactionEventListeners(List transactionEventListeners) { + if (transactionEventListeners != null) { + for (TransactionEventListener c : transactionEventListeners) { + this.transactionEventListenerInstances.add(c); + // don't automatically instantiate + this.transactionEventListenerList.remove(c.getClass()); + } + } + } + + public void addPersistListeners(List> listenerInstances) { + if (listenerInstances != null) { + for (BeanPersistListener l : listenerInstances) { + this.persistListenerInstances.add(l); + // don't automatically instantiate + this.beanListenerList.remove(l.getClass()); + } + } + } + + public void addServerConfigStartup(List startupInstances) { + if (startupInstances != null) { + for (ServerConfigStartup l : startupInstances) { + this.serverConfigStartupInstances.add(l); + // don't automatically instantiate + this.serverConfigStartupList.remove(l.getClass()); + } + } + } + + public List getBeanQueryAdapters() { + // add class registered BeanQueryAdapter to the + // already created instances + for (Class cls : beanQueryAdapterList) { + try { + BeanQueryAdapter newInstance = (BeanQueryAdapter) cls.newInstance(); + queryAdapterInstances.add(newInstance); + } catch (Exception e) { + String msg = "Error creating BeanQueryAdapter " + cls; + logger.log(Level.SEVERE, msg, e); + } + } + + return queryAdapterInstances; + } + + public List> getBeanPersistListeners() { + // add class registered BeanPersistController to the + // already created instances + for (Class cls : beanListenerList) { + try { + BeanPersistListener newInstance = (BeanPersistListener) cls.newInstance(); + persistListenerInstances.add(newInstance); + } catch (Exception e) { + String msg = "Error creating BeanPersistController " + cls; + logger.log(Level.SEVERE, msg, e); + } + } + + return persistListenerInstances; + } + + public List getBeanPersistControllers() { + // add class registered BeanPersistController to the + // already created instances + for (Class cls : beanControllerList) { + try { + BeanPersistController newInstance = (BeanPersistController) cls.newInstance(); + persistControllerInstances.add(newInstance); + } catch (Exception e) { + String msg = "Error creating BeanPersistController " + cls; + logger.log(Level.SEVERE, msg, e); + } + } + + return persistControllerInstances; + } + + public List getTransactionEventListeners() { + // add class registered TransactionEventListener to the + // already created instances + for (Class cls : transactionEventListenerList) { + try { + TransactionEventListener newInstance = (TransactionEventListener) cls.newInstance(); + transactionEventListenerInstances.add(newInstance); + } catch (Exception e) { + String msg = "Error creating TransactionEventListener " + cls; + logger.log(Level.SEVERE, msg, e); + } + } + + return transactionEventListenerInstances; + } + + /** + * Return the list of Embeddable classes. + */ + public ArrayList> getEmbeddables() { + return embeddableList; + } + + /** + * Return the list of entity classes. + */ + public ArrayList> getEntities() { + return entityList; + } + + /** + * Return the list of ScalarTypes found. + */ + public ArrayList> getScalarTypes() { + return scalarTypeList; + } + + /** + * Return the list of ScalarConverters found. + */ + public ArrayList> getScalarConverters() { + return scalarConverterList; + } + + /** + * Return the list of ScalarConverters found. + */ + public ArrayList> getCompoundTypes() { + return compoundTypeList; + } + + /** + * Return the list of BeanControllers found. + */ + public ArrayList> getBeanControllers() { + return beanControllerList; + } + + /** + * Return the list of TransactionEventListeners found + */ + public ArrayList> getTransactionEventListenerList() { + return transactionEventListenerList; + } + + /** + * Return the list of BeanFinders found. + */ + public ArrayList> getBeanFinders() { + return beanFinderList; + } + + /** + * Return the list of BeanListeners found. + */ + public ArrayList> getBeanListeners() { + return beanListenerList; + } + + /** + * Return the list of XML Beans. + */ + public ArrayList> getXmlBeanList() { + return xmlBeanList; + } + + public void add(Iterator> 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. + *

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

+ */ + private boolean isInterestingInterface(Class cls) { + + boolean interesting = false; + + if (BeanPersistController.class.isAssignableFrom(cls)) { + beanControllerList.add(cls); + interesting = true; + } + + if (TransactionEventListener.class.isAssignableFrom(cls)) { + transactionEventListenerList.add(cls); + interesting = true; + } + + if (ScalarType.class.isAssignableFrom(cls)) { + scalarTypeList.add(cls); + interesting = true; + } + + if (ScalarTypeConverter.class.isAssignableFrom(cls)) { + scalarConverterList.add(cls); + interesting = true; + } + + if (CompoundType.class.isAssignableFrom(cls)) { + compoundTypeList.add(cls); + interesting = true; + } + + if (BeanFinder.class.isAssignableFrom(cls)) { + beanFinderList.add(cls); + interesting = true; + } + + if (BeanPersistListener.class.isAssignableFrom(cls)) { + beanListenerList.add(cls); + interesting = true; + } + + if (BeanQueryAdapter.class.isAssignableFrom(cls)) { + beanQueryAdapterList.add(cls); + interesting = true; + } + + if (ServerConfigStartup.class.isAssignableFrom(cls)){ + serverConfigStartupList.add(cls); + interesting = true; + } + + return interesting; + } + + private boolean isEntity(Class cls) { + + Annotation ann = cls.getAnnotation(Entity.class); + if (ann != null) { + return true; + } + ann = cls.getAnnotation(Table.class); + if (ann != null) { + return true; + } + 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; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DatabasePlatformFactory.java b/src/main/java/com/avaje/ebeaninternal/server/core/DatabasePlatformFactory.java index 7023acf5e..372fef4a1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DatabasePlatformFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DatabasePlatformFactory.java @@ -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. - *

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

- */ -public class DatabasePlatformFactory { - - private static final Logger logger = 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. + *

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

+ */ +public class DatabasePlatformFactory { + + private static final Logger logger = 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(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBackgroundExecutor.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBackgroundExecutor.java index 026ae1735..2dccea7ae 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBackgroundExecutor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBackgroundExecutor.java @@ -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(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java index 2a3c7ae2b..5b94b3a8f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java @@ -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. - *

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

- *

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

- */ - private int getBatchSize(int 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> batch = loadRequest.getBatch(); - - int batchSize = getBatchSize(batch.size(), loadRequest.getBatchSize()); - - LoadManyContext ctx = loadRequest.getLoadContext(); - BeanPropertyAssocMany many = ctx.getBeanProperty(); - - PersistenceContext pc = ctx.getPersistenceContext(); - - ArrayList idList = new ArrayList(batchSize); - - for (int i = 0; i < batch.size(); i++) { - BeanCollection bc = batch.get(i); - 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 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 idList = new ArrayList(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. + *

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

+ *

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

+ */ + private int getBatchSize(int 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> batch = loadRequest.getBatch(); + + int batchSize = getBatchSize(batch.size(), loadRequest.getBatchSize()); + + LoadManyContext ctx = loadRequest.getLoadContext(); + BeanPropertyAssocMany many = ctx.getBeanProperty(); + + PersistenceContext pc = ctx.getPersistenceContext(); + + ArrayList idList = new ArrayList(batchSize); + + for (int i = 0; i < batch.size(); i++) { + BeanCollection bc = batch.get(i); + 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 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 idList = new ArrayList(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); + } + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultCallableSql.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultCallableSql.java index 389e901b7..5bce1535b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultCallableSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultCallableSql.java @@ -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; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index c47f7be08..05dc938b4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -1,2131 +1,2112 @@ -/** - * 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.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.FutureTask; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.management.InstanceAlreadyExistsException; -import javax.management.MBeanServer; -import javax.management.ObjectName; -import javax.persistence.PersistenceException; - -import com.avaje.ebean.AdminAutofetch; -import com.avaje.ebean.AdminLogging; -import com.avaje.ebean.BackgroundExecutor; -import com.avaje.ebean.BeanState; -import com.avaje.ebean.CallableSql; -import com.avaje.ebean.Ebean; -import com.avaje.ebean.ExpressionFactory; -import com.avaje.ebean.Filter; -import com.avaje.ebean.FutureIds; -import com.avaje.ebean.FutureList; -import com.avaje.ebean.FutureRowCount; -import com.avaje.ebean.InvalidValue; -import com.avaje.ebean.PagingList; -import com.avaje.ebean.Query; -import com.avaje.ebean.QueryIterator; -import com.avaje.ebean.QueryResultVisitor; -import com.avaje.ebean.SqlFutureList; -import com.avaje.ebean.SqlQuery; -import com.avaje.ebean.SqlRow; -import com.avaje.ebean.SqlUpdate; -import com.avaje.ebean.Transaction; -import com.avaje.ebean.TxCallable; -import com.avaje.ebean.TxIsolation; -import com.avaje.ebean.TxRunnable; -import com.avaje.ebean.TxScope; -import com.avaje.ebean.TxType; -import com.avaje.ebean.Update; -import com.avaje.ebean.ValuePair; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.CallStack; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebean.cache.ServerCacheManager; -import com.avaje.ebean.config.EncryptKeyManager; -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebean.config.ldap.LdapConfig; -import com.avaje.ebean.event.BeanPersistController; -import com.avaje.ebean.event.BeanQueryAdapter; -import com.avaje.ebean.text.csv.CsvReader; -import com.avaje.ebean.text.json.JsonContext; -import com.avaje.ebean.text.json.JsonElement; -import com.avaje.ebeaninternal.api.LoadBeanRequest; -import com.avaje.ebeaninternal.api.LoadManyRequest; -import com.avaje.ebeaninternal.api.ScopeTrans; -import com.avaje.ebeaninternal.api.SpiBackgroundExecutor; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.api.SpiQuery.Mode; -import com.avaje.ebeaninternal.api.SpiQuery.Type; -import com.avaje.ebeaninternal.api.SpiSqlQuery; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.api.TransactionEventTable; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; -import com.avaje.ebeaninternal.server.ddl.DdlGenerator; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; -import com.avaje.ebeaninternal.server.deploy.BeanManager; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.DNativeQuery; -import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; -import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; -import com.avaje.ebeaninternal.server.deploy.InheritInfo; -import com.avaje.ebeaninternal.server.el.ElFilter; -import com.avaje.ebeaninternal.server.jmx.MAdminAutofetch; -import com.avaje.ebeaninternal.server.ldap.DefaultLdapOrmQuery; -import com.avaje.ebeaninternal.server.ldap.LdapOrmQueryEngine; -import com.avaje.ebeaninternal.server.ldap.LdapOrmQueryRequest; -import com.avaje.ebeaninternal.server.ldap.expression.LdapExpressionFactory; -import com.avaje.ebeaninternal.server.lib.ShutdownManager; -import com.avaje.ebeaninternal.server.loadcontext.DLoadContext; -import com.avaje.ebeaninternal.server.query.CQuery; -import com.avaje.ebeaninternal.server.query.CQueryEngine; -import com.avaje.ebeaninternal.server.query.CallableQueryIds; -import com.avaje.ebeaninternal.server.query.CallableQueryList; -import com.avaje.ebeaninternal.server.query.CallableQueryRowCount; -import com.avaje.ebeaninternal.server.query.CallableSqlQueryList; -import com.avaje.ebeaninternal.server.query.LimitOffsetPagingQuery; -import com.avaje.ebeaninternal.server.query.QueryFutureIds; -import com.avaje.ebeaninternal.server.query.QueryFutureList; -import com.avaje.ebeaninternal.server.query.QueryFutureRowCount; -import com.avaje.ebeaninternal.server.query.SqlQueryFutureList; -import com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery; -import com.avaje.ebeaninternal.server.querydefn.DefaultOrmUpdate; -import com.avaje.ebeaninternal.server.querydefn.DefaultRelationalQuery; -import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam; -import com.avaje.ebeaninternal.server.text.csv.TCsvReader; -import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext; -import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; -import com.avaje.ebeaninternal.server.transaction.TransactionManager; -import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager; -import com.avaje.ebeaninternal.util.ParamTypeHelper; -import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo; - -/** - * The default server side implementation of EbeanServer. - */ -public final class DefaultServer implements SpiEbeanServer { - - private static final Logger logger = Logger.getLogger(DefaultServer.class.getName()); - - /** - * Used when no errors are found validating a property. - */ - private static final InvalidValue[] EMPTY_INVALID_VALUES = new InvalidValue[0]; - - private final String serverName; - - private final DatabasePlatform databasePlatform; - - private final AdminLogging adminLogging; - - private final AdminAutofetch adminAutofetch; - - private final TransactionManager transactionManager; - - private final TransactionScopeManager transactionScopeManager; - - private final int maxCallStack; - - /** - * Ebean defaults this to true but for EJB compatible behaviour set this to - * false; - */ - private final boolean rollbackOnChecked; - private final boolean defaultDeleteMissingChildren; - private final boolean defaultUpdateNullProperties; - - /** - * Set to true if vanilla objects should be returned by default from queries - * (with dynamic subclassing). - */ - private final boolean vanillaMode; - private final boolean vanillaRefMode; - - private final LdapOrmQueryEngine ldapQueryEngine; - - /** - * Handles the save, delete, updateSql CallableSql. - */ - private final Persister persister; - - private final OrmQueryEngine queryEngine; - - private final RelationalQueryEngine relationalQueryEngine; - - private final ServerCacheManager serverCacheManager; - - private final BeanDescriptorManager beanDescriptorManager; - - private final DiffHelp diffHelp = new DiffHelp(); - - private final AutoFetchManager autoFetchManager; - - private final CQueryEngine cqueryEngine; - - private final DdlGenerator ddlGenerator; - - private final ExpressionFactory ldapExpressionFactory = new LdapExpressionFactory(); - - private final ExpressionFactory expressionFactory; - - private final SpiBackgroundExecutor backgroundExecutor; - - private final DefaultBeanLoader beanLoader; - - private final EncryptKeyManager encryptKeyManager; - - private final JsonContext jsonContext; - - /** - * The MBean name used to register Ebean. - */ - private String mbeanName; - - /** - * The MBeanServer Ebean is registered with. - */ - private MBeanServer mbeanServer; - - /** - * The default batch size for lazy loading beans or collections. - */ - private int lazyLoadBatchSize; - - /** The query batch size */ - private int queryBatchSize; - /** - * JDBC driver specific handling for JDBC batch execution. - */ - private PstmtBatch pstmtBatch; - - /** - * Create the DefaultServer. - */ - public DefaultServer(InternalConfiguration config, ServerCacheManager cache) { - - this.vanillaMode = config.getServerConfig().isVanillaMode(); - this.vanillaRefMode = config.getServerConfig().isVanillaRefMode(); - - this.serverCacheManager = cache; - this.pstmtBatch = config.getPstmtBatch(); - this.databasePlatform = config.getDatabasePlatform(); - this.backgroundExecutor = config.getBackgroundExecutor(); - this.serverName = config.getServerConfig().getName(); - this.lazyLoadBatchSize = config.getServerConfig().getLazyLoadBatchSize(); - this.queryBatchSize = config.getServerConfig().getQueryBatchSize(); - this.cqueryEngine = config.getCQueryEngine(); - this.expressionFactory = config.getExpressionFactory(); - this.adminLogging = config.getLogControl(); - this.encryptKeyManager = config.getServerConfig().getEncryptKeyManager(); - - this.beanDescriptorManager = config.getBeanDescriptorManager(); - beanDescriptorManager.setEbeanServer(this); - - this.maxCallStack = GlobalProperties.getInt("ebean.maxCallStack", 5); - - this.defaultUpdateNullProperties = "true" - .equalsIgnoreCase(config.getServerConfig().getProperty("defaultUpdateNullProperties", "false")); - this.defaultDeleteMissingChildren = "true".equalsIgnoreCase(config.getServerConfig() - .getProperty("defaultDeleteMissingChildren", "true")); - - this.rollbackOnChecked = GlobalProperties.getBoolean("ebean.transaction.rollbackOnChecked", true); - this.transactionManager = config.getTransactionManager(); - this.transactionScopeManager = config.getTransactionScopeManager(); - - this.persister = config.createPersister(this); - this.queryEngine = config.createOrmQueryEngine(); - this.relationalQueryEngine = config.createRelationalQueryEngine(); - - this.autoFetchManager = config.createAutoFetchManager(this); - this.adminAutofetch = new MAdminAutofetch(autoFetchManager); - - this.ddlGenerator = new DdlGenerator(this, config.getDatabasePlatform(), config.getServerConfig()); - this.beanLoader = new DefaultBeanLoader(this, config.getDebugLazyLoad()); - this.jsonContext = config.createJsonContext(this); - - LdapConfig ldapConfig = config.getServerConfig().getLdapConfig(); - if (ldapConfig == null) { - this.ldapQueryEngine = null; - } else { - this.ldapQueryEngine = new LdapOrmQueryEngine(ldapConfig.isVanillaMode(), ldapConfig.getContextFactory()); - } - - ShutdownManager.register(new Shutdown()); - } - - public boolean isDefaultDeleteMissingChildren() { - return defaultDeleteMissingChildren; - } - - public boolean isDefaultUpdateNullProperties() { - return defaultUpdateNullProperties; - } - - public boolean isVanillaMode() { - return vanillaMode; - } - - public int getLazyLoadBatchSize() { - return lazyLoadBatchSize; - } - - public PstmtBatch getPstmtBatch() { - return pstmtBatch; - } - - public DatabasePlatform getDatabasePlatform() { - return databasePlatform; - } - - public BackgroundExecutor getBackgroundExecutor() { - return backgroundExecutor; - } - - public ExpressionFactory getExpressionFactory() { - return expressionFactory; - } - - public DdlGenerator getDdlGenerator() { - return ddlGenerator; - } - - public AdminLogging getAdminLogging() { - return adminLogging; - } - - public AdminAutofetch getAdminAutofetch() { - return adminAutofetch; - } - - public AutoFetchManager getAutoFetchManager() { - return autoFetchManager; - } - - /** - * Run any initialisation required before registering with the ClusterManager. - */ - public void initialise() { - if (encryptKeyManager != null) { - encryptKeyManager.initialise(); - } - List> list = beanDescriptorManager.getBeanDescriptorList(); - for (int i = 0; i < list.size(); i++) { - list.get(i).cacheInitialise(); - } - - } - - /** - * Start any services after registering with the ClusterManager. - */ - public void start() { - } - - public void registerMBeans(MBeanServer mbeanServer, int uniqueServerId) { - - this.mbeanServer = mbeanServer; - this.mbeanName = "Ebean:server=" + serverName + uniqueServerId; - - ObjectName adminName; - ObjectName autofethcName; - try { - adminName = new ObjectName(mbeanName + ",function=Logging"); - autofethcName = new ObjectName(mbeanName + ",key=AutoFetch"); - } catch (Exception e) { - String msg = "Failed to register the JMX beans for Ebean server [" + serverName + "]."; - logger.log(Level.SEVERE, msg, e); - return; - } - - try { - mbeanServer.registerMBean(adminLogging, adminName); - mbeanServer.registerMBean(adminAutofetch, autofethcName); - - } catch (InstanceAlreadyExistsException e) { - // tomcat webapp reloading - String msg = "JMX beans for Ebean server [" + serverName + "] already registered. Will try unregister/register" + e.getMessage(); - logger.log(Level.WARNING, msg); - try { - mbeanServer.unregisterMBean(adminName); - mbeanServer.unregisterMBean(autofethcName); - // re-register - mbeanServer.registerMBean(adminLogging, adminName); - mbeanServer.registerMBean(adminAutofetch, autofethcName); - - } catch (Exception ae) { - String amsg = "Unable to unregister/register the JMX beans for Ebean server [" + serverName + "]."; - logger.log(Level.SEVERE, amsg, ae); - } - } catch (Exception e) { - String msg = "Error registering MBean[" + mbeanName + "]"; - logger.log(Level.SEVERE, msg, e); - } - } - - private final class Shutdown implements Runnable { - public void run() { - try { - if (mbeanServer != null) { - mbeanServer.unregisterMBean(new ObjectName(mbeanName + ",function=Logging")); - mbeanServer.unregisterMBean(new ObjectName(mbeanName + ",key=AutoFetch")); - } - } catch (Exception e) { - String msg = "Error unregistering Ebean " + mbeanName; - logger.log(Level.SEVERE, msg, e); - } - - // shutdown services - transactionManager.shutdown(); - autoFetchManager.shutdown(); - backgroundExecutor.shutdown(); - } - } - - /** - * Return the server name. - */ - public String getName() { - return serverName; - } - - public BeanState getBeanState(Object bean) { - if (bean instanceof EntityBean) { - return new DefaultBeanState((EntityBean) bean); - } - // if using "subclassing" (not enhancement) this will - // return null for 'vanilla' instances (not subclassed) - return null; - } - - /** - * Run the cache warming queries on all beans that have them defined. - */ - public void runCacheWarming() { - List> descList = beanDescriptorManager.getBeanDescriptorList(); - for (int i = 0; i < descList.size(); i++) { - descList.get(i).runCacheWarming(); - } - } - - public void runCacheWarming(Class beanType) { - BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(beanType); - if (desc == null) { - String msg = "Is " + beanType + " an entity? Could not find a BeanDescriptor"; - throw new PersistenceException(msg); - } else { - desc.runCacheWarming(); - } - } - - /** - * Compile a query. Only valid for ORM queries (not LDAP) - */ - public CQuery compileQuery(Query query, Transaction t) { - SpiOrmQueryRequest qr = createQueryRequest(Type.SUBQUERY, query, t); - OrmQueryRequest orm = (OrmQueryRequest) qr; - return cqueryEngine.buildQuery(orm); - } - - public CQueryEngine getQueryEngine() { - return cqueryEngine; - } - - public ServerCacheManager getServerCacheManager() { - return serverCacheManager; - } - - /** - * Return the Profile Listener. - */ - public AutoFetchManager getProfileListener() { - return autoFetchManager; - } - - /** - * Return the Relational query engine. - */ - public RelationalQueryEngine getRelationalQueryEngine() { - return relationalQueryEngine; - } - - public void refreshMany(Object parentBean, String propertyName, Transaction t) { - - beanLoader.refreshMany(parentBean, propertyName, t); - } - - public void refreshMany(Object parentBean, String propertyName) { - - beanLoader.refreshMany(parentBean, propertyName); - } - - public void loadMany(LoadManyRequest loadRequest) { - - beanLoader.loadMany(loadRequest); - } - - public void loadMany(BeanCollection bc, boolean onlyIds) { - - beanLoader.loadMany(bc, null, onlyIds); - } - - public void refresh(Object bean) { - - beanLoader.refresh(bean); - } - - public void loadBean(LoadBeanRequest loadRequest) { - - beanLoader.loadBean(loadRequest); - } - - public void loadBean(EntityBeanIntercept ebi) { - - beanLoader.loadBean(ebi); - } - - public InvalidValue validate(Object bean) { - if (bean == null) { - return null; - } - BeanDescriptor beanDescriptor = getBeanDescriptor(bean.getClass()); - return beanDescriptor.validate(true, bean); - } - - public InvalidValue[] validate(Object bean, String propertyName, Object value) { - if (bean == null) { - return null; - } - BeanDescriptor beanDescriptor = getBeanDescriptor(bean.getClass()); - BeanProperty prop = beanDescriptor.getBeanProperty(propertyName); - if (prop == null) { - String msg = "property " + propertyName + " was not found?"; - throw new PersistenceException(msg); - } - if (value == null) { - value = prop.getValue(bean); - } - List errors = prop.validate(true, value); - if (errors == null) { - return EMPTY_INVALID_VALUES; - } else { - return InvalidValue.toArray(errors); - } - } - - public Map diff(Object a, Object b) { - if (a == null) { - return null; - } - - BeanDescriptor desc = getBeanDescriptor(a.getClass()); - return diffHelp.diff(a, b, desc); - } - - /** - * Process committed beans from another framework or server in another - * cluster. - *

- * This notifies this instance of the framework that beans have been committed - * externally to it. Either by another framework or clustered server. It needs - * to maintain its cache and text indexes appropriately. - *

- */ - public void externalModification(TransactionEventTable tableEvent) { - SpiTransaction t = transactionScopeManager.get(); - if (t != null) { - t.getEvent().add(tableEvent); - } else { - transactionManager.externalModification(tableEvent); - } - } - - /** - * Developer informing eBean that tables where modified outside of eBean. - * Invalidate the cache etc as required. - */ - public void externalModification(String tableName, boolean inserts, boolean updates, boolean deletes) { - - TransactionEventTable evt = new TransactionEventTable(); - evt.add(tableName, inserts, updates, deletes); - - externalModification(evt); - } - - /** - * Clear the query execution statistics. - */ - public void clearQueryStatistics() { - for (BeanDescriptor desc : getBeanDescriptors()) { - desc.clearQueryStatistics(); - } - } - - /** - * Create a new EntityBean bean. - *

- * This will generally return a subclass of the parameter 'type' which - * additionally implements the EntityBean interface. That is, the returned - * bean is typically an instance of a dynamically generated class. - *

- */ - @SuppressWarnings("unchecked") - public T createEntityBean(Class type) { - BeanDescriptor desc = getBeanDescriptor(type); - return (T) desc.createEntityBean(); - } - - public ObjectInputStream createProxyObjectInputStream(InputStream is) { - - try { - return new ProxyBeanObjectInputStream(is, this); - } catch (IOException e) { - throw new PersistenceException(e); - } - } - - /** - * Return a Reference bean. - *

- * If a current transaction is active then this will check the Context of that - * transaction to see if the bean is already loaded. If it is already loaded - * then it will returned that object. - *

- */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public T getReference(Class type, Object id) { - - if (id == null) { - throw new NullPointerException("The id is null"); - } - - BeanDescriptor desc = getBeanDescriptor(type); - // convert the id type if necessary - id = desc.convertId(id); - - Object ref = null; - PersistenceContext ctx = null; - - SpiTransaction t = transactionScopeManager.get(); - if (t != null) { - // first try the persistence context - ctx = t.getPersistenceContext(); - ref = ctx.get(type, id); - } - - if (ref == null) { - InheritInfo inheritInfo = desc.getInheritInfo(); - if (inheritInfo != null) { - // we actually need to do a query because - // we don't know the type without the - // discriminator value - BeanProperty[] idProps = desc.propertiesId(); - String idNames; - switch (idProps.length) { - case 0: - throw new PersistenceException("No ID properties for this type? " + desc); - case 1: - idNames = idProps[0].getName(); - break; - default: - idNames = Arrays.toString(idProps); - idNames = idNames.substring(1, idNames.length() - 1); - } - - // just select the id properties and - // the discriminator column (auto added) - Query query = createQuery(type); - query.select(idNames).setId(id); - - ref = query.findUnique(); - - } else { - // use the default reference options - ref = desc.createReference(vanillaRefMode, null, id, null); - } - - if (ctx != null && (ref instanceof EntityBean)) { - // Not putting a vanilla reference in the persistence context - ctx.put(id, ref); - } - } - return (T) ref; - } - - /** - * Creates a new Transaction that is NOT stored in TransactionThreadLocal. Use - * this when you want a thread to have a second independent transaction. - */ - public Transaction createTransaction() { - - return transactionManager.createTransaction(true, -1); - } - - /** - * Create a transaction additionally specify the Isolation level. - *

- * Note that this transaction is not stored in a thread local. - *

- */ - public Transaction createTransaction(TxIsolation isolation) { - - return transactionManager.createTransaction(true, isolation.getLevel()); - } - - /** - * Log a comment to the transaction log (of the current transaction). - */ - public void logComment(String msg) { - Transaction t = transactionScopeManager.get(); - if (t != null) { - t.log(msg); - } - } - - public T execute(TxCallable c) { - return execute(null, c); - } - - public T execute(TxScope scope, TxCallable c) { - ScopeTrans scopeTrans = createScopeTrans(scope); - try { - return c.call(); - - } catch (Error e) { - throw scopeTrans.caughtError(e); - - } catch (RuntimeException e) { - throw scopeTrans.caughtThrowable(e); - - } finally { - scopeTrans.onFinally(); - } - } - - public void execute(TxRunnable r) { - execute(null, r); - } - - public void execute(TxScope scope, TxRunnable r) { - ScopeTrans scopeTrans = createScopeTrans(scope); - try { - r.run(); - - } catch (Error e) { - throw scopeTrans.caughtError(e); - - } catch (RuntimeException e) { - throw scopeTrans.caughtThrowable(e); - - } finally { - scopeTrans.onFinally(); - } - } - - /** - * Determine whether to create a new transaction or not. - *

- * This will also potentially throw exceptions for MANDATORY and NEVER types. - *

- */ - private boolean createNewTransaction(SpiTransaction t, TxScope scope) { - - TxType type = scope.getType(); - switch (type) { - case REQUIRED: - return t == null; - - case REQUIRES_NEW: - return true; - - case MANDATORY: - if (t == null) { - throw new PersistenceException("Transaction missing when MANDATORY"); - } - return true; - - case NEVER: - if (t != null) { - throw new PersistenceException("Transaction exists for Transactional NEVER"); - } - return false; - - case SUPPORTS: - return false; - - case NOT_SUPPORTED: - throw new RuntimeException("NOT_SUPPORTED should already be handled?"); - - default: - throw new RuntimeException("Should never get here?"); - } - } - - public ScopeTrans createScopeTrans(TxScope txScope) { - - if (txScope == null) { - // create a TxScope with default settings - txScope = new TxScope(); - } - - SpiTransaction suspended = null; - - // get current transaction from ThreadLocal or equivalent - SpiTransaction t = transactionScopeManager.get(); - - boolean newTransaction; - if (txScope.getType().equals(TxType.NOT_SUPPORTED)) { - // Suspend existing transaction and - // run without a transaction in scope - newTransaction = false; - suspended = t; - t = null; - - } else { - // create a new Transaction based on TxType and t - newTransaction = createNewTransaction(t, txScope); - - if (newTransaction) { - // suspend existing transaction (if there is one) - suspended = t; - - // create a new transaction - int isoLevel = -1; - TxIsolation isolation = txScope.getIsolation(); - if (isolation != null) { - isoLevel = isolation.getLevel(); - } - t = transactionManager.createTransaction(true, isoLevel); - } - } - - // replace the current transaction ... ScopeTrans.onFinally() - // has the job of restoring the suspended transaction - transactionScopeManager.replace(t); - - return new ScopeTrans(rollbackOnChecked, newTransaction, t, txScope, suspended, transactionScopeManager); - } - - /** - * Returns the current transaction (or null) from the scope. - */ - public SpiTransaction getCurrentServerTransaction() { - return transactionScopeManager.get(); - } - - /** - * Start a transaction. - *

- * Note that the transaction is stored in a ThreadLocal variable. - *

- */ - public Transaction beginTransaction() { - // start an explicit transaction - SpiTransaction t = transactionManager.createTransaction(true, -1); - transactionScopeManager.set(t); - return t; - } - - /** - * Start a transaction with a specific Isolation Level. - *

- * Note that the transaction is stored in a ThreadLocal variable. - *

- */ - public Transaction beginTransaction(TxIsolation isolation) { - // start an explicit transaction - SpiTransaction t = transactionManager.createTransaction(true, isolation.getLevel()); - transactionScopeManager.set(t); - return t; - } - - /** - * Return the current transaction or null if there is not one currently in - * scope. - */ - public Transaction currentTransaction() { - return transactionScopeManager.get(); - } - - /** - * Commit the current transaction. - */ - public void commitTransaction() { - transactionScopeManager.commit(); - } - - /** - * Rollback the current transaction. - */ - public void rollbackTransaction() { - transactionScopeManager.rollback(); - } - - /** - * If the current transaction has already been committed do nothing otherwise - * rollback the transaction. - *

- * Useful to put in a finally block to ensure the transaction is ended, rather - * than a rollbackTransaction() in each catch block. - *

- *

- * Code example:
- * - *

-   * <code>
-   * Ebean.startTransaction();
-   * try {
-   * 	// do some fetching and or persisting
-   * 
-   * 	// commit at the end
-   * 	Ebean.commitTransaction();
-   * 
-   * } finally {
-   * 	// if commit didn't occur then rollback the transaction
-   * 	Ebean.endTransaction();
-   * }
-   * </code>
-   * 
- * - *

- */ - public void endTransaction() { - transactionScopeManager.end(); - } - - /** - * return the next unique identity value. - *

- * Uses the BeanDescriptor deployment information to determine the sequence to - * use. - *

- */ - public Object nextId(Class beanType) { - BeanDescriptor desc = getBeanDescriptor(beanType); - return desc.nextId(null); - } - - @SuppressWarnings("unchecked") - public void sort(List list, String sortByClause) { - - if (list == null) { - throw new NullPointerException("list is null"); - } - if (sortByClause == null) { - throw new NullPointerException("sortByClause is null"); - } - if (list.size() == 0) { - // don't need to sort an empty list - return; - } - // use first bean in the list as the correct type - Class beanType = (Class) list.get(0).getClass(); - BeanDescriptor beanDescriptor = getBeanDescriptor(beanType); - if (beanDescriptor == null) { - String m = "BeanDescriptor not found, is [" + beanType + "] an entity bean?"; - throw new PersistenceException(m); - } - beanDescriptor.sort(list, sortByClause); - } - - public Query createQuery(Class beanType) throws PersistenceException { - return createQuery(beanType, null); - } - - public Query createNamedQuery(Class beanType, String namedQuery) throws PersistenceException { - - BeanDescriptor desc = getBeanDescriptor(beanType); - if (desc == null) { - throw new PersistenceException("Is " + beanType.getName() + " an Entity Bean? BeanDescriptor not found?"); - } - DeployNamedQuery deployQuery = desc.getNamedQuery(namedQuery); - if (deployQuery == null) { - throw new PersistenceException("named query " + namedQuery + " was not found for " + desc.getFullName()); - } - - // this will parse the query - return new DefaultOrmQuery(beanType, this, expressionFactory, deployQuery); - } - - public Filter filter(Class beanType) { - BeanDescriptor desc = getBeanDescriptor(beanType); - if (desc == null) { - String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; - throw new PersistenceException(m); - } - return new ElFilter(desc); - } - - public CsvReader createCsvReader(Class beanType) { - BeanDescriptor descriptor = getBeanDescriptor(beanType); - if (descriptor == null) { - throw new NullPointerException("BeanDescriptor for " + beanType.getName() + " not found"); - } - return new TCsvReader(this, descriptor); - } - - public Query find(Class beanType) { - return createQuery(beanType); - } - - public Query createQuery(Class beanType, String query) { - BeanDescriptor desc = getBeanDescriptor(beanType); - if (desc == null) { - String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; - throw new PersistenceException(m); - } - switch (desc.getEntityType()) { - case SQL: - if (query != null) { - throw new PersistenceException("You must used Named queries for this Entity " + desc.getFullName()); - } - // use the "default" SqlSelect - DeployNamedQuery defaultSqlSelect = desc.getNamedQuery("default"); - return new DefaultOrmQuery(beanType, this, expressionFactory, defaultSqlSelect); - - case LDAP: - return new DefaultLdapOrmQuery(beanType, this, ldapExpressionFactory, query); - - default: - return new DefaultOrmQuery(beanType, this, expressionFactory, query); - } - } - - public Update createNamedUpdate(Class beanType, String namedUpdate) { - BeanDescriptor desc = getBeanDescriptor(beanType); - if (desc == null) { - String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; - throw new PersistenceException(m); - } - - DeployNamedUpdate deployUpdate = desc.getNamedUpdate(namedUpdate); - if (deployUpdate == null) { - throw new PersistenceException("named update " + namedUpdate + " was not found for " + desc.getFullName()); - } - - return new DefaultOrmUpdate(beanType, this, desc.getBaseTable(), deployUpdate); - } - - public Update createUpdate(Class beanType, String ormUpdate) { - BeanDescriptor desc = getBeanDescriptor(beanType); - if (desc == null) { - String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; - throw new PersistenceException(m); - } - - return new DefaultOrmUpdate(beanType, this, desc.getBaseTable(), ormUpdate); - } - - public SqlQuery createSqlQuery(String sql) { - return new DefaultRelationalQuery(this, sql); - } - - public SqlQuery createNamedSqlQuery(String namedQuery) { - DNativeQuery nq = beanDescriptorManager.getNativeQuery(namedQuery); - if (nq == null) { - throw new PersistenceException("SqlQuery " + namedQuery + " not found."); - } - return new DefaultRelationalQuery(this, nq.getQuery()); - } - - public SqlUpdate createSqlUpdate(String sql) { - return new DefaultSqlUpdate(this, sql); - } - - public CallableSql createCallableSql(String sql) { - return new DefaultCallableSql(this, sql); - } - - public SqlUpdate createNamedSqlUpdate(String namedQuery) { - DNativeQuery nq = beanDescriptorManager.getNativeQuery(namedQuery); - if (nq == null) { - throw new PersistenceException("SqlUpdate " + namedQuery + " not found."); - } - return new DefaultSqlUpdate(this, nq.getQuery()); - } - - public T find(Class beanType, Object uid) { - - return find(beanType, uid, null); - } - - /** - * Find a bean using its unique id. - */ - public T find(Class beanType, Object id, Transaction t) { - - if (id == null) { - throw new NullPointerException("The id is null"); - } - - Query query = createQuery(beanType).setId(id); - return findId(query, t); - } - - private SpiOrmQueryRequest createQueryRequest(Type type, Query query, Transaction t) { - - SpiQuery spiQuery = (SpiQuery) query; - spiQuery.setType(type); - - BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(spiQuery.getBeanType()); - spiQuery.setBeanDescriptor(desc); - - return createQueryRequest(desc, spiQuery, t); - } - - public SpiOrmQueryRequest createQueryRequest(BeanDescriptor desc, SpiQuery query, Transaction t) { - - if (desc.isLdapEntityType()) { - return new LdapOrmQueryRequest(query, desc, ldapQueryEngine); - } - - if (desc.isAutoFetchTunable() && !query.isSqlSelect()) { - // its a tunable query - if (autoFetchManager.tuneQuery(query)) { - // was automatically tuned by Autofetch - } else { - // use deployment FetchType.LAZY/EAGER annotations - // to define the 'default' select clause - query.setDefaultSelectClause(); - } - } - - if (query.selectAllForLazyLoadProperty()) { - // we need to select all properties to ensure the lazy load property - // was included (was not included by default or via autofetch). - if (logger.isLoggable(Level.FINE)) { - logger.log(Level.FINE, "Using selectAllForLazyLoadProperty"); - } - } - - if (true) { - // if determine cost and no origin for Autofetch - if (query.getParentNode() == null) { - CallStack callStack = createCallStack(); - query.setOrigin(callStack); - } - } - - // determine extra joins required to support where clause - // predicates on *ToMany properties - if (query.initManyWhereJoins()) { - // we need a sql distinct now - query.setDistinct(true); - } - - boolean allowOneManyFetch = true; - if (Mode.LAZYLOAD_MANY.equals(query.getMode())) { - allowOneManyFetch = false; - - } else if (query.hasMaxRowsOrFirstRow() && !query.isRawSql() && !query.isSqlSelect() && query.getBackgroundFetchAfter() == 0) { - // convert ALL fetch joins to Many's to be query joins - // so that limit offset type SQL clauses work - allowOneManyFetch = false; - } - - query.convertManyFetchJoinsToQueryJoins(allowOneManyFetch, queryBatchSize); - - SpiTransaction serverTrans = (SpiTransaction) t; - OrmQueryRequest request = new OrmQueryRequest(this, queryEngine, query, desc, serverTrans); - - BeanQueryAdapter queryAdapter = desc.getQueryAdapter(); - if (queryAdapter != null) { - // adaption of the query probably based on the - // current user - queryAdapter.preQuery(request); - } - - // the query hash after any tuning - request.calculateQueryPlanHash(); - - return request; - } - - /** - * Try to get the object out of the persistence context. - */ - @SuppressWarnings("unchecked") - private T findIdCheckPersistenceContextAndCache(Transaction transaction, BeanDescriptor beanDescriptor, SpiQuery query) { - - SpiTransaction t = (SpiTransaction) transaction; - if (t == null) { - t = getCurrentServerTransaction(); - } - PersistenceContext context = null; - if (t != null) { - // first look in the persistence context - context = t.getPersistenceContext(); - if (context != null) { - Object o = context.get(beanDescriptor.getBeanType(), query.getId()); - if (o != null) { - return (T) o; - } - } - } - - if (!beanDescriptor.calculateUseCache(query.isUseBeanCache())) { - // not using bean cache - return null; - } - - // boolean readOnly = beanDescriptor.calculateReadOnly(query.isReadOnly()); - boolean vanilla = query.isVanillaMode(vanillaMode); - Object cachedBean = beanDescriptor.cacheGetBean(query.getId(), vanilla, query.isReadOnly()); - if (cachedBean != null) { - if (context == null) { - context = new DefaultPersistenceContext(); - - } - context.put(query.getId(), cachedBean); - if (!vanilla) { - - DLoadContext loadContext = new DLoadContext(this, beanDescriptor, query.isReadOnly(), false, null, false); - loadContext.setPersistenceContext(context); - - EntityBeanIntercept ebi = ((EntityBean) cachedBean)._ebean_getIntercept(); - ebi.setPersistenceContext(context); - loadContext.register(null, ebi); - - } - } - - return (T) cachedBean; - } - - @SuppressWarnings("unchecked") - private T findId(Query query, Transaction t) { - - SpiQuery spiQuery = (SpiQuery) query; - spiQuery.setType(Type.BEAN); - - BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(spiQuery.getBeanType()); - spiQuery.setBeanDescriptor(desc); - - if (SpiQuery.Mode.NORMAL.equals(spiQuery.getMode()) && !spiQuery.isLoadBeanCache()) { - // See if we can skip doing the fetch completely by getting the bean from the - // persistence context or the bean cache - T bean = findIdCheckPersistenceContextAndCache(t, desc, spiQuery); - if (bean != null) { - return bean; - } - } - - SpiOrmQueryRequest request = createQueryRequest(desc, spiQuery, t); - - try { - request.initTransIfRequired(); - - T bean = (T) request.findId(); - request.endTransIfRequired(); - - return bean; - - } catch (RuntimeException ex) { - request.rollbackTransIfRequired(); - throw ex; - } - } - - public T findUnique(Query query, Transaction t) { - - // actually a find by Id type of query... - // ... perhaps with joins and cache hints? - SpiQuery q = (SpiQuery) query; - Object id = q.getId(); - if (id != null) { - return findId(query, t); - } - - BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(q.getBeanType()); - - if (desc.calculateUseNaturalKeyCache(q.isUseBeanCache())) { - // check if it is a find by unique id - NaturalKeyBindParam keyBindParam = q.getNaturalKeyBindParam(); - if (keyBindParam != null && desc.cacheIsNaturalKey(keyBindParam.getName())) { - Object id2 = desc.cacheGetNaturalKeyId(keyBindParam.getValue()); - if (id2 != null) { - SpiQuery copy = q.copy(); - copy.convertWhereNaturalKeyToId(id2); - return findId(copy, t); - } - } - } - - // a query that is expected to return either 0 or 1 rows - List list = findList(query, t); - - if (list.size() == 0) { - return null; - } else if (list.size() > 1) { - String m = "Unique expecting 0 or 1 rows but got [" + list.size() + "]"; - throw new PersistenceException(m); - } else { - return list.get(0); - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Set findSet(Query query, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.SET, query, t); - - Object result = request.getFromQueryCache(); - if (result != null) { - return (Set) result; - } - - try { - request.initTransIfRequired(); - Set set = (Set) request.findSet(); - request.endTransIfRequired(); - - return set; - - } catch (RuntimeException ex) { - // String stackTrace = throwablePrinter.print(ex); - request.rollbackTransIfRequired(); - throw ex; - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Map findMap(Query query, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.MAP, query, t); - - Object result = request.getFromQueryCache(); - if (result != null) { - return (Map) result; - } - - try { - request.initTransIfRequired(); - Map map = (Map) request.findMap(); - request.endTransIfRequired(); - - return map; - - } catch (RuntimeException ex) { - // String stackTrace = throwablePrinter.print(ex); - request.rollbackTransIfRequired(); - throw ex; - } - } - - public int findRowCount(Query query, Transaction t) { - - SpiQuery copy = ((SpiQuery) query).copy(); - return findRowCountWithCopy(copy, t); - } - - public int findRowCountWithCopy(Query query, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.ROWCOUNT, query, t); - try { - request.initTransIfRequired(); - int rowCount = request.findRowCount(); - request.endTransIfRequired(); - - return rowCount; - - } catch (RuntimeException ex) { - request.rollbackTransIfRequired(); - throw ex; - } - } - - public List findIds(Query query, Transaction t) { - - SpiQuery copy = ((SpiQuery) query).copy(); - - return findIdsWithCopy(copy, t); - } - - public List findIdsWithCopy(Query query, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.ID_LIST, query, t); - try { - request.initTransIfRequired(); - List list = request.findIds(); - request.endTransIfRequired(); - - return list; - - } catch (RuntimeException ex) { - request.rollbackTransIfRequired(); - throw ex; - } - } - - public FutureRowCount findFutureRowCount(Query q, Transaction t) { - - SpiQuery copy = ((SpiQuery) q).copy(); - copy.setFutureFetch(true); - - Transaction newTxn = createTransaction(); - - CallableQueryRowCount call = new CallableQueryRowCount(this, copy, newTxn); - FutureTask futureTask = new FutureTask(call); - - QueryFutureRowCount queryFuture = new QueryFutureRowCount(copy, futureTask); - backgroundExecutor.execute(futureTask); - - return queryFuture; - } - - public FutureIds findFutureIds(Query query, Transaction t) { - - SpiQuery copy = ((SpiQuery) query).copy(); - copy.setFutureFetch(true); - - // this is the list we will put the id's in ... create it now so - // it is available for other threads to read while the id query - // is still executing (we don't need to wait for it to finish) - List idList = Collections.synchronizedList(new ArrayList()); - copy.setIdList(idList); - - Transaction newTxn = createTransaction(); - - CallableQueryIds call = new CallableQueryIds(this, copy, newTxn); - FutureTask> futureTask = new FutureTask>(call); - - QueryFutureIds queryFuture = new QueryFutureIds(copy, futureTask); - - backgroundExecutor.execute(futureTask); - - return queryFuture; - } - - public FutureList findFutureList(Query query, Transaction t) { - - SpiQuery spiQuery = (SpiQuery) query; - spiQuery.setFutureFetch(true); - - if (spiQuery.getPersistenceContext() == null) { - if (t != null) { - spiQuery.setPersistenceContext(((SpiTransaction) t).getPersistenceContext()); - } else { - SpiTransaction st = getCurrentServerTransaction(); - if (st != null) { - spiQuery.setPersistenceContext(st.getPersistenceContext()); - } - } - } - - Transaction newTxn = createTransaction(); - CallableQueryList call = new CallableQueryList(this, query, newTxn); - - FutureTask> futureTask = new FutureTask>(call); - - backgroundExecutor.execute(futureTask); - - return new QueryFutureList(query, futureTask); - } - - public PagingList findPagingList(Query query, Transaction t, int pageSize) { - - SpiQuery spiQuery = (SpiQuery) query; - - // we want to use a single PersistenceContext to be used - // for all the paging queries so we make sure there is a - // PersistenceContext on the query - PersistenceContext pc = spiQuery.getPersistenceContext(); - if (pc == null) { - SpiTransaction currentTransaction = getCurrentServerTransaction(); - if (currentTransaction != null) { - pc = currentTransaction.getPersistenceContext(); - } - if (pc == null) { - pc = new DefaultPersistenceContext(); - } - spiQuery.setPersistenceContext(pc); - } - - return new LimitOffsetPagingQuery(this, spiQuery, pageSize); - } - - public void findVisit(Query query, QueryResultVisitor visitor, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); - - try { - request.initTransIfRequired(); - request.findVisit(visitor); - - } catch (RuntimeException ex) { - request.rollbackTransIfRequired(); - throw ex; - } - } - - public QueryIterator findIterate(Query query, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); - - try { - request.initTransIfRequired(); - return request.findIterate(); - // request.endTransIfRequired(); - - } catch (RuntimeException ex) { - request.rollbackTransIfRequired(); - throw ex; - } - } - - @SuppressWarnings("unchecked") - public List findList(Query query, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); - - Object result = request.getFromQueryCache(); - if (result != null) { - return (List) result; - } - - try { - request.initTransIfRequired(); - List list = request.findList(); - request.endTransIfRequired(); - - return list; - - } catch (RuntimeException ex) { - request.rollbackTransIfRequired(); - throw ex; - } - } - - public SqlRow findUnique(SqlQuery query, Transaction t) { - - // no findId() method for SqlQuery... - // a query that is expected to return either 0 or 1 rows - List list = findList(query, t); - - if (list.size() == 0) { - return null; - - } else if (list.size() > 1) { - String m = "Unique expecting 0 or 1 rows but got [" + list.size() + "]"; - throw new PersistenceException(m); - - } else { - return list.get(0); - } - } - - public SqlFutureList findFutureList(SqlQuery query, Transaction t) { - - SpiSqlQuery spiQuery = (SpiSqlQuery) query; - spiQuery.setFutureFetch(true); - - Transaction newTxn = createTransaction(); - CallableSqlQueryList call = new CallableSqlQueryList(this, query, newTxn); - - FutureTask> futureTask = new FutureTask>(call); - - backgroundExecutor.execute(futureTask); - - return new SqlQueryFutureList(query, futureTask); - } - - public List findList(SqlQuery query, Transaction t) { - - RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t); - - try { - request.initTransIfRequired(); - List list = request.findList(); - request.endTransIfRequired(); - - return list; - - } catch (RuntimeException ex) { - request.rollbackTransIfRequired(); - throw ex; - } - } - - public Set findSet(SqlQuery query, Transaction t) { - - RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t); - - try { - request.initTransIfRequired(); - Set set = request.findSet(); - request.endTransIfRequired(); - - return set; - - } catch (RuntimeException ex) { - request.rollbackTransIfRequired(); - throw ex; - } - } - - public Map findMap(SqlQuery query, Transaction t) { - - RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t); - try { - request.initTransIfRequired(); - Map map = request.findMap(); - request.endTransIfRequired(); - - return map; - - } catch (RuntimeException ex) { - request.rollbackTransIfRequired(); - throw ex; - } - } - - /** - * Persist the bean by either performing an insert or update. - */ - public void save(Object bean) { - save(bean, null); - } - - /** - * Save the bean with an explicit transaction. - */ - public void save(Object bean, Transaction t) { - if (bean == null) { - throw new NullPointerException(Message.msg("bean.isnull")); - } - persister.save(bean, t); - } - - /** - * Force an update using the bean updating non-null properties. - */ - public void update(Object bean) { - update(bean, null, null); - } - - /** - * Force an update using the bean explicitly stating which properties to - * include in the update. - */ - public void update(Object bean, Set updateProps) { - update(bean, updateProps, null); - } - - /** - * Force an update using the bean updating non-null properties. - */ - public void update(Object bean, Transaction t) { - update(bean, null, t); - } - - /** - * Force an update using the bean explicitly stating which properties to - * include in the update. - */ - public void update(Object bean, Set updateProps, Transaction t) { - update(bean, updateProps, t, defaultDeleteMissingChildren, defaultUpdateNullProperties); - } - - /** - * Force an update using the bean explicitly stating which properties to - * include in the update. - */ - public void update(Object bean, Set updateProps, Transaction t, boolean deleteMissingChildren, boolean updateNullProperties) { - if (bean == null) { - throw new NullPointerException(Message.msg("bean.isnull")); - } - persister.forceUpdate(bean, updateProps, t, deleteMissingChildren, updateNullProperties); - } - - /** - * Force the bean to be saved with an explicit insert. - *

- * Typically you would use save() and let Ebean determine if the bean should - * be inserted or updated. This can be useful when you are transferring data - * between databases and want to explicitly insert a bean into a different - * database that it came from. - *

- */ - public void insert(Object bean) { - insert(bean, null); - } - - /** - * Force the bean to be saved with an explicit insert. - *

- * Typically you would use save() and let Ebean determine if the bean should - * be inserted or updated. This can be useful when you are transferring data - * between databases and want to explicitly insert a bean into a different - * database that it came from. - *

- */ - public void insert(Object bean, Transaction t) { - if (bean == null) { - throw new NullPointerException(Message.msg("bean.isnull")); - } - persister.forceInsert(bean, t); - } - - /** - * Delete the associations (from the intersection table) of a ManyToMany given - * the owner bean and the propertyName of the ManyToMany collection. - *

- * This returns the number of associations deleted. - *

- */ - public int deleteManyToManyAssociations(Object ownerBean, String propertyName) { - return deleteManyToManyAssociations(ownerBean, propertyName, null); - } - - /** - * Delete the associations (from the intersection table) of a ManyToMany given - * the owner bean and the propertyName of the ManyToMany collection. - *

- * This returns the number of associations deleted. - *

- */ - public int deleteManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) { - - TransWrapper wrap = initTransIfRequired(t); - try { - SpiTransaction trans = wrap.transaction; - int rc = persister.deleteManyToManyAssociations(ownerBean, propertyName, trans); - wrap.commitIfCreated(); - return rc; - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - /** - * Save the associations of a ManyToMany given the owner bean and the - * propertyName of the ManyToMany collection. - */ - public void saveManyToManyAssociations(Object ownerBean, String propertyName) { - saveManyToManyAssociations(ownerBean, propertyName, null); - } - - /** - * 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) { - - TransWrapper wrap = initTransIfRequired(t); - try { - SpiTransaction trans = wrap.transaction; - - persister.saveManyToManyAssociations(ownerBean, propertyName, trans); - - wrap.commitIfCreated(); - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - public void saveAssociation(Object ownerBean, String propertyName) { - saveAssociation(ownerBean, propertyName, null); - } - - public void saveAssociation(Object ownerBean, String propertyName, Transaction t) { - - if (ownerBean instanceof EntityBean) { - Set loadedProps = ((EntityBean) ownerBean)._ebean_getIntercept().getLoadedProps(); - if (loadedProps != null && !loadedProps.contains(propertyName)) { - // skip as property is not actually loaded in this partially - // loaded bean - logger.fine("Skip saveAssociation as property " + propertyName + " is not loaded"); - return; - } - } - - TransWrapper wrap = initTransIfRequired(t); - try { - SpiTransaction trans = wrap.transaction; - - persister.saveAssociation(ownerBean, propertyName, trans); - - wrap.commitIfCreated(); - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - /** - * Perform an update or insert on each bean in the iterator. Returns the - * number of beans that where saved. - */ - public int save(Iterator it) { - return save(it, null); - } - - /** - * Perform an update or insert on each bean in the collection. Returns the - * number of beans that where saved. - */ - public int save(Collection c) { - return save(c.iterator(), null); - } - - /** - * Save all beans in the iterator with an explicit transaction. - */ - public int save(Iterator it, Transaction t) { - - TransWrapper wrap = initTransIfRequired(t); - try { - SpiTransaction trans = wrap.transaction; - int saveCount = 0; - while (it.hasNext()) { - Object bean = it.next(); - persister.save(bean, trans); - saveCount++; - } - - wrap.commitIfCreated(); - - return saveCount; - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - public int delete(Class beanType, Object id) { - return delete(beanType, id, null); - } - - public int delete(Class beanType, Object id, Transaction t) { - - TransWrapper wrap = initTransIfRequired(t); - try { - SpiTransaction trans = wrap.transaction; - int rowCount = persister.delete(beanType, id, trans); - wrap.commitIfCreated(); - - return rowCount; - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - public void delete(Class beanType, Collection ids) { - delete(beanType, ids, null); - } - - public void delete(Class beanType, Collection ids, Transaction t) { - - TransWrapper wrap = initTransIfRequired(t); - try { - SpiTransaction trans = wrap.transaction; - persister.deleteMany(beanType, ids, trans); - wrap.commitIfCreated(); - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - /** - * Delete the bean. - */ - public void delete(Object bean) { - delete(bean, null); - } - - /** - * Delete the bean with the explicit transaction. - */ - public void delete(Object bean, Transaction t) { - if (bean == null) { - throw new NullPointerException(Message.msg("bean.isnull")); - } - persister.delete(bean, t); - } - - /** - * Delete all the beans in the iterator. - */ - public int delete(Iterator it) { - return delete(it, null); - } - - /** - * Delete all the beans in the collection. - */ - public int delete(Collection c) { - return delete(c.iterator(), null); - } - - /** - * Delete all the beans in the iterator with an explicit transaction. - */ - public int delete(Iterator it, Transaction t) { - - TransWrapper wrap = initTransIfRequired(t); - - try { - SpiTransaction trans = wrap.transaction; - int deleteCount = 0; - while (it.hasNext()) { - Object bean = it.next(); - persister.delete(bean, trans); - deleteCount++; - } - - wrap.commitIfCreated(); - - return deleteCount; - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - /** - * Execute the CallableSql with an explicit transaction. - */ - public int execute(CallableSql callSql, Transaction t) { - return persister.executeCallable(callSql, t); - } - - /** - * Execute the CallableSql. - */ - public int execute(CallableSql callSql) { - return execute(callSql, null); - } - - /** - * Execute the updateSql with an explicit transaction. - */ - public int execute(SqlUpdate updSql, Transaction t) { - return persister.executeSqlUpdate(updSql, t); - } - - /** - * Execute the updateSql. - */ - public int execute(SqlUpdate updSql) { - return execute(updSql, null); - } - - /** - * Execute the updateSql with an explicit transaction. - */ - public int execute(Update update, Transaction t) { - return persister.executeOrmUpdate(update, t); - } - - /** - * Execute the orm update. - */ - public int execute(Update update) { - return execute(update, null); - } - - public BeanManager getBeanManager(Class beanClass) { - return beanDescriptorManager.getBeanManager(beanClass); - } - - /** - * Return all the BeanDescriptors. - */ - public List> getBeanDescriptors() { - return beanDescriptorManager.getBeanDescriptorList(); - } - - public void register(BeanPersistController c) { - List> list = beanDescriptorManager.getBeanDescriptorList(); - for (int i = 0; i < list.size(); i++) { - list.get(i).register(c); - } - } - - public void deregister(BeanPersistController c) { - List> list = beanDescriptorManager.getBeanDescriptorList(); - for (int i = 0; i < list.size(); i++) { - list.get(i).deregister(c); - } - } - - public boolean isSupportedType(java.lang.reflect.Type genericType) { - - TypeInfo typeInfo = ParamTypeHelper.getTypeInfo(genericType); - if (typeInfo == null) { - return false; - } - Class beanType = typeInfo.getBeanType(); - if (JsonElement.class.isAssignableFrom(beanType)) { - return true; - } - return getBeanDescriptor(typeInfo.getBeanType()) != null; - } - - public Object getBeanId(Object bean) { - BeanDescriptor desc = getBeanDescriptor(bean.getClass()); - if (desc == null) { - String m = bean.getClass().getName() + " is NOT an Entity Bean registered with this server?"; - throw new PersistenceException(m); - } - - return desc.getId(bean); - } - - /** - * Return the BeanDescriptor for a given type of bean. - */ - public BeanDescriptor getBeanDescriptor(Class beanClass) { - return beanDescriptorManager.getBeanDescriptor(beanClass); - } - - /** - * Return the BeanDescriptor's for a given table name. - */ - public List> getBeanDescriptors(String tableName) { - return beanDescriptorManager.getBeanDescriptors(tableName); - } - - /** - * Return the BeanDescriptor using its unique id. - */ - public BeanDescriptor getBeanDescriptorById(String descriptorId) { - return beanDescriptorManager.getBeanDescriptorById(descriptorId); - } - - /** - * Another server in the cluster sent this event so that we can inform local - * BeanListeners of inserts updates and deletes that occurred remotely (on - * another server in the cluster). - */ - public void remoteTransactionEvent(RemoteTransactionEvent event) { - transactionManager.remoteTransactionEvent(event); - } - - /** - * Create a transaction if one is not currently active in the - * TransactionThreadLocal. - *

- * Returns a TransWrapper which contains the wasCreated flag. If this is true - * then the transaction was created for this request in which case it will - * need to be committed after the request has been processed. - *

- */ - TransWrapper initTransIfRequired(Transaction t) { - - if (t != null) { - return new TransWrapper((SpiTransaction) t, false); - } - - boolean wasCreated = false; - SpiTransaction trans = transactionScopeManager.get(); - if (trans == null) { - // create a transaction - trans = transactionManager.createTransaction(false, -1); - wasCreated = true; - } - return new TransWrapper(trans, wasCreated); - } - - public SpiTransaction createServerTransaction(boolean isExplicit, int isolationLevel) { - return transactionManager.createTransaction(isExplicit, isolationLevel); - } - - public SpiTransaction createQueryTransaction() { - return transactionManager.createQueryTransaction(); - } - - private static final int IGNORE_LEADING_ELEMENTS = 5; - private static final String AVAJE_EBEAN = Ebean.class.getName().substring(0, 15); - - /** - * Create a CallStack object. - *

- * This trims off the avaje ebean part of the stack trace so that the first - * element in the CallStack should be application code. - *

- */ - public CallStack createCallStack() { - - StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); - - // ignore the first 6 as they are always avaje stack elements - int startIndex = IGNORE_LEADING_ELEMENTS; - - // find the first non-avaje stackElement - for (; startIndex < stackTrace.length; startIndex++) { - if (!stackTrace[startIndex].getClassName().startsWith(AVAJE_EBEAN)) { - break; - } - } - - int stackLength = stackTrace.length - startIndex; - if (stackLength > maxCallStack) { - // maximum of maxCallStack stackTrace elements - stackLength = maxCallStack; - } - - // create the 'interesting' part of the stackTrace - StackTraceElement[] finalTrace = new StackTraceElement[stackLength]; - for (int i = 0; i < stackLength; i++) { - finalTrace[i] = stackTrace[i + startIndex]; - } - - if (stackLength < 1) { - // this should not really happen - throw new RuntimeException("StackTraceElement size 0? stack: " + Arrays.toString(stackTrace)); - } - - return new CallStack(finalTrace); - } - - public JsonContext createJsonContext() { - // immutable thread safe so return shared instance - return jsonContext; - } - -} +package com.avaje.ebeaninternal.server.core; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.FutureTask; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.management.InstanceAlreadyExistsException; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.persistence.PersistenceException; + +import com.avaje.ebean.AdminAutofetch; +import com.avaje.ebean.AdminLogging; +import com.avaje.ebean.BackgroundExecutor; +import com.avaje.ebean.BeanState; +import com.avaje.ebean.CallableSql; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.ExpressionFactory; +import com.avaje.ebean.Filter; +import com.avaje.ebean.FutureIds; +import com.avaje.ebean.FutureList; +import com.avaje.ebean.FutureRowCount; +import com.avaje.ebean.InvalidValue; +import com.avaje.ebean.PagingList; +import com.avaje.ebean.Query; +import com.avaje.ebean.QueryIterator; +import com.avaje.ebean.QueryResultVisitor; +import com.avaje.ebean.SqlFutureList; +import com.avaje.ebean.SqlQuery; +import com.avaje.ebean.SqlRow; +import com.avaje.ebean.SqlUpdate; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.TxCallable; +import com.avaje.ebean.TxIsolation; +import com.avaje.ebean.TxRunnable; +import com.avaje.ebean.TxScope; +import com.avaje.ebean.TxType; +import com.avaje.ebean.Update; +import com.avaje.ebean.ValuePair; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.CallStack; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebean.cache.ServerCacheManager; +import com.avaje.ebean.config.EncryptKeyManager; +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.config.ldap.LdapConfig; +import com.avaje.ebean.event.BeanPersistController; +import com.avaje.ebean.event.BeanQueryAdapter; +import com.avaje.ebean.text.csv.CsvReader; +import com.avaje.ebean.text.json.JsonContext; +import com.avaje.ebean.text.json.JsonElement; +import com.avaje.ebeaninternal.api.LoadBeanRequest; +import com.avaje.ebeaninternal.api.LoadManyRequest; +import com.avaje.ebeaninternal.api.ScopeTrans; +import com.avaje.ebeaninternal.api.SpiBackgroundExecutor; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.api.SpiQuery.Mode; +import com.avaje.ebeaninternal.api.SpiQuery.Type; +import com.avaje.ebeaninternal.api.SpiSqlQuery; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.api.TransactionEventTable; +import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; +import com.avaje.ebeaninternal.server.ddl.DdlGenerator; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; +import com.avaje.ebeaninternal.server.deploy.BeanManager; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.DNativeQuery; +import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; +import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.el.ElFilter; +import com.avaje.ebeaninternal.server.jmx.MAdminAutofetch; +import com.avaje.ebeaninternal.server.ldap.DefaultLdapOrmQuery; +import com.avaje.ebeaninternal.server.ldap.LdapOrmQueryEngine; +import com.avaje.ebeaninternal.server.ldap.LdapOrmQueryRequest; +import com.avaje.ebeaninternal.server.ldap.expression.LdapExpressionFactory; +import com.avaje.ebeaninternal.server.lib.ShutdownManager; +import com.avaje.ebeaninternal.server.loadcontext.DLoadContext; +import com.avaje.ebeaninternal.server.query.CQuery; +import com.avaje.ebeaninternal.server.query.CQueryEngine; +import com.avaje.ebeaninternal.server.query.CallableQueryIds; +import com.avaje.ebeaninternal.server.query.CallableQueryList; +import com.avaje.ebeaninternal.server.query.CallableQueryRowCount; +import com.avaje.ebeaninternal.server.query.CallableSqlQueryList; +import com.avaje.ebeaninternal.server.query.LimitOffsetPagingQuery; +import com.avaje.ebeaninternal.server.query.QueryFutureIds; +import com.avaje.ebeaninternal.server.query.QueryFutureList; +import com.avaje.ebeaninternal.server.query.QueryFutureRowCount; +import com.avaje.ebeaninternal.server.query.SqlQueryFutureList; +import com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery; +import com.avaje.ebeaninternal.server.querydefn.DefaultOrmUpdate; +import com.avaje.ebeaninternal.server.querydefn.DefaultRelationalQuery; +import com.avaje.ebeaninternal.server.querydefn.NaturalKeyBindParam; +import com.avaje.ebeaninternal.server.text.csv.TCsvReader; +import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext; +import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; +import com.avaje.ebeaninternal.server.transaction.TransactionManager; +import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager; +import com.avaje.ebeaninternal.util.ParamTypeHelper; +import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo; + +/** + * The default server side implementation of EbeanServer. + */ +public final class DefaultServer implements SpiEbeanServer { + + private static final Logger logger = Logger.getLogger(DefaultServer.class.getName()); + + /** + * Used when no errors are found validating a property. + */ + private static final InvalidValue[] EMPTY_INVALID_VALUES = new InvalidValue[0]; + + private final String serverName; + + private final DatabasePlatform databasePlatform; + + private final AdminLogging adminLogging; + + private final AdminAutofetch adminAutofetch; + + private final TransactionManager transactionManager; + + private final TransactionScopeManager transactionScopeManager; + + private final int maxCallStack; + + /** + * Ebean defaults this to true but for EJB compatible behaviour set this to + * false; + */ + private final boolean rollbackOnChecked; + private final boolean defaultDeleteMissingChildren; + private final boolean defaultUpdateNullProperties; + + /** + * Set to true if vanilla objects should be returned by default from queries + * (with dynamic subclassing). + */ + private final boolean vanillaMode; + private final boolean vanillaRefMode; + + private final LdapOrmQueryEngine ldapQueryEngine; + + /** + * Handles the save, delete, updateSql CallableSql. + */ + private final Persister persister; + + private final OrmQueryEngine queryEngine; + + private final RelationalQueryEngine relationalQueryEngine; + + private final ServerCacheManager serverCacheManager; + + private final BeanDescriptorManager beanDescriptorManager; + + private final DiffHelp diffHelp = new DiffHelp(); + + private final AutoFetchManager autoFetchManager; + + private final CQueryEngine cqueryEngine; + + private final DdlGenerator ddlGenerator; + + private final ExpressionFactory ldapExpressionFactory = new LdapExpressionFactory(); + + private final ExpressionFactory expressionFactory; + + private final SpiBackgroundExecutor backgroundExecutor; + + private final DefaultBeanLoader beanLoader; + + private final EncryptKeyManager encryptKeyManager; + + private final JsonContext jsonContext; + + /** + * The MBean name used to register Ebean. + */ + private String mbeanName; + + /** + * The MBeanServer Ebean is registered with. + */ + private MBeanServer mbeanServer; + + /** + * The default batch size for lazy loading beans or collections. + */ + private int lazyLoadBatchSize; + + /** The query batch size */ + private int queryBatchSize; + /** + * JDBC driver specific handling for JDBC batch execution. + */ + private PstmtBatch pstmtBatch; + + /** + * Create the DefaultServer. + */ + public DefaultServer(InternalConfiguration config, ServerCacheManager cache) { + + this.vanillaMode = config.getServerConfig().isVanillaMode(); + this.vanillaRefMode = config.getServerConfig().isVanillaRefMode(); + + this.serverCacheManager = cache; + this.pstmtBatch = config.getPstmtBatch(); + this.databasePlatform = config.getDatabasePlatform(); + this.backgroundExecutor = config.getBackgroundExecutor(); + this.serverName = config.getServerConfig().getName(); + this.lazyLoadBatchSize = config.getServerConfig().getLazyLoadBatchSize(); + this.queryBatchSize = config.getServerConfig().getQueryBatchSize(); + this.cqueryEngine = config.getCQueryEngine(); + this.expressionFactory = config.getExpressionFactory(); + this.adminLogging = config.getLogControl(); + this.encryptKeyManager = config.getServerConfig().getEncryptKeyManager(); + + this.beanDescriptorManager = config.getBeanDescriptorManager(); + beanDescriptorManager.setEbeanServer(this); + + this.maxCallStack = GlobalProperties.getInt("ebean.maxCallStack", 5); + + this.defaultUpdateNullProperties = "true" + .equalsIgnoreCase(config.getServerConfig().getProperty("defaultUpdateNullProperties", "false")); + this.defaultDeleteMissingChildren = "true".equalsIgnoreCase(config.getServerConfig() + .getProperty("defaultDeleteMissingChildren", "true")); + + this.rollbackOnChecked = GlobalProperties.getBoolean("ebean.transaction.rollbackOnChecked", true); + this.transactionManager = config.getTransactionManager(); + this.transactionScopeManager = config.getTransactionScopeManager(); + + this.persister = config.createPersister(this); + this.queryEngine = config.createOrmQueryEngine(); + this.relationalQueryEngine = config.createRelationalQueryEngine(); + + this.autoFetchManager = config.createAutoFetchManager(this); + this.adminAutofetch = new MAdminAutofetch(autoFetchManager); + + this.ddlGenerator = new DdlGenerator(this, config.getDatabasePlatform(), config.getServerConfig()); + this.beanLoader = new DefaultBeanLoader(this, config.getDebugLazyLoad()); + this.jsonContext = config.createJsonContext(this); + + LdapConfig ldapConfig = config.getServerConfig().getLdapConfig(); + if (ldapConfig == null) { + this.ldapQueryEngine = null; + } else { + this.ldapQueryEngine = new LdapOrmQueryEngine(ldapConfig.isVanillaMode(), ldapConfig.getContextFactory()); + } + + ShutdownManager.register(new Shutdown()); + } + + public boolean isDefaultDeleteMissingChildren() { + return defaultDeleteMissingChildren; + } + + public boolean isDefaultUpdateNullProperties() { + return defaultUpdateNullProperties; + } + + public boolean isVanillaMode() { + return vanillaMode; + } + + public int getLazyLoadBatchSize() { + return lazyLoadBatchSize; + } + + public PstmtBatch getPstmtBatch() { + return pstmtBatch; + } + + public DatabasePlatform getDatabasePlatform() { + return databasePlatform; + } + + public BackgroundExecutor getBackgroundExecutor() { + return backgroundExecutor; + } + + public ExpressionFactory getExpressionFactory() { + return expressionFactory; + } + + public DdlGenerator getDdlGenerator() { + return ddlGenerator; + } + + public AdminLogging getAdminLogging() { + return adminLogging; + } + + public AdminAutofetch getAdminAutofetch() { + return adminAutofetch; + } + + public AutoFetchManager getAutoFetchManager() { + return autoFetchManager; + } + + /** + * Run any initialisation required before registering with the ClusterManager. + */ + public void initialise() { + if (encryptKeyManager != null) { + encryptKeyManager.initialise(); + } + List> list = beanDescriptorManager.getBeanDescriptorList(); + for (int i = 0; i < list.size(); i++) { + list.get(i).cacheInitialise(); + } + + } + + /** + * Start any services after registering with the ClusterManager. + */ + public void start() { + } + + public void registerMBeans(MBeanServer mbeanServer, int uniqueServerId) { + + this.mbeanServer = mbeanServer; + this.mbeanName = "Ebean:server=" + serverName + uniqueServerId; + + ObjectName adminName; + ObjectName autofethcName; + try { + adminName = new ObjectName(mbeanName + ",function=Logging"); + autofethcName = new ObjectName(mbeanName + ",key=AutoFetch"); + } catch (Exception e) { + String msg = "Failed to register the JMX beans for Ebean server [" + serverName + "]."; + logger.log(Level.SEVERE, msg, e); + return; + } + + try { + mbeanServer.registerMBean(adminLogging, adminName); + mbeanServer.registerMBean(adminAutofetch, autofethcName); + + } catch (InstanceAlreadyExistsException e) { + // tomcat webapp reloading + String msg = "JMX beans for Ebean server [" + serverName + "] already registered. Will try unregister/register" + e.getMessage(); + logger.log(Level.WARNING, msg); + try { + mbeanServer.unregisterMBean(adminName); + mbeanServer.unregisterMBean(autofethcName); + // re-register + mbeanServer.registerMBean(adminLogging, adminName); + mbeanServer.registerMBean(adminAutofetch, autofethcName); + + } catch (Exception ae) { + String amsg = "Unable to unregister/register the JMX beans for Ebean server [" + serverName + "]."; + logger.log(Level.SEVERE, amsg, ae); + } + } catch (Exception e) { + String msg = "Error registering MBean[" + mbeanName + "]"; + logger.log(Level.SEVERE, msg, e); + } + } + + private final class Shutdown implements Runnable { + public void run() { + try { + if (mbeanServer != null) { + mbeanServer.unregisterMBean(new ObjectName(mbeanName + ",function=Logging")); + mbeanServer.unregisterMBean(new ObjectName(mbeanName + ",key=AutoFetch")); + } + } catch (Exception e) { + String msg = "Error unregistering Ebean " + mbeanName; + logger.log(Level.SEVERE, msg, e); + } + + // shutdown services + transactionManager.shutdown(); + autoFetchManager.shutdown(); + backgroundExecutor.shutdown(); + } + } + + /** + * Return the server name. + */ + public String getName() { + return serverName; + } + + public BeanState getBeanState(Object bean) { + if (bean instanceof EntityBean) { + return new DefaultBeanState((EntityBean) bean); + } + // if using "subclassing" (not enhancement) this will + // return null for 'vanilla' instances (not subclassed) + return null; + } + + /** + * Run the cache warming queries on all beans that have them defined. + */ + public void runCacheWarming() { + List> descList = beanDescriptorManager.getBeanDescriptorList(); + for (int i = 0; i < descList.size(); i++) { + descList.get(i).runCacheWarming(); + } + } + + public void runCacheWarming(Class beanType) { + BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(beanType); + if (desc == null) { + String msg = "Is " + beanType + " an entity? Could not find a BeanDescriptor"; + throw new PersistenceException(msg); + } else { + desc.runCacheWarming(); + } + } + + /** + * Compile a query. Only valid for ORM queries (not LDAP) + */ + public CQuery compileQuery(Query query, Transaction t) { + SpiOrmQueryRequest qr = createQueryRequest(Type.SUBQUERY, query, t); + OrmQueryRequest orm = (OrmQueryRequest) qr; + return cqueryEngine.buildQuery(orm); + } + + public CQueryEngine getQueryEngine() { + return cqueryEngine; + } + + public ServerCacheManager getServerCacheManager() { + return serverCacheManager; + } + + /** + * Return the Profile Listener. + */ + public AutoFetchManager getProfileListener() { + return autoFetchManager; + } + + /** + * Return the Relational query engine. + */ + public RelationalQueryEngine getRelationalQueryEngine() { + return relationalQueryEngine; + } + + public void refreshMany(Object parentBean, String propertyName, Transaction t) { + + beanLoader.refreshMany(parentBean, propertyName, t); + } + + public void refreshMany(Object parentBean, String propertyName) { + + beanLoader.refreshMany(parentBean, propertyName); + } + + public void loadMany(LoadManyRequest loadRequest) { + + beanLoader.loadMany(loadRequest); + } + + public void loadMany(BeanCollection bc, boolean onlyIds) { + + beanLoader.loadMany(bc, null, onlyIds); + } + + public void refresh(Object bean) { + + beanLoader.refresh(bean); + } + + public void loadBean(LoadBeanRequest loadRequest) { + + beanLoader.loadBean(loadRequest); + } + + public void loadBean(EntityBeanIntercept ebi) { + + beanLoader.loadBean(ebi); + } + + public InvalidValue validate(Object bean) { + if (bean == null) { + return null; + } + BeanDescriptor beanDescriptor = getBeanDescriptor(bean.getClass()); + return beanDescriptor.validate(true, bean); + } + + public InvalidValue[] validate(Object bean, String propertyName, Object value) { + if (bean == null) { + return null; + } + BeanDescriptor beanDescriptor = getBeanDescriptor(bean.getClass()); + BeanProperty prop = beanDescriptor.getBeanProperty(propertyName); + if (prop == null) { + String msg = "property " + propertyName + " was not found?"; + throw new PersistenceException(msg); + } + if (value == null) { + value = prop.getValue(bean); + } + List errors = prop.validate(true, value); + if (errors == null) { + return EMPTY_INVALID_VALUES; + } else { + return InvalidValue.toArray(errors); + } + } + + public Map diff(Object a, Object b) { + if (a == null) { + return null; + } + + BeanDescriptor desc = getBeanDescriptor(a.getClass()); + return diffHelp.diff(a, b, desc); + } + + /** + * Process committed beans from another framework or server in another + * cluster. + *

+ * This notifies this instance of the framework that beans have been committed + * externally to it. Either by another framework or clustered server. It needs + * to maintain its cache and text indexes appropriately. + *

+ */ + public void externalModification(TransactionEventTable tableEvent) { + SpiTransaction t = transactionScopeManager.get(); + if (t != null) { + t.getEvent().add(tableEvent); + } else { + transactionManager.externalModification(tableEvent); + } + } + + /** + * Developer informing eBean that tables where modified outside of eBean. + * Invalidate the cache etc as required. + */ + public void externalModification(String tableName, boolean inserts, boolean updates, boolean deletes) { + + TransactionEventTable evt = new TransactionEventTable(); + evt.add(tableName, inserts, updates, deletes); + + externalModification(evt); + } + + /** + * Clear the query execution statistics. + */ + public void clearQueryStatistics() { + for (BeanDescriptor desc : getBeanDescriptors()) { + desc.clearQueryStatistics(); + } + } + + /** + * Create a new EntityBean bean. + *

+ * This will generally return a subclass of the parameter 'type' which + * additionally implements the EntityBean interface. That is, the returned + * bean is typically an instance of a dynamically generated class. + *

+ */ + @SuppressWarnings("unchecked") + public T createEntityBean(Class type) { + BeanDescriptor desc = getBeanDescriptor(type); + return (T) desc.createEntityBean(); + } + + public ObjectInputStream createProxyObjectInputStream(InputStream is) { + + try { + return new ProxyBeanObjectInputStream(is, this); + } catch (IOException e) { + throw new PersistenceException(e); + } + } + + /** + * Return a Reference bean. + *

+ * If a current transaction is active then this will check the Context of that + * transaction to see if the bean is already loaded. If it is already loaded + * then it will returned that object. + *

+ */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public T getReference(Class type, Object id) { + + if (id == null) { + throw new NullPointerException("The id is null"); + } + + BeanDescriptor desc = getBeanDescriptor(type); + // convert the id type if necessary + id = desc.convertId(id); + + Object ref = null; + PersistenceContext ctx = null; + + SpiTransaction t = transactionScopeManager.get(); + if (t != null) { + // first try the persistence context + ctx = t.getPersistenceContext(); + ref = ctx.get(type, id); + } + + if (ref == null) { + InheritInfo inheritInfo = desc.getInheritInfo(); + if (inheritInfo != null) { + // we actually need to do a query because + // we don't know the type without the + // discriminator value + BeanProperty[] idProps = desc.propertiesId(); + String idNames; + switch (idProps.length) { + case 0: + throw new PersistenceException("No ID properties for this type? " + desc); + case 1: + idNames = idProps[0].getName(); + break; + default: + idNames = Arrays.toString(idProps); + idNames = idNames.substring(1, idNames.length() - 1); + } + + // just select the id properties and + // the discriminator column (auto added) + Query query = createQuery(type); + query.select(idNames).setId(id); + + ref = query.findUnique(); + + } else { + // use the default reference options + ref = desc.createReference(vanillaRefMode, null, id, null); + } + + if (ctx != null && (ref instanceof EntityBean)) { + // Not putting a vanilla reference in the persistence context + ctx.put(id, ref); + } + } + return (T) ref; + } + + /** + * Creates a new Transaction that is NOT stored in TransactionThreadLocal. Use + * this when you want a thread to have a second independent transaction. + */ + public Transaction createTransaction() { + + return transactionManager.createTransaction(true, -1); + } + + /** + * Create a transaction additionally specify the Isolation level. + *

+ * Note that this transaction is not stored in a thread local. + *

+ */ + public Transaction createTransaction(TxIsolation isolation) { + + return transactionManager.createTransaction(true, isolation.getLevel()); + } + + /** + * Log a comment to the transaction log (of the current transaction). + */ + public void logComment(String msg) { + Transaction t = transactionScopeManager.get(); + if (t != null) { + t.log(msg); + } + } + + public T execute(TxCallable c) { + return execute(null, c); + } + + public T execute(TxScope scope, TxCallable c) { + ScopeTrans scopeTrans = createScopeTrans(scope); + try { + return c.call(); + + } catch (Error e) { + throw scopeTrans.caughtError(e); + + } catch (RuntimeException e) { + throw scopeTrans.caughtThrowable(e); + + } finally { + scopeTrans.onFinally(); + } + } + + public void execute(TxRunnable r) { + execute(null, r); + } + + public void execute(TxScope scope, TxRunnable r) { + ScopeTrans scopeTrans = createScopeTrans(scope); + try { + r.run(); + + } catch (Error e) { + throw scopeTrans.caughtError(e); + + } catch (RuntimeException e) { + throw scopeTrans.caughtThrowable(e); + + } finally { + scopeTrans.onFinally(); + } + } + + /** + * Determine whether to create a new transaction or not. + *

+ * This will also potentially throw exceptions for MANDATORY and NEVER types. + *

+ */ + private boolean createNewTransaction(SpiTransaction t, TxScope scope) { + + TxType type = scope.getType(); + switch (type) { + case REQUIRED: + return t == null; + + case REQUIRES_NEW: + return true; + + case MANDATORY: + if (t == null) { + throw new PersistenceException("Transaction missing when MANDATORY"); + } + return true; + + case NEVER: + if (t != null) { + throw new PersistenceException("Transaction exists for Transactional NEVER"); + } + return false; + + case SUPPORTS: + return false; + + case NOT_SUPPORTED: + throw new RuntimeException("NOT_SUPPORTED should already be handled?"); + + default: + throw new RuntimeException("Should never get here?"); + } + } + + public ScopeTrans createScopeTrans(TxScope txScope) { + + if (txScope == null) { + // create a TxScope with default settings + txScope = new TxScope(); + } + + SpiTransaction suspended = null; + + // get current transaction from ThreadLocal or equivalent + SpiTransaction t = transactionScopeManager.get(); + + boolean newTransaction; + if (txScope.getType().equals(TxType.NOT_SUPPORTED)) { + // Suspend existing transaction and + // run without a transaction in scope + newTransaction = false; + suspended = t; + t = null; + + } else { + // create a new Transaction based on TxType and t + newTransaction = createNewTransaction(t, txScope); + + if (newTransaction) { + // suspend existing transaction (if there is one) + suspended = t; + + // create a new transaction + int isoLevel = -1; + TxIsolation isolation = txScope.getIsolation(); + if (isolation != null) { + isoLevel = isolation.getLevel(); + } + t = transactionManager.createTransaction(true, isoLevel); + } + } + + // replace the current transaction ... ScopeTrans.onFinally() + // has the job of restoring the suspended transaction + transactionScopeManager.replace(t); + + return new ScopeTrans(rollbackOnChecked, newTransaction, t, txScope, suspended, transactionScopeManager); + } + + /** + * Returns the current transaction (or null) from the scope. + */ + public SpiTransaction getCurrentServerTransaction() { + return transactionScopeManager.get(); + } + + /** + * Start a transaction. + *

+ * Note that the transaction is stored in a ThreadLocal variable. + *

+ */ + public Transaction beginTransaction() { + // start an explicit transaction + SpiTransaction t = transactionManager.createTransaction(true, -1); + transactionScopeManager.set(t); + return t; + } + + /** + * Start a transaction with a specific Isolation Level. + *

+ * Note that the transaction is stored in a ThreadLocal variable. + *

+ */ + public Transaction beginTransaction(TxIsolation isolation) { + // start an explicit transaction + SpiTransaction t = transactionManager.createTransaction(true, isolation.getLevel()); + transactionScopeManager.set(t); + return t; + } + + /** + * Return the current transaction or null if there is not one currently in + * scope. + */ + public Transaction currentTransaction() { + return transactionScopeManager.get(); + } + + /** + * Commit the current transaction. + */ + public void commitTransaction() { + transactionScopeManager.commit(); + } + + /** + * Rollback the current transaction. + */ + public void rollbackTransaction() { + transactionScopeManager.rollback(); + } + + /** + * If the current transaction has already been committed do nothing otherwise + * rollback the transaction. + *

+ * Useful to put in a finally block to ensure the transaction is ended, rather + * than a rollbackTransaction() in each catch block. + *

+ *

+ * Code example:
+ * + *

+   * <code>
+   * Ebean.startTransaction();
+   * try {
+   * 	// do some fetching and or persisting
+   * 
+   * 	// commit at the end
+   * 	Ebean.commitTransaction();
+   * 
+   * } finally {
+   * 	// if commit didn't occur then rollback the transaction
+   * 	Ebean.endTransaction();
+   * }
+   * </code>
+   * 
+ * + *

+ */ + public void endTransaction() { + transactionScopeManager.end(); + } + + /** + * return the next unique identity value. + *

+ * Uses the BeanDescriptor deployment information to determine the sequence to + * use. + *

+ */ + public Object nextId(Class beanType) { + BeanDescriptor desc = getBeanDescriptor(beanType); + return desc.nextId(null); + } + + @SuppressWarnings("unchecked") + public void sort(List list, String sortByClause) { + + if (list == null) { + throw new NullPointerException("list is null"); + } + if (sortByClause == null) { + throw new NullPointerException("sortByClause is null"); + } + if (list.size() == 0) { + // don't need to sort an empty list + return; + } + // use first bean in the list as the correct type + Class beanType = (Class) list.get(0).getClass(); + BeanDescriptor beanDescriptor = getBeanDescriptor(beanType); + if (beanDescriptor == null) { + String m = "BeanDescriptor not found, is [" + beanType + "] an entity bean?"; + throw new PersistenceException(m); + } + beanDescriptor.sort(list, sortByClause); + } + + public Query createQuery(Class beanType) throws PersistenceException { + return createQuery(beanType, null); + } + + public Query createNamedQuery(Class beanType, String namedQuery) throws PersistenceException { + + BeanDescriptor desc = getBeanDescriptor(beanType); + if (desc == null) { + throw new PersistenceException("Is " + beanType.getName() + " an Entity Bean? BeanDescriptor not found?"); + } + DeployNamedQuery deployQuery = desc.getNamedQuery(namedQuery); + if (deployQuery == null) { + throw new PersistenceException("named query " + namedQuery + " was not found for " + desc.getFullName()); + } + + // this will parse the query + return new DefaultOrmQuery(beanType, this, expressionFactory, deployQuery); + } + + public Filter filter(Class beanType) { + BeanDescriptor desc = getBeanDescriptor(beanType); + if (desc == null) { + String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; + throw new PersistenceException(m); + } + return new ElFilter(desc); + } + + public CsvReader createCsvReader(Class beanType) { + BeanDescriptor descriptor = getBeanDescriptor(beanType); + if (descriptor == null) { + throw new NullPointerException("BeanDescriptor for " + beanType.getName() + " not found"); + } + return new TCsvReader(this, descriptor); + } + + public Query find(Class beanType) { + return createQuery(beanType); + } + + public Query createQuery(Class beanType, String query) { + BeanDescriptor desc = getBeanDescriptor(beanType); + if (desc == null) { + String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; + throw new PersistenceException(m); + } + switch (desc.getEntityType()) { + case SQL: + if (query != null) { + throw new PersistenceException("You must used Named queries for this Entity " + desc.getFullName()); + } + // use the "default" SqlSelect + DeployNamedQuery defaultSqlSelect = desc.getNamedQuery("default"); + return new DefaultOrmQuery(beanType, this, expressionFactory, defaultSqlSelect); + + case LDAP: + return new DefaultLdapOrmQuery(beanType, this, ldapExpressionFactory, query); + + default: + return new DefaultOrmQuery(beanType, this, expressionFactory, query); + } + } + + public Update createNamedUpdate(Class beanType, String namedUpdate) { + BeanDescriptor desc = getBeanDescriptor(beanType); + if (desc == null) { + String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; + throw new PersistenceException(m); + } + + DeployNamedUpdate deployUpdate = desc.getNamedUpdate(namedUpdate); + if (deployUpdate == null) { + throw new PersistenceException("named update " + namedUpdate + " was not found for " + desc.getFullName()); + } + + return new DefaultOrmUpdate(beanType, this, desc.getBaseTable(), deployUpdate); + } + + public Update createUpdate(Class beanType, String ormUpdate) { + BeanDescriptor desc = getBeanDescriptor(beanType); + if (desc == null) { + String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; + throw new PersistenceException(m); + } + + return new DefaultOrmUpdate(beanType, this, desc.getBaseTable(), ormUpdate); + } + + public SqlQuery createSqlQuery(String sql) { + return new DefaultRelationalQuery(this, sql); + } + + public SqlQuery createNamedSqlQuery(String namedQuery) { + DNativeQuery nq = beanDescriptorManager.getNativeQuery(namedQuery); + if (nq == null) { + throw new PersistenceException("SqlQuery " + namedQuery + " not found."); + } + return new DefaultRelationalQuery(this, nq.getQuery()); + } + + public SqlUpdate createSqlUpdate(String sql) { + return new DefaultSqlUpdate(this, sql); + } + + public CallableSql createCallableSql(String sql) { + return new DefaultCallableSql(this, sql); + } + + public SqlUpdate createNamedSqlUpdate(String namedQuery) { + DNativeQuery nq = beanDescriptorManager.getNativeQuery(namedQuery); + if (nq == null) { + throw new PersistenceException("SqlUpdate " + namedQuery + " not found."); + } + return new DefaultSqlUpdate(this, nq.getQuery()); + } + + public T find(Class beanType, Object uid) { + + return find(beanType, uid, null); + } + + /** + * Find a bean using its unique id. + */ + public T find(Class beanType, Object id, Transaction t) { + + if (id == null) { + throw new NullPointerException("The id is null"); + } + + Query query = createQuery(beanType).setId(id); + return findId(query, t); + } + + private SpiOrmQueryRequest createQueryRequest(Type type, Query query, Transaction t) { + + SpiQuery spiQuery = (SpiQuery) query; + spiQuery.setType(type); + + BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(spiQuery.getBeanType()); + spiQuery.setBeanDescriptor(desc); + + return createQueryRequest(desc, spiQuery, t); + } + + public SpiOrmQueryRequest createQueryRequest(BeanDescriptor desc, SpiQuery query, Transaction t) { + + if (desc.isLdapEntityType()) { + return new LdapOrmQueryRequest(query, desc, ldapQueryEngine); + } + + if (desc.isAutoFetchTunable() && !query.isSqlSelect()) { + // its a tunable query + if (autoFetchManager.tuneQuery(query)) { + // was automatically tuned by Autofetch + } else { + // use deployment FetchType.LAZY/EAGER annotations + // to define the 'default' select clause + query.setDefaultSelectClause(); + } + } + + if (query.selectAllForLazyLoadProperty()) { + // we need to select all properties to ensure the lazy load property + // was included (was not included by default or via autofetch). + if (logger.isLoggable(Level.FINE)) { + logger.log(Level.FINE, "Using selectAllForLazyLoadProperty"); + } + } + + if (true) { + // if determine cost and no origin for Autofetch + if (query.getParentNode() == null) { + CallStack callStack = createCallStack(); + query.setOrigin(callStack); + } + } + + // determine extra joins required to support where clause + // predicates on *ToMany properties + if (query.initManyWhereJoins()) { + // we need a sql distinct now + query.setDistinct(true); + } + + boolean allowOneManyFetch = true; + if (Mode.LAZYLOAD_MANY.equals(query.getMode())) { + allowOneManyFetch = false; + + } else if (query.hasMaxRowsOrFirstRow() && !query.isRawSql() && !query.isSqlSelect() && query.getBackgroundFetchAfter() == 0) { + // convert ALL fetch joins to Many's to be query joins + // so that limit offset type SQL clauses work + allowOneManyFetch = false; + } + + query.convertManyFetchJoinsToQueryJoins(allowOneManyFetch, queryBatchSize); + + SpiTransaction serverTrans = (SpiTransaction) t; + OrmQueryRequest request = new OrmQueryRequest(this, queryEngine, query, desc, serverTrans); + + BeanQueryAdapter queryAdapter = desc.getQueryAdapter(); + if (queryAdapter != null) { + // adaption of the query probably based on the + // current user + queryAdapter.preQuery(request); + } + + // the query hash after any tuning + request.calculateQueryPlanHash(); + + return request; + } + + /** + * Try to get the object out of the persistence context. + */ + @SuppressWarnings("unchecked") + private T findIdCheckPersistenceContextAndCache(Transaction transaction, BeanDescriptor beanDescriptor, SpiQuery query) { + + SpiTransaction t = (SpiTransaction) transaction; + if (t == null) { + t = getCurrentServerTransaction(); + } + PersistenceContext context = null; + if (t != null) { + // first look in the persistence context + context = t.getPersistenceContext(); + if (context != null) { + Object o = context.get(beanDescriptor.getBeanType(), query.getId()); + if (o != null) { + return (T) o; + } + } + } + + if (!beanDescriptor.calculateUseCache(query.isUseBeanCache())) { + // not using bean cache + return null; + } + + // boolean readOnly = beanDescriptor.calculateReadOnly(query.isReadOnly()); + boolean vanilla = query.isVanillaMode(vanillaMode); + Object cachedBean = beanDescriptor.cacheGetBean(query.getId(), vanilla, query.isReadOnly()); + if (cachedBean != null) { + if (context == null) { + context = new DefaultPersistenceContext(); + + } + context.put(query.getId(), cachedBean); + if (!vanilla) { + + DLoadContext loadContext = new DLoadContext(this, beanDescriptor, query.isReadOnly(), false, null, false); + loadContext.setPersistenceContext(context); + + EntityBeanIntercept ebi = ((EntityBean) cachedBean)._ebean_getIntercept(); + ebi.setPersistenceContext(context); + loadContext.register(null, ebi); + + } + } + + return (T) cachedBean; + } + + @SuppressWarnings("unchecked") + private T findId(Query query, Transaction t) { + + SpiQuery spiQuery = (SpiQuery) query; + spiQuery.setType(Type.BEAN); + + BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(spiQuery.getBeanType()); + spiQuery.setBeanDescriptor(desc); + + if (SpiQuery.Mode.NORMAL.equals(spiQuery.getMode()) && !spiQuery.isLoadBeanCache()) { + // See if we can skip doing the fetch completely by getting the bean from the + // persistence context or the bean cache + T bean = findIdCheckPersistenceContextAndCache(t, desc, spiQuery); + if (bean != null) { + return bean; + } + } + + SpiOrmQueryRequest request = createQueryRequest(desc, spiQuery, t); + + try { + request.initTransIfRequired(); + + T bean = (T) request.findId(); + request.endTransIfRequired(); + + return bean; + + } catch (RuntimeException ex) { + request.rollbackTransIfRequired(); + throw ex; + } + } + + public T findUnique(Query query, Transaction t) { + + // actually a find by Id type of query... + // ... perhaps with joins and cache hints? + SpiQuery q = (SpiQuery) query; + Object id = q.getId(); + if (id != null) { + return findId(query, t); + } + + BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(q.getBeanType()); + + if (desc.calculateUseNaturalKeyCache(q.isUseBeanCache())) { + // check if it is a find by unique id + NaturalKeyBindParam keyBindParam = q.getNaturalKeyBindParam(); + if (keyBindParam != null && desc.cacheIsNaturalKey(keyBindParam.getName())) { + Object id2 = desc.cacheGetNaturalKeyId(keyBindParam.getValue()); + if (id2 != null) { + SpiQuery copy = q.copy(); + copy.convertWhereNaturalKeyToId(id2); + return findId(copy, t); + } + } + } + + // a query that is expected to return either 0 or 1 rows + List list = findList(query, t); + + if (list.size() == 0) { + return null; + } else if (list.size() > 1) { + String m = "Unique expecting 0 or 1 rows but got [" + list.size() + "]"; + throw new PersistenceException(m); + } else { + return list.get(0); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Set findSet(Query query, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.SET, query, t); + + Object result = request.getFromQueryCache(); + if (result != null) { + return (Set) result; + } + + try { + request.initTransIfRequired(); + Set set = (Set) request.findSet(); + request.endTransIfRequired(); + + return set; + + } catch (RuntimeException ex) { + // String stackTrace = throwablePrinter.print(ex); + request.rollbackTransIfRequired(); + throw ex; + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Map findMap(Query query, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.MAP, query, t); + + Object result = request.getFromQueryCache(); + if (result != null) { + return (Map) result; + } + + try { + request.initTransIfRequired(); + Map map = (Map) request.findMap(); + request.endTransIfRequired(); + + return map; + + } catch (RuntimeException ex) { + // String stackTrace = throwablePrinter.print(ex); + request.rollbackTransIfRequired(); + throw ex; + } + } + + public int findRowCount(Query query, Transaction t) { + + SpiQuery copy = ((SpiQuery) query).copy(); + return findRowCountWithCopy(copy, t); + } + + public int findRowCountWithCopy(Query query, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.ROWCOUNT, query, t); + try { + request.initTransIfRequired(); + int rowCount = request.findRowCount(); + request.endTransIfRequired(); + + return rowCount; + + } catch (RuntimeException ex) { + request.rollbackTransIfRequired(); + throw ex; + } + } + + public List findIds(Query query, Transaction t) { + + SpiQuery copy = ((SpiQuery) query).copy(); + + return findIdsWithCopy(copy, t); + } + + public List findIdsWithCopy(Query query, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.ID_LIST, query, t); + try { + request.initTransIfRequired(); + List list = request.findIds(); + request.endTransIfRequired(); + + return list; + + } catch (RuntimeException ex) { + request.rollbackTransIfRequired(); + throw ex; + } + } + + public FutureRowCount findFutureRowCount(Query q, Transaction t) { + + SpiQuery copy = ((SpiQuery) q).copy(); + copy.setFutureFetch(true); + + Transaction newTxn = createTransaction(); + + CallableQueryRowCount call = new CallableQueryRowCount(this, copy, newTxn); + FutureTask futureTask = new FutureTask(call); + + QueryFutureRowCount queryFuture = new QueryFutureRowCount(copy, futureTask); + backgroundExecutor.execute(futureTask); + + return queryFuture; + } + + public FutureIds findFutureIds(Query query, Transaction t) { + + SpiQuery copy = ((SpiQuery) query).copy(); + copy.setFutureFetch(true); + + // this is the list we will put the id's in ... create it now so + // it is available for other threads to read while the id query + // is still executing (we don't need to wait for it to finish) + List idList = Collections.synchronizedList(new ArrayList()); + copy.setIdList(idList); + + Transaction newTxn = createTransaction(); + + CallableQueryIds call = new CallableQueryIds(this, copy, newTxn); + FutureTask> futureTask = new FutureTask>(call); + + QueryFutureIds queryFuture = new QueryFutureIds(copy, futureTask); + + backgroundExecutor.execute(futureTask); + + return queryFuture; + } + + public FutureList findFutureList(Query query, Transaction t) { + + SpiQuery spiQuery = (SpiQuery) query; + spiQuery.setFutureFetch(true); + + if (spiQuery.getPersistenceContext() == null) { + if (t != null) { + spiQuery.setPersistenceContext(((SpiTransaction) t).getPersistenceContext()); + } else { + SpiTransaction st = getCurrentServerTransaction(); + if (st != null) { + spiQuery.setPersistenceContext(st.getPersistenceContext()); + } + } + } + + Transaction newTxn = createTransaction(); + CallableQueryList call = new CallableQueryList(this, query, newTxn); + + FutureTask> futureTask = new FutureTask>(call); + + backgroundExecutor.execute(futureTask); + + return new QueryFutureList(query, futureTask); + } + + public PagingList findPagingList(Query query, Transaction t, int pageSize) { + + SpiQuery spiQuery = (SpiQuery) query; + + // we want to use a single PersistenceContext to be used + // for all the paging queries so we make sure there is a + // PersistenceContext on the query + PersistenceContext pc = spiQuery.getPersistenceContext(); + if (pc == null) { + SpiTransaction currentTransaction = getCurrentServerTransaction(); + if (currentTransaction != null) { + pc = currentTransaction.getPersistenceContext(); + } + if (pc == null) { + pc = new DefaultPersistenceContext(); + } + spiQuery.setPersistenceContext(pc); + } + + return new LimitOffsetPagingQuery(this, spiQuery, pageSize); + } + + public void findVisit(Query query, QueryResultVisitor visitor, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); + + try { + request.initTransIfRequired(); + request.findVisit(visitor); + + } catch (RuntimeException ex) { + request.rollbackTransIfRequired(); + throw ex; + } + } + + public QueryIterator findIterate(Query query, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); + + try { + request.initTransIfRequired(); + return request.findIterate(); + // request.endTransIfRequired(); + + } catch (RuntimeException ex) { + request.rollbackTransIfRequired(); + throw ex; + } + } + + @SuppressWarnings("unchecked") + public List findList(Query query, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); + + Object result = request.getFromQueryCache(); + if (result != null) { + return (List) result; + } + + try { + request.initTransIfRequired(); + List list = request.findList(); + request.endTransIfRequired(); + + return list; + + } catch (RuntimeException ex) { + request.rollbackTransIfRequired(); + throw ex; + } + } + + public SqlRow findUnique(SqlQuery query, Transaction t) { + + // no findId() method for SqlQuery... + // a query that is expected to return either 0 or 1 rows + List list = findList(query, t); + + if (list.size() == 0) { + return null; + + } else if (list.size() > 1) { + String m = "Unique expecting 0 or 1 rows but got [" + list.size() + "]"; + throw new PersistenceException(m); + + } else { + return list.get(0); + } + } + + public SqlFutureList findFutureList(SqlQuery query, Transaction t) { + + SpiSqlQuery spiQuery = (SpiSqlQuery) query; + spiQuery.setFutureFetch(true); + + Transaction newTxn = createTransaction(); + CallableSqlQueryList call = new CallableSqlQueryList(this, query, newTxn); + + FutureTask> futureTask = new FutureTask>(call); + + backgroundExecutor.execute(futureTask); + + return new SqlQueryFutureList(query, futureTask); + } + + public List findList(SqlQuery query, Transaction t) { + + RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t); + + try { + request.initTransIfRequired(); + List list = request.findList(); + request.endTransIfRequired(); + + return list; + + } catch (RuntimeException ex) { + request.rollbackTransIfRequired(); + throw ex; + } + } + + public Set findSet(SqlQuery query, Transaction t) { + + RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t); + + try { + request.initTransIfRequired(); + Set set = request.findSet(); + request.endTransIfRequired(); + + return set; + + } catch (RuntimeException ex) { + request.rollbackTransIfRequired(); + throw ex; + } + } + + public Map findMap(SqlQuery query, Transaction t) { + + RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t); + try { + request.initTransIfRequired(); + Map map = request.findMap(); + request.endTransIfRequired(); + + return map; + + } catch (RuntimeException ex) { + request.rollbackTransIfRequired(); + throw ex; + } + } + + /** + * Persist the bean by either performing an insert or update. + */ + public void save(Object bean) { + save(bean, null); + } + + /** + * Save the bean with an explicit transaction. + */ + public void save(Object bean, Transaction t) { + if (bean == null) { + throw new NullPointerException(Message.msg("bean.isnull")); + } + persister.save(bean, t); + } + + /** + * Force an update using the bean updating non-null properties. + */ + public void update(Object bean) { + update(bean, null, null); + } + + /** + * Force an update using the bean explicitly stating which properties to + * include in the update. + */ + public void update(Object bean, Set updateProps) { + update(bean, updateProps, null); + } + + /** + * Force an update using the bean updating non-null properties. + */ + public void update(Object bean, Transaction t) { + update(bean, null, t); + } + + /** + * Force an update using the bean explicitly stating which properties to + * include in the update. + */ + public void update(Object bean, Set updateProps, Transaction t) { + update(bean, updateProps, t, defaultDeleteMissingChildren, defaultUpdateNullProperties); + } + + /** + * Force an update using the bean explicitly stating which properties to + * include in the update. + */ + public void update(Object bean, Set updateProps, Transaction t, boolean deleteMissingChildren, boolean updateNullProperties) { + if (bean == null) { + throw new NullPointerException(Message.msg("bean.isnull")); + } + persister.forceUpdate(bean, updateProps, t, deleteMissingChildren, updateNullProperties); + } + + /** + * Force the bean to be saved with an explicit insert. + *

+ * Typically you would use save() and let Ebean determine if the bean should + * be inserted or updated. This can be useful when you are transferring data + * between databases and want to explicitly insert a bean into a different + * database that it came from. + *

+ */ + public void insert(Object bean) { + insert(bean, null); + } + + /** + * Force the bean to be saved with an explicit insert. + *

+ * Typically you would use save() and let Ebean determine if the bean should + * be inserted or updated. This can be useful when you are transferring data + * between databases and want to explicitly insert a bean into a different + * database that it came from. + *

+ */ + public void insert(Object bean, Transaction t) { + if (bean == null) { + throw new NullPointerException(Message.msg("bean.isnull")); + } + persister.forceInsert(bean, t); + } + + /** + * Delete the associations (from the intersection table) of a ManyToMany given + * the owner bean and the propertyName of the ManyToMany collection. + *

+ * This returns the number of associations deleted. + *

+ */ + public int deleteManyToManyAssociations(Object ownerBean, String propertyName) { + return deleteManyToManyAssociations(ownerBean, propertyName, null); + } + + /** + * Delete the associations (from the intersection table) of a ManyToMany given + * the owner bean and the propertyName of the ManyToMany collection. + *

+ * This returns the number of associations deleted. + *

+ */ + public int deleteManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) { + + TransWrapper wrap = initTransIfRequired(t); + try { + SpiTransaction trans = wrap.transaction; + int rc = persister.deleteManyToManyAssociations(ownerBean, propertyName, trans); + wrap.commitIfCreated(); + return rc; + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + /** + * Save the associations of a ManyToMany given the owner bean and the + * propertyName of the ManyToMany collection. + */ + public void saveManyToManyAssociations(Object ownerBean, String propertyName) { + saveManyToManyAssociations(ownerBean, propertyName, null); + } + + /** + * 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) { + + TransWrapper wrap = initTransIfRequired(t); + try { + SpiTransaction trans = wrap.transaction; + + persister.saveManyToManyAssociations(ownerBean, propertyName, trans); + + wrap.commitIfCreated(); + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + public void saveAssociation(Object ownerBean, String propertyName) { + saveAssociation(ownerBean, propertyName, null); + } + + public void saveAssociation(Object ownerBean, String propertyName, Transaction t) { + + if (ownerBean instanceof EntityBean) { + Set loadedProps = ((EntityBean) ownerBean)._ebean_getIntercept().getLoadedProps(); + if (loadedProps != null && !loadedProps.contains(propertyName)) { + // skip as property is not actually loaded in this partially + // loaded bean + logger.fine("Skip saveAssociation as property " + propertyName + " is not loaded"); + return; + } + } + + TransWrapper wrap = initTransIfRequired(t); + try { + SpiTransaction trans = wrap.transaction; + + persister.saveAssociation(ownerBean, propertyName, trans); + + wrap.commitIfCreated(); + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + /** + * Perform an update or insert on each bean in the iterator. Returns the + * number of beans that where saved. + */ + public int save(Iterator it) { + return save(it, null); + } + + /** + * Perform an update or insert on each bean in the collection. Returns the + * number of beans that where saved. + */ + public int save(Collection c) { + return save(c.iterator(), null); + } + + /** + * Save all beans in the iterator with an explicit transaction. + */ + public int save(Iterator it, Transaction t) { + + TransWrapper wrap = initTransIfRequired(t); + try { + SpiTransaction trans = wrap.transaction; + int saveCount = 0; + while (it.hasNext()) { + Object bean = it.next(); + persister.save(bean, trans); + saveCount++; + } + + wrap.commitIfCreated(); + + return saveCount; + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + public int delete(Class beanType, Object id) { + return delete(beanType, id, null); + } + + public int delete(Class beanType, Object id, Transaction t) { + + TransWrapper wrap = initTransIfRequired(t); + try { + SpiTransaction trans = wrap.transaction; + int rowCount = persister.delete(beanType, id, trans); + wrap.commitIfCreated(); + + return rowCount; + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + public void delete(Class beanType, Collection ids) { + delete(beanType, ids, null); + } + + public void delete(Class beanType, Collection ids, Transaction t) { + + TransWrapper wrap = initTransIfRequired(t); + try { + SpiTransaction trans = wrap.transaction; + persister.deleteMany(beanType, ids, trans); + wrap.commitIfCreated(); + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + /** + * Delete the bean. + */ + public void delete(Object bean) { + delete(bean, null); + } + + /** + * Delete the bean with the explicit transaction. + */ + public void delete(Object bean, Transaction t) { + if (bean == null) { + throw new NullPointerException(Message.msg("bean.isnull")); + } + persister.delete(bean, t); + } + + /** + * Delete all the beans in the iterator. + */ + public int delete(Iterator it) { + return delete(it, null); + } + + /** + * Delete all the beans in the collection. + */ + public int delete(Collection c) { + return delete(c.iterator(), null); + } + + /** + * Delete all the beans in the iterator with an explicit transaction. + */ + public int delete(Iterator it, Transaction t) { + + TransWrapper wrap = initTransIfRequired(t); + + try { + SpiTransaction trans = wrap.transaction; + int deleteCount = 0; + while (it.hasNext()) { + Object bean = it.next(); + persister.delete(bean, trans); + deleteCount++; + } + + wrap.commitIfCreated(); + + return deleteCount; + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + /** + * Execute the CallableSql with an explicit transaction. + */ + public int execute(CallableSql callSql, Transaction t) { + return persister.executeCallable(callSql, t); + } + + /** + * Execute the CallableSql. + */ + public int execute(CallableSql callSql) { + return execute(callSql, null); + } + + /** + * Execute the updateSql with an explicit transaction. + */ + public int execute(SqlUpdate updSql, Transaction t) { + return persister.executeSqlUpdate(updSql, t); + } + + /** + * Execute the updateSql. + */ + public int execute(SqlUpdate updSql) { + return execute(updSql, null); + } + + /** + * Execute the updateSql with an explicit transaction. + */ + public int execute(Update update, Transaction t) { + return persister.executeOrmUpdate(update, t); + } + + /** + * Execute the orm update. + */ + public int execute(Update update) { + return execute(update, null); + } + + public BeanManager getBeanManager(Class beanClass) { + return beanDescriptorManager.getBeanManager(beanClass); + } + + /** + * Return all the BeanDescriptors. + */ + public List> getBeanDescriptors() { + return beanDescriptorManager.getBeanDescriptorList(); + } + + public void register(BeanPersistController c) { + List> list = beanDescriptorManager.getBeanDescriptorList(); + for (int i = 0; i < list.size(); i++) { + list.get(i).register(c); + } + } + + public void deregister(BeanPersistController c) { + List> list = beanDescriptorManager.getBeanDescriptorList(); + for (int i = 0; i < list.size(); i++) { + list.get(i).deregister(c); + } + } + + public boolean isSupportedType(java.lang.reflect.Type genericType) { + + TypeInfo typeInfo = ParamTypeHelper.getTypeInfo(genericType); + if (typeInfo == null) { + return false; + } + Class beanType = typeInfo.getBeanType(); + if (JsonElement.class.isAssignableFrom(beanType)) { + return true; + } + return getBeanDescriptor(typeInfo.getBeanType()) != null; + } + + public Object getBeanId(Object bean) { + BeanDescriptor desc = getBeanDescriptor(bean.getClass()); + if (desc == null) { + String m = bean.getClass().getName() + " is NOT an Entity Bean registered with this server?"; + throw new PersistenceException(m); + } + + return desc.getId(bean); + } + + /** + * Return the BeanDescriptor for a given type of bean. + */ + public BeanDescriptor getBeanDescriptor(Class beanClass) { + return beanDescriptorManager.getBeanDescriptor(beanClass); + } + + /** + * Return the BeanDescriptor's for a given table name. + */ + public List> getBeanDescriptors(String tableName) { + return beanDescriptorManager.getBeanDescriptors(tableName); + } + + /** + * Return the BeanDescriptor using its unique id. + */ + public BeanDescriptor getBeanDescriptorById(String descriptorId) { + return beanDescriptorManager.getBeanDescriptorById(descriptorId); + } + + /** + * Another server in the cluster sent this event so that we can inform local + * BeanListeners of inserts updates and deletes that occurred remotely (on + * another server in the cluster). + */ + public void remoteTransactionEvent(RemoteTransactionEvent event) { + transactionManager.remoteTransactionEvent(event); + } + + /** + * Create a transaction if one is not currently active in the + * TransactionThreadLocal. + *

+ * Returns a TransWrapper which contains the wasCreated flag. If this is true + * then the transaction was created for this request in which case it will + * need to be committed after the request has been processed. + *

+ */ + TransWrapper initTransIfRequired(Transaction t) { + + if (t != null) { + return new TransWrapper((SpiTransaction) t, false); + } + + boolean wasCreated = false; + SpiTransaction trans = transactionScopeManager.get(); + if (trans == null) { + // create a transaction + trans = transactionManager.createTransaction(false, -1); + wasCreated = true; + } + return new TransWrapper(trans, wasCreated); + } + + public SpiTransaction createServerTransaction(boolean isExplicit, int isolationLevel) { + return transactionManager.createTransaction(isExplicit, isolationLevel); + } + + public SpiTransaction createQueryTransaction() { + return transactionManager.createQueryTransaction(); + } + + private static final int IGNORE_LEADING_ELEMENTS = 5; + private static final String AVAJE_EBEAN = Ebean.class.getName().substring(0, 15); + + /** + * Create a CallStack object. + *

+ * This trims off the avaje ebean part of the stack trace so that the first + * element in the CallStack should be application code. + *

+ */ + public CallStack createCallStack() { + + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + + // ignore the first 6 as they are always avaje stack elements + int startIndex = IGNORE_LEADING_ELEMENTS; + + // find the first non-avaje stackElement + for (; startIndex < stackTrace.length; startIndex++) { + if (!stackTrace[startIndex].getClassName().startsWith(AVAJE_EBEAN)) { + break; + } + } + + int stackLength = stackTrace.length - startIndex; + if (stackLength > maxCallStack) { + // maximum of maxCallStack stackTrace elements + stackLength = maxCallStack; + } + + // create the 'interesting' part of the stackTrace + StackTraceElement[] finalTrace = new StackTraceElement[stackLength]; + for (int i = 0; i < stackLength; i++) { + finalTrace[i] = stackTrace[i + startIndex]; + } + + if (stackLength < 1) { + // this should not really happen + throw new RuntimeException("StackTraceElement size 0? stack: " + Arrays.toString(stackTrace)); + } + + return new CallStack(finalTrace); + } + + public JsonContext createJsonContext() { + // immutable thread safe so return shared instance + return jsonContext; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultSqlUpdate.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultSqlUpdate.java index f87037df8..1960b543d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultSqlUpdate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultSqlUpdate.java @@ -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. - *

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

- *

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

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

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

- *

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

- *

- * EXAMPLE: Using JDBC batching with SqlUpdate - *

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

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

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

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

+ *

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

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

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

+ *

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

+ *

+ * EXAMPLE: Using JDBC batching with SqlUpdate + *

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

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

+ */ + public DefaultSqlUpdate(EbeanServer server, String sql, BindParams bindParams) { + this.server = server; + this.sql = sql; + this.bindParams = bindParams; + } + + /** + * Create with a specific server. This means you can use the + * SqlUpdate.execute() method. + */ + public DefaultSqlUpdate(EbeanServer server, String sql) { + this(server, sql, new BindParams()); + } + + /** + * Create with some sql. + */ + public DefaultSqlUpdate(String sql) { + this(null, sql, new BindParams()); + } + + public int execute() { + if (server != null) { + return server.execute(this); + } else { + // Hopefully this doesn't catch anyone out... + return Ebean.execute(this); + } + } + + public boolean isAutoTableMod() { + return isAutoTableMod; + } + + public SqlUpdate setAutoTableMod(boolean isAutoTableMod) { + this.isAutoTableMod = isAutoTableMod; + return this; + } + + public String getLabel() { + return label; + } + + public SqlUpdate setLabel(String label) { + this.label = label; + return this; + } + + public String getSql() { + return sql; + } + + public int getTimeout() { + return timeout; + } + + public SqlUpdate setTimeout(int secs) { + this.timeout = secs; + return this; + } + + public SqlUpdate addParameter(Object value) { + return setParameter(++addPos, value); + } + + public SqlUpdate setParameter(int position, Object value) { + bindParams.setParameter(position, value); + return this; + } + + public SqlUpdate setNull(int position, int jdbcType) { + bindParams.setNullParameter(position, jdbcType); + return this; + } + + public SqlUpdate setNullParameter(int position, int jdbcType) { + bindParams.setNullParameter(position, jdbcType); + return this; + } + + public SqlUpdate setParameter(String name, Object param) { + bindParams.setParameter(name, param); + return this; + } + + public SqlUpdate setNull(String name, int jdbcType) { + bindParams.setNullParameter(name, jdbcType); + return this; + } + + public SqlUpdate setNullParameter(String name, int jdbcType) { + bindParams.setNullParameter(name, jdbcType); + return this; + } + + /** + * Return the bind parameters. + */ + public BindParams getBindParams() { + return bindParams; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DiffHelp.java b/src/main/java/com/avaje/ebeaninternal/server/core/DiffHelp.java index 77f20e37e..90d586a05 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DiffHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DiffHelp.java @@ -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. - *

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

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

- * A and B must be of the same type. B can be null, in which case the - * 'OldValues' of a is used to compare with (as B). - *

- *

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

- */ - public Map 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 map = new LinkedHashMap(); - - 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. - *

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

- */ - private void diffEmbedded(Object a, Object b, BeanDescriptor desc, Map 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 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. + *

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

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

+ * A and B must be of the same type. B can be null, in which case the + * 'OldValues' of a is used to compare with (as B). + *

+ *

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

+ */ + public Map 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 map = new LinkedHashMap(); + + 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. + *

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

+ */ + private void diffEmbedded(Object a, Object b, BeanDescriptor desc, Map 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 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; + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java index ba8c7859a..adc08984d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java @@ -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; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/JndiDataSourceLookup.java b/src/main/java/com/avaje/ebeaninternal/server/core/JndiDataSourceLookup.java index 0f814f216..41ab2aff7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/JndiDataSourceLookup.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/JndiDataSourceLookup.java @@ -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. - *

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

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

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

+ */ + 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); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/Message.java b/src/main/java/com/avaje/ebeaninternal/server/core/Message.java index edaea3145..83c7a5f20 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/Message.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/Message.java @@ -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; + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OnBootupClassSearchMatcher.java b/src/main/java/com/avaje/ebeaninternal/server/core/OnBootupClassSearchMatcher.java index 1946be9fb..59f602aaa 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/OnBootupClassSearchMatcher.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/OnBootupClassSearchMatcher.java @@ -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; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java index c6406a70b..f7230bb81 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryEngine.java @@ -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 findId(OrmQueryRequest request); - - /** - * Execute the findList, findSet, findMap query returning an appropriate BeanCollection. - */ - public BeanCollection findMany(OrmQueryRequest request); - - /** - * Execute the query using a QueryIterator. - */ - public QueryIterator findIterate(OrmQueryRequest request); - - /** - * Execute the row count query. - */ - public int findRowCount(OrmQueryRequest request); - - /** - * Execute the find id's query. - */ - public BeanIdList findIds(OrmQueryRequest request); - - -} +package com.avaje.ebeaninternal.server.core; + +import com.avaje.ebean.QueryIterator; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebeaninternal.api.BeanIdList; + +/** + * The Object Relational query execution API. + */ +public interface OrmQueryEngine { + + /** + * Execute the 'find by id' query returning a single bean. + */ + public T findId(OrmQueryRequest request); + + /** + * Execute the findList, findSet, findMap query returning an appropriate BeanCollection. + */ + public BeanCollection findMany(OrmQueryRequest request); + + /** + * Execute the query using a QueryIterator. + */ + public QueryIterator findIterate(OrmQueryRequest request); + + /** + * Execute the row count query. + */ + public int findRowCount(OrmQueryRequest request); + + /** + * Execute the find id's query. + */ + public BeanIdList findIds(OrmQueryRequest request); + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java index 96b075353..f3a06e745 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java @@ -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 extends BeanRequest implements BeanQueryRequest, SpiOrmQueryRequest { - - private final BeanDescriptor beanDescriptor; - - private final OrmQueryEngine queryEngine; - - private final SpiQuery query; - - private final boolean vanillaMode; - - private final BeanFinder 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 query, BeanDescriptor desc, SpiTransaction t) { - - super(server, t); - - this.beanDescriptor = desc; - this.rawSql = query.getRawSql(); - this.finder = beanDescriptor.getBeanFinder(); - this.queryEngine = queryEngine; - this.query = query; - this.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. - *

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

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

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

- */ - @Override - public void initTransIfRequired() { - // first check if the query requires its own transaction - if (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. - *

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

- */ - 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 findIds() { - BeanIdList idList = queryEngine.findIds(this); - return idList.getIdList(); - } - - public void findVisit(QueryResultVisitor visitor) { - QueryIterator it = queryEngine.findIterate(this); - try { - while (it.hasNext()) { - if (!visitor.accept(it.next())) { - break; - } - } - } finally { - it.close(); - } - } - - public QueryIterator findIterate() { - return queryEngine.findIterate(this); - } - - /** - * Execute the query as findList. - */ - @SuppressWarnings("unchecked") - public List findList() { - BeanCollection bc = queryEngine.findMany(this); - return (List) (vanillaMode ? bc.getActualCollection() : bc); - } - - /** - * Execute the query as findSet. - */ - @SuppressWarnings("unchecked") - public Set findSet() { - BeanCollection bc = queryEngine.findMany(this); - return (Set) (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 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 getBeanFinder() { - return finder; - } - - /** - * Return the find that is to be performed. - */ - public SpiQuery getQuery() { - return query; - } - - /** - * Return the many property that is fetched in the query or null if there is - * not one. - */ - public BeanPropertyAssocMany getManyProperty() { - return beanDescriptor.getManyProperty(query); - } - - /** - * Return a queryPlan for the current query if one exists. Returns null if no - * query plan for this query exists. - */ - public CQueryPlan getQueryPlan() { - return beanDescriptor.getQueryPlan(queryPlanHash); - } - - /** - * Return the queryPlanHash. - *

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

- */ - public 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 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 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(bc, beanDescriptor, ctx, 5).copy(); - // } - // return bc; - } - - public void putToQueryCache(BeanCollection queryResult) { - beanDescriptor.queryCachePut(cacheKey, queryResult); - } - - /** - * Set an Query object that owns the PreparedStatement that can be cancelled. - */ - public void setCancelableQuery(CancelableQuery cancelableQuery) { - query.setCancelableQuery(cancelableQuery); - } - - /** - * Log the SQL if the logLevel is appropriate. - */ - public void logSql(String sql) { - 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 extends BeanRequest implements BeanQueryRequest, SpiOrmQueryRequest { + + private final BeanDescriptor beanDescriptor; + + private final OrmQueryEngine queryEngine; + + private final SpiQuery query; + + private final boolean vanillaMode; + + private final BeanFinder 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 query, BeanDescriptor desc, SpiTransaction t) { + + super(server, t); + + this.beanDescriptor = desc; + this.rawSql = query.getRawSql(); + this.finder = beanDescriptor.getBeanFinder(); + this.queryEngine = queryEngine; + this.query = query; + this.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. + *

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

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

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

+ */ + @Override + public void initTransIfRequired() { + // first check if the query requires its own transaction + if (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. + *

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

+ */ + 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 findIds() { + BeanIdList idList = queryEngine.findIds(this); + return idList.getIdList(); + } + + public void findVisit(QueryResultVisitor visitor) { + QueryIterator it = queryEngine.findIterate(this); + try { + while (it.hasNext()) { + if (!visitor.accept(it.next())) { + break; + } + } + } finally { + it.close(); + } + } + + public QueryIterator findIterate() { + return queryEngine.findIterate(this); + } + + /** + * Execute the query as findList. + */ + @SuppressWarnings("unchecked") + public List findList() { + BeanCollection bc = queryEngine.findMany(this); + return (List) (vanillaMode ? bc.getActualCollection() : bc); + } + + /** + * Execute the query as findSet. + */ + @SuppressWarnings("unchecked") + public Set findSet() { + BeanCollection bc = queryEngine.findMany(this); + return (Set) (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 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 getBeanFinder() { + return finder; + } + + /** + * Return the find that is to be performed. + */ + public SpiQuery getQuery() { + return query; + } + + /** + * Return the many property that is fetched in the query or null if there is + * not one. + */ + public BeanPropertyAssocMany getManyProperty() { + return beanDescriptor.getManyProperty(query); + } + + /** + * Return a queryPlan for the current query if one exists. Returns null if no + * query plan for this query exists. + */ + public CQueryPlan getQueryPlan() { + return beanDescriptor.getQueryPlan(queryPlanHash); + } + + /** + * Return the queryPlanHash. + *

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

+ */ + public 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 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 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(bc, beanDescriptor, ctx, 5).copy(); + // } + // return bc; + } + + public void putToQueryCache(BeanCollection queryResult) { + beanDescriptor.queryCachePut(cacheKey, queryResult); + } + + /** + * Set an Query object that owns the PreparedStatement that can be cancelled. + */ + public void setCancelableQuery(CancelableQuery cancelableQuery) { + query.setCancelableQuery(cancelableQuery); + } + + /** + * Log the SQL if the logLevel is appropriate. + */ + public void logSql(String sql) { + if (transaction.isLogSql()) { + transaction.logInternal(sql); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequest.java index 2ce3014d3..51f20722f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequest.java @@ -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; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java index 61c67ea6a..c705c6b1f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestBean.java @@ -1,708 +1,689 @@ -/** - * 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 java.util.List; -import java.util.Set; - -import javax.persistence.OptimisticLockException; - -import com.avaje.ebean.InvalidValue; -import com.avaje.ebean.ValidationException; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.event.BeanPersistController; -import com.avaje.ebean.event.BeanPersistListener; -import com.avaje.ebean.event.BeanPersistRequest; -import com.avaje.ebeaninternal.api.DerivedRelationshipData; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.api.TransactionEvent; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanManager; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.persist.BatchControl; -import com.avaje.ebeaninternal.server.persist.PersistExecute; -import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; -import com.avaje.ebeaninternal.server.transaction.BeanDelta; -import com.avaje.ebeaninternal.server.transaction.BeanPersistIdMap; - -/** - * PersistRequest for insert update or delete of a bean. - */ -public class PersistRequestBean extends PersistRequest implements BeanPersistRequest { - - protected final BeanManager beanManager; - - protected final BeanDescriptor beanDescriptor; - - protected final BeanPersistListener beanPersistListener; - - /** - * For per post insert update delete control. - */ - protected final BeanPersistController controller; - - /** - * The associated intercept. - */ - protected final EntityBeanIntercept intercept; - - /** - * The parent bean for unidirectional save. - */ - protected final Object parentBean; - - protected final boolean isDirty; - - /** - * True if this is a vanilla bean. - */ - protected final boolean vanilla; - - /** - * The bean being persisted. - */ - protected final T bean; - - /** - * Old values used for concurrency checking. - */ - protected T oldValues; - - /** - * The concurrency mode used for update or delete. - */ - protected ConcurrencyMode concurrencyMode; - - protected final Set loadedProps; - - /** - * The unique id used for logging summary. - */ - protected Object idValue; - - /** - * Hash value used to handle cascade delete both ways in a relationship. - */ - protected Integer beanHash; - protected Integer beanIdentityHash; - - protected final Set changedProps; - - protected boolean notifyCache; - - private boolean statelessUpdate; - private boolean deleteMissingChildren; - private boolean updateNullProperties; - - /** - * Used for forced update of a bean. - */ - public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager mgr, SpiTransaction t, - PersistExecute persistExecute, Set updateProps, ConcurrencyMode concurrencyMode) { - - super(server, t, persistExecute); - this.beanManager = mgr; - this.beanDescriptor = mgr.getBeanDescriptor(); - this.beanPersistListener = beanDescriptor.getPersistListener(); - this.bean = bean; - this.parentBean = parentBean; - - this.controller = beanDescriptor.getPersistController(); - this.concurrencyMode = beanDescriptor.getConcurrencyMode(); - - this.concurrencyMode = concurrencyMode; - this.loadedProps = updateProps; - this.changedProps = updateProps; - - this.vanilla = true; - this.isDirty = true; - this.oldValues = bean; - if (bean instanceof EntityBean) { - this.intercept = ((EntityBean) bean)._ebean_getIntercept(); - } else { - this.intercept = null; - } - } - - @SuppressWarnings("unchecked") - public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager mgr, - SpiTransaction t, PersistExecute persistExecute) { - - super(server, t, persistExecute); - this.beanManager = mgr; - this.beanDescriptor = mgr.getBeanDescriptor(); - this.beanPersistListener = beanDescriptor.getPersistListener(); - this.bean = bean; - this.parentBean = parentBean; - - this.controller = beanDescriptor.getPersistController(); - this.concurrencyMode = beanDescriptor.getConcurrencyMode(); - - if (bean instanceof EntityBean) { - this.intercept = ((EntityBean) bean)._ebean_getIntercept(); - if (intercept.isReference()) { - // allowed to delete reference objects - // with no concurrency checking - this.concurrencyMode = ConcurrencyMode.NONE; - } - // this is ok to not use isNewOrDirty() as used for updates only - this.isDirty = intercept.isDirty(); - if (!isDirty) { - this.changedProps = intercept.getChangedProps(); - } else { - // merge changed properties on the bean with changed embedded beans - Set beanChangedProps = intercept.getChangedProps(); - Set dirtyEmbedded = beanDescriptor.getDirtyEmbeddedProperties(bean); - this.changedProps = mergeChangedProperties(beanChangedProps, dirtyEmbedded); - } - this.loadedProps = intercept.getLoadedProps(); - this.oldValues = (T) intercept.getOldValues(); - this.vanilla = false; - - } else { - // have to assume the vanilla bean is dirty - this.vanilla = true; - this.isDirty = true; - this.loadedProps = null; - this.changedProps = null; - this.intercept = null; - - // degrade concurrency checking to none for vanilla bean - if (concurrencyMode.equals(ConcurrencyMode.ALL)) { - this.concurrencyMode = ConcurrencyMode.NONE; - } - } - } - - /** - * Merge the changed properties for the bean and embedded beans. - */ - private Set mergeChangedProperties(Set beanChangedProps, Set embChanged) { - if (embChanged == null) { - return beanChangedProps; - } else if (beanChangedProps == null) { - return embChanged; - } else { - beanChangedProps.addAll(embChanged); - return beanChangedProps; - } - } - - public boolean isNotify(TransactionEvent txnEvent) { - return notifyCache || isNotifyPersistListener(); - } - - public boolean isNotifyCache() { - return notifyCache; - } - - public boolean isNotifyPersistListener() { - return beanPersistListener != null; - } - - public void notifyCache() { - if (notifyCache) { - switch (type) { - case INSERT: - beanDescriptor.cacheInsert(idValue, this); - break; - case UPDATE: - beanDescriptor.cacheUpdate(idValue, this); - break; - case DELETE: - beanDescriptor.cacheDelete(idValue, this); - break; - default: - throw new IllegalStateException("Invalid type "+type); - } - } - } - - public void addToPersistMap(BeanPersistIdMap beanPersistMap) { - - beanPersistMap.add(beanDescriptor, type, idValue); - } - - public boolean notifyLocalPersistListener() { - if (beanPersistListener == null) { - return false; - - } else { - switch (type) { - case INSERT: - return beanPersistListener.inserted(bean); - - case UPDATE: - return beanPersistListener.updated(bean, getUpdatedProperties()); - - case DELETE: - return beanPersistListener.deleted(bean); - - default: - return false; - } - } - } - - public boolean isParent(Object o) { - return o == parentBean; - } - - /** - * Return true if this bean has been already been persisted - * (inserted/updated or deleted) in this transaction. - */ - public boolean isRegisteredBean() { - return transaction.isRegisteredBean(bean); - } - - public void unRegisterBean() { - transaction.unregisterBean(bean); - } - - /** - * The hash used to register the bean with the transaction. - *

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

- */ - private Integer getBeanHash() { - if (beanHash == null) { - Object id = beanDescriptor.getId(bean); - int hc = 31 * bean.getClass().getName().hashCode(); - if (id != null) { - hc += id.hashCode(); - } - beanHash = Integer.valueOf(hc); - } - return beanHash; - } - - public void registerDeleteBean() { - Integer hash = getBeanHash(); - transaction.registerDeleteBean(hash); - } - - public void unregisterDeleteBean() { - Integer hash = getBeanHash(); - transaction.unregisterDeleteBean(hash); - } - - public boolean isRegisteredForDeleteBean() { - if (transaction == null){ - return false; - } else { - Integer hash = getBeanHash(); - return transaction.isRegisteredDeleteBean(hash); - } - } - - /** - * Set the type of this request. One of INSERT, UPDATE, DELETE, UPDATESQL or - * CALLABLESQL. - */ - @Override - public void setType(Type type) { - this.type = type; - notifyCache = beanDescriptor.isCacheNotify(); - if (type == Type.DELETE || type == Type.UPDATE) { - if (oldValues == null) { - oldValues = bean; - } - } - } - - public BeanManager getBeanManager() { - return beanManager; - } - - /** - * Return the BeanDescriptor for the associated bean. - */ - public BeanDescriptor getBeanDescriptor() { - return beanDescriptor; - } - - /** - * Return true if this is a stateless update. - */ - public boolean isStatelessUpdate() { - return statelessUpdate; - } - - /** - * Return true if a stateless update should also delete any missing details - * beans. - */ - public boolean isDeleteMissingChildren() { - return deleteMissingChildren; - } - - /** - * Return true if null properties should be updated (treated as loaded) for - * stateless updates. - */ - public boolean isUpdateNullProperties() { - return updateNullProperties; - } - - /** - * Set to true if this is a stateless update. - *

- * By Stateless it means that the bean was not previously fetched (and so - * does not have it's previous state) so we are doing an update on a bean - * that was probably created from JSON or XML. - *

- */ - public void setStatelessUpdate(boolean statelessUpdate, boolean deleteMissingChildren, boolean updateNullProperties) { - this.statelessUpdate = statelessUpdate; - this.deleteMissingChildren = deleteMissingChildren; - this.updateNullProperties = updateNullProperties; - } - - /** - * Used to skip updates if we know the bean is not dirty. This is the case - * for EntityBeans that have not been modified. - */ - public boolean isDirty() { - return isDirty; - } - - /** - * Return the concurrency mode used for this persist. - */ - public ConcurrencyMode getConcurrencyMode() { - return concurrencyMode; - } - - /** - * Set loaded properties when generated values has added properties such as - * created and updated timestamps. - */ - public void setLoadedProps(Set additionalProps) { - if (intercept != null) { - intercept.setLoadedProps(additionalProps); - } - } - - public Set getLoadedProperties() { - return loadedProps; - } - - /** - * Returns a description of the request. This is typically the bean class - * name or the base table for MapBeans. - *

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

- */ - public String getFullName() { - return beanDescriptor.getFullName(); - } - - /** - * Return the bean associated with this request. - */ - public T getBean() { - return bean; - } - - /** - * Return the Id value for the bean. - */ - public Object getBeanId() { - return beanDescriptor.getId(bean); - } - - public BeanDelta createDeltaBean() { - return new BeanDelta(beanDescriptor, getBeanId()); - } - - /** - * Get the old values bean. This is used to perform optimistic concurrency - * checking on updates and deletes. - */ - public T getOldValues() { - return oldValues; - } - - /** - * Return the parent bean for cascading save with unidirectional - * relationship. - */ - public Object getParentBean() { - return parentBean; - } - - /** - * Return the controller if there is one associated with this type of bean. - * This returns null if there is no controller associated. - */ - public BeanPersistController getBeanController() { - return controller; - } - - /** - * Return the intercept if there is one. - */ - public EntityBeanIntercept getEntityBeanIntercept() { - return intercept; - } - - /** - * Validate the bean. This is not recursive and only runs the 'local' - * validation rules. - */ - public void validate() { - InvalidValue errs = beanDescriptor.validate(false, bean); - if (errs != null) { - throw new ValidationException(errs); - } - } - - /** - * Return true if this property is loaded (full bean or included in partial - * bean). - */ - public boolean isLoadedProperty(BeanProperty prop) { - if (loadedProps == null) { - return true; - } else { - return loadedProps.contains(prop.getName()); - } - } - - @Override - public int executeNow() { - switch (type) { - case INSERT: - persistExecute.executeInsertBean(this); - return -1; - - case UPDATE: - persistExecute.executeUpdateBean(this); - return -1; - - case DELETE: - persistExecute.executeDeleteBean(this); - return -1; - - default: - throw new RuntimeException("Invalid type " + type); - } - } - - @Override - public int executeOrQueue() { - - boolean batch = transaction.isBatchThisRequest(); - - BatchControl control = transaction.getBatchControl(); - if (control != null) { - return control.executeOrQueue(this, batch); - } - if (batch) { - control = persistExecute.createBatchControl(transaction); - return control.executeOrQueue(this, batch); - - } else { - return executeNow(); - } - } - - /** - * Set the generated key back to the bean. Only used for inserts with - * getGeneratedKeys. - */ - public void setGeneratedKey(Object idValue) { - if (idValue != null) { - - // set back to the bean so that we can use the same bean later - // for update [refer ebeanIntercept.setLoaded(true)]. - idValue = beanDescriptor.convertSetId(idValue, bean); - - // remember it for logging summary - this.idValue = idValue; - } - } - - /** - * Set the Id value that was bound. Used for the purposes of logging summary - * information on this request. - */ - public void setBoundId(Object idValue) { - this.idValue = idValue; - } - - /** - * Check for optimistic concurrency exception. - */ - public final void checkRowCount(int rowCount) throws SQLException { - if (rowCount != 1) { - String m = Message.msg("persist.conc2", "" + rowCount); - throw new OptimisticLockException(m, null, bean); - } - } - - /** - * Post processing. - */ - public void postExecute() throws SQLException { - - if (controller != null) { - controllerPost(); - } - - if (intercept != null) { - // if bean persisted again then should result in an update - intercept.setLoaded(); - } - - addEvent(); - - if (isLogSummary()) { - logSummary(); - } - } - - private void controllerPost() { - switch (type) { - case INSERT: - controller.postInsert(this); - break; - case UPDATE: - controller.postUpdate(this); - break; - case DELETE: - controller.postDelete(this); - break; - default: - break; - } - } - - private void logSummary() { - - String name = beanDescriptor.getName(); - switch (type) { - case INSERT: - transaction.logInternal("Inserted [" + name + "] [" + idValue + "]"); - break; - case UPDATE: - transaction.logInternal("Updated [" + name + "] [" + idValue + "]"); - break; - case DELETE: - transaction.logInternal("Deleted [" + name + "] [" + idValue + "]"); - break; - default: - break; - } - } - - /** - * Add the bean to the TransactionEvent. This will be used by - * TransactionManager to synch Cache, Cluster and text indexes. - */ - private void addEvent() { - - TransactionEvent event = transaction.getEvent(); - if (event != null) { - event.add(this); - } - } - - /** - * Determine the concurrency mode depending on fully/partially populated - * bean. - *

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

- */ - public ConcurrencyMode determineConcurrencyMode() { - if (loadedProps != null) { - // 'partial bean' update/delete... - if (concurrencyMode.equals(ConcurrencyMode.VERSION)) { - // check the version property was loaded - BeanProperty prop = beanDescriptor.firstVersionProperty(); - if (prop != null && loadedProps.contains(prop.getName())) { - // OK to use version property - } else { - concurrencyMode = ConcurrencyMode.ALL; - } - } - } - return concurrencyMode; - } - - /** - * Return true if the update DML/SQL must be dynamically generated. - *

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

- */ - public boolean isDynamicUpdateSql() { - return !vanilla && beanDescriptor.isUpdateChangesOnly() || (loadedProps != null); - } - - /** - * Create a GenerateDmlRequest used to generate the DML. - *

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

- */ - public GenerateDmlRequest createGenerateDmlRequest(boolean emptyStringAsNull) { - if (beanDescriptor.isUpdateChangesOnly()) { - return new GenerateDmlRequest(emptyStringAsNull, changedProps, loadedProps, oldValues); - } else { - return new GenerateDmlRequest(emptyStringAsNull, loadedProps, loadedProps, oldValues); - } - } - - /** - * Return the updated properties. If this returns null then all the - * properties on the bean where updated. - */ - public Set getUpdatedProperties() { - if (changedProps != null) { - return changedProps; - } - return loadedProps; - } - - /** - * Test if the property value has changed and if so include it in the - * update. - */ - public boolean hasChanged(BeanProperty prop) { - - return changedProps.contains(prop.getName()); - } - - public List getDerivedRelationships() { - return transaction.getDerivedRelationship(bean); - } - -} +package com.avaje.ebeaninternal.server.core; + +import java.sql.SQLException; +import java.util.List; +import java.util.Set; + +import javax.persistence.OptimisticLockException; + +import com.avaje.ebean.InvalidValue; +import com.avaje.ebean.ValidationException; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.event.BeanPersistController; +import com.avaje.ebean.event.BeanPersistListener; +import com.avaje.ebean.event.BeanPersistRequest; +import com.avaje.ebeaninternal.api.DerivedRelationshipData; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.api.TransactionEvent; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanManager; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.persist.BatchControl; +import com.avaje.ebeaninternal.server.persist.PersistExecute; +import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; +import com.avaje.ebeaninternal.server.transaction.BeanDelta; +import com.avaje.ebeaninternal.server.transaction.BeanPersistIdMap; + +/** + * PersistRequest for insert update or delete of a bean. + */ +public class PersistRequestBean extends PersistRequest implements BeanPersistRequest { + + protected final BeanManager beanManager; + + protected final BeanDescriptor beanDescriptor; + + protected final BeanPersistListener beanPersistListener; + + /** + * For per post insert update delete control. + */ + protected final BeanPersistController controller; + + /** + * The associated intercept. + */ + protected final EntityBeanIntercept intercept; + + /** + * The parent bean for unidirectional save. + */ + protected final Object parentBean; + + protected final boolean isDirty; + + /** + * True if this is a vanilla bean. + */ + protected final boolean vanilla; + + /** + * The bean being persisted. + */ + protected final T bean; + + /** + * Old values used for concurrency checking. + */ + protected T oldValues; + + /** + * The concurrency mode used for update or delete. + */ + protected ConcurrencyMode concurrencyMode; + + protected final Set loadedProps; + + /** + * The unique id used for logging summary. + */ + protected Object idValue; + + /** + * Hash value used to handle cascade delete both ways in a relationship. + */ + protected Integer beanHash; + protected Integer beanIdentityHash; + + protected final Set changedProps; + + protected boolean notifyCache; + + private boolean statelessUpdate; + private boolean deleteMissingChildren; + private boolean updateNullProperties; + + /** + * Used for forced update of a bean. + */ + public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager mgr, SpiTransaction t, + PersistExecute persistExecute, Set updateProps, ConcurrencyMode concurrencyMode) { + + super(server, t, persistExecute); + this.beanManager = mgr; + this.beanDescriptor = mgr.getBeanDescriptor(); + this.beanPersistListener = beanDescriptor.getPersistListener(); + this.bean = bean; + this.parentBean = parentBean; + + this.controller = beanDescriptor.getPersistController(); + this.concurrencyMode = beanDescriptor.getConcurrencyMode(); + + this.concurrencyMode = concurrencyMode; + this.loadedProps = updateProps; + this.changedProps = updateProps; + + this.vanilla = true; + this.isDirty = true; + this.oldValues = bean; + if (bean instanceof EntityBean) { + this.intercept = ((EntityBean) bean)._ebean_getIntercept(); + } else { + this.intercept = null; + } + } + + @SuppressWarnings("unchecked") + public PersistRequestBean(SpiEbeanServer server, T bean, Object parentBean, BeanManager mgr, + SpiTransaction t, PersistExecute persistExecute) { + + super(server, t, persistExecute); + this.beanManager = mgr; + this.beanDescriptor = mgr.getBeanDescriptor(); + this.beanPersistListener = beanDescriptor.getPersistListener(); + this.bean = bean; + this.parentBean = parentBean; + + this.controller = beanDescriptor.getPersistController(); + this.concurrencyMode = beanDescriptor.getConcurrencyMode(); + + if (bean instanceof EntityBean) { + this.intercept = ((EntityBean) bean)._ebean_getIntercept(); + if (intercept.isReference()) { + // allowed to delete reference objects + // with no concurrency checking + this.concurrencyMode = ConcurrencyMode.NONE; + } + // this is ok to not use isNewOrDirty() as used for updates only + this.isDirty = intercept.isDirty(); + if (!isDirty) { + this.changedProps = intercept.getChangedProps(); + } else { + // merge changed properties on the bean with changed embedded beans + Set beanChangedProps = intercept.getChangedProps(); + Set dirtyEmbedded = beanDescriptor.getDirtyEmbeddedProperties(bean); + this.changedProps = mergeChangedProperties(beanChangedProps, dirtyEmbedded); + } + this.loadedProps = intercept.getLoadedProps(); + this.oldValues = (T) intercept.getOldValues(); + this.vanilla = false; + + } else { + // have to assume the vanilla bean is dirty + this.vanilla = true; + this.isDirty = true; + this.loadedProps = null; + this.changedProps = null; + this.intercept = null; + + // degrade concurrency checking to none for vanilla bean + if (concurrencyMode.equals(ConcurrencyMode.ALL)) { + this.concurrencyMode = ConcurrencyMode.NONE; + } + } + } + + /** + * Merge the changed properties for the bean and embedded beans. + */ + private Set mergeChangedProperties(Set beanChangedProps, Set embChanged) { + if (embChanged == null) { + return beanChangedProps; + } else if (beanChangedProps == null) { + return embChanged; + } else { + beanChangedProps.addAll(embChanged); + return beanChangedProps; + } + } + + public boolean isNotify(TransactionEvent txnEvent) { + return notifyCache || isNotifyPersistListener(); + } + + public boolean isNotifyCache() { + return notifyCache; + } + + public boolean isNotifyPersistListener() { + return beanPersistListener != null; + } + + public void notifyCache() { + if (notifyCache) { + switch (type) { + case INSERT: + beanDescriptor.cacheInsert(idValue, this); + break; + case UPDATE: + beanDescriptor.cacheUpdate(idValue, this); + break; + case DELETE: + beanDescriptor.cacheDelete(idValue, this); + break; + default: + throw new IllegalStateException("Invalid type "+type); + } + } + } + + public void addToPersistMap(BeanPersistIdMap beanPersistMap) { + + beanPersistMap.add(beanDescriptor, type, idValue); + } + + public boolean notifyLocalPersistListener() { + if (beanPersistListener == null) { + return false; + + } else { + switch (type) { + case INSERT: + return beanPersistListener.inserted(bean); + + case UPDATE: + return beanPersistListener.updated(bean, getUpdatedProperties()); + + case DELETE: + return beanPersistListener.deleted(bean); + + default: + return false; + } + } + } + + public boolean isParent(Object o) { + return o == parentBean; + } + + /** + * Return true if this bean has been already been persisted + * (inserted/updated or deleted) in this transaction. + */ + public boolean isRegisteredBean() { + return transaction.isRegisteredBean(bean); + } + + public void unRegisterBean() { + transaction.unregisterBean(bean); + } + + /** + * The hash used to register the bean with the transaction. + *

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

+ */ + private Integer getBeanHash() { + if (beanHash == null) { + Object id = beanDescriptor.getId(bean); + int hc = 31 * bean.getClass().getName().hashCode(); + if (id != null) { + hc += id.hashCode(); + } + beanHash = Integer.valueOf(hc); + } + return beanHash; + } + + public void registerDeleteBean() { + Integer hash = getBeanHash(); + transaction.registerDeleteBean(hash); + } + + public void unregisterDeleteBean() { + Integer hash = getBeanHash(); + transaction.unregisterDeleteBean(hash); + } + + public boolean isRegisteredForDeleteBean() { + if (transaction == null){ + return false; + } else { + Integer hash = getBeanHash(); + return transaction.isRegisteredDeleteBean(hash); + } + } + + /** + * Set the type of this request. One of INSERT, UPDATE, DELETE, UPDATESQL or + * CALLABLESQL. + */ + @Override + public void setType(Type type) { + this.type = type; + notifyCache = beanDescriptor.isCacheNotify(); + if (type == Type.DELETE || type == Type.UPDATE) { + if (oldValues == null) { + oldValues = bean; + } + } + } + + public BeanManager getBeanManager() { + return beanManager; + } + + /** + * Return the BeanDescriptor for the associated bean. + */ + public BeanDescriptor getBeanDescriptor() { + return beanDescriptor; + } + + /** + * Return true if this is a stateless update. + */ + public boolean isStatelessUpdate() { + return statelessUpdate; + } + + /** + * Return true if a stateless update should also delete any missing details + * beans. + */ + public boolean isDeleteMissingChildren() { + return deleteMissingChildren; + } + + /** + * Return true if null properties should be updated (treated as loaded) for + * stateless updates. + */ + public boolean isUpdateNullProperties() { + return updateNullProperties; + } + + /** + * Set to true if this is a stateless update. + *

+ * By Stateless it means that the bean was not previously fetched (and so + * does not have it's previous state) so we are doing an update on a bean + * that was probably created from JSON or XML. + *

+ */ + public void setStatelessUpdate(boolean statelessUpdate, boolean deleteMissingChildren, boolean updateNullProperties) { + this.statelessUpdate = statelessUpdate; + this.deleteMissingChildren = deleteMissingChildren; + this.updateNullProperties = updateNullProperties; + } + + /** + * Used to skip updates if we know the bean is not dirty. This is the case + * for EntityBeans that have not been modified. + */ + public boolean isDirty() { + return isDirty; + } + + /** + * Return the concurrency mode used for this persist. + */ + public ConcurrencyMode getConcurrencyMode() { + return concurrencyMode; + } + + /** + * Set loaded properties when generated values has added properties such as + * created and updated timestamps. + */ + public void setLoadedProps(Set additionalProps) { + if (intercept != null) { + intercept.setLoadedProps(additionalProps); + } + } + + public Set getLoadedProperties() { + return loadedProps; + } + + /** + * Returns a description of the request. This is typically the bean class + * name or the base table for MapBeans. + *

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

+ */ + public String getFullName() { + return beanDescriptor.getFullName(); + } + + /** + * Return the bean associated with this request. + */ + public T getBean() { + return bean; + } + + /** + * Return the Id value for the bean. + */ + public Object getBeanId() { + return beanDescriptor.getId(bean); + } + + public BeanDelta createDeltaBean() { + return new BeanDelta(beanDescriptor, getBeanId()); + } + + /** + * Get the old values bean. This is used to perform optimistic concurrency + * checking on updates and deletes. + */ + public T getOldValues() { + return oldValues; + } + + /** + * Return the parent bean for cascading save with unidirectional + * relationship. + */ + public Object getParentBean() { + return parentBean; + } + + /** + * Return the controller if there is one associated with this type of bean. + * This returns null if there is no controller associated. + */ + public BeanPersistController getBeanController() { + return controller; + } + + /** + * Return the intercept if there is one. + */ + public EntityBeanIntercept getEntityBeanIntercept() { + return intercept; + } + + /** + * Validate the bean. This is not recursive and only runs the 'local' + * validation rules. + */ + public void validate() { + InvalidValue errs = beanDescriptor.validate(false, bean); + if (errs != null) { + throw new ValidationException(errs); + } + } + + /** + * Return true if this property is loaded (full bean or included in partial + * bean). + */ + public boolean isLoadedProperty(BeanProperty prop) { + if (loadedProps == null) { + return true; + } else { + return loadedProps.contains(prop.getName()); + } + } + + @Override + public int executeNow() { + switch (type) { + case INSERT: + persistExecute.executeInsertBean(this); + return -1; + + case UPDATE: + persistExecute.executeUpdateBean(this); + return -1; + + case DELETE: + persistExecute.executeDeleteBean(this); + return -1; + + default: + throw new RuntimeException("Invalid type " + type); + } + } + + @Override + public int executeOrQueue() { + + boolean batch = transaction.isBatchThisRequest(); + + BatchControl control = transaction.getBatchControl(); + if (control != null) { + return control.executeOrQueue(this, batch); + } + if (batch) { + control = persistExecute.createBatchControl(transaction); + return control.executeOrQueue(this, batch); + + } else { + return executeNow(); + } + } + + /** + * Set the generated key back to the bean. Only used for inserts with + * getGeneratedKeys. + */ + public void setGeneratedKey(Object idValue) { + if (idValue != null) { + + // set back to the bean so that we can use the same bean later + // for update [refer ebeanIntercept.setLoaded(true)]. + idValue = beanDescriptor.convertSetId(idValue, bean); + + // remember it for logging summary + this.idValue = idValue; + } + } + + /** + * Set the Id value that was bound. Used for the purposes of logging summary + * information on this request. + */ + public void setBoundId(Object idValue) { + this.idValue = idValue; + } + + /** + * Check for optimistic concurrency exception. + */ + public final void checkRowCount(int rowCount) throws SQLException { + if (rowCount != 1) { + String m = Message.msg("persist.conc2", "" + rowCount); + throw new OptimisticLockException(m, null, bean); + } + } + + /** + * Post processing. + */ + public void postExecute() throws SQLException { + + if (controller != null) { + controllerPost(); + } + + if (intercept != null) { + // if bean persisted again then should result in an update + intercept.setLoaded(); + } + + addEvent(); + + if (isLogSummary()) { + logSummary(); + } + } + + private void controllerPost() { + switch (type) { + case INSERT: + controller.postInsert(this); + break; + case UPDATE: + controller.postUpdate(this); + break; + case DELETE: + controller.postDelete(this); + break; + default: + break; + } + } + + private void logSummary() { + + String name = beanDescriptor.getName(); + switch (type) { + case INSERT: + transaction.logInternal("Inserted [" + name + "] [" + idValue + "]"); + break; + case UPDATE: + transaction.logInternal("Updated [" + name + "] [" + idValue + "]"); + break; + case DELETE: + transaction.logInternal("Deleted [" + name + "] [" + idValue + "]"); + break; + default: + break; + } + } + + /** + * Add the bean to the TransactionEvent. This will be used by + * TransactionManager to synch Cache, Cluster and text indexes. + */ + private void addEvent() { + + TransactionEvent event = transaction.getEvent(); + if (event != null) { + event.add(this); + } + } + + /** + * Determine the concurrency mode depending on fully/partially populated + * bean. + *

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

+ */ + public ConcurrencyMode determineConcurrencyMode() { + if (loadedProps != null) { + // 'partial bean' update/delete... + if (concurrencyMode.equals(ConcurrencyMode.VERSION)) { + // check the version property was loaded + BeanProperty prop = beanDescriptor.firstVersionProperty(); + if (prop != null && loadedProps.contains(prop.getName())) { + // OK to use version property + } else { + concurrencyMode = ConcurrencyMode.ALL; + } + } + } + return concurrencyMode; + } + + /** + * Return true if the update DML/SQL must be dynamically generated. + *

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

+ */ + public boolean isDynamicUpdateSql() { + return !vanilla && beanDescriptor.isUpdateChangesOnly() || (loadedProps != null); + } + + /** + * Create a GenerateDmlRequest used to generate the DML. + *

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

+ */ + public GenerateDmlRequest createGenerateDmlRequest(boolean emptyStringAsNull) { + if (beanDescriptor.isUpdateChangesOnly()) { + return new GenerateDmlRequest(emptyStringAsNull, changedProps, loadedProps, oldValues); + } else { + return new GenerateDmlRequest(emptyStringAsNull, loadedProps, loadedProps, oldValues); + } + } + + /** + * Return the updated properties. If this returns null then all the + * properties on the bean where updated. + */ + public Set getUpdatedProperties() { + if (changedProps != null) { + return changedProps; + } + return loadedProps; + } + + /** + * Test if the property value has changed and if so include it in the + * update. + */ + public boolean hasChanged(BeanProperty prop) { + + return changedProps.contains(prop.getName()); + } + + public List getDerivedRelationships() { + return transaction.getDerivedRelationship(bean); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestCallableSql.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestCallableSql.java index f949e3e92..b65ba0bdc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestCallableSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestCallableSql.java @@ -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 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 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); + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestOrmUpdate.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestOrmUpdate.java index 3b52c737e..d8cc19353 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestOrmUpdate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestOrmUpdate.java @@ -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; + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestUpdateSql.java b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestUpdateSql.java index e5a5b5882..008c3a861 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestUpdateSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PersistRequestUpdateSql.java @@ -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; + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java b/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java index dbfdfe587..319a5fd73 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/Persister.java @@ -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 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. - *

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

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

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

+ */ + 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); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/ProxyBeanObjectInputStream.java b/src/main/java/com/avaje/ebeaninternal/server/core/ProxyBeanObjectInputStream.java index 64068d998..c497b18b1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/ProxyBeanObjectInputStream.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/ProxyBeanObjectInputStream.java @@ -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. - *

- * 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. - *

- */ -public class ProxyBeanObjectInputStream extends ObjectInputStream { - - private final SpiEbeanServer ebeanServer; - - /** - * Create with a given InputStream and EbeanServer. - *

- * The EbeanServer should be the one that created the 'proxy' classes that - * were serialised. - *

- */ - public ProxyBeanObjectInputStream(InputStream in, EbeanServer ebeanServer) - throws IOException { - - super(in); - this.ebeanServer = (SpiEbeanServer) ebeanServer; - SerializeControl.setVanilla(false); - } - - /** - * close and reset the serialization mode. - *

- * uses SerializeControl.resetToDefault(). - *

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

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

+ */ +public class ProxyBeanObjectInputStream extends ObjectInputStream { + + private final SpiEbeanServer ebeanServer; + + /** + * Create with a given InputStream and EbeanServer. + *

+ * The EbeanServer should be the one that created the 'proxy' classes that + * were serialised. + *

+ */ + public ProxyBeanObjectInputStream(InputStream in, EbeanServer ebeanServer) + throws IOException { + + super(in); + this.ebeanServer = (SpiEbeanServer) ebeanServer; + SerializeControl.setVanilla(false); + } + + /** + * close and reset the serialization mode. + *

+ * uses SerializeControl.resetToDefault(). + *

+ */ + 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); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/PstmtBatch.java b/src/main/java/com/avaje/ebeaninternal/server/core/PstmtBatch.java index d06a6c5d9..124845f46 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/PstmtBatch.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/PstmtBatch.java @@ -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; + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/RefreshHelp.java b/src/main/java/com/avaje/ebeaninternal/server/core/RefreshHelp.java index 1b7f9fd7b..6a1c1047e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/RefreshHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/RefreshHelp.java @@ -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. - *

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

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

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

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

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

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

- * Initialise the properties file using SystemProperties.initWebapp(); - * and start Ebean. - *

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

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

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

+ * Initialise the properties file using SystemProperties.initWebapp(); + * and start Ebean. + *

+ */ + 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(); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java index d923647d1..145b07798 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/SpiOrmQueryRequest.java @@ -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 { - - /** - * Return the query. - */ - public SpiQuery getQuery(); - - /** - * Return the associated BeanDescriptor. - */ - public BeanDescriptor getBeanDescriptor(); - - /** - * This will create a local (readOnly) transaction if no current transaction - * exists. - *

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

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

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

- */ - public void endTransIfRequired(); - - 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 findIds(); - - /** - * Execute the find returning a QueryIterator and visitor pattern. - */ - public void findVisit(QueryResultVisitor visitor); - - /** - * Execute the find returning a QueryIterator. - */ - public QueryIterator findIterate(); - - /** - * Execute the query as findList. - */ - public List findList(); - - /** - * Execute the query as findSet. - */ - public Set findSet(); - - /** - * Execute the query as findMap. - */ - public Map findMap(); - - /** - * Try to get the object out of the persistence context. - */ - //public T getFromPersistenceContextOrCache(); - - /** - * Try to get the query result from the query cache. - */ - public BeanCollection getFromQueryCache(); - +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 { + + /** + * Return the query. + */ + public SpiQuery getQuery(); + + /** + * Return the associated BeanDescriptor. + */ + public BeanDescriptor getBeanDescriptor(); + + /** + * This will create a local (readOnly) transaction if no current transaction + * exists. + *

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

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

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

+ */ + public void endTransIfRequired(); + + 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 findIds(); + + /** + * Execute the find returning a QueryIterator and visitor pattern. + */ + public void findVisit(QueryResultVisitor visitor); + + /** + * Execute the find returning a QueryIterator. + */ + public QueryIterator findIterate(); + + /** + * Execute the query as findList. + */ + public List findList(); + + /** + * Execute the query as findSet. + */ + public Set findSet(); + + /** + * Execute the query as findMap. + */ + public Map findMap(); + + /** + * Try to get the object out of the persistence context. + */ + //public T getFromPersistenceContextOrCache(); + + /** + * Try to get the query result from the query cache. + */ + public BeanCollection getFromQueryCache(); + } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/TraditionalBackgroundExecutor.java b/src/main/java/com/avaje/ebeaninternal/server/core/TraditionalBackgroundExecutor.java index 5edca60b2..4710cc57a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/TraditionalBackgroundExecutor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/TraditionalBackgroundExecutor.java @@ -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(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfig.java b/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfig.java index f2995c01e..d1246ee4e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfig.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfig.java @@ -1,83 +1,64 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.core; - -import java.util.ArrayList; -import java.util.List; - -import com.avaje.ebeaninternal.server.lib.util.Dnode; - -/** - * Holds the orm.xml and ebean-orm.xml deployment information. - * - * @author rbygrave - */ -public class XmlConfig { - - private final List ebeanOrmXml; - private final List ormXml; - private final List allXml; - - public XmlConfig(List ormXml, List ebeanOrmXml){ - this.ormXml = ormXml; - this.ebeanOrmXml = ebeanOrmXml; - this.allXml = new ArrayList(ormXml.size() + ebeanOrmXml.size()); - allXml.addAll(ormXml); - allXml.addAll(ebeanOrmXml); - } - - public List getEbeanOrmXml() { - return ebeanOrmXml; - } - - public List getOrmXml() { - return ormXml; - } - - public List find(List entityXml, String element) { - ArrayList hits = new ArrayList(); - for (int i = 0; i < entityXml.size(); i++) { - hits.addAll(entityXml.get(i).findAll(element, 1)); - } - return hits; - } - - /** - * Find the deployment xml for a given entity. - *

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

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

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

+ */ + public List findEntityXml(String className) { + + ArrayList hits = new ArrayList(2); + + for (Dnode ormXml : allXml) { + Dnode entityMappings = ormXml.find("entity-mappings"); + + List entities = entityMappings.findAll("entity", "class", className, 1); + if (entities.size() == 1) { + hits.add(entities.get(0)); + } + } + + return hits; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfigLoader.java b/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfigLoader.java index b8beaec3a..50d7bdf33 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfigLoader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/XmlConfigLoader.java @@ -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 ormXml = search("META-INF/orm.xml"); - List ebeanOrmXml = search("META-INF/ebean-orm.xml"); - - return new XmlConfig(ormXml, ebeanOrmXml); - } - - public List search(String searchFor) { - - ArrayList xmlList = new ArrayList(); - - 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 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 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 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 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 ormXml = search("META-INF/orm.xml"); + List ebeanOrmXml = search("META-INF/ebean-orm.xml"); + + return new XmlConfig(ormXml, ebeanOrmXml); + } + + public List search(String searchFor) { + + ArrayList xmlList = new ArrayList(); + + 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 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 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 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 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(); + } + } + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/ddl/AbstractBeanVisitor.java b/src/main/java/com/avaje/ebeaninternal/server/ddl/AbstractBeanVisitor.java index f92e37e38..5f4afab3b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ddl/AbstractBeanVisitor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ddl/AbstractBeanVisitor.java @@ -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); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/ddl/DdlGenContext.java b/src/main/java/com/avaje/ebeaninternal/server/ddl/DdlGenContext.java index ecf28ea46..8a0a40649 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ddl/DdlGenContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ddl/DdlGenContext.java @@ -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 contentBuffer = new ArrayList(); - - private Set intersectionTables = new HashSet(); - - private List intersectionTablesCreateDdl = new ArrayList(); - private List intersectionTablesFkDdl = new ArrayList(); - - 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 contentBuffer = new ArrayList(); + + private Set intersectionTables = new HashSet(); + + private List intersectionTablesCreateDdl = new ArrayList(); + private List intersectionTablesFkDdl = new ArrayList(); + + 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; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/ddl/package-info.java b/src/main/java/com/avaje/ebeaninternal/server/ddl/package-info.java index 4dff5126c..320bfa876 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ddl/package-info.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ddl/package-info.java @@ -1,4 +1 @@ -/** - * DDL generation. - */ package com.avaje.ebeaninternal.server.ddl; \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCascadeInfo.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCascadeInfo.java index 5caab69c7..857dc4d0c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCascadeInfo.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanCascadeInfo.java @@ -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. - *

- * This is set to associated Beans, Table joins and List. - *

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

+ * This is set to associated Beans, Table joins and List. + *

+ */ +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; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java index ef2c127fc..0da4786a5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -1,2650 +1,2631 @@ -/** - * 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.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.naming.InvalidNameException; -import javax.naming.directory.Attributes; -import javax.naming.directory.BasicAttribute; -import javax.naming.directory.BasicAttributes; -import javax.naming.ldap.LdapName; -import javax.persistence.PersistenceException; - -import com.avaje.ebean.InvalidValue; -import com.avaje.ebean.Query; -import com.avaje.ebean.Query.UseIndex; -import com.avaje.ebean.SqlUpdate; -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.PersistenceContext; -import com.avaje.ebean.cache.ServerCache; -import com.avaje.ebean.cache.ServerCacheManager; -import com.avaje.ebean.config.EncryptKey; -import com.avaje.ebean.config.dbplatform.IdGenerator; -import com.avaje.ebean.config.dbplatform.IdType; -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.text.TextException; -import com.avaje.ebean.text.json.JsonWriteBeanVisitor; -import com.avaje.ebean.validation.factory.Validator; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.api.SpiUpdatePlan; -import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD; -import com.avaje.ebeaninternal.server.cache.CachedBeanData; -import com.avaje.ebeaninternal.server.cache.CachedBeanDataFromBean; -import com.avaje.ebeaninternal.server.cache.CachedBeanDataToBean; -import com.avaje.ebeaninternal.server.cache.CachedBeanDataUpdate; -import com.avaje.ebeaninternal.server.cache.CachedManyIds; -import com.avaje.ebeaninternal.server.core.CacheOptions; -import com.avaje.ebeaninternal.server.core.ConcurrencyMode; -import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; -import com.avaje.ebeaninternal.server.core.InternString; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.id.IdBinder; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyLists; -import com.avaje.ebeaninternal.server.el.ElComparator; -import com.avaje.ebeaninternal.server.el.ElComparatorCompound; -import com.avaje.ebeaninternal.server.el.ElComparatorProperty; -import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; -import com.avaje.ebeaninternal.server.el.ElPropertyDeploy; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.ldap.LdapPersistenceException; -import com.avaje.ebeaninternal.server.persist.DmlUtil; -import com.avaje.ebeaninternal.server.query.CQueryPlan; -import com.avaje.ebeaninternal.server.query.SplitName; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; -import com.avaje.ebeaninternal.server.reflect.BeanReflect; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext.ReadBeanState; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext.WriteBeanState; -import com.avaje.ebeaninternal.server.type.DataBind; -import com.avaje.ebeaninternal.server.type.TypeManager; -import com.avaje.ebeaninternal.util.SortByClause; -import com.avaje.ebeaninternal.util.SortByClause.Property; -import com.avaje.ebeaninternal.util.SortByClauseParser; - -/** - * Describes Beans including their deployment information. - */ -public class BeanDescriptor { - - private static final Logger logger = Logger.getLogger(BeanDescriptor.class.getName()); - - private final ConcurrentHashMap updatePlanCache = new ConcurrentHashMap(); - - private final ConcurrentHashMap queryPlanCache = new ConcurrentHashMap(); - - private final ConcurrentHashMap elGetCache = new ConcurrentHashMap(); - - private final ConcurrentHashMap> comparatorCache = new ConcurrentHashMap>(); - - private final ConcurrentHashMap fkeyMap = new ConcurrentHashMap(); - - public enum EntityType { - ORM, EMBEDDED, SQL, META, LDAP, XMLELEMENT - } - - /** - * The EbeanServer name. Same as the plugin name. - */ - private final String serverName; - - /** - * Set to true if this is a LDAP domain object. - */ - private final EntityType entityType; - - /** - * Type of Identity generation strategy used. - */ - private final IdType idType; - - private final IdGenerator idGenerator; - - /** - * The database sequence name (optional). - */ - private final String sequenceName; - - private final String ldapBaseDn; - private final String[] ldapObjectclasses; - - /** - * SQL used to return last inserted id. Used for Identity columns where - * getGeneratedKeys is not supported. - */ - private final String selectLastInsertedId; - - private final boolean autoFetchTunable; - - /** - * Flag indicating this bean has no relationships. - */ - private final boolean cacheSharableBeans; - - private final String lazyFetchIncludes; - - /** - * The concurrency mode for beans of this type. - */ - private final ConcurrencyMode concurrencyMode; - - /** - * The tables this bean is dependent on. - */ - private final String[] dependantTables; - - private final CompoundUniqueContraint[] compoundUniqueConstraints; - - /** - * Extra deployment attributes. - */ - private final Map extraAttrMap; - - /** - * The base database table. - */ - private final String baseTable; - - /** - * Used to provide mechanism to new EntityBean instances. Generated code - * faster than reflection at this stage. - */ - private final BeanReflect beanReflect; - - /** - * Map of BeanProperty Linked so as to preserve order. - */ - private final LinkedHashMap propMap; - private final LinkedHashMap propMapByDbColumn; - - /** - * The type of bean this describes. - */ - private final Class beanType; - - /** - * This is not sent to a remote client. - */ - private final BeanDescriptorMap owner; - - /** - * The EntityBean type used to create new EntityBeans. - */ - private final Class factoryType; - - private final boolean enhancedBean; - - /** - * Intercept pre post on insert,update,delete and postLoad(). Server side - * only. - */ - private volatile BeanPersistController persistController; - - /** - * Listens for post commit insert update and delete events. - */ - private volatile BeanPersistListener persistListener; - - private volatile BeanQueryAdapter queryAdapter; - - /** - * If set overrides the find implementation. Server side only. - */ - private final BeanFinder beanFinder; - - /** - * The table joins for this bean. - */ - private final TableJoin[] derivedTableJoins; - - /** - * Inheritance information. Server side only. - */ - private final InheritInfo inheritInfo; - - /** - * Derived list of properties that make up the unique id. - */ - private final BeanProperty[] propertiesId; - - /** - * Derived list of properties that are used for version concurrency checking. - */ - private final BeanProperty[] propertiesVersion; - private final BeanProperty propertiesNaturalKey; - - /** - * Properties local to this type (not from a super type). - */ - private final BeanProperty[] propertiesLocal; - - private final BeanPropertyAssocOne unidirectional; - - /** - * A hashcode of all the many property names. This is used to efficiently - * create sets of loaded property names (for partial objects). - */ - private final int namesOfManyPropsHash; - - /** - * The set of names of the many properties. - */ - private final Set namesOfManyProps; - - /** - * list of properties that are Lists/Sets/Maps (Derived). - */ - private final BeanProperty[] propertiesNonMany; - private final BeanPropertyAssocMany[] propertiesMany; - private final BeanPropertyAssocMany[] propertiesManySave; - private final BeanPropertyAssocMany[] propertiesManyDelete; - private final BeanPropertyAssocMany[] propertiesManyToMany; - - /** - * list of properties that are associated beans and not embedded (Derived). - */ - private final BeanPropertyAssocOne[] propertiesOne; - - private final BeanPropertyAssocOne[] propertiesOneImported; - private final BeanPropertyAssocOne[] propertiesOneImportedSave; - private final BeanPropertyAssocOne[] propertiesOneImportedDelete; - - private final BeanPropertyAssocOne[] propertiesOneExported; - private final BeanPropertyAssocOne[] propertiesOneExportedSave; - private final BeanPropertyAssocOne[] propertiesOneExportedDelete; - - /** - * list of properties that are embedded beans. - */ - private final BeanPropertyAssocOne[] propertiesEmbedded; - - /** - * List of the scalar properties excluding id and secondary table properties. - */ - private final BeanProperty[] propertiesBaseScalar; - private final BeanPropertyCompound[] propertiesBaseCompound; - - private final BeanProperty[] propertiesTransient; - - /** - * All non transient properties excluding the id properties. - */ - final BeanProperty[] propertiesNonTransient; - - /** - * Set to true if the bean has version properties or an embedded bean has - * version properties. - */ - private final BeanProperty propertyFirstVersion; - - /** - * Set when the Id property is a single non-embedded property. Can make life - * simpler for this case. - */ - private final BeanProperty propertySingleId; - - /** - * The bean class name or the table name for MapBeans. - */ - private final String fullName; - - private final Map namedQueries; - - private final Map namedUpdates; - - /** - * Has local validation rules. - */ - private final boolean hasLocalValidation; - - /** - * Has local or recursive validation rules. - */ - private final boolean hasCascadeValidation; - - /** - * Properties with local validation rules. - */ - private final BeanProperty[] propertiesValidationLocal; - - /** - * Properties with local or cascade validation rules. - */ - private final BeanProperty[] propertiesValidationCascade; - - private final Validator[] beanValidators; - - /** - * Flag used to determine if saves can be skipped. - */ - private boolean saveRecurseSkippable; - - /** - * Flag used to determine if deletes can be skipped. - */ - private boolean deleteRecurseSkippable; - - /** - * Make the TypeManager available for helping SqlSelect. - */ - private final TypeManager typeManager; - - private final IdBinder idBinder; - - private String idBinderInLHSSql; - - private String idBinderIdSql; - - private String deleteByIdSql; - - private String deleteByIdInSql; - - private final String name; - - private final String baseTableAlias; - - /** - * If true then only changed properties get updated. - */ - private final boolean updateChangesOnly; - - private final ServerCacheManager cacheManager; - - private final CacheOptions cacheOptions; - - private final String defaultSelectClause; - private final Set defaultSelectClauseSet; - private final String[] defaultSelectDbArray; - - private final String descriptorId; - - private final UseIndex useIndex; - - private SpiEbeanServer ebeanServer; - - private ServerCache beanCache; - private ServerCache naturalKeyCache; - private ServerCache queryCache; - - /** - * Construct the BeanDescriptor. - */ - public BeanDescriptor(BeanDescriptorMap owner, TypeManager typeManager, DeployBeanDescriptor deploy, String descriptorId) { - - this.owner = owner; - this.cacheManager = owner.getCacheManager(); - this.serverName = owner.getServerName(); - this.entityType = deploy.getEntityType(); - this.name = InternString.intern(deploy.getName()); - this.baseTableAlias = "t0"; - this.fullName = InternString.intern(deploy.getFullName()); - this.descriptorId = descriptorId; - - this.useIndex = deploy.getUseIndex(); - this.typeManager = typeManager; - this.beanType = deploy.getBeanType(); - this.factoryType = deploy.getFactoryType(); - this.enhancedBean = beanType.equals(factoryType); - this.namedQueries = deploy.getNamedQueries(); - this.namedUpdates = deploy.getNamedUpdates(); - - this.inheritInfo = deploy.getInheritInfo(); - - this.beanFinder = deploy.getBeanFinder(); - this.persistController = deploy.getPersistController(); - this.persistListener = deploy.getPersistListener(); - this.queryAdapter = deploy.getQueryAdapter(); - this.cacheOptions = deploy.getCacheOptions(); - - this.defaultSelectClause = deploy.getDefaultSelectClause(); - this.defaultSelectClauseSet = deploy.parseDefaultSelectClause(defaultSelectClause); - this.defaultSelectDbArray = deploy.getDefaultSelectDbArray(defaultSelectClauseSet); - - this.idType = deploy.getIdType(); - this.idGenerator = deploy.getIdGenerator(); - this.ldapBaseDn = deploy.getLdapBaseDn(); - this.ldapObjectclasses = deploy.getLdapObjectclasses(); - this.sequenceName = deploy.getSequenceName(); - this.selectLastInsertedId = deploy.getSelectLastInsertedId(); - this.lazyFetchIncludes = InternString.intern(deploy.getLazyFetchIncludes()); - this.concurrencyMode = deploy.getConcurrencyMode(); - this.updateChangesOnly = deploy.isUpdateChangesOnly(); - - this.dependantTables = deploy.getDependantTables(); - this.compoundUniqueConstraints = deploy.getCompoundUniqueConstraints(); - - this.extraAttrMap = deploy.getExtraAttributeMap(); - - this.baseTable = InternString.intern(deploy.getBaseTable()); - - this.beanReflect = deploy.getBeanReflect(); - - this.autoFetchTunable = EntityType.ORM.equals(entityType) && (beanFinder == null); - - // helper object used to derive lists of properties - DeployBeanPropertyLists listHelper = new DeployBeanPropertyLists(owner, this, deploy); - - this.propMap = listHelper.getPropertyMap(); - this.propMapByDbColumn = getReverseMap(propMap); - this.propertiesTransient = listHelper.getTransients(); - this.propertiesNonTransient = listHelper.getNonTransients(); - this.propertiesBaseScalar = listHelper.getBaseScalar(); - this.propertiesBaseCompound = listHelper.getBaseCompound(); - this.propertiesId = listHelper.getId(); - this.propertiesNaturalKey = listHelper.getNaturalKey(); - this.propertiesVersion = listHelper.getVersion(); - this.propertiesEmbedded = listHelper.getEmbedded(); - this.propertiesLocal = listHelper.getLocal(); - this.unidirectional = listHelper.getUnidirectional(); - this.propertiesOne = listHelper.getOnes(); - this.propertiesOneExported = listHelper.getOneExported(); - this.propertiesOneExportedSave = listHelper.getOneExportedSave(); - this.propertiesOneExportedDelete = listHelper.getOneExportedDelete(); - this.propertiesOneImported = listHelper.getOneImported(); - this.propertiesOneImportedSave = listHelper.getOneImportedSave(); - this.propertiesOneImportedDelete = listHelper.getOneImportedDelete(); - - this.propertiesMany = listHelper.getMany(); - this.propertiesNonMany = listHelper.getNonMany(); - this.propertiesManySave = listHelper.getManySave(); - this.propertiesManyDelete = listHelper.getManyDelete(); - this.propertiesManyToMany = listHelper.getManyToMany(); - boolean noRelationships = propertiesOne.length + propertiesMany.length == 0; - this.cacheSharableBeans = noRelationships && cacheOptions.isReadOnly(); - - this.namesOfManyProps = deriveManyPropNames(); - this.namesOfManyPropsHash = namesOfManyProps.hashCode(); - - this.derivedTableJoins = listHelper.getTableJoin(); - this.propertyFirstVersion = listHelper.getFirstVersion(); - - if (propertiesId.length == 1) { - this.propertySingleId = propertiesId[0]; - } else { - this.propertySingleId = null; - } - - // Check if there are no cascade save associated beans ( subject to change - // in initialiseOther()). Note that if we are in an inheritance hierarchy - // then we also need to check every BeanDescriptors in the InheritInfo as - // well. We do that later in initialiseOther(). - - saveRecurseSkippable = (0 == (propertiesOneExportedSave.length + propertiesOneImportedSave.length + propertiesManySave.length)); - - // Check if there are no cascade delete associated beans (also subject to - // change in initialiseOther()). - deleteRecurseSkippable = (0 == (propertiesOneExportedDelete.length + propertiesOneImportedDelete.length + propertiesManyDelete.length)); - - this.propertiesValidationLocal = listHelper.getPropertiesWithValidators(false); - this.propertiesValidationCascade = listHelper.getPropertiesWithValidators(true); - this.beanValidators = listHelper.getBeanValidators(); - this.hasLocalValidation = (propertiesValidationLocal.length > 0 || beanValidators.length > 0); - this.hasCascadeValidation = (propertiesValidationCascade.length > 0 || beanValidators.length > 0); - - // object used to handle Id values - this.idBinder = owner.createIdBinder(propertiesId); - } - - private LinkedHashMap getReverseMap(LinkedHashMap propMap) { - - LinkedHashMap revMap = new LinkedHashMap(propMap.size() * 2); - - for (BeanProperty prop : propMap.values()) { - if (prop.getDbColumn() != null) { - revMap.put(prop.getDbColumn(), prop); - } - } - - return revMap; - } - - /** - * Set the server. Primarily so that the Many's can lazy load. - */ - public void setEbeanServer(SpiEbeanServer ebeanServer) { - this.ebeanServer = ebeanServer; - for (int i = 0; i < propertiesMany.length; i++) { - // used for creating lazy loading lists etc - propertiesMany[i].setLoader(ebeanServer); - } - } - - /** - * Determine the concurrency mode based on the existence of a non-null version - * property value. - */ - public ConcurrencyMode determineConcurrencyMode(Object bean) { - - if (propertyFirstVersion == null) { - return ConcurrencyMode.NONE; - } - Object v = propertyFirstVersion.getValue(bean); - return (v == null) ? ConcurrencyMode.NONE : ConcurrencyMode.VERSION; - } - - /** - * Return the Set of embedded beans that have changed. - */ - public Set getDirtyEmbeddedProperties(Object bean) { - - HashSet dirtyProperties = null; - - for (int i = 0; i < propertiesEmbedded.length; i++) { - Object embValue = propertiesEmbedded[i].getValue(bean); - if (embValue instanceof EntityBean) { - if (((EntityBean) embValue)._ebean_getIntercept().isDirty()) { - // this embedded is dirty so should be included in an update - if (dirtyProperties == null) { - dirtyProperties = new HashSet(); - } - dirtyProperties.add(propertiesEmbedded[i].getName()); - } - } else { - // must assume it is dirty - if (dirtyProperties == null) { - dirtyProperties = new HashSet(); - } - dirtyProperties.add(propertiesEmbedded[i].getName()); - } - } - - return dirtyProperties; - } - - /** - * Determine the non-null properties of the bean. - */ - public Set determineLoadedProperties(Object bean) { - - HashSet nonNullProps = new HashSet(); - - for (int j = 0; j < propertiesId.length; j++) { - if (propertiesId[j].getValue(bean) != null) { - nonNullProps.add(propertiesId[j].getName()); - } - } - for (int i = 0; i < propertiesNonTransient.length; i++) { - if (propertiesNonTransient[i].getValue(bean) != null) { - nonNullProps.add(propertiesNonTransient[i].getName()); - } - } - return nonNullProps; - } - - /** - * Return the EbeanServer instance that owns this BeanDescriptor. - */ - public SpiEbeanServer getEbeanServer() { - return ebeanServer; - } - - /** - * Return the type of this domain object. - */ - public EntityType getEntityType() { - return entityType; - } - - /** - * Return the default strategy for using a lucene index (if an index is - * defined on this bean type). - */ - public UseIndex getUseIndex() { - return useIndex; - } - - /** - * Initialise the Id properties first. - *

- * These properties need to be initialised prior to the association properties - * as they are used to get the imported and exported properties. - *

- */ - public void initialiseId() { - - if (logger.isLoggable(Level.FINER)) { - logger.finer("BeanDescriptor initialise " + fullName); - } - - if (inheritInfo != null) { - inheritInfo.setDescriptor(this); - } - - if (isEmbedded()) { - // initialise all the properties - Iterator it = propertiesAll(); - while (it.hasNext()) { - BeanProperty prop = it.next(); - prop.initialise(); - } - } else { - // initialise just the Id properties - BeanProperty[] idProps = propertiesId(); - for (int i = 0; i < idProps.length; i++) { - idProps[i].initialise(); - } - } - } - - /** - * Initialise the exported and imported parts for associated properties. - */ - public void initialiseOther() { - - if (!isEmbedded()) { - // initialise all the non-id properties - Iterator it = propertiesAll(); - while (it.hasNext()) { - BeanProperty prop = it.next(); - if (!prop.isId()) { - prop.initialise(); - } - } - } - - if (unidirectional != null) { - unidirectional.initialise(); - } - - idBinder.initialise(); - idBinderInLHSSql = idBinder.getBindIdInSql(baseTableAlias); - idBinderIdSql = idBinder.getBindIdSql(baseTableAlias); - String idBinderInLHSSqlNoAlias = idBinder.getBindIdInSql(null); - String idEqualsSql = idBinder.getBindIdSql(null); - - deleteByIdSql = "delete from " + baseTable + " where " + idEqualsSql; - deleteByIdInSql = "delete from " + baseTable + " where " + idBinderInLHSSqlNoAlias + " "; - - if (!isEmbedded()) { - // parse every named update up front into sql dml - for (DeployNamedUpdate namedUpdate : namedUpdates.values()) { - DeployUpdateParser parser = new DeployUpdateParser(this); - namedUpdate.initialise(parser); - } - } - - } - - public void initInheritInfo() { - if (inheritInfo != null) { - // need to check every BeanDescriptor in the inheritance hierarchy - if (saveRecurseSkippable) { - saveRecurseSkippable = inheritInfo.isSaveRecurseSkippable(); - } - if (deleteRecurseSkippable) { - deleteRecurseSkippable = inheritInfo.isDeleteRecurseSkippable(); - } - } - } - - /** - * Initialise the cache once the server has started. - */ - public void cacheInitialise() { - if (cacheOptions.isUseNaturalKeyCache()) { - this.naturalKeyCache = cacheManager.getNaturalKeyCache(beanType); - } - if (cacheOptions.isUseCache()) { - this.beanCache = cacheManager.getBeanCache(beanType); - } - } - - protected boolean hasInheritance() { - return inheritInfo != null; - } - - protected boolean isDynamicSubclass() { - return !beanType.equals(factoryType); - } - - /** - * Set the LDAP objectClasses to the attributes. - */ - public void setLdapObjectClasses(Attributes attributes) { - - if (ldapObjectclasses != null) { - BasicAttribute ocAttrs = new BasicAttribute("objectclass"); - for (int i = 0; i < ldapObjectclasses.length; i++) { - ocAttrs.add(ldapObjectclasses[i]); - } - attributes.put(ocAttrs); - } - } - - /** - * Creates Attributes with the objectclass. - */ - public Attributes createAttributes() { - - Attributes attrs = new BasicAttributes(true); - setLdapObjectClasses(attrs); - return attrs; - } - - public String getLdapBaseDn() { - return ldapBaseDn; - } - - public LdapName createLdapNameById(Object id) throws InvalidNameException { - - LdapName baseDn = new LdapName(ldapBaseDn); - idBinder.createLdapNameById(baseDn, id); - return baseDn; - } - - public LdapName createLdapName(Object bean) { - - try { - LdapName name = new LdapName(ldapBaseDn); - if (bean != null) { - idBinder.createLdapNameByBean(name, bean); - } - return name; - - } catch (InvalidNameException e) { - throw new LdapPersistenceException(e); - } - } - - public SqlUpdate deleteById(Object id, List idList) { - if (id != null) { - return deleteById(id); - } else { - return deleteByIdList(idList); - } - } - - /** - * Return SQL that can be used to delete a list of Id's without any optimistic - * concurrency checking. - */ - private SqlUpdate deleteByIdList(List idList) { - - StringBuilder sb = new StringBuilder(deleteByIdInSql); - String inClause = idBinder.getIdInValueExprDelete(idList.size()); - sb.append(inClause); - - DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString()); - for (int i = 0; i < idList.size(); i++) { - idBinder.bindId(delete, idList.get(i)); - } - return delete; - } - - /** - * Return SQL that can be used to delete by Id without any optimistic - * concurrency checking. - */ - private SqlUpdate deleteById(Object id) { - - DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByIdSql); - - Object[] bindValues = idBinder.getBindValues(id); - for (int i = 0; i < bindValues.length; i++) { - sqlDelete.addParameter(bindValues[i]); - } - - return sqlDelete; - } - - /** - * Add objects to ElPropertyDeploy etc. These are used so that expressions on - * foreign keys don't require an extra join. - */ - public void add(BeanFkeyProperty fkey) { - fkeyMap.put(fkey.getName(), fkey); - } - - public void initialiseFkeys() { - for (int i = 0; i < propertiesOneImported.length; i++) { - propertiesOneImported[i].addFkey(); - } - } - - public boolean calculateUseCache(Boolean queryUseCache) { - return (queryUseCache != null) ? queryUseCache.booleanValue() : isBeanCaching(); - } - - public boolean calculateUseNaturalKeyCache(Boolean queryUseCache) { - return (queryUseCache != null) ? queryUseCache.booleanValue() : isBeanCaching(); - } - - /** - * Return the cache options. - */ - public CacheOptions getCacheOptions() { - return cacheOptions; - } - - /** - * Return the Encrypt key given the BeanProperty. - */ - public EncryptKey getEncryptKey(BeanProperty p) { - return owner.getEncryptKey(baseTable, p.getDbColumn()); - } - - /** - * Return the Encrypt key given the table and column name. - */ - public EncryptKey getEncryptKey(String tableName, String columnName) { - return owner.getEncryptKey(tableName, columnName); - } - - /** - * Execute the warming cache query (if defined) and load the cache. - */ - public void runCacheWarming() { - if (cacheOptions == null) { - return; - } - String warmingQuery = cacheOptions.getWarmingQuery(); - if (warmingQuery != null && warmingQuery.trim().length() > 0) { - Query query = ebeanServer.createQuery(beanType, warmingQuery); - query.setUseCache(true); - query.setReadOnly(true); - query.setLoadBeanCache(true); - List list = query.findList(); - if (logger.isLoggable(Level.INFO)) { - String msg = "Loaded " + beanType + " cache with [" + list.size() + "] beans"; - logger.info(msg); - } - } - } - - /** - * Return true if this bean type has a default select clause that is not - * simply select all properties. - */ - public boolean hasDefaultSelectClause() { - return defaultSelectClause != null; - } - - /** - * Return the default select clause. - */ - public String getDefaultSelectClause() { - return defaultSelectClause; - } - - /** - * Return the default select clause already parsed into an ordered Set. - */ - public Set getDefaultSelectClauseSet() { - return defaultSelectClauseSet; - } - - /** - * For LDAP return array of (DB) attributes to include in query by default. - */ - public String[] getDefaultSelectDbArray() { - return defaultSelectDbArray; - } - - /** - * Return true if this object is the root level object in its entity - * inheritance. - */ - public boolean isInheritanceRoot() { - return inheritInfo == null || inheritInfo.isRoot(); - } - - /** - * Return true if there is currently query caching for this type of bean. - */ - public boolean isQueryCaching() { - return queryCache != null; - } - - /** - * Return true if there is currently bean caching for this type of bean. - */ - public boolean isBeanCaching() { - return beanCache != null; - } - - public boolean cacheIsUseManyId() { - return isBeanCaching(); - } - - /** - * Return true if the persist request needs to notify the cache. - */ - public boolean isCacheNotify() { - - if (isBeanCaching() || isQueryCaching()) { - return true; - } - for (int i = 0; i < propertiesOneImported.length; i++) { - if (propertiesOneImported[i].getTargetDescriptor().isBeanCaching()) { - return true; - } - } - return false; - } - - /** - * Return true if there is L2 caching (Lucene or Bean cache) for this bean - * type. - */ - public boolean isUsingL2Cache() { - return isBeanCaching(); - } - - /** - * Invalidate parts of cache due to SqlUpdate or external modification etc. - */ - public void cacheNotify(TableIUD tableIUD) { - // inserts don't invalidate the bean cache - if (tableIUD.isUpdateOrDelete()) { - cacheClear(); - } - // any change invalidates the query cache - queryCacheClear(); - } - - /** - * Clear the query cache. - */ - public void queryCacheClear() { - if (queryCache != null) { - queryCache.clear(); - } - } - - /** - * Get a query result from the query cache. - */ - @SuppressWarnings("unchecked") - public BeanCollection queryCacheGet(Object id) { - if (queryCache == null) { - return null; - } else { - return (BeanCollection) queryCache.get(id); - } - } - - /** - * Put a query result into the query cache. - */ - public void queryCachePut(Object id, BeanCollection query) { - if (queryCache == null) { - queryCache = cacheManager.getQueryCache(beanType); - } - queryCache.put(id, query); - } - - private ServerCache getBeanCache() { - if (beanCache == null) { - beanCache = cacheManager.getBeanCache(beanType); - } - return beanCache; - } - - /** - * Clear the bean cache. - */ - public void cacheClear() { - if (beanCache != null) { - beanCache.clear(); - } - } - - /** - * Put a bean into the bean cache. - */ - public void cachePutBeanData(Object bean) { - - CachedBeanData beanData = CachedBeanDataFromBean.extract(this, bean); - - Object id = getId(bean); - getBeanCache().put(id, beanData); - if (beanData.isNaturalKeyUpdate() && naturalKeyCache != null) { - Object naturalKey = beanData.getNaturalKey(); - if (naturalKey != null) { - naturalKeyCache.put(naturalKey, id); - } - } - } - - public boolean cacheLoadMany(BeanPropertyAssocMany many, BeanCollection bc, Object parentId, Boolean readOnly, boolean vanilla) { - - CachedManyIds ids = cacheGetCachedManyIds(parentId, many.getName()); - if (ids == null) { - return false; - } - - Object ownerBean = bc.getOwnerBean(); - EntityBeanIntercept ebi = ((EntityBean) ownerBean)._ebean_getIntercept(); - PersistenceContext persistenceContext = ebi.getPersistenceContext(); - - BeanDescriptor targetDescriptor = many.getTargetDescriptor(); - - List idList = ids.getIdList(); - bc.checkEmptyLazyLoad(); - for (int i = 0; i < idList.size(); i++) { - Object id = idList.get(i); - Object refBean = targetDescriptor.createReference(vanilla, readOnly, id, null); - EntityBeanIntercept refEbi = ((EntityBean) refBean)._ebean_getIntercept(); - - many.add(bc, refBean); - persistenceContext.put(id, refBean); - refEbi.setPersistenceContext(persistenceContext); - } - return true; - } - - public void cachePutMany(BeanPropertyAssocMany many, BeanCollection bc, Object parentId) { - BeanDescriptor targetDescriptor = many.getTargetDescriptor(); - Collection actualDetails = bc.getActualDetails(); - ArrayList idList = new ArrayList(); - for (Object bean : actualDetails) { - Object id = targetDescriptor.getId(bean); - idList.add(id); - } - CachedManyIds ids = new CachedManyIds(idList); - cachePutCachedManyIds(parentId, many.getName(), ids); - } - - public void cacheRemoveCachedManyIds(Object parentId, String propertyName) { - ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); - collectionIdsCache.remove(parentId); - } - - public void cacheClearCachedManyIds(String propertyName) { - ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); - collectionIdsCache.clear(); - } - - public CachedManyIds cacheGetCachedManyIds(Object parentId, String propertyName) { - ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); - return (CachedManyIds) collectionIdsCache.get(parentId); - } - - public void cachePutCachedManyIds(Object parentId, String propertyName, CachedManyIds ids) { - ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); - collectionIdsCache.put(parentId, ids); - } - - /** - * Return a bean from the bean cache. - */ - @SuppressWarnings("unchecked") - public T cacheGetBean(Object id, boolean vanilla, Boolean readOnly) { - - CachedBeanData d = (CachedBeanData) getBeanCache().get(id); - if (d == null) { - return null; - } - if (cacheSharableBeans && !vanilla && !Boolean.FALSE.equals(readOnly)) { - Object bean = d.getSharableBean(); - if (bean != null) { - return (T) bean; - } - } - - T bean = (T) createBean(vanilla); - convertSetId(id, bean); - if (!vanilla && Boolean.TRUE.equals(readOnly)) { - ((EntityBean) bean)._ebean_getIntercept().setReadOnly(true); - } - - CachedBeanDataToBean.load(this, bean, d); - return bean; - } - - public boolean cacheIsNaturalKey(String propName) { - return propName != null && propName.equals(cacheOptions.getNaturalKey()); - } - - public Object cacheGetNaturalKeyId(Object uniqueKeyValue) { - if (naturalKeyCache != null) { - return naturalKeyCache.get(uniqueKeyValue); - } - return null; - } - - /** - * Remove a bean from the cache given its Id. - */ - public void cacheRemove(Object id) { - if (beanCache != null) { - beanCache.remove(id); - } - for (int i = 0; i < propertiesOneImported.length; i++) { - propertiesOneImported[i].cacheClear(); - } - } - - /** - * Remove a bean from the cache given its Id. - */ - public void cacheDelete(Object id, PersistRequestBean deleteRequest) { - if (beanCache != null) { - beanCache.remove(id); - } - for (int i = 0; i < propertiesOneImported.length; i++) { - BeanPropertyAssocMany many = propertiesOneImported[i].getRelationshipProperty(); - if (many != null) { - propertiesOneImported[i].cacheDelete(true, deleteRequest); - } - } - } - - public void cacheInsert(Object id, PersistRequestBean insertRequest) { - if (queryCache != null) { - queryCache.clear(); - } - for (int i = 0; i < propertiesOneImported.length; i++) { - propertiesOneImported[i].cacheDelete(false, insertRequest.getBean()); - } - } - - /** - * Update the cached bean data. - */ - public void cacheUpdate(Object id, PersistRequestBean updateRequest) { - - ServerCache cache = getBeanCache(); - CachedBeanData cd = (CachedBeanData) cache.get(id); - if (cd != null) { - CachedBeanData newCd = CachedBeanDataUpdate.update(this, cd, updateRequest); - cache.put(id, newCd); - if (newCd.isNaturalKeyUpdate() && naturalKeyCache != null) { - Object oldKey = propertiesNaturalKey.getValue(updateRequest.getOldValues()); - Object newKey = propertiesNaturalKey.getValue(updateRequest.getBean()); - if (oldKey != null) { - naturalKeyCache.remove(oldKey); - } - if (newKey != null) { - naturalKeyCache.put(newKey, id); - } - } - } - } - - /** - * Return the base table alias. This is always the first letter of the bean - * name. - */ - public String getBaseTableAlias() { - return baseTableAlias; - } - - public boolean loadFromCache(EntityBeanIntercept ebi) { - Object bean = ebi.getOwner(); - Object id = getId(bean); - - return loadFromCache(bean, ebi, id); - } - - public boolean loadFromCache(Object bean, EntityBeanIntercept ebi, Object id) { - - CachedBeanData cacheData = (CachedBeanData) getBeanCache().get(id); - if (cacheData == null) { - return false; - } - String lazyLoadProperty = ebi.getLazyLoadProperty(); - if (lazyLoadProperty != null && !cacheData.containsProperty(lazyLoadProperty)) { - return false; - } - - CachedBeanDataToBean.load(this, bean, ebi, cacheData); - return true; - } - - public void preAllocateIds(int batchSize) { - if (idGenerator != null) { - idGenerator.preAllocateIds(batchSize); - } - } - - public Object nextId(Transaction t) { - if (idGenerator != null) { - return idGenerator.nextId(t); - } else { - return null; - } - } - - public DeployPropertyParser createDeployPropertyParser() { - return new DeployPropertyParser(this); - } - - /** - * Convert the logical orm update statement into sql by converting the bean - * properties and bean name to database columns and table. - */ - public String convertOrmUpdateToSql(String ormUpdateStatement) { - return new DeployUpdateParser(this).parse(ormUpdateStatement); - } - - /** - * Reset the statistics on all the query plans. - */ - public void clearQueryStatistics() { - Iterator it = queryPlanCache.values().iterator(); - while (it.hasNext()) { - CQueryPlan queryPlan = (CQueryPlan) it.next(); - queryPlan.resetStatistics(); - } - } - - /** - * Execute the postLoad if a BeanPersistController exists for this bean. - */ - @SuppressWarnings("unchecked") - public void postLoad(Object bean, Set includedProperties) { - BeanPersistController c = persistController; - if (c != null) { - c.postLoad((T) bean, includedProperties); - } - } - - /** - * Return the query plans for this BeanDescriptor. - */ - public Iterator queryPlans() { - return queryPlanCache.values().iterator(); - } - - public CQueryPlan getQueryPlan(Integer key) { - return queryPlanCache.get(key); - } - - public void putQueryPlan(Integer key, CQueryPlan plan) { - queryPlanCache.put(key, plan); - } - - /** - * Get a UpdatePlan for a given hash. - */ - public SpiUpdatePlan getUpdatePlan(Integer key) { - return updatePlanCache.get(key); - } - - /** - * Add a UpdatePlan to the cache with a given hash. - */ - public void putUpdatePlan(Integer key, SpiUpdatePlan plan) { - updatePlanCache.put(key, plan); - } - - /** - * Return the TypeManager. - */ - public TypeManager getTypeManager() { - return typeManager; - } - - /** - * Return true if updates should only include changed properties. Otherwise - * all loaded properties are included in the update. - */ - public boolean isUpdateChangesOnly() { - return updateChangesOnly; - } - - /** - * Return true if save does not recurse to other beans. That is return true if - * there are no assoc one or assoc many beans that cascade save. - */ - public boolean isSaveRecurseSkippable() { - return saveRecurseSkippable; - } - - /** - * Return true if delete does not recurse to other beans. That is return true - * if there are no assoc one or assoc many beans that cascade delete. - */ - public boolean isDeleteRecurseSkippable() { - return deleteRecurseSkippable; - } - - /** - * Return true if this type has local validation rules. - */ - public boolean hasLocalValidation() { - return hasLocalValidation; - } - - /** - * Return true if this type has local or cascading validation rules. - */ - public boolean hasCascadeValidation() { - return hasCascadeValidation; - } - - public InvalidValue validate(boolean cascade, Object bean) { - - if (!hasCascadeValidation) { - // no validation rules at all on this bean - return null; - } - - List errList = null; - - Set loadedProps = null; - if (bean instanceof EntityBean) { - EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept(); - loadedProps = ebi.getLoadedProps(); - } - if (loadedProps != null) { - // validate just the loaded properties - Iterator propIt = loadedProps.iterator(); - while (propIt.hasNext()) { - String propName = (String) propIt.next(); - BeanProperty property = getBeanProperty(propName); - - // check if we should fire validation on this property - if (property != null && property.hasValidationRules(cascade)) { - Object value = property.getValue(bean); - List errs = property.validate(cascade, value); - if (errs != null) { - if (errList == null) { - errList = new ArrayList(); - } - errList.addAll(errs); - } - } - } - } else { - // get appropriate list of properties with validation rules - BeanProperty[] props = cascade ? propertiesValidationCascade : propertiesValidationLocal; - - // validate all the properties - for (int i = 0; i < props.length; i++) { - BeanProperty prop = props[i]; - Object value = prop.getValue(bean); - List errs = prop.validate(cascade, value); - if (errs != null) { - if (errList == null) { - errList = new ArrayList(); - } - errList.addAll(errs); - } - } - } - - for (int i = 0; i < beanValidators.length; i++) { - if (!beanValidators[i].isValid(bean)) { - if (errList == null) { - errList = new ArrayList(); - } - Validator v = beanValidators[i]; - errList.add(new InvalidValue(v.getKey(), v.getAttributes(), getFullName(), null, bean)); - } - } - - if (errList == null) { - return null; - } - - return new InvalidValue(null, getFullName(), bean, InvalidValue.toArray(errList)); - } - - /** - * Return the many property included in the query or null if one is not. - */ - public BeanPropertyAssocMany getManyProperty(SpiQuery query) { - - OrmQueryDetail detail = query.getDetail(); - for (int i = 0; i < propertiesMany.length; i++) { - if (detail.includes(propertiesMany[i].getName())) { - return propertiesMany[i]; - } - } - - return null; - } - - /** - * Return the IdBinder which is helpful for handling the various types of Id. - */ - public IdBinder getIdBinder() { - return idBinder; - } - - /** - * Return the sql for binding an id. This is the columns with table alias that - * make up the id. - */ - public String getIdBinderIdSql() { - return idBinderIdSql; - } - - /** - * Return the sql for binding id's using an IN clause. - */ - public String getIdBinderInLHSSql() { - return idBinderInLHSSql; - } - - /** - * Bind the idValue to the preparedStatement. - *

- * This takes care of the various id types such as embedded beans etc. - *

- */ - public void bindId(DataBind dataBind, Object idValue) throws SQLException { - idBinder.bindId(dataBind, idValue); - } - - /** - * Return the id as an array of scalar bindable values. - *

- * This 'flattens' any EmbeddedId or multiple Id property cases. - *

- */ - public Object[] getBindIdValues(Object idValue) { - return idBinder.getBindValues(idValue); - } - - /** - * Return a named query. - */ - public DeployNamedQuery getNamedQuery(String name) { - return namedQueries.get(name); - } - - public DeployNamedQuery addNamedQuery(DeployNamedQuery deployNamedQuery) { - return namedQueries.put(deployNamedQuery.getName(), deployNamedQuery); - } - - /** - * Return a named update. - */ - public DeployNamedUpdate getNamedUpdate(String name) { - return namedUpdates.get(name); - } - - /** - * Create an EntityBean or "Vanilla" bean depending on the flag. - */ - public Object createBean(boolean vanillaMode) { - return vanillaMode ? createVanillaBean() : createEntityBean(); - } - - /** - * Create a plain vanilla object. - *

- * Used for EmbeddedId Bean construction. - *

- */ - public Object createVanillaBean() { - return beanReflect.createVanillaBean(); - } - - /** - * Creates a new EntityBean without using the creation queue. - */ - public EntityBean createEntityBean() { - try { - // Note factoryType is used indirectly via beanReflect - EntityBean eb = (EntityBean) beanReflect.createEntityBean(); - - return eb; - - } catch (Exception ex) { - throw new PersistenceException(ex); - } - } - - /** - * Create a reference bean based on the id. - */ - @SuppressWarnings("unchecked") - public T createReference(boolean vanillaMode, Boolean readOnly, Object id, Object parent) { - - if (cacheSharableBeans && !vanillaMode && !Boolean.FALSE.equals(readOnly)) { - CachedBeanData d = (CachedBeanData) getBeanCache().get(id); - if (d != null) { - Object shareableBean = d.getSharableBean(); - if (shareableBean != null) { - return (T) shareableBean; - } - } - } - try { - Object bean = createBean(vanillaMode); - - convertSetId(id, bean); - - if (!vanillaMode) { - EntityBean eb = (EntityBean) bean; - - EntityBeanIntercept ebi = eb._ebean_getIntercept(); - ebi.setBeanLoaderByServerName(ebeanServer.getName()); - - if (parent != null) { - // Special case for a OneToOne ... parent - // needs to be added to context prior to query - ebi.setParentBean(parent); - } - - // Note: not creating proxies for many's... - ebi.setReference(); - } - - return (T) bean; - - } catch (Exception ex) { - throw new PersistenceException(ex); - } - } - - /** - * Return the BeanProperty for the given deployment name. - */ - public BeanProperty getBeanPropertyFromDbColumn(String dbColumn) { - return propMapByDbColumn.get(dbColumn); - } - - /** - * Return the bean property traversing the object graph and taking into - * account inheritance. - */ - public BeanProperty getBeanPropertyFromPath(String path) { - - String[] split = SplitName.splitBegin(path); - if (split[1] == null) { - return _findBeanProperty(split[0]); - } - BeanPropertyAssoc assocProp = (BeanPropertyAssoc) _findBeanProperty(split[0]); - BeanDescriptor targetDesc = assocProp.getTargetDescriptor(); - - return targetDesc.getBeanPropertyFromPath(split[1]); - } - - /** - * Return the BeanDescriptor for a given path of Associated One or Many beans. - */ - public BeanDescriptor getBeanDescriptor(String path) { - if (path == null) { - return this; - } - String[] splitBegin = SplitName.splitBegin(path); - - BeanProperty beanProperty = propMap.get(splitBegin[0]); - if (beanProperty instanceof BeanPropertyAssoc) { - BeanPropertyAssoc assocProp = (BeanPropertyAssoc) beanProperty; - return assocProp.getTargetDescriptor().getBeanDescriptor(splitBegin[1]); - - } else { - throw new RuntimeException("Error getting BeanDescriptor for path " + path + " from " + getFullName()); - } - } - - /** - * Return the BeanDescriptor of another bean type. - */ - public BeanDescriptor getBeanDescriptor(Class otherType) { - return owner.getBeanDescriptor(otherType); - } - - /** - * Return the "shadow" property to support unidirectional relationships. - *

- * For bidirectional this is a real property on the bean. For unidirectional - * relationships we have this 'shadow' property which is not externally - * visible. - *

- */ - public BeanPropertyAssocOne getUnidirectional() { - if (unidirectional != null) { - return unidirectional; - } - if (inheritInfo != null && !inheritInfo.isRoot()) { - return inheritInfo.getParent().getBeanDescriptor().getUnidirectional(); - } - return null; - } - - /** - * Get a property value from a bean of this type. - */ - public Object getValue(Object bean, String property) { - return getBeanProperty(property).getValue(bean); - } - - /** - * Return true if this bean type should use IdGeneration. - *

- * If this is false and the Id is null it is assumed that a database auto - * increment feature is being used to populate the id. - *

- */ - public boolean isUseIdGenerator() { - return idGenerator != null; - } - - /** - * Return the alternate "Id" that identifies this BeanDescriptor. This is an - * alternative to using the bean class name. - */ - public String getDescriptorId() { - return descriptorId; - } - - /** - * Return the class type this BeanDescriptor describes. - */ - public Class getBeanType() { - return beanType; - } - - /** - * Return the class type this BeanDescriptor describes. - */ - public Class getFactoryType() { - return factoryType; - } - - /** - * Return the bean class name this descriptor is used for. - *

- * If this BeanDescriptor is for a table then this returns the table name - * instead. - *

- */ - public String getFullName() { - return fullName; - } - - /** - * Return the short name of the entity bean. - */ - public String getName() { - return name; - } - - /** - * Summary description. - */ - public String toString() { - return fullName; - } - - /** - * Helper method to return the unique property. If only one property makes up - * the unique id then it's value is returned. If there is a concatenated - * unique id then a Map is built with the keys being the names of the - * properties that make up the unique id. - */ - public Object getId(Object bean) { - - if (propertySingleId != null) { - if (inheritInfo != null && !enhancedBean) { - // avoid generated method via forced reflection use - return propertySingleId.getValueViaReflection(bean); - - } else { - return propertySingleId.getValue(bean); - } - } - - // it is a concatenated id Not embedded - // so return a Map - LinkedHashMap idMap = new LinkedHashMap(); - for (int i = 0; i < propertiesId.length; i++) { - - Object value = propertiesId[i].getValue(bean); - idMap.put(propertiesId[i].getName(), value); - } - return idMap; - } - - /** - * Return false if the id is a simple scalar and false if it is embedded or - * concatenated. - */ - public boolean isComplexId() { - return idBinder.isComplexId(); - } - - /** - * Return the default order by that may need to be added if a many property is - * included in the query. - */ - public String getDefaultOrderBy() { - return idBinder.getDefaultOrderBy(); - } - - /** - * Convert the type of the idValue if required. - */ - public Object convertId(Object idValue) { - return idBinder.convertSetId(idValue, null); - } - - /** - * Convert and set the id value. - *

- * If the bean is not null, the id value is set to the id property of the bean - * after it has been converted to the correct type. - *

- */ - public Object convertSetId(Object idValue, Object bean) { - return idBinder.convertSetId(idValue, bean); - } - - /** - * Get a BeanProperty by its name. - */ - public BeanProperty getBeanProperty(String propName) { - return (BeanProperty) propMap.get(propName); - } - - public void sort(List list, String sortByClause) { - - ElComparator comparator = getElComparator(sortByClause); - Collections.sort(list, comparator); - } - - public ElComparator getElComparator(String propNameOrSortBy) { - ElComparator c = comparatorCache.get(propNameOrSortBy); - if (c == null) { - c = createComparator(propNameOrSortBy); - comparatorCache.put(propNameOrSortBy, c); - } - return c; - } - - /** - * Return true if the lazy loading property is a Many in which case just - * define a Reference for the collection and not invoke a query. - */ - public boolean lazyLoadMany(EntityBeanIntercept ebi) { - - String lazyLoadProperty = ebi.getLazyLoadProperty(); - BeanProperty lazyLoadBeanProp = getBeanProperty(lazyLoadProperty); - - if (lazyLoadBeanProp instanceof BeanPropertyAssocMany) { - BeanPropertyAssocMany manyProp = (BeanPropertyAssocMany) lazyLoadBeanProp; - manyProp.createReference(ebi.getOwner()); - Set loadedProps = ebi.getLoadedProps(); - HashSet newLoadedProps = new HashSet(); - if (loadedProps != null) { - newLoadedProps.addAll(loadedProps); - } - newLoadedProps.add(lazyLoadProperty); - ebi.setLoadedProps(newLoadedProps); - ebi.setLoadedLazy(); - return true; - } - - return false; - } - - /** - * Return a Comparator for local sorting of lists. - * - * @param sortByClause - * list of property names with optional ASC or DESC suffix. - */ - @SuppressWarnings("unchecked") - private ElComparator createComparator(String sortByClause) { - - SortByClause sortBy = SortByClauseParser.parse(sortByClause); - if (sortBy.size() == 1) { - // simple comparator for a single property - return createPropertyComparator(sortBy.getProperties().get(0)); - } - - // create a compound comparator based on the list of properties - ElComparator[] comparators = new ElComparator[sortBy.size()]; - - List sortProps = sortBy.getProperties(); - for (int i = 0; i < sortProps.size(); i++) { - Property sortProperty = sortProps.get(i); - comparators[i] = createPropertyComparator(sortProperty); - } - - return new ElComparatorCompound(comparators); - } - - private ElComparator createPropertyComparator(Property sortProp) { - - ElPropertyValue elGetValue = getElGetValue(sortProp.getName()); - - Boolean nullsHigh = sortProp.getNullsHigh(); - if (nullsHigh == null) { - nullsHigh = Boolean.TRUE; - } - return new ElComparatorProperty(elGetValue, sortProp.isAscending(), nullsHigh); - } - - /** - * Get an Expression language Value object. - */ - public ElPropertyValue getElGetValue(String propName) { - return getElPropertyValue(propName, false); - } - - /** - * Similar to ElPropertyValue but also uses foreign key shortcuts. - *

- * The foreign key shortcuts means we can avoid unnecessary joins. - *

- */ - public ElPropertyDeploy getElPropertyDeploy(String propName) { - ElPropertyDeploy fk = fkeyMap.get(propName); - if (fk != null) { - return fk; - } - return getElPropertyValue(propName, true); - } - - private ElPropertyValue getElPropertyValue(String propName, boolean propertyDeploy) { - ElPropertyValue elGetValue = elGetCache.get(propName); - if (elGetValue == null) { - // need to build it potentially navigating the BeanDescriptors - elGetValue = buildElGetValue(propName, null, propertyDeploy); - if (elGetValue == null) { - return null; - } - if (elGetValue instanceof BeanFkeyProperty) { - fkeyMap.put(propName, (BeanFkeyProperty) elGetValue); - } else { - elGetCache.put(propName, elGetValue); - } - } - return elGetValue; - } - - protected ElPropertyValue buildElGetValue(String propName, ElPropertyChainBuilder chain, boolean propertyDeploy) { - - if (propertyDeploy && chain != null) { - BeanFkeyProperty fk = fkeyMap.get(propName); - if (fk != null) { - return fk.create(chain.getExpression()); - } - } - - int basePos = propName.indexOf('.'); - if (basePos > -1) { - // nested or embedded property - String baseName = propName.substring(0, basePos); - String remainder = propName.substring(basePos + 1); - - BeanProperty assocProp = _findBeanProperty(baseName); - if (assocProp == null) { - return null; - } - return assocProp.buildElPropertyValue(propName, remainder, chain, propertyDeploy); - } - - BeanProperty property = _findBeanProperty(propName); - if (chain == null) { - return property; - } - if (property == null) { - throw new PersistenceException("No property found for [" + propName + "] in expression " + chain.getExpression()); - } - if (property.containsMany()) { - chain.setContainsMany(true); - } - return chain.add(property).build(); - } - - /** - * Find a BeanProperty including searching the inheritance hierarchy. - *

- * This searches this BeanDescriptor and then searches further down the - * inheritance tree (not up). - *

- */ - public BeanProperty findBeanProperty(String propName) { - int basePos = propName.indexOf('.'); - if (basePos > -1) { - // embedded property - String baseName = propName.substring(0, basePos); - return _findBeanProperty(baseName); - } - - return _findBeanProperty(propName); - } - - private BeanProperty _findBeanProperty(String propName) { - BeanProperty prop = propMap.get(propName); - if (prop == null && inheritInfo != null) { - // search in sub types... - return inheritInfo.findSubTypeProperty(propName); - } - return prop; - } - - protected Object getBeanPropertyWithInheritance(Object bean, String propName) { - - BeanDescriptor desc = getBeanDescriptor(bean.getClass()); - BeanProperty beanProperty = desc.findBeanProperty(propName); - return beanProperty.getValue(bean); - } - - /** - * Return the name of the server this BeanDescriptor belongs to. - */ - public String getServerName() { - return serverName; - } - - /** - * Return true if this bean can cache sharable instances. - *

- * This means is has no relationships and has readOnly=true in its cache - * options. - *

- */ - public boolean isCacheSharableBeans() { - return cacheSharableBeans; - } - - /** - * Return true if queries for beans of this type are autoFetch tunable. - */ - public boolean isAutoFetchTunable() { - return autoFetchTunable; - } - - /** - * Returns the Inheritance mapping information. This will be null if this type - * of bean is not involved in any ORM inheritance mapping. - */ - public InheritInfo getInheritInfo() { - return inheritInfo; - } - - /** - * Return true if this is an embedded bean. - */ - public boolean isEmbedded() { - return EntityType.EMBEDDED.equals(entityType); - } - - public boolean isBaseTableType() { - return EntityType.ORM.equals(entityType); - } - - /** - * Return the concurrency mode used for beans of this type. - */ - public ConcurrencyMode getConcurrencyMode() { - return concurrencyMode; - } - - /** - * Return the tables this bean is dependent on. This implies that if any of - * these tables are modified then cached beans may be invalidated. - */ - public String[] getDependantTables() { - return dependantTables; - } - - /** - * Return the compound unique constraints. - */ - public CompoundUniqueContraint[] getCompoundUniqueConstraints() { - return compoundUniqueConstraints; - } - - /** - * Return the beanListener. - */ - public BeanPersistListener getPersistListener() { - return persistListener; - } - - /** - * Return the beanFinder. Usually null unless overriding the finder. - */ - public BeanFinder getBeanFinder() { - return beanFinder; - } - - /** - * Return the BeanQueryAdapter or null if none is defined. - */ - public BeanQueryAdapter getQueryAdapter() { - return queryAdapter; - } - - /** - * De-register the BeanPersistListener. - */ - @SuppressWarnings("unchecked") - public void deregister(BeanPersistListener listener) { - // volatile read... - BeanPersistListener currListener = persistListener; - if (currListener == null) { - // nothing to deregister - } else { - BeanPersistListener deregListener = (BeanPersistListener) listener; - if (currListener instanceof ChainedBeanPersistListener) { - // remove it from the existing chain - persistListener = ((ChainedBeanPersistListener) currListener).deregister(deregListener); - } else if (currListener.equals(deregListener)) { - persistListener = null; - } - } - } - - /** - * De-register the BeanPersistController. - */ - public void deregister(BeanPersistController controller) { - // volatile read... - BeanPersistController c = persistController; - if (c == null) { - // nothing to deregister - } else { - if (c instanceof ChainedBeanPersistController) { - // remove it from the existing chain - persistController = ((ChainedBeanPersistController) c).deregister(controller); - } else if (c.equals(controller)) { - persistController = null; - } - } - } - - /** - * Register the new BeanPersistController. - */ - @SuppressWarnings("unchecked") - public void register(BeanPersistListener newPersistListener) { - - if (!PersistListenerManager.isRegisterFor(beanType, newPersistListener)) { - // skip - } else { - BeanPersistListener newListener = (BeanPersistListener) newPersistListener; - // volatile read... - BeanPersistListener currListener = persistListener; - if (currListener == null) { - persistListener = newListener; - } else { - if (currListener instanceof ChainedBeanPersistListener) { - // add it to the existing chain - persistListener = ((ChainedBeanPersistListener) currListener).register(newListener); - } else { - // build new chain of the 2 - persistListener = new ChainedBeanPersistListener(currListener, newListener); - } - } - } - } - - /** - * Register the new BeanPersistController. - */ - public void register(BeanPersistController newController) { - - if (!newController.isRegisterFor(beanType)) { - // skip - } else { - // volatile read... - BeanPersistController c = persistController; - if (c == null) { - persistController = newController; - } else { - if (c instanceof ChainedBeanPersistController) { - // add it to the existing chain - persistController = ((ChainedBeanPersistController) c).register(newController); - } else { - // build new chain of the 2 - persistController = new ChainedBeanPersistController(c, newController); - } - } - } - } - - /** - * Return the Controller. - */ - public BeanPersistController getPersistController() { - return persistController; - } - - /** - * Returns true if this bean is based on a table (or possibly view) and - * returns false if this bean is based on a raw sql select statement. - *

- * When false querying this bean is based on a supplied sql select statement - * placed in the orm xml file (as opposed to Ebean generated sql). - *

- */ - public boolean isSqlSelectBased() { - return EntityType.SQL.equals(entityType); - } - - /** - * Return true if this an LDAP object. - */ - public boolean isLdapEntityType() { - return EntityType.LDAP.equals(entityType); - } - - /** - * Return the base table. Only properties mapped to the base table are by - * default persisted. - */ - public String getBaseTable() { - return baseTable; - } - - /** - * Get a named extra attribute. - */ - public String getExtraAttribute(String key) { - return (String) extraAttrMap.get(key); - } - - /** - * Return the identity generation type. - */ - public IdType getIdType() { - return idType; - } - - /** - * Return the sequence name. - */ - public String getSequenceName() { - return sequenceName; - } - - /** - * Return the SQL used to return the last inserted id. - *

- * This is only used with Identity columns and getGeneratedKeys is not - * supported. - *

- */ - public String getSelectLastInsertedId() { - return selectLastInsertedId; - } - - /** - * Return the IdGenerator. - */ - public IdGenerator getIdGenerator() { - return idGenerator; - } - - /** - * Return the includes for getReference(). - */ - public String getLazyFetchIncludes() { - return lazyFetchIncludes; - } - - /** - * Return the TableJoins. - *

- * For properties mapped to secondary tables rather than the base table. - *

- */ - public TableJoin[] tableJoins() { - return derivedTableJoins; - } - - /** - * Return an Iterator of all BeanProperty. This includes transient properties. - */ - public Iterator propertiesAll() { - return propMap.values().iterator(); - } - - /** - * Return the BeanProperty that make up the unique id. - *

- * The order of these properties can be relied on to be consistent if the bean - * itself doesn't change or the xml deployment order does not change. - *

- */ - public BeanProperty[] propertiesId() { - return propertiesId; - } - - /** - * Return the non transient non id properties. - */ - public BeanProperty[] propertiesNonTransient() { - return propertiesNonTransient; - } - - /** - * Return the transient properties. - */ - public BeanProperty[] propertiesTransient() { - return propertiesTransient; - } - - /** - * If the Id is a single non-embedded property then returns that, otherwise - * returns null. - */ - public BeanProperty getSingleIdProperty() { - return propertySingleId; - } - - /** - * Return the beans that are embedded. These share the base table with the - * owner bean. - */ - public BeanPropertyAssocOne[] propertiesEmbedded() { - return propertiesEmbedded; - } - - /** - * All the BeanPropertyAssocOne that are not embedded. These are effectively - * joined beans. For ManyToOne and OneToOne associations. - */ - public BeanPropertyAssocOne[] propertiesOne() { - return propertiesOne; - } - - /** - * Returns ManyToOnes and OneToOnes on the imported owning side. - *

- * Excludes OneToOnes on the exported side. - *

- */ - public BeanPropertyAssocOne[] propertiesOneImported() { - return propertiesOneImported; - } - - /** - * Imported Assoc Ones with cascade save true. - */ - public BeanPropertyAssocOne[] propertiesOneImportedSave() { - return propertiesOneImportedSave; - } - - /** - * Imported Assoc Ones with cascade delete true. - */ - public BeanPropertyAssocOne[] propertiesOneImportedDelete() { - return propertiesOneImportedDelete; - } - - /** - * Returns OneToOnes that are on the exported side of a OneToOne. - *

- * These associations do not own the relationship. - *

- */ - public BeanPropertyAssocOne[] propertiesOneExported() { - return propertiesOneExported; - } - - /** - * Exported assoc ones with cascade save. - */ - public BeanPropertyAssocOne[] propertiesOneExportedSave() { - return propertiesOneExportedSave; - } - - /** - * Exported assoc ones with delete cascade. - */ - public BeanPropertyAssocOne[] propertiesOneExportedDelete() { - return propertiesOneExportedDelete; - } - - private Set deriveManyPropNames() { - - LinkedHashSet names = new LinkedHashSet(); - for (int i = 0; i < propertiesMany.length; i++) { - names.add(propertiesMany[i].getName()); - } - - return Collections.unmodifiableSet(names); - } - - /** - * Return a hash of the names of the many properties on this bean type. This - * is used for efficient building of included properties sets for partial - * objects. - */ - public int getNamesOfManyPropsHash() { - return namesOfManyPropsHash; - } - - /** - * Returns the set of many property names for this bean type. - */ - public Set getNamesOfManyProps() { - return namesOfManyProps; - } - - /** - * All Non Assoc Many's for this descriptor. - */ - public BeanProperty[] propertiesNonMany() { - return propertiesNonMany; - } - - /** - * All Assoc Many's for this descriptor. - */ - public BeanPropertyAssocMany[] propertiesMany() { - return propertiesMany; - } - - /** - * Assoc Many's with save cascade. - */ - public BeanPropertyAssocMany[] propertiesManySave() { - return propertiesManySave; - } - - /** - * Assoc Many's with delete cascade. - */ - public BeanPropertyAssocMany[] propertiesManyDelete() { - return propertiesManyDelete; - } - - /** - * Assoc ManyToMany's. - */ - public BeanPropertyAssocMany[] propertiesManyToMany() { - return propertiesManyToMany; - } - - /** - * Return the first version property that exists on the bean. Returns null if - * no version property exists on the bean. - *

- * Note that this DOES NOT find a version property on an embedded bean. - *

- */ - public BeanProperty firstVersionProperty() { - return propertyFirstVersion; - } - - /** - * Return true if this an Insert (rather than Update) on a non-enhanced bean. - */ - public boolean isVanillaInsert(Object bean) { - if (propertyFirstVersion == null) { - return true; - } - Object versionValue = propertyFirstVersion.getValue(bean); - return DmlUtil.isNullOrZero(versionValue); - } - - /** - * Return true if this is an Update (rather than insert) given that the bean - * is involved in a stateless update. - */ - public boolean isStatelessUpdate(Object bean) { - if (propertyFirstVersion == null) { - Object versionValue = getId(bean); - return !DmlUtil.isNullOrZero(versionValue); - } else { - Object versionValue = propertyFirstVersion.getValue(bean); - return !DmlUtil.isNullOrZero(versionValue); - } - } - - /** - * Returns 'Version' properties on this bean. These are 'Counter' or 'Update - * Timestamp' type properties. Note version properties can also be on embedded - * beans rather than on the bean itself. - */ - public BeanProperty[] propertiesVersion() { - return propertiesVersion; - } - - /** - * Scalar properties without the unique id or secondary table properties. - */ - public BeanProperty[] propertiesBaseScalar() { - return propertiesBaseScalar; - } - - /** - * Return properties that are immutable compound value objects. - *

- * These are compound types but are not enhanced (Embedded are enhanced). - *

- */ - public BeanPropertyCompound[] propertiesBaseCompound() { - return propertiesBaseCompound; - } - - /** - * Return the properties local to this type for inheritance. - */ - public BeanProperty[] propertiesLocal() { - return propertiesLocal; - } - - public void jsonWrite(WriteJsonContext ctx, Object bean) { - - if (bean != null) { - - ctx.appendObjectBegin(); - WriteBeanState prevState = ctx.pushBeanState(bean); - - if (inheritInfo != null) { - InheritInfo localInheritInfo = inheritInfo.readType(bean.getClass()); - String discValue = localInheritInfo.getDiscriminatorStringValue(); - String discColumn = localInheritInfo.getDiscriminatorColumn(); - ctx.appendDiscriminator(discColumn, discValue); - - BeanDescriptor localDescriptor = localInheritInfo.getBeanDescriptor(); - localDescriptor.jsonWriteProperties(ctx, bean); - - } else { - jsonWriteProperties(ctx, bean); - } - - ctx.pushPreviousState(prevState); - ctx.appendObjectEnd(); - } - } - - @SuppressWarnings("unchecked") - private void jsonWriteProperties(WriteJsonContext ctx, Object bean) { - - boolean referenceBean = ctx.isReferenceBean(); - - JsonWriteBeanVisitor beanVisitor = (JsonWriteBeanVisitor) ctx.getBeanVisitor(); - - Set props = ctx.getIncludeProperties(); - - boolean explicitAllProps; - if (props == null) { - explicitAllProps = false; - } else { - explicitAllProps = props.contains("*"); - if (explicitAllProps || props.isEmpty()) { - props = null; - } - } - - for (int i = 0; i < propertiesId.length; i++) { - Object idValue = propertiesId[i].getValue(bean); - if (idValue != null) { - if (props == null || props.contains(propertiesId[i].getName())) { - propertiesId[i].jsonWrite(ctx, bean); - } - } - } - - if (!explicitAllProps && props == null) { - // just render the loaded properties - props = ctx.getLoadedProps(); - } - if (props != null) { - // render only the appropriate properties (when not all properties) - for (String prop : props) { - BeanProperty p = getBeanProperty(prop); - if (p != null && !p.isId()) { - p.jsonWrite(ctx, bean); - } - } - } else { - if (explicitAllProps || !referenceBean) { - // render all the properties and invoke lazy loading if required - for (int j = 0; j < propertiesNonTransient.length; j++) { - propertiesNonTransient[j].jsonWrite(ctx, bean); - } - } - } - - if (beanVisitor != null) { - beanVisitor.visit((T) bean, ctx); - } - } - - @SuppressWarnings("unchecked") - public T jsonReadBean(ReadJsonContext ctx, String path) { - ReadBeanState beanState = jsonRead(ctx, path); - if (beanState == null) { - return null; - } else { - beanState.setLoadedState(); - return (T) beanState.getBean(); - } - } - - public ReadBeanState jsonRead(ReadJsonContext ctx, String path) { - if (!ctx.readObjectBegin()) { - // the object is null - return null; - } - - if (inheritInfo == null) { - return jsonReadObject(ctx, path); - - } else { - // read the discriminator value to determine the correct sub type - String discColumn = inheritInfo.getRoot().getDiscriminatorColumn(); - - if (!ctx.readKeyNext()) { - String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?"; - throw new TextException(msg); - } - String propName = ctx.getTokenKey(); - - if (!propName.equalsIgnoreCase(discColumn)) { - String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but read [" + propName + "]"; - throw new TextException(msg); - } - - String discValue = ctx.readScalarValue(); - if (!ctx.readValueNext()) { - String msg = "Error reading inheritance discriminator [" + discColumn + "]. Expected more json name values?"; - throw new TextException(msg); - } - - // determine the sub type for this particular json object - InheritInfo localInheritInfo = inheritInfo.readType(discValue); - BeanDescriptor localDescriptor = localInheritInfo.getBeanDescriptor(); - return localDescriptor.jsonReadObject(ctx, path); - } - } - - @SuppressWarnings("unchecked") - private ReadBeanState jsonReadObject(ReadJsonContext ctx, String path) { - - T bean = (T) createEntityBean(); - ctx.pushBean(bean, path, this); - - do { - if (!ctx.readKeyNext()) { - break; - } else { - // we read a property key ... - String propName = ctx.getTokenKey(); - BeanProperty p = getBeanProperty(propName); - if (p != null) { - p.jsonRead(ctx, bean); - ctx.setProperty(propName); - } else { - // unknown property key ... - ctx.readUnmappedJson(propName); - } - - if (!ctx.readValueNext()) { - break; - } - } - } while (true); - - return ctx.popBeanState(); - } - - /** - * Set the loaded properties with additional check to see if the bean is a - * reference. - */ - public void setLoadedProps(EntityBeanIntercept ebi, Set loadedProps) { - if (isLoadedReference(loadedProps)) { - ebi.setReference(); - } else { - ebi.setLoadedProps(loadedProps); - } - } - - /** - * Return true if the loadedProperties is just the Id property and therefore - * this is really a reference. - */ - public boolean isLoadedReference(Set loadedProps) { - - if (loadedProps != null) { - if (loadedProps.size() == propertiesId.length) { - for (int i = 0; i < propertiesId.length; i++) { - if (!loadedProps.contains(propertiesId[i].getName())) { - return false; - } - } - return true; - } - } - - return false; - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.naming.InvalidNameException; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.ldap.LdapName; +import javax.persistence.PersistenceException; + +import com.avaje.ebean.InvalidValue; +import com.avaje.ebean.Query; +import com.avaje.ebean.Query.UseIndex; +import com.avaje.ebean.SqlUpdate; +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.PersistenceContext; +import com.avaje.ebean.cache.ServerCache; +import com.avaje.ebean.cache.ServerCacheManager; +import com.avaje.ebean.config.EncryptKey; +import com.avaje.ebean.config.dbplatform.IdGenerator; +import com.avaje.ebean.config.dbplatform.IdType; +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.text.TextException; +import com.avaje.ebean.text.json.JsonWriteBeanVisitor; +import com.avaje.ebean.validation.factory.Validator; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.api.SpiUpdatePlan; +import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD; +import com.avaje.ebeaninternal.server.cache.CachedBeanData; +import com.avaje.ebeaninternal.server.cache.CachedBeanDataFromBean; +import com.avaje.ebeaninternal.server.cache.CachedBeanDataToBean; +import com.avaje.ebeaninternal.server.cache.CachedBeanDataUpdate; +import com.avaje.ebeaninternal.server.cache.CachedManyIds; +import com.avaje.ebeaninternal.server.core.CacheOptions; +import com.avaje.ebeaninternal.server.core.ConcurrencyMode; +import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.id.IdBinder; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyLists; +import com.avaje.ebeaninternal.server.el.ElComparator; +import com.avaje.ebeaninternal.server.el.ElComparatorCompound; +import com.avaje.ebeaninternal.server.el.ElComparatorProperty; +import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; +import com.avaje.ebeaninternal.server.el.ElPropertyDeploy; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.ldap.LdapPersistenceException; +import com.avaje.ebeaninternal.server.persist.DmlUtil; +import com.avaje.ebeaninternal.server.query.CQueryPlan; +import com.avaje.ebeaninternal.server.query.SplitName; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; +import com.avaje.ebeaninternal.server.reflect.BeanReflect; +import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; +import com.avaje.ebeaninternal.server.text.json.ReadJsonContext.ReadBeanState; +import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJsonContext.WriteBeanState; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.type.TypeManager; +import com.avaje.ebeaninternal.util.SortByClause; +import com.avaje.ebeaninternal.util.SortByClause.Property; +import com.avaje.ebeaninternal.util.SortByClauseParser; + +/** + * Describes Beans including their deployment information. + */ +public class BeanDescriptor { + + private static final Logger logger = Logger.getLogger(BeanDescriptor.class.getName()); + + private final ConcurrentHashMap updatePlanCache = new ConcurrentHashMap(); + + private final ConcurrentHashMap queryPlanCache = new ConcurrentHashMap(); + + private final ConcurrentHashMap elGetCache = new ConcurrentHashMap(); + + private final ConcurrentHashMap> comparatorCache = new ConcurrentHashMap>(); + + private final ConcurrentHashMap fkeyMap = new ConcurrentHashMap(); + + public enum EntityType { + ORM, EMBEDDED, SQL, META, LDAP, XMLELEMENT + } + + /** + * The EbeanServer name. Same as the plugin name. + */ + private final String serverName; + + /** + * Set to true if this is a LDAP domain object. + */ + private final EntityType entityType; + + /** + * Type of Identity generation strategy used. + */ + private final IdType idType; + + private final IdGenerator idGenerator; + + /** + * The database sequence name (optional). + */ + private final String sequenceName; + + private final String ldapBaseDn; + private final String[] ldapObjectclasses; + + /** + * SQL used to return last inserted id. Used for Identity columns where + * getGeneratedKeys is not supported. + */ + private final String selectLastInsertedId; + + private final boolean autoFetchTunable; + + /** + * Flag indicating this bean has no relationships. + */ + private final boolean cacheSharableBeans; + + private final String lazyFetchIncludes; + + /** + * The concurrency mode for beans of this type. + */ + private final ConcurrencyMode concurrencyMode; + + /** + * The tables this bean is dependent on. + */ + private final String[] dependantTables; + + private final CompoundUniqueContraint[] compoundUniqueConstraints; + + /** + * Extra deployment attributes. + */ + private final Map extraAttrMap; + + /** + * The base database table. + */ + private final String baseTable; + + /** + * Used to provide mechanism to new EntityBean instances. Generated code + * faster than reflection at this stage. + */ + private final BeanReflect beanReflect; + + /** + * Map of BeanProperty Linked so as to preserve order. + */ + private final LinkedHashMap propMap; + private final LinkedHashMap propMapByDbColumn; + + /** + * The type of bean this describes. + */ + private final Class beanType; + + /** + * This is not sent to a remote client. + */ + private final BeanDescriptorMap owner; + + /** + * The EntityBean type used to create new EntityBeans. + */ + private final Class factoryType; + + private final boolean enhancedBean; + + /** + * Intercept pre post on insert,update,delete and postLoad(). Server side + * only. + */ + private volatile BeanPersistController persistController; + + /** + * Listens for post commit insert update and delete events. + */ + private volatile BeanPersistListener persistListener; + + private volatile BeanQueryAdapter queryAdapter; + + /** + * If set overrides the find implementation. Server side only. + */ + private final BeanFinder beanFinder; + + /** + * The table joins for this bean. + */ + private final TableJoin[] derivedTableJoins; + + /** + * Inheritance information. Server side only. + */ + private final InheritInfo inheritInfo; + + /** + * Derived list of properties that make up the unique id. + */ + private final BeanProperty[] propertiesId; + + /** + * Derived list of properties that are used for version concurrency checking. + */ + private final BeanProperty[] propertiesVersion; + private final BeanProperty propertiesNaturalKey; + + /** + * Properties local to this type (not from a super type). + */ + private final BeanProperty[] propertiesLocal; + + private final BeanPropertyAssocOne unidirectional; + + /** + * A hashcode of all the many property names. This is used to efficiently + * create sets of loaded property names (for partial objects). + */ + private final int namesOfManyPropsHash; + + /** + * The set of names of the many properties. + */ + private final Set namesOfManyProps; + + /** + * list of properties that are Lists/Sets/Maps (Derived). + */ + private final BeanProperty[] propertiesNonMany; + private final BeanPropertyAssocMany[] propertiesMany; + private final BeanPropertyAssocMany[] propertiesManySave; + private final BeanPropertyAssocMany[] propertiesManyDelete; + private final BeanPropertyAssocMany[] propertiesManyToMany; + + /** + * list of properties that are associated beans and not embedded (Derived). + */ + private final BeanPropertyAssocOne[] propertiesOne; + + private final BeanPropertyAssocOne[] propertiesOneImported; + private final BeanPropertyAssocOne[] propertiesOneImportedSave; + private final BeanPropertyAssocOne[] propertiesOneImportedDelete; + + private final BeanPropertyAssocOne[] propertiesOneExported; + private final BeanPropertyAssocOne[] propertiesOneExportedSave; + private final BeanPropertyAssocOne[] propertiesOneExportedDelete; + + /** + * list of properties that are embedded beans. + */ + private final BeanPropertyAssocOne[] propertiesEmbedded; + + /** + * List of the scalar properties excluding id and secondary table properties. + */ + private final BeanProperty[] propertiesBaseScalar; + private final BeanPropertyCompound[] propertiesBaseCompound; + + private final BeanProperty[] propertiesTransient; + + /** + * All non transient properties excluding the id properties. + */ + final BeanProperty[] propertiesNonTransient; + + /** + * Set to true if the bean has version properties or an embedded bean has + * version properties. + */ + private final BeanProperty propertyFirstVersion; + + /** + * Set when the Id property is a single non-embedded property. Can make life + * simpler for this case. + */ + private final BeanProperty propertySingleId; + + /** + * The bean class name or the table name for MapBeans. + */ + private final String fullName; + + private final Map namedQueries; + + private final Map namedUpdates; + + /** + * Has local validation rules. + */ + private final boolean hasLocalValidation; + + /** + * Has local or recursive validation rules. + */ + private final boolean hasCascadeValidation; + + /** + * Properties with local validation rules. + */ + private final BeanProperty[] propertiesValidationLocal; + + /** + * Properties with local or cascade validation rules. + */ + private final BeanProperty[] propertiesValidationCascade; + + private final Validator[] beanValidators; + + /** + * Flag used to determine if saves can be skipped. + */ + private boolean saveRecurseSkippable; + + /** + * Flag used to determine if deletes can be skipped. + */ + private boolean deleteRecurseSkippable; + + /** + * Make the TypeManager available for helping SqlSelect. + */ + private final TypeManager typeManager; + + private final IdBinder idBinder; + + private String idBinderInLHSSql; + + private String idBinderIdSql; + + private String deleteByIdSql; + + private String deleteByIdInSql; + + private final String name; + + private final String baseTableAlias; + + /** + * If true then only changed properties get updated. + */ + private final boolean updateChangesOnly; + + private final ServerCacheManager cacheManager; + + private final CacheOptions cacheOptions; + + private final String defaultSelectClause; + private final Set defaultSelectClauseSet; + private final String[] defaultSelectDbArray; + + private final String descriptorId; + + private final UseIndex useIndex; + + private SpiEbeanServer ebeanServer; + + private ServerCache beanCache; + private ServerCache naturalKeyCache; + private ServerCache queryCache; + + /** + * Construct the BeanDescriptor. + */ + public BeanDescriptor(BeanDescriptorMap owner, TypeManager typeManager, DeployBeanDescriptor deploy, String descriptorId) { + + this.owner = owner; + this.cacheManager = owner.getCacheManager(); + this.serverName = owner.getServerName(); + this.entityType = deploy.getEntityType(); + this.name = InternString.intern(deploy.getName()); + this.baseTableAlias = "t0"; + this.fullName = InternString.intern(deploy.getFullName()); + this.descriptorId = descriptorId; + + this.useIndex = deploy.getUseIndex(); + this.typeManager = typeManager; + this.beanType = deploy.getBeanType(); + this.factoryType = deploy.getFactoryType(); + this.enhancedBean = beanType.equals(factoryType); + this.namedQueries = deploy.getNamedQueries(); + this.namedUpdates = deploy.getNamedUpdates(); + + this.inheritInfo = deploy.getInheritInfo(); + + this.beanFinder = deploy.getBeanFinder(); + this.persistController = deploy.getPersistController(); + this.persistListener = deploy.getPersistListener(); + this.queryAdapter = deploy.getQueryAdapter(); + this.cacheOptions = deploy.getCacheOptions(); + + this.defaultSelectClause = deploy.getDefaultSelectClause(); + this.defaultSelectClauseSet = deploy.parseDefaultSelectClause(defaultSelectClause); + this.defaultSelectDbArray = deploy.getDefaultSelectDbArray(defaultSelectClauseSet); + + this.idType = deploy.getIdType(); + this.idGenerator = deploy.getIdGenerator(); + this.ldapBaseDn = deploy.getLdapBaseDn(); + this.ldapObjectclasses = deploy.getLdapObjectclasses(); + this.sequenceName = deploy.getSequenceName(); + this.selectLastInsertedId = deploy.getSelectLastInsertedId(); + this.lazyFetchIncludes = InternString.intern(deploy.getLazyFetchIncludes()); + this.concurrencyMode = deploy.getConcurrencyMode(); + this.updateChangesOnly = deploy.isUpdateChangesOnly(); + + this.dependantTables = deploy.getDependantTables(); + this.compoundUniqueConstraints = deploy.getCompoundUniqueConstraints(); + + this.extraAttrMap = deploy.getExtraAttributeMap(); + + this.baseTable = InternString.intern(deploy.getBaseTable()); + + this.beanReflect = deploy.getBeanReflect(); + + this.autoFetchTunable = EntityType.ORM.equals(entityType) && (beanFinder == null); + + // helper object used to derive lists of properties + DeployBeanPropertyLists listHelper = new DeployBeanPropertyLists(owner, this, deploy); + + this.propMap = listHelper.getPropertyMap(); + this.propMapByDbColumn = getReverseMap(propMap); + this.propertiesTransient = listHelper.getTransients(); + this.propertiesNonTransient = listHelper.getNonTransients(); + this.propertiesBaseScalar = listHelper.getBaseScalar(); + this.propertiesBaseCompound = listHelper.getBaseCompound(); + this.propertiesId = listHelper.getId(); + this.propertiesNaturalKey = listHelper.getNaturalKey(); + this.propertiesVersion = listHelper.getVersion(); + this.propertiesEmbedded = listHelper.getEmbedded(); + this.propertiesLocal = listHelper.getLocal(); + this.unidirectional = listHelper.getUnidirectional(); + this.propertiesOne = listHelper.getOnes(); + this.propertiesOneExported = listHelper.getOneExported(); + this.propertiesOneExportedSave = listHelper.getOneExportedSave(); + this.propertiesOneExportedDelete = listHelper.getOneExportedDelete(); + this.propertiesOneImported = listHelper.getOneImported(); + this.propertiesOneImportedSave = listHelper.getOneImportedSave(); + this.propertiesOneImportedDelete = listHelper.getOneImportedDelete(); + + this.propertiesMany = listHelper.getMany(); + this.propertiesNonMany = listHelper.getNonMany(); + this.propertiesManySave = listHelper.getManySave(); + this.propertiesManyDelete = listHelper.getManyDelete(); + this.propertiesManyToMany = listHelper.getManyToMany(); + boolean noRelationships = propertiesOne.length + propertiesMany.length == 0; + this.cacheSharableBeans = noRelationships && cacheOptions.isReadOnly(); + + this.namesOfManyProps = deriveManyPropNames(); + this.namesOfManyPropsHash = namesOfManyProps.hashCode(); + + this.derivedTableJoins = listHelper.getTableJoin(); + this.propertyFirstVersion = listHelper.getFirstVersion(); + + if (propertiesId.length == 1) { + this.propertySingleId = propertiesId[0]; + } else { + this.propertySingleId = null; + } + + // Check if there are no cascade save associated beans ( subject to change + // in initialiseOther()). Note that if we are in an inheritance hierarchy + // then we also need to check every BeanDescriptors in the InheritInfo as + // well. We do that later in initialiseOther(). + + saveRecurseSkippable = (0 == (propertiesOneExportedSave.length + propertiesOneImportedSave.length + propertiesManySave.length)); + + // Check if there are no cascade delete associated beans (also subject to + // change in initialiseOther()). + deleteRecurseSkippable = (0 == (propertiesOneExportedDelete.length + propertiesOneImportedDelete.length + propertiesManyDelete.length)); + + this.propertiesValidationLocal = listHelper.getPropertiesWithValidators(false); + this.propertiesValidationCascade = listHelper.getPropertiesWithValidators(true); + this.beanValidators = listHelper.getBeanValidators(); + this.hasLocalValidation = (propertiesValidationLocal.length > 0 || beanValidators.length > 0); + this.hasCascadeValidation = (propertiesValidationCascade.length > 0 || beanValidators.length > 0); + + // object used to handle Id values + this.idBinder = owner.createIdBinder(propertiesId); + } + + private LinkedHashMap getReverseMap(LinkedHashMap propMap) { + + LinkedHashMap revMap = new LinkedHashMap(propMap.size() * 2); + + for (BeanProperty prop : propMap.values()) { + if (prop.getDbColumn() != null) { + revMap.put(prop.getDbColumn(), prop); + } + } + + return revMap; + } + + /** + * Set the server. Primarily so that the Many's can lazy load. + */ + public void setEbeanServer(SpiEbeanServer ebeanServer) { + this.ebeanServer = ebeanServer; + for (int i = 0; i < propertiesMany.length; i++) { + // used for creating lazy loading lists etc + propertiesMany[i].setLoader(ebeanServer); + } + } + + /** + * Determine the concurrency mode based on the existence of a non-null version + * property value. + */ + public ConcurrencyMode determineConcurrencyMode(Object bean) { + + if (propertyFirstVersion == null) { + return ConcurrencyMode.NONE; + } + Object v = propertyFirstVersion.getValue(bean); + return (v == null) ? ConcurrencyMode.NONE : ConcurrencyMode.VERSION; + } + + /** + * Return the Set of embedded beans that have changed. + */ + public Set getDirtyEmbeddedProperties(Object bean) { + + HashSet dirtyProperties = null; + + for (int i = 0; i < propertiesEmbedded.length; i++) { + Object embValue = propertiesEmbedded[i].getValue(bean); + if (embValue instanceof EntityBean) { + if (((EntityBean) embValue)._ebean_getIntercept().isDirty()) { + // this embedded is dirty so should be included in an update + if (dirtyProperties == null) { + dirtyProperties = new HashSet(); + } + dirtyProperties.add(propertiesEmbedded[i].getName()); + } + } else { + // must assume it is dirty + if (dirtyProperties == null) { + dirtyProperties = new HashSet(); + } + dirtyProperties.add(propertiesEmbedded[i].getName()); + } + } + + return dirtyProperties; + } + + /** + * Determine the non-null properties of the bean. + */ + public Set determineLoadedProperties(Object bean) { + + HashSet nonNullProps = new HashSet(); + + for (int j = 0; j < propertiesId.length; j++) { + if (propertiesId[j].getValue(bean) != null) { + nonNullProps.add(propertiesId[j].getName()); + } + } + for (int i = 0; i < propertiesNonTransient.length; i++) { + if (propertiesNonTransient[i].getValue(bean) != null) { + nonNullProps.add(propertiesNonTransient[i].getName()); + } + } + return nonNullProps; + } + + /** + * Return the EbeanServer instance that owns this BeanDescriptor. + */ + public SpiEbeanServer getEbeanServer() { + return ebeanServer; + } + + /** + * Return the type of this domain object. + */ + public EntityType getEntityType() { + return entityType; + } + + /** + * Return the default strategy for using a lucene index (if an index is + * defined on this bean type). + */ + public UseIndex getUseIndex() { + return useIndex; + } + + /** + * Initialise the Id properties first. + *

+ * These properties need to be initialised prior to the association properties + * as they are used to get the imported and exported properties. + *

+ */ + public void initialiseId() { + + if (logger.isLoggable(Level.FINER)) { + logger.finer("BeanDescriptor initialise " + fullName); + } + + if (inheritInfo != null) { + inheritInfo.setDescriptor(this); + } + + if (isEmbedded()) { + // initialise all the properties + Iterator it = propertiesAll(); + while (it.hasNext()) { + BeanProperty prop = it.next(); + prop.initialise(); + } + } else { + // initialise just the Id properties + BeanProperty[] idProps = propertiesId(); + for (int i = 0; i < idProps.length; i++) { + idProps[i].initialise(); + } + } + } + + /** + * Initialise the exported and imported parts for associated properties. + */ + public void initialiseOther() { + + if (!isEmbedded()) { + // initialise all the non-id properties + Iterator it = propertiesAll(); + while (it.hasNext()) { + BeanProperty prop = it.next(); + if (!prop.isId()) { + prop.initialise(); + } + } + } + + if (unidirectional != null) { + unidirectional.initialise(); + } + + idBinder.initialise(); + idBinderInLHSSql = idBinder.getBindIdInSql(baseTableAlias); + idBinderIdSql = idBinder.getBindIdSql(baseTableAlias); + String idBinderInLHSSqlNoAlias = idBinder.getBindIdInSql(null); + String idEqualsSql = idBinder.getBindIdSql(null); + + deleteByIdSql = "delete from " + baseTable + " where " + idEqualsSql; + deleteByIdInSql = "delete from " + baseTable + " where " + idBinderInLHSSqlNoAlias + " "; + + if (!isEmbedded()) { + // parse every named update up front into sql dml + for (DeployNamedUpdate namedUpdate : namedUpdates.values()) { + DeployUpdateParser parser = new DeployUpdateParser(this); + namedUpdate.initialise(parser); + } + } + + } + + public void initInheritInfo() { + if (inheritInfo != null) { + // need to check every BeanDescriptor in the inheritance hierarchy + if (saveRecurseSkippable) { + saveRecurseSkippable = inheritInfo.isSaveRecurseSkippable(); + } + if (deleteRecurseSkippable) { + deleteRecurseSkippable = inheritInfo.isDeleteRecurseSkippable(); + } + } + } + + /** + * Initialise the cache once the server has started. + */ + public void cacheInitialise() { + if (cacheOptions.isUseNaturalKeyCache()) { + this.naturalKeyCache = cacheManager.getNaturalKeyCache(beanType); + } + if (cacheOptions.isUseCache()) { + this.beanCache = cacheManager.getBeanCache(beanType); + } + } + + protected boolean hasInheritance() { + return inheritInfo != null; + } + + protected boolean isDynamicSubclass() { + return !beanType.equals(factoryType); + } + + /** + * Set the LDAP objectClasses to the attributes. + */ + public void setLdapObjectClasses(Attributes attributes) { + + if (ldapObjectclasses != null) { + BasicAttribute ocAttrs = new BasicAttribute("objectclass"); + for (int i = 0; i < ldapObjectclasses.length; i++) { + ocAttrs.add(ldapObjectclasses[i]); + } + attributes.put(ocAttrs); + } + } + + /** + * Creates Attributes with the objectclass. + */ + public Attributes createAttributes() { + + Attributes attrs = new BasicAttributes(true); + setLdapObjectClasses(attrs); + return attrs; + } + + public String getLdapBaseDn() { + return ldapBaseDn; + } + + public LdapName createLdapNameById(Object id) throws InvalidNameException { + + LdapName baseDn = new LdapName(ldapBaseDn); + idBinder.createLdapNameById(baseDn, id); + return baseDn; + } + + public LdapName createLdapName(Object bean) { + + try { + LdapName name = new LdapName(ldapBaseDn); + if (bean != null) { + idBinder.createLdapNameByBean(name, bean); + } + return name; + + } catch (InvalidNameException e) { + throw new LdapPersistenceException(e); + } + } + + public SqlUpdate deleteById(Object id, List idList) { + if (id != null) { + return deleteById(id); + } else { + return deleteByIdList(idList); + } + } + + /** + * Return SQL that can be used to delete a list of Id's without any optimistic + * concurrency checking. + */ + private SqlUpdate deleteByIdList(List idList) { + + StringBuilder sb = new StringBuilder(deleteByIdInSql); + String inClause = idBinder.getIdInValueExprDelete(idList.size()); + sb.append(inClause); + + DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString()); + for (int i = 0; i < idList.size(); i++) { + idBinder.bindId(delete, idList.get(i)); + } + return delete; + } + + /** + * Return SQL that can be used to delete by Id without any optimistic + * concurrency checking. + */ + private SqlUpdate deleteById(Object id) { + + DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByIdSql); + + Object[] bindValues = idBinder.getBindValues(id); + for (int i = 0; i < bindValues.length; i++) { + sqlDelete.addParameter(bindValues[i]); + } + + return sqlDelete; + } + + /** + * Add objects to ElPropertyDeploy etc. These are used so that expressions on + * foreign keys don't require an extra join. + */ + public void add(BeanFkeyProperty fkey) { + fkeyMap.put(fkey.getName(), fkey); + } + + public void initialiseFkeys() { + for (int i = 0; i < propertiesOneImported.length; i++) { + propertiesOneImported[i].addFkey(); + } + } + + public boolean calculateUseCache(Boolean queryUseCache) { + return (queryUseCache != null) ? queryUseCache.booleanValue() : isBeanCaching(); + } + + public boolean calculateUseNaturalKeyCache(Boolean queryUseCache) { + return (queryUseCache != null) ? queryUseCache.booleanValue() : isBeanCaching(); + } + + /** + * Return the cache options. + */ + public CacheOptions getCacheOptions() { + return cacheOptions; + } + + /** + * Return the Encrypt key given the BeanProperty. + */ + public EncryptKey getEncryptKey(BeanProperty p) { + return owner.getEncryptKey(baseTable, p.getDbColumn()); + } + + /** + * Return the Encrypt key given the table and column name. + */ + public EncryptKey getEncryptKey(String tableName, String columnName) { + return owner.getEncryptKey(tableName, columnName); + } + + /** + * Execute the warming cache query (if defined) and load the cache. + */ + public void runCacheWarming() { + if (cacheOptions == null) { + return; + } + String warmingQuery = cacheOptions.getWarmingQuery(); + if (warmingQuery != null && warmingQuery.trim().length() > 0) { + Query query = ebeanServer.createQuery(beanType, warmingQuery); + query.setUseCache(true); + query.setReadOnly(true); + query.setLoadBeanCache(true); + List list = query.findList(); + if (logger.isLoggable(Level.INFO)) { + String msg = "Loaded " + beanType + " cache with [" + list.size() + "] beans"; + logger.info(msg); + } + } + } + + /** + * Return true if this bean type has a default select clause that is not + * simply select all properties. + */ + public boolean hasDefaultSelectClause() { + return defaultSelectClause != null; + } + + /** + * Return the default select clause. + */ + public String getDefaultSelectClause() { + return defaultSelectClause; + } + + /** + * Return the default select clause already parsed into an ordered Set. + */ + public Set getDefaultSelectClauseSet() { + return defaultSelectClauseSet; + } + + /** + * For LDAP return array of (DB) attributes to include in query by default. + */ + public String[] getDefaultSelectDbArray() { + return defaultSelectDbArray; + } + + /** + * Return true if this object is the root level object in its entity + * inheritance. + */ + public boolean isInheritanceRoot() { + return inheritInfo == null || inheritInfo.isRoot(); + } + + /** + * Return true if there is currently query caching for this type of bean. + */ + public boolean isQueryCaching() { + return queryCache != null; + } + + /** + * Return true if there is currently bean caching for this type of bean. + */ + public boolean isBeanCaching() { + return beanCache != null; + } + + public boolean cacheIsUseManyId() { + return isBeanCaching(); + } + + /** + * Return true if the persist request needs to notify the cache. + */ + public boolean isCacheNotify() { + + if (isBeanCaching() || isQueryCaching()) { + return true; + } + for (int i = 0; i < propertiesOneImported.length; i++) { + if (propertiesOneImported[i].getTargetDescriptor().isBeanCaching()) { + return true; + } + } + return false; + } + + /** + * Return true if there is L2 caching (Lucene or Bean cache) for this bean + * type. + */ + public boolean isUsingL2Cache() { + return isBeanCaching(); + } + + /** + * Invalidate parts of cache due to SqlUpdate or external modification etc. + */ + public void cacheNotify(TableIUD tableIUD) { + // inserts don't invalidate the bean cache + if (tableIUD.isUpdateOrDelete()) { + cacheClear(); + } + // any change invalidates the query cache + queryCacheClear(); + } + + /** + * Clear the query cache. + */ + public void queryCacheClear() { + if (queryCache != null) { + queryCache.clear(); + } + } + + /** + * Get a query result from the query cache. + */ + @SuppressWarnings("unchecked") + public BeanCollection queryCacheGet(Object id) { + if (queryCache == null) { + return null; + } else { + return (BeanCollection) queryCache.get(id); + } + } + + /** + * Put a query result into the query cache. + */ + public void queryCachePut(Object id, BeanCollection query) { + if (queryCache == null) { + queryCache = cacheManager.getQueryCache(beanType); + } + queryCache.put(id, query); + } + + private ServerCache getBeanCache() { + if (beanCache == null) { + beanCache = cacheManager.getBeanCache(beanType); + } + return beanCache; + } + + /** + * Clear the bean cache. + */ + public void cacheClear() { + if (beanCache != null) { + beanCache.clear(); + } + } + + /** + * Put a bean into the bean cache. + */ + public void cachePutBeanData(Object bean) { + + CachedBeanData beanData = CachedBeanDataFromBean.extract(this, bean); + + Object id = getId(bean); + getBeanCache().put(id, beanData); + if (beanData.isNaturalKeyUpdate() && naturalKeyCache != null) { + Object naturalKey = beanData.getNaturalKey(); + if (naturalKey != null) { + naturalKeyCache.put(naturalKey, id); + } + } + } + + public boolean cacheLoadMany(BeanPropertyAssocMany many, BeanCollection bc, Object parentId, Boolean readOnly, boolean vanilla) { + + CachedManyIds ids = cacheGetCachedManyIds(parentId, many.getName()); + if (ids == null) { + return false; + } + + Object ownerBean = bc.getOwnerBean(); + EntityBeanIntercept ebi = ((EntityBean) ownerBean)._ebean_getIntercept(); + PersistenceContext persistenceContext = ebi.getPersistenceContext(); + + BeanDescriptor targetDescriptor = many.getTargetDescriptor(); + + List idList = ids.getIdList(); + bc.checkEmptyLazyLoad(); + for (int i = 0; i < idList.size(); i++) { + Object id = idList.get(i); + Object refBean = targetDescriptor.createReference(vanilla, readOnly, id, null); + EntityBeanIntercept refEbi = ((EntityBean) refBean)._ebean_getIntercept(); + + many.add(bc, refBean); + persistenceContext.put(id, refBean); + refEbi.setPersistenceContext(persistenceContext); + } + return true; + } + + public void cachePutMany(BeanPropertyAssocMany many, BeanCollection bc, Object parentId) { + BeanDescriptor targetDescriptor = many.getTargetDescriptor(); + Collection actualDetails = bc.getActualDetails(); + ArrayList idList = new ArrayList(); + for (Object bean : actualDetails) { + Object id = targetDescriptor.getId(bean); + idList.add(id); + } + CachedManyIds ids = new CachedManyIds(idList); + cachePutCachedManyIds(parentId, many.getName(), ids); + } + + public void cacheRemoveCachedManyIds(Object parentId, String propertyName) { + ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); + collectionIdsCache.remove(parentId); + } + + public void cacheClearCachedManyIds(String propertyName) { + ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); + collectionIdsCache.clear(); + } + + public CachedManyIds cacheGetCachedManyIds(Object parentId, String propertyName) { + ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); + return (CachedManyIds) collectionIdsCache.get(parentId); + } + + public void cachePutCachedManyIds(Object parentId, String propertyName, CachedManyIds ids) { + ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName); + collectionIdsCache.put(parentId, ids); + } + + /** + * Return a bean from the bean cache. + */ + @SuppressWarnings("unchecked") + public T cacheGetBean(Object id, boolean vanilla, Boolean readOnly) { + + CachedBeanData d = (CachedBeanData) getBeanCache().get(id); + if (d == null) { + return null; + } + if (cacheSharableBeans && !vanilla && !Boolean.FALSE.equals(readOnly)) { + Object bean = d.getSharableBean(); + if (bean != null) { + return (T) bean; + } + } + + T bean = (T) createBean(vanilla); + convertSetId(id, bean); + if (!vanilla && Boolean.TRUE.equals(readOnly)) { + ((EntityBean) bean)._ebean_getIntercept().setReadOnly(true); + } + + CachedBeanDataToBean.load(this, bean, d); + return bean; + } + + public boolean cacheIsNaturalKey(String propName) { + return propName != null && propName.equals(cacheOptions.getNaturalKey()); + } + + public Object cacheGetNaturalKeyId(Object uniqueKeyValue) { + if (naturalKeyCache != null) { + return naturalKeyCache.get(uniqueKeyValue); + } + return null; + } + + /** + * Remove a bean from the cache given its Id. + */ + public void cacheRemove(Object id) { + if (beanCache != null) { + beanCache.remove(id); + } + for (int i = 0; i < propertiesOneImported.length; i++) { + propertiesOneImported[i].cacheClear(); + } + } + + /** + * Remove a bean from the cache given its Id. + */ + public void cacheDelete(Object id, PersistRequestBean deleteRequest) { + if (beanCache != null) { + beanCache.remove(id); + } + for (int i = 0; i < propertiesOneImported.length; i++) { + BeanPropertyAssocMany many = propertiesOneImported[i].getRelationshipProperty(); + if (many != null) { + propertiesOneImported[i].cacheDelete(true, deleteRequest); + } + } + } + + public void cacheInsert(Object id, PersistRequestBean insertRequest) { + if (queryCache != null) { + queryCache.clear(); + } + for (int i = 0; i < propertiesOneImported.length; i++) { + propertiesOneImported[i].cacheDelete(false, insertRequest.getBean()); + } + } + + /** + * Update the cached bean data. + */ + public void cacheUpdate(Object id, PersistRequestBean updateRequest) { + + ServerCache cache = getBeanCache(); + CachedBeanData cd = (CachedBeanData) cache.get(id); + if (cd != null) { + CachedBeanData newCd = CachedBeanDataUpdate.update(this, cd, updateRequest); + cache.put(id, newCd); + if (newCd.isNaturalKeyUpdate() && naturalKeyCache != null) { + Object oldKey = propertiesNaturalKey.getValue(updateRequest.getOldValues()); + Object newKey = propertiesNaturalKey.getValue(updateRequest.getBean()); + if (oldKey != null) { + naturalKeyCache.remove(oldKey); + } + if (newKey != null) { + naturalKeyCache.put(newKey, id); + } + } + } + } + + /** + * Return the base table alias. This is always the first letter of the bean + * name. + */ + public String getBaseTableAlias() { + return baseTableAlias; + } + + public boolean loadFromCache(EntityBeanIntercept ebi) { + Object bean = ebi.getOwner(); + Object id = getId(bean); + + return loadFromCache(bean, ebi, id); + } + + public boolean loadFromCache(Object bean, EntityBeanIntercept ebi, Object id) { + + CachedBeanData cacheData = (CachedBeanData) getBeanCache().get(id); + if (cacheData == null) { + return false; + } + String lazyLoadProperty = ebi.getLazyLoadProperty(); + if (lazyLoadProperty != null && !cacheData.containsProperty(lazyLoadProperty)) { + return false; + } + + CachedBeanDataToBean.load(this, bean, ebi, cacheData); + return true; + } + + public void preAllocateIds(int batchSize) { + if (idGenerator != null) { + idGenerator.preAllocateIds(batchSize); + } + } + + public Object nextId(Transaction t) { + if (idGenerator != null) { + return idGenerator.nextId(t); + } else { + return null; + } + } + + public DeployPropertyParser createDeployPropertyParser() { + return new DeployPropertyParser(this); + } + + /** + * Convert the logical orm update statement into sql by converting the bean + * properties and bean name to database columns and table. + */ + public String convertOrmUpdateToSql(String ormUpdateStatement) { + return new DeployUpdateParser(this).parse(ormUpdateStatement); + } + + /** + * Reset the statistics on all the query plans. + */ + public void clearQueryStatistics() { + Iterator it = queryPlanCache.values().iterator(); + while (it.hasNext()) { + CQueryPlan queryPlan = (CQueryPlan) it.next(); + queryPlan.resetStatistics(); + } + } + + /** + * Execute the postLoad if a BeanPersistController exists for this bean. + */ + @SuppressWarnings("unchecked") + public void postLoad(Object bean, Set includedProperties) { + BeanPersistController c = persistController; + if (c != null) { + c.postLoad((T) bean, includedProperties); + } + } + + /** + * Return the query plans for this BeanDescriptor. + */ + public Iterator queryPlans() { + return queryPlanCache.values().iterator(); + } + + public CQueryPlan getQueryPlan(Integer key) { + return queryPlanCache.get(key); + } + + public void putQueryPlan(Integer key, CQueryPlan plan) { + queryPlanCache.put(key, plan); + } + + /** + * Get a UpdatePlan for a given hash. + */ + public SpiUpdatePlan getUpdatePlan(Integer key) { + return updatePlanCache.get(key); + } + + /** + * Add a UpdatePlan to the cache with a given hash. + */ + public void putUpdatePlan(Integer key, SpiUpdatePlan plan) { + updatePlanCache.put(key, plan); + } + + /** + * Return the TypeManager. + */ + public TypeManager getTypeManager() { + return typeManager; + } + + /** + * Return true if updates should only include changed properties. Otherwise + * all loaded properties are included in the update. + */ + public boolean isUpdateChangesOnly() { + return updateChangesOnly; + } + + /** + * Return true if save does not recurse to other beans. That is return true if + * there are no assoc one or assoc many beans that cascade save. + */ + public boolean isSaveRecurseSkippable() { + return saveRecurseSkippable; + } + + /** + * Return true if delete does not recurse to other beans. That is return true + * if there are no assoc one or assoc many beans that cascade delete. + */ + public boolean isDeleteRecurseSkippable() { + return deleteRecurseSkippable; + } + + /** + * Return true if this type has local validation rules. + */ + public boolean hasLocalValidation() { + return hasLocalValidation; + } + + /** + * Return true if this type has local or cascading validation rules. + */ + public boolean hasCascadeValidation() { + return hasCascadeValidation; + } + + public InvalidValue validate(boolean cascade, Object bean) { + + if (!hasCascadeValidation) { + // no validation rules at all on this bean + return null; + } + + List errList = null; + + Set loadedProps = null; + if (bean instanceof EntityBean) { + EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept(); + loadedProps = ebi.getLoadedProps(); + } + if (loadedProps != null) { + // validate just the loaded properties + Iterator propIt = loadedProps.iterator(); + while (propIt.hasNext()) { + String propName = (String) propIt.next(); + BeanProperty property = getBeanProperty(propName); + + // check if we should fire validation on this property + if (property != null && property.hasValidationRules(cascade)) { + Object value = property.getValue(bean); + List errs = property.validate(cascade, value); + if (errs != null) { + if (errList == null) { + errList = new ArrayList(); + } + errList.addAll(errs); + } + } + } + } else { + // get appropriate list of properties with validation rules + BeanProperty[] props = cascade ? propertiesValidationCascade : propertiesValidationLocal; + + // validate all the properties + for (int i = 0; i < props.length; i++) { + BeanProperty prop = props[i]; + Object value = prop.getValue(bean); + List errs = prop.validate(cascade, value); + if (errs != null) { + if (errList == null) { + errList = new ArrayList(); + } + errList.addAll(errs); + } + } + } + + for (int i = 0; i < beanValidators.length; i++) { + if (!beanValidators[i].isValid(bean)) { + if (errList == null) { + errList = new ArrayList(); + } + Validator v = beanValidators[i]; + errList.add(new InvalidValue(v.getKey(), v.getAttributes(), getFullName(), null, bean)); + } + } + + if (errList == null) { + return null; + } + + return new InvalidValue(null, getFullName(), bean, InvalidValue.toArray(errList)); + } + + /** + * Return the many property included in the query or null if one is not. + */ + public BeanPropertyAssocMany getManyProperty(SpiQuery query) { + + OrmQueryDetail detail = query.getDetail(); + for (int i = 0; i < propertiesMany.length; i++) { + if (detail.includes(propertiesMany[i].getName())) { + return propertiesMany[i]; + } + } + + return null; + } + + /** + * Return the IdBinder which is helpful for handling the various types of Id. + */ + public IdBinder getIdBinder() { + return idBinder; + } + + /** + * Return the sql for binding an id. This is the columns with table alias that + * make up the id. + */ + public String getIdBinderIdSql() { + return idBinderIdSql; + } + + /** + * Return the sql for binding id's using an IN clause. + */ + public String getIdBinderInLHSSql() { + return idBinderInLHSSql; + } + + /** + * Bind the idValue to the preparedStatement. + *

+ * This takes care of the various id types such as embedded beans etc. + *

+ */ + public void bindId(DataBind dataBind, Object idValue) throws SQLException { + idBinder.bindId(dataBind, idValue); + } + + /** + * Return the id as an array of scalar bindable values. + *

+ * This 'flattens' any EmbeddedId or multiple Id property cases. + *

+ */ + public Object[] getBindIdValues(Object idValue) { + return idBinder.getBindValues(idValue); + } + + /** + * Return a named query. + */ + public DeployNamedQuery getNamedQuery(String name) { + return namedQueries.get(name); + } + + public DeployNamedQuery addNamedQuery(DeployNamedQuery deployNamedQuery) { + return namedQueries.put(deployNamedQuery.getName(), deployNamedQuery); + } + + /** + * Return a named update. + */ + public DeployNamedUpdate getNamedUpdate(String name) { + return namedUpdates.get(name); + } + + /** + * Create an EntityBean or "Vanilla" bean depending on the flag. + */ + public Object createBean(boolean vanillaMode) { + return vanillaMode ? createVanillaBean() : createEntityBean(); + } + + /** + * Create a plain vanilla object. + *

+ * Used for EmbeddedId Bean construction. + *

+ */ + public Object createVanillaBean() { + return beanReflect.createVanillaBean(); + } + + /** + * Creates a new EntityBean without using the creation queue. + */ + public EntityBean createEntityBean() { + try { + // Note factoryType is used indirectly via beanReflect + EntityBean eb = (EntityBean) beanReflect.createEntityBean(); + + return eb; + + } catch (Exception ex) { + throw new PersistenceException(ex); + } + } + + /** + * Create a reference bean based on the id. + */ + @SuppressWarnings("unchecked") + public T createReference(boolean vanillaMode, Boolean readOnly, Object id, Object parent) { + + if (cacheSharableBeans && !vanillaMode && !Boolean.FALSE.equals(readOnly)) { + CachedBeanData d = (CachedBeanData) getBeanCache().get(id); + if (d != null) { + Object shareableBean = d.getSharableBean(); + if (shareableBean != null) { + return (T) shareableBean; + } + } + } + try { + Object bean = createBean(vanillaMode); + + convertSetId(id, bean); + + if (!vanillaMode) { + EntityBean eb = (EntityBean) bean; + + EntityBeanIntercept ebi = eb._ebean_getIntercept(); + ebi.setBeanLoaderByServerName(ebeanServer.getName()); + + if (parent != null) { + // Special case for a OneToOne ... parent + // needs to be added to context prior to query + ebi.setParentBean(parent); + } + + // Note: not creating proxies for many's... + ebi.setReference(); + } + + return (T) bean; + + } catch (Exception ex) { + throw new PersistenceException(ex); + } + } + + /** + * Return the BeanProperty for the given deployment name. + */ + public BeanProperty getBeanPropertyFromDbColumn(String dbColumn) { + return propMapByDbColumn.get(dbColumn); + } + + /** + * Return the bean property traversing the object graph and taking into + * account inheritance. + */ + public BeanProperty getBeanPropertyFromPath(String path) { + + String[] split = SplitName.splitBegin(path); + if (split[1] == null) { + return _findBeanProperty(split[0]); + } + BeanPropertyAssoc assocProp = (BeanPropertyAssoc) _findBeanProperty(split[0]); + BeanDescriptor targetDesc = assocProp.getTargetDescriptor(); + + return targetDesc.getBeanPropertyFromPath(split[1]); + } + + /** + * Return the BeanDescriptor for a given path of Associated One or Many beans. + */ + public BeanDescriptor getBeanDescriptor(String path) { + if (path == null) { + return this; + } + String[] splitBegin = SplitName.splitBegin(path); + + BeanProperty beanProperty = propMap.get(splitBegin[0]); + if (beanProperty instanceof BeanPropertyAssoc) { + BeanPropertyAssoc assocProp = (BeanPropertyAssoc) beanProperty; + return assocProp.getTargetDescriptor().getBeanDescriptor(splitBegin[1]); + + } else { + throw new RuntimeException("Error getting BeanDescriptor for path " + path + " from " + getFullName()); + } + } + + /** + * Return the BeanDescriptor of another bean type. + */ + public BeanDescriptor getBeanDescriptor(Class otherType) { + return owner.getBeanDescriptor(otherType); + } + + /** + * Return the "shadow" property to support unidirectional relationships. + *

+ * For bidirectional this is a real property on the bean. For unidirectional + * relationships we have this 'shadow' property which is not externally + * visible. + *

+ */ + public BeanPropertyAssocOne getUnidirectional() { + if (unidirectional != null) { + return unidirectional; + } + if (inheritInfo != null && !inheritInfo.isRoot()) { + return inheritInfo.getParent().getBeanDescriptor().getUnidirectional(); + } + return null; + } + + /** + * Get a property value from a bean of this type. + */ + public Object getValue(Object bean, String property) { + return getBeanProperty(property).getValue(bean); + } + + /** + * Return true if this bean type should use IdGeneration. + *

+ * If this is false and the Id is null it is assumed that a database auto + * increment feature is being used to populate the id. + *

+ */ + public boolean isUseIdGenerator() { + return idGenerator != null; + } + + /** + * Return the alternate "Id" that identifies this BeanDescriptor. This is an + * alternative to using the bean class name. + */ + public String getDescriptorId() { + return descriptorId; + } + + /** + * Return the class type this BeanDescriptor describes. + */ + public Class getBeanType() { + return beanType; + } + + /** + * Return the class type this BeanDescriptor describes. + */ + public Class getFactoryType() { + return factoryType; + } + + /** + * Return the bean class name this descriptor is used for. + *

+ * If this BeanDescriptor is for a table then this returns the table name + * instead. + *

+ */ + public String getFullName() { + return fullName; + } + + /** + * Return the short name of the entity bean. + */ + public String getName() { + return name; + } + + /** + * Summary description. + */ + public String toString() { + return fullName; + } + + /** + * Helper method to return the unique property. If only one property makes up + * the unique id then it's value is returned. If there is a concatenated + * unique id then a Map is built with the keys being the names of the + * properties that make up the unique id. + */ + public Object getId(Object bean) { + + if (propertySingleId != null) { + if (inheritInfo != null && !enhancedBean) { + // avoid generated method via forced reflection use + return propertySingleId.getValueViaReflection(bean); + + } else { + return propertySingleId.getValue(bean); + } + } + + // it is a concatenated id Not embedded + // so return a Map + LinkedHashMap idMap = new LinkedHashMap(); + for (int i = 0; i < propertiesId.length; i++) { + + Object value = propertiesId[i].getValue(bean); + idMap.put(propertiesId[i].getName(), value); + } + return idMap; + } + + /** + * Return false if the id is a simple scalar and false if it is embedded or + * concatenated. + */ + public boolean isComplexId() { + return idBinder.isComplexId(); + } + + /** + * Return the default order by that may need to be added if a many property is + * included in the query. + */ + public String getDefaultOrderBy() { + return idBinder.getDefaultOrderBy(); + } + + /** + * Convert the type of the idValue if required. + */ + public Object convertId(Object idValue) { + return idBinder.convertSetId(idValue, null); + } + + /** + * Convert and set the id value. + *

+ * If the bean is not null, the id value is set to the id property of the bean + * after it has been converted to the correct type. + *

+ */ + public Object convertSetId(Object idValue, Object bean) { + return idBinder.convertSetId(idValue, bean); + } + + /** + * Get a BeanProperty by its name. + */ + public BeanProperty getBeanProperty(String propName) { + return (BeanProperty) propMap.get(propName); + } + + public void sort(List list, String sortByClause) { + + ElComparator comparator = getElComparator(sortByClause); + Collections.sort(list, comparator); + } + + public ElComparator getElComparator(String propNameOrSortBy) { + ElComparator c = comparatorCache.get(propNameOrSortBy); + if (c == null) { + c = createComparator(propNameOrSortBy); + comparatorCache.put(propNameOrSortBy, c); + } + return c; + } + + /** + * Return true if the lazy loading property is a Many in which case just + * define a Reference for the collection and not invoke a query. + */ + public boolean lazyLoadMany(EntityBeanIntercept ebi) { + + String lazyLoadProperty = ebi.getLazyLoadProperty(); + BeanProperty lazyLoadBeanProp = getBeanProperty(lazyLoadProperty); + + if (lazyLoadBeanProp instanceof BeanPropertyAssocMany) { + BeanPropertyAssocMany manyProp = (BeanPropertyAssocMany) lazyLoadBeanProp; + manyProp.createReference(ebi.getOwner()); + Set loadedProps = ebi.getLoadedProps(); + HashSet newLoadedProps = new HashSet(); + if (loadedProps != null) { + newLoadedProps.addAll(loadedProps); + } + newLoadedProps.add(lazyLoadProperty); + ebi.setLoadedProps(newLoadedProps); + ebi.setLoadedLazy(); + return true; + } + + return false; + } + + /** + * Return a Comparator for local sorting of lists. + * + * @param sortByClause + * list of property names with optional ASC or DESC suffix. + */ + @SuppressWarnings("unchecked") + private ElComparator createComparator(String sortByClause) { + + SortByClause sortBy = SortByClauseParser.parse(sortByClause); + if (sortBy.size() == 1) { + // simple comparator for a single property + return createPropertyComparator(sortBy.getProperties().get(0)); + } + + // create a compound comparator based on the list of properties + ElComparator[] comparators = new ElComparator[sortBy.size()]; + + List sortProps = sortBy.getProperties(); + for (int i = 0; i < sortProps.size(); i++) { + Property sortProperty = sortProps.get(i); + comparators[i] = createPropertyComparator(sortProperty); + } + + return new ElComparatorCompound(comparators); + } + + private ElComparator createPropertyComparator(Property sortProp) { + + ElPropertyValue elGetValue = getElGetValue(sortProp.getName()); + + Boolean nullsHigh = sortProp.getNullsHigh(); + if (nullsHigh == null) { + nullsHigh = Boolean.TRUE; + } + return new ElComparatorProperty(elGetValue, sortProp.isAscending(), nullsHigh); + } + + /** + * Get an Expression language Value object. + */ + public ElPropertyValue getElGetValue(String propName) { + return getElPropertyValue(propName, false); + } + + /** + * Similar to ElPropertyValue but also uses foreign key shortcuts. + *

+ * The foreign key shortcuts means we can avoid unnecessary joins. + *

+ */ + public ElPropertyDeploy getElPropertyDeploy(String propName) { + ElPropertyDeploy fk = fkeyMap.get(propName); + if (fk != null) { + return fk; + } + return getElPropertyValue(propName, true); + } + + private ElPropertyValue getElPropertyValue(String propName, boolean propertyDeploy) { + ElPropertyValue elGetValue = elGetCache.get(propName); + if (elGetValue == null) { + // need to build it potentially navigating the BeanDescriptors + elGetValue = buildElGetValue(propName, null, propertyDeploy); + if (elGetValue == null) { + return null; + } + if (elGetValue instanceof BeanFkeyProperty) { + fkeyMap.put(propName, (BeanFkeyProperty) elGetValue); + } else { + elGetCache.put(propName, elGetValue); + } + } + return elGetValue; + } + + protected ElPropertyValue buildElGetValue(String propName, ElPropertyChainBuilder chain, boolean propertyDeploy) { + + if (propertyDeploy && chain != null) { + BeanFkeyProperty fk = fkeyMap.get(propName); + if (fk != null) { + return fk.create(chain.getExpression()); + } + } + + int basePos = propName.indexOf('.'); + if (basePos > -1) { + // nested or embedded property + String baseName = propName.substring(0, basePos); + String remainder = propName.substring(basePos + 1); + + BeanProperty assocProp = _findBeanProperty(baseName); + if (assocProp == null) { + return null; + } + return assocProp.buildElPropertyValue(propName, remainder, chain, propertyDeploy); + } + + BeanProperty property = _findBeanProperty(propName); + if (chain == null) { + return property; + } + if (property == null) { + throw new PersistenceException("No property found for [" + propName + "] in expression " + chain.getExpression()); + } + if (property.containsMany()) { + chain.setContainsMany(true); + } + return chain.add(property).build(); + } + + /** + * Find a BeanProperty including searching the inheritance hierarchy. + *

+ * This searches this BeanDescriptor and then searches further down the + * inheritance tree (not up). + *

+ */ + public BeanProperty findBeanProperty(String propName) { + int basePos = propName.indexOf('.'); + if (basePos > -1) { + // embedded property + String baseName = propName.substring(0, basePos); + return _findBeanProperty(baseName); + } + + return _findBeanProperty(propName); + } + + private BeanProperty _findBeanProperty(String propName) { + BeanProperty prop = propMap.get(propName); + if (prop == null && inheritInfo != null) { + // search in sub types... + return inheritInfo.findSubTypeProperty(propName); + } + return prop; + } + + protected Object getBeanPropertyWithInheritance(Object bean, String propName) { + + BeanDescriptor desc = getBeanDescriptor(bean.getClass()); + BeanProperty beanProperty = desc.findBeanProperty(propName); + return beanProperty.getValue(bean); + } + + /** + * Return the name of the server this BeanDescriptor belongs to. + */ + public String getServerName() { + return serverName; + } + + /** + * Return true if this bean can cache sharable instances. + *

+ * This means is has no relationships and has readOnly=true in its cache + * options. + *

+ */ + public boolean isCacheSharableBeans() { + return cacheSharableBeans; + } + + /** + * Return true if queries for beans of this type are autoFetch tunable. + */ + public boolean isAutoFetchTunable() { + return autoFetchTunable; + } + + /** + * Returns the Inheritance mapping information. This will be null if this type + * of bean is not involved in any ORM inheritance mapping. + */ + public InheritInfo getInheritInfo() { + return inheritInfo; + } + + /** + * Return true if this is an embedded bean. + */ + public boolean isEmbedded() { + return EntityType.EMBEDDED.equals(entityType); + } + + public boolean isBaseTableType() { + return EntityType.ORM.equals(entityType); + } + + /** + * Return the concurrency mode used for beans of this type. + */ + public ConcurrencyMode getConcurrencyMode() { + return concurrencyMode; + } + + /** + * Return the tables this bean is dependent on. This implies that if any of + * these tables are modified then cached beans may be invalidated. + */ + public String[] getDependantTables() { + return dependantTables; + } + + /** + * Return the compound unique constraints. + */ + public CompoundUniqueContraint[] getCompoundUniqueConstraints() { + return compoundUniqueConstraints; + } + + /** + * Return the beanListener. + */ + public BeanPersistListener getPersistListener() { + return persistListener; + } + + /** + * Return the beanFinder. Usually null unless overriding the finder. + */ + public BeanFinder getBeanFinder() { + return beanFinder; + } + + /** + * Return the BeanQueryAdapter or null if none is defined. + */ + public BeanQueryAdapter getQueryAdapter() { + return queryAdapter; + } + + /** + * De-register the BeanPersistListener. + */ + @SuppressWarnings("unchecked") + public void deregister(BeanPersistListener listener) { + // volatile read... + BeanPersistListener currListener = persistListener; + if (currListener == null) { + // nothing to deregister + } else { + BeanPersistListener deregListener = (BeanPersistListener) listener; + if (currListener instanceof ChainedBeanPersistListener) { + // remove it from the existing chain + persistListener = ((ChainedBeanPersistListener) currListener).deregister(deregListener); + } else if (currListener.equals(deregListener)) { + persistListener = null; + } + } + } + + /** + * De-register the BeanPersistController. + */ + public void deregister(BeanPersistController controller) { + // volatile read... + BeanPersistController c = persistController; + if (c == null) { + // nothing to deregister + } else { + if (c instanceof ChainedBeanPersistController) { + // remove it from the existing chain + persistController = ((ChainedBeanPersistController) c).deregister(controller); + } else if (c.equals(controller)) { + persistController = null; + } + } + } + + /** + * Register the new BeanPersistController. + */ + @SuppressWarnings("unchecked") + public void register(BeanPersistListener newPersistListener) { + + if (!PersistListenerManager.isRegisterFor(beanType, newPersistListener)) { + // skip + } else { + BeanPersistListener newListener = (BeanPersistListener) newPersistListener; + // volatile read... + BeanPersistListener currListener = persistListener; + if (currListener == null) { + persistListener = newListener; + } else { + if (currListener instanceof ChainedBeanPersistListener) { + // add it to the existing chain + persistListener = ((ChainedBeanPersistListener) currListener).register(newListener); + } else { + // build new chain of the 2 + persistListener = new ChainedBeanPersistListener(currListener, newListener); + } + } + } + } + + /** + * Register the new BeanPersistController. + */ + public void register(BeanPersistController newController) { + + if (!newController.isRegisterFor(beanType)) { + // skip + } else { + // volatile read... + BeanPersistController c = persistController; + if (c == null) { + persistController = newController; + } else { + if (c instanceof ChainedBeanPersistController) { + // add it to the existing chain + persistController = ((ChainedBeanPersistController) c).register(newController); + } else { + // build new chain of the 2 + persistController = new ChainedBeanPersistController(c, newController); + } + } + } + } + + /** + * Return the Controller. + */ + public BeanPersistController getPersistController() { + return persistController; + } + + /** + * Returns true if this bean is based on a table (or possibly view) and + * returns false if this bean is based on a raw sql select statement. + *

+ * When false querying this bean is based on a supplied sql select statement + * placed in the orm xml file (as opposed to Ebean generated sql). + *

+ */ + public boolean isSqlSelectBased() { + return EntityType.SQL.equals(entityType); + } + + /** + * Return true if this an LDAP object. + */ + public boolean isLdapEntityType() { + return EntityType.LDAP.equals(entityType); + } + + /** + * Return the base table. Only properties mapped to the base table are by + * default persisted. + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Get a named extra attribute. + */ + public String getExtraAttribute(String key) { + return (String) extraAttrMap.get(key); + } + + /** + * Return the identity generation type. + */ + public IdType getIdType() { + return idType; + } + + /** + * Return the sequence name. + */ + public String getSequenceName() { + return sequenceName; + } + + /** + * Return the SQL used to return the last inserted id. + *

+ * This is only used with Identity columns and getGeneratedKeys is not + * supported. + *

+ */ + public String getSelectLastInsertedId() { + return selectLastInsertedId; + } + + /** + * Return the IdGenerator. + */ + public IdGenerator getIdGenerator() { + return idGenerator; + } + + /** + * Return the includes for getReference(). + */ + public String getLazyFetchIncludes() { + return lazyFetchIncludes; + } + + /** + * Return the TableJoins. + *

+ * For properties mapped to secondary tables rather than the base table. + *

+ */ + public TableJoin[] tableJoins() { + return derivedTableJoins; + } + + /** + * Return an Iterator of all BeanProperty. This includes transient properties. + */ + public Iterator propertiesAll() { + return propMap.values().iterator(); + } + + /** + * Return the BeanProperty that make up the unique id. + *

+ * The order of these properties can be relied on to be consistent if the bean + * itself doesn't change or the xml deployment order does not change. + *

+ */ + public BeanProperty[] propertiesId() { + return propertiesId; + } + + /** + * Return the non transient non id properties. + */ + public BeanProperty[] propertiesNonTransient() { + return propertiesNonTransient; + } + + /** + * Return the transient properties. + */ + public BeanProperty[] propertiesTransient() { + return propertiesTransient; + } + + /** + * If the Id is a single non-embedded property then returns that, otherwise + * returns null. + */ + public BeanProperty getSingleIdProperty() { + return propertySingleId; + } + + /** + * Return the beans that are embedded. These share the base table with the + * owner bean. + */ + public BeanPropertyAssocOne[] propertiesEmbedded() { + return propertiesEmbedded; + } + + /** + * All the BeanPropertyAssocOne that are not embedded. These are effectively + * joined beans. For ManyToOne and OneToOne associations. + */ + public BeanPropertyAssocOne[] propertiesOne() { + return propertiesOne; + } + + /** + * Returns ManyToOnes and OneToOnes on the imported owning side. + *

+ * Excludes OneToOnes on the exported side. + *

+ */ + public BeanPropertyAssocOne[] propertiesOneImported() { + return propertiesOneImported; + } + + /** + * Imported Assoc Ones with cascade save true. + */ + public BeanPropertyAssocOne[] propertiesOneImportedSave() { + return propertiesOneImportedSave; + } + + /** + * Imported Assoc Ones with cascade delete true. + */ + public BeanPropertyAssocOne[] propertiesOneImportedDelete() { + return propertiesOneImportedDelete; + } + + /** + * Returns OneToOnes that are on the exported side of a OneToOne. + *

+ * These associations do not own the relationship. + *

+ */ + public BeanPropertyAssocOne[] propertiesOneExported() { + return propertiesOneExported; + } + + /** + * Exported assoc ones with cascade save. + */ + public BeanPropertyAssocOne[] propertiesOneExportedSave() { + return propertiesOneExportedSave; + } + + /** + * Exported assoc ones with delete cascade. + */ + public BeanPropertyAssocOne[] propertiesOneExportedDelete() { + return propertiesOneExportedDelete; + } + + private Set deriveManyPropNames() { + + LinkedHashSet names = new LinkedHashSet(); + for (int i = 0; i < propertiesMany.length; i++) { + names.add(propertiesMany[i].getName()); + } + + return Collections.unmodifiableSet(names); + } + + /** + * Return a hash of the names of the many properties on this bean type. This + * is used for efficient building of included properties sets for partial + * objects. + */ + public int getNamesOfManyPropsHash() { + return namesOfManyPropsHash; + } + + /** + * Returns the set of many property names for this bean type. + */ + public Set getNamesOfManyProps() { + return namesOfManyProps; + } + + /** + * All Non Assoc Many's for this descriptor. + */ + public BeanProperty[] propertiesNonMany() { + return propertiesNonMany; + } + + /** + * All Assoc Many's for this descriptor. + */ + public BeanPropertyAssocMany[] propertiesMany() { + return propertiesMany; + } + + /** + * Assoc Many's with save cascade. + */ + public BeanPropertyAssocMany[] propertiesManySave() { + return propertiesManySave; + } + + /** + * Assoc Many's with delete cascade. + */ + public BeanPropertyAssocMany[] propertiesManyDelete() { + return propertiesManyDelete; + } + + /** + * Assoc ManyToMany's. + */ + public BeanPropertyAssocMany[] propertiesManyToMany() { + return propertiesManyToMany; + } + + /** + * Return the first version property that exists on the bean. Returns null if + * no version property exists on the bean. + *

+ * Note that this DOES NOT find a version property on an embedded bean. + *

+ */ + public BeanProperty firstVersionProperty() { + return propertyFirstVersion; + } + + /** + * Return true if this an Insert (rather than Update) on a non-enhanced bean. + */ + public boolean isVanillaInsert(Object bean) { + if (propertyFirstVersion == null) { + return true; + } + Object versionValue = propertyFirstVersion.getValue(bean); + return DmlUtil.isNullOrZero(versionValue); + } + + /** + * Return true if this is an Update (rather than insert) given that the bean + * is involved in a stateless update. + */ + public boolean isStatelessUpdate(Object bean) { + if (propertyFirstVersion == null) { + Object versionValue = getId(bean); + return !DmlUtil.isNullOrZero(versionValue); + } else { + Object versionValue = propertyFirstVersion.getValue(bean); + return !DmlUtil.isNullOrZero(versionValue); + } + } + + /** + * Returns 'Version' properties on this bean. These are 'Counter' or 'Update + * Timestamp' type properties. Note version properties can also be on embedded + * beans rather than on the bean itself. + */ + public BeanProperty[] propertiesVersion() { + return propertiesVersion; + } + + /** + * Scalar properties without the unique id or secondary table properties. + */ + public BeanProperty[] propertiesBaseScalar() { + return propertiesBaseScalar; + } + + /** + * Return properties that are immutable compound value objects. + *

+ * These are compound types but are not enhanced (Embedded are enhanced). + *

+ */ + public BeanPropertyCompound[] propertiesBaseCompound() { + return propertiesBaseCompound; + } + + /** + * Return the properties local to this type for inheritance. + */ + public BeanProperty[] propertiesLocal() { + return propertiesLocal; + } + + public void jsonWrite(WriteJsonContext ctx, Object bean) { + + if (bean != null) { + + ctx.appendObjectBegin(); + WriteBeanState prevState = ctx.pushBeanState(bean); + + if (inheritInfo != null) { + InheritInfo localInheritInfo = inheritInfo.readType(bean.getClass()); + String discValue = localInheritInfo.getDiscriminatorStringValue(); + String discColumn = localInheritInfo.getDiscriminatorColumn(); + ctx.appendDiscriminator(discColumn, discValue); + + BeanDescriptor localDescriptor = localInheritInfo.getBeanDescriptor(); + localDescriptor.jsonWriteProperties(ctx, bean); + + } else { + jsonWriteProperties(ctx, bean); + } + + ctx.pushPreviousState(prevState); + ctx.appendObjectEnd(); + } + } + + @SuppressWarnings("unchecked") + private void jsonWriteProperties(WriteJsonContext ctx, Object bean) { + + boolean referenceBean = ctx.isReferenceBean(); + + JsonWriteBeanVisitor beanVisitor = (JsonWriteBeanVisitor) ctx.getBeanVisitor(); + + Set props = ctx.getIncludeProperties(); + + boolean explicitAllProps; + if (props == null) { + explicitAllProps = false; + } else { + explicitAllProps = props.contains("*"); + if (explicitAllProps || props.isEmpty()) { + props = null; + } + } + + for (int i = 0; i < propertiesId.length; i++) { + Object idValue = propertiesId[i].getValue(bean); + if (idValue != null) { + if (props == null || props.contains(propertiesId[i].getName())) { + propertiesId[i].jsonWrite(ctx, bean); + } + } + } + + if (!explicitAllProps && props == null) { + // just render the loaded properties + props = ctx.getLoadedProps(); + } + if (props != null) { + // render only the appropriate properties (when not all properties) + for (String prop : props) { + BeanProperty p = getBeanProperty(prop); + if (p != null && !p.isId()) { + p.jsonWrite(ctx, bean); + } + } + } else { + if (explicitAllProps || !referenceBean) { + // render all the properties and invoke lazy loading if required + for (int j = 0; j < propertiesNonTransient.length; j++) { + propertiesNonTransient[j].jsonWrite(ctx, bean); + } + } + } + + if (beanVisitor != null) { + beanVisitor.visit((T) bean, ctx); + } + } + + @SuppressWarnings("unchecked") + public T jsonReadBean(ReadJsonContext ctx, String path) { + ReadBeanState beanState = jsonRead(ctx, path); + if (beanState == null) { + return null; + } else { + beanState.setLoadedState(); + return (T) beanState.getBean(); + } + } + + public ReadBeanState jsonRead(ReadJsonContext ctx, String path) { + if (!ctx.readObjectBegin()) { + // the object is null + return null; + } + + if (inheritInfo == null) { + return jsonReadObject(ctx, path); + + } else { + // read the discriminator value to determine the correct sub type + String discColumn = inheritInfo.getRoot().getDiscriminatorColumn(); + + if (!ctx.readKeyNext()) { + String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but no json key?"; + throw new TextException(msg); + } + String propName = ctx.getTokenKey(); + + if (!propName.equalsIgnoreCase(discColumn)) { + String msg = "Error reading inheritance discriminator - expected [" + discColumn + "] but read [" + propName + "]"; + throw new TextException(msg); + } + + String discValue = ctx.readScalarValue(); + if (!ctx.readValueNext()) { + String msg = "Error reading inheritance discriminator [" + discColumn + "]. Expected more json name values?"; + throw new TextException(msg); + } + + // determine the sub type for this particular json object + InheritInfo localInheritInfo = inheritInfo.readType(discValue); + BeanDescriptor localDescriptor = localInheritInfo.getBeanDescriptor(); + return localDescriptor.jsonReadObject(ctx, path); + } + } + + @SuppressWarnings("unchecked") + private ReadBeanState jsonReadObject(ReadJsonContext ctx, String path) { + + T bean = (T) createEntityBean(); + ctx.pushBean(bean, path, this); + + do { + if (!ctx.readKeyNext()) { + break; + } else { + // we read a property key ... + String propName = ctx.getTokenKey(); + BeanProperty p = getBeanProperty(propName); + if (p != null) { + p.jsonRead(ctx, bean); + ctx.setProperty(propName); + } else { + // unknown property key ... + ctx.readUnmappedJson(propName); + } + + if (!ctx.readValueNext()) { + break; + } + } + } while (true); + + return ctx.popBeanState(); + } + + /** + * Set the loaded properties with additional check to see if the bean is a + * reference. + */ + public void setLoadedProps(EntityBeanIntercept ebi, Set loadedProps) { + if (isLoadedReference(loadedProps)) { + ebi.setReference(); + } else { + ebi.setLoadedProps(loadedProps); + } + } + + /** + * Return true if the loadedProperties is just the Id property and therefore + * this is really a reference. + */ + public boolean isLoadedReference(Set loadedProps) { + + if (loadedProps != null) { + if (loadedProps.size() == propertiesId.length) { + for (int i = 0; i < propertiesId.length; i++) { + if (!loadedProps.contains(propertiesId[i].getName())) { + return false; + } + } + return true; + } + } + + return false; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java index a514b61e9..0eeb15d95 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -1,1566 +1,1547 @@ -/** - * 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.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; -import javax.sql.DataSource; - -import com.avaje.ebean.BackgroundExecutor; -import com.avaje.ebean.RawSql; -import com.avaje.ebean.RawSqlBuilder; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.cache.ServerCacheManager; -import com.avaje.ebean.config.EncryptKey; -import com.avaje.ebean.config.EncryptKeyManager; -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebean.config.NamingConvention; -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebean.config.dbplatform.DbIdentity; -import com.avaje.ebean.config.dbplatform.IdGenerator; -import com.avaje.ebean.config.dbplatform.IdType; -import com.avaje.ebean.event.BeanFinder; -import com.avaje.ebean.validation.factory.LengthValidatorFactory; -import com.avaje.ebean.validation.factory.NotNullValidatorFactory; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.TransactionEventTable; -import com.avaje.ebeaninternal.server.core.BootupClasses; -import com.avaje.ebeaninternal.server.core.ConcurrencyMode; -import com.avaje.ebeaninternal.server.core.InternString; -import com.avaje.ebeaninternal.server.core.InternalConfiguration; -import com.avaje.ebeaninternal.server.core.Message; -import com.avaje.ebeaninternal.server.core.XmlConfig; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; -import com.avaje.ebeaninternal.server.deploy.id.IdBinder; -import com.avaje.ebeaninternal.server.deploy.id.IdBinderEmbedded; -import com.avaje.ebeaninternal.server.deploy.id.IdBinderFactory; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable; -import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; -import com.avaje.ebeaninternal.server.deploy.parse.DeployBeanInfo; -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.deploy.parse.ReadAnnotations; -import com.avaje.ebeaninternal.server.deploy.parse.TransientProperties; -import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator; -import com.avaje.ebeaninternal.server.lib.util.Dnode; -import com.avaje.ebeaninternal.server.reflect.BeanReflect; -import com.avaje.ebeaninternal.server.reflect.BeanReflectFactory; -import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter; -import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter; -import com.avaje.ebeaninternal.server.reflect.EnhanceBeanReflectFactory; -import com.avaje.ebeaninternal.server.subclass.SubClassManager; -import com.avaje.ebeaninternal.server.subclass.SubClassUtil; -import com.avaje.ebeaninternal.server.type.TypeManager; - -/** - * Creates BeanDescriptors. - */ -public class BeanDescriptorManager implements BeanDescriptorMap { - - private static final Logger logger = Logger.getLogger(BeanDescriptorManager.class.getName()); - - private static final BeanDescComparator beanDescComparator = new BeanDescComparator(); - - private final ReadAnnotations readAnnotations = new ReadAnnotations(); - - private final TransientProperties transientProperties; - - /** - * Helper to derive inheritance information. - */ - private final DeployInherit deplyInherit; - - private final BeanReflectFactory reflectFactory; - - private final DeployUtil deployUtil; - - private final TypeManager typeManager; - - private final PersistControllerManager persistControllerManager; - - private final BeanFinderManager beanFinderManager; - - private final PersistListenerManager persistListenerManager; - - private final BeanQueryAdapterManager beanQueryAdapterManager; - - private final SubClassManager subClassManager; - - private final NamingConvention namingConvention; - - private final DeployCreateProperties createProperties; - - private final DeployOrmXml deployOrmXml; - - private final BeanManagerFactory beanManagerFactory; - - private int enhancedClassCount; - private int subclassClassCount; - private final HashSet subclassedEntities = new HashSet(); - - private final boolean updateChangesOnly; - - private final BootupClasses bootupClasses; - - private final String serverName; - - private Map, DeployBeanInfo> deplyInfoMap = new HashMap, DeployBeanInfo>(); - - private final Map, BeanTable> beanTableMap = new HashMap, BeanTable>(); - - private final Map> descMap = new HashMap>(); - private final Map> idDescMap = new HashMap>(); - - private final Map> beanManagerMap = new HashMap>(); - - private final Map>> tableToDescMap = new HashMap>>(); - - private List> immutableDescriptorList; - - private final Set descriptorUniqueIds = new HashSet(); - - private final DbIdentity dbIdentity; - - private final DataSource dataSource; - - private final DatabasePlatform databasePlatform; - - private final UuidIdGenerator uuidIdGenerator = new UuidIdGenerator(); - - private final ServerCacheManager cacheManager; - - private final BackgroundExecutor backgroundExecutor; - - private final int dbSequenceBatchSize; - - private final EncryptKeyManager encryptKeyManager; - - private final IdBinderFactory idBinderFactory; - - private final XmlConfig xmlConfig; - - private final boolean allowSubclassing; - - /** - * Create for a given database dbConfig. - */ - public BeanDescriptorManager(InternalConfiguration config) { - - this.serverName = InternString.intern(config.getServerConfig().getName()); - this.cacheManager = config.getCacheManager(); - this.xmlConfig = config.getXmlConfig(); - this.dbSequenceBatchSize = config.getServerConfig().getDatabaseSequenceBatchSize(); - this.backgroundExecutor = config.getBackgroundExecutor(); - this.dataSource = config.getServerConfig().getDataSource(); - this.encryptKeyManager = config.getServerConfig().getEncryptKeyManager(); - this.databasePlatform = config.getServerConfig().getDatabasePlatform(); - this.idBinderFactory = new IdBinderFactory(databasePlatform.isIdInExpandedForm()); - - this.bootupClasses = config.getBootupClasses(); - this.createProperties = config.getDeployCreateProperties(); - this.subClassManager = config.getSubClassManager(); - this.typeManager = config.getTypeManager(); - this.namingConvention = config.getServerConfig().getNamingConvention(); - this.dbIdentity = config.getDatabasePlatform().getDbIdentity(); - this.deplyInherit = config.getDeployInherit(); - this.deployOrmXml = config.getDeployOrmXml(); - this.deployUtil = config.getDeployUtil(); - - this.beanManagerFactory = new BeanManagerFactory(config.getServerConfig(), config.getDatabasePlatform()); - - this.updateChangesOnly = config.getServerConfig().isUpdateChangesOnly(); - - this.persistControllerManager = new PersistControllerManager(bootupClasses); - this.persistListenerManager = new PersistListenerManager(bootupClasses); - this.beanQueryAdapterManager = new BeanQueryAdapterManager(bootupClasses); - - this.beanFinderManager = new DefaultBeanFinderManager(); - - this.reflectFactory = createReflectionFactory(); - this.transientProperties = new TransientProperties(); - this.allowSubclassing = config.getServerConfig().isAllowSubclassing(); - } - - public BeanDescriptor getBeanDescriptorById(String descriptorId) { - return idDescMap.get(descriptorId); - } - - @SuppressWarnings("unchecked") - public BeanDescriptor getBeanDescriptor(Class entityType) { - - // remove $$EntityBean stuff - String className = SubClassUtil.getSuperClassName(entityType.getName()); - return (BeanDescriptor) descMap.get(className); - } - - @SuppressWarnings("unchecked") - public BeanDescriptor getBeanDescriptor(String entityClassName) { - - // remove $$EntityBean stuff - entityClassName = SubClassUtil.getSuperClassName(entityClassName); - return (BeanDescriptor) descMap.get(entityClassName); - } - - public String getServerName() { - return serverName; - } - - public ServerCacheManager getCacheManager() { - return cacheManager; - } - - public NamingConvention getNamingConvention() { - return namingConvention; - } - - /** - * Set the internal EbeanServer instance to all BeanDescriptors. - */ - public void setEbeanServer(SpiEbeanServer internalEbean) { - for (BeanDescriptor desc : immutableDescriptorList) { - desc.setEbeanServer(internalEbean); - } - } - - public IdBinder createIdBinder(BeanProperty[] uids) { - return idBinderFactory.createIdBinder(uids); - } - - public void deploy() { - - try { - createListeners(); - readEmbeddedDeployment(); - readEntityDeploymentInitial(); - readEntityBeanTable(); - readEntityDeploymentAssociations(); - readInheritedIdGenerators(); - - // creates the BeanDescriptors - readEntityRelationships(); - readRawSqlQueries(); - - List> list = new ArrayList>(descMap.values()); - Collections.sort(list, beanDescComparator); - immutableDescriptorList = Collections.unmodifiableList(list); - - // put into map using the "desriptorId" (alternative to class name) - for (BeanDescriptor d : list) { - idDescMap.put(d.getDescriptorId(), d); - } - - initialiseAll(); - readForeignKeys(); - - readTableToDescriptor(); - - logStatus(); - - deplyInfoMap.clear(); - deplyInfoMap = null; - } catch (RuntimeException e) { - String msg = "Error in deployment"; - logger.log(Level.SEVERE, msg, e); - throw e; - } - } - - /** - * Return the Encrypt key given the table and column name. - */ - public EncryptKey getEncryptKey(String tableName, String columnName) { - return encryptKeyManager.getEncryptKey(tableName, columnName); - } - - /** - * For SQL based modifications we need to invalidate appropriate parts of the - * cache. - */ - public void cacheNotify(TransactionEventTable.TableIUD tableIUD) { - - List> list = getBeanDescriptors(tableIUD.getTableName()); - if (list != null) { - for (int i = 0; i < list.size(); i++) { - list.get(i).cacheNotify(tableIUD); - } - } - } - - /** - * Return the BeanDescriptors mapped to the table. - */ - public List> getBeanDescriptors(String tableName) { - return tableToDescMap.get(tableName.toLowerCase()); - } - - /** - * Build a map of table names to BeanDescriptors. - *

- * This is generally used to maintain caches from table names. - *

- */ - private void readTableToDescriptor() { - - for (BeanDescriptor desc : descMap.values()) { - String baseTable = desc.getBaseTable(); - if (baseTable == null) { - - } else { - baseTable = baseTable.toLowerCase(); - - List> list = tableToDescMap.get(baseTable); - if (list == null) { - list = new ArrayList>(1); - tableToDescMap.put(baseTable, list); - } - list.add(desc); - } - } - } - - private void readForeignKeys() { - - for (BeanDescriptor d : descMap.values()) { - d.initialiseFkeys(); - } - } - - /** - * Initialise all the BeanDescriptors. - *

- * This occurs after all the BeanDescriptors have been created. This resolves - * circular relationships between BeanDescriptors. - *

- *

- * Also responsible for creating all the BeanManagers which contain the - * persister, listener etc. - *

- */ - private void initialiseAll() { - - // now that all the BeanDescriptors are in their map - // we can initialise them which sorts out circular - // dependencies for OneToMany and ManyToOne etc - - // PASS 1: - // initialise the ID properties of all the beans - // first (as they are needed to initialise the - // associated properties in the second pass). - for (BeanDescriptor d : descMap.values()) { - d.initialiseId(); - } - - // PASS 2: - // now initialise all the inherit info - for (BeanDescriptor d : descMap.values()) { - d.initInheritInfo(); - } - - // PASS 3: - // now initialise all the associated properties - for (BeanDescriptor d : descMap.values()) { - d.initialiseOther(); - } - - // create BeanManager for each non-embedded entity bean - for (BeanDescriptor d : descMap.values()) { - if (!d.isEmbedded()) { - BeanManager m = beanManagerFactory.create(d); - beanManagerMap.put(d.getFullName(), m); - - checkForValidEmbeddedId(d); - } - } - } - - private void checkForValidEmbeddedId(BeanDescriptor d) { - IdBinder idBinder = d.getIdBinder(); - if (idBinder != null && idBinder instanceof IdBinderEmbedded) { - IdBinderEmbedded embId = (IdBinderEmbedded) idBinder; - BeanDescriptor idBeanDescriptor = embId.getIdBeanDescriptor(); - Class idType = idBeanDescriptor.getBeanType(); - try { - idType.getDeclaredMethod("hashCode", new Class[] {}); - idType.getDeclaredMethod("equals", new Class[] { Object.class }); - } catch (NoSuchMethodException e) { - checkMissingHashCodeOrEquals(e, idType, d.getBeanType()); - } - } - } - - private void checkMissingHashCodeOrEquals(Exception source, Class idType, Class beanType) { - - String msg = "SERIOUS ERROR: The hashCode() and equals() methods *MUST* be implemented "; - msg += "on Embedded bean " + idType + " as it is used as an Id for " + beanType; - - if (GlobalProperties.getBoolean("ebean.strict", true)) { - throw new PersistenceException(msg, source); - } else { - logger.log(Level.SEVERE, msg, source); - } - } - - /** - * Return an immutable list of all the BeanDescriptors. - */ - public List> getBeanDescriptorList() { - return immutableDescriptorList; - } - - public Map, BeanTable> getBeanTables() { - return beanTableMap; - } - - public BeanTable getBeanTable(Class type) { - return beanTableMap.get(type); - } - - public Map> getBeanDescriptors() { - return descMap; - } - - @SuppressWarnings("unchecked") - public BeanManager getBeanManager(Class entityType) { - - return (BeanManager) getBeanManager(entityType.getName()); - } - - public BeanManager getBeanManager(String beanClassName) { - - beanClassName = SubClassUtil.getSuperClassName(beanClassName); - return beanManagerMap.get(beanClassName); - } - - public DNativeQuery getNativeQuery(String name) { - return deployOrmXml.getNativeQuery(name); - } - - /** - * Create the BeanControllers, BeanFinders and BeanListeners. - */ - private void createListeners() { - - int qa = beanQueryAdapterManager.getRegisterCount(); - int cc = persistControllerManager.getRegisterCount(); - int lc = persistListenerManager.getRegisterCount(); - int fc = beanFinderManager.createBeanFinders(bootupClasses.getBeanFinders()); - - logger - .fine("BeanPersistControllers[" + cc + "] BeanFinders[" + fc + "] BeanPersistListeners[" + lc + "] BeanQueryAdapters[" + qa + "]"); - } - - /** - * Log Warning if mixing subclass and enhancement. - *

- * If enhancement is used for some classes it is expected to be used for all - * and vice versa. - *

- */ - private void logStatus() { - - String msg = "Entities enhanced[" + enhancedClassCount + "] subclassed[" + subclassClassCount + "]"; - logger.info(msg); - - if (enhancedClassCount > 0) { - if (subclassClassCount > 0) { - String subclassEntityNames = subclassedEntities.toString(); - - String m = "Mixing enhanced and subclassed entities. Subclassed classes:" + subclassEntityNames; - logger.warning(m); - } - } - } - - private BeanDescriptor createEmbedded(Class beanClass) { - - DeployBeanInfo info = createDeployBeanInfo(beanClass); - readDeployAssociations(info); - - Integer key = getUniqueHash(info.getDescriptor()); - - return new BeanDescriptor(this, typeManager, info.getDescriptor(), key.toString()); - } - - private void registerBeanDescriptor(BeanDescriptor desc) { - descMap.put(desc.getBeanType().getName(), desc); - } - - /** - * Read deployment information for all the embedded beans. - */ - private void readEmbeddedDeployment() { - - ArrayList> embeddedClasses = bootupClasses.getEmbeddables(); - for (int i = 0; i < embeddedClasses.size(); i++) { - Class cls = embeddedClasses.get(i); - if (logger.isLoggable(Level.FINER)) { - String msg = "load deployinfo for embeddable:" + cls.getName(); - logger.finer(msg); - } - BeanDescriptor embDesc = createEmbedded(cls); - registerBeanDescriptor(embDesc); - } - } - - /** - * Read the initial deployment information for the entities. - *

- * This stops short of reading relationship meta data until after the - * BeanTables have all been created. - *

- */ - private void readEntityDeploymentInitial() { - - ArrayList> entityClasses = bootupClasses.getEntities(); - - for (Class entityClass : entityClasses) { - DeployBeanInfo info = createDeployBeanInfo(entityClass); - deplyInfoMap.put(entityClass, info); - } - } - - /** - * Create the BeanTable information which has the base table and id. - *

- * This is determined prior to resolving relationship information. - *

- */ - private void readEntityBeanTable() { - - Iterator> it = deplyInfoMap.values().iterator(); - while (it.hasNext()) { - DeployBeanInfo info = it.next(); - BeanTable beanTable = createBeanTable(info); - beanTableMap.put(beanTable.getBeanType(), beanTable); - } - } - - /** - * Create the BeanTable information which has the base table and id. - *

- * This is determined prior to resolving relationship information. - *

- */ - private void readEntityDeploymentAssociations() { - - Iterator> it = deplyInfoMap.values().iterator(); - while (it.hasNext()) { - DeployBeanInfo info = it.next(); - readDeployAssociations(info); - } - } - - private void readInheritedIdGenerators() { - - Iterator> it = deplyInfoMap.values().iterator(); - while (it.hasNext()) { - DeployBeanInfo info = it.next(); - DeployBeanDescriptor descriptor = info.getDescriptor(); - InheritInfo inheritInfo = descriptor.getInheritInfo(); - if (inheritInfo != null && !inheritInfo.isRoot()) { - DeployBeanInfo rootBeanInfo = deplyInfoMap.get(inheritInfo.getRoot().getType()); - IdGenerator rootIdGen = rootBeanInfo.getDescriptor().getIdGenerator(); - if (rootIdGen != null) { - descriptor.setIdGenerator(rootIdGen); - } - } - } - } - - /** - * Create the BeanTable from the deployment information gathered so far. - */ - private BeanTable createBeanTable(DeployBeanInfo info) { - - DeployBeanDescriptor deployDescriptor = info.getDescriptor(); - DeployBeanTable beanTable = deployDescriptor.createDeployBeanTable(); - return new BeanTable(beanTable, this); - } - - /** - * Parse the named Raw Sql queries using BeanDescriptor. - */ - private void readRawSqlQueries() { - - for (DeployBeanInfo info : deplyInfoMap.values()) { - - DeployBeanDescriptor deployDesc = info.getDescriptor(); - BeanDescriptor desc = getBeanDescriptor(deployDesc.getBeanType()); - - for (DRawSqlMeta rawSqlMeta : deployDesc.getRawSqlMeta()) { - if (rawSqlMeta.getQuery() == null) { - - } else { - DeployNamedQuery nq = new DRawSqlSelectBuilder(namingConvention, desc, rawSqlMeta).parse(); - desc.addNamedQuery(nq); - } - } - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private void readEntityRelationships() { - - // We only perform 'circular' checks etc after we have - // all the DeployBeanDescriptors created and in the map. - - for (DeployBeanInfo info : deplyInfoMap.values()) { - checkMappedBy(info); - } - - for (DeployBeanInfo info : deplyInfoMap.values()) { - secondaryPropsJoins(info); - } - - for (DeployBeanInfo info : deplyInfoMap.values()) { - DeployBeanDescriptor deployBeanDescriptor = info.getDescriptor(); - Integer key = getUniqueHash(deployBeanDescriptor); - registerBeanDescriptor(new BeanDescriptor(this, typeManager, info.getDescriptor(), key.toString())); - } - } - - private Integer getUniqueHash(DeployBeanDescriptor deployBeanDescriptor) { - - int hashCode = deployBeanDescriptor.getFullName().hashCode(); - - for (int i = 0; i < 100000; i++) { - Integer key = Integer.valueOf(hashCode + i); - if (!descriptorUniqueIds.contains(key)) { - return key; - } - } - throw new RuntimeException("Failed to generate a unique hash for " + deployBeanDescriptor.getFullName()); - } - - private void secondaryPropsJoins(DeployBeanInfo info) { - - DeployBeanDescriptor descriptor = info.getDescriptor(); - for (DeployBeanProperty prop : descriptor.propertiesBase()) { - if (prop.isSecondaryTable()) { - String tableName = prop.getSecondaryTable(); - // find a join to that table... - DeployBeanPropertyAssocOne assocOne = descriptor.findJoinToTable(tableName); - if (assocOne == null) { - String msg = "Error with property " + prop.getFullBeanName() + ". Could not find a Relationship to table " + tableName - + ". Perhaps you could use a @JoinColumn instead."; - throw new RuntimeException(msg); - } - DeployTableJoin tableJoin = assocOne.getTableJoin(); - prop.setSecondaryTableJoin(tableJoin, assocOne.getName()); - } - } - } - - /** - * Check the mappedBy attributes for properties on this descriptor. - *

- * This will read join information defined on the 'owning/other' side of the - * relationship. It also does some extra work for unidirectional - * relationships. - *

- */ - private void checkMappedBy(DeployBeanInfo info) { - - for (DeployBeanPropertyAssocOne oneProp : info.getDescriptor().propertiesAssocOne()) { - if (!oneProp.isTransient()) { - if (oneProp.getMappedBy() != null) { - checkMappedByOneToOne(info, oneProp); - } - } - } - - for (DeployBeanPropertyAssocMany manyProp : info.getDescriptor().propertiesAssocMany()) { - if (!manyProp.isTransient()) { - if (manyProp.isManyToMany()) { - checkMappedByManyToMany(info, manyProp); - } else { - checkMappedByOneToMany(info, manyProp); - } - } - } - } - - private DeployBeanDescriptor getTargetDescriptor(DeployBeanPropertyAssoc prop) { - - Class targetType = prop.getTargetType(); - DeployBeanInfo info = deplyInfoMap.get(targetType); - if (info == null) { - String msg = "Can not find descriptor [" + targetType + "] for " + prop.getFullBeanName(); - throw new PersistenceException(msg); - } - - return info.getDescriptor(); - } - - /** - * Check that the many property has either an implied mappedBy property or - * mark it as unidirectional. - */ - private boolean findMappedBy(DeployBeanPropertyAssocMany prop) { - - // this is the entity bean type - that owns this property - Class owningType = prop.getOwningType(); - - Set matchSet = new HashSet(); - - // get the bean descriptor that holds the mappedBy property - DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); - List> ones = targetDesc.propertiesAssocOne(); - for (DeployBeanPropertyAssocOne possibleMappedBy : ones) { - Class possibleMappedByType = possibleMappedBy.getTargetType(); - if (possibleMappedByType.equals(owningType)) { - prop.setMappedBy(possibleMappedBy.getName()); - matchSet.add(possibleMappedBy.getName()); - } - } - - if (matchSet.size() == 0) { - // this is a unidirectional relationship - // ... that is no matching property on the 'detail' bean - return false; - } - if (matchSet.size() == 1) { - // all right with the world - return true; - } - if (matchSet.size() == 2) { - // try to find a match implicitly using a common naming convention - // e.g. List loggedBugs; ... search for "logged" in matchSet - String name = prop.getName(); - - // get the target type short name - String targetType = prop.getTargetType().getName(); - String shortTypeName = targetType.substring(targetType.lastIndexOf(".") + 1); - - // name includes (probably ends with) the target type short name? - int p = name.indexOf(shortTypeName); - if (p > 1) { - // ok, get the 'interesting' part of the property name - // That is the name without the target type - String searchName = name.substring(0, p).toLowerCase(); - - // search for this in the possible matches - Iterator it = matchSet.iterator(); - while (it.hasNext()) { - String possibleMappedBy = it.next(); - String possibleLower = possibleMappedBy.toLowerCase(); - if (possibleLower.indexOf(searchName) > -1) { - // we have a match.. - prop.setMappedBy(possibleMappedBy); - - String m = "Implicitly found mappedBy for " + targetDesc + "." + prop; - m += " by searching for [" + searchName + "] against " + matchSet; - logger.fine(m); - - return true; - } - } - - } - } - // multiple options so should specify mappedBy property - String msg = "Error on " + prop.getFullBeanName() + " missing mappedBy."; - msg += " There are [" + matchSet.size() + "] possible properties in " + targetDesc; - msg += " that this association could be mapped to. Please specify one using "; - msg += "the mappedBy attribute on @OneToMany."; - throw new PersistenceException(msg); - } - - /** - * A OneToMany with no matching mappedBy property in the target so must be - * unidirectional. - *

- * This means that inserts MUST cascade for this property. - *

- *

- * Create a "Shadow"/Unidirectional property on the target. It is used with - * inserts to set the foreign key value (e.g. inserts the foreign key value - * into the order_id column on the order_lines table). - *

- */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - private void makeUnidirectional(DeployBeanInfo info, DeployBeanPropertyAssocMany oneToMany) { - - DeployBeanDescriptor targetDesc = getTargetDescriptor(oneToMany); - - Class owningType = oneToMany.getOwningType(); - - if (!oneToMany.getCascadeInfo().isSave()) { - // The property MUST have persist cascading so that inserts work. - - Class targetType = oneToMany.getTargetType(); - String msg = "Error on " + oneToMany.getFullBeanName() + ". @OneToMany MUST have "; - msg += "Cascade.PERSIST or Cascade.ALL because this is a unidirectional "; - msg += "relationship. That is, there is no property of type " + owningType + " on " + targetType; - - throw new PersistenceException(msg); - } - - // mark this property as unidirectional - oneToMany.setUnidirectional(true); - - // create the 'shadow' unidirectional property - // which is put on the target descriptor - DeployBeanPropertyAssocOne unidirectional = new DeployBeanPropertyAssocOne(targetDesc, owningType); - unidirectional.setUndirectionalShadow(true); - unidirectional.setNullable(false); - unidirectional.setDbRead(true); - unidirectional.setDbInsertable(true); - unidirectional.setDbUpdateable(false); - - targetDesc.setUnidirectional(unidirectional); - - // specify table and table alias... - BeanTable beanTable = getBeanTable(owningType); - unidirectional.setBeanTable(beanTable); - unidirectional.setName(beanTable.getBaseTable()); - - info.setBeanJoinType(unidirectional, true); - - // define the TableJoin - DeployTableJoin oneToManyJoin = oneToMany.getTableJoin(); - if (!oneToManyJoin.hasJoinColumns()) { - throw new RuntimeException("No join columns"); - } - - // inverse of the oneToManyJoin - DeployTableJoin unidirectionalJoin = unidirectional.getTableJoin(); - unidirectionalJoin.setColumns(oneToManyJoin.columns(), true); - - } - - private void checkMappedByOneToOne(DeployBeanInfo info, DeployBeanPropertyAssocOne prop) { - - // check that the mappedBy property is valid and read - // its associated join information if it is available - String mappedBy = prop.getMappedBy(); - - // get the mappedBy property - DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); - DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy); - if (mappedProp == null) { - String m = "Error on " + prop.getFullBeanName(); - m += " Can not find mappedBy property [" + targetDesc + "." + mappedBy + "] "; - throw new PersistenceException(m); - } - - if (!(mappedProp instanceof DeployBeanPropertyAssocOne)) { - String m = "Error on " + prop.getFullBeanName(); - m += ". mappedBy property [" + targetDesc + "." + mappedBy + "]is not a OneToOne?"; - throw new PersistenceException(m); - } - - DeployBeanPropertyAssocOne mappedAssocOne = (DeployBeanPropertyAssocOne) mappedProp; - - if (!mappedAssocOne.isOneToOne()) { - String m = "Error on " + prop.getFullBeanName(); - m += ". mappedBy property [" + targetDesc + "." + mappedBy + "]is not a OneToOne?"; - throw new PersistenceException(m); - } - - DeployTableJoin tableJoin = prop.getTableJoin(); - if (!tableJoin.hasJoinColumns()) { - // define Join as the inverse of the mappedBy property - DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin(); - otherTableJoin.copyTo(tableJoin, true, tableJoin.getTable()); - } - } - - /** - * If the property has mappedBy set then do two things. Make sure the mappedBy - * property exists, and secondly read its join information. - *

- * We can use the join information from the mappedBy property and reverse it - * for using in the OneToMany direction. - *

- */ - private void checkMappedByOneToMany(DeployBeanInfo info, DeployBeanPropertyAssocMany prop) { - - // get the bean descriptor that holds the mappedBy property - - if (prop.getMappedBy() == null) { - if (!findMappedBy(prop)) { - makeUnidirectional(info, prop); - return; - } - } - - // check that the mappedBy property is valid and read - // its associated join information if it is available - String mappedBy = prop.getMappedBy(); - - // get the mappedBy property - DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); - DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy); - if (mappedProp == null) { - - String m = "Error on " + prop.getFullBeanName(); - m += " Can not find mappedBy property [" + mappedBy + "] "; - m += "in [" + targetDesc + "]"; - throw new PersistenceException(m); - } - - if (!(mappedProp instanceof DeployBeanPropertyAssocOne)) { - String m = "Error on " + prop.getFullBeanName(); - m += ". mappedBy property [" + mappedBy + "]is not a ManyToOne?"; - m += "in [" + targetDesc + "]"; - throw new PersistenceException(m); - } - - DeployBeanPropertyAssocOne mappedAssocOne = (DeployBeanPropertyAssocOne) mappedProp; - - DeployTableJoin tableJoin = prop.getTableJoin(); - if (!tableJoin.hasJoinColumns()) { - // define Join as the inverse of the mappedBy property - DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin(); - otherTableJoin.copyTo(tableJoin, true, tableJoin.getTable()); - } - - } - - /** - * For mappedBy copy the joins from the other side. - */ - private void checkMappedByManyToMany(DeployBeanInfo info, DeployBeanPropertyAssocMany prop) { - - // get the bean descriptor that holds the mappedBy property - String mappedBy = prop.getMappedBy(); - if (mappedBy == null) { - return; - } - - // get the mappedBy property - DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); - DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy); - - if (mappedProp == null) { - String m = "Error on " + prop.getFullBeanName(); - m += " Can not find mappedBy property [" + mappedBy + "] "; - m += "in [" + targetDesc + "]"; - throw new PersistenceException(m); - } - - if (!(mappedProp instanceof DeployBeanPropertyAssocMany)) { - String m = "Error on " + prop.getFullBeanName(); - m += ". mappedBy property [" + targetDesc + "." + mappedBy + "] is not a ManyToMany?"; - throw new PersistenceException(m); - } - - DeployBeanPropertyAssocMany mappedAssocMany = (DeployBeanPropertyAssocMany) mappedProp; - - if (!mappedAssocMany.isManyToMany()) { - String m = "Error on " + prop.getFullBeanName(); - m += ". mappedBy property [" + targetDesc + "." + mappedBy + "] is not a ManyToMany?"; - throw new PersistenceException(m); - } - - // define the relationships/joins on this side as the - // reverse of the other mappedBy side ... - - // DeployTableJoin mappedJoin = mappedAssocMany.getTableJoin(); - DeployTableJoin mappedIntJoin = mappedAssocMany.getIntersectionJoin(); - DeployTableJoin mappendInverseJoin = mappedAssocMany.getInverseJoin(); - - String intTableName = mappedIntJoin.getTable(); - - DeployTableJoin tableJoin = prop.getTableJoin(); - mappedIntJoin.copyTo(tableJoin, true, targetDesc.getBaseTable()); - - DeployTableJoin intJoin = new DeployTableJoin(); - mappendInverseJoin.copyTo(intJoin, false, intTableName); - prop.setIntersectionJoin(intJoin); - - DeployTableJoin inverseJoin = new DeployTableJoin(); - mappedIntJoin.copyTo(inverseJoin, false, intTableName); - prop.setInverseJoin(inverseJoin); - } - - private void setBeanControllerFinderListener(DeployBeanDescriptor descriptor) { - - Class beanType = descriptor.getBeanType(); - - persistControllerManager.addPersistControllers(descriptor); - persistListenerManager.addPersistListeners(descriptor); - beanQueryAdapterManager.addQueryAdapter(descriptor); - - BeanFinder beanFinder = beanFinderManager.getBeanFinder(beanType); - if (beanFinder != null) { - descriptor.setBeanFinder(beanFinder); - logger.fine("BeanFinder on[" + descriptor.getFullName() + "] " + beanFinder.getClass().getName()); - } - } - - /** - * Read the initial deployment information for a given bean type. - */ - private DeployBeanInfo createDeployBeanInfo(Class beanClass) { - - DeployBeanDescriptor desc = new DeployBeanDescriptor(beanClass); - - desc.setUpdateChangesOnly(updateChangesOnly); - - // set bean controller, finder and listener - setBeanControllerFinderListener(desc); - deplyInherit.process(desc); - - createProperties.createProperties(desc); - - DeployBeanInfo info = new DeployBeanInfo(deployUtil, desc); - - readAnnotations.readInitial(info); - return info; - } - - private void readDeployAssociations(DeployBeanInfo info) { - - DeployBeanDescriptor desc = info.getDescriptor(); - - readAnnotations.readAssociations(info, this); - - readXml(desc); - - if (!EntityType.ORM.equals(desc.getEntityType())) { - // not using base table - desc.setBaseTable(null); - } - - // mark transient properties - transientProperties.process(desc); - setScalarType(desc); - - if (!desc.isEmbedded()) { - // Set IdGenerator or use DB Identity - setIdGeneration(desc); - - // find the appropriate default concurrency mode - setConcurrencyMode(desc); - } - - autoAddValidators(desc); - - // generate the byte code - createByteCode(desc); - } - - /** - * Set the Identity generation mechanism. - */ - private IdType setIdGeneration(DeployBeanDescriptor desc) { - - if (desc.propertiesId().size() == 0) { - // bean doen't have an Id property - if (!desc.isBaseTableType() || desc.getBeanFinder() != null) { - // using BeanFinder so perhaps valid without an id - } else { - // expecting an id property - logger.warning(Message.msg("deploy.nouid", desc.getFullName())); - } - return null; - } - - if (IdType.SEQUENCE.equals(desc.getIdType()) && !dbIdentity.isSupportsSequence()) { - // explicit sequence but not supported by the DatabasePlatform - logger.info("Explicit sequence on " + desc.getFullName() + " but not supported by DB Platform - ignored"); - desc.setIdType(null); - } - if (IdType.IDENTITY.equals(desc.getIdType()) && !dbIdentity.isSupportsIdentity()) { - // explicit identity but not supported by the DatabasePlatform - logger.info("Explicit Identity on " + desc.getFullName() + " but not supported by DB Platform - ignored"); - desc.setIdType(null); - } - - if (desc.getIdType() == null) { - // use the default. IDENTITY or SEQUENCE. - desc.setIdType(dbIdentity.getIdType()); - } - - if (IdType.GENERATOR.equals(desc.getIdType())) { - String genName = desc.getIdGeneratorName(); - if (UuidIdGenerator.AUTO_UUID.equals(genName)) { - desc.setIdGenerator(uuidIdGenerator); - return IdType.GENERATOR; - } - } - - if (desc.getBaseTable() == null) { - // no base table so not going to set Identity - // of sequence information - return null; - } - - if (IdType.IDENTITY.equals(desc.getIdType())) { - // used when getGeneratedKeys is not supported (SQL Server 2000) - String selectLastInsertedId = dbIdentity.getSelectLastInsertedId(desc.getBaseTable()); - desc.setSelectLastInsertedId(selectLastInsertedId); - return IdType.IDENTITY; - } - - String seqName = desc.getIdGeneratorName(); - if (seqName != null) { - logger.fine("explicit sequence " + seqName + " on " + desc.getFullName()); - } else { - String primaryKeyColumn = desc.getSinglePrimaryKeyColumn(); - // use namingConvention to define sequence name - seqName = namingConvention.getSequenceName(desc.getBaseTable(), primaryKeyColumn); - } - - // create the sequence based IdGenerator - IdGenerator seqIdGen = createSequenceIdGenerator(seqName); - desc.setIdGenerator(seqIdGen); - - return IdType.SEQUENCE; - } - - private IdGenerator createSequenceIdGenerator(String seqName) { - return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, seqName, dbSequenceBatchSize); - } - - private void createByteCode(DeployBeanDescriptor deploy) { - - // check to see if the bean supports EntityBean interface - // generate a subclass if required - setEntityBeanClass(deploy); - - // use Code generation or Standard reflection to support - // getter and setter methods - setBeanReflect(deploy); - } - - /** - * Add Length and NotNull validators based on Column annotation etc. - */ - private void autoAddValidators(DeployBeanDescriptor deployDesc) { - - for (DeployBeanProperty prop : deployDesc.propertiesBase()) { - autoAddValidators(prop); - } - } - - /** - * Add Length and NotNull validators based on Column annotation etc. - */ - private void autoAddValidators(DeployBeanProperty prop) { - - if (String.class.equals(prop.getPropertyType()) && prop.getDbLength() > 0) { - // check if the property already has the LengthValidator - if (!prop.containsValidatorType(LengthValidatorFactory.LengthValidator.class)) { - prop.addValidator(LengthValidatorFactory.create(0, prop.getDbLength())); - } - } - if (!prop.isNullable() && !prop.isId() && !prop.isGenerated()) { - // check if the property already has the NotNullValidator - if (!prop.containsValidatorType(NotNullValidatorFactory.NotNullValidator.class)) { - prop.addValidator(NotNullValidatorFactory.NOT_NULL); - } - } - } - - /** - * Set the Scalar Types on all the simple types. This is done AFTER transients - * have been identified. This is because a non-transient field MUST have a - * ScalarType. It is useful for transients to have ScalarTypes because then - * they can be used in a SqlSelect query. - *

- * Enums are treated a bit differently in that they always have a ScalarType - * as one is built for them. - *

- */ - private void setScalarType(DeployBeanDescriptor deployDesc) { - - Iterator it = deployDesc.propertiesAll(); - while (it.hasNext()) { - DeployBeanProperty prop = it.next(); - if (prop instanceof DeployBeanPropertyAssoc) { - - } else { - deployUtil.setScalarType(prop); - } - } - } - - private void readXml(DeployBeanDescriptor deployDesc) { - - List eXml = xmlConfig.findEntityXml(deployDesc.getFullName()); - readXmlRawSql(deployDesc, eXml); - - Dnode entityXml = deployOrmXml.findEntityDeploymentXml(deployDesc.getFullName()); - - if (entityXml != null) { - readXmlNamedQueries(deployDesc, entityXml); - readXmlSql(deployDesc, entityXml); - } - } - - /** - * Read sql-select (FUTURE: additionally sql-insert, sql-update, sql-delete). - * If found this entity bean is based on raw sql. - */ - private void readXmlSql(DeployBeanDescriptor deployDesc, Dnode entityXml) { - - List sqlSelectList = entityXml.findAll("sql-select", entityXml.getLevel() + 1); - for (int i = 0; i < sqlSelectList.size(); i++) { - Dnode sqlSelect = sqlSelectList.get(i); - readSqlSelect(deployDesc, sqlSelect); - } - } - - private String findContent(Dnode node, String nodeName) { - Dnode found = node.find(nodeName); - if (found != null) { - return found.getNodeContent(); - } else { - return null; - } - } - - private void readSqlSelect(DeployBeanDescriptor deployDesc, Dnode sqlSelect) { - - String name = sqlSelect.getStringAttr("name", "default"); - String extend = sqlSelect.getStringAttr("extend", null); - String queryDebug = sqlSelect.getStringAttr("debug", null); - boolean debug = (queryDebug != null && queryDebug.equalsIgnoreCase("true")); - - // the raw sql select - String query = findContent(sqlSelect, "query"); - String where = findContent(sqlSelect, "where"); - String having = findContent(sqlSelect, "having"); - String columnMapping = findContent(sqlSelect, "columnMapping"); - - DRawSqlMeta m = new DRawSqlMeta(name, extend, query, debug, where, having, columnMapping); - - deployDesc.add(m); - - } - - private void readXmlRawSql(DeployBeanDescriptor deployDesc, List entityXml) { - - List rawSqlQueries = xmlConfig.find(entityXml, "raw-sql"); - for (int i = 0; i < rawSqlQueries.size(); i++) { - Dnode rawSqlDnode = rawSqlQueries.get(i); - String name = rawSqlDnode.getAttribute("name"); - if (isEmpty(name)) { - throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " missing name attribute"); - } - Dnode queryNode = rawSqlDnode.find("query"); - if (queryNode == null) { - throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " missing query element"); - } - String sql = queryNode.getNodeContent(); - if (isEmpty(sql)) { - throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " has empty sql in the query element?"); - } - - List columnMappings = rawSqlDnode.findAll("columnMapping", 1); - - RawSqlBuilder rawSqlBuilder = RawSqlBuilder.parse(sql); - for (int j = 0; j < columnMappings.size(); j++) { - Dnode cm = columnMappings.get(j); - String column = cm.getAttribute("column"); - String property = cm.getAttribute("property"); - rawSqlBuilder.columnMapping(column, property); - } - RawSql rawSql = rawSqlBuilder.create(); - - DeployNamedQuery namedQuery = new DeployNamedQuery(name, rawSql); - deployDesc.add(namedQuery); - } - } - - private boolean isEmpty(String s) { - return s == null || s.trim().length() == 0; - } - - /** - * Read named queries for this bean type. - */ - private void readXmlNamedQueries(DeployBeanDescriptor deployDesc, Dnode entityXml) { - - // look for named-query... - List namedQueries = entityXml.findAll("named-query", 1); - - for (Dnode namedQueryXml : namedQueries) { - - String name = (String) namedQueryXml.getAttribute("name"); - Dnode query = namedQueryXml.find("query"); - if (query == null) { - logger.warning("orm.xml " + deployDesc.getFullName() + " named-query missing query element?"); - - } else { - String oql = query.getNodeContent(); - // TODO: QueryHints not read from xml yet - if (name == null || oql == null) { - logger.warning("orm.xml " + deployDesc.getFullName() + " named-query has no query content?"); - } else { - // add the named query - DeployNamedQuery q = new DeployNamedQuery(name, oql, null); - deployDesc.add(q); - } - } - } - } - - private BeanReflectFactory createReflectionFactory() { - - return new EnhanceBeanReflectFactory(); - } - - /** - * Set BeanReflect BeanReflectGetter and BeanReflectSetter properties. - *

- * This sets the implementation of constructing entity beans and the setting - * and getting of properties. It is generally faster to use code generation - * rather than reflection to do this. - *

- */ - private void setBeanReflect(DeployBeanDescriptor desc) { - - // Set the BeanReflectGetter and BeanReflectSetter that typically - // use generated code. NB: Due to Bug 166 so now doing this for - // abstract classes as well. - - Class beanType = desc.getBeanType(); - Class factType = desc.getFactoryType(); - - BeanReflect beanReflect = reflectFactory.create(beanType, factType); - desc.setBeanReflect(beanReflect); - - try { - Iterator it = desc.propertiesAll(); - while (it.hasNext()) { - DeployBeanProperty prop = it.next(); - String propName = prop.getName(); - - if (desc.isAbstract() || beanReflect.isVanillaOnly()) { - // use reflection in the case of imported abstract class - // with - // inheritance. Refer Bug 166 - prop.setGetter(ReflectGetter.create(prop)); - prop.setSetter(ReflectSetter.create(prop)); - - } else { - // use generated code for getting setting property values - BeanReflectGetter getter = beanReflect.getGetter(propName); - BeanReflectSetter setter = beanReflect.getSetter(propName); - prop.setGetter(getter); - prop.setSetter(setter); - if (getter == null) { - // should never happen - String m = "BeanReflectGetter for " + prop.getFullBeanName() + " was not found?"; - throw new RuntimeException(m); - } - } - - } - } catch (IllegalArgumentException e) { - Class superClass = desc.getBeanType().getSuperclass(); - String msg = "Error with [" + desc.getFullName() + "] I believe it is not enhanced but it's superClass [" + superClass + "] is?" - + " (You are not allowed to mix enhancement in a single inheritance hierarchy)"; - throw new PersistenceException(msg, e); - } - } - - /** - * DevNote: It is assumed that Embedded can contain version properties. It is - * also assumed that Embedded beans do NOT themselves contain Embedded beans - * which contain version properties. - */ - private void setConcurrencyMode(DeployBeanDescriptor desc) { - - if (!desc.getConcurrencyMode().equals(ConcurrencyMode.ALL)) { - // concurrency mode explicitly set during deployment - return; - } - - if (checkForVersionProperties(desc)) { - desc.setConcurrencyMode(ConcurrencyMode.VERSION); - } - } - - /** - * Search for version properties also including embedded beans. - */ - private boolean checkForVersionProperties(DeployBeanDescriptor desc) { - - boolean hasVersionProperty = false; - - List props = desc.propertiesBase(); - for (int i = 0; i < props.size(); i++) { - if (props.get(i).isVersionColumn()) { - hasVersionProperty = true; - } - } - - return hasVersionProperty; - } - - private boolean hasEntityBeanInterface(Class beanClass) { - - Class[] interfaces = beanClass.getInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - if (interfaces[i].equals(EntityBean.class)) { - return true; - } - } - return false; - } - - /** - * Test the bean type to see if it implements EntityBean interface already. - */ - private void setEntityBeanClass(DeployBeanDescriptor desc) { - - Class beanClass = desc.getBeanType(); - - if (desc.isAbstract()) { - if (hasEntityBeanInterface(beanClass)) { - checkEnhanced(desc, beanClass); - } else { - checkSubclass(desc, beanClass); - } - return; - } - try { - Object testBean = null; - try { - testBean = beanClass.newInstance(); - } catch (InstantiationException e) { - // expected when no default constructor - logger.fine("no default constructor on " + beanClass + " e:" + e); - } catch (IllegalAccessException e) { - // expected when no default constructor - logger.fine("no default constructor on " + beanClass + " e:" + e); - } - if (testBean instanceof EntityBean == false) { - checkSubclass(desc, beanClass); - - } else { - String className = beanClass.getName(); - try { - // check that it really is enhanced (rather than mixed - // enhancement) - String marker = ((EntityBean) testBean)._ebean_getMarker(); - if (!marker.equals(className)) { - String msg = "Error with [" + desc.getFullName() + "] It has not been enhanced but it's superClass [" - + beanClass.getSuperclass() + "] is?" + " (You are not allowed to mix enhancement in a single inheritance hierarchy)" - + " marker[" + marker + "] className[" + className + "]"; - throw new PersistenceException(msg); - } - } catch (AbstractMethodError e) { - throw new PersistenceException("Old Ebean v1.0 enhancement detected in Ebean v1.1 - please do a clean enhancement.", e); - } - - checkEnhanced(desc, beanClass); - } - - } catch (PersistenceException ex) { - throw ex; - - } catch (Exception ex) { - throw new PersistenceException(ex); - } - } - - private void checkEnhanced(DeployBeanDescriptor desc, Class beanClass) { - // the bean already implements EntityBean - checkInheritedClasses(true, beanClass); - - desc.setFactoryType(beanClass); - if (!beanClass.getName().startsWith("com.avaje.ebean.meta")) { - enhancedClassCount++; - } - } - - private void checkSubclass(DeployBeanDescriptor desc, Class beanClass) { - - checkInheritedClasses(false, beanClass); - desc.checkReadAndWriteMethods(); - - EntityType entityType = desc.getEntityType(); - if (EntityType.XMLELEMENT.equals(entityType)) { - desc.setFactoryType(beanClass); - - } else { - if (!allowSubclassing) { - throw new PersistenceException("This configuration does not allow entity subclassing [" + beanClass + "]"); - } - subclassClassCount++; - Class subClass = subClassManager.resolve(beanClass.getName()); - desc.setFactoryType(subClass); - subclassedEntities.add(desc.getName()); - } - } - - /** - * Check that the inherited classes are the same as the entity bean (aka all - * enhanced or all dynamically subclassed). - */ - private void checkInheritedClasses(boolean ensureEnhanced, Class beanClass) { - Class superclass = beanClass.getSuperclass(); - if (Object.class.equals(superclass)) { - // we got to the top of the inheritance - return; - } - boolean isClassEnhanced = EntityBean.class.isAssignableFrom(superclass); - - if (ensureEnhanced != isClassEnhanced) { - String msg; - if (ensureEnhanced) { - msg = "Class [" + superclass + "] is not enhanced and [" + beanClass + "] is - (you can not mix!!)"; - } else { - msg = "Class [" + superclass + "] is enhanced and [" + beanClass + "] is not - (you can not mix!!)"; - } - throw new IllegalStateException(msg); - } - - // recursively continue up the inheritance hierarchy - checkInheritedClasses(ensureEnhanced, superclass); - } - - /** - * Comparator to sort the BeanDescriptors by name. - */ - private static final class BeanDescComparator implements Comparator>, Serializable { - - private static final long serialVersionUID = 1L; - - public int compare(BeanDescriptor o1, BeanDescriptor o2) { - - return o1.getName().compareTo(o2.getName()); - } - } -} +package com.avaje.ebeaninternal.server.deploy; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; +import javax.sql.DataSource; + +import com.avaje.ebean.BackgroundExecutor; +import com.avaje.ebean.RawSql; +import com.avaje.ebean.RawSqlBuilder; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.cache.ServerCacheManager; +import com.avaje.ebean.config.EncryptKey; +import com.avaje.ebean.config.EncryptKeyManager; +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebean.config.NamingConvention; +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.config.dbplatform.DbIdentity; +import com.avaje.ebean.config.dbplatform.IdGenerator; +import com.avaje.ebean.config.dbplatform.IdType; +import com.avaje.ebean.event.BeanFinder; +import com.avaje.ebean.validation.factory.LengthValidatorFactory; +import com.avaje.ebean.validation.factory.NotNullValidatorFactory; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.TransactionEventTable; +import com.avaje.ebeaninternal.server.core.BootupClasses; +import com.avaje.ebeaninternal.server.core.ConcurrencyMode; +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.core.InternalConfiguration; +import com.avaje.ebeaninternal.server.core.Message; +import com.avaje.ebeaninternal.server.core.XmlConfig; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; +import com.avaje.ebeaninternal.server.deploy.id.IdBinder; +import com.avaje.ebeaninternal.server.deploy.id.IdBinderEmbedded; +import com.avaje.ebeaninternal.server.deploy.id.IdBinderFactory; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable; +import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; +import com.avaje.ebeaninternal.server.deploy.parse.DeployBeanInfo; +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.deploy.parse.ReadAnnotations; +import com.avaje.ebeaninternal.server.deploy.parse.TransientProperties; +import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator; +import com.avaje.ebeaninternal.server.lib.util.Dnode; +import com.avaje.ebeaninternal.server.reflect.BeanReflect; +import com.avaje.ebeaninternal.server.reflect.BeanReflectFactory; +import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter; +import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter; +import com.avaje.ebeaninternal.server.reflect.EnhanceBeanReflectFactory; +import com.avaje.ebeaninternal.server.subclass.SubClassManager; +import com.avaje.ebeaninternal.server.subclass.SubClassUtil; +import com.avaje.ebeaninternal.server.type.TypeManager; + +/** + * Creates BeanDescriptors. + */ +public class BeanDescriptorManager implements BeanDescriptorMap { + + private static final Logger logger = Logger.getLogger(BeanDescriptorManager.class.getName()); + + private static final BeanDescComparator beanDescComparator = new BeanDescComparator(); + + private final ReadAnnotations readAnnotations = new ReadAnnotations(); + + private final TransientProperties transientProperties; + + /** + * Helper to derive inheritance information. + */ + private final DeployInherit deplyInherit; + + private final BeanReflectFactory reflectFactory; + + private final DeployUtil deployUtil; + + private final TypeManager typeManager; + + private final PersistControllerManager persistControllerManager; + + private final BeanFinderManager beanFinderManager; + + private final PersistListenerManager persistListenerManager; + + private final BeanQueryAdapterManager beanQueryAdapterManager; + + private final SubClassManager subClassManager; + + private final NamingConvention namingConvention; + + private final DeployCreateProperties createProperties; + + private final DeployOrmXml deployOrmXml; + + private final BeanManagerFactory beanManagerFactory; + + private int enhancedClassCount; + private int subclassClassCount; + private final HashSet subclassedEntities = new HashSet(); + + private final boolean updateChangesOnly; + + private final BootupClasses bootupClasses; + + private final String serverName; + + private Map, DeployBeanInfo> deplyInfoMap = new HashMap, DeployBeanInfo>(); + + private final Map, BeanTable> beanTableMap = new HashMap, BeanTable>(); + + private final Map> descMap = new HashMap>(); + private final Map> idDescMap = new HashMap>(); + + private final Map> beanManagerMap = new HashMap>(); + + private final Map>> tableToDescMap = new HashMap>>(); + + private List> immutableDescriptorList; + + private final Set descriptorUniqueIds = new HashSet(); + + private final DbIdentity dbIdentity; + + private final DataSource dataSource; + + private final DatabasePlatform databasePlatform; + + private final UuidIdGenerator uuidIdGenerator = new UuidIdGenerator(); + + private final ServerCacheManager cacheManager; + + private final BackgroundExecutor backgroundExecutor; + + private final int dbSequenceBatchSize; + + private final EncryptKeyManager encryptKeyManager; + + private final IdBinderFactory idBinderFactory; + + private final XmlConfig xmlConfig; + + private final boolean allowSubclassing; + + /** + * Create for a given database dbConfig. + */ + public BeanDescriptorManager(InternalConfiguration config) { + + this.serverName = InternString.intern(config.getServerConfig().getName()); + this.cacheManager = config.getCacheManager(); + this.xmlConfig = config.getXmlConfig(); + this.dbSequenceBatchSize = config.getServerConfig().getDatabaseSequenceBatchSize(); + this.backgroundExecutor = config.getBackgroundExecutor(); + this.dataSource = config.getServerConfig().getDataSource(); + this.encryptKeyManager = config.getServerConfig().getEncryptKeyManager(); + this.databasePlatform = config.getServerConfig().getDatabasePlatform(); + this.idBinderFactory = new IdBinderFactory(databasePlatform.isIdInExpandedForm()); + + this.bootupClasses = config.getBootupClasses(); + this.createProperties = config.getDeployCreateProperties(); + this.subClassManager = config.getSubClassManager(); + this.typeManager = config.getTypeManager(); + this.namingConvention = config.getServerConfig().getNamingConvention(); + this.dbIdentity = config.getDatabasePlatform().getDbIdentity(); + this.deplyInherit = config.getDeployInherit(); + this.deployOrmXml = config.getDeployOrmXml(); + this.deployUtil = config.getDeployUtil(); + + this.beanManagerFactory = new BeanManagerFactory(config.getServerConfig(), config.getDatabasePlatform()); + + this.updateChangesOnly = config.getServerConfig().isUpdateChangesOnly(); + + this.persistControllerManager = new PersistControllerManager(bootupClasses); + this.persistListenerManager = new PersistListenerManager(bootupClasses); + this.beanQueryAdapterManager = new BeanQueryAdapterManager(bootupClasses); + + this.beanFinderManager = new DefaultBeanFinderManager(); + + this.reflectFactory = createReflectionFactory(); + this.transientProperties = new TransientProperties(); + this.allowSubclassing = config.getServerConfig().isAllowSubclassing(); + } + + public BeanDescriptor getBeanDescriptorById(String descriptorId) { + return idDescMap.get(descriptorId); + } + + @SuppressWarnings("unchecked") + public BeanDescriptor getBeanDescriptor(Class entityType) { + + // remove $$EntityBean stuff + String className = SubClassUtil.getSuperClassName(entityType.getName()); + return (BeanDescriptor) descMap.get(className); + } + + @SuppressWarnings("unchecked") + public BeanDescriptor getBeanDescriptor(String entityClassName) { + + // remove $$EntityBean stuff + entityClassName = SubClassUtil.getSuperClassName(entityClassName); + return (BeanDescriptor) descMap.get(entityClassName); + } + + public String getServerName() { + return serverName; + } + + public ServerCacheManager getCacheManager() { + return cacheManager; + } + + public NamingConvention getNamingConvention() { + return namingConvention; + } + + /** + * Set the internal EbeanServer instance to all BeanDescriptors. + */ + public void setEbeanServer(SpiEbeanServer internalEbean) { + for (BeanDescriptor desc : immutableDescriptorList) { + desc.setEbeanServer(internalEbean); + } + } + + public IdBinder createIdBinder(BeanProperty[] uids) { + return idBinderFactory.createIdBinder(uids); + } + + public void deploy() { + + try { + createListeners(); + readEmbeddedDeployment(); + readEntityDeploymentInitial(); + readEntityBeanTable(); + readEntityDeploymentAssociations(); + readInheritedIdGenerators(); + + // creates the BeanDescriptors + readEntityRelationships(); + readRawSqlQueries(); + + List> list = new ArrayList>(descMap.values()); + Collections.sort(list, beanDescComparator); + immutableDescriptorList = Collections.unmodifiableList(list); + + // put into map using the "desriptorId" (alternative to class name) + for (BeanDescriptor d : list) { + idDescMap.put(d.getDescriptorId(), d); + } + + initialiseAll(); + readForeignKeys(); + + readTableToDescriptor(); + + logStatus(); + + deplyInfoMap.clear(); + deplyInfoMap = null; + } catch (RuntimeException e) { + String msg = "Error in deployment"; + logger.log(Level.SEVERE, msg, e); + throw e; + } + } + + /** + * Return the Encrypt key given the table and column name. + */ + public EncryptKey getEncryptKey(String tableName, String columnName) { + return encryptKeyManager.getEncryptKey(tableName, columnName); + } + + /** + * For SQL based modifications we need to invalidate appropriate parts of the + * cache. + */ + public void cacheNotify(TransactionEventTable.TableIUD tableIUD) { + + List> list = getBeanDescriptors(tableIUD.getTableName()); + if (list != null) { + for (int i = 0; i < list.size(); i++) { + list.get(i).cacheNotify(tableIUD); + } + } + } + + /** + * Return the BeanDescriptors mapped to the table. + */ + public List> getBeanDescriptors(String tableName) { + return tableToDescMap.get(tableName.toLowerCase()); + } + + /** + * Build a map of table names to BeanDescriptors. + *

+ * This is generally used to maintain caches from table names. + *

+ */ + private void readTableToDescriptor() { + + for (BeanDescriptor desc : descMap.values()) { + String baseTable = desc.getBaseTable(); + if (baseTable == null) { + + } else { + baseTable = baseTable.toLowerCase(); + + List> list = tableToDescMap.get(baseTable); + if (list == null) { + list = new ArrayList>(1); + tableToDescMap.put(baseTable, list); + } + list.add(desc); + } + } + } + + private void readForeignKeys() { + + for (BeanDescriptor d : descMap.values()) { + d.initialiseFkeys(); + } + } + + /** + * Initialise all the BeanDescriptors. + *

+ * This occurs after all the BeanDescriptors have been created. This resolves + * circular relationships between BeanDescriptors. + *

+ *

+ * Also responsible for creating all the BeanManagers which contain the + * persister, listener etc. + *

+ */ + private void initialiseAll() { + + // now that all the BeanDescriptors are in their map + // we can initialise them which sorts out circular + // dependencies for OneToMany and ManyToOne etc + + // PASS 1: + // initialise the ID properties of all the beans + // first (as they are needed to initialise the + // associated properties in the second pass). + for (BeanDescriptor d : descMap.values()) { + d.initialiseId(); + } + + // PASS 2: + // now initialise all the inherit info + for (BeanDescriptor d : descMap.values()) { + d.initInheritInfo(); + } + + // PASS 3: + // now initialise all the associated properties + for (BeanDescriptor d : descMap.values()) { + d.initialiseOther(); + } + + // create BeanManager for each non-embedded entity bean + for (BeanDescriptor d : descMap.values()) { + if (!d.isEmbedded()) { + BeanManager m = beanManagerFactory.create(d); + beanManagerMap.put(d.getFullName(), m); + + checkForValidEmbeddedId(d); + } + } + } + + private void checkForValidEmbeddedId(BeanDescriptor d) { + IdBinder idBinder = d.getIdBinder(); + if (idBinder != null && idBinder instanceof IdBinderEmbedded) { + IdBinderEmbedded embId = (IdBinderEmbedded) idBinder; + BeanDescriptor idBeanDescriptor = embId.getIdBeanDescriptor(); + Class idType = idBeanDescriptor.getBeanType(); + try { + idType.getDeclaredMethod("hashCode", new Class[] {}); + idType.getDeclaredMethod("equals", new Class[] { Object.class }); + } catch (NoSuchMethodException e) { + checkMissingHashCodeOrEquals(e, idType, d.getBeanType()); + } + } + } + + private void checkMissingHashCodeOrEquals(Exception source, Class idType, Class beanType) { + + String msg = "SERIOUS ERROR: The hashCode() and equals() methods *MUST* be implemented "; + msg += "on Embedded bean " + idType + " as it is used as an Id for " + beanType; + + if (GlobalProperties.getBoolean("ebean.strict", true)) { + throw new PersistenceException(msg, source); + } else { + logger.log(Level.SEVERE, msg, source); + } + } + + /** + * Return an immutable list of all the BeanDescriptors. + */ + public List> getBeanDescriptorList() { + return immutableDescriptorList; + } + + public Map, BeanTable> getBeanTables() { + return beanTableMap; + } + + public BeanTable getBeanTable(Class type) { + return beanTableMap.get(type); + } + + public Map> getBeanDescriptors() { + return descMap; + } + + @SuppressWarnings("unchecked") + public BeanManager getBeanManager(Class entityType) { + + return (BeanManager) getBeanManager(entityType.getName()); + } + + public BeanManager getBeanManager(String beanClassName) { + + beanClassName = SubClassUtil.getSuperClassName(beanClassName); + return beanManagerMap.get(beanClassName); + } + + public DNativeQuery getNativeQuery(String name) { + return deployOrmXml.getNativeQuery(name); + } + + /** + * Create the BeanControllers, BeanFinders and BeanListeners. + */ + private void createListeners() { + + int qa = beanQueryAdapterManager.getRegisterCount(); + int cc = persistControllerManager.getRegisterCount(); + int lc = persistListenerManager.getRegisterCount(); + int fc = beanFinderManager.createBeanFinders(bootupClasses.getBeanFinders()); + + logger + .fine("BeanPersistControllers[" + cc + "] BeanFinders[" + fc + "] BeanPersistListeners[" + lc + "] BeanQueryAdapters[" + qa + "]"); + } + + /** + * Log Warning if mixing subclass and enhancement. + *

+ * If enhancement is used for some classes it is expected to be used for all + * and vice versa. + *

+ */ + private void logStatus() { + + String msg = "Entities enhanced[" + enhancedClassCount + "] subclassed[" + subclassClassCount + "]"; + logger.info(msg); + + if (enhancedClassCount > 0) { + if (subclassClassCount > 0) { + String subclassEntityNames = subclassedEntities.toString(); + + String m = "Mixing enhanced and subclassed entities. Subclassed classes:" + subclassEntityNames; + logger.warning(m); + } + } + } + + private BeanDescriptor createEmbedded(Class beanClass) { + + DeployBeanInfo info = createDeployBeanInfo(beanClass); + readDeployAssociations(info); + + Integer key = getUniqueHash(info.getDescriptor()); + + return new BeanDescriptor(this, typeManager, info.getDescriptor(), key.toString()); + } + + private void registerBeanDescriptor(BeanDescriptor desc) { + descMap.put(desc.getBeanType().getName(), desc); + } + + /** + * Read deployment information for all the embedded beans. + */ + private void readEmbeddedDeployment() { + + ArrayList> embeddedClasses = bootupClasses.getEmbeddables(); + for (int i = 0; i < embeddedClasses.size(); i++) { + Class cls = embeddedClasses.get(i); + if (logger.isLoggable(Level.FINER)) { + String msg = "load deployinfo for embeddable:" + cls.getName(); + logger.finer(msg); + } + BeanDescriptor embDesc = createEmbedded(cls); + registerBeanDescriptor(embDesc); + } + } + + /** + * Read the initial deployment information for the entities. + *

+ * This stops short of reading relationship meta data until after the + * BeanTables have all been created. + *

+ */ + private void readEntityDeploymentInitial() { + + ArrayList> entityClasses = bootupClasses.getEntities(); + + for (Class entityClass : entityClasses) { + DeployBeanInfo info = createDeployBeanInfo(entityClass); + deplyInfoMap.put(entityClass, info); + } + } + + /** + * Create the BeanTable information which has the base table and id. + *

+ * This is determined prior to resolving relationship information. + *

+ */ + private void readEntityBeanTable() { + + Iterator> it = deplyInfoMap.values().iterator(); + while (it.hasNext()) { + DeployBeanInfo info = it.next(); + BeanTable beanTable = createBeanTable(info); + beanTableMap.put(beanTable.getBeanType(), beanTable); + } + } + + /** + * Create the BeanTable information which has the base table and id. + *

+ * This is determined prior to resolving relationship information. + *

+ */ + private void readEntityDeploymentAssociations() { + + Iterator> it = deplyInfoMap.values().iterator(); + while (it.hasNext()) { + DeployBeanInfo info = it.next(); + readDeployAssociations(info); + } + } + + private void readInheritedIdGenerators() { + + Iterator> it = deplyInfoMap.values().iterator(); + while (it.hasNext()) { + DeployBeanInfo info = it.next(); + DeployBeanDescriptor descriptor = info.getDescriptor(); + InheritInfo inheritInfo = descriptor.getInheritInfo(); + if (inheritInfo != null && !inheritInfo.isRoot()) { + DeployBeanInfo rootBeanInfo = deplyInfoMap.get(inheritInfo.getRoot().getType()); + IdGenerator rootIdGen = rootBeanInfo.getDescriptor().getIdGenerator(); + if (rootIdGen != null) { + descriptor.setIdGenerator(rootIdGen); + } + } + } + } + + /** + * Create the BeanTable from the deployment information gathered so far. + */ + private BeanTable createBeanTable(DeployBeanInfo info) { + + DeployBeanDescriptor deployDescriptor = info.getDescriptor(); + DeployBeanTable beanTable = deployDescriptor.createDeployBeanTable(); + return new BeanTable(beanTable, this); + } + + /** + * Parse the named Raw Sql queries using BeanDescriptor. + */ + private void readRawSqlQueries() { + + for (DeployBeanInfo info : deplyInfoMap.values()) { + + DeployBeanDescriptor deployDesc = info.getDescriptor(); + BeanDescriptor desc = getBeanDescriptor(deployDesc.getBeanType()); + + for (DRawSqlMeta rawSqlMeta : deployDesc.getRawSqlMeta()) { + if (rawSqlMeta.getQuery() == null) { + + } else { + DeployNamedQuery nq = new DRawSqlSelectBuilder(namingConvention, desc, rawSqlMeta).parse(); + desc.addNamedQuery(nq); + } + } + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void readEntityRelationships() { + + // We only perform 'circular' checks etc after we have + // all the DeployBeanDescriptors created and in the map. + + for (DeployBeanInfo info : deplyInfoMap.values()) { + checkMappedBy(info); + } + + for (DeployBeanInfo info : deplyInfoMap.values()) { + secondaryPropsJoins(info); + } + + for (DeployBeanInfo info : deplyInfoMap.values()) { + DeployBeanDescriptor deployBeanDescriptor = info.getDescriptor(); + Integer key = getUniqueHash(deployBeanDescriptor); + registerBeanDescriptor(new BeanDescriptor(this, typeManager, info.getDescriptor(), key.toString())); + } + } + + private Integer getUniqueHash(DeployBeanDescriptor deployBeanDescriptor) { + + int hashCode = deployBeanDescriptor.getFullName().hashCode(); + + for (int i = 0; i < 100000; i++) { + Integer key = Integer.valueOf(hashCode + i); + if (!descriptorUniqueIds.contains(key)) { + return key; + } + } + throw new RuntimeException("Failed to generate a unique hash for " + deployBeanDescriptor.getFullName()); + } + + private void secondaryPropsJoins(DeployBeanInfo info) { + + DeployBeanDescriptor descriptor = info.getDescriptor(); + for (DeployBeanProperty prop : descriptor.propertiesBase()) { + if (prop.isSecondaryTable()) { + String tableName = prop.getSecondaryTable(); + // find a join to that table... + DeployBeanPropertyAssocOne assocOne = descriptor.findJoinToTable(tableName); + if (assocOne == null) { + String msg = "Error with property " + prop.getFullBeanName() + ". Could not find a Relationship to table " + tableName + + ". Perhaps you could use a @JoinColumn instead."; + throw new RuntimeException(msg); + } + DeployTableJoin tableJoin = assocOne.getTableJoin(); + prop.setSecondaryTableJoin(tableJoin, assocOne.getName()); + } + } + } + + /** + * Check the mappedBy attributes for properties on this descriptor. + *

+ * This will read join information defined on the 'owning/other' side of the + * relationship. It also does some extra work for unidirectional + * relationships. + *

+ */ + private void checkMappedBy(DeployBeanInfo info) { + + for (DeployBeanPropertyAssocOne oneProp : info.getDescriptor().propertiesAssocOne()) { + if (!oneProp.isTransient()) { + if (oneProp.getMappedBy() != null) { + checkMappedByOneToOne(info, oneProp); + } + } + } + + for (DeployBeanPropertyAssocMany manyProp : info.getDescriptor().propertiesAssocMany()) { + if (!manyProp.isTransient()) { + if (manyProp.isManyToMany()) { + checkMappedByManyToMany(info, manyProp); + } else { + checkMappedByOneToMany(info, manyProp); + } + } + } + } + + private DeployBeanDescriptor getTargetDescriptor(DeployBeanPropertyAssoc prop) { + + Class targetType = prop.getTargetType(); + DeployBeanInfo info = deplyInfoMap.get(targetType); + if (info == null) { + String msg = "Can not find descriptor [" + targetType + "] for " + prop.getFullBeanName(); + throw new PersistenceException(msg); + } + + return info.getDescriptor(); + } + + /** + * Check that the many property has either an implied mappedBy property or + * mark it as unidirectional. + */ + private boolean findMappedBy(DeployBeanPropertyAssocMany prop) { + + // this is the entity bean type - that owns this property + Class owningType = prop.getOwningType(); + + Set matchSet = new HashSet(); + + // get the bean descriptor that holds the mappedBy property + DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); + List> ones = targetDesc.propertiesAssocOne(); + for (DeployBeanPropertyAssocOne possibleMappedBy : ones) { + Class possibleMappedByType = possibleMappedBy.getTargetType(); + if (possibleMappedByType.equals(owningType)) { + prop.setMappedBy(possibleMappedBy.getName()); + matchSet.add(possibleMappedBy.getName()); + } + } + + if (matchSet.size() == 0) { + // this is a unidirectional relationship + // ... that is no matching property on the 'detail' bean + return false; + } + if (matchSet.size() == 1) { + // all right with the world + return true; + } + if (matchSet.size() == 2) { + // try to find a match implicitly using a common naming convention + // e.g. List loggedBugs; ... search for "logged" in matchSet + String name = prop.getName(); + + // get the target type short name + String targetType = prop.getTargetType().getName(); + String shortTypeName = targetType.substring(targetType.lastIndexOf(".") + 1); + + // name includes (probably ends with) the target type short name? + int p = name.indexOf(shortTypeName); + if (p > 1) { + // ok, get the 'interesting' part of the property name + // That is the name without the target type + String searchName = name.substring(0, p).toLowerCase(); + + // search for this in the possible matches + Iterator it = matchSet.iterator(); + while (it.hasNext()) { + String possibleMappedBy = it.next(); + String possibleLower = possibleMappedBy.toLowerCase(); + if (possibleLower.indexOf(searchName) > -1) { + // we have a match.. + prop.setMappedBy(possibleMappedBy); + + String m = "Implicitly found mappedBy for " + targetDesc + "." + prop; + m += " by searching for [" + searchName + "] against " + matchSet; + logger.fine(m); + + return true; + } + } + + } + } + // multiple options so should specify mappedBy property + String msg = "Error on " + prop.getFullBeanName() + " missing mappedBy."; + msg += " There are [" + matchSet.size() + "] possible properties in " + targetDesc; + msg += " that this association could be mapped to. Please specify one using "; + msg += "the mappedBy attribute on @OneToMany."; + throw new PersistenceException(msg); + } + + /** + * A OneToMany with no matching mappedBy property in the target so must be + * unidirectional. + *

+ * This means that inserts MUST cascade for this property. + *

+ *

+ * Create a "Shadow"/Unidirectional property on the target. It is used with + * inserts to set the foreign key value (e.g. inserts the foreign key value + * into the order_id column on the order_lines table). + *

+ */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void makeUnidirectional(DeployBeanInfo info, DeployBeanPropertyAssocMany oneToMany) { + + DeployBeanDescriptor targetDesc = getTargetDescriptor(oneToMany); + + Class owningType = oneToMany.getOwningType(); + + if (!oneToMany.getCascadeInfo().isSave()) { + // The property MUST have persist cascading so that inserts work. + + Class targetType = oneToMany.getTargetType(); + String msg = "Error on " + oneToMany.getFullBeanName() + ". @OneToMany MUST have "; + msg += "Cascade.PERSIST or Cascade.ALL because this is a unidirectional "; + msg += "relationship. That is, there is no property of type " + owningType + " on " + targetType; + + throw new PersistenceException(msg); + } + + // mark this property as unidirectional + oneToMany.setUnidirectional(true); + + // create the 'shadow' unidirectional property + // which is put on the target descriptor + DeployBeanPropertyAssocOne unidirectional = new DeployBeanPropertyAssocOne(targetDesc, owningType); + unidirectional.setUndirectionalShadow(true); + unidirectional.setNullable(false); + unidirectional.setDbRead(true); + unidirectional.setDbInsertable(true); + unidirectional.setDbUpdateable(false); + + targetDesc.setUnidirectional(unidirectional); + + // specify table and table alias... + BeanTable beanTable = getBeanTable(owningType); + unidirectional.setBeanTable(beanTable); + unidirectional.setName(beanTable.getBaseTable()); + + info.setBeanJoinType(unidirectional, true); + + // define the TableJoin + DeployTableJoin oneToManyJoin = oneToMany.getTableJoin(); + if (!oneToManyJoin.hasJoinColumns()) { + throw new RuntimeException("No join columns"); + } + + // inverse of the oneToManyJoin + DeployTableJoin unidirectionalJoin = unidirectional.getTableJoin(); + unidirectionalJoin.setColumns(oneToManyJoin.columns(), true); + + } + + private void checkMappedByOneToOne(DeployBeanInfo info, DeployBeanPropertyAssocOne prop) { + + // check that the mappedBy property is valid and read + // its associated join information if it is available + String mappedBy = prop.getMappedBy(); + + // get the mappedBy property + DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); + DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy); + if (mappedProp == null) { + String m = "Error on " + prop.getFullBeanName(); + m += " Can not find mappedBy property [" + targetDesc + "." + mappedBy + "] "; + throw new PersistenceException(m); + } + + if (!(mappedProp instanceof DeployBeanPropertyAssocOne)) { + String m = "Error on " + prop.getFullBeanName(); + m += ". mappedBy property [" + targetDesc + "." + mappedBy + "]is not a OneToOne?"; + throw new PersistenceException(m); + } + + DeployBeanPropertyAssocOne mappedAssocOne = (DeployBeanPropertyAssocOne) mappedProp; + + if (!mappedAssocOne.isOneToOne()) { + String m = "Error on " + prop.getFullBeanName(); + m += ". mappedBy property [" + targetDesc + "." + mappedBy + "]is not a OneToOne?"; + throw new PersistenceException(m); + } + + DeployTableJoin tableJoin = prop.getTableJoin(); + if (!tableJoin.hasJoinColumns()) { + // define Join as the inverse of the mappedBy property + DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin(); + otherTableJoin.copyTo(tableJoin, true, tableJoin.getTable()); + } + } + + /** + * If the property has mappedBy set then do two things. Make sure the mappedBy + * property exists, and secondly read its join information. + *

+ * We can use the join information from the mappedBy property and reverse it + * for using in the OneToMany direction. + *

+ */ + private void checkMappedByOneToMany(DeployBeanInfo info, DeployBeanPropertyAssocMany prop) { + + // get the bean descriptor that holds the mappedBy property + + if (prop.getMappedBy() == null) { + if (!findMappedBy(prop)) { + makeUnidirectional(info, prop); + return; + } + } + + // check that the mappedBy property is valid and read + // its associated join information if it is available + String mappedBy = prop.getMappedBy(); + + // get the mappedBy property + DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); + DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy); + if (mappedProp == null) { + + String m = "Error on " + prop.getFullBeanName(); + m += " Can not find mappedBy property [" + mappedBy + "] "; + m += "in [" + targetDesc + "]"; + throw new PersistenceException(m); + } + + if (!(mappedProp instanceof DeployBeanPropertyAssocOne)) { + String m = "Error on " + prop.getFullBeanName(); + m += ". mappedBy property [" + mappedBy + "]is not a ManyToOne?"; + m += "in [" + targetDesc + "]"; + throw new PersistenceException(m); + } + + DeployBeanPropertyAssocOne mappedAssocOne = (DeployBeanPropertyAssocOne) mappedProp; + + DeployTableJoin tableJoin = prop.getTableJoin(); + if (!tableJoin.hasJoinColumns()) { + // define Join as the inverse of the mappedBy property + DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin(); + otherTableJoin.copyTo(tableJoin, true, tableJoin.getTable()); + } + + } + + /** + * For mappedBy copy the joins from the other side. + */ + private void checkMappedByManyToMany(DeployBeanInfo info, DeployBeanPropertyAssocMany prop) { + + // get the bean descriptor that holds the mappedBy property + String mappedBy = prop.getMappedBy(); + if (mappedBy == null) { + return; + } + + // get the mappedBy property + DeployBeanDescriptor targetDesc = getTargetDescriptor(prop); + DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy); + + if (mappedProp == null) { + String m = "Error on " + prop.getFullBeanName(); + m += " Can not find mappedBy property [" + mappedBy + "] "; + m += "in [" + targetDesc + "]"; + throw new PersistenceException(m); + } + + if (!(mappedProp instanceof DeployBeanPropertyAssocMany)) { + String m = "Error on " + prop.getFullBeanName(); + m += ". mappedBy property [" + targetDesc + "." + mappedBy + "] is not a ManyToMany?"; + throw new PersistenceException(m); + } + + DeployBeanPropertyAssocMany mappedAssocMany = (DeployBeanPropertyAssocMany) mappedProp; + + if (!mappedAssocMany.isManyToMany()) { + String m = "Error on " + prop.getFullBeanName(); + m += ". mappedBy property [" + targetDesc + "." + mappedBy + "] is not a ManyToMany?"; + throw new PersistenceException(m); + } + + // define the relationships/joins on this side as the + // reverse of the other mappedBy side ... + + // DeployTableJoin mappedJoin = mappedAssocMany.getTableJoin(); + DeployTableJoin mappedIntJoin = mappedAssocMany.getIntersectionJoin(); + DeployTableJoin mappendInverseJoin = mappedAssocMany.getInverseJoin(); + + String intTableName = mappedIntJoin.getTable(); + + DeployTableJoin tableJoin = prop.getTableJoin(); + mappedIntJoin.copyTo(tableJoin, true, targetDesc.getBaseTable()); + + DeployTableJoin intJoin = new DeployTableJoin(); + mappendInverseJoin.copyTo(intJoin, false, intTableName); + prop.setIntersectionJoin(intJoin); + + DeployTableJoin inverseJoin = new DeployTableJoin(); + mappedIntJoin.copyTo(inverseJoin, false, intTableName); + prop.setInverseJoin(inverseJoin); + } + + private void setBeanControllerFinderListener(DeployBeanDescriptor descriptor) { + + Class beanType = descriptor.getBeanType(); + + persistControllerManager.addPersistControllers(descriptor); + persistListenerManager.addPersistListeners(descriptor); + beanQueryAdapterManager.addQueryAdapter(descriptor); + + BeanFinder beanFinder = beanFinderManager.getBeanFinder(beanType); + if (beanFinder != null) { + descriptor.setBeanFinder(beanFinder); + logger.fine("BeanFinder on[" + descriptor.getFullName() + "] " + beanFinder.getClass().getName()); + } + } + + /** + * Read the initial deployment information for a given bean type. + */ + private DeployBeanInfo createDeployBeanInfo(Class beanClass) { + + DeployBeanDescriptor desc = new DeployBeanDescriptor(beanClass); + + desc.setUpdateChangesOnly(updateChangesOnly); + + // set bean controller, finder and listener + setBeanControllerFinderListener(desc); + deplyInherit.process(desc); + + createProperties.createProperties(desc); + + DeployBeanInfo info = new DeployBeanInfo(deployUtil, desc); + + readAnnotations.readInitial(info); + return info; + } + + private void readDeployAssociations(DeployBeanInfo info) { + + DeployBeanDescriptor desc = info.getDescriptor(); + + readAnnotations.readAssociations(info, this); + + readXml(desc); + + if (!EntityType.ORM.equals(desc.getEntityType())) { + // not using base table + desc.setBaseTable(null); + } + + // mark transient properties + transientProperties.process(desc); + setScalarType(desc); + + if (!desc.isEmbedded()) { + // Set IdGenerator or use DB Identity + setIdGeneration(desc); + + // find the appropriate default concurrency mode + setConcurrencyMode(desc); + } + + autoAddValidators(desc); + + // generate the byte code + createByteCode(desc); + } + + /** + * Set the Identity generation mechanism. + */ + private IdType setIdGeneration(DeployBeanDescriptor desc) { + + if (desc.propertiesId().size() == 0) { + // bean doen't have an Id property + if (!desc.isBaseTableType() || desc.getBeanFinder() != null) { + // using BeanFinder so perhaps valid without an id + } else { + // expecting an id property + logger.warning(Message.msg("deploy.nouid", desc.getFullName())); + } + return null; + } + + if (IdType.SEQUENCE.equals(desc.getIdType()) && !dbIdentity.isSupportsSequence()) { + // explicit sequence but not supported by the DatabasePlatform + logger.info("Explicit sequence on " + desc.getFullName() + " but not supported by DB Platform - ignored"); + desc.setIdType(null); + } + if (IdType.IDENTITY.equals(desc.getIdType()) && !dbIdentity.isSupportsIdentity()) { + // explicit identity but not supported by the DatabasePlatform + logger.info("Explicit Identity on " + desc.getFullName() + " but not supported by DB Platform - ignored"); + desc.setIdType(null); + } + + if (desc.getIdType() == null) { + // use the default. IDENTITY or SEQUENCE. + desc.setIdType(dbIdentity.getIdType()); + } + + if (IdType.GENERATOR.equals(desc.getIdType())) { + String genName = desc.getIdGeneratorName(); + if (UuidIdGenerator.AUTO_UUID.equals(genName)) { + desc.setIdGenerator(uuidIdGenerator); + return IdType.GENERATOR; + } + } + + if (desc.getBaseTable() == null) { + // no base table so not going to set Identity + // of sequence information + return null; + } + + if (IdType.IDENTITY.equals(desc.getIdType())) { + // used when getGeneratedKeys is not supported (SQL Server 2000) + String selectLastInsertedId = dbIdentity.getSelectLastInsertedId(desc.getBaseTable()); + desc.setSelectLastInsertedId(selectLastInsertedId); + return IdType.IDENTITY; + } + + String seqName = desc.getIdGeneratorName(); + if (seqName != null) { + logger.fine("explicit sequence " + seqName + " on " + desc.getFullName()); + } else { + String primaryKeyColumn = desc.getSinglePrimaryKeyColumn(); + // use namingConvention to define sequence name + seqName = namingConvention.getSequenceName(desc.getBaseTable(), primaryKeyColumn); + } + + // create the sequence based IdGenerator + IdGenerator seqIdGen = createSequenceIdGenerator(seqName); + desc.setIdGenerator(seqIdGen); + + return IdType.SEQUENCE; + } + + private IdGenerator createSequenceIdGenerator(String seqName) { + return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, seqName, dbSequenceBatchSize); + } + + private void createByteCode(DeployBeanDescriptor deploy) { + + // check to see if the bean supports EntityBean interface + // generate a subclass if required + setEntityBeanClass(deploy); + + // use Code generation or Standard reflection to support + // getter and setter methods + setBeanReflect(deploy); + } + + /** + * Add Length and NotNull validators based on Column annotation etc. + */ + private void autoAddValidators(DeployBeanDescriptor deployDesc) { + + for (DeployBeanProperty prop : deployDesc.propertiesBase()) { + autoAddValidators(prop); + } + } + + /** + * Add Length and NotNull validators based on Column annotation etc. + */ + private void autoAddValidators(DeployBeanProperty prop) { + + if (String.class.equals(prop.getPropertyType()) && prop.getDbLength() > 0) { + // check if the property already has the LengthValidator + if (!prop.containsValidatorType(LengthValidatorFactory.LengthValidator.class)) { + prop.addValidator(LengthValidatorFactory.create(0, prop.getDbLength())); + } + } + if (!prop.isNullable() && !prop.isId() && !prop.isGenerated()) { + // check if the property already has the NotNullValidator + if (!prop.containsValidatorType(NotNullValidatorFactory.NotNullValidator.class)) { + prop.addValidator(NotNullValidatorFactory.NOT_NULL); + } + } + } + + /** + * Set the Scalar Types on all the simple types. This is done AFTER transients + * have been identified. This is because a non-transient field MUST have a + * ScalarType. It is useful for transients to have ScalarTypes because then + * they can be used in a SqlSelect query. + *

+ * Enums are treated a bit differently in that they always have a ScalarType + * as one is built for them. + *

+ */ + private void setScalarType(DeployBeanDescriptor deployDesc) { + + Iterator it = deployDesc.propertiesAll(); + while (it.hasNext()) { + DeployBeanProperty prop = it.next(); + if (prop instanceof DeployBeanPropertyAssoc) { + + } else { + deployUtil.setScalarType(prop); + } + } + } + + private void readXml(DeployBeanDescriptor deployDesc) { + + List eXml = xmlConfig.findEntityXml(deployDesc.getFullName()); + readXmlRawSql(deployDesc, eXml); + + Dnode entityXml = deployOrmXml.findEntityDeploymentXml(deployDesc.getFullName()); + + if (entityXml != null) { + readXmlNamedQueries(deployDesc, entityXml); + readXmlSql(deployDesc, entityXml); + } + } + + /** + * Read sql-select (FUTURE: additionally sql-insert, sql-update, sql-delete). + * If found this entity bean is based on raw sql. + */ + private void readXmlSql(DeployBeanDescriptor deployDesc, Dnode entityXml) { + + List sqlSelectList = entityXml.findAll("sql-select", entityXml.getLevel() + 1); + for (int i = 0; i < sqlSelectList.size(); i++) { + Dnode sqlSelect = sqlSelectList.get(i); + readSqlSelect(deployDesc, sqlSelect); + } + } + + private String findContent(Dnode node, String nodeName) { + Dnode found = node.find(nodeName); + if (found != null) { + return found.getNodeContent(); + } else { + return null; + } + } + + private void readSqlSelect(DeployBeanDescriptor deployDesc, Dnode sqlSelect) { + + String name = sqlSelect.getStringAttr("name", "default"); + String extend = sqlSelect.getStringAttr("extend", null); + String queryDebug = sqlSelect.getStringAttr("debug", null); + boolean debug = (queryDebug != null && queryDebug.equalsIgnoreCase("true")); + + // the raw sql select + String query = findContent(sqlSelect, "query"); + String where = findContent(sqlSelect, "where"); + String having = findContent(sqlSelect, "having"); + String columnMapping = findContent(sqlSelect, "columnMapping"); + + DRawSqlMeta m = new DRawSqlMeta(name, extend, query, debug, where, having, columnMapping); + + deployDesc.add(m); + + } + + private void readXmlRawSql(DeployBeanDescriptor deployDesc, List entityXml) { + + List rawSqlQueries = xmlConfig.find(entityXml, "raw-sql"); + for (int i = 0; i < rawSqlQueries.size(); i++) { + Dnode rawSqlDnode = rawSqlQueries.get(i); + String name = rawSqlDnode.getAttribute("name"); + if (isEmpty(name)) { + throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " missing name attribute"); + } + Dnode queryNode = rawSqlDnode.find("query"); + if (queryNode == null) { + throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " missing query element"); + } + String sql = queryNode.getNodeContent(); + if (isEmpty(sql)) { + throw new IllegalStateException("raw-sql for " + deployDesc.getFullName() + " has empty sql in the query element?"); + } + + List columnMappings = rawSqlDnode.findAll("columnMapping", 1); + + RawSqlBuilder rawSqlBuilder = RawSqlBuilder.parse(sql); + for (int j = 0; j < columnMappings.size(); j++) { + Dnode cm = columnMappings.get(j); + String column = cm.getAttribute("column"); + String property = cm.getAttribute("property"); + rawSqlBuilder.columnMapping(column, property); + } + RawSql rawSql = rawSqlBuilder.create(); + + DeployNamedQuery namedQuery = new DeployNamedQuery(name, rawSql); + deployDesc.add(namedQuery); + } + } + + private boolean isEmpty(String s) { + return s == null || s.trim().length() == 0; + } + + /** + * Read named queries for this bean type. + */ + private void readXmlNamedQueries(DeployBeanDescriptor deployDesc, Dnode entityXml) { + + // look for named-query... + List namedQueries = entityXml.findAll("named-query", 1); + + for (Dnode namedQueryXml : namedQueries) { + + String name = (String) namedQueryXml.getAttribute("name"); + Dnode query = namedQueryXml.find("query"); + if (query == null) { + logger.warning("orm.xml " + deployDesc.getFullName() + " named-query missing query element?"); + + } else { + String oql = query.getNodeContent(); + // TODO: QueryHints not read from xml yet + if (name == null || oql == null) { + logger.warning("orm.xml " + deployDesc.getFullName() + " named-query has no query content?"); + } else { + // add the named query + DeployNamedQuery q = new DeployNamedQuery(name, oql, null); + deployDesc.add(q); + } + } + } + } + + private BeanReflectFactory createReflectionFactory() { + + return new EnhanceBeanReflectFactory(); + } + + /** + * Set BeanReflect BeanReflectGetter and BeanReflectSetter properties. + *

+ * This sets the implementation of constructing entity beans and the setting + * and getting of properties. It is generally faster to use code generation + * rather than reflection to do this. + *

+ */ + private void setBeanReflect(DeployBeanDescriptor desc) { + + // Set the BeanReflectGetter and BeanReflectSetter that typically + // use generated code. NB: Due to Bug 166 so now doing this for + // abstract classes as well. + + Class beanType = desc.getBeanType(); + Class factType = desc.getFactoryType(); + + BeanReflect beanReflect = reflectFactory.create(beanType, factType); + desc.setBeanReflect(beanReflect); + + try { + Iterator it = desc.propertiesAll(); + while (it.hasNext()) { + DeployBeanProperty prop = it.next(); + String propName = prop.getName(); + + if (desc.isAbstract() || beanReflect.isVanillaOnly()) { + // use reflection in the case of imported abstract class + // with + // inheritance. Refer Bug 166 + prop.setGetter(ReflectGetter.create(prop)); + prop.setSetter(ReflectSetter.create(prop)); + + } else { + // use generated code for getting setting property values + BeanReflectGetter getter = beanReflect.getGetter(propName); + BeanReflectSetter setter = beanReflect.getSetter(propName); + prop.setGetter(getter); + prop.setSetter(setter); + if (getter == null) { + // should never happen + String m = "BeanReflectGetter for " + prop.getFullBeanName() + " was not found?"; + throw new RuntimeException(m); + } + } + + } + } catch (IllegalArgumentException e) { + Class superClass = desc.getBeanType().getSuperclass(); + String msg = "Error with [" + desc.getFullName() + "] I believe it is not enhanced but it's superClass [" + superClass + "] is?" + + " (You are not allowed to mix enhancement in a single inheritance hierarchy)"; + throw new PersistenceException(msg, e); + } + } + + /** + * DevNote: It is assumed that Embedded can contain version properties. It is + * also assumed that Embedded beans do NOT themselves contain Embedded beans + * which contain version properties. + */ + private void setConcurrencyMode(DeployBeanDescriptor desc) { + + if (!desc.getConcurrencyMode().equals(ConcurrencyMode.ALL)) { + // concurrency mode explicitly set during deployment + return; + } + + if (checkForVersionProperties(desc)) { + desc.setConcurrencyMode(ConcurrencyMode.VERSION); + } + } + + /** + * Search for version properties also including embedded beans. + */ + private boolean checkForVersionProperties(DeployBeanDescriptor desc) { + + boolean hasVersionProperty = false; + + List props = desc.propertiesBase(); + for (int i = 0; i < props.size(); i++) { + if (props.get(i).isVersionColumn()) { + hasVersionProperty = true; + } + } + + return hasVersionProperty; + } + + private boolean hasEntityBeanInterface(Class beanClass) { + + Class[] interfaces = beanClass.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + if (interfaces[i].equals(EntityBean.class)) { + return true; + } + } + return false; + } + + /** + * Test the bean type to see if it implements EntityBean interface already. + */ + private void setEntityBeanClass(DeployBeanDescriptor desc) { + + Class beanClass = desc.getBeanType(); + + if (desc.isAbstract()) { + if (hasEntityBeanInterface(beanClass)) { + checkEnhanced(desc, beanClass); + } else { + checkSubclass(desc, beanClass); + } + return; + } + try { + Object testBean = null; + try { + testBean = beanClass.newInstance(); + } catch (InstantiationException e) { + // expected when no default constructor + logger.fine("no default constructor on " + beanClass + " e:" + e); + } catch (IllegalAccessException e) { + // expected when no default constructor + logger.fine("no default constructor on " + beanClass + " e:" + e); + } + if (testBean instanceof EntityBean == false) { + checkSubclass(desc, beanClass); + + } else { + String className = beanClass.getName(); + try { + // check that it really is enhanced (rather than mixed + // enhancement) + String marker = ((EntityBean) testBean)._ebean_getMarker(); + if (!marker.equals(className)) { + String msg = "Error with [" + desc.getFullName() + "] It has not been enhanced but it's superClass [" + + beanClass.getSuperclass() + "] is?" + " (You are not allowed to mix enhancement in a single inheritance hierarchy)" + + " marker[" + marker + "] className[" + className + "]"; + throw new PersistenceException(msg); + } + } catch (AbstractMethodError e) { + throw new PersistenceException("Old Ebean v1.0 enhancement detected in Ebean v1.1 - please do a clean enhancement.", e); + } + + checkEnhanced(desc, beanClass); + } + + } catch (PersistenceException ex) { + throw ex; + + } catch (Exception ex) { + throw new PersistenceException(ex); + } + } + + private void checkEnhanced(DeployBeanDescriptor desc, Class beanClass) { + // the bean already implements EntityBean + checkInheritedClasses(true, beanClass); + + desc.setFactoryType(beanClass); + if (!beanClass.getName().startsWith("com.avaje.ebean.meta")) { + enhancedClassCount++; + } + } + + private void checkSubclass(DeployBeanDescriptor desc, Class beanClass) { + + checkInheritedClasses(false, beanClass); + desc.checkReadAndWriteMethods(); + + EntityType entityType = desc.getEntityType(); + if (EntityType.XMLELEMENT.equals(entityType)) { + desc.setFactoryType(beanClass); + + } else { + if (!allowSubclassing) { + throw new PersistenceException("This configuration does not allow entity subclassing [" + beanClass + "]"); + } + subclassClassCount++; + Class subClass = subClassManager.resolve(beanClass.getName()); + desc.setFactoryType(subClass); + subclassedEntities.add(desc.getName()); + } + } + + /** + * Check that the inherited classes are the same as the entity bean (aka all + * enhanced or all dynamically subclassed). + */ + private void checkInheritedClasses(boolean ensureEnhanced, Class beanClass) { + Class superclass = beanClass.getSuperclass(); + if (Object.class.equals(superclass)) { + // we got to the top of the inheritance + return; + } + boolean isClassEnhanced = EntityBean.class.isAssignableFrom(superclass); + + if (ensureEnhanced != isClassEnhanced) { + String msg; + if (ensureEnhanced) { + msg = "Class [" + superclass + "] is not enhanced and [" + beanClass + "] is - (you can not mix!!)"; + } else { + msg = "Class [" + superclass + "] is enhanced and [" + beanClass + "] is not - (you can not mix!!)"; + } + throw new IllegalStateException(msg); + } + + // recursively continue up the inheritance hierarchy + checkInheritedClasses(ensureEnhanced, superclass); + } + + /** + * Comparator to sort the BeanDescriptors by name. + */ + private static final class BeanDescComparator implements Comparator>, Serializable { + + private static final long serialVersionUID = 1L; + + public int compare(BeanDescriptor o1, BeanDescriptor o2) { + + return o1.getName().compareTo(o2.getName()); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorMap.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorMap.java index 867cb1c99..73a197f5b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorMap.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorMap.java @@ -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. - *

- * Used during deployment of to resolve relationships between beans. - *

- */ -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 BeanDescriptor getBeanDescriptor(Class 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. + *

+ * Used during deployment of to resolve relationships between beans. + *

+ */ +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 BeanDescriptor getBeanDescriptor(Class entityType); + + /** + * Return the Encrypt key given the table and column name. + */ + public EncryptKey getEncryptKey(String tableName, String columnName); + + public IdBinder createIdBinder(BeanProperty[] uids); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMeta.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMeta.java index 8b5c63ea9..aa0987c00 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMeta.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMeta.java @@ -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; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java index 8832b30ce..e1f463e2c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanEmbeddedMetaFactory.java @@ -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 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 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); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanFinderManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanFinderManager.java index 539dee383..c48a1bdc7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanFinderManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanFinderManager.java @@ -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> finderClassList); - - /** - * Return the BeanController for a given entity type. - */ - public BeanFinder getBeanFinder(Class 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> finderClassList); + + /** + * Return the BeanController for a given entity type. + */ + public BeanFinder getBeanFinder(Class entityType); +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanForeignKey.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanForeignKey.java index 89e9367ef..6227bc88a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanForeignKey.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanForeignKey.java @@ -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; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManager.java index 354db2b0d..c30020204 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManager.java @@ -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 { - - private final BeanPersister persister; - - private final BeanDescriptor descriptor; - - public BeanManager(BeanDescriptor descriptor, BeanPersister persister) { - this.descriptor = descriptor; - this.persister = persister; - } - - /** - * Return the associated BeanPersister. - */ - public BeanPersister getBeanPersister() { - return persister; - } - - /** - * Return the BeanDescriptor. - */ - public BeanDescriptor 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 { + + private final BeanPersister persister; + + private final BeanDescriptor descriptor; + + public BeanManager(BeanDescriptor descriptor, BeanPersister persister) { + this.descriptor = descriptor; + this.persister = persister; + } + + /** + * Return the associated BeanPersister. + */ + public BeanPersister getBeanPersister() { + return persister; + } + + /** + * Return the BeanDescriptor. + */ + public BeanDescriptor getBeanDescriptor() { + return descriptor; + } + + /** + * Return true if this bean type is an LDAP entity type. + */ + public boolean isLdapEntityType() { + return descriptor.isLdapEntityType(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManagerFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManagerFactory.java index 6ee31721d..b795d3c9d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManagerFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanManagerFactory.java @@ -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 BeanManager create(BeanDescriptor desc) { - - BeanPersister persister = peristerFactory.create(desc); - - return new BeanManager(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 BeanManager create(BeanDescriptor desc) { + + BeanPersister persister = peristerFactory.create(desc); + + return new BeanManager(desc, persister); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java index 0d02ff85f..e78c882fc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanProperty.java @@ -1,1330 +1,1311 @@ -/** - * 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.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.sql.SQLException; -import java.sql.Types; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.BasicAttribute; -import javax.persistence.PersistenceException; - -import com.avaje.ebean.InvalidValue; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.config.EncryptKey; -import com.avaje.ebean.config.dbplatform.DbEncryptFunction; -import com.avaje.ebean.config.dbplatform.DbType; -import com.avaje.ebean.config.ldap.LdapAttributeAdapter; -import com.avaje.ebean.text.StringFormatter; -import com.avaje.ebean.text.StringParser; -import com.avaje.ebean.text.TextException; -import com.avaje.ebean.validation.factory.Validator; -import com.avaje.ebeaninternal.server.core.InternString; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; -import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.ldap.LdapPersistenceException; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; -import com.avaje.ebeaninternal.server.query.SqlBeanLoad; -import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter; -import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; -import com.avaje.ebeaninternal.server.type.DataBind; -import com.avaje.ebeaninternal.server.type.ScalarType; -import com.avaje.ebeaninternal.util.ValueUtil; - -/** - * Description of a property of a bean. Includes its deployment information such - * as database column mapping information. - */ -public class BeanProperty implements ElPropertyValue { - - /** - * Advanced bean deployment. To exclude this property from update where - * clause. - */ - public static final String EXCLUDE_FROM_UPDATE_WHERE = "EXCLUDE_FROM_UPDATE_WHERE"; - - /** - * Advanced bean deployment. To exclude this property from delete where - * clause. - */ - public static final String EXCLUDE_FROM_DELETE_WHERE = "EXCLUDE_FROM_DELETE_WHERE"; - - /** - * Advanced bean deployment. To exclude this property from insert. - */ - public static final String EXCLUDE_FROM_INSERT = "EXCLUDE_FROM_INSERT"; - - /** - * Advanced bean deployment. To exclude this property from update set - * clause. - */ - public static final String EXCLUDE_FROM_UPDATE = "EXCLUDE_FROM_UPDATE"; - - /** - * Flag to mark this at part of the unique id. - */ - final boolean id; - - /** - * Flag to make this as a dummy property for unidirecitonal relationships. - */ - final boolean unidirectionalShadow; - - /** - * Flag to mark the property as embedded. This could be on - * BeanPropertyAssocOne rather than here. Put it here for checking Id type - * (embedded or not). - */ - final boolean embedded; - - /** - * Flag indicating if this the version property. - */ - final boolean version; - - final boolean naturalKey; - - /** - * Set if this property is nullable. - */ - final boolean nullable; - - final boolean unique; - - /** - * Is this property include in database resultSet. - */ - final boolean dbRead; - - /** - * Include in DB insert. - */ - final boolean dbInsertable; - - /** - * Include in DB update. - */ - final boolean dbUpdatable; - - /** - * True if the property is based on a SECONDARY table. - */ - final boolean secondaryTable; - - final TableJoin secondaryTableJoin; - final String secondaryTableJoinPrefix; - - /** - * The property is inherited from a super class. - */ - final boolean inherited; - - final Class owningType; - - final boolean local; - - /** - * True if the property is a Clob, Blob LongVarchar or LongVarbinary. - */ - final boolean lob; - - final boolean fetchEager; - - final boolean isTransient; - - /** - * The logical bean property name. - */ - final String name; - - /** - * The reflected field. - */ - final Field field; - - /** - * The bean type. - */ - final Class propertyType; - - final String dbBind; - - /** - * The database column. This can include quoted identifiers. - */ - final String dbColumn; - - final String elPlaceHolder; - final String elPlaceHolderEncrypted; - - /** - * Select part of a SQL Formula used to populate this property. - */ - final String sqlFormulaSelect; - - /** - * Join part of a SQL Formula. - */ - final String sqlFormulaJoin; - - final boolean formula; - - /** - * Set to true if stored encrypted. - */ - final boolean dbEncrypted; - - final boolean localEncrypted; - - final int dbEncryptedType; - - /** - * The jdbc data type this maps to. - */ - final int dbType; - - /** - * The default value to insert if null. - */ - final Object defaultValue; - - /** - * Extra deployment parameters. - */ - final Map extraAttributeMap; - - /** - * The method used to read the property. - */ - final Method readMethod; - - /** - * The method used to write the property. - */ - final Method writeMethod; - - /** - * Generator for insert or update timestamp etc. - */ - final GeneratedProperty generatedProperty; - - final BeanReflectGetter getter; - - final BeanReflectSetter setter; - - final BeanDescriptor descriptor; - - /** - * Used for non-jdbc native types (java.util.Date Enums etc). Converts from - * logical to jdbc types. - */ - @SuppressWarnings("rawtypes") - final ScalarType scalarType; - - /** - * For LDAP attributes that have custom conversion. - */ - final LdapAttributeAdapter ldapAttributeAdapter; - - final Validator[] validators; - - final boolean hasLocalValidators; - - boolean cascadeValidate; - - /** - * The length or precision for DB column. - */ - final int dbLength; - - /** - * The scale for DB column (decimal). - */ - final int dbScale; - - /** - * Deployment defined DB column definition. - */ - final String dbColumnDefn; - - /** - * DB Constraint (typically check constraint on enum) - */ - final String dbConstraintExpression; - - final DbEncryptFunction dbEncryptFunction; - - final boolean dynamicSubclassWithInheritance; - - int deployOrder; - - public BeanProperty(DeployBeanProperty deploy) { - this(null, null, deploy); - } - - public BeanProperty(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanProperty deploy) { - - this.descriptor = descriptor; - this.name = InternString.intern(deploy.getName()); - if (descriptor != null) { - this.dynamicSubclassWithInheritance = (descriptor.isDynamicSubclass() && descriptor.hasInheritance()); - } else { - this.dynamicSubclassWithInheritance = false; - } - this.unidirectionalShadow = deploy.isUndirectionalShadow(); - this.localEncrypted = deploy.isLocalEncrypted(); - this.dbEncrypted = deploy.isDbEncrypted(); - this.dbEncryptedType = deploy.getDbEncryptedType(); - this.dbEncryptFunction = deploy.getDbEncryptFunction(); - this.dbBind = deploy.getDbBind(); - this.dbRead = deploy.isDbRead(); - this.dbInsertable = deploy.isDbInsertable(); - this.dbUpdatable = deploy.isDbUpdateable(); - - this.secondaryTable = deploy.isSecondaryTable(); - if (secondaryTable) { - this.secondaryTableJoin = new TableJoin(deploy.getSecondaryTableJoin(), null); - this.secondaryTableJoinPrefix = deploy.getSecondaryTableJoinPrefix(); - } else { - this.secondaryTableJoin = null; - this.secondaryTableJoinPrefix = null; - } - this.fetchEager = deploy.isFetchEager(); - this.isTransient = deploy.isTransient(); - this.nullable = deploy.isNullable(); - this.unique = deploy.isUnique(); - this.naturalKey = deploy.isNaturalKey(); - this.dbLength = deploy.getDbLength(); - this.dbScale = deploy.getDbScale(); - this.dbColumnDefn = InternString.intern(deploy.getDbColumnDefn()); - this.dbConstraintExpression = InternString.intern(deploy.getDbConstraintExpression()); - - this.inherited = false;// deploy.isInherited(); - this.owningType = deploy.getOwningType(); - this.local = deploy.isLocal(); - - this.version = deploy.isVersionColumn(); - this.embedded = deploy.isEmbedded(); - this.id = deploy.isId(); - this.generatedProperty = deploy.getGeneratedProperty(); - this.readMethod = deploy.getReadMethod(); - this.writeMethod = deploy.getWriteMethod(); - this.getter = deploy.getGetter(); - if (descriptor != null && getter == null) { - if (!unidirectionalShadow) { - String m = "Null Getter for: " + getFullBeanName(); - throw new RuntimeException(m); - } - } - this.setter = deploy.getSetter(); - - this.dbColumn = tableAliasIntern(descriptor, deploy.getDbColumn(), false, null); - this.sqlFormulaJoin = InternString.intern(deploy.getSqlFormulaJoin()); - this.sqlFormulaSelect = InternString.intern(deploy.getSqlFormulaSelect()); - this.formula = sqlFormulaSelect != null; - - this.extraAttributeMap = deploy.getExtraAttributeMap(); - this.defaultValue = deploy.getDefaultValue(); - this.dbType = deploy.getDbType(); - this.scalarType = deploy.getScalarType(); - this.ldapAttributeAdapter = deploy.getLdapAttributeAdapter(); - this.lob = isLobType(dbType); - this.propertyType = deploy.getPropertyType(); - this.field = deploy.getField(); - this.validators = deploy.getValidators(); - this.hasLocalValidators = (validators.length > 0); - - EntityType et = descriptor == null ? null : descriptor.getEntityType(); - this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), false, null); - this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), dbEncrypted, dbColumn); - } - - private String tableAliasIntern(BeanDescriptor descriptor, String s, boolean dbEncrypted, String dbColumn) { - if (descriptor != null) { - s = StringHelper.replaceString(s, "${ta}.", "${}"); - s = StringHelper.replaceString(s, "${ta}", "${}"); - - if (dbEncrypted) { - s = dbEncryptFunction.getDecryptSql(s); - String namedParam = ":encryptkey_" + descriptor.getBaseTable() + "___" + dbColumn; - s = StringHelper.replaceString(s, "?", namedParam); - } - } - return InternString.intern(s); - } - - /** - * Create a Matching BeanProperty with some attributes overridden. - *

- * Primarily for supporting Embedded beans with overridden dbColumn - * mappings. - *

- */ - public BeanProperty(BeanProperty source, BeanPropertyOverride override) { - - this.descriptor = source.descriptor; - this.name = InternString.intern(source.getName()); - this.dynamicSubclassWithInheritance = source.dynamicSubclassWithInheritance; - - this.dbColumn = InternString.intern(override.getDbColumn()); - this.sqlFormulaJoin = InternString.intern(override.getSqlFormulaJoin()); - this.sqlFormulaSelect = InternString.intern(override.getSqlFormulaSelect()); - this.formula = sqlFormulaSelect != null; - - this.fetchEager = source.fetchEager; - this.unidirectionalShadow = source.unidirectionalShadow; - this.localEncrypted = source.isLocalEncrypted(); - this.isTransient = source.isTransient(); - this.secondaryTable = source.isSecondaryTable(); - this.secondaryTableJoin = source.secondaryTableJoin; - this.secondaryTableJoinPrefix = source.secondaryTableJoinPrefix; - - this.dbBind = source.getDbBind(); - this.dbEncrypted = source.isDbEncrypted(); - this.dbEncryptedType = source.getDbEncryptedType(); - this.dbEncryptFunction = source.dbEncryptFunction; - this.dbRead = source.isDbRead(); - this.dbInsertable = source.isDbInsertable(); - this.dbUpdatable = source.isDbUpdatable(); - this.nullable = source.isNullable(); - this.unique = source.isUnique(); - this.naturalKey = source.isNaturalKey(); - this.dbLength = source.getDbLength(); - this.dbScale = source.getDbScale(); - this.dbColumnDefn = InternString.intern(source.getDbColumnDefn()); - this.dbConstraintExpression = InternString.intern(source.getDbConstraintExpression()); - - this.inherited = source.isInherited(); - this.owningType = source.owningType; - this.local = owningType.equals(descriptor.getBeanType()); - - this.version = source.isVersion(); - this.embedded = source.isEmbedded(); - this.id = source.isId(); - this.generatedProperty = source.getGeneratedProperty(); - this.readMethod = source.getReadMethod(); - this.writeMethod = source.getWriteMethod(); - this.getter = source.getter; - this.setter = source.setter; - this.extraAttributeMap = source.extraAttributeMap; - this.defaultValue = source.getDefaultValue(); - this.dbType = source.getDbType(); - this.scalarType = source.scalarType; - this.ldapAttributeAdapter = source.ldapAttributeAdapter; - this.lob = isLobType(dbType); - this.propertyType = source.getPropertyType(); - this.field = source.getField(); - this.validators = source.getValidators(); - this.hasLocalValidators = validators.length > 0; - - this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn); - this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn); - } - - /** - * Initialise the property before returning to client code. Used to - * initialise variables that can't be done in construction due to recursive - * issues. - */ - public void initialise() { - // do nothing for normal BeanProperty - if (!isTransient && scalarType == null) { - String msg = "No ScalarType assigned to " + descriptor.getFullName() + "." + getName(); - throw new RuntimeException(msg); - } - } - - /** - * Return the order this property appears in the bean. - */ - public int getDeployOrder() { - return deployOrder; - } - - /** - * Set the order this property appears in the bean. - */ - public void setDeployOrder(int deployOrder) { - this.deployOrder = deployOrder; - } - - public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, - boolean propertyDeploy) { - throw new PersistenceException("Not valid on scalar bean property " + getFullBeanName()); - } - - /** - * Return the BeanDescriptor that owns this property. - */ - public BeanDescriptor getBeanDescriptor() { - return descriptor; - } - - /** - * Return true is this is a simple scalar property. - */ - public boolean isScalar() { - return true; - } - - /** - * Return true if this property is based on a formula. - */ - public boolean isFormula() { - return formula; - } - - public boolean hasChanged(Object bean, Object oldValues) { - Object value = getValue(bean); - Object oldVal = getValue(oldValues); - - return !ValueUtil.areEqual(value, oldVal); - } - - public void copyProperty(Object sourceBean, Object destBean) { - Object value = getValue(sourceBean); - setValue(destBean, value); - } - - /** - * Return the encrypt key for the column matching this property. - */ - public EncryptKey getEncryptKey() { - return descriptor.getEncryptKey(this); - } - - public String getDecryptProperty() { - return dbEncryptFunction.getDecryptSql(this.getName()); - } - - public String getDecryptProperty(String propertyName) { - return dbEncryptFunction.getDecryptSql(propertyName); - } - - public String getDecryptSql() { - return dbEncryptFunction.getDecryptSql(this.getDbColumn()); - } - - public String getDecryptSql(String tableAlias) { - return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()); - } - - /** - * Add any extra joins required to support this property. Generally a no - * operation except for a OneToOne exported. - */ - public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) { - if (formula && sqlFormulaJoin != null) { - ctx.appendFormulaJoin(sqlFormulaJoin, forceOuterJoin); - - } else if (secondaryTableJoin != null) { - - String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); - secondaryTableJoin.addJoin(forceOuterJoin, relativePrefix, ctx); - } - } - - /** - * Returns null unless this property is using a secondary table. In that - * case this returns the logical property prefix. - */ - public String getSecondaryTableJoinPrefix() { - return secondaryTableJoinPrefix; - } - - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - if (formula) { - ctx.appendFormulaSelect(sqlFormulaSelect); - - } else if (!isTransient) { - - if (secondaryTableJoin != null) { - String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); - ctx.pushTableAlias(relativePrefix); - } - - if (dbEncrypted) { - String decryptSql = getDecryptSql(ctx.peekTableAlias()); - ctx.appendRawColumn(decryptSql); - ctx.addEncryptedProp(this); - - } else { - ctx.appendColumn(dbColumn); - } - - if (secondaryTableJoin != null) { - ctx.popTableAlias(); - } - } - } - - public boolean isAssignableFrom(Class type) { - return owningType.isAssignableFrom(type); - } - - public Object readSetOwning(DbReadContext ctx, Object bean, Class type) throws SQLException { - - try { - Object value = scalarType.read(ctx.getDataReader()); - if (value == null || bean == null) { - // not setting the value... - } else { - if (owningType.equals(type)) { - setValue(bean, value); - } - } - return value; - } catch (Exception e) { - String msg = "Error readSet on " + descriptor + "." + name; - throw new PersistenceException(msg, e); - } - } - - public void loadIgnore(DbReadContext ctx) { - scalarType.loadIgnore(ctx.getDataReader()); - } - - public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { - sqlBeanLoad.load(this); - } - - public void buildSelectExpressionChain(String prefix, List selectChain) { - if (prefix == null) { - selectChain.add(name); - } else { - selectChain.add(prefix + "." + name); - } - } - - public Object read(DbReadContext ctx) throws SQLException { - return scalarType.read(ctx.getDataReader()); - } - - public Object readSet(DbReadContext ctx, Object bean, Class type) throws SQLException { - - try { - Object value = scalarType.read(ctx.getDataReader()); - if (bean == null || (type != null && !owningType.isAssignableFrom(type))) { - // not setting the value... - } else { - setValue(bean, value); - } - return value; - } catch (Exception e) { - String msg = "Error readSet on " + descriptor + "." + name; - throw new PersistenceException(msg, e); - } - } - - /** - * Convert the type to the bean type if required. - *

- * Generally only used to ensure id properties are converted for - * Query.setId() use. - *

- */ - public Object toBeanType(Object value) { - return scalarType.toBeanType(value); - } - - @SuppressWarnings("unchecked") - public void bind(DataBind b, Object value) throws SQLException { - scalarType.bind(b, value); - } - - public void writeData(DataOutput dataOutput, Object value) throws IOException { - scalarType.writeData(dataOutput, value); - } - - public Object readData(DataInput dataInput) throws IOException { - return scalarType.readData(dataInput); - } - - Validator[] getValidators() { - return validators; - } - - public boolean isCascadeValidate() { - return cascadeValidate; - } - - public boolean hasLocalValidators() { - return hasLocalValidators; - } - - public boolean hasValidationRules(boolean cascade) { - return hasLocalValidators || (cascade && cascadeValidate); - } - - /** - * Checks to see if a bean is a reference (will be lazy loaded) or a - * BeanCollection that has not yet been populated. - *

- * For base types this returns true. - *

- */ - public boolean isValueLoaded(Object value) { - return true; - } - - /** - * Cascade the validation to the associated bean or collection. - */ - public InvalidValue validateCascade(Object value) { - return null; - } - - /** - * Validate the property with the given value. - * - * @param cascade - * if true cascade for assoc beans and collections. - * @param value - * the value to validate - * @return the list of errors that occurred. - */ - public final List validate(boolean cascade, Object value) { - - if (!isValueLoaded(value)) { - return null; - } - - ArrayList list = null; - for (int i = 0; i < validators.length; i++) { - if (!validators[i].isValid(value)) { - if (list == null) { - list = new ArrayList(); - } - Validator v = validators[i]; - list.add(new InvalidValue(v.getKey(), v.getAttributes(), descriptor.getFullName(), name, value)); - } - } - - if (list == null && cascade && cascadeValidate) { - // cascade the validation for assoc beans - InvalidValue recursive = validateCascade(value); - if (recursive != null) { - return InvalidValue.toList(recursive); - - } - } - return list; - } - - public BeanProperty getBeanProperty() { - return this; - } - - /** - * Return the getter method. - */ - public Method getReadMethod() { - return readMethod; - } - - /** - * Return the setter method. - */ - public Method getWriteMethod() { - return writeMethod; - } - - /** - * Return true if this object is part of an inheritance hierarchy. - */ - public boolean isInherited() { - return inherited; - } - - /** - * Return true is this type is not from a super type. - */ - public boolean isLocal() { - return local; - } - - public Attribute createAttribute(Object bean) { - Object v = getValue(bean); - if (v == null) { - return null; - } - if (ldapAttributeAdapter != null) { - return ldapAttributeAdapter.createAttribute(v); - } - Object ldapValue = scalarType.toJdbcType(v); - return new BasicAttribute(dbColumn, ldapValue); - } - - public void setAttributeValue(Object bean, Attribute attr) { - try { - if (attr != null) { - Object beanValue; - if (ldapAttributeAdapter != null) { - beanValue = ldapAttributeAdapter.readAttribute(attr); - } else { - beanValue = scalarType.toBeanType(attr.get()); - } - - setValue(bean, beanValue); - } - } catch (NamingException e) { - throw new LdapPersistenceException(e); - } - } - - /** - * Set the value of the property without interception or - * PropertyChangeSupport. - */ - public void setValue(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 + " on [" + descriptor + "] arg[" + value + "] type[" + beanType - + "] threw error"; - throw new RuntimeException(msg, ex); - } - } - - /** - * Set the value of the property. - */ - public void setValueIntercept(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 + " on [" + descriptor + "] arg[" + value + "] type[" + beanType - + "] threw error"; - throw new RuntimeException(msg, ex); - } - } - - private static Object[] NO_ARGS = new Object[0]; - - /** - * Return the property value taking inheritance into account. - */ - public Object getValueWithInheritance(Object bean) { - if (dynamicSubclassWithInheritance) { - return descriptor.getBeanPropertyWithInheritance(bean, name); - } - return getValue(bean); - } - - public Object getCacheDataValue(Object bean){ - return getValue(bean); - } - - public void setCacheDataValue(Object bean, Object cacheData, Object oldValues, boolean readOnly){ - setValue(bean, cacheData); - } - - /** - * Return the value of the property method. - */ - public Object getValue(Object bean) { - try { - if (bean instanceof EntityBean) { - return getter.get(bean); - } else { - return readMethod.invoke(bean, NO_ARGS); - } - } catch (Exception ex) { - String beanType = bean == null ? "null" : bean.getClass().getName(); - String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; - throw new RuntimeException(msg, ex); - } - } - - /** - * Explicitly use reflection to get value. - */ - public Object getValueViaReflection(Object bean) { - try { - return readMethod.invoke(bean, NO_ARGS); - } catch (Exception ex) { - String beanType = bean == null ? "null" : bean.getClass().getName(); - String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; - throw new RuntimeException(msg, ex); - } - } - - public Object getValueIntercept(Object bean) { - try { - if (bean instanceof EntityBean) { - return getter.getIntercept(bean); - } else { - return readMethod.invoke(bean, NO_ARGS); - } - } catch (Exception ex) { - String beanType = bean == null ? "null" : bean.getClass().getName(); - String msg = "getIntercept " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; - throw new RuntimeException(msg, ex); - } - } - - public Object elConvertType(Object value) { - if (value == null) { - return null; - } - return convertToLogicalType(value); - } - - public void elSetReference(Object bean) { - throw new RuntimeException("Should not be called"); - } - - public void elSetValue(Object bean, Object value, boolean populate, boolean reference) { - if (bean != null) { - setValueIntercept(bean, value); - } - } - - public Object elGetValue(Object bean) { - if (bean == null) { - return null; - } - return getValueIntercept(bean); - } - - public Object elGetReference(Object bean) { - throw new RuntimeException("Not expected to call this"); - } - - /** - * Return the name of the property. - */ - public String getName() { - return name; - } - - public String getElName() { - return name; - } - - /** - * This is a full ElGetValue. - */ - public boolean isDeployOnly() { - return false; - } - - public boolean containsManySince(String sinceProperty) { - return containsMany(); - } - - public boolean containsMany() { - return false; - } - - public Object[] getAssocOneIdValues(Object bean) { - // Returns null as not an AssocOne. - return null; - } - - public String getAssocOneIdExpr(String prefix, String operator) { - // Returns null as not an AssocOne. - return null; - } - - public String getAssocIdInExpr(String prefix) { - // Returns null as not an AssocOne. - return null; - } - - public String getAssocIdInValueExpr(int size) { - // Returns null as not an AssocOne. - return null; - } - - public boolean isAssocId() { - // Returns false - override in BeanPropertyAssocOne. - return false; - } - - public boolean isAssocProperty() { - // Returns false - override in BeanPropertyAssocOne. - return false; - } - - public String getElPlaceholder(boolean encrypted) { - return encrypted ? elPlaceHolderEncrypted : elPlaceHolder; - } - - public String getElPrefix() { - return secondaryTableJoinPrefix; - } - - /** - * Return the full name of this property. - */ - public String getFullBeanName() { - return descriptor.getFullName() + "." + name; - } - - /** - * Return the scalarType. - */ - public ScalarType getScalarType() { - return scalarType; - } - - public StringFormatter getStringFormatter() { - return scalarType; - } - - public StringParser getStringParser() { - return scalarType; - } - - public boolean isDateTimeCapable() { - return scalarType != null && scalarType.isDateTimeCapable(); - } - - public int getJdbcType() { - return scalarType == null ? 0 : scalarType.getJdbcType(); - } - - public Object parseDateTime(long systemTimeMillis) { - return scalarType.parseDateTime(systemTimeMillis); - } - - /** - * Return the DB max length (varchar) or precision (decimal). - */ - public int getDbLength() { - return dbLength; - } - - /** - * Return the DB scale for numeric columns. - */ - public int getDbScale() { - return dbScale; - } - - /** - * Return a specific column DDL definition if specified (otherwise null). - */ - public String getDbColumnDefn() { - return dbColumnDefn; - } - - /** - * Return the DB constraint expression (can be null). - *

- * For an Enum returns IN expression for the set of Enum values. - *

- */ - public String getDbConstraintExpression() { - return dbConstraintExpression; - } - - /** - * Return the DB column type definition. - */ - public String renderDbType(DbType dbType) { - if (dbColumnDefn != null) { - return dbColumnDefn; - } - return dbType.renderType(dbLength, dbScale); - } - - /** - * Return the bean Field associated with this property. - */ - public Field getField() { - return field; - } - - /** - * Return the GeneratedValue. Used to generate update timestamp etc. - */ - public GeneratedProperty getGeneratedProperty() { - return generatedProperty; - } - - /** - * Return true if this is the natural key property. - */ - public boolean isNaturalKey() { - return naturalKey; - } - - /** - * Return true if this property is mandatory. - */ - public boolean isNullable() { - return nullable; - } - - /** - * Return true if DDL Not NULL constraint should be defined for this column - * based on it being a version column or having a generated property. - */ - public boolean isDDLNotNull() { - return isVersion() || (generatedProperty != null && generatedProperty.isDDLNotNullable()); - } - - /** - * Return true if the DB column should be unique. - */ - public boolean isUnique() { - return unique; - } - - /** - * Return true if the property is transient. - */ - public boolean isTransient() { - return isTransient; - } - - /** - * Return true if this is a version column used for concurrency checking. - */ - public boolean isVersion() { - return version; - } - - public String getDeployProperty() { - return dbColumn; - } - - /** - * The database column name this is mapped to. - */ - public String getDbColumn() { - return dbColumn; - } - - /** - * Return the database jdbc data type this is mapped to. - */ - public int getDbType() { - return dbType; - } - - /** - * Perform DB to Logical type conversion (if necessary). - */ - public Object convertToLogicalType(Object value) { - if (scalarType != null) { - return scalarType.toBeanType(value); - } - return value; - } - -// private ArrayList luceneIndexes; -// -// public void registerLuceneIndex(LuceneIndex luceneIndex) { -// if (luceneIndexes == null) { -// luceneIndexes = new ArrayList(); -// } -// luceneIndexes.add(luceneIndex); -// } -// -// public boolean isDeltaRequired() { -// return true;//luceneIndexes != null; -// } - - /** - * Return true if by default this property is set to fetch eager. - * Lob's usually default to fetch lazy. - */ - public boolean isFetchEager() { - return fetchEager; - } - - /** - * Return true if this is mapped to a Clob Blob LongVarchar or - * LongVarbinary. - */ - public boolean isLob() { - return lob; - } - - private boolean isLobType(int type) { - switch (type) { - case Types.CLOB: - return true; - case Types.BLOB: - return true; - case Types.LONGVARBINARY: - return true; - case Types.LONGVARCHAR: - return true; - - default: - return false; - } - } - - /** - * Return the DB bind parameter. Typically is "?" but different for - * encrypted bind. - */ - public String getDbBind() { - return dbBind; - } - - /** - * Returns true if DB encrypted. - */ - public boolean isLocalEncrypted() { - return localEncrypted; - } - - /** - * Return true if this property is stored encrypted. - */ - public boolean isDbEncrypted() { - return dbEncrypted; - } - - public int getDbEncryptedType() { - return dbEncryptedType; - } - - /** - * Return true if this property should be included in an Insert. - */ - public boolean isDbInsertable() { - return dbInsertable; - } - - /** - * Return true if this property should be included in an Update. - */ - public boolean isDbUpdatable() { - return dbUpdatable; - } - - /** - * Return true if this property is included in database queries. - */ - public boolean isDbRead() { - return dbRead; - } - - /** - * Return true if this property is based on a secondary table (not the base - * table). - */ - public boolean isSecondaryTable() { - return secondaryTable; - } - - /** - * Return the property type. - */ - public Class getPropertyType() { - return propertyType; - } - - /** - * Return true if this is included in the unique id. - */ - public boolean isId() { - return id; - } - - /** - * Return true if this is an Embedded property. In this case it shares the - * table and primary key of its owner object. - */ - public boolean isEmbedded() { - return embedded; - } - - /** - * Return an extra attribute set on this property. - */ - public String getExtraAttribute(String key) { - return extraAttributeMap.get(key); - } - - /** - * Return the default value. - */ - public Object getDefaultValue() { - return defaultValue; - } - - public String toString() { - return name; - } - - @SuppressWarnings("unchecked") - public void jsonWrite(WriteJsonContext ctx, Object bean) { - - Object value = getValueIntercept(bean); - if (value == null) { - ctx.appendNull(name); - } else { - ctx.appendNameValue(name, scalarType, value); - } - } - - public void jsonRead(ReadJsonContext ctx, Object bean) { - - String jsonValue; - try { - jsonValue = ctx.readScalarValue(); - } catch (TextException e){ - throw new TextException("Error reading property "+getFullBeanName(), e); - } - Object objValue; - if (jsonValue == null) { - objValue = null; - } else { - objValue = scalarType.jsonFromString(jsonValue, ctx.getValueAdapter()); - } - setValue(bean, objValue); - } -} +package com.avaje.ebeaninternal.server.deploy; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.BasicAttribute; +import javax.persistence.PersistenceException; + +import com.avaje.ebean.InvalidValue; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.config.EncryptKey; +import com.avaje.ebean.config.dbplatform.DbEncryptFunction; +import com.avaje.ebean.config.dbplatform.DbType; +import com.avaje.ebean.config.ldap.LdapAttributeAdapter; +import com.avaje.ebean.text.StringFormatter; +import com.avaje.ebean.text.StringParser; +import com.avaje.ebean.text.TextException; +import com.avaje.ebean.validation.factory.Validator; +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; +import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.ldap.LdapPersistenceException; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import com.avaje.ebeaninternal.server.query.SqlBeanLoad; +import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter; +import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter; +import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.type.ScalarType; +import com.avaje.ebeaninternal.util.ValueUtil; + +/** + * Description of a property of a bean. Includes its deployment information such + * as database column mapping information. + */ +public class BeanProperty implements ElPropertyValue { + + /** + * Advanced bean deployment. To exclude this property from update where + * clause. + */ + public static final String EXCLUDE_FROM_UPDATE_WHERE = "EXCLUDE_FROM_UPDATE_WHERE"; + + /** + * Advanced bean deployment. To exclude this property from delete where + * clause. + */ + public static final String EXCLUDE_FROM_DELETE_WHERE = "EXCLUDE_FROM_DELETE_WHERE"; + + /** + * Advanced bean deployment. To exclude this property from insert. + */ + public static final String EXCLUDE_FROM_INSERT = "EXCLUDE_FROM_INSERT"; + + /** + * Advanced bean deployment. To exclude this property from update set + * clause. + */ + public static final String EXCLUDE_FROM_UPDATE = "EXCLUDE_FROM_UPDATE"; + + /** + * Flag to mark this at part of the unique id. + */ + final boolean id; + + /** + * Flag to make this as a dummy property for unidirecitonal relationships. + */ + final boolean unidirectionalShadow; + + /** + * Flag to mark the property as embedded. This could be on + * BeanPropertyAssocOne rather than here. Put it here for checking Id type + * (embedded or not). + */ + final boolean embedded; + + /** + * Flag indicating if this the version property. + */ + final boolean version; + + final boolean naturalKey; + + /** + * Set if this property is nullable. + */ + final boolean nullable; + + final boolean unique; + + /** + * Is this property include in database resultSet. + */ + final boolean dbRead; + + /** + * Include in DB insert. + */ + final boolean dbInsertable; + + /** + * Include in DB update. + */ + final boolean dbUpdatable; + + /** + * True if the property is based on a SECONDARY table. + */ + final boolean secondaryTable; + + final TableJoin secondaryTableJoin; + final String secondaryTableJoinPrefix; + + /** + * The property is inherited from a super class. + */ + final boolean inherited; + + final Class owningType; + + final boolean local; + + /** + * True if the property is a Clob, Blob LongVarchar or LongVarbinary. + */ + final boolean lob; + + final boolean fetchEager; + + final boolean isTransient; + + /** + * The logical bean property name. + */ + final String name; + + /** + * The reflected field. + */ + final Field field; + + /** + * The bean type. + */ + final Class propertyType; + + final String dbBind; + + /** + * The database column. This can include quoted identifiers. + */ + final String dbColumn; + + final String elPlaceHolder; + final String elPlaceHolderEncrypted; + + /** + * Select part of a SQL Formula used to populate this property. + */ + final String sqlFormulaSelect; + + /** + * Join part of a SQL Formula. + */ + final String sqlFormulaJoin; + + final boolean formula; + + /** + * Set to true if stored encrypted. + */ + final boolean dbEncrypted; + + final boolean localEncrypted; + + final int dbEncryptedType; + + /** + * The jdbc data type this maps to. + */ + final int dbType; + + /** + * The default value to insert if null. + */ + final Object defaultValue; + + /** + * Extra deployment parameters. + */ + final Map extraAttributeMap; + + /** + * The method used to read the property. + */ + final Method readMethod; + + /** + * The method used to write the property. + */ + final Method writeMethod; + + /** + * Generator for insert or update timestamp etc. + */ + final GeneratedProperty generatedProperty; + + final BeanReflectGetter getter; + + final BeanReflectSetter setter; + + final BeanDescriptor descriptor; + + /** + * Used for non-jdbc native types (java.util.Date Enums etc). Converts from + * logical to jdbc types. + */ + @SuppressWarnings("rawtypes") + final ScalarType scalarType; + + /** + * For LDAP attributes that have custom conversion. + */ + final LdapAttributeAdapter ldapAttributeAdapter; + + final Validator[] validators; + + final boolean hasLocalValidators; + + boolean cascadeValidate; + + /** + * The length or precision for DB column. + */ + final int dbLength; + + /** + * The scale for DB column (decimal). + */ + final int dbScale; + + /** + * Deployment defined DB column definition. + */ + final String dbColumnDefn; + + /** + * DB Constraint (typically check constraint on enum) + */ + final String dbConstraintExpression; + + final DbEncryptFunction dbEncryptFunction; + + final boolean dynamicSubclassWithInheritance; + + int deployOrder; + + public BeanProperty(DeployBeanProperty deploy) { + this(null, null, deploy); + } + + public BeanProperty(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanProperty deploy) { + + this.descriptor = descriptor; + this.name = InternString.intern(deploy.getName()); + if (descriptor != null) { + this.dynamicSubclassWithInheritance = (descriptor.isDynamicSubclass() && descriptor.hasInheritance()); + } else { + this.dynamicSubclassWithInheritance = false; + } + this.unidirectionalShadow = deploy.isUndirectionalShadow(); + this.localEncrypted = deploy.isLocalEncrypted(); + this.dbEncrypted = deploy.isDbEncrypted(); + this.dbEncryptedType = deploy.getDbEncryptedType(); + this.dbEncryptFunction = deploy.getDbEncryptFunction(); + this.dbBind = deploy.getDbBind(); + this.dbRead = deploy.isDbRead(); + this.dbInsertable = deploy.isDbInsertable(); + this.dbUpdatable = deploy.isDbUpdateable(); + + this.secondaryTable = deploy.isSecondaryTable(); + if (secondaryTable) { + this.secondaryTableJoin = new TableJoin(deploy.getSecondaryTableJoin(), null); + this.secondaryTableJoinPrefix = deploy.getSecondaryTableJoinPrefix(); + } else { + this.secondaryTableJoin = null; + this.secondaryTableJoinPrefix = null; + } + this.fetchEager = deploy.isFetchEager(); + this.isTransient = deploy.isTransient(); + this.nullable = deploy.isNullable(); + this.unique = deploy.isUnique(); + this.naturalKey = deploy.isNaturalKey(); + this.dbLength = deploy.getDbLength(); + this.dbScale = deploy.getDbScale(); + this.dbColumnDefn = InternString.intern(deploy.getDbColumnDefn()); + this.dbConstraintExpression = InternString.intern(deploy.getDbConstraintExpression()); + + this.inherited = false;// deploy.isInherited(); + this.owningType = deploy.getOwningType(); + this.local = deploy.isLocal(); + + this.version = deploy.isVersionColumn(); + this.embedded = deploy.isEmbedded(); + this.id = deploy.isId(); + this.generatedProperty = deploy.getGeneratedProperty(); + this.readMethod = deploy.getReadMethod(); + this.writeMethod = deploy.getWriteMethod(); + this.getter = deploy.getGetter(); + if (descriptor != null && getter == null) { + if (!unidirectionalShadow) { + String m = "Null Getter for: " + getFullBeanName(); + throw new RuntimeException(m); + } + } + this.setter = deploy.getSetter(); + + this.dbColumn = tableAliasIntern(descriptor, deploy.getDbColumn(), false, null); + this.sqlFormulaJoin = InternString.intern(deploy.getSqlFormulaJoin()); + this.sqlFormulaSelect = InternString.intern(deploy.getSqlFormulaSelect()); + this.formula = sqlFormulaSelect != null; + + this.extraAttributeMap = deploy.getExtraAttributeMap(); + this.defaultValue = deploy.getDefaultValue(); + this.dbType = deploy.getDbType(); + this.scalarType = deploy.getScalarType(); + this.ldapAttributeAdapter = deploy.getLdapAttributeAdapter(); + this.lob = isLobType(dbType); + this.propertyType = deploy.getPropertyType(); + this.field = deploy.getField(); + this.validators = deploy.getValidators(); + this.hasLocalValidators = (validators.length > 0); + + EntityType et = descriptor == null ? null : descriptor.getEntityType(); + this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), false, null); + this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(et), dbEncrypted, dbColumn); + } + + private String tableAliasIntern(BeanDescriptor descriptor, String s, boolean dbEncrypted, String dbColumn) { + if (descriptor != null) { + s = StringHelper.replaceString(s, "${ta}.", "${}"); + s = StringHelper.replaceString(s, "${ta}", "${}"); + + if (dbEncrypted) { + s = dbEncryptFunction.getDecryptSql(s); + String namedParam = ":encryptkey_" + descriptor.getBaseTable() + "___" + dbColumn; + s = StringHelper.replaceString(s, "?", namedParam); + } + } + return InternString.intern(s); + } + + /** + * Create a Matching BeanProperty with some attributes overridden. + *

+ * Primarily for supporting Embedded beans with overridden dbColumn + * mappings. + *

+ */ + public BeanProperty(BeanProperty source, BeanPropertyOverride override) { + + this.descriptor = source.descriptor; + this.name = InternString.intern(source.getName()); + this.dynamicSubclassWithInheritance = source.dynamicSubclassWithInheritance; + + this.dbColumn = InternString.intern(override.getDbColumn()); + this.sqlFormulaJoin = InternString.intern(override.getSqlFormulaJoin()); + this.sqlFormulaSelect = InternString.intern(override.getSqlFormulaSelect()); + this.formula = sqlFormulaSelect != null; + + this.fetchEager = source.fetchEager; + this.unidirectionalShadow = source.unidirectionalShadow; + this.localEncrypted = source.isLocalEncrypted(); + this.isTransient = source.isTransient(); + this.secondaryTable = source.isSecondaryTable(); + this.secondaryTableJoin = source.secondaryTableJoin; + this.secondaryTableJoinPrefix = source.secondaryTableJoinPrefix; + + this.dbBind = source.getDbBind(); + this.dbEncrypted = source.isDbEncrypted(); + this.dbEncryptedType = source.getDbEncryptedType(); + this.dbEncryptFunction = source.dbEncryptFunction; + this.dbRead = source.isDbRead(); + this.dbInsertable = source.isDbInsertable(); + this.dbUpdatable = source.isDbUpdatable(); + this.nullable = source.isNullable(); + this.unique = source.isUnique(); + this.naturalKey = source.isNaturalKey(); + this.dbLength = source.getDbLength(); + this.dbScale = source.getDbScale(); + this.dbColumnDefn = InternString.intern(source.getDbColumnDefn()); + this.dbConstraintExpression = InternString.intern(source.getDbConstraintExpression()); + + this.inherited = source.isInherited(); + this.owningType = source.owningType; + this.local = owningType.equals(descriptor.getBeanType()); + + this.version = source.isVersion(); + this.embedded = source.isEmbedded(); + this.id = source.isId(); + this.generatedProperty = source.getGeneratedProperty(); + this.readMethod = source.getReadMethod(); + this.writeMethod = source.getWriteMethod(); + this.getter = source.getter; + this.setter = source.setter; + this.extraAttributeMap = source.extraAttributeMap; + this.defaultValue = source.getDefaultValue(); + this.dbType = source.getDbType(); + this.scalarType = source.scalarType; + this.ldapAttributeAdapter = source.ldapAttributeAdapter; + this.lob = isLobType(dbType); + this.propertyType = source.getPropertyType(); + this.field = source.getField(); + this.validators = source.getValidators(); + this.hasLocalValidators = validators.length > 0; + + this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn); + this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn); + } + + /** + * Initialise the property before returning to client code. Used to + * initialise variables that can't be done in construction due to recursive + * issues. + */ + public void initialise() { + // do nothing for normal BeanProperty + if (!isTransient && scalarType == null) { + String msg = "No ScalarType assigned to " + descriptor.getFullName() + "." + getName(); + throw new RuntimeException(msg); + } + } + + /** + * Return the order this property appears in the bean. + */ + public int getDeployOrder() { + return deployOrder; + } + + /** + * Set the order this property appears in the bean. + */ + public void setDeployOrder(int deployOrder) { + this.deployOrder = deployOrder; + } + + public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, + boolean propertyDeploy) { + throw new PersistenceException("Not valid on scalar bean property " + getFullBeanName()); + } + + /** + * Return the BeanDescriptor that owns this property. + */ + public BeanDescriptor getBeanDescriptor() { + return descriptor; + } + + /** + * Return true is this is a simple scalar property. + */ + public boolean isScalar() { + return true; + } + + /** + * Return true if this property is based on a formula. + */ + public boolean isFormula() { + return formula; + } + + public boolean hasChanged(Object bean, Object oldValues) { + Object value = getValue(bean); + Object oldVal = getValue(oldValues); + + return !ValueUtil.areEqual(value, oldVal); + } + + public void copyProperty(Object sourceBean, Object destBean) { + Object value = getValue(sourceBean); + setValue(destBean, value); + } + + /** + * Return the encrypt key for the column matching this property. + */ + public EncryptKey getEncryptKey() { + return descriptor.getEncryptKey(this); + } + + public String getDecryptProperty() { + return dbEncryptFunction.getDecryptSql(this.getName()); + } + + public String getDecryptProperty(String propertyName) { + return dbEncryptFunction.getDecryptSql(propertyName); + } + + public String getDecryptSql() { + return dbEncryptFunction.getDecryptSql(this.getDbColumn()); + } + + public String getDecryptSql(String tableAlias) { + return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()); + } + + /** + * Add any extra joins required to support this property. Generally a no + * operation except for a OneToOne exported. + */ + public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) { + if (formula && sqlFormulaJoin != null) { + ctx.appendFormulaJoin(sqlFormulaJoin, forceOuterJoin); + + } else if (secondaryTableJoin != null) { + + String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); + secondaryTableJoin.addJoin(forceOuterJoin, relativePrefix, ctx); + } + } + + /** + * Returns null unless this property is using a secondary table. In that + * case this returns the logical property prefix. + */ + public String getSecondaryTableJoinPrefix() { + return secondaryTableJoinPrefix; + } + + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + if (formula) { + ctx.appendFormulaSelect(sqlFormulaSelect); + + } else if (!isTransient) { + + if (secondaryTableJoin != null) { + String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); + ctx.pushTableAlias(relativePrefix); + } + + if (dbEncrypted) { + String decryptSql = getDecryptSql(ctx.peekTableAlias()); + ctx.appendRawColumn(decryptSql); + ctx.addEncryptedProp(this); + + } else { + ctx.appendColumn(dbColumn); + } + + if (secondaryTableJoin != null) { + ctx.popTableAlias(); + } + } + } + + public boolean isAssignableFrom(Class type) { + return owningType.isAssignableFrom(type); + } + + public Object readSetOwning(DbReadContext ctx, Object bean, Class type) throws SQLException { + + try { + Object value = scalarType.read(ctx.getDataReader()); + if (value == null || bean == null) { + // not setting the value... + } else { + if (owningType.equals(type)) { + setValue(bean, value); + } + } + return value; + } catch (Exception e) { + String msg = "Error readSet on " + descriptor + "." + name; + throw new PersistenceException(msg, e); + } + } + + public void loadIgnore(DbReadContext ctx) { + scalarType.loadIgnore(ctx.getDataReader()); + } + + public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { + sqlBeanLoad.load(this); + } + + public void buildSelectExpressionChain(String prefix, List selectChain) { + if (prefix == null) { + selectChain.add(name); + } else { + selectChain.add(prefix + "." + name); + } + } + + public Object read(DbReadContext ctx) throws SQLException { + return scalarType.read(ctx.getDataReader()); + } + + public Object readSet(DbReadContext ctx, Object bean, Class type) throws SQLException { + + try { + Object value = scalarType.read(ctx.getDataReader()); + if (bean == null || (type != null && !owningType.isAssignableFrom(type))) { + // not setting the value... + } else { + setValue(bean, value); + } + return value; + } catch (Exception e) { + String msg = "Error readSet on " + descriptor + "." + name; + throw new PersistenceException(msg, e); + } + } + + /** + * Convert the type to the bean type if required. + *

+ * Generally only used to ensure id properties are converted for + * Query.setId() use. + *

+ */ + public Object toBeanType(Object value) { + return scalarType.toBeanType(value); + } + + @SuppressWarnings("unchecked") + public void bind(DataBind b, Object value) throws SQLException { + scalarType.bind(b, value); + } + + public void writeData(DataOutput dataOutput, Object value) throws IOException { + scalarType.writeData(dataOutput, value); + } + + public Object readData(DataInput dataInput) throws IOException { + return scalarType.readData(dataInput); + } + + Validator[] getValidators() { + return validators; + } + + public boolean isCascadeValidate() { + return cascadeValidate; + } + + public boolean hasLocalValidators() { + return hasLocalValidators; + } + + public boolean hasValidationRules(boolean cascade) { + return hasLocalValidators || (cascade && cascadeValidate); + } + + /** + * Checks to see if a bean is a reference (will be lazy loaded) or a + * BeanCollection that has not yet been populated. + *

+ * For base types this returns true. + *

+ */ + public boolean isValueLoaded(Object value) { + return true; + } + + /** + * Cascade the validation to the associated bean or collection. + */ + public InvalidValue validateCascade(Object value) { + return null; + } + + /** + * Validate the property with the given value. + * + * @param cascade + * if true cascade for assoc beans and collections. + * @param value + * the value to validate + * @return the list of errors that occurred. + */ + public final List validate(boolean cascade, Object value) { + + if (!isValueLoaded(value)) { + return null; + } + + ArrayList list = null; + for (int i = 0; i < validators.length; i++) { + if (!validators[i].isValid(value)) { + if (list == null) { + list = new ArrayList(); + } + Validator v = validators[i]; + list.add(new InvalidValue(v.getKey(), v.getAttributes(), descriptor.getFullName(), name, value)); + } + } + + if (list == null && cascade && cascadeValidate) { + // cascade the validation for assoc beans + InvalidValue recursive = validateCascade(value); + if (recursive != null) { + return InvalidValue.toList(recursive); + + } + } + return list; + } + + public BeanProperty getBeanProperty() { + return this; + } + + /** + * Return the getter method. + */ + public Method getReadMethod() { + return readMethod; + } + + /** + * Return the setter method. + */ + public Method getWriteMethod() { + return writeMethod; + } + + /** + * Return true if this object is part of an inheritance hierarchy. + */ + public boolean isInherited() { + return inherited; + } + + /** + * Return true is this type is not from a super type. + */ + public boolean isLocal() { + return local; + } + + public Attribute createAttribute(Object bean) { + Object v = getValue(bean); + if (v == null) { + return null; + } + if (ldapAttributeAdapter != null) { + return ldapAttributeAdapter.createAttribute(v); + } + Object ldapValue = scalarType.toJdbcType(v); + return new BasicAttribute(dbColumn, ldapValue); + } + + public void setAttributeValue(Object bean, Attribute attr) { + try { + if (attr != null) { + Object beanValue; + if (ldapAttributeAdapter != null) { + beanValue = ldapAttributeAdapter.readAttribute(attr); + } else { + beanValue = scalarType.toBeanType(attr.get()); + } + + setValue(bean, beanValue); + } + } catch (NamingException e) { + throw new LdapPersistenceException(e); + } + } + + /** + * Set the value of the property without interception or + * PropertyChangeSupport. + */ + public void setValue(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 + " on [" + descriptor + "] arg[" + value + "] type[" + beanType + + "] threw error"; + throw new RuntimeException(msg, ex); + } + } + + /** + * Set the value of the property. + */ + public void setValueIntercept(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 + " on [" + descriptor + "] arg[" + value + "] type[" + beanType + + "] threw error"; + throw new RuntimeException(msg, ex); + } + } + + private static Object[] NO_ARGS = new Object[0]; + + /** + * Return the property value taking inheritance into account. + */ + public Object getValueWithInheritance(Object bean) { + if (dynamicSubclassWithInheritance) { + return descriptor.getBeanPropertyWithInheritance(bean, name); + } + return getValue(bean); + } + + public Object getCacheDataValue(Object bean){ + return getValue(bean); + } + + public void setCacheDataValue(Object bean, Object cacheData, Object oldValues, boolean readOnly){ + setValue(bean, cacheData); + } + + /** + * Return the value of the property method. + */ + public Object getValue(Object bean) { + try { + if (bean instanceof EntityBean) { + return getter.get(bean); + } else { + return readMethod.invoke(bean, NO_ARGS); + } + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; + throw new RuntimeException(msg, ex); + } + } + + /** + * Explicitly use reflection to get value. + */ + public Object getValueViaReflection(Object bean) { + try { + return readMethod.invoke(bean, NO_ARGS); + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; + throw new RuntimeException(msg, ex); + } + } + + public Object getValueIntercept(Object bean) { + try { + if (bean instanceof EntityBean) { + return getter.getIntercept(bean); + } else { + return readMethod.invoke(bean, NO_ARGS); + } + } catch (Exception ex) { + String beanType = bean == null ? "null" : bean.getClass().getName(); + String msg = "getIntercept " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; + throw new RuntimeException(msg, ex); + } + } + + public Object elConvertType(Object value) { + if (value == null) { + return null; + } + return convertToLogicalType(value); + } + + public void elSetReference(Object bean) { + throw new RuntimeException("Should not be called"); + } + + public void elSetValue(Object bean, Object value, boolean populate, boolean reference) { + if (bean != null) { + setValueIntercept(bean, value); + } + } + + public Object elGetValue(Object bean) { + if (bean == null) { + return null; + } + return getValueIntercept(bean); + } + + public Object elGetReference(Object bean) { + throw new RuntimeException("Not expected to call this"); + } + + /** + * Return the name of the property. + */ + public String getName() { + return name; + } + + public String getElName() { + return name; + } + + /** + * This is a full ElGetValue. + */ + public boolean isDeployOnly() { + return false; + } + + public boolean containsManySince(String sinceProperty) { + return containsMany(); + } + + public boolean containsMany() { + return false; + } + + public Object[] getAssocOneIdValues(Object bean) { + // Returns null as not an AssocOne. + return null; + } + + public String getAssocOneIdExpr(String prefix, String operator) { + // Returns null as not an AssocOne. + return null; + } + + public String getAssocIdInExpr(String prefix) { + // Returns null as not an AssocOne. + return null; + } + + public String getAssocIdInValueExpr(int size) { + // Returns null as not an AssocOne. + return null; + } + + public boolean isAssocId() { + // Returns false - override in BeanPropertyAssocOne. + return false; + } + + public boolean isAssocProperty() { + // Returns false - override in BeanPropertyAssocOne. + return false; + } + + public String getElPlaceholder(boolean encrypted) { + return encrypted ? elPlaceHolderEncrypted : elPlaceHolder; + } + + public String getElPrefix() { + return secondaryTableJoinPrefix; + } + + /** + * Return the full name of this property. + */ + public String getFullBeanName() { + return descriptor.getFullName() + "." + name; + } + + /** + * Return the scalarType. + */ + public ScalarType getScalarType() { + return scalarType; + } + + public StringFormatter getStringFormatter() { + return scalarType; + } + + public StringParser getStringParser() { + return scalarType; + } + + public boolean isDateTimeCapable() { + return scalarType != null && scalarType.isDateTimeCapable(); + } + + public int getJdbcType() { + return scalarType == null ? 0 : scalarType.getJdbcType(); + } + + public Object parseDateTime(long systemTimeMillis) { + return scalarType.parseDateTime(systemTimeMillis); + } + + /** + * Return the DB max length (varchar) or precision (decimal). + */ + public int getDbLength() { + return dbLength; + } + + /** + * Return the DB scale for numeric columns. + */ + public int getDbScale() { + return dbScale; + } + + /** + * Return a specific column DDL definition if specified (otherwise null). + */ + public String getDbColumnDefn() { + return dbColumnDefn; + } + + /** + * Return the DB constraint expression (can be null). + *

+ * For an Enum returns IN expression for the set of Enum values. + *

+ */ + public String getDbConstraintExpression() { + return dbConstraintExpression; + } + + /** + * Return the DB column type definition. + */ + public String renderDbType(DbType dbType) { + if (dbColumnDefn != null) { + return dbColumnDefn; + } + return dbType.renderType(dbLength, dbScale); + } + + /** + * Return the bean Field associated with this property. + */ + public Field getField() { + return field; + } + + /** + * Return the GeneratedValue. Used to generate update timestamp etc. + */ + public GeneratedProperty getGeneratedProperty() { + return generatedProperty; + } + + /** + * Return true if this is the natural key property. + */ + public boolean isNaturalKey() { + return naturalKey; + } + + /** + * Return true if this property is mandatory. + */ + public boolean isNullable() { + return nullable; + } + + /** + * Return true if DDL Not NULL constraint should be defined for this column + * based on it being a version column or having a generated property. + */ + public boolean isDDLNotNull() { + return isVersion() || (generatedProperty != null && generatedProperty.isDDLNotNullable()); + } + + /** + * Return true if the DB column should be unique. + */ + public boolean isUnique() { + return unique; + } + + /** + * Return true if the property is transient. + */ + public boolean isTransient() { + return isTransient; + } + + /** + * Return true if this is a version column used for concurrency checking. + */ + public boolean isVersion() { + return version; + } + + public String getDeployProperty() { + return dbColumn; + } + + /** + * The database column name this is mapped to. + */ + public String getDbColumn() { + return dbColumn; + } + + /** + * Return the database jdbc data type this is mapped to. + */ + public int getDbType() { + return dbType; + } + + /** + * Perform DB to Logical type conversion (if necessary). + */ + public Object convertToLogicalType(Object value) { + if (scalarType != null) { + return scalarType.toBeanType(value); + } + return value; + } + +// private ArrayList luceneIndexes; +// +// public void registerLuceneIndex(LuceneIndex luceneIndex) { +// if (luceneIndexes == null) { +// luceneIndexes = new ArrayList(); +// } +// luceneIndexes.add(luceneIndex); +// } +// +// public boolean isDeltaRequired() { +// return true;//luceneIndexes != null; +// } + + /** + * Return true if by default this property is set to fetch eager. + * Lob's usually default to fetch lazy. + */ + public boolean isFetchEager() { + return fetchEager; + } + + /** + * Return true if this is mapped to a Clob Blob LongVarchar or + * LongVarbinary. + */ + public boolean isLob() { + return lob; + } + + private boolean isLobType(int type) { + switch (type) { + case Types.CLOB: + return true; + case Types.BLOB: + return true; + case Types.LONGVARBINARY: + return true; + case Types.LONGVARCHAR: + return true; + + default: + return false; + } + } + + /** + * Return the DB bind parameter. Typically is "?" but different for + * encrypted bind. + */ + public String getDbBind() { + return dbBind; + } + + /** + * Returns true if DB encrypted. + */ + public boolean isLocalEncrypted() { + return localEncrypted; + } + + /** + * Return true if this property is stored encrypted. + */ + public boolean isDbEncrypted() { + return dbEncrypted; + } + + public int getDbEncryptedType() { + return dbEncryptedType; + } + + /** + * Return true if this property should be included in an Insert. + */ + public boolean isDbInsertable() { + return dbInsertable; + } + + /** + * Return true if this property should be included in an Update. + */ + public boolean isDbUpdatable() { + return dbUpdatable; + } + + /** + * Return true if this property is included in database queries. + */ + public boolean isDbRead() { + return dbRead; + } + + /** + * Return true if this property is based on a secondary table (not the base + * table). + */ + public boolean isSecondaryTable() { + return secondaryTable; + } + + /** + * Return the property type. + */ + public Class getPropertyType() { + return propertyType; + } + + /** + * Return true if this is included in the unique id. + */ + public boolean isId() { + return id; + } + + /** + * Return true if this is an Embedded property. In this case it shares the + * table and primary key of its owner object. + */ + public boolean isEmbedded() { + return embedded; + } + + /** + * Return an extra attribute set on this property. + */ + public String getExtraAttribute(String key) { + return extraAttributeMap.get(key); + } + + /** + * Return the default value. + */ + public Object getDefaultValue() { + return defaultValue; + } + + public String toString() { + return name; + } + + @SuppressWarnings("unchecked") + public void jsonWrite(WriteJsonContext ctx, Object bean) { + + Object value = getValueIntercept(bean); + if (value == null) { + ctx.appendNull(name); + } else { + ctx.appendNameValue(name, scalarType, value); + } + } + + public void jsonRead(ReadJsonContext ctx, Object bean) { + + String jsonValue; + try { + jsonValue = ctx.readScalarValue(); + } catch (TextException e){ + throw new TextException("Error reading property "+getFullBeanName(), e); + } + Object objValue; + if (jsonValue == null) { + objValue = null; + } else { + objValue = scalarType.jsonFromString(jsonValue, ctx.getValueAdapter()); + } + setValue(bean, objValue); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssoc.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssoc.java index dd1658bc1..e9ad69581 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssoc.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssoc.java @@ -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 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 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 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 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. - *

- * This will return null for multiple Id properties. - *

- */ - public String getTargetIdProperty() { - return targetIdProperty; - } - - /** - * Return the BeanDescriptor of the target. - */ - public BeanDescriptor 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. - *

- * 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. - *

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

- * This is the class of the associated bean, or beans contained in a list, - * set or map. - *

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

- * This has the table name which is used to determine the relationship for - * this association. - *

- */ - 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 list = new ArrayList(); - - 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 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 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 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 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. + *

+ * This will return null for multiple Id properties. + *

+ */ + public String getTargetIdProperty() { + return targetIdProperty; + } + + /** + * Return the BeanDescriptor of the target. + */ + public BeanDescriptor 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. + *

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

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

+ * This is the class of the associated bean, or beans contained in a list, + * set or map. + *

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

+ * This has the table name which is used to determine the relationship for + * this association. + *

+ */ + 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 list = new ArrayList(); + + 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); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java index a4925a766..960676d44 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocMany.java @@ -1,845 +1,826 @@ -/** - * 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.Iterator; -import java.util.List; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.Expression; -import com.avaje.ebean.InvalidValue; -import com.avaje.ebean.Query; -import com.avaje.ebean.SqlUpdate; -import com.avaje.ebean.Transaction; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; -import com.avaje.ebean.bean.BeanCollectionAdd; -import com.avaje.ebean.bean.BeanCollectionLoader; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; -import com.avaje.ebeaninternal.server.deploy.id.ImportedId; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; -import com.avaje.ebeaninternal.server.query.SqlBeanLoad; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext.ReadBeanState; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; - -/** - * Property mapped to a List Set or Map. - */ -public class BeanPropertyAssocMany extends BeanPropertyAssoc { - - /** - * Join for manyToMany intersection table. - */ - final TableJoin intersectionJoin; - - /** - * For ManyToMany this is the Inverse join used to build reference queries. - */ - final TableJoin inverseJoin; - - /** - * Flag to indicate that this is a unidirectional relationship. - */ - final boolean unidirectional; - - /** - * Flag to indicate manyToMany relationship. - */ - final boolean manyToMany; - - final String fetchOrderBy; - - final String mapKey; - - /** - * The type of the many, set, list or map. - */ - final ManyType manyType; - - final String serverName; - - final ModifyListenMode modifyListenMode; - - BeanProperty mapKeyProperty; - /** - * Derived list of exported property and matching foreignKey - */ - ExportedProperty[] exportedProperties; - - /** - * Property on the 'child' bean that links back to the 'master'. - */ - BeanPropertyAssocOne childMasterProperty; - - boolean embeddedExportedProperties; - - BeanCollectionHelp help; - - ImportedId importedId; - - String deleteByParentIdSql; - String deleteByParentIdInSql; - - - final CollectionTypeConverter typeConverter; - - /** - * Create this property. - */ - public BeanPropertyAssocMany(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertyAssocMany deploy) { - super(owner, descriptor, deploy); - this.unidirectional = deploy.isUnidirectional(); - this.manyToMany = deploy.isManyToMany(); - this.serverName = descriptor.getServerName(); - this.manyType = deploy.getManyType(); - this.typeConverter = manyType.getTypeConverter(); - this.mapKey = deploy.getMapKey(); - this.fetchOrderBy = deploy.getFetchOrderBy(); - - this.intersectionJoin = deploy.createIntersectionTableJoin(); - this.inverseJoin = deploy.createInverseTableJoin(); - this.modifyListenMode = deploy.getModifyListenMode(); - } - - public void initialise() { - super.initialise(); - - if (!isTransient){ - this.help = BeanCollectionHelpFactory.create(this); - - if (manyToMany){ - // only manyToMany's have imported properties - importedId = createImportedId(this, targetDescriptor, tableJoin); - - } else { - // find the property in the many that matches - // back to the master (Order in the OrderDetail bean) - childMasterProperty = initChildMasterProperty(); - if (childMasterProperty != null){ - childMasterProperty.setRelationshipProperty(this); - } - } - - if (mapKey != null){ - mapKeyProperty = initMapKeyProperty(); - } - - exportedProperties = createExported(); - if (exportedProperties.length > 0){ - embeddedExportedProperties = exportedProperties[0].isEmbedded(); - } - - String delStmt; - if (manyToMany){ - delStmt = "delete from "+inverseJoin.getTable()+" where "; - } else { - delStmt = "delete from "+targetDescriptor.getBaseTable()+" where "; - } - deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false); - deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true); - } - } - - /** - * Get the underlying List Set or Map. - * For unwrapping scala collection types etc. - */ - public Object getValueUnderlying(Object bean) { - - Object value = getValue(bean); - if (typeConverter != null){ - value = typeConverter.toUnderlying(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) { - if (typeConverter != null){ - value = typeConverter.toWrapped(value); - } - super.setValue(bean, value); - } - - @Override - public void setValueIntercept(Object bean, Object value) { - if (typeConverter != null){ - value = typeConverter.toWrapped(value); - } - super.setValueIntercept(bean, value); - } - - public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { - return createElPropertyValue(propName, remainder, chain, propertyDeploy); - } - - public SqlUpdate deleteByParentId(Object parentId, List parentIdist) { - if (parentId != null){ - return deleteByParentId(parentId); - } else { - return deleteByParentIdList(parentIdist); - } - } - - private SqlUpdate deleteByParentId(Object parentId) { - DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByParentIdSql); - bindWhereParendId(sqlDelete, parentId); - return sqlDelete; - } - - /** - * Find the Id's of detail beans given a parent Id or list of parent Id's. - */ - public List findIdsByParentId(Object parentId, List parentIdist, Transaction t, ArrayList excludeDetailIds) { - if (parentId != null){ - return findIdsByParentId(parentId, t, excludeDetailIds); - } else { - return findIdsByParentIdList(parentIdist, t, excludeDetailIds); - } - } - - private List findIdsByParentId(Object parentId, Transaction t, ArrayList excludeDetailIds) { - - String rawWhere = deriveWhereParentIdSql(false); - - EbeanServer server = getBeanDescriptor().getEbeanServer(); - Query q = server.find(getPropertyType()) - .where().raw(rawWhere).query(); - - bindWhereParendId(1, q, parentId); - - if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { - Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds); - q.where().not(idIn); - } - - return server.findIds(q, t); - } - - private List findIdsByParentIdList(List parentIdist, Transaction t, ArrayList excludeDetailIds) { - - String rawWhere = deriveWhereParentIdSql(true); - String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size()); - - String expr = rawWhere+inClause; - - EbeanServer server = getBeanDescriptor().getEbeanServer(); - Query q = server.find(getPropertyType()) - .where().raw(expr).query(); - - int pos = 1; - for (int i = 0; i < parentIdist.size(); i++) { - pos = bindWhereParendId(pos, q, parentIdist.get(i)); - } - - if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { - Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds); - q.where().not(idIn); - } - - return server.findIds(q, t); - } - - private SqlUpdate deleteByParentIdList(List parentIdist) { - - StringBuilder sb = new StringBuilder(100); - sb.append(deleteByParentIdInSql); - - String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size()); - sb.append(inClause); - - DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString()); - for (int i = 0; i < parentIdist.size(); i++) { - bindWhereParendId(delete, parentIdist.get(i)); - } - - return delete; - } - - /** - * Set the lazy load server to help create reference collections (that lazy - * load on demand). - */ - public void setLoader(BeanCollectionLoader loader){ - if (help != null){ - help.setLoader(loader); - } - } - - /** - * Return the mode for listening to modifications to collections for this - * association. - */ - public ModifyListenMode getModifyListenMode() { - return modifyListenMode; - } - - /** - * Ignore changes for Many properties. - */ - public boolean hasChanged(Object bean, Object oldValues) { - return false; - } - - @Override - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - } - - @Override - public void loadIgnore(DbReadContext ctx) { - // nothing to ignore for Many - } - - @Override - public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { - sqlBeanLoad.loadAssocMany(this); - } - - @Override - public Object readSet(DbReadContext ctx, Object bean, Class type) throws SQLException { - return null; - } - - @Override - public Object read(DbReadContext ctx) throws SQLException { - return null; - } - - - @Override - public boolean isValueLoaded(Object value) { - if (value instanceof BeanCollection){ - return ((BeanCollection)value).isPopulated(); - } - return true; - } - - public void add(BeanCollection collection, Object bean) { - help.add(collection, bean); - } - - @Override - public InvalidValue validateCascade(Object manyValue) { - - ArrayList errs = help.validate(manyValue); - - if (errs == null){ - return null; - } else { - return new InvalidValue("recurse.many", targetDescriptor.getFullName(), manyValue, InvalidValue.toArray(errs)); - } - } - - /** - * Refresh the appropriate list set or map. - */ - public void refresh(EbeanServer server, Query query, Transaction t, Object parentBean) { - help.refresh(server, query, t, parentBean); - } - - /** - * Apply the refreshed BeanCollection to the property of the parentBean. - */ - public void refresh(BeanCollection bc, Object parentBean) { - help.refresh(bc, parentBean); - } - - /** - * Return the Id values from the given bean. - */ - @Override - public Object[] getAssocOneIdValues(Object bean) { - return targetDescriptor.getIdBinder().getIdValues(bean); - } - - /** - * Return the Id expression to add to where clause etc. - */ - public String getAssocOneIdExpr(String prefix, String operator) { - return targetDescriptor.getIdBinder().getAssocOneIdExpr(prefix, operator); - } - - /** - * Return the logical id value expression taking into account embedded id's. - */ - @Override - public String getAssocIdInValueExpr(int size){ - return targetDescriptor.getIdBinder().getIdInValueExpr(size); - } - - /** - * Return the logical id in expression taking into account embedded id's. - */ - @Override - public String getAssocIdInExpr(String prefix){ - return targetDescriptor.getIdBinder().getAssocIdInExpr(prefix); - } - - - @Override - public boolean isAssocId() { - return true; - } - - @Override - public boolean isAssocProperty() { - return true; - } - - /** - * Returns true. - */ - @Override - public boolean containsMany(){ - return true; - } - - /** - * Return the many type. - */ - public ManyType getManyType() { - return manyType; - } - - /** - * Return true if this is many to many. - */ - public boolean isManyToMany() { - return manyToMany; - } - - /** - * ManyToMany only, join from local table to intersection table. - */ - public TableJoin getIntersectionTableJoin() { - return intersectionJoin; - } - - /** - * Set the join properties from the parent bean to the child bean. - * This is only valid for OneToMany and NOT valid for ManyToMany. - */ - public void setJoinValuesToChild(Object parent, Object child, Object mapKeyValue) { - - if (mapKeyProperty != null){ - mapKeyProperty.setValue(child, mapKeyValue); - } - - if (!manyToMany){ - if (childMasterProperty != null){ - // bidirectional in the sense that the 'master' property - // exists on the 'detail' bean - childMasterProperty.setValue(child, parent); - } else { - // unidirectional in the sense that the 'master' property - // does NOT exist on the 'detail' bean - } - } - } - - /** - * Return the order by clause used to order the fetching of the data for - * this list, set or map. - */ - public String getFetchOrderBy() { - return fetchOrderBy; - } - - /** - * Return the default mapKey when returning a Map. - */ - public String getMapKey() { - return mapKey; - } - - public BeanCollection createReferenceIfNull(Object parentBean) { - - Object v = getValue(parentBean); - if (v instanceof BeanCollection){ - BeanCollection bc = (BeanCollection)v; - return bc.isReference() ? bc : null; - } else { - return createReference(parentBean); - } - } - - public BeanCollection createReference(Object parentBean) { - - BeanCollection ref = help.createReference(parentBean, name); - setValue(parentBean, ref); - return ref; - } - - public Object createEmpty(boolean vanilla) { - return help.createEmpty(vanilla); - } - - public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { - return help.getBeanCollectionAdd(bc, mapKey); - } - - public Object getParentId(Object parentBean) { - return descriptor.getId(parentBean); - } - - private void bindWhereParendId(DefaultSqlUpdate sqlUpd, Object parentId){ - - if (exportedProperties.length == 1){ - sqlUpd.addParameter(parentId); - return; - } - for (int i = 0; i < exportedProperties.length; i++) { - Object embVal = exportedProperties[i].getValue(parentId); - sqlUpd.addParameter(embVal); - } - } - - private int bindWhereParendId(int pos, Query q, Object parentId) { - - if (exportedProperties.length == 1) { - q.setParameter(pos++, parentId); - - } else { - - for (int i = 0; i < exportedProperties.length; i++) { - Object embVal = exportedProperties[i].getValue(parentId); - q.setParameter(pos++, embVal); - } - } - return pos; - } - - private String deriveWhereParentIdSql(boolean inClause) { - - StringBuilder sb = new StringBuilder(); - - if (inClause){ - sb.append("("); - } - for (int i = 0; i < exportedProperties.length; i++) { - String fkColumn = exportedProperties[i].getForeignDbColumn(); - if (i > 0){ - String s = inClause ? "," : " and "; - sb.append(s); - } - sb.append(fkColumn); - if (!inClause){ - sb.append("=? "); - } - } - if (inClause){ - sb.append(")"); - } - return sb.toString(); - } - - public void setPredicates(SpiQuery query, Object parentBean) { - - if (manyToMany){ - // for ManyToMany lazy loading we need to include a - // join to the intersection table. The predicate column - // is not on the 'destination many table'. - query.setIncludeTableJoin(inverseJoin); - } - - if (embeddedExportedProperties) { - // use the EmbeddedId object instead of the parentBean - BeanProperty[] uids = descriptor.propertiesId(); - parentBean = uids[0].getValue(parentBean); - } - - for (int i = 0; i < exportedProperties.length; i++) { - Object val = exportedProperties[i].getValue(parentBean); - String fkColumn = exportedProperties[i].getForeignDbColumn(); - if (!manyToMany){ - fkColumn = targetDescriptor.getBaseTableAlias()+"."+fkColumn; - } else { - // use hard coded alias for intersection table - fkColumn = "int_."+fkColumn; - } - query.where().eq(fkColumn, val); - } - - if (extraWhere != null){ - // replace the table alias place holder - String ta = targetDescriptor.getBaseTableAlias(); - String where = StringHelper.replaceString(extraWhere, "${ta}", ta); - query.where().raw(where); - } - - if (fetchOrderBy != null){ - query.order(fetchOrderBy); - } - } - - /** - * Create the array of ExportedProperty used to build reference objects. - */ - private ExportedProperty[] createExported() { - - BeanProperty[] uids = descriptor.propertiesId(); - - ArrayList list = new ArrayList(); - - if (uids.length == 1 && uids[0].isEmbedded()) { - - BeanPropertyAssocOne one = (BeanPropertyAssocOne) uids[0]; - BeanDescriptor targetDesc = one.getTargetDescriptor(); - BeanProperty[] emIds = targetDesc.propertiesBaseScalar(); - try { - for (int i = 0; i < emIds.length; i++) { - ExportedProperty expProp = findMatch(true, emIds[i]); - list.add(expProp); - } - } catch (PersistenceException e){ - // not found as individual scalar properties - e.printStackTrace(); - } - - } else { - for (int i = 0; i < uids.length; i++) { - ExportedProperty expProp = findMatch(false, uids[i]); - list.add(expProp); - } - } - - return (ExportedProperty[]) list.toArray(new ExportedProperty[list.size()]); - } - - /** - * Find the matching foreignDbColumn for a given local property. - */ - private ExportedProperty findMatch(boolean embedded,BeanProperty prop) { - - String matchColumn = prop.getDbColumn(); - - String searchTable; - TableJoinColumn[] columns; - if (manyToMany){ - // look for column going to intersection - columns = intersectionJoin.columns(); - searchTable = intersectionJoin.getTable(); - - } else { - columns = tableJoin.columns(); - searchTable = tableJoin.getTable(); - } - for (int i = 0; i < columns.length; i++) { - String matchTo = columns[i].getLocalDbColumn(); - - if (matchColumn.equalsIgnoreCase(matchTo)) { - String foreignCol = columns[i].getForeignDbColumn(); - return new ExportedProperty(embedded, foreignCol, prop); - } - } - - String msg = "Error with the Join on ["+getFullBeanName() - +"]. Could not find the matching foreign key for ["+matchColumn+"] in table["+searchTable+"]?" - +" Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?"; - throw new PersistenceException(msg); - } - - /** - * Return the child property that links back to the master bean. - *

- * Note that childMasterProperty will be null if a field is used instead of - * a ManyToOne bean association. - *

- */ - private BeanPropertyAssocOne initChildMasterProperty() { - - if (unidirectional){ - return null; - } - - // search for the property, to see if it exists - Class beanType = descriptor.getBeanType(); - BeanDescriptor targetDesc = getTargetDescriptor(); - - BeanPropertyAssocOne[] ones = targetDesc.propertiesOne(); - for (int i = 0; i < ones.length; i++) { - BeanPropertyAssocOne prop = ones[i]; - if (mappedBy != null){ - // match using mappedBy as property name - if (mappedBy.equalsIgnoreCase(prop.getName())) { - return prop; - } - } else { - // assume only one property that matches parent object type - if (prop.getTargetType().equals(beanType)) { - // found it, stop search - return prop; - } - } - } - - String msg = "Can not find Master [" + beanType + "] in Child[" + targetDesc + "]"; - throw new RuntimeException(msg); - } - - /** - * Search for and return the mapKey property. - */ - private BeanProperty initMapKeyProperty() { - - // search for the property - - BeanDescriptor targetDesc = getTargetDescriptor(); - - Iterator it = targetDesc.propertiesAll(); - while (it.hasNext()){ - BeanProperty prop = it.next(); - if (mapKey.equalsIgnoreCase(prop.getName())) { - return prop; - } - } - - String from = descriptor.getFullName(); - String to = targetDesc.getFullName(); - String msg = from+": Could not find mapKey property ["+mapKey+"] on ["+to+"]"; - throw new PersistenceException(msg); - } - - public IntersectionRow buildManyDeleteChildren(Object parentBean, ArrayList excludeDetailIds) { - - IntersectionRow row = new IntersectionRow(tableJoin.getTable()); - if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { - row.setExcludeIds(excludeDetailIds, getTargetDescriptor()); - } - buildExport(row, parentBean); - return row; - } - - public IntersectionRow buildManyToManyDeleteChildren(Object parentBean) { - - IntersectionRow row = new IntersectionRow(intersectionJoin.getTable()); - buildExport(row, parentBean); - return row; - } - - public IntersectionRow buildManyToManyMapBean(Object parent, Object other) { - - IntersectionRow row = new IntersectionRow(intersectionJoin.getTable()); - - buildExport(row, parent); - buildImport(row, other); - return row; - } - - private void buildExport(IntersectionRow row, Object parentBean) { - - if (embeddedExportedProperties) { - BeanProperty[] uids = descriptor.propertiesId(); - parentBean = uids[0].getValue(parentBean); - } - for (int i = 0; i < exportedProperties.length; i++) { - Object val = exportedProperties[i].getValue(parentBean); - String fkColumn = exportedProperties[i].getForeignDbColumn(); - - row.put(fkColumn, val); - } - } - - /** - * Set the predicates for lazy loading of the association. - * Handles predicates for both OneToMany and ManyToMany. - */ - private void buildImport(IntersectionRow row, Object otherBean) { - - importedId.buildImport(row, otherBean); - } - - /** - * Return true if the otherBean has an Id value. - */ - public boolean hasImportedId(Object otherBean) { - - return null != targetDescriptor.getId(otherBean); - } - - public void jsonWrite(WriteJsonContext ctx, Object bean) { - - Boolean include = ctx.includeMany(name); - if (Boolean.FALSE.equals(include)){ - return; - } - - Object value = getValueIntercept(bean); - if (value != null){ - ctx.pushParentBeanMany(bean); - help.jsonWrite(ctx, name, value, include != null); - ctx.popParentBeanMany(); - } - } - - public void jsonRead(ReadJsonContext ctx, Object bean){ - - if (!ctx.readArrayBegin()) { - // the array is null - return; - } - - Object collection = help.createEmpty(false); - BeanCollectionAdd add = getBeanCollectionAdd(collection, null); - do { - ReadBeanState detailBeanState = targetDescriptor.jsonRead(ctx, name); - if (detailBeanState == null){ - // probably empty array - break; - } - Object detailBean = detailBeanState.getBean(); - add.addBean(detailBean); - - if (bean != null && childMasterProperty != null){ - // bind detail bean back to master via mappedBy property - childMasterProperty.setValue(detailBean, bean); - detailBeanState.setLoaded(childMasterProperty.getName()); - } - - detailBeanState.setLoadedState(); - - if (!ctx.readArrayNext()){ - break; - } - } while(true); - - setValue(bean, collection); - - } -} +package com.avaje.ebeaninternal.server.deploy; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.Expression; +import com.avaje.ebean.InvalidValue; +import com.avaje.ebean.Query; +import com.avaje.ebean.SqlUpdate; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; +import com.avaje.ebean.bean.BeanCollectionAdd; +import com.avaje.ebean.bean.BeanCollectionLoader; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; +import com.avaje.ebeaninternal.server.deploy.id.ImportedId; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import com.avaje.ebeaninternal.server.query.SqlBeanLoad; +import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; +import com.avaje.ebeaninternal.server.text.json.ReadJsonContext.ReadBeanState; +import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; + +/** + * Property mapped to a List Set or Map. + */ +public class BeanPropertyAssocMany extends BeanPropertyAssoc { + + /** + * Join for manyToMany intersection table. + */ + final TableJoin intersectionJoin; + + /** + * For ManyToMany this is the Inverse join used to build reference queries. + */ + final TableJoin inverseJoin; + + /** + * Flag to indicate that this is a unidirectional relationship. + */ + final boolean unidirectional; + + /** + * Flag to indicate manyToMany relationship. + */ + final boolean manyToMany; + + final String fetchOrderBy; + + final String mapKey; + + /** + * The type of the many, set, list or map. + */ + final ManyType manyType; + + final String serverName; + + final ModifyListenMode modifyListenMode; + + BeanProperty mapKeyProperty; + /** + * Derived list of exported property and matching foreignKey + */ + ExportedProperty[] exportedProperties; + + /** + * Property on the 'child' bean that links back to the 'master'. + */ + BeanPropertyAssocOne childMasterProperty; + + boolean embeddedExportedProperties; + + BeanCollectionHelp help; + + ImportedId importedId; + + String deleteByParentIdSql; + String deleteByParentIdInSql; + + + final CollectionTypeConverter typeConverter; + + /** + * Create this property. + */ + public BeanPropertyAssocMany(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertyAssocMany deploy) { + super(owner, descriptor, deploy); + this.unidirectional = deploy.isUnidirectional(); + this.manyToMany = deploy.isManyToMany(); + this.serverName = descriptor.getServerName(); + this.manyType = deploy.getManyType(); + this.typeConverter = manyType.getTypeConverter(); + this.mapKey = deploy.getMapKey(); + this.fetchOrderBy = deploy.getFetchOrderBy(); + + this.intersectionJoin = deploy.createIntersectionTableJoin(); + this.inverseJoin = deploy.createInverseTableJoin(); + this.modifyListenMode = deploy.getModifyListenMode(); + } + + public void initialise() { + super.initialise(); + + if (!isTransient){ + this.help = BeanCollectionHelpFactory.create(this); + + if (manyToMany){ + // only manyToMany's have imported properties + importedId = createImportedId(this, targetDescriptor, tableJoin); + + } else { + // find the property in the many that matches + // back to the master (Order in the OrderDetail bean) + childMasterProperty = initChildMasterProperty(); + if (childMasterProperty != null){ + childMasterProperty.setRelationshipProperty(this); + } + } + + if (mapKey != null){ + mapKeyProperty = initMapKeyProperty(); + } + + exportedProperties = createExported(); + if (exportedProperties.length > 0){ + embeddedExportedProperties = exportedProperties[0].isEmbedded(); + } + + String delStmt; + if (manyToMany){ + delStmt = "delete from "+inverseJoin.getTable()+" where "; + } else { + delStmt = "delete from "+targetDescriptor.getBaseTable()+" where "; + } + deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false); + deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true); + } + } + + /** + * Get the underlying List Set or Map. + * For unwrapping scala collection types etc. + */ + public Object getValueUnderlying(Object bean) { + + Object value = getValue(bean); + if (typeConverter != null){ + value = typeConverter.toUnderlying(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) { + if (typeConverter != null){ + value = typeConverter.toWrapped(value); + } + super.setValue(bean, value); + } + + @Override + public void setValueIntercept(Object bean, Object value) { + if (typeConverter != null){ + value = typeConverter.toWrapped(value); + } + super.setValueIntercept(bean, value); + } + + public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { + return createElPropertyValue(propName, remainder, chain, propertyDeploy); + } + + public SqlUpdate deleteByParentId(Object parentId, List parentIdist) { + if (parentId != null){ + return deleteByParentId(parentId); + } else { + return deleteByParentIdList(parentIdist); + } + } + + private SqlUpdate deleteByParentId(Object parentId) { + DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByParentIdSql); + bindWhereParendId(sqlDelete, parentId); + return sqlDelete; + } + + /** + * Find the Id's of detail beans given a parent Id or list of parent Id's. + */ + public List findIdsByParentId(Object parentId, List parentIdist, Transaction t, ArrayList excludeDetailIds) { + if (parentId != null){ + return findIdsByParentId(parentId, t, excludeDetailIds); + } else { + return findIdsByParentIdList(parentIdist, t, excludeDetailIds); + } + } + + private List findIdsByParentId(Object parentId, Transaction t, ArrayList excludeDetailIds) { + + String rawWhere = deriveWhereParentIdSql(false); + + EbeanServer server = getBeanDescriptor().getEbeanServer(); + Query q = server.find(getPropertyType()) + .where().raw(rawWhere).query(); + + bindWhereParendId(1, q, parentId); + + if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { + Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds); + q.where().not(idIn); + } + + return server.findIds(q, t); + } + + private List findIdsByParentIdList(List parentIdist, Transaction t, ArrayList excludeDetailIds) { + + String rawWhere = deriveWhereParentIdSql(true); + String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size()); + + String expr = rawWhere+inClause; + + EbeanServer server = getBeanDescriptor().getEbeanServer(); + Query q = server.find(getPropertyType()) + .where().raw(expr).query(); + + int pos = 1; + for (int i = 0; i < parentIdist.size(); i++) { + pos = bindWhereParendId(pos, q, parentIdist.get(i)); + } + + if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { + Expression idIn = q.getExpressionFactory().idIn(excludeDetailIds); + q.where().not(idIn); + } + + return server.findIds(q, t); + } + + private SqlUpdate deleteByParentIdList(List parentIdist) { + + StringBuilder sb = new StringBuilder(100); + sb.append(deleteByParentIdInSql); + + String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size()); + sb.append(inClause); + + DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString()); + for (int i = 0; i < parentIdist.size(); i++) { + bindWhereParendId(delete, parentIdist.get(i)); + } + + return delete; + } + + /** + * Set the lazy load server to help create reference collections (that lazy + * load on demand). + */ + public void setLoader(BeanCollectionLoader loader){ + if (help != null){ + help.setLoader(loader); + } + } + + /** + * Return the mode for listening to modifications to collections for this + * association. + */ + public ModifyListenMode getModifyListenMode() { + return modifyListenMode; + } + + /** + * Ignore changes for Many properties. + */ + public boolean hasChanged(Object bean, Object oldValues) { + return false; + } + + @Override + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + } + + @Override + public void loadIgnore(DbReadContext ctx) { + // nothing to ignore for Many + } + + @Override + public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { + sqlBeanLoad.loadAssocMany(this); + } + + @Override + public Object readSet(DbReadContext ctx, Object bean, Class type) throws SQLException { + return null; + } + + @Override + public Object read(DbReadContext ctx) throws SQLException { + return null; + } + + + @Override + public boolean isValueLoaded(Object value) { + if (value instanceof BeanCollection){ + return ((BeanCollection)value).isPopulated(); + } + return true; + } + + public void add(BeanCollection collection, Object bean) { + help.add(collection, bean); + } + + @Override + public InvalidValue validateCascade(Object manyValue) { + + ArrayList errs = help.validate(manyValue); + + if (errs == null){ + return null; + } else { + return new InvalidValue("recurse.many", targetDescriptor.getFullName(), manyValue, InvalidValue.toArray(errs)); + } + } + + /** + * Refresh the appropriate list set or map. + */ + public void refresh(EbeanServer server, Query query, Transaction t, Object parentBean) { + help.refresh(server, query, t, parentBean); + } + + /** + * Apply the refreshed BeanCollection to the property of the parentBean. + */ + public void refresh(BeanCollection bc, Object parentBean) { + help.refresh(bc, parentBean); + } + + /** + * Return the Id values from the given bean. + */ + @Override + public Object[] getAssocOneIdValues(Object bean) { + return targetDescriptor.getIdBinder().getIdValues(bean); + } + + /** + * Return the Id expression to add to where clause etc. + */ + public String getAssocOneIdExpr(String prefix, String operator) { + return targetDescriptor.getIdBinder().getAssocOneIdExpr(prefix, operator); + } + + /** + * Return the logical id value expression taking into account embedded id's. + */ + @Override + public String getAssocIdInValueExpr(int size){ + return targetDescriptor.getIdBinder().getIdInValueExpr(size); + } + + /** + * Return the logical id in expression taking into account embedded id's. + */ + @Override + public String getAssocIdInExpr(String prefix){ + return targetDescriptor.getIdBinder().getAssocIdInExpr(prefix); + } + + + @Override + public boolean isAssocId() { + return true; + } + + @Override + public boolean isAssocProperty() { + return true; + } + + /** + * Returns true. + */ + @Override + public boolean containsMany(){ + return true; + } + + /** + * Return the many type. + */ + public ManyType getManyType() { + return manyType; + } + + /** + * Return true if this is many to many. + */ + public boolean isManyToMany() { + return manyToMany; + } + + /** + * ManyToMany only, join from local table to intersection table. + */ + public TableJoin getIntersectionTableJoin() { + return intersectionJoin; + } + + /** + * Set the join properties from the parent bean to the child bean. + * This is only valid for OneToMany and NOT valid for ManyToMany. + */ + public void setJoinValuesToChild(Object parent, Object child, Object mapKeyValue) { + + if (mapKeyProperty != null){ + mapKeyProperty.setValue(child, mapKeyValue); + } + + if (!manyToMany){ + if (childMasterProperty != null){ + // bidirectional in the sense that the 'master' property + // exists on the 'detail' bean + childMasterProperty.setValue(child, parent); + } else { + // unidirectional in the sense that the 'master' property + // does NOT exist on the 'detail' bean + } + } + } + + /** + * Return the order by clause used to order the fetching of the data for + * this list, set or map. + */ + public String getFetchOrderBy() { + return fetchOrderBy; + } + + /** + * Return the default mapKey when returning a Map. + */ + public String getMapKey() { + return mapKey; + } + + public BeanCollection createReferenceIfNull(Object parentBean) { + + Object v = getValue(parentBean); + if (v instanceof BeanCollection){ + BeanCollection bc = (BeanCollection)v; + return bc.isReference() ? bc : null; + } else { + return createReference(parentBean); + } + } + + public BeanCollection createReference(Object parentBean) { + + BeanCollection ref = help.createReference(parentBean, name); + setValue(parentBean, ref); + return ref; + } + + public Object createEmpty(boolean vanilla) { + return help.createEmpty(vanilla); + } + + public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { + return help.getBeanCollectionAdd(bc, mapKey); + } + + public Object getParentId(Object parentBean) { + return descriptor.getId(parentBean); + } + + private void bindWhereParendId(DefaultSqlUpdate sqlUpd, Object parentId){ + + if (exportedProperties.length == 1){ + sqlUpd.addParameter(parentId); + return; + } + for (int i = 0; i < exportedProperties.length; i++) { + Object embVal = exportedProperties[i].getValue(parentId); + sqlUpd.addParameter(embVal); + } + } + + private int bindWhereParendId(int pos, Query q, Object parentId) { + + if (exportedProperties.length == 1) { + q.setParameter(pos++, parentId); + + } else { + + for (int i = 0; i < exportedProperties.length; i++) { + Object embVal = exportedProperties[i].getValue(parentId); + q.setParameter(pos++, embVal); + } + } + return pos; + } + + private String deriveWhereParentIdSql(boolean inClause) { + + StringBuilder sb = new StringBuilder(); + + if (inClause){ + sb.append("("); + } + for (int i = 0; i < exportedProperties.length; i++) { + String fkColumn = exportedProperties[i].getForeignDbColumn(); + if (i > 0){ + String s = inClause ? "," : " and "; + sb.append(s); + } + sb.append(fkColumn); + if (!inClause){ + sb.append("=? "); + } + } + if (inClause){ + sb.append(")"); + } + return sb.toString(); + } + + public void setPredicates(SpiQuery query, Object parentBean) { + + if (manyToMany){ + // for ManyToMany lazy loading we need to include a + // join to the intersection table. The predicate column + // is not on the 'destination many table'. + query.setIncludeTableJoin(inverseJoin); + } + + if (embeddedExportedProperties) { + // use the EmbeddedId object instead of the parentBean + BeanProperty[] uids = descriptor.propertiesId(); + parentBean = uids[0].getValue(parentBean); + } + + for (int i = 0; i < exportedProperties.length; i++) { + Object val = exportedProperties[i].getValue(parentBean); + String fkColumn = exportedProperties[i].getForeignDbColumn(); + if (!manyToMany){ + fkColumn = targetDescriptor.getBaseTableAlias()+"."+fkColumn; + } else { + // use hard coded alias for intersection table + fkColumn = "int_."+fkColumn; + } + query.where().eq(fkColumn, val); + } + + if (extraWhere != null){ + // replace the table alias place holder + String ta = targetDescriptor.getBaseTableAlias(); + String where = StringHelper.replaceString(extraWhere, "${ta}", ta); + query.where().raw(where); + } + + if (fetchOrderBy != null){ + query.order(fetchOrderBy); + } + } + + /** + * Create the array of ExportedProperty used to build reference objects. + */ + private ExportedProperty[] createExported() { + + BeanProperty[] uids = descriptor.propertiesId(); + + ArrayList list = new ArrayList(); + + if (uids.length == 1 && uids[0].isEmbedded()) { + + BeanPropertyAssocOne one = (BeanPropertyAssocOne) uids[0]; + BeanDescriptor targetDesc = one.getTargetDescriptor(); + BeanProperty[] emIds = targetDesc.propertiesBaseScalar(); + try { + for (int i = 0; i < emIds.length; i++) { + ExportedProperty expProp = findMatch(true, emIds[i]); + list.add(expProp); + } + } catch (PersistenceException e){ + // not found as individual scalar properties + e.printStackTrace(); + } + + } else { + for (int i = 0; i < uids.length; i++) { + ExportedProperty expProp = findMatch(false, uids[i]); + list.add(expProp); + } + } + + return (ExportedProperty[]) list.toArray(new ExportedProperty[list.size()]); + } + + /** + * Find the matching foreignDbColumn for a given local property. + */ + private ExportedProperty findMatch(boolean embedded,BeanProperty prop) { + + String matchColumn = prop.getDbColumn(); + + String searchTable; + TableJoinColumn[] columns; + if (manyToMany){ + // look for column going to intersection + columns = intersectionJoin.columns(); + searchTable = intersectionJoin.getTable(); + + } else { + columns = tableJoin.columns(); + searchTable = tableJoin.getTable(); + } + for (int i = 0; i < columns.length; i++) { + String matchTo = columns[i].getLocalDbColumn(); + + if (matchColumn.equalsIgnoreCase(matchTo)) { + String foreignCol = columns[i].getForeignDbColumn(); + return new ExportedProperty(embedded, foreignCol, prop); + } + } + + String msg = "Error with the Join on ["+getFullBeanName() + +"]. Could not find the matching foreign key for ["+matchColumn+"] in table["+searchTable+"]?" + +" Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?"; + throw new PersistenceException(msg); + } + + /** + * Return the child property that links back to the master bean. + *

+ * Note that childMasterProperty will be null if a field is used instead of + * a ManyToOne bean association. + *

+ */ + private BeanPropertyAssocOne initChildMasterProperty() { + + if (unidirectional){ + return null; + } + + // search for the property, to see if it exists + Class beanType = descriptor.getBeanType(); + BeanDescriptor targetDesc = getTargetDescriptor(); + + BeanPropertyAssocOne[] ones = targetDesc.propertiesOne(); + for (int i = 0; i < ones.length; i++) { + BeanPropertyAssocOne prop = ones[i]; + if (mappedBy != null){ + // match using mappedBy as property name + if (mappedBy.equalsIgnoreCase(prop.getName())) { + return prop; + } + } else { + // assume only one property that matches parent object type + if (prop.getTargetType().equals(beanType)) { + // found it, stop search + return prop; + } + } + } + + String msg = "Can not find Master [" + beanType + "] in Child[" + targetDesc + "]"; + throw new RuntimeException(msg); + } + + /** + * Search for and return the mapKey property. + */ + private BeanProperty initMapKeyProperty() { + + // search for the property + + BeanDescriptor targetDesc = getTargetDescriptor(); + + Iterator it = targetDesc.propertiesAll(); + while (it.hasNext()){ + BeanProperty prop = it.next(); + if (mapKey.equalsIgnoreCase(prop.getName())) { + return prop; + } + } + + String from = descriptor.getFullName(); + String to = targetDesc.getFullName(); + String msg = from+": Could not find mapKey property ["+mapKey+"] on ["+to+"]"; + throw new PersistenceException(msg); + } + + public IntersectionRow buildManyDeleteChildren(Object parentBean, ArrayList excludeDetailIds) { + + IntersectionRow row = new IntersectionRow(tableJoin.getTable()); + if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) { + row.setExcludeIds(excludeDetailIds, getTargetDescriptor()); + } + buildExport(row, parentBean); + return row; + } + + public IntersectionRow buildManyToManyDeleteChildren(Object parentBean) { + + IntersectionRow row = new IntersectionRow(intersectionJoin.getTable()); + buildExport(row, parentBean); + return row; + } + + public IntersectionRow buildManyToManyMapBean(Object parent, Object other) { + + IntersectionRow row = new IntersectionRow(intersectionJoin.getTable()); + + buildExport(row, parent); + buildImport(row, other); + return row; + } + + private void buildExport(IntersectionRow row, Object parentBean) { + + if (embeddedExportedProperties) { + BeanProperty[] uids = descriptor.propertiesId(); + parentBean = uids[0].getValue(parentBean); + } + for (int i = 0; i < exportedProperties.length; i++) { + Object val = exportedProperties[i].getValue(parentBean); + String fkColumn = exportedProperties[i].getForeignDbColumn(); + + row.put(fkColumn, val); + } + } + + /** + * Set the predicates for lazy loading of the association. + * Handles predicates for both OneToMany and ManyToMany. + */ + private void buildImport(IntersectionRow row, Object otherBean) { + + importedId.buildImport(row, otherBean); + } + + /** + * Return true if the otherBean has an Id value. + */ + public boolean hasImportedId(Object otherBean) { + + return null != targetDescriptor.getId(otherBean); + } + + public void jsonWrite(WriteJsonContext ctx, Object bean) { + + Boolean include = ctx.includeMany(name); + if (Boolean.FALSE.equals(include)){ + return; + } + + Object value = getValueIntercept(bean); + if (value != null){ + ctx.pushParentBeanMany(bean); + help.jsonWrite(ctx, name, value, include != null); + ctx.popParentBeanMany(); + } + } + + public void jsonRead(ReadJsonContext ctx, Object bean){ + + if (!ctx.readArrayBegin()) { + // the array is null + return; + } + + Object collection = help.createEmpty(false); + BeanCollectionAdd add = getBeanCollectionAdd(collection, null); + do { + ReadBeanState detailBeanState = targetDescriptor.jsonRead(ctx, name); + if (detailBeanState == null){ + // probably empty array + break; + } + Object detailBean = detailBeanState.getBean(); + add.addBean(detailBean); + + if (bean != null && childMasterProperty != null){ + // bind detail bean back to master via mappedBy property + childMasterProperty.setValue(detailBean, bean); + detailBeanState.setLoaded(childMasterProperty.getName()); + } + + detailBeanState.setLoadedState(); + + if (!ctx.readArrayNext()){ + break; + } + } while(true); + + setValue(bean, collection); + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index ccbd8b083..24394d9d2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -1,930 +1,911 @@ -/** - * 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 java.util.List; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.InvalidValue; -import com.avaje.ebean.Query; -import com.avaje.ebean.SqlUpdate; -import com.avaje.ebean.Transaction; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; -import com.avaje.ebeaninternal.server.deploy.id.IdBinder; -import com.avaje.ebeaninternal.server.deploy.id.ImportedId; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.query.SplitName; -import com.avaje.ebeaninternal.server.query.SqlBeanLoad; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; - -/** - * Property mapped to a joined bean. - */ -public class BeanPropertyAssocOne extends BeanPropertyAssoc { - - private final boolean oneToOne; - - private final boolean oneToOneExported; - - private final boolean embeddedVersion; - - private final boolean importedPrimaryKey; - - private final LocalHelp localHelp; - - private final BeanProperty[] embeddedProps; - - private final HashMap embeddedPropsMap; - - /** - * The information for Imported foreign Keys. - */ - private ImportedId importedId; - - private ExportedProperty[] exportedProperties; - - private String deleteByParentIdSql; - private String deleteByParentIdInSql; - BeanPropertyAssocMany relationshipProperty; - - /** - * Create based on deploy information of an EmbeddedId. - */ - public BeanPropertyAssocOne(BeanDescriptorMap owner, DeployBeanPropertyAssocOne deploy) { - this(owner, null, deploy); - } - - /** - * Create the property. - */ - public BeanPropertyAssocOne(BeanDescriptorMap owner, BeanDescriptor descriptor, - DeployBeanPropertyAssocOne deploy) { - - super(owner, descriptor, deploy); - - importedPrimaryKey = deploy.isImportedPrimaryKey(); - oneToOne = deploy.isOneToOne(); - oneToOneExported = deploy.isOneToOneExported(); - - if (embedded) { - // Overriding of the columns and use table alias of owning BeanDescriptor - BeanEmbeddedMeta overrideMeta = BeanEmbeddedMetaFactory.create(owner, deploy, descriptor); - embeddedProps = overrideMeta.getProperties(); - if (id) { - embeddedVersion = false; - } else { - embeddedVersion = overrideMeta.isEmbeddedVersion(); - } - embeddedPropsMap = new HashMap(); - for (int i = 0; i < embeddedProps.length; i++) { - embeddedPropsMap.put(embeddedProps[i].getName(), embeddedProps[i]); - } - - } else { - embeddedProps = null; - embeddedPropsMap = null; - embeddedVersion = false; - } - localHelp = createHelp(embedded, oneToOneExported); - } - - @Override - public void initialise() { - super.initialise(); - if (!isTransient) { - if (embedded) { - // no imported or exported information - } else if (!oneToOneExported) { - importedId = createImportedId(this, targetDescriptor, tableJoin); - } else { - exportedProperties = createExported(); - - String delStmt = "delete from "+targetDescriptor.getBaseTable()+" where "; - - deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false); - deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true); - - } - } - } - - public void setRelationshipProperty(BeanPropertyAssocMany relationshipProperty){ - this.relationshipProperty = relationshipProperty; - } - - public BeanPropertyAssocMany getRelationshipProperty() { - return relationshipProperty; - } - - public void cacheClear() { - if (targetDescriptor.isBeanCaching() && relationshipProperty != null) { - targetDescriptor.cacheClearCachedManyIds(relationshipProperty.getName()); - } - } - - public void cacheDelete(boolean clearOnNull, Object bean) { - if (targetDescriptor.isBeanCaching() && relationshipProperty != null) { - Object assocBean = getValue(bean); - if (assocBean != null) { - Object parentId = targetDescriptor.getId(assocBean); - if (parentId != null) { - targetDescriptor.cacheRemoveCachedManyIds(parentId, relationshipProperty.getName()); - return; - } - } - if (clearOnNull) { - targetDescriptor.cacheClearCachedManyIds(relationshipProperty.getName()); - } - } - } - - public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { - - if (embedded){ - BeanProperty embProp = embeddedPropsMap.get(remainder); - if (embProp == null){ - String msg = "Embedded Property "+remainder+" not found in "+getFullBeanName(); - throw new PersistenceException(msg); - } - if (chain == null) { - chain = new ElPropertyChainBuilder(true, propName); - } - chain.add(this); - return chain.add(embProp).build(); - } - - return createElPropertyValue(propName, remainder, chain, propertyDeploy); - } - - @Override - public String getElPlaceholder(boolean encrypted) { - return encrypted ? elPlaceHolderEncrypted : elPlaceHolder; - } - - public SqlUpdate deleteByParentId(Object parentId, List parentIdist) { - if (parentId != null){ - return deleteByParentId(parentId); - } else { - return deleteByParentIdList(parentIdist); - } - } - - private SqlUpdate deleteByParentIdList(List parentIdist) { - - StringBuilder sb = new StringBuilder(100); - sb.append(deleteByParentIdInSql); - - String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size()); - sb.append(inClause); - - DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString()); - for (int i = 0; i < parentIdist.size(); i++) { - targetIdBinder.bindId(delete, parentIdist.get(i)); - } - - return delete; - } - - private SqlUpdate deleteByParentId(Object parentId) { - - DefaultSqlUpdate delete = new DefaultSqlUpdate(deleteByParentIdSql); - if (exportedProperties.length == 1){ - delete.addParameter(parentId); - } else { - targetDescriptor.getIdBinder().bindId(delete, parentId); - } - return delete; - } - - public List findIdsByParentId(Object parentId, List parentIdist, Transaction t) { - if (parentId != null){ - return findIdsByParentId(parentId, t); - } else { - return findIdsByParentIdList(parentIdist, t); - } - } - - private List findIdsByParentId(Object parentId, Transaction t) { - - String rawWhere = deriveWhereParentIdSql(false); - - EbeanServer server = getBeanDescriptor().getEbeanServer(); - Query q = server.find(getPropertyType()) - .where().raw(rawWhere).query(); - - bindWhereParendId(q, parentId); - return server.findIds(q, t); - } - - private List findIdsByParentIdList(List parentIdist, Transaction t) { - - String rawWhere = deriveWhereParentIdSql(true); - String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size()); - - String expr = rawWhere+inClause; - - EbeanServer server = getBeanDescriptor().getEbeanServer(); - Query q = (Query)server.find(getPropertyType()) - .where().raw(expr); - - for (int i = 0; i < parentIdist.size(); i++) { - bindWhereParendId(q, parentIdist.get(i)); - } - - return server.findIds(q, t); - } - - private void bindWhereParendId(Query q, Object parentId) { - - if (exportedProperties.length == 1) { - q.setParameter(1, parentId); - - } else { - int pos = 1; - for (int i = 0; i < exportedProperties.length; i++) { - Object embVal = exportedProperties[i].getValue(parentId); - q.setParameter(pos++, embVal); - } - } - } - - public void addFkey() { - if (importedId != null) { - importedId.addFkeys(name); - } - } - - @Override - public boolean isValueLoaded(Object value) { - if (value instanceof EntityBean) { - return ((EntityBean) value)._ebean_getIntercept().isLoaded(); - } - return true; - } - - @Override - public InvalidValue validateCascade(Object value) { - - BeanDescriptor target = getTargetDescriptor(); - return target.validate(true, value); - } - - private boolean hasChangedEmbedded(Object bean, Object oldValues) { - - Object embValue = getValue(oldValues); - if (embValue instanceof EntityBean) { - // the embedded bean .. has its own old values - return ((EntityBean) embValue)._ebean_getIntercept().isNewOrDirty(); - } - if (embValue == null) { - return getValue(bean) != null; - } else { - return false; - } - } - - @Override - public boolean hasChanged(Object bean, Object oldValues) { - if (embedded) { - return hasChangedEmbedded(bean, oldValues); - } - Object value = getValue(bean); - Object oldVal = getValue(oldValues); - if (oneToOneExported) { - // FKey on other side - return false; - } else { - if (value == null) { - return oldVal != null; - } else if (oldValues == null) { - return true; - } - - return importedId.hasChanged(value, oldVal); - } - } - - /** - * Return meta data for the deployment of the embedded bean specific to this - * property. - */ - public BeanProperty[] getProperties() { - return embeddedProps; - } - - public void buildSelectExpressionChain(String prefix, List selectChain) { - - prefix = SplitName.add(prefix, name); - - if (!embedded){ - targetIdBinder.buildSelectExpressionChain(prefix, selectChain); - - } else { - for (int i = 0; i < embeddedProps.length; i++) { - embeddedProps[i].buildSelectExpressionChain(prefix, selectChain); - } - } - } - - - /** - * Return true if this a OneToOne property. Otherwise assumed ManyToOne. - */ - public boolean isOneToOne() { - return oneToOne; - } - - /** - * Return true if this is the exported side of a OneToOne. - */ - public boolean isOneToOneExported() { - return oneToOneExported; - } - - /** - * Returns true if the associated bean has version properties. - */ - public boolean isEmbeddedVersion() { - return embeddedVersion; - } - - /** - * If true this bean maps to the primary key. - */ - public boolean isImportedPrimaryKey() { - return importedPrimaryKey; - } - - /** - * Same as getPropertyType(). Return the type of the bean this property - * represents. - */ - public Class getTargetType() { - return getPropertyType(); - } - - public Object getCacheDataValue(Object bean){ - if (embedded) { - throw new RuntimeException(); - } else { - Object ap = getValue(bean); - if (ap == null){ - return null; - } else { - return targetDescriptor.getId(ap); - } - } - } - - public void setCacheDataValue(Object bean, Object cacheData, Object oldValues, boolean readOnly){ - if (cacheData != null) { - if (embedded){ - throw new RuntimeException(); - } else { - boolean vanillaMode = false; - T ref = targetDescriptor.createReference(vanillaMode, Boolean.FALSE, cacheData, null); - setValue(bean, ref); - if (oldValues != null){ - setValue(oldValues, ref); - } - if (readOnly && !vanillaMode){ - ((EntityBean)ref)._ebean_intercept().setReadOnly(true); - } - } - } - } - - /** - * Return the Id values from the given bean. - */ - @Override - public Object[] getAssocOneIdValues(Object bean) { - return targetDescriptor.getIdBinder().getIdValues(bean); - } - - /** - * Return the Id expression to add to where clause etc. - */ - public String getAssocOneIdExpr(String prefix, String operator) { - return targetDescriptor.getIdBinder().getAssocOneIdExpr(prefix, operator); - } - - /** - * Return the logical id value expression taking into account embedded id's. - */ - @Override - public String getAssocIdInValueExpr(int size){ - return targetDescriptor.getIdBinder().getIdInValueExpr(size); - } - - /** - * Return the logical id in expression taking into account embedded id's. - */ - @Override - public String getAssocIdInExpr(String prefix){ - return targetDescriptor.getIdBinder().getAssocIdInExpr(prefix); - } - - @Override - public boolean isAssocId() { - return !embedded; - } - - @Override - public boolean isAssocProperty() { - return !embedded; - } - - - /** - * Create a vanilla bean of the target type to be used as an embeddedId - * value. - */ - public Object createEmbeddedId() { - return getTargetDescriptor().createVanillaBean(); - } - - /** - * Return an empty reference object. - */ - public Object createEmptyReference() { - return targetDescriptor.createEntityBean(); - } - - public void elSetReference(Object bean) { - Object value = getValueIntercept(bean); - if (value != null) { - ((EntityBean) value)._ebean_getIntercept().setReference(); - } - } - - @Override - public Object elGetReference(Object bean) { - Object value = getValueIntercept(bean); - if (value == null) { - value = targetDescriptor.createEntityBean(); - setValueIntercept(bean, value); - } - return value; - } - - public ImportedId getImportedId() { - return importedId; - } - - private String deriveWhereParentIdSql(boolean inClause) { - - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < exportedProperties.length; i++) { - String fkColumn = exportedProperties[i].getForeignDbColumn(); - if (i > 0){ - String s = inClause ? "," : " and "; - sb.append(s); - } - sb.append(fkColumn); - if (!inClause){ - sb.append("=? "); - } - } - return sb.toString(); - } - - /** - * Create the array of ExportedProperty used to build reference objects. - */ - private ExportedProperty[] createExported() { - - BeanProperty[] uids = descriptor.propertiesId(); - - ArrayList list = new ArrayList(); - - if (uids.length == 1 && uids[0].isEmbedded()) { - - BeanPropertyAssocOne one = (BeanPropertyAssocOne) uids[0]; - BeanDescriptor targetDesc = one.getTargetDescriptor(); - BeanProperty[] emIds = targetDesc.propertiesBaseScalar(); - try { - for (int i = 0; i < emIds.length; i++) { - ExportedProperty expProp = findMatch(true, emIds[i]); - list.add(expProp); - } - } catch (PersistenceException e){ - // not found as individual scalar properties - e.printStackTrace(); - } - - } else { - for (int i = 0; i < uids.length; i++) { - ExportedProperty expProp = findMatch(false, uids[i]); - list.add(expProp); - } - } - - return (ExportedProperty[]) list.toArray(new ExportedProperty[list.size()]); - } - - /** - * Find the matching foreignDbColumn for a given local property. - */ - private ExportedProperty findMatch(boolean embeddedProp,BeanProperty prop) { - - String matchColumn = prop.getDbColumn(); - - String searchTable = tableJoin.getTable(); - TableJoinColumn[] columns = tableJoin.columns(); - - for (int i = 0; i < columns.length; i++) { - String matchTo = columns[i].getLocalDbColumn(); - - if (matchColumn.equalsIgnoreCase(matchTo)) { - String foreignCol = columns[i].getForeignDbColumn(); - return new ExportedProperty(embeddedProp, foreignCol, prop); - } - } - - String msg = "Error with the Join on ["+getFullBeanName() - +"]. Could not find the matching foreign key for ["+matchColumn+"] in table["+searchTable+"]?" - +" Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?"; - throw new PersistenceException(msg); - } - - - @Override - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - if (!isTransient) { - localHelp.appendSelect(ctx, subQuery); - } - } - - @Override - public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) { - if (!isTransient) { - localHelp.appendFrom(ctx, forceOuterJoin); - } - } - - @Override - public Object readSet(DbReadContext ctx, Object bean, Class type) throws SQLException { - boolean assignable = (type == null || owningType.isAssignableFrom(type)); - return localHelp.readSet(ctx, bean, assignable); - } - - /** - * Read the data from the resultSet effectively ignoring it and returning null. - */ - @Override - public Object read(DbReadContext ctx) throws SQLException { - // just read the resultSet incrementing the column index - // pass in null for the bean so any data read is ignored - return localHelp.read(ctx); - } - - @Override - public void loadIgnore(DbReadContext ctx) { - localHelp.loadIgnore(ctx); - } - - @Override - public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { - Object dbVal = sqlBeanLoad.load(this); - if (embedded && sqlBeanLoad.isLazyLoad()){ - if (dbVal instanceof EntityBean){ - ((EntityBean)dbVal)._ebean_getIntercept().setLoaded(); - } - } - } - - private LocalHelp createHelp(boolean embedded, boolean oneToOneExported) { - if (embedded) { - return new Embedded(); - } else if (oneToOneExported) { - return new ReferenceExported(); - } else { - return new Reference(this); - } - } - - /** - * Local interface to handle Embedded, Reference and Reference Exported - * cases. - */ - private abstract class LocalHelp { - - abstract void loadIgnore(DbReadContext ctx); - - abstract Object read(DbReadContext ctx) throws SQLException; - - abstract Object readSet(DbReadContext ctx, Object bean, boolean assignAble) throws SQLException; - - abstract void appendSelect(DbSqlContext ctx, boolean subQuery); - - abstract void appendFrom(DbSqlContext ctx, boolean forceOuterJoin); - - } - - private final class Embedded extends LocalHelp { - - void loadIgnore(DbReadContext ctx) { - for (int i = 0; i < embeddedProps.length; i++) { - embeddedProps[i].loadIgnore(ctx); - } - } - - @Override - Object readSet(DbReadContext ctx, Object bean, boolean assignable) throws SQLException { - Object dbVal = read(ctx); - if (bean != null && assignable) { - // set back to the parent bean - setValue(bean, dbVal); - ctx.propagateState(dbVal); - return dbVal; - - } else { - return null; - } - } - - Object read(DbReadContext ctx) throws SQLException { - - EntityBean embeddedBean = targetDescriptor.createEntityBean(); - - boolean notNull = false; - for (int i = 0; i < embeddedProps.length; i++) { - Object value = embeddedProps[i].readSet(ctx, embeddedBean, null); - if (value != null) { - notNull = true; - } - } - if (notNull) { - ctx.propagateState(embeddedBean); - return embeddedBean; - } else { - return null; - } - } - - @Override - void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) { - } - - @Override - void appendSelect(DbSqlContext ctx, boolean subQuery) { - for (int i = 0; i < embeddedProps.length; i++) { - embeddedProps[i].appendSelect(ctx, subQuery); - } - } - } - - /** - * For imported reference - this is the common case. - */ - private final class Reference extends LocalHelp { - - //private final BeanPropertyAssocOne beanProp; - - Reference(BeanPropertyAssocOne beanProp) { -// this.beanProp = beanProp; - } - - void loadIgnore(DbReadContext ctx) { - targetIdBinder.loadIgnore(ctx); - if (targetInheritInfo != null) { - ctx.getDataReader().incrementPos(1); - } - } - - Object readSet(DbReadContext ctx, Object bean, boolean assignable) throws SQLException { - Object val = read(ctx); - if (bean != null && assignable) { - setValue(bean, val); - ctx.propagateState(val); - } - return val; - } - - /** - * Read and set a Reference bean. - */ - @Override - Object read(DbReadContext ctx) throws SQLException { - - BeanDescriptor rowDescriptor = null; - Class rowType = targetType; - if (targetInheritInfo != null) { - // read discriminator to determine the type - InheritInfo rowInheritInfo = targetInheritInfo.readType(ctx); - if (rowInheritInfo != null) { - rowType = rowInheritInfo.getType(); - rowDescriptor = rowInheritInfo.getBeanDescriptor(); - } - } - - // read the foreign key column(s) - Object id = targetIdBinder.read(ctx); - if (id == null) { - return null; - } - - // check transaction context to see if it already exists - Object existing = ctx.getPersistenceContext().get(rowType, id); - - if (existing != null) { - return existing; - } - - // parent always null for this case (but here to document) - Object parent = null; - boolean vanillaMode = ctx.isVanillaMode(); - //ReferenceOptions options = ctx.getReferenceOptionsFor(beanProp); - - Boolean readOnly = ctx.isReadOnly(); - Object ref; - if (targetInheritInfo != null) { - // for inheritance hierarchy create the correct type for this row... - ref = rowDescriptor.createReference(vanillaMode, readOnly, id, parent); - } else { - ref = targetDescriptor.createReference(vanillaMode, readOnly, id, parent); - } - - Object existingBean = ctx.getPersistenceContext().putIfAbsent(id, ref); - if (existingBean != null) { - // advanced case when we use multiple concurrent threads to - // build a single object graph, and another thread has since - // loaded a matching bean so we will use that instead. - ref = existingBean; - - } else if (!vanillaMode){ - EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept(); - if (Boolean.TRUE.equals(ctx.isReadOnly())){ - ebi.setReadOnly(true); - } - ctx.register(name, ebi); - } - - return ref; - } - - @Override - void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) { - if (targetInheritInfo != null) { - // add join to support the discriminator column - String relativePrefix = ctx.getRelativePrefix(name); - tableJoin.addJoin(forceOuterJoin, relativePrefix, ctx); - } - } - - /** - * Append columns for foreign key columns. - */ - @Override - void appendSelect(DbSqlContext ctx, boolean subQuery) { - - if (!subQuery && targetInheritInfo != null) { - // add discriminator column - String relativePrefix = ctx.getRelativePrefix(getName()); - String tableAlias = ctx.getTableAlias(relativePrefix); - ctx.appendColumn(tableAlias, targetInheritInfo.getDiscriminatorColumn()); - } - importedId.sqlAppend(ctx); - } - } - - /** - * For OneToOne exported reference - not so common. - */ - private final class ReferenceExported extends LocalHelp { - - @Override - void loadIgnore(DbReadContext ctx) { - targetDescriptor.getIdBinder().loadIgnore(ctx); - } - - /** - * Read and set a Reference bean. - */ - @Override - Object readSet(DbReadContext ctx, Object bean, boolean assignable) throws SQLException { - - Object dbVal = read(ctx); - if (bean != null && assignable) { - setValue(bean, dbVal); - ctx.propagateState(dbVal); - } - return dbVal; - } - - @Override - Object read(DbReadContext ctx) throws SQLException { - - // TODO: Support for Inheritance hierarchy on exported OneToOne ? - IdBinder idBinder = targetDescriptor.getIdBinder(); - Object id = idBinder.read(ctx); - if (id == null) { - return null; - } - - PersistenceContext persistCtx = ctx.getPersistenceContext(); - Object existing = persistCtx.get(targetType, id); - - if (existing != null) { - return existing; - } - boolean vanillaMode = ctx.isVanillaMode(); - Object parent = null; - Object ref = targetDescriptor.createReference(vanillaMode, ctx.isReadOnly(), id, parent); - - if (!vanillaMode){ - EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept(); - if (Boolean.TRUE.equals(ctx.isReadOnly())) { - ebi.setReadOnly(true); - } - persistCtx.put(id, ref); - ctx.register(name, ebi); - } - return ref; - } - - /** - * Append columns for foreign key columns. - */ - @Override - void appendSelect(DbSqlContext ctx, boolean subQuery) { - - // set appropriate tableAlias for - // the exported id columns - - String relativePrefix = ctx.getRelativePrefix(getName()); - ctx.pushTableAlias(relativePrefix); - - IdBinder idBinder = targetDescriptor.getIdBinder(); - idBinder.appendSelect(ctx, subQuery); - - ctx.popTableAlias(); - } - - @Override - void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) { - - String relativePrefix = ctx.getRelativePrefix(getName()); - tableJoin.addJoin(forceOuterJoin, relativePrefix, ctx); - } - } - - @Override - public void jsonWrite(WriteJsonContext ctx, Object bean) { - - Object value = getValueIntercept(bean); - if (value == null){ - ctx.beginAssocOneIsNull(name); - - } else { - if (ctx.isParentBean(value)){ - // bi-directional and already rendered parent - - } else { - ctx.pushParentBean(bean); - ctx.beginAssocOne(name); - BeanDescriptor refDesc = descriptor.getBeanDescriptor(value.getClass()); - refDesc.jsonWrite(ctx, value); - ctx.endAssocOne(); - ctx.popParentBean(); - } - } - } - - @Override - public void jsonRead(ReadJsonContext ctx, Object bean){ - - T assocBean = targetDescriptor.jsonReadBean(ctx, name); - setValue(bean, assocBean); - } -} +package com.avaje.ebeaninternal.server.deploy; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.InvalidValue; +import com.avaje.ebean.Query; +import com.avaje.ebean.SqlUpdate; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebeaninternal.server.core.DefaultSqlUpdate; +import com.avaje.ebeaninternal.server.deploy.id.IdBinder; +import com.avaje.ebeaninternal.server.deploy.id.ImportedId; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.el.ElPropertyChainBuilder; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.query.SplitName; +import com.avaje.ebeaninternal.server.query.SqlBeanLoad; +import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; + +/** + * Property mapped to a joined bean. + */ +public class BeanPropertyAssocOne extends BeanPropertyAssoc { + + private final boolean oneToOne; + + private final boolean oneToOneExported; + + private final boolean embeddedVersion; + + private final boolean importedPrimaryKey; + + private final LocalHelp localHelp; + + private final BeanProperty[] embeddedProps; + + private final HashMap embeddedPropsMap; + + /** + * The information for Imported foreign Keys. + */ + private ImportedId importedId; + + private ExportedProperty[] exportedProperties; + + private String deleteByParentIdSql; + private String deleteByParentIdInSql; + BeanPropertyAssocMany relationshipProperty; + + /** + * Create based on deploy information of an EmbeddedId. + */ + public BeanPropertyAssocOne(BeanDescriptorMap owner, DeployBeanPropertyAssocOne deploy) { + this(owner, null, deploy); + } + + /** + * Create the property. + */ + public BeanPropertyAssocOne(BeanDescriptorMap owner, BeanDescriptor descriptor, + DeployBeanPropertyAssocOne deploy) { + + super(owner, descriptor, deploy); + + importedPrimaryKey = deploy.isImportedPrimaryKey(); + oneToOne = deploy.isOneToOne(); + oneToOneExported = deploy.isOneToOneExported(); + + if (embedded) { + // Overriding of the columns and use table alias of owning BeanDescriptor + BeanEmbeddedMeta overrideMeta = BeanEmbeddedMetaFactory.create(owner, deploy, descriptor); + embeddedProps = overrideMeta.getProperties(); + if (id) { + embeddedVersion = false; + } else { + embeddedVersion = overrideMeta.isEmbeddedVersion(); + } + embeddedPropsMap = new HashMap(); + for (int i = 0; i < embeddedProps.length; i++) { + embeddedPropsMap.put(embeddedProps[i].getName(), embeddedProps[i]); + } + + } else { + embeddedProps = null; + embeddedPropsMap = null; + embeddedVersion = false; + } + localHelp = createHelp(embedded, oneToOneExported); + } + + @Override + public void initialise() { + super.initialise(); + if (!isTransient) { + if (embedded) { + // no imported or exported information + } else if (!oneToOneExported) { + importedId = createImportedId(this, targetDescriptor, tableJoin); + } else { + exportedProperties = createExported(); + + String delStmt = "delete from "+targetDescriptor.getBaseTable()+" where "; + + deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false); + deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true); + + } + } + } + + public void setRelationshipProperty(BeanPropertyAssocMany relationshipProperty){ + this.relationshipProperty = relationshipProperty; + } + + public BeanPropertyAssocMany getRelationshipProperty() { + return relationshipProperty; + } + + public void cacheClear() { + if (targetDescriptor.isBeanCaching() && relationshipProperty != null) { + targetDescriptor.cacheClearCachedManyIds(relationshipProperty.getName()); + } + } + + public void cacheDelete(boolean clearOnNull, Object bean) { + if (targetDescriptor.isBeanCaching() && relationshipProperty != null) { + Object assocBean = getValue(bean); + if (assocBean != null) { + Object parentId = targetDescriptor.getId(assocBean); + if (parentId != null) { + targetDescriptor.cacheRemoveCachedManyIds(parentId, relationshipProperty.getName()); + return; + } + } + if (clearOnNull) { + targetDescriptor.cacheClearCachedManyIds(relationshipProperty.getName()); + } + } + } + + public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { + + if (embedded){ + BeanProperty embProp = embeddedPropsMap.get(remainder); + if (embProp == null){ + String msg = "Embedded Property "+remainder+" not found in "+getFullBeanName(); + throw new PersistenceException(msg); + } + if (chain == null) { + chain = new ElPropertyChainBuilder(true, propName); + } + chain.add(this); + return chain.add(embProp).build(); + } + + return createElPropertyValue(propName, remainder, chain, propertyDeploy); + } + + @Override + public String getElPlaceholder(boolean encrypted) { + return encrypted ? elPlaceHolderEncrypted : elPlaceHolder; + } + + public SqlUpdate deleteByParentId(Object parentId, List parentIdist) { + if (parentId != null){ + return deleteByParentId(parentId); + } else { + return deleteByParentIdList(parentIdist); + } + } + + private SqlUpdate deleteByParentIdList(List parentIdist) { + + StringBuilder sb = new StringBuilder(100); + sb.append(deleteByParentIdInSql); + + String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size()); + sb.append(inClause); + + DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString()); + for (int i = 0; i < parentIdist.size(); i++) { + targetIdBinder.bindId(delete, parentIdist.get(i)); + } + + return delete; + } + + private SqlUpdate deleteByParentId(Object parentId) { + + DefaultSqlUpdate delete = new DefaultSqlUpdate(deleteByParentIdSql); + if (exportedProperties.length == 1){ + delete.addParameter(parentId); + } else { + targetDescriptor.getIdBinder().bindId(delete, parentId); + } + return delete; + } + + public List findIdsByParentId(Object parentId, List parentIdist, Transaction t) { + if (parentId != null){ + return findIdsByParentId(parentId, t); + } else { + return findIdsByParentIdList(parentIdist, t); + } + } + + private List findIdsByParentId(Object parentId, Transaction t) { + + String rawWhere = deriveWhereParentIdSql(false); + + EbeanServer server = getBeanDescriptor().getEbeanServer(); + Query q = server.find(getPropertyType()) + .where().raw(rawWhere).query(); + + bindWhereParendId(q, parentId); + return server.findIds(q, t); + } + + private List findIdsByParentIdList(List parentIdist, Transaction t) { + + String rawWhere = deriveWhereParentIdSql(true); + String inClause = targetIdBinder.getIdInValueExpr(parentIdist.size()); + + String expr = rawWhere+inClause; + + EbeanServer server = getBeanDescriptor().getEbeanServer(); + Query q = (Query)server.find(getPropertyType()) + .where().raw(expr); + + for (int i = 0; i < parentIdist.size(); i++) { + bindWhereParendId(q, parentIdist.get(i)); + } + + return server.findIds(q, t); + } + + private void bindWhereParendId(Query q, Object parentId) { + + if (exportedProperties.length == 1) { + q.setParameter(1, parentId); + + } else { + int pos = 1; + for (int i = 0; i < exportedProperties.length; i++) { + Object embVal = exportedProperties[i].getValue(parentId); + q.setParameter(pos++, embVal); + } + } + } + + public void addFkey() { + if (importedId != null) { + importedId.addFkeys(name); + } + } + + @Override + public boolean isValueLoaded(Object value) { + if (value instanceof EntityBean) { + return ((EntityBean) value)._ebean_getIntercept().isLoaded(); + } + return true; + } + + @Override + public InvalidValue validateCascade(Object value) { + + BeanDescriptor target = getTargetDescriptor(); + return target.validate(true, value); + } + + private boolean hasChangedEmbedded(Object bean, Object oldValues) { + + Object embValue = getValue(oldValues); + if (embValue instanceof EntityBean) { + // the embedded bean .. has its own old values + return ((EntityBean) embValue)._ebean_getIntercept().isNewOrDirty(); + } + if (embValue == null) { + return getValue(bean) != null; + } else { + return false; + } + } + + @Override + public boolean hasChanged(Object bean, Object oldValues) { + if (embedded) { + return hasChangedEmbedded(bean, oldValues); + } + Object value = getValue(bean); + Object oldVal = getValue(oldValues); + if (oneToOneExported) { + // FKey on other side + return false; + } else { + if (value == null) { + return oldVal != null; + } else if (oldValues == null) { + return true; + } + + return importedId.hasChanged(value, oldVal); + } + } + + /** + * Return meta data for the deployment of the embedded bean specific to this + * property. + */ + public BeanProperty[] getProperties() { + return embeddedProps; + } + + public void buildSelectExpressionChain(String prefix, List selectChain) { + + prefix = SplitName.add(prefix, name); + + if (!embedded){ + targetIdBinder.buildSelectExpressionChain(prefix, selectChain); + + } else { + for (int i = 0; i < embeddedProps.length; i++) { + embeddedProps[i].buildSelectExpressionChain(prefix, selectChain); + } + } + } + + + /** + * Return true if this a OneToOne property. Otherwise assumed ManyToOne. + */ + public boolean isOneToOne() { + return oneToOne; + } + + /** + * Return true if this is the exported side of a OneToOne. + */ + public boolean isOneToOneExported() { + return oneToOneExported; + } + + /** + * Returns true if the associated bean has version properties. + */ + public boolean isEmbeddedVersion() { + return embeddedVersion; + } + + /** + * If true this bean maps to the primary key. + */ + public boolean isImportedPrimaryKey() { + return importedPrimaryKey; + } + + /** + * Same as getPropertyType(). Return the type of the bean this property + * represents. + */ + public Class getTargetType() { + return getPropertyType(); + } + + public Object getCacheDataValue(Object bean){ + if (embedded) { + throw new RuntimeException(); + } else { + Object ap = getValue(bean); + if (ap == null){ + return null; + } else { + return targetDescriptor.getId(ap); + } + } + } + + public void setCacheDataValue(Object bean, Object cacheData, Object oldValues, boolean readOnly){ + if (cacheData != null) { + if (embedded){ + throw new RuntimeException(); + } else { + boolean vanillaMode = false; + T ref = targetDescriptor.createReference(vanillaMode, Boolean.FALSE, cacheData, null); + setValue(bean, ref); + if (oldValues != null){ + setValue(oldValues, ref); + } + if (readOnly && !vanillaMode){ + ((EntityBean)ref)._ebean_intercept().setReadOnly(true); + } + } + } + } + + /** + * Return the Id values from the given bean. + */ + @Override + public Object[] getAssocOneIdValues(Object bean) { + return targetDescriptor.getIdBinder().getIdValues(bean); + } + + /** + * Return the Id expression to add to where clause etc. + */ + public String getAssocOneIdExpr(String prefix, String operator) { + return targetDescriptor.getIdBinder().getAssocOneIdExpr(prefix, operator); + } + + /** + * Return the logical id value expression taking into account embedded id's. + */ + @Override + public String getAssocIdInValueExpr(int size){ + return targetDescriptor.getIdBinder().getIdInValueExpr(size); + } + + /** + * Return the logical id in expression taking into account embedded id's. + */ + @Override + public String getAssocIdInExpr(String prefix){ + return targetDescriptor.getIdBinder().getAssocIdInExpr(prefix); + } + + @Override + public boolean isAssocId() { + return !embedded; + } + + @Override + public boolean isAssocProperty() { + return !embedded; + } + + + /** + * Create a vanilla bean of the target type to be used as an embeddedId + * value. + */ + public Object createEmbeddedId() { + return getTargetDescriptor().createVanillaBean(); + } + + /** + * Return an empty reference object. + */ + public Object createEmptyReference() { + return targetDescriptor.createEntityBean(); + } + + public void elSetReference(Object bean) { + Object value = getValueIntercept(bean); + if (value != null) { + ((EntityBean) value)._ebean_getIntercept().setReference(); + } + } + + @Override + public Object elGetReference(Object bean) { + Object value = getValueIntercept(bean); + if (value == null) { + value = targetDescriptor.createEntityBean(); + setValueIntercept(bean, value); + } + return value; + } + + public ImportedId getImportedId() { + return importedId; + } + + private String deriveWhereParentIdSql(boolean inClause) { + + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < exportedProperties.length; i++) { + String fkColumn = exportedProperties[i].getForeignDbColumn(); + if (i > 0){ + String s = inClause ? "," : " and "; + sb.append(s); + } + sb.append(fkColumn); + if (!inClause){ + sb.append("=? "); + } + } + return sb.toString(); + } + + /** + * Create the array of ExportedProperty used to build reference objects. + */ + private ExportedProperty[] createExported() { + + BeanProperty[] uids = descriptor.propertiesId(); + + ArrayList list = new ArrayList(); + + if (uids.length == 1 && uids[0].isEmbedded()) { + + BeanPropertyAssocOne one = (BeanPropertyAssocOne) uids[0]; + BeanDescriptor targetDesc = one.getTargetDescriptor(); + BeanProperty[] emIds = targetDesc.propertiesBaseScalar(); + try { + for (int i = 0; i < emIds.length; i++) { + ExportedProperty expProp = findMatch(true, emIds[i]); + list.add(expProp); + } + } catch (PersistenceException e){ + // not found as individual scalar properties + e.printStackTrace(); + } + + } else { + for (int i = 0; i < uids.length; i++) { + ExportedProperty expProp = findMatch(false, uids[i]); + list.add(expProp); + } + } + + return (ExportedProperty[]) list.toArray(new ExportedProperty[list.size()]); + } + + /** + * Find the matching foreignDbColumn for a given local property. + */ + private ExportedProperty findMatch(boolean embeddedProp,BeanProperty prop) { + + String matchColumn = prop.getDbColumn(); + + String searchTable = tableJoin.getTable(); + TableJoinColumn[] columns = tableJoin.columns(); + + for (int i = 0; i < columns.length; i++) { + String matchTo = columns[i].getLocalDbColumn(); + + if (matchColumn.equalsIgnoreCase(matchTo)) { + String foreignCol = columns[i].getForeignDbColumn(); + return new ExportedProperty(embeddedProp, foreignCol, prop); + } + } + + String msg = "Error with the Join on ["+getFullBeanName() + +"]. Could not find the matching foreign key for ["+matchColumn+"] in table["+searchTable+"]?" + +" Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?"; + throw new PersistenceException(msg); + } + + + @Override + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + if (!isTransient) { + localHelp.appendSelect(ctx, subQuery); + } + } + + @Override + public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) { + if (!isTransient) { + localHelp.appendFrom(ctx, forceOuterJoin); + } + } + + @Override + public Object readSet(DbReadContext ctx, Object bean, Class type) throws SQLException { + boolean assignable = (type == null || owningType.isAssignableFrom(type)); + return localHelp.readSet(ctx, bean, assignable); + } + + /** + * Read the data from the resultSet effectively ignoring it and returning null. + */ + @Override + public Object read(DbReadContext ctx) throws SQLException { + // just read the resultSet incrementing the column index + // pass in null for the bean so any data read is ignored + return localHelp.read(ctx); + } + + @Override + public void loadIgnore(DbReadContext ctx) { + localHelp.loadIgnore(ctx); + } + + @Override + public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { + Object dbVal = sqlBeanLoad.load(this); + if (embedded && sqlBeanLoad.isLazyLoad()){ + if (dbVal instanceof EntityBean){ + ((EntityBean)dbVal)._ebean_getIntercept().setLoaded(); + } + } + } + + private LocalHelp createHelp(boolean embedded, boolean oneToOneExported) { + if (embedded) { + return new Embedded(); + } else if (oneToOneExported) { + return new ReferenceExported(); + } else { + return new Reference(this); + } + } + + /** + * Local interface to handle Embedded, Reference and Reference Exported + * cases. + */ + private abstract class LocalHelp { + + abstract void loadIgnore(DbReadContext ctx); + + abstract Object read(DbReadContext ctx) throws SQLException; + + abstract Object readSet(DbReadContext ctx, Object bean, boolean assignAble) throws SQLException; + + abstract void appendSelect(DbSqlContext ctx, boolean subQuery); + + abstract void appendFrom(DbSqlContext ctx, boolean forceOuterJoin); + + } + + private final class Embedded extends LocalHelp { + + void loadIgnore(DbReadContext ctx) { + for (int i = 0; i < embeddedProps.length; i++) { + embeddedProps[i].loadIgnore(ctx); + } + } + + @Override + Object readSet(DbReadContext ctx, Object bean, boolean assignable) throws SQLException { + Object dbVal = read(ctx); + if (bean != null && assignable) { + // set back to the parent bean + setValue(bean, dbVal); + ctx.propagateState(dbVal); + return dbVal; + + } else { + return null; + } + } + + Object read(DbReadContext ctx) throws SQLException { + + EntityBean embeddedBean = targetDescriptor.createEntityBean(); + + boolean notNull = false; + for (int i = 0; i < embeddedProps.length; i++) { + Object value = embeddedProps[i].readSet(ctx, embeddedBean, null); + if (value != null) { + notNull = true; + } + } + if (notNull) { + ctx.propagateState(embeddedBean); + return embeddedBean; + } else { + return null; + } + } + + @Override + void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) { + } + + @Override + void appendSelect(DbSqlContext ctx, boolean subQuery) { + for (int i = 0; i < embeddedProps.length; i++) { + embeddedProps[i].appendSelect(ctx, subQuery); + } + } + } + + /** + * For imported reference - this is the common case. + */ + private final class Reference extends LocalHelp { + + //private final BeanPropertyAssocOne beanProp; + + Reference(BeanPropertyAssocOne beanProp) { +// this.beanProp = beanProp; + } + + void loadIgnore(DbReadContext ctx) { + targetIdBinder.loadIgnore(ctx); + if (targetInheritInfo != null) { + ctx.getDataReader().incrementPos(1); + } + } + + Object readSet(DbReadContext ctx, Object bean, boolean assignable) throws SQLException { + Object val = read(ctx); + if (bean != null && assignable) { + setValue(bean, val); + ctx.propagateState(val); + } + return val; + } + + /** + * Read and set a Reference bean. + */ + @Override + Object read(DbReadContext ctx) throws SQLException { + + BeanDescriptor rowDescriptor = null; + Class rowType = targetType; + if (targetInheritInfo != null) { + // read discriminator to determine the type + InheritInfo rowInheritInfo = targetInheritInfo.readType(ctx); + if (rowInheritInfo != null) { + rowType = rowInheritInfo.getType(); + rowDescriptor = rowInheritInfo.getBeanDescriptor(); + } + } + + // read the foreign key column(s) + Object id = targetIdBinder.read(ctx); + if (id == null) { + return null; + } + + // check transaction context to see if it already exists + Object existing = ctx.getPersistenceContext().get(rowType, id); + + if (existing != null) { + return existing; + } + + // parent always null for this case (but here to document) + Object parent = null; + boolean vanillaMode = ctx.isVanillaMode(); + //ReferenceOptions options = ctx.getReferenceOptionsFor(beanProp); + + Boolean readOnly = ctx.isReadOnly(); + Object ref; + if (targetInheritInfo != null) { + // for inheritance hierarchy create the correct type for this row... + ref = rowDescriptor.createReference(vanillaMode, readOnly, id, parent); + } else { + ref = targetDescriptor.createReference(vanillaMode, readOnly, id, parent); + } + + Object existingBean = ctx.getPersistenceContext().putIfAbsent(id, ref); + if (existingBean != null) { + // advanced case when we use multiple concurrent threads to + // build a single object graph, and another thread has since + // loaded a matching bean so we will use that instead. + ref = existingBean; + + } else if (!vanillaMode){ + EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept(); + if (Boolean.TRUE.equals(ctx.isReadOnly())){ + ebi.setReadOnly(true); + } + ctx.register(name, ebi); + } + + return ref; + } + + @Override + void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) { + if (targetInheritInfo != null) { + // add join to support the discriminator column + String relativePrefix = ctx.getRelativePrefix(name); + tableJoin.addJoin(forceOuterJoin, relativePrefix, ctx); + } + } + + /** + * Append columns for foreign key columns. + */ + @Override + void appendSelect(DbSqlContext ctx, boolean subQuery) { + + if (!subQuery && targetInheritInfo != null) { + // add discriminator column + String relativePrefix = ctx.getRelativePrefix(getName()); + String tableAlias = ctx.getTableAlias(relativePrefix); + ctx.appendColumn(tableAlias, targetInheritInfo.getDiscriminatorColumn()); + } + importedId.sqlAppend(ctx); + } + } + + /** + * For OneToOne exported reference - not so common. + */ + private final class ReferenceExported extends LocalHelp { + + @Override + void loadIgnore(DbReadContext ctx) { + targetDescriptor.getIdBinder().loadIgnore(ctx); + } + + /** + * Read and set a Reference bean. + */ + @Override + Object readSet(DbReadContext ctx, Object bean, boolean assignable) throws SQLException { + + Object dbVal = read(ctx); + if (bean != null && assignable) { + setValue(bean, dbVal); + ctx.propagateState(dbVal); + } + return dbVal; + } + + @Override + Object read(DbReadContext ctx) throws SQLException { + + // TODO: Support for Inheritance hierarchy on exported OneToOne ? + IdBinder idBinder = targetDescriptor.getIdBinder(); + Object id = idBinder.read(ctx); + if (id == null) { + return null; + } + + PersistenceContext persistCtx = ctx.getPersistenceContext(); + Object existing = persistCtx.get(targetType, id); + + if (existing != null) { + return existing; + } + boolean vanillaMode = ctx.isVanillaMode(); + Object parent = null; + Object ref = targetDescriptor.createReference(vanillaMode, ctx.isReadOnly(), id, parent); + + if (!vanillaMode){ + EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept(); + if (Boolean.TRUE.equals(ctx.isReadOnly())) { + ebi.setReadOnly(true); + } + persistCtx.put(id, ref); + ctx.register(name, ebi); + } + return ref; + } + + /** + * Append columns for foreign key columns. + */ + @Override + void appendSelect(DbSqlContext ctx, boolean subQuery) { + + // set appropriate tableAlias for + // the exported id columns + + String relativePrefix = ctx.getRelativePrefix(getName()); + ctx.pushTableAlias(relativePrefix); + + IdBinder idBinder = targetDescriptor.getIdBinder(); + idBinder.appendSelect(ctx, subQuery); + + ctx.popTableAlias(); + } + + @Override + void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) { + + String relativePrefix = ctx.getRelativePrefix(getName()); + tableJoin.addJoin(forceOuterJoin, relativePrefix, ctx); + } + } + + @Override + public void jsonWrite(WriteJsonContext ctx, Object bean) { + + Object value = getValueIntercept(bean); + if (value == null){ + ctx.beginAssocOneIsNull(name); + + } else { + if (ctx.isParentBean(value)){ + // bi-directional and already rendered parent + + } else { + ctx.pushParentBean(bean); + ctx.beginAssocOne(name); + BeanDescriptor refDesc = descriptor.getBeanDescriptor(value.getClass()); + refDesc.jsonWrite(ctx, value); + ctx.endAssocOne(); + ctx.popParentBean(); + } + } + } + + @Override + public void jsonRead(ReadJsonContext ctx, Object bean){ + + T assocBean = targetDescriptor.jsonReadBean(ctx, name); + setValue(bean, assocBean); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompound.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompound.java index 5af4c324a..81a70971f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompound.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompound.java @@ -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. - *

- * 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. - *

- */ -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 propertyMap = new LinkedHashMap(); - - private final LinkedHashMap nonScalarMap = new LinkedHashMap(); - - 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 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. + *

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

+ */ +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 propertyMap = new LinkedHashMap(); + + private final LinkedHashMap nonScalarMap = new LinkedHashMap(); + + 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 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); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundRoot.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundRoot.java index 7c034c4c6..64a5ed286 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundRoot.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundRoot.java @@ -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. - *

- * 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. - *

- * - * @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 propMap; - - private final ArrayList propList; - - private List 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(); - this.propMap = new LinkedHashMap(); - } - - 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 getNonScalarProperties() { - return nonScalarProperties; - } - - public void setNonScalarProperties(List 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. + *

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

+ * + * @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 propMap; + + private final ArrayList propList; + + private List 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(); + this.propMap = new LinkedHashMap(); + } + + 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 getNonScalarProperties() { + return nonScalarProperties; + } + + public void setNonScalarProperties(List 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); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundScalar.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundScalar.java index 383e70051..928cc5dd8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundScalar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyCompoundScalar.java @@ -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); + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyOverride.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyOverride.java index 5047dea69..17924df4a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyOverride.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyOverride.java @@ -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. - *

- * Typically this is for Embedded Beans. - *

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

+ * Typically this is for Embedded Beans. + *

+ */ +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); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertySimpleCollection.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertySimpleCollection.java index d58286599..81f499ca9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertySimpleCollection.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertySimpleCollection.java @@ -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 extends BeanPropertyAssocMany { - - private final ScalarType collectionScalarType; - - public BeanPropertySimpleCollection(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertySimpleCollection 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 extends BeanPropertyAssocMany { + + private final ScalarType collectionScalarType; + + public BeanPropertySimpleCollection(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertySimpleCollection 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); + } + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanQueryAdapterManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanQueryAdapterManager.java index 940a248c6..f42b4b889 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanQueryAdapterManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanQueryAdapterManager.java @@ -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 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 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); + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanTable.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanTable.java index efbf171f4..5337364a4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanTable.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanTable.java @@ -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. - *

- * It holds the main deployment information and not all the detail that is held - * in a BeanDescriptor. - *

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

+ * It holds the main deployment information and not all the detail that is held + * in a BeanDescriptor. + *

+ */ +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); + } + + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/CollectionTypeConverter.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/CollectionTypeConverter.java index d02f00a9f..29ecff903 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/CollectionTypeConverter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/CollectionTypeConverter.java @@ -1,43 +1,24 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.deploy; - -/** - * Used to convert between collection types. - *

- * This typically means wrap and unwrap mutable scala collection types of Buffer, Set and Map. - *

- * - * @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. + *

+ * This typically means wrap and unwrap mutable scala collection types of Buffer, Set and Map. + *

+ * + * @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); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/CompoundUniqueContraint.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/CompoundUniqueContraint.java index df9c819f9..04a5e5819 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/CompoundUniqueContraint.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/CompoundUniqueContraint.java @@ -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; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/CopyContext.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/CopyContext.java index d4176fbbe..72d4296c4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/CopyContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/CopyContext.java @@ -1,83 +1,64 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.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); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlColumnInfo.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlColumnInfo.java index 6af03fe74..02b735c2e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlColumnInfo.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/DRawSqlColumnInfo.java @@ -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; + } } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/DefaultBeanFinderManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/DefaultBeanFinderManager.java index 525a42d46..067e245b5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/DefaultBeanFinderManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/DefaultBeanFinderManager.java @@ -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, BeanFinder> registerFor = new HashMap, BeanFinder>(); - - public int createBeanFinders(List> 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 BeanFinder getBeanFinder(Class entityType) { - return (BeanFinder)registerFor.get(entityType); - } - - /** - * Find the entity class given the controller class. - *

- * This uses reflection to find the generics parameter type. - *

- */ - 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, BeanFinder> registerFor = new HashMap, BeanFinder>(); + + public int createBeanFinders(List> 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 BeanFinder getBeanFinder(Class entityType) { + return (BeanFinder)registerFor.get(entityType); + } + + /** + * Find the entity class given the controller class. + *

+ * This uses reflection to find the generics parameter type. + *

+ */ + 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; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/DeployOrmXml.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/DeployOrmXml.java index 4f2917554..6a405ddd9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/DeployOrmXml.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/DeployOrmXml.java @@ -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). - *

- * Also supports some other deployment features such as type conversion. - *

- */ -public class DeployOrmXml { - - private static final Logger logger = Logger.getLogger(DeployOrmXml.class.getName()); - - - private final HashMap nativeQueryCache; - - private final ArrayList ormXmlList; - - private final ResourceSource resSource; - - public DeployOrmXml(ResourceSource resSource) { - - this.resSource = resSource; - this.nativeQueryCache = new HashMap(); - 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 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. - *

- * These are loaded from the orm.xml deployment file. - *

- */ - public DNativeQuery getNativeQuery(String name) { - return nativeQueryCache.get(name); - } - - private ArrayList findAllOrmXml() { - - ArrayList ormXmlList = new ArrayList(); - - - 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 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. - *

- * This searches all the ormXml files and returns the first match. - *

- */ - public Dnode findEntityDeploymentXml(String className) { - - for (Dnode ormXml : ormXmlList) { - Dnode entityMappings = ormXml.find("entity-mappings"); - - List 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). + *

+ * Also supports some other deployment features such as type conversion. + *

+ */ +public class DeployOrmXml { + + private static final Logger logger = Logger.getLogger(DeployOrmXml.class.getName()); + + + private final HashMap nativeQueryCache; + + private final ArrayList ormXmlList; + + private final ResourceSource resSource; + + public DeployOrmXml(ResourceSource resSource) { + + this.resSource = resSource; + this.nativeQueryCache = new HashMap(); + 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 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. + *

+ * These are loaded from the orm.xml deployment file. + *

+ */ + public DNativeQuery getNativeQuery(String name) { + return nativeQueryCache.get(name); + } + + private ArrayList findAllOrmXml() { + + ArrayList ormXmlList = new ArrayList(); + + + 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 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. + *

+ * This searches all the ormXml files and returns the first match. + *

+ */ + public Dnode findEntityDeploymentXml(String className) { + + for (Dnode ormXml : ormXmlList) { + Dnode entityMappings = ormXml.find("entity-mappings"); + + List entities = entityMappings.findAll("entity", "class", className, 1); + if (entities.size() == 1) { + return entities.get(0); + } + } + + return null; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ExportedProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ExportedProperty.java index 0a7999944..71cfb09e1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/ExportedProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ExportedProperty.java @@ -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. - *

- * Used to for Assoc Manys to create references etc. - *

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

- * 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. - *

- */ - public String getForeignDbColumn() { - return foreignDbColumn; - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebeaninternal.server.core.InternString; + +/** + * The Exported foreign key and property. + *

+ * Used to for Assoc Manys to create references etc. + *

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

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

+ */ + public String getForeignDbColumn() { + return foreignDbColumn; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/InheritInfo.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/InheritInfo.java index 7b84f8ea7..eaf2c853a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/InheritInfo.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/InheritInfo.java @@ -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 children = new ArrayList(); - - /** - * Map of discriminator values to InheritInfo. - */ - private final HashMap discMap; - - /** - * Map of class types to InheritInfo (taking into account subclass proxy classes). - */ - private final HashMap 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(); - typeMap = new HashMap(); - 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. - *

- * The root has a map of discriminator values to types. - *

- */ - 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 children = new ArrayList(); + + /** + * Map of discriminator values to InheritInfo. + */ + private final HashMap discMap; + + /** + * Map of class types to InheritInfo (taking into account subclass proxy classes). + */ + private final HashMap 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(); + typeMap = new HashMap(); + 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. + *

+ * The root has a map of discriminator values to types. + *

+ */ + 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 + "]"; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ManyType.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ManyType.java index 64dc68c08..8fff64f24 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/ManyType.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ManyType.java @@ -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; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistControllerManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistControllerManager.java index 2ac85ed63..14d4731bd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistControllerManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistControllerManager.java @@ -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 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 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); + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistListenerManager.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistListenerManager.java index ce7a61aaf..79c13df10 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistListenerManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/PersistListenerManager.java @@ -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> 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 void addPersistListeners(DeployBeanDescriptor 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) 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. - *

- * This uses reflection to find the generics parameter type. - *

- */ - 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> 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 void addPersistListeners(DeployBeanDescriptor 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) 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. + *

+ * This uses reflection to find the generics parameter type. + *

+ */ + 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; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ReflectGetter.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ReflectGetter.java index 3e86c91e5..2163bb757 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/ReflectGetter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ReflectGetter.java @@ -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. - *

- * This provides the BeanReflectGetter objects to do that. - *

- * @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. + *

+ * This provides the BeanReflectGetter objects to do that. + *

+ * @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); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ReflectSetter.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ReflectSetter.java index 47efaa5bc..b36c240ef 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/ReflectSetter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ReflectSetter.java @@ -1,72 +1,53 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.deploy; - -import java.lang.reflect.Method; - -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter; - -/** - * A place holder for BeanReflectSetter that should never be called. - *

- * This is for properties of classes that are abstract and at the root - * of an inheritance hierarchy. - *

- * @author rbygrave - */ -public class ReflectSetter { - - /** - * Creates place holder objects that should never be called. - */ - public static BeanReflectSetter create(DeployBeanProperty prop) { - - String fullName = prop.getFullBeanName(); - Method writeMethod = prop.getWriteMethod(); - return new RefCalled(fullName, writeMethod); - } - - static class RefCalled implements BeanReflectSetter { - - final String fullName; - final Method writeMethod; - - RefCalled(String fullName, Method writeMethod) { - this.fullName = fullName; - this.writeMethod = writeMethod; - } - public void set(Object bean, Object value) { - Object[] a = new Object[1]; - a[0] = value; - try { - writeMethod.invoke(bean, a); - } catch (Exception e) { - String beanType = bean == null ? "null" : bean.getClass().toString(); - String msg = "Error setting value on "+fullName+" value["+value+"] on type["+beanType+"]"; - throw new RuntimeException(msg, e); - } - } - public void setIntercept(Object bean, Object value) { - String msg = "Not expecting setIntercept to be called. Refer Bug 368"; - throw new RuntimeException(msg); - } - } -} +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.BeanReflectSetter; + +/** + * A place holder for BeanReflectSetter that should never be called. + *

+ * This is for properties of classes that are abstract and at the root + * of an inheritance hierarchy. + *

+ * @author rbygrave + */ +public class ReflectSetter { + + /** + * Creates place holder objects that should never be called. + */ + public static BeanReflectSetter create(DeployBeanProperty prop) { + + String fullName = prop.getFullBeanName(); + Method writeMethod = prop.getWriteMethod(); + return new RefCalled(fullName, writeMethod); + } + + static class RefCalled implements BeanReflectSetter { + + final String fullName; + final Method writeMethod; + + RefCalled(String fullName, Method writeMethod) { + this.fullName = fullName; + this.writeMethod = writeMethod; + } + public void set(Object bean, Object value) { + Object[] a = new Object[1]; + a[0] = value; + try { + writeMethod.invoke(bean, a); + } catch (Exception e) { + String beanType = bean == null ? "null" : bean.getClass().toString(); + String msg = "Error setting value on "+fullName+" value["+value+"] on type["+beanType+"]"; + throw new RuntimeException(msg, e); + } + } + public void setIntercept(Object bean, Object value) { + String msg = "Not expecting setIntercept to be called. Refer Bug 368"; + throw new RuntimeException(msg); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ScalaBufferConverter.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ScalaBufferConverter.java index 0d95d6ff5..50bb84543 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/ScalaBufferConverter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ScalaBufferConverter.java @@ -1,48 +1,29 @@ -/** - * 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 scala.collection.JavaConversions; - -/** - * Converts between Java List and Scala mutable Buffer. - * - * @author rbygrave - */ -public class ScalaBufferConverter implements CollectionTypeConverter { - -// @SuppressWarnings({ "rawtypes" }) - public Object toUnderlying(Object wrapped) { - throw new IllegalArgumentException("Scala types not supported in this build"); -// if (wrapped instanceof JavaConversions.JListWrapper){ -// return ((JavaConversions.JListWrapper)wrapped).underlying(); -// } -// return null; - } - - public Object toWrapped(Object wrapped) { - throw new IllegalArgumentException("Scala types not supported in this build"); -// if (wrapped instanceof java.util.List){ -// return JavaConversions.asScalaBuffer((java.util.List)wrapped); -// } -// return wrapped; - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import scala.collection.JavaConversions; + +/** + * Converts between Java List and Scala mutable Buffer. + * + * @author rbygrave + */ +public class ScalaBufferConverter implements CollectionTypeConverter { + +// @SuppressWarnings({ "rawtypes" }) + public Object toUnderlying(Object wrapped) { + throw new IllegalArgumentException("Scala types not supported in this build"); +// if (wrapped instanceof JavaConversions.JListWrapper){ +// return ((JavaConversions.JListWrapper)wrapped).underlying(); +// } +// return null; + } + + public Object toWrapped(Object wrapped) { + throw new IllegalArgumentException("Scala types not supported in this build"); +// if (wrapped instanceof java.util.List){ +// return JavaConversions.asScalaBuffer((java.util.List)wrapped); +// } +// return wrapped; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ScalaMapConverter.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ScalaMapConverter.java index bcb6e693a..525d4b8f8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/ScalaMapConverter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ScalaMapConverter.java @@ -1,48 +1,29 @@ -/** - * 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 scala.collection.JavaConversions; - -/** - * Converts between Java Map and Scala mutable Map. - * - * @author rbygrave - */ -public class ScalaMapConverter implements CollectionTypeConverter { - -// @SuppressWarnings({ "rawtypes" }) - public Object toUnderlying(Object wrapped) { - throw new IllegalArgumentException("Scala types not supported in this build"); -// if (wrapped instanceof JavaConversions.JMapWrapper){ -// return ((JavaConversions.JMapWrapper)wrapped).underlying(); -// } -// return null; - } - - public Object toWrapped(Object wrapped) { - throw new IllegalArgumentException("Scala types not supported in this build"); -// if (wrapped instanceof java.util.Map){ -// return JavaConversions.mapAsScalaMap((java.util.Map)wrapped); -// } -// return wrapped; - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import scala.collection.JavaConversions; + +/** + * Converts between Java Map and Scala mutable Map. + * + * @author rbygrave + */ +public class ScalaMapConverter implements CollectionTypeConverter { + +// @SuppressWarnings({ "rawtypes" }) + public Object toUnderlying(Object wrapped) { + throw new IllegalArgumentException("Scala types not supported in this build"); +// if (wrapped instanceof JavaConversions.JMapWrapper){ +// return ((JavaConversions.JMapWrapper)wrapped).underlying(); +// } +// return null; + } + + public Object toWrapped(Object wrapped) { + throw new IllegalArgumentException("Scala types not supported in this build"); +// if (wrapped instanceof java.util.Map){ +// return JavaConversions.mapAsScalaMap((java.util.Map)wrapped); +// } +// return wrapped; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ScalaSetConverter.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ScalaSetConverter.java index 19b029866..238c0de3c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/ScalaSetConverter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ScalaSetConverter.java @@ -1,49 +1,30 @@ -/** - * 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 scala.collection.JavaConversions; -import scala.collection.convert.DecorateAsScala; - -/** - * Converts between Java Set and Scala mutable Set. - * - * @author rbygrave - */ -public class ScalaSetConverter implements CollectionTypeConverter { - -// @SuppressWarnings({ "rawtypes" }) - public Object toUnderlying(Object wrapped) { - throw new IllegalArgumentException("Scala types not supported in this build"); -// if (wrapped instanceof JavaConversions.JSetWrapper){ -// return ((JavaConversions.JSetWrapper)wrapped).underlying(); -// } -// return null; - } - - public Object toWrapped(Object wrapped) { - throw new IllegalArgumentException("Scala types not supported in this build"); -// if (wrapped instanceof java.util.Set){ -// return JavaConversions.asScalaSet((java.util.Set)wrapped); -// } -// return wrapped; - } - -} +package com.avaje.ebeaninternal.server.deploy; + +import scala.collection.JavaConversions; +import scala.collection.convert.DecorateAsScala; + +/** + * Converts between Java Set and Scala mutable Set. + * + * @author rbygrave + */ +public class ScalaSetConverter implements CollectionTypeConverter { + +// @SuppressWarnings({ "rawtypes" }) + public Object toUnderlying(Object wrapped) { + throw new IllegalArgumentException("Scala types not supported in this build"); +// if (wrapped instanceof JavaConversions.JSetWrapper){ +// return ((JavaConversions.JSetWrapper)wrapped).underlying(); +// } +// return null; + } + + public Object toWrapped(Object wrapped) { + throw new IllegalArgumentException("Scala types not supported in this build"); +// if (wrapped instanceof java.util.Set){ +// return JavaConversions.asScalaSet((java.util.Set)wrapped); +// } +// return wrapped; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoin.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoin.java index e43f1aeb5..57025e68e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoin.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoin.java @@ -1,227 +1,208 @@ -/** - * 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.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; -import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn; -import com.avaje.ebeaninternal.server.query.SplitName; -import com.avaje.ebeaninternal.server.query.SqlBeanLoad; - -import java.sql.SQLException; -import java.util.LinkedHashMap; - -/** - * Represents a join to another table. - */ -public final class TableJoin { - - - public static final String NEW_LINE = "\n"; - - public static final String LEFT_OUTER = "left outer join"; - - public static final String JOIN = "join"; - - /** - * Flag set when the imported key maps to the primary key. - * This occurs for intersection tables (ManyToMany). - */ - private final boolean importedPrimaryKey; - - /** - * The joined table. - */ - private final String table; - - /** - * The type of join. LEFT OUTER etc. - */ - private final String type; - - /** - * The persist cascade info. - */ - private final BeanCascadeInfo cascadeInfo; - - /** - * Properties as an array. - */ - private final BeanProperty[] properties; - - /** - * Columns as an array. - */ - private final TableJoinColumn[] columns; - - /** - * Create a TableJoin. - */ - public TableJoin(DeployTableJoin deploy, LinkedHashMap propMap) { - - this.importedPrimaryKey = deploy.isImportedPrimaryKey(); - this.table = InternString.intern(deploy.getTable()); - this.type = InternString.intern(deploy.getType()); - this.cascadeInfo = deploy.getCascadeInfo(); - - DeployTableJoinColumn[] deployCols = deploy.columns(); - this.columns = new TableJoinColumn[deployCols.length]; - for (int i = 0; i < deployCols.length; i++) { - this.columns[i] = new TableJoinColumn(deployCols[i]); - } - - DeployBeanProperty[] deployProps = deploy.properties(); - if (deployProps.length > 0 && propMap == null){ - throw new NullPointerException("propMap is null?"); - } - - this.properties = new BeanProperty[deployProps.length]; - for (int i = 0; i < deployProps.length; i++) { - BeanProperty prop = propMap.get(deployProps[i].getName()); - this.properties[i] = prop; - } - - } - - /** - * Create a tableJoin based on this object but with different alias. - */ - public TableJoin createWithAlias(String localAlias, String foreignAlias) { - - return new TableJoin(this, localAlias, foreignAlias); - } - - /** - * Construct a copy but with different table alias'. - */ - private TableJoin(TableJoin join, String localAlias, String foreignAlias){ - - // copy the immutable fields - this.importedPrimaryKey = join.importedPrimaryKey; - this.table = join.table; - this.type = join.type; - this.cascadeInfo = join.cascadeInfo; - this.properties = join.properties; - this.columns = join.columns; - } - - - public String toString() { - StringBuilder sb = new StringBuilder(30); - sb.append(type).append(" ").append(table).append(" "); - for (int i = 0; i < columns.length; i++) { - sb.append(columns[i]).append(" "); - } - return sb.toString(); - } - - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - for (int i = 0, x = properties.length; i < x; i++) { - properties[i].appendSelect(ctx, subQuery); - } - } - - public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { - for (int i = 0, x = properties.length; i < x; i++) { - properties[i].load(sqlBeanLoad); - } - } - - public Object readSet(DbReadContext ctx, Object bean, Class type) throws SQLException { - for (int i = 0, x = properties.length; i < x; i++) { - properties[i].readSet(ctx, bean, type); - } - return null; - } - - /** - * Return true if the imported foreign key maps to the primary key. - */ - public boolean isImportedPrimaryKey() { - return importedPrimaryKey; - } - - /** - * Return the persist info. - */ - public BeanCascadeInfo getCascadeInfo() { - return cascadeInfo; - } - - /** - * Return the join columns. - */ - public TableJoinColumn[] columns() { - return columns; - } - - - /** - * For secondary table joins returns the properties mapped to that table. - */ - public BeanProperty[] properties() { - return properties; - } - - /** - * Return the joined table name. - */ - public String getTable() { - return table; - } - - /** - * Return the type of join. LEFT OUTER JOIN etc. - */ - public String getType() { - return type; - } - - /** - * Return true if this join is a left outer join. - */ - public boolean isOuterJoin() { - return type.equals(LEFT_OUTER); - } - - public boolean addJoin(boolean forceOuterJoin, String prefix, DbSqlContext ctx) { - - String[] names = SplitName.split(prefix); - String a1 = ctx.getTableAlias(names[0]); - String a2 = ctx.getTableAlias(prefix); - - return addJoin(forceOuterJoin, a1, a2, ctx); - } - - public boolean addJoin(boolean forceOuterJoin, String a1, String a2, DbSqlContext ctx) { - - ctx.addJoin(forceOuterJoin?LEFT_OUTER:type, table, columns(), a1, a2); - - return forceOuterJoin || LEFT_OUTER.equals(type); - } - - /** - * Explicitly add a (non-outer) join. - */ - public void addInnerJoin(String a1, String a2, DbSqlContext ctx) { - ctx.addJoin(JOIN, table, columns(), a1, a2); - } -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; +import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn; +import com.avaje.ebeaninternal.server.query.SplitName; +import com.avaje.ebeaninternal.server.query.SqlBeanLoad; + +import java.sql.SQLException; +import java.util.LinkedHashMap; + +/** + * Represents a join to another table. + */ +public final class TableJoin { + + + public static final String NEW_LINE = "\n"; + + public static final String LEFT_OUTER = "left outer join"; + + public static final String JOIN = "join"; + + /** + * Flag set when the imported key maps to the primary key. + * This occurs for intersection tables (ManyToMany). + */ + private final boolean importedPrimaryKey; + + /** + * The joined table. + */ + private final String table; + + /** + * The type of join. LEFT OUTER etc. + */ + private final String type; + + /** + * The persist cascade info. + */ + private final BeanCascadeInfo cascadeInfo; + + /** + * Properties as an array. + */ + private final BeanProperty[] properties; + + /** + * Columns as an array. + */ + private final TableJoinColumn[] columns; + + /** + * Create a TableJoin. + */ + public TableJoin(DeployTableJoin deploy, LinkedHashMap propMap) { + + this.importedPrimaryKey = deploy.isImportedPrimaryKey(); + this.table = InternString.intern(deploy.getTable()); + this.type = InternString.intern(deploy.getType()); + this.cascadeInfo = deploy.getCascadeInfo(); + + DeployTableJoinColumn[] deployCols = deploy.columns(); + this.columns = new TableJoinColumn[deployCols.length]; + for (int i = 0; i < deployCols.length; i++) { + this.columns[i] = new TableJoinColumn(deployCols[i]); + } + + DeployBeanProperty[] deployProps = deploy.properties(); + if (deployProps.length > 0 && propMap == null){ + throw new NullPointerException("propMap is null?"); + } + + this.properties = new BeanProperty[deployProps.length]; + for (int i = 0; i < deployProps.length; i++) { + BeanProperty prop = propMap.get(deployProps[i].getName()); + this.properties[i] = prop; + } + + } + + /** + * Create a tableJoin based on this object but with different alias. + */ + public TableJoin createWithAlias(String localAlias, String foreignAlias) { + + return new TableJoin(this, localAlias, foreignAlias); + } + + /** + * Construct a copy but with different table alias'. + */ + private TableJoin(TableJoin join, String localAlias, String foreignAlias){ + + // copy the immutable fields + this.importedPrimaryKey = join.importedPrimaryKey; + this.table = join.table; + this.type = join.type; + this.cascadeInfo = join.cascadeInfo; + this.properties = join.properties; + this.columns = join.columns; + } + + + public String toString() { + StringBuilder sb = new StringBuilder(30); + sb.append(type).append(" ").append(table).append(" "); + for (int i = 0; i < columns.length; i++) { + sb.append(columns[i]).append(" "); + } + return sb.toString(); + } + + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + for (int i = 0, x = properties.length; i < x; i++) { + properties[i].appendSelect(ctx, subQuery); + } + } + + public void load(SqlBeanLoad sqlBeanLoad) throws SQLException { + for (int i = 0, x = properties.length; i < x; i++) { + properties[i].load(sqlBeanLoad); + } + } + + public Object readSet(DbReadContext ctx, Object bean, Class type) throws SQLException { + for (int i = 0, x = properties.length; i < x; i++) { + properties[i].readSet(ctx, bean, type); + } + return null; + } + + /** + * Return true if the imported foreign key maps to the primary key. + */ + public boolean isImportedPrimaryKey() { + return importedPrimaryKey; + } + + /** + * Return the persist info. + */ + public BeanCascadeInfo getCascadeInfo() { + return cascadeInfo; + } + + /** + * Return the join columns. + */ + public TableJoinColumn[] columns() { + return columns; + } + + + /** + * For secondary table joins returns the properties mapped to that table. + */ + public BeanProperty[] properties() { + return properties; + } + + /** + * Return the joined table name. + */ + public String getTable() { + return table; + } + + /** + * Return the type of join. LEFT OUTER JOIN etc. + */ + public String getType() { + return type; + } + + /** + * Return true if this join is a left outer join. + */ + public boolean isOuterJoin() { + return type.equals(LEFT_OUTER); + } + + public boolean addJoin(boolean forceOuterJoin, String prefix, DbSqlContext ctx) { + + String[] names = SplitName.split(prefix); + String a1 = ctx.getTableAlias(names[0]); + String a2 = ctx.getTableAlias(prefix); + + return addJoin(forceOuterJoin, a1, a2, ctx); + } + + public boolean addJoin(boolean forceOuterJoin, String a1, String a2, DbSqlContext ctx) { + + ctx.addJoin(forceOuterJoin?LEFT_OUTER:type, table, columns(), a1, a2); + + return forceOuterJoin || LEFT_OUTER.equals(type); + } + + /** + * Explicitly add a (non-outer) join. + */ + public void addInnerJoin(String a1, String a2, DbSqlContext ctx) { + ctx.addJoin(JOIN, table, columns(), a1, a2); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoinColumn.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoinColumn.java index eba5ba090..ea5cfbfea 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoinColumn.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/TableJoinColumn.java @@ -1,86 +1,67 @@ -/** - * 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.deploy.meta.DeployTableJoinColumn; - -/** - * A join pair of local and foreign properties. - */ -public class TableJoinColumn { - - /** - * The local database column name. - */ - private final String localDbColumn; - - /** - * The foreign database column name. - */ - private final String foreignDbColumn; - - private final boolean insertable; - - private final boolean updateable; - - /** - * Create the pair. - */ - public TableJoinColumn(DeployTableJoinColumn deploy) { - this.localDbColumn = InternString.intern(deploy.getLocalDbColumn()); - this.foreignDbColumn = InternString.intern(deploy.getForeignDbColumn()); - this.insertable = deploy.isInsertable(); - this.updateable = deploy.isUpdateable(); - } - - public String toString() { - return localDbColumn+" = "+foreignDbColumn; - } - - - /** - * Return the foreign database column name. - */ - public String getForeignDbColumn() { - return foreignDbColumn; - } - - /** - * Return the local database column name. - */ - public String getLocalDbColumn() { - return localDbColumn; - } - - /** - * Return true if this column should be insertable. - */ - public boolean isInsertable() { - return insertable; - } - - /** - * Return true if this column should be updateable. - */ - public boolean isUpdateable() { - return updateable; - } -} +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn; + +/** + * A join pair of local and foreign properties. + */ +public class TableJoinColumn { + + /** + * The local database column name. + */ + private final String localDbColumn; + + /** + * The foreign database column name. + */ + private final String foreignDbColumn; + + private final boolean insertable; + + private final boolean updateable; + + /** + * Create the pair. + */ + public TableJoinColumn(DeployTableJoinColumn deploy) { + this.localDbColumn = InternString.intern(deploy.getLocalDbColumn()); + this.foreignDbColumn = InternString.intern(deploy.getForeignDbColumn()); + this.insertable = deploy.isInsertable(); + this.updateable = deploy.isUpdateable(); + } + + public String toString() { + return localDbColumn+" = "+foreignDbColumn; + } + + + /** + * Return the foreign database column name. + */ + public String getForeignDbColumn() { + return foreignDbColumn; + } + + /** + * Return the local database column name. + */ + public String getLocalDbColumn() { + return localDbColumn; + } + + /** + * Return true if this column should be insertable. + */ + public boolean isInsertable() { + return insertable; + } + + /** + * Return true if this column should be updateable. + */ + public boolean isUpdateable() { + return updateable; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/CounterFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/CounterFactory.java index 717ed5a47..b8bf187d1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/CounterFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/CounterFactory.java @@ -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.deploy.generatedproperty; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.sql.Types; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; - -/** - * Creates "Counter" GeneratedProperty for various types of number. - *

- * Aka, Integer, Long, Short etc. - *

- */ -public class CounterFactory { - - final GeneratedCounterInteger integerCounter = new GeneratedCounterInteger(); - - final GeneratedCounterLong longCounter = new GeneratedCounterLong(); - - public void setCounter(DeployBeanProperty property) { - - property.setGeneratedProperty(createCounter(property)); - } - - /** - * Create the GeneratedProperty based on the property type. - */ - private GeneratedProperty createCounter(DeployBeanProperty property) { - - Class propType = property.getPropertyType(); - if (propType.equals(Integer.class) || propType.equals(int.class)) { - return integerCounter; - } - if (propType.equals(Long.class) || propType.equals(long.class)) { - return longCounter; - } - - int type = getType(propType); - return new GeneratedCounter(type); - } - - private int getType(Class propType){ - if (propType.equals(Short.class) || propType.equals(short.class)){ - return Types.TINYINT; - } - if (propType.equals(BigDecimal.class)){ - return Types.DECIMAL; - } - if (propType.equals(Double.class) || propType.equals(double.class)){ - return Types.DOUBLE; - } - if (propType.equals(Float.class) || propType.equals(float.class)){ - return Types.REAL; - } - if (propType.equals(BigInteger.class)){ - return Types.BIGINT; - } - String msg = "Can not support Counter for type "+propType.getName(); - throw new PersistenceException(msg); - } -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Types; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; + +/** + * Creates "Counter" GeneratedProperty for various types of number. + *

+ * Aka, Integer, Long, Short etc. + *

+ */ +public class CounterFactory { + + final GeneratedCounterInteger integerCounter = new GeneratedCounterInteger(); + + final GeneratedCounterLong longCounter = new GeneratedCounterLong(); + + public void setCounter(DeployBeanProperty property) { + + property.setGeneratedProperty(createCounter(property)); + } + + /** + * Create the GeneratedProperty based on the property type. + */ + private GeneratedProperty createCounter(DeployBeanProperty property) { + + Class propType = property.getPropertyType(); + if (propType.equals(Integer.class) || propType.equals(int.class)) { + return integerCounter; + } + if (propType.equals(Long.class) || propType.equals(long.class)) { + return longCounter; + } + + int type = getType(propType); + return new GeneratedCounter(type); + } + + private int getType(Class propType){ + if (propType.equals(Short.class) || propType.equals(short.class)){ + return Types.TINYINT; + } + if (propType.equals(BigDecimal.class)){ + return Types.DECIMAL; + } + if (propType.equals(Double.class) || propType.equals(double.class)){ + return Types.DOUBLE; + } + if (propType.equals(Float.class) || propType.equals(float.class)){ + return Types.REAL; + } + if (propType.equals(BigInteger.class)){ + return Types.BIGINT; + } + String msg = "Can not support Counter for type "+propType.getName(); + throw new PersistenceException(msg); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounter.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounter.java index 8a39d5174..1281f386f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounter.java @@ -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.generatedproperty; - -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * A general number counter for various number types. - */ -public class GeneratedCounter implements GeneratedProperty { - - final int numberType; - - public GeneratedCounter(int numberType) { - this.numberType = numberType; - } - - /** - * Always returns a 1. - */ - public Object getInsertValue(BeanProperty prop, Object bean) { - Integer i = Integer.valueOf(1); - return BasicTypeConverter.convert(i, numberType); - } - - /** - * Increments the current value by one. - */ - public Object getUpdateValue(BeanProperty prop, Object bean) { - Number currVal = (Number) prop.getValue(bean); - Integer nextVal = Integer.valueOf(currVal.intValue() + 1); - return BasicTypeConverter.convert(nextVal, numberType); - } - - /** - * Include this in every update. - */ - public boolean includeInUpdate() { - return true; - } - - /** - * Include this in every insert setting initial counter value to 1. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * A general number counter for various number types. + */ +public class GeneratedCounter implements GeneratedProperty { + + final int numberType; + + public GeneratedCounter(int numberType) { + this.numberType = numberType; + } + + /** + * Always returns a 1. + */ + public Object getInsertValue(BeanProperty prop, Object bean) { + Integer i = Integer.valueOf(1); + return BasicTypeConverter.convert(i, numberType); + } + + /** + * Increments the current value by one. + */ + public Object getUpdateValue(BeanProperty prop, Object bean) { + Number currVal = (Number) prop.getValue(bean); + Integer nextVal = Integer.valueOf(currVal.intValue() + 1); + return BasicTypeConverter.convert(nextVal, numberType); + } + + /** + * Include this in every update. + */ + public boolean includeInUpdate() { + return true; + } + + /** + * Include this in every insert setting initial counter value to 1. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterInteger.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterInteger.java index a1e6562da..71b627a73 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterInteger.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterInteger.java @@ -1,66 +1,47 @@ -/** - * 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.generatedproperty; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Used to create a counter version column for Integer. - */ -public class GeneratedCounterInteger implements GeneratedProperty { - - public GeneratedCounterInteger() { - - } - - /** - * Always returns a 1. - */ - public Object getInsertValue(BeanProperty prop, Object bean) { - return Integer.valueOf(1); - } - - /** - * Increments the current value by one. - */ - public Object getUpdateValue(BeanProperty prop, Object bean) { - Integer i = (Integer) prop.getValue(bean); - return Integer.valueOf(i.intValue() + 1); - } - - /** - * Include this in every update. - */ - public boolean includeInUpdate() { - return true; - } - - /** - * Include this in every insert setting initial counter value to 1. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Used to create a counter version column for Integer. + */ +public class GeneratedCounterInteger implements GeneratedProperty { + + public GeneratedCounterInteger() { + + } + + /** + * Always returns a 1. + */ + public Object getInsertValue(BeanProperty prop, Object bean) { + return Integer.valueOf(1); + } + + /** + * Increments the current value by one. + */ + public Object getUpdateValue(BeanProperty prop, Object bean) { + Integer i = (Integer) prop.getValue(bean); + return Integer.valueOf(i.intValue() + 1); + } + + /** + * Include this in every update. + */ + public boolean includeInUpdate() { + return true; + } + + /** + * Include this in every insert setting initial counter value to 1. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterLong.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterLong.java index 844bb4d4b..941fc6717 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterLong.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedCounterLong.java @@ -1,66 +1,47 @@ -/** - * 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.generatedproperty; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Used to create a counter version column for Long. - */ -public class GeneratedCounterLong implements GeneratedProperty { - - public GeneratedCounterLong() { - - } - - /** - * Always returns a 1. - */ - public Object getInsertValue(BeanProperty prop, Object bean) { - return Long.valueOf(1); - } - - /** - * Increments the current value by one. - */ - public Object getUpdateValue(BeanProperty prop, Object bean) { - Long i = (Long) prop.getValue(bean); - return Long.valueOf(i.longValue() + 1); - } - - /** - * Include this in every update. - */ - public boolean includeInUpdate() { - return true; - } - - /** - * Include this in every insert setting initial counter value to 1. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Used to create a counter version column for Long. + */ +public class GeneratedCounterLong implements GeneratedProperty { + + public GeneratedCounterLong() { + + } + + /** + * Always returns a 1. + */ + public Object getInsertValue(BeanProperty prop, Object bean) { + return Long.valueOf(1); + } + + /** + * Increments the current value by one. + */ + public Object getUpdateValue(BeanProperty prop, Object bean) { + Long i = (Long) prop.getValue(bean); + return Long.valueOf(i.longValue() + 1); + } + + /** + * Include this in every update. + */ + public boolean includeInUpdate() { + return true; + } + + /** + * Include this in every insert setting initial counter value to 1. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertDate.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertDate.java index fcdd8a970..7af37a8cb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertDate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertDate.java @@ -1,63 +1,44 @@ -/** - * 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.generatedproperty; - -import java.util.Date; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Used to generate a (java.util.Date) timestamp when a bean is inserted. - */ -public class GeneratedInsertDate implements GeneratedProperty { - - /** - * Return the current time as a Timestamp. - */ - public Object getInsertValue(BeanProperty prop, Object bean) { - return new Date(System.currentTimeMillis()); - } - - /** - * Just returns the beans original insert timestamp value. - */ - public Object getUpdateValue(BeanProperty prop, Object bean) { - return prop.getValue(bean); - } - - /** - * Return false. - */ - public boolean includeInUpdate() { - return false; - } - - /** - * Return true. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.util.Date; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Used to generate a (java.util.Date) timestamp when a bean is inserted. + */ +public class GeneratedInsertDate implements GeneratedProperty { + + /** + * Return the current time as a Timestamp. + */ + public Object getInsertValue(BeanProperty prop, Object bean) { + return new Date(System.currentTimeMillis()); + } + + /** + * Just returns the beans original insert timestamp value. + */ + public Object getUpdateValue(BeanProperty prop, Object bean) { + return prop.getValue(bean); + } + + /** + * Return false. + */ + public boolean includeInUpdate() { + return false; + } + + /** + * Return true. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertLong.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertLong.java index 081219278..d32f53fc9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertLong.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertLong.java @@ -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.generatedproperty; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Used to generate a (Long) timestamp when a bean is inserted. - */ -public class GeneratedInsertLong implements GeneratedProperty { - - /** - * Return the current time as a Timestamp. - */ - public Object getInsertValue(BeanProperty prop, Object bean) { - return Long.valueOf(System.currentTimeMillis()); - } - - /** - * Just returns the beans original insert timestamp value. - */ - public Object getUpdateValue(BeanProperty prop, Object bean) { - return prop.getValue(bean); - } - - /** - * Return false. - */ - public boolean includeInUpdate() { - return false; - } - - /** - * Return true. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Used to generate a (Long) timestamp when a bean is inserted. + */ +public class GeneratedInsertLong implements GeneratedProperty { + + /** + * Return the current time as a Timestamp. + */ + public Object getInsertValue(BeanProperty prop, Object bean) { + return Long.valueOf(System.currentTimeMillis()); + } + + /** + * Just returns the beans original insert timestamp value. + */ + public Object getUpdateValue(BeanProperty prop, Object bean) { + return prop.getValue(bean); + } + + /** + * Return false. + */ + public boolean includeInUpdate() { + return false; + } + + /** + * Return true. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertTimestamp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertTimestamp.java index 53f9e7d68..4f3634de1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertTimestamp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedInsertTimestamp.java @@ -1,63 +1,44 @@ -/** - * 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.generatedproperty; - -import java.sql.Timestamp; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Used to generate a timestamp when a bean is inserted. - */ -public class GeneratedInsertTimestamp implements GeneratedProperty { - - /** - * Return the current time as a Timestamp. - */ - public Object getInsertValue(BeanProperty prop, Object bean) { - return new Timestamp(System.currentTimeMillis()); - } - - /** - * Just returns the beans original insert timestamp value. - */ - public Object getUpdateValue(BeanProperty prop, Object bean) { - return prop.getValue(bean); - } - - /** - * Return false. - */ - public boolean includeInUpdate() { - return false; - } - - /** - * Return true. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.sql.Timestamp; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Used to generate a timestamp when a bean is inserted. + */ +public class GeneratedInsertTimestamp implements GeneratedProperty { + + /** + * Return the current time as a Timestamp. + */ + public Object getInsertValue(BeanProperty prop, Object bean) { + return new Timestamp(System.currentTimeMillis()); + } + + /** + * Just returns the beans original insert timestamp value. + */ + public Object getUpdateValue(BeanProperty prop, Object bean) { + return prop.getValue(bean); + } + + /** + * Return false. + */ + public boolean includeInUpdate() { + return false; + } + + /** + * Return true. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedProperty.java index 5a3e754c5..1a20eff0e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedProperty.java @@ -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.generatedproperty; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Used to generate values for a property rather than have then set by the user. - * For example generate the update timestamp when a bean is updated. - */ -public interface GeneratedProperty { - - /** - * Get the generated insert value for a specific property of a bean. - */ - public Object getInsertValue(BeanProperty prop, Object bean); - - /** - * Get the generated update value for a specific property of a bean. - */ - public Object getUpdateValue(BeanProperty prop, Object bean); - - /** - * Return true if this should always be includes in an update statement. - *

- * Used to include GeneratedUpdateTimestamp in dynamic table updates. - *

- */ - public boolean includeInUpdate(); - - /** - * Return true if this should be included in insert statements. - */ - public boolean includeInInsert(); - - /** - * Return true if the GeneratedProperty implies the DDL to create the DB - * column should have a not null constraint. - */ - public boolean isDDLNotNullable(); - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Used to generate values for a property rather than have then set by the user. + * For example generate the update timestamp when a bean is updated. + */ +public interface GeneratedProperty { + + /** + * Get the generated insert value for a specific property of a bean. + */ + public Object getInsertValue(BeanProperty prop, Object bean); + + /** + * Get the generated update value for a specific property of a bean. + */ + public Object getUpdateValue(BeanProperty prop, Object bean); + + /** + * Return true if this should always be includes in an update statement. + *

+ * Used to include GeneratedUpdateTimestamp in dynamic table updates. + *

+ */ + public boolean includeInUpdate(); + + /** + * Return true if this should be included in insert statements. + */ + public boolean includeInInsert(); + + /** + * Return true if the GeneratedProperty implies the DDL to create the DB + * column should have a not null constraint. + */ + public boolean isDDLNotNullable(); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedPropertyFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedPropertyFactory.java index ef81bb272..3d58d972d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedPropertyFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedPropertyFactory.java @@ -1,84 +1,65 @@ -/** - * 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.generatedproperty; - -import java.math.BigDecimal; -import java.util.HashSet; - -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; - -/** - * Default implementation of GeneratedPropertyFactory. - */ -public class GeneratedPropertyFactory { - - CounterFactory counterFactory; - - InsertTimestampFactory insertFactory; - - UpdateTimestampFactory updateFactory; - - HashSet numberTypes = new HashSet(); - - public GeneratedPropertyFactory() { - counterFactory = new CounterFactory(); - insertFactory = new InsertTimestampFactory(); - updateFactory = new UpdateTimestampFactory(); - - - numberTypes.add(Integer.class.getName()); - numberTypes.add(int.class.getName()); - numberTypes.add(Long.class.getName()); - numberTypes.add(long.class.getName()); - numberTypes.add(Short.class.getName()); - numberTypes.add(short.class.getName()); - numberTypes.add(Double.class.getName()); - numberTypes.add(double.class.getName()); - numberTypes.add(BigDecimal.class.getName()); - } - - private boolean isNumberType(String typeClassName) { - return numberTypes.contains(typeClassName); - } - - public void setVersion(DeployBeanProperty property) { - if (isNumberType(property.getPropertyType().getName())) { - setCounter(property); - } else { - setUpdateTimestamp(property); - } - } - - public void setCounter(DeployBeanProperty property) { - - counterFactory.setCounter(property); - } - - public void setInsertTimestamp(DeployBeanProperty property) { - - insertFactory.setInsertTimestamp(property); - } - - public void setUpdateTimestamp(DeployBeanProperty property) { - - updateFactory.setUpdateTimestamp(property); - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.math.BigDecimal; +import java.util.HashSet; + +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; + +/** + * Default implementation of GeneratedPropertyFactory. + */ +public class GeneratedPropertyFactory { + + CounterFactory counterFactory; + + InsertTimestampFactory insertFactory; + + UpdateTimestampFactory updateFactory; + + HashSet numberTypes = new HashSet(); + + public GeneratedPropertyFactory() { + counterFactory = new CounterFactory(); + insertFactory = new InsertTimestampFactory(); + updateFactory = new UpdateTimestampFactory(); + + + numberTypes.add(Integer.class.getName()); + numberTypes.add(int.class.getName()); + numberTypes.add(Long.class.getName()); + numberTypes.add(long.class.getName()); + numberTypes.add(Short.class.getName()); + numberTypes.add(short.class.getName()); + numberTypes.add(Double.class.getName()); + numberTypes.add(double.class.getName()); + numberTypes.add(BigDecimal.class.getName()); + } + + private boolean isNumberType(String typeClassName) { + return numberTypes.contains(typeClassName); + } + + public void setVersion(DeployBeanProperty property) { + if (isNumberType(property.getPropertyType().getName())) { + setCounter(property); + } else { + setUpdateTimestamp(property); + } + } + + public void setCounter(DeployBeanProperty property) { + + counterFactory.setCounter(property); + } + + public void setInsertTimestamp(DeployBeanProperty property) { + + insertFactory.setInsertTimestamp(property); + } + + public void setUpdateTimestamp(DeployBeanProperty property) { + + updateFactory.setUpdateTimestamp(property); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateDate.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateDate.java index e89a304f5..031a6e3cd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateDate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateDate.java @@ -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.generatedproperty; - -import java.util.Date; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Generate a (java.util.Date) Timestamp whenever the bean is inserted or - * updated. - */ -public class GeneratedUpdateDate implements GeneratedProperty { - - /** - * Return now as a Timestamp. - */ - public Object getInsertValue(BeanProperty prop, Object bean) { - return new Date(System.currentTimeMillis()); - } - - /** - * Return now as a Timestamp. - */ - public Object getUpdateValue(BeanProperty prop, Object bean) { - return new Date(System.currentTimeMillis()); - } - - /** - * For dynamic table updates make sure this is included. - */ - public boolean includeInUpdate() { - return true; - } - - /** - * Include this in every insert. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.util.Date; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Generate a (java.util.Date) Timestamp whenever the bean is inserted or + * updated. + */ +public class GeneratedUpdateDate implements GeneratedProperty { + + /** + * Return now as a Timestamp. + */ + public Object getInsertValue(BeanProperty prop, Object bean) { + return new Date(System.currentTimeMillis()); + } + + /** + * Return now as a Timestamp. + */ + public Object getUpdateValue(BeanProperty prop, Object bean) { + return new Date(System.currentTimeMillis()); + } + + /** + * For dynamic table updates make sure this is included. + */ + public boolean includeInUpdate() { + return true; + } + + /** + * Include this in every insert. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateLong.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateLong.java index 11917df53..29e5248e4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateLong.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateLong.java @@ -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.generatedproperty; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Generate a (Long) Timestamp whenever the bean is inserted or updated. - */ -public class GeneratedUpdateLong implements GeneratedProperty { - - /** - * Return now as a Timestamp. - */ - public Object getInsertValue(BeanProperty prop, Object bean) { - return Long.valueOf(System.currentTimeMillis()); - } - - /** - * Return now as a Timestamp. - */ - public Object getUpdateValue(BeanProperty prop, Object bean) { - return Long.valueOf(System.currentTimeMillis()); - } - - /** - * For dynamic table updates make sure this is included. - */ - public boolean includeInUpdate() { - return true; - } - - /** - * Include this in every insert. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Generate a (Long) Timestamp whenever the bean is inserted or updated. + */ +public class GeneratedUpdateLong implements GeneratedProperty { + + /** + * Return now as a Timestamp. + */ + public Object getInsertValue(BeanProperty prop, Object bean) { + return Long.valueOf(System.currentTimeMillis()); + } + + /** + * Return now as a Timestamp. + */ + public Object getUpdateValue(BeanProperty prop, Object bean) { + return Long.valueOf(System.currentTimeMillis()); + } + + /** + * For dynamic table updates make sure this is included. + */ + public boolean includeInUpdate() { + return true; + } + + /** + * Include this in every insert. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateTimestamp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateTimestamp.java index ee627f19f..648ebf538 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateTimestamp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/GeneratedUpdateTimestamp.java @@ -1,63 +1,44 @@ -/** - * 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.generatedproperty; - -import java.sql.Timestamp; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Generate a Timestamp whenever the bean is inserted or updated. - */ -public class GeneratedUpdateTimestamp implements GeneratedProperty { - - /** - * Return now as a Timestamp. - */ - public Object getInsertValue(BeanProperty prop, Object bean) { - return new Timestamp(System.currentTimeMillis()); - } - - /** - * Return now as a Timestamp. - */ - public Object getUpdateValue(BeanProperty prop, Object bean) { - return new Timestamp(System.currentTimeMillis()); - } - - /** - * For dynamic table updates make sure this is included. - */ - public boolean includeInUpdate() { - return true; - } - - /** - * Include this in every insert. - */ - public boolean includeInInsert() { - return true; - } - - public boolean isDDLNotNullable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.sql.Timestamp; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Generate a Timestamp whenever the bean is inserted or updated. + */ +public class GeneratedUpdateTimestamp implements GeneratedProperty { + + /** + * Return now as a Timestamp. + */ + public Object getInsertValue(BeanProperty prop, Object bean) { + return new Timestamp(System.currentTimeMillis()); + } + + /** + * Return now as a Timestamp. + */ + public Object getUpdateValue(BeanProperty prop, Object bean) { + return new Timestamp(System.currentTimeMillis()); + } + + /** + * For dynamic table updates make sure this is included. + */ + public boolean includeInUpdate() { + return true; + } + + /** + * Include this in every insert. + */ + public boolean includeInInsert() { + return true; + } + + public boolean isDDLNotNullable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/InsertTimestampFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/InsertTimestampFactory.java index 5b048449e..2c50fba1b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/InsertTimestampFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/InsertTimestampFactory.java @@ -1,66 +1,47 @@ -/** - * 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.generatedproperty; - -import java.sql.Timestamp; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; - -/** - * Helper for creating Insert timestamp GeneratedProperty objects. - */ -public class InsertTimestampFactory { - - final GeneratedInsertTimestamp timestamp = new GeneratedInsertTimestamp(); - - final GeneratedInsertDate utilDate = new GeneratedInsertDate(); - - final GeneratedInsertLong longTime = new GeneratedInsertLong(); - - public void setInsertTimestamp(DeployBeanProperty property) { - - property.setGeneratedProperty(createInsertTimestamp(property)); - } - - /** - * Create the insert GeneratedProperty depending on the property type. - */ - public GeneratedProperty createInsertTimestamp(DeployBeanProperty property) { - - Class propType = property.getPropertyType(); - if (propType.equals(Timestamp.class)) { - return timestamp; - } - if (propType.equals(java.util.Date.class)) { - return utilDate; - } - if (propType.equals(Long.class) || propType.equals(long.class)) { - return longTime; - } - - //TODO: Support JODA Time objects ... perhaps others? - - String msg = "Generated Insert Timestamp not supported on "+propType.getName(); - throw new PersistenceException(msg); - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.sql.Timestamp; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; + +/** + * Helper for creating Insert timestamp GeneratedProperty objects. + */ +public class InsertTimestampFactory { + + final GeneratedInsertTimestamp timestamp = new GeneratedInsertTimestamp(); + + final GeneratedInsertDate utilDate = new GeneratedInsertDate(); + + final GeneratedInsertLong longTime = new GeneratedInsertLong(); + + public void setInsertTimestamp(DeployBeanProperty property) { + + property.setGeneratedProperty(createInsertTimestamp(property)); + } + + /** + * Create the insert GeneratedProperty depending on the property type. + */ + public GeneratedProperty createInsertTimestamp(DeployBeanProperty property) { + + Class propType = property.getPropertyType(); + if (propType.equals(Timestamp.class)) { + return timestamp; + } + if (propType.equals(java.util.Date.class)) { + return utilDate; + } + if (propType.equals(Long.class) || propType.equals(long.class)) { + return longTime; + } + + //TODO: Support JODA Time objects ... perhaps others? + + String msg = "Generated Insert Timestamp not supported on "+propType.getName(); + throw new PersistenceException(msg); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/UpdateTimestampFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/UpdateTimestampFactory.java index 372328f7b..0606d628b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/UpdateTimestampFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/generatedproperty/UpdateTimestampFactory.java @@ -1,66 +1,47 @@ -/** - * 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.generatedproperty; - -import java.sql.Timestamp; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; - -/** - * Helper for creating Update timestamp GeneratedProperty objects. - */ -public class UpdateTimestampFactory { - - final GeneratedUpdateTimestamp timestamp = new GeneratedUpdateTimestamp(); - - final GeneratedUpdateDate utilDate = new GeneratedUpdateDate(); - - final GeneratedUpdateLong longTime = new GeneratedUpdateLong(); - - public void setUpdateTimestamp(DeployBeanProperty property) { - - property.setGeneratedProperty(createUpdateTimestamp(property)); - } - - /** - * Create the update GeneratedProperty depending on the property type. - */ - private GeneratedProperty createUpdateTimestamp(DeployBeanProperty property) { - - Class propType = property.getPropertyType(); - if (propType.equals(Timestamp.class)) { - return timestamp; - } - if (propType.equals(java.util.Date.class)) { - return utilDate; - } - if (propType.equals(Long.class) || propType.equals(long.class)) { - return longTime; - } - - //TODO: Support JODA Time objects ... perhaps others? - - String msg = "Generated update Timestamp not supported on "+propType.getName(); - throw new PersistenceException(msg); - } - -} +package com.avaje.ebeaninternal.server.deploy.generatedproperty; + +import java.sql.Timestamp; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; + +/** + * Helper for creating Update timestamp GeneratedProperty objects. + */ +public class UpdateTimestampFactory { + + final GeneratedUpdateTimestamp timestamp = new GeneratedUpdateTimestamp(); + + final GeneratedUpdateDate utilDate = new GeneratedUpdateDate(); + + final GeneratedUpdateLong longTime = new GeneratedUpdateLong(); + + public void setUpdateTimestamp(DeployBeanProperty property) { + + property.setGeneratedProperty(createUpdateTimestamp(property)); + } + + /** + * Create the update GeneratedProperty depending on the property type. + */ + private GeneratedProperty createUpdateTimestamp(DeployBeanProperty property) { + + Class propType = property.getPropertyType(); + if (propType.equals(Timestamp.class)) { + return timestamp; + } + if (propType.equals(java.util.Date.class)) { + return utilDate; + } + if (propType.equals(Long.class) || propType.equals(long.class)) { + return longTime; + } + + //TODO: Support JODA Time objects ... perhaps others? + + String msg = "Generated update Timestamp not supported on "+propType.getName(); + throw new PersistenceException(msg); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderFactory.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderFactory.java index 55d946abb..b11ea2f79 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/id/IdBinderFactory.java @@ -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.id; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; - -/** - * Creates the appropriate IdConvertSet depending on the type of Id property(s). - */ -public class IdBinderFactory { - - private static final IdBinderEmpty EMPTY = new IdBinderEmpty(); - - private final boolean idInExpandedForm; - - public IdBinderFactory(boolean idInExpandedForm) { - this.idInExpandedForm = idInExpandedForm; - } - - /** - * Create the IdConvertSet for the given type of Id properties. - */ - public IdBinder createIdBinder(BeanProperty[] uids) { - - if (uids.length == 0){ - // for report type beans that don't need an id - return EMPTY; - - } else if (uids.length == 1){ - if (uids[0].isEmbedded()){ - return new IdBinderEmbedded(idInExpandedForm, (BeanPropertyAssocOne)uids[0]); - } else { - return new IdBinderSimple(uids[0]); - } - - } else { - return new IdBinderMultiple(uids); - } - } - -} +package com.avaje.ebeaninternal.server.deploy.id; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; + +/** + * Creates the appropriate IdConvertSet depending on the type of Id property(s). + */ +public class IdBinderFactory { + + private static final IdBinderEmpty EMPTY = new IdBinderEmpty(); + + private final boolean idInExpandedForm; + + public IdBinderFactory(boolean idInExpandedForm) { + this.idInExpandedForm = idInExpandedForm; + } + + /** + * Create the IdConvertSet for the given type of Id properties. + */ + public IdBinder createIdBinder(BeanProperty[] uids) { + + if (uids.length == 0){ + // for report type beans that don't need an id + return EMPTY; + + } else if (uids.length == 1){ + if (uids[0].isEmbedded()){ + return new IdBinderEmbedded(idInExpandedForm, (BeanPropertyAssocOne)uids[0]); + } else { + return new IdBinderSimple(uids[0]); + } + + } else { + return new IdBinderMultiple(uids); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java index 7d3624a36..20db21417 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java @@ -1,1013 +1,994 @@ -/** - * 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.meta; - -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebean.Query.UseIndex; -import com.avaje.ebean.config.TableName; -import com.avaje.ebean.config.dbplatform.IdGenerator; -import com.avaje.ebean.config.dbplatform.IdType; -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.meta.MetaAutoFetchStatistic; -import com.avaje.ebeaninternal.server.core.CacheOptions; -import com.avaje.ebeaninternal.server.core.ConcurrencyMode; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; -import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistController; -import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistListener; -import com.avaje.ebeaninternal.server.deploy.ChainedBeanQueryAdapter; -import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; -import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta; -import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; -import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; -import com.avaje.ebeaninternal.server.deploy.InheritInfo; -import com.avaje.ebeaninternal.server.reflect.BeanReflect; - -/** - * Describes Beans including their deployment information. - */ -public class DeployBeanDescriptor { - - static class PropOrder implements Comparator { - - public int compare(DeployBeanProperty o1, DeployBeanProperty o2) { - - int v2 = o1.getSortOrder(); - int v1 = o2.getSortOrder(); - return (v1 < v2 ? -1 : (v1 == v2 ? 0 : 1)); - } - } - - private static final PropOrder PROP_ORDER = new PropOrder(); - - private static final String I_SCALAOBJECT = "scala.ScalaObject"; - - private static final Logger logger = Logger.getLogger(DeployBeanDescriptor.class.getName()); - - private static final String META_BEAN_PREFIX = MetaAutoFetchStatistic.class.getName().substring(0, 20); - - /** - * Map of BeanProperty Linked so as to preserve order. - */ - private LinkedHashMap propMap = new LinkedHashMap(); - - /** - * The type of bean this describes. - */ - private final Class beanType; - - private EntityType entityType; - - private final Map namedQueries = new LinkedHashMap(); - - private final Map namedUpdates = new LinkedHashMap(); - - private final Map rawSqlMetas = new LinkedHashMap(); - - private DeployBeanPropertyAssocOne unidirectional; - - /** - * Type of Identity generation strategy used. - */ - private IdType idType; - - /** - * The name of an IdGenerator (optional). - */ - private String idGeneratorName; - - private IdGenerator idGenerator; - - /** - * The database sequence name (optional). - */ - private String sequenceName; - - private String ldapBaseDn; - - private String[] ldapObjectclasses; - - /** - * Used with Identity columns but no getGeneratedKeys support. - */ - private String selectLastInsertedId; - - private String lazyFetchIncludes; - - /** - * The concurrency mode for beans of this type. - */ - private ConcurrencyMode concurrencyMode = ConcurrencyMode.ALL; - - private boolean updateChangesOnly; - - /** - * The tables this bean is dependent on. - */ - private String[] dependantTables; - - private List compoundUniqueConstraints; - - /** - * Extra deployment attributes. - */ - private HashMap extraAttrMap = new HashMap(); - - /** - * The base database table. - */ - private String baseTable; - private TableName baseTableFull; - - /** - * Used to provide mechanism to new EntityBean instances. Generated code - * faster than reflection at this stage. - */ - private BeanReflect beanReflect; - - /** - * The EntityBean type used to create new EntityBeans. - */ - private Class factoryType; - - private List persistControllers = new ArrayList(); - private List> persistListeners = new ArrayList>(); - private List queryAdapters = new ArrayList(); - - private CacheOptions cacheOptions = new CacheOptions(); - - /** - * If set overrides the find implementation. Server side only. - */ - private BeanFinder beanFinder; - - private UseIndex useIndex; - - /** - * The table joins for this bean. Server side only. - */ - private ArrayList tableJoinList = new ArrayList(); - - /** - * Inheritance information. Server side only. - */ - private InheritInfo inheritInfo; - - private String name; - - private boolean processedRawSqlExtend; - - /** - * Construct the BeanDescriptor. - */ - public DeployBeanDescriptor(Class beanType) { - this.beanType = beanType; - } - - /** - * Return true if this beanType is an abstract class. - */ - public boolean isAbstract() { - return Modifier.isAbstract(beanType.getModifiers()); - } - - /** - * Return the default UseIndex strategy. - */ - public UseIndex getUseIndex() { - return useIndex; - } - - /** - * Set the default UseIndex strategy. - */ - public void setUseIndex(UseIndex useIndex) { - this.useIndex = useIndex; - } - - public boolean isScalaObject() { - Class[] interfaces = beanType.getInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - String iname = interfaces[i].getName(); - if (I_SCALAOBJECT.equals(iname)) { - return true; - } - } - return false; - } - - public Collection getRawSqlMeta() { - if (!processedRawSqlExtend) { - rawSqlProcessExtend(); - processedRawSqlExtend = true; - } - return rawSqlMetas.values(); - } - - /** - * Process the "extend" attributes of raw SQL. Aka inherit the query and - * column mapping. - */ - private void rawSqlProcessExtend() { - - for (DRawSqlMeta rawSqlMeta : rawSqlMetas.values()) { - String extend = rawSqlMeta.getExtend(); - if (extend != null) { - DRawSqlMeta parentQuery = rawSqlMetas.get(extend); - if (parentQuery == null) { - throw new RuntimeException("parent query [" + extend + "] not found for sql-select " + rawSqlMeta.getName()); - } - rawSqlMeta.extend(parentQuery); - } - } - } - - public DeployBeanTable createDeployBeanTable() { - - DeployBeanTable beanTable = new DeployBeanTable(getBeanType()); - beanTable.setBaseTable(baseTable); - beanTable.setIdProperties(propertiesId()); - - return beanTable; - } - - /** - * Check all the properties to see if they all have read and write methods - * (required if using "subclassing" but not for "enhancement"). - */ - public boolean checkReadAndWriteMethods() { - - if (isMeta()) { - return true; - } - boolean missingMethods = false; - - Iterator it = propMap.values().iterator(); - while (it.hasNext()) { - DeployBeanProperty prop = it.next(); - if (!prop.isTransient()) { - String m = ""; - if (prop.getReadMethod() == null) { - m += " missing readMethod "; - } - if (prop.getWriteMethod() == null) { - m += " missing writeMethod "; - } - if (!"".equals(m)) { - m += ". Should it be transient?"; - String msg = "Bean property " + getFullName() + "." + prop.getName() + " has " + m; - logger.log(Level.SEVERE, msg); - missingMethods = true; - } - } - } - return !missingMethods; - } - - public void setEntityType(EntityType entityType) { - this.entityType = entityType; - } - - public boolean isEmbedded() { - return EntityType.EMBEDDED.equals(entityType); - } - - public boolean isBaseTableType() { - EntityType et = getEntityType(); - return EntityType.ORM.equals(et); - } - - public EntityType getEntityType() { - if (entityType == null) { - entityType = isMeta() ? EntityType.META : EntityType.ORM; - } - return entityType; - } - - /** - * Return true if this is a Meta entity bean. - *

- * The Meta entity beans are not based on real tables but get meta information - * from memory such as all the entity bean meta data. - *

- */ - private boolean isMeta() { - return beanType.getName().startsWith(META_BEAN_PREFIX); - } - - public void add(DRawSqlMeta rawSqlMeta) { - rawSqlMetas.put(rawSqlMeta.getName(), rawSqlMeta); - if ("default".equals(rawSqlMeta.getName())) { - setEntityType(EntityType.SQL); - } - } - - public void add(DeployNamedUpdate namedUpdate) { - namedUpdates.put(namedUpdate.getName(), namedUpdate); - } - - public void add(DeployNamedQuery namedQuery) { - namedQueries.put(namedQuery.getName(), namedQuery); - if ("default".equals(namedQuery.getName())) { - setEntityType(EntityType.SQL); - } - } - - public Map getNamedQueries() { - return namedQueries; - } - - public Map getNamedUpdates() { - return namedUpdates; - } - - public BeanReflect getBeanReflect() { - return beanReflect; - } - - /** - * Return the class type this BeanDescriptor describes. - */ - public Class getBeanType() { - return beanType; - } - - /** - * Return the class type this BeanDescriptor describes. - */ - public Class getFactoryType() { - return factoryType; - } - - /** - * Set the class used to create new EntityBean instances. - *

- * Normally this would be a subclass dynamically generated for this bean. - *

- */ - public void setFactoryType(Class factoryType) { - this.factoryType = factoryType; - } - - /** - * Set the BeanReflect used to create new instances of an EntityBean. This - * could use reflection or code generation to do this. - */ - public void setBeanReflect(BeanReflect beanReflect) { - this.beanReflect = beanReflect; - } - - /** - * Returns the Inheritance mapping information. This will be null if this type - * of bean is not involved in any ORM inheritance mapping. - */ - public InheritInfo getInheritInfo() { - return inheritInfo; - } - - /** - * Set the ORM inheritance mapping information. - */ - public void setInheritInfo(InheritInfo inheritInfo) { - this.inheritInfo = inheritInfo; - } - - /** - * Return the reference options. - */ - public CacheOptions getCacheOptions() { - return cacheOptions; - } - - public boolean isNaturalKeyProperty(String name) { - return name.equals(cacheOptions.getNaturalKey()); - } - - public DeployBeanPropertyAssocOne getUnidirectional() { - return unidirectional; - } - - public void setUnidirectional(DeployBeanPropertyAssocOne unidirectional) { - this.unidirectional = unidirectional; - } - - /** - * Return the concurrency mode used for beans of this type. - */ - public ConcurrencyMode getConcurrencyMode() { - return concurrencyMode; - } - - /** - * Set the concurrency mode used for beans of this type. - */ - public void setConcurrencyMode(ConcurrencyMode concurrencyMode) { - this.concurrencyMode = concurrencyMode; - } - - public String getLdapBaseDn() { - return ldapBaseDn; - } - - public void setLdapBaseDn(String ldapBaseDn) { - this.ldapBaseDn = ldapBaseDn; - } - - public String[] getLdapObjectclasses() { - return ldapObjectclasses; - } - - public void setLdapObjectclasses(String[] ldapObjectclasses) { - this.ldapObjectclasses = ldapObjectclasses; - } - - public boolean isUpdateChangesOnly() { - return updateChangesOnly; - } - - public void setUpdateChangesOnly(boolean updateChangesOnly) { - this.updateChangesOnly = updateChangesOnly; - } - - /** - * Return the tables this bean is dependant on. This implies that if any of - * these tables are modified then cached beans may be invalidated. - */ - public String[] getDependantTables() { - return dependantTables; - } - - /** - * Add a compound unique constraint. - */ - public void addCompoundUniqueConstraint(CompoundUniqueContraint c) { - if (compoundUniqueConstraints == null) { - compoundUniqueConstraints = new ArrayList(); - } - compoundUniqueConstraints.add(c); - } - - /** - * Return the compound unique constraints (can be null). - */ - public CompoundUniqueContraint[] getCompoundUniqueConstraints() { - if (compoundUniqueConstraints == null) { - return null; - } else { - return compoundUniqueConstraints.toArray(new CompoundUniqueContraint[compoundUniqueConstraints.size()]); - } - } - - /** - * Set the tables this bean is dependant on. This implies that if any of these - * tables are modified then cached beans may be invalidated. - */ - public void setDependantTables(String[] dependantTables) { - this.dependantTables = dependantTables; - } - - /** - * Return the beanFinder. Usually null unless overriding the finder. - */ - public BeanFinder getBeanFinder() { - return beanFinder; - } - - /** - * Set the BeanFinder to use for beans of this type. This is set to override - * the finding from the default. - */ - public void setBeanFinder(BeanFinder beanFinder) { - this.beanFinder = beanFinder; - } - - /** - * Return the BeanPersistController (could be a chain of them, 1 or null). - */ - public BeanPersistController getPersistController() { - if (persistControllers.size() == 0) { - return null; - } else if (persistControllers.size() == 1) { - return persistControllers.get(0); - } else { - return new ChainedBeanPersistController(persistControllers); - } - } - - /** - * Return the BeanPersistListener (could be a chain of them, 1 or null). - */ - public BeanPersistListener getPersistListener() { - if (persistListeners.size() == 0) { - return null; - } else if (persistListeners.size() == 1) { - return persistListeners.get(0); - } else { - return new ChainedBeanPersistListener(persistListeners); - } - } - - public BeanQueryAdapter getQueryAdapter() { - if (queryAdapters.size() == 0) { - return null; - } else if (queryAdapters.size() == 1) { - return queryAdapters.get(0); - } else { - return new ChainedBeanQueryAdapter(queryAdapters); - } - } - - /** - * Set the Controller. - */ - public void addPersistController(BeanPersistController controller) { - persistControllers.add(controller); - } - - public void addPersistListener(BeanPersistListener listener) { - persistListeners.add(listener); - } - - public void addQueryAdapter(BeanQueryAdapter queryAdapter) { - queryAdapters.add(queryAdapter); - } - - /** - * Return true if this bean type should use IdGeneration. - *

- * If this is false and the Id is null it is assumed that a database auto - * increment feature is being used to populate the id. - *

- */ - public boolean isUseIdGenerator() { - return idType == IdType.GENERATOR; - } - - /** - * Return the base table. Only properties mapped to the base table are by - * default persisted. - */ - public String getBaseTable() { - return baseTable; - } - - /** - * Return the base table with full structure. - */ - public TableName getBaseTableFull() { - return baseTableFull; - } - - /** - * Set the base table. Only properties mapped to the base table are by default - * persisted. - */ - public void setBaseTable(TableName baseTableFull) { - this.baseTableFull = baseTableFull; - this.baseTable = baseTableFull == null ? null : baseTableFull.getQualifiedName(); - } - - public void sortProperties() { - - ArrayList list = new ArrayList(); - list.addAll(propMap.values()); - - Collections.sort(list, PROP_ORDER); - - propMap = new LinkedHashMap(list.size()); - for (int i = 0; i < list.size(); i++) { - addBeanProperty(list.get(i)); - } - } - - /** - * Add a bean property. - */ - public DeployBeanProperty addBeanProperty(DeployBeanProperty prop) { - return propMap.put(prop.getName(), prop); - } - - /** - * Get a BeanProperty by its name. - */ - public DeployBeanProperty getBeanProperty(String propName) { - return propMap.get(propName); - } - - public Map getExtraAttributeMap() { - return extraAttrMap; - } - - /** - * Get a named extra attribute. - */ - public String getExtraAttribute(String key) { - return (String) extraAttrMap.get(key); - } - - /** - * Set an extra attribute with a given name. - * - * @param key - * the name of the extra attribute - * @param value - * the value of the extra attribute - */ - public void setExtraAttribute(String key, String value) { - extraAttrMap.put(key, value); - } - - /** - * Return the bean class name this descriptor is used for. - *

- * If this BeanDescriptor is for a table then this returns the table name - * instead. - *

- */ - public String getFullName() { - return beanType.getName(); - } - - /** - * Return the bean short name. - */ - public String getName() { - return name; - } - - /** - * Set the bean shortName. - */ - public void setName(String name) { - this.name = name; - } - - /** - * Return the identity generation type. - */ - public IdType getIdType() { - return idType; - } - - /** - * Set the identity generation type. - */ - public void setIdType(IdType idType) { - this.idType = idType; - } - - /** - * Return the DB sequence name (can be null). - */ - public String getSequenceName() { - return sequenceName; - } - - /** - * Set the DB sequence name. - */ - public void setSequenceName(String sequenceName) { - this.sequenceName = sequenceName; - } - - /** - * Return the SQL used to return the last inserted Id. - *

- * Used with Identity columns where getGeneratedKeys is not supported. - *

- */ - public String getSelectLastInsertedId() { - return selectLastInsertedId; - } - - /** - * Set the SQL used to return the last inserted Id. - */ - public void setSelectLastInsertedId(String selectLastInsertedId) { - this.selectLastInsertedId = selectLastInsertedId; - } - - /** - * Return the name of the IdGenerator that should be used with this type of - * bean. A null value could be used to specify the 'default' IdGenerator. - */ - public String getIdGeneratorName() { - return idGeneratorName; - } - - /** - * Set the name of the IdGenerator that should be used with this type of bean. - */ - public void setIdGeneratorName(String idGeneratorName) { - this.idGeneratorName = idGeneratorName; - } - - /** - * Return the actual IdGenerator for this bean type (can be null). - */ - public IdGenerator getIdGenerator() { - return idGenerator; - } - - /** - * Set the actual IdGenerator for this bean type. - */ - public void setIdGenerator(IdGenerator idGenerator) { - this.idGenerator = idGenerator; - if (idGenerator != null && idGenerator.isDbSequence()) { - setSequenceName(idGenerator.getName()); - } - } - - /** - * Return the includes for getReference(). - */ - public String getLazyFetchIncludes() { - return lazyFetchIncludes; - } - - /** - * Set includes to use for lazy loading by getReference(). Note queries also - * build references and includes on the actual association are used for those - * references. - */ - public void setLazyFetchIncludes(String lazyFetchIncludes) { - if (lazyFetchIncludes != null && lazyFetchIncludes.length() > 0) { - this.lazyFetchIncludes = lazyFetchIncludes; - } - } - - /** - * Summary description. - */ - public String toString() { - return getFullName(); - } - - /** - * Add a TableJoin to this type of bean. For Secondary table properties. - */ - public void addTableJoin(DeployTableJoin join) { - tableJoinList.add(join); - } - - public List getTableJoins() { - return tableJoinList; - } - - /** - * Return an Iterator of all BeanProperty. - */ - public Iterator propertiesAll() { - return propMap.values().iterator(); - } - - /** - * Return the defaultSelectClause using FetchType.LAZY and FetchType.EAGER. - */ - public String getDefaultSelectClause() { - - StringBuilder sb = new StringBuilder(); - - boolean hasLazyFetch = false; - - Iterator it = propMap.values().iterator(); - while (it.hasNext()) { - DeployBeanProperty prop = it.next(); - if (prop.isTransient()) { - // ignore transient props etc - } else if (prop instanceof DeployBeanPropertyAssocMany) { - // ignore the associated many properties - } else { - if (prop.isFetchEager()) { - sb.append(prop.getName()).append(","); - } else { - hasLazyFetch = true; - } - } - } - - if (!hasLazyFetch) { - return null; - } - String selectClause = sb.toString(); - return selectClause.substring(0, selectClause.length() - 1); - } - - /** - * Return an Array of properties to include in default fetch (for LDAP). - */ - public String[] getDefaultSelectDbArray(Set defaultSelect) { - - ArrayList list = new ArrayList(); - for (DeployBeanProperty p : propMap.values()) { - if (defaultSelect != null) { - if (defaultSelect.contains(p.getName())) { - // properties in defaultSelect - list.add(p.getDbColumn()); - } - } else if (!p.isTransient() && p.isDbRead()) { - // non transient db properties - list.add(p.getDbColumn()); - } - } - return list.toArray(new String[list.size()]); - } - - /** - * Parse the include separating by comma or semicolon. - */ - public Set parseDefaultSelectClause(String rawList) { - - if (rawList == null) { - return null; - } - - String[] res = rawList.split(","); - - LinkedHashSet set = new LinkedHashSet(res.length + 3); - - String temp = null; - for (int i = 0; i < res.length; i++) { - temp = res[i].trim(); - if (temp.length() > 0) { - set.add(temp); - } - } - return Collections.unmodifiableSet(set); - } - - /** - * Return the Primary Key column assuming it is a single column (not - * compound). This is for the purpose of defining a sequence name. - */ - public String getSinglePrimaryKeyColumn() { - List ids = propertiesId(); - if (ids.size() == 1) { - DeployBeanProperty p = ids.get(0); - if (p instanceof DeployBeanPropertyAssoc) { - // its a compound primary key - return null; - } else { - return p.getDbColumn(); - } - } - return null; - } - - /** - * Return the BeanProperty that make up the unique id. - *

- * The order of these properties can be relied on to be consistent if the bean - * itself doesn't change or the xml deployment order does not change. - *

- */ - public List propertiesId() { - - ArrayList list = new ArrayList(2); - - Iterator it = propMap.values().iterator(); - while (it.hasNext()) { - DeployBeanProperty prop = it.next(); - if (prop.isId()) { - list.add(prop); - } - } - - return list; - } - - public DeployBeanPropertyAssocOne findJoinToTable(String tableName) { - - List> assocOne = propertiesAssocOne(); - for (DeployBeanPropertyAssocOne prop : assocOne) { - DeployTableJoin tableJoin = prop.getTableJoin(); - if (tableJoin != null && tableJoin.getTable().equalsIgnoreCase(tableName)) { - return prop; - } - } - return null; - } - - /** - * Return an Iterator of BeanPropertyAssocOne that are not embedded. These are - * effectively joined beans. For ManyToOne and OneToOne associations. - */ - public List> propertiesAssocOne() { - - ArrayList> list = new ArrayList>(); - - Iterator it = propMap.values().iterator(); - while (it.hasNext()) { - DeployBeanProperty prop = it.next(); - if (prop instanceof DeployBeanPropertyAssocOne) { - if (!prop.isEmbedded()) { - list.add((DeployBeanPropertyAssocOne) prop); - } - } - } - - return list; - - } - - /** - * Return BeanPropertyAssocMany for this descriptor. - */ - public List> propertiesAssocMany() { - - ArrayList> list = new ArrayList>(); - - Iterator it = propMap.values().iterator(); - while (it.hasNext()) { - DeployBeanProperty prop = it.next(); - if (prop instanceof DeployBeanPropertyAssocMany) { - list.add((DeployBeanPropertyAssocMany) prop); - } - } - - return list; - } - - /** - * Returns 'Version' properties on this bean. These are 'Counter' or 'Update - * Timestamp' type properties. Note version properties can also be on embedded - * beans rather than on the bean itself. - */ - public List propertiesVersion() { - - ArrayList list = new ArrayList(); - - Iterator it = propMap.values().iterator(); - while (it.hasNext()) { - DeployBeanProperty prop = it.next(); - - if (prop instanceof DeployBeanPropertyAssoc) { - - } else { - if (!prop.isId() && prop.isVersionColumn()) { - list.add(prop); - } - } - } - - return list; - } - - /** - * base properties without the unique id properties. - */ - public List propertiesBase() { - - ArrayList list = new ArrayList(); - - Iterator it = propMap.values().iterator(); - while (it.hasNext()) { - DeployBeanProperty prop = it.next(); - - if (prop instanceof DeployBeanPropertyAssoc) { - - } else { - if (!prop.isId()) { - list.add(prop); - } - } - } - - return list; - } - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebean.Query.UseIndex; +import com.avaje.ebean.config.TableName; +import com.avaje.ebean.config.dbplatform.IdGenerator; +import com.avaje.ebean.config.dbplatform.IdType; +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.meta.MetaAutoFetchStatistic; +import com.avaje.ebeaninternal.server.core.CacheOptions; +import com.avaje.ebeaninternal.server.core.ConcurrencyMode; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; +import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistController; +import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistListener; +import com.avaje.ebeaninternal.server.deploy.ChainedBeanQueryAdapter; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta; +import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; +import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.reflect.BeanReflect; + +/** + * Describes Beans including their deployment information. + */ +public class DeployBeanDescriptor { + + static class PropOrder implements Comparator { + + public int compare(DeployBeanProperty o1, DeployBeanProperty o2) { + + int v2 = o1.getSortOrder(); + int v1 = o2.getSortOrder(); + return (v1 < v2 ? -1 : (v1 == v2 ? 0 : 1)); + } + } + + private static final PropOrder PROP_ORDER = new PropOrder(); + + private static final String I_SCALAOBJECT = "scala.ScalaObject"; + + private static final Logger logger = Logger.getLogger(DeployBeanDescriptor.class.getName()); + + private static final String META_BEAN_PREFIX = MetaAutoFetchStatistic.class.getName().substring(0, 20); + + /** + * Map of BeanProperty Linked so as to preserve order. + */ + private LinkedHashMap propMap = new LinkedHashMap(); + + /** + * The type of bean this describes. + */ + private final Class beanType; + + private EntityType entityType; + + private final Map namedQueries = new LinkedHashMap(); + + private final Map namedUpdates = new LinkedHashMap(); + + private final Map rawSqlMetas = new LinkedHashMap(); + + private DeployBeanPropertyAssocOne unidirectional; + + /** + * Type of Identity generation strategy used. + */ + private IdType idType; + + /** + * The name of an IdGenerator (optional). + */ + private String idGeneratorName; + + private IdGenerator idGenerator; + + /** + * The database sequence name (optional). + */ + private String sequenceName; + + private String ldapBaseDn; + + private String[] ldapObjectclasses; + + /** + * Used with Identity columns but no getGeneratedKeys support. + */ + private String selectLastInsertedId; + + private String lazyFetchIncludes; + + /** + * The concurrency mode for beans of this type. + */ + private ConcurrencyMode concurrencyMode = ConcurrencyMode.ALL; + + private boolean updateChangesOnly; + + /** + * The tables this bean is dependent on. + */ + private String[] dependantTables; + + private List compoundUniqueConstraints; + + /** + * Extra deployment attributes. + */ + private HashMap extraAttrMap = new HashMap(); + + /** + * The base database table. + */ + private String baseTable; + private TableName baseTableFull; + + /** + * Used to provide mechanism to new EntityBean instances. Generated code + * faster than reflection at this stage. + */ + private BeanReflect beanReflect; + + /** + * The EntityBean type used to create new EntityBeans. + */ + private Class factoryType; + + private List persistControllers = new ArrayList(); + private List> persistListeners = new ArrayList>(); + private List queryAdapters = new ArrayList(); + + private CacheOptions cacheOptions = new CacheOptions(); + + /** + * If set overrides the find implementation. Server side only. + */ + private BeanFinder beanFinder; + + private UseIndex useIndex; + + /** + * The table joins for this bean. Server side only. + */ + private ArrayList tableJoinList = new ArrayList(); + + /** + * Inheritance information. Server side only. + */ + private InheritInfo inheritInfo; + + private String name; + + private boolean processedRawSqlExtend; + + /** + * Construct the BeanDescriptor. + */ + public DeployBeanDescriptor(Class beanType) { + this.beanType = beanType; + } + + /** + * Return true if this beanType is an abstract class. + */ + public boolean isAbstract() { + return Modifier.isAbstract(beanType.getModifiers()); + } + + /** + * Return the default UseIndex strategy. + */ + public UseIndex getUseIndex() { + return useIndex; + } + + /** + * Set the default UseIndex strategy. + */ + public void setUseIndex(UseIndex useIndex) { + this.useIndex = useIndex; + } + + public boolean isScalaObject() { + Class[] interfaces = beanType.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + String iname = interfaces[i].getName(); + if (I_SCALAOBJECT.equals(iname)) { + return true; + } + } + return false; + } + + public Collection getRawSqlMeta() { + if (!processedRawSqlExtend) { + rawSqlProcessExtend(); + processedRawSqlExtend = true; + } + return rawSqlMetas.values(); + } + + /** + * Process the "extend" attributes of raw SQL. Aka inherit the query and + * column mapping. + */ + private void rawSqlProcessExtend() { + + for (DRawSqlMeta rawSqlMeta : rawSqlMetas.values()) { + String extend = rawSqlMeta.getExtend(); + if (extend != null) { + DRawSqlMeta parentQuery = rawSqlMetas.get(extend); + if (parentQuery == null) { + throw new RuntimeException("parent query [" + extend + "] not found for sql-select " + rawSqlMeta.getName()); + } + rawSqlMeta.extend(parentQuery); + } + } + } + + public DeployBeanTable createDeployBeanTable() { + + DeployBeanTable beanTable = new DeployBeanTable(getBeanType()); + beanTable.setBaseTable(baseTable); + beanTable.setIdProperties(propertiesId()); + + return beanTable; + } + + /** + * Check all the properties to see if they all have read and write methods + * (required if using "subclassing" but not for "enhancement"). + */ + public boolean checkReadAndWriteMethods() { + + if (isMeta()) { + return true; + } + boolean missingMethods = false; + + Iterator it = propMap.values().iterator(); + while (it.hasNext()) { + DeployBeanProperty prop = it.next(); + if (!prop.isTransient()) { + String m = ""; + if (prop.getReadMethod() == null) { + m += " missing readMethod "; + } + if (prop.getWriteMethod() == null) { + m += " missing writeMethod "; + } + if (!"".equals(m)) { + m += ". Should it be transient?"; + String msg = "Bean property " + getFullName() + "." + prop.getName() + " has " + m; + logger.log(Level.SEVERE, msg); + missingMethods = true; + } + } + } + return !missingMethods; + } + + public void setEntityType(EntityType entityType) { + this.entityType = entityType; + } + + public boolean isEmbedded() { + return EntityType.EMBEDDED.equals(entityType); + } + + public boolean isBaseTableType() { + EntityType et = getEntityType(); + return EntityType.ORM.equals(et); + } + + public EntityType getEntityType() { + if (entityType == null) { + entityType = isMeta() ? EntityType.META : EntityType.ORM; + } + return entityType; + } + + /** + * Return true if this is a Meta entity bean. + *

+ * The Meta entity beans are not based on real tables but get meta information + * from memory such as all the entity bean meta data. + *

+ */ + private boolean isMeta() { + return beanType.getName().startsWith(META_BEAN_PREFIX); + } + + public void add(DRawSqlMeta rawSqlMeta) { + rawSqlMetas.put(rawSqlMeta.getName(), rawSqlMeta); + if ("default".equals(rawSqlMeta.getName())) { + setEntityType(EntityType.SQL); + } + } + + public void add(DeployNamedUpdate namedUpdate) { + namedUpdates.put(namedUpdate.getName(), namedUpdate); + } + + public void add(DeployNamedQuery namedQuery) { + namedQueries.put(namedQuery.getName(), namedQuery); + if ("default".equals(namedQuery.getName())) { + setEntityType(EntityType.SQL); + } + } + + public Map getNamedQueries() { + return namedQueries; + } + + public Map getNamedUpdates() { + return namedUpdates; + } + + public BeanReflect getBeanReflect() { + return beanReflect; + } + + /** + * Return the class type this BeanDescriptor describes. + */ + public Class getBeanType() { + return beanType; + } + + /** + * Return the class type this BeanDescriptor describes. + */ + public Class getFactoryType() { + return factoryType; + } + + /** + * Set the class used to create new EntityBean instances. + *

+ * Normally this would be a subclass dynamically generated for this bean. + *

+ */ + public void setFactoryType(Class factoryType) { + this.factoryType = factoryType; + } + + /** + * Set the BeanReflect used to create new instances of an EntityBean. This + * could use reflection or code generation to do this. + */ + public void setBeanReflect(BeanReflect beanReflect) { + this.beanReflect = beanReflect; + } + + /** + * Returns the Inheritance mapping information. This will be null if this type + * of bean is not involved in any ORM inheritance mapping. + */ + public InheritInfo getInheritInfo() { + return inheritInfo; + } + + /** + * Set the ORM inheritance mapping information. + */ + public void setInheritInfo(InheritInfo inheritInfo) { + this.inheritInfo = inheritInfo; + } + + /** + * Return the reference options. + */ + public CacheOptions getCacheOptions() { + return cacheOptions; + } + + public boolean isNaturalKeyProperty(String name) { + return name.equals(cacheOptions.getNaturalKey()); + } + + public DeployBeanPropertyAssocOne getUnidirectional() { + return unidirectional; + } + + public void setUnidirectional(DeployBeanPropertyAssocOne unidirectional) { + this.unidirectional = unidirectional; + } + + /** + * Return the concurrency mode used for beans of this type. + */ + public ConcurrencyMode getConcurrencyMode() { + return concurrencyMode; + } + + /** + * Set the concurrency mode used for beans of this type. + */ + public void setConcurrencyMode(ConcurrencyMode concurrencyMode) { + this.concurrencyMode = concurrencyMode; + } + + public String getLdapBaseDn() { + return ldapBaseDn; + } + + public void setLdapBaseDn(String ldapBaseDn) { + this.ldapBaseDn = ldapBaseDn; + } + + public String[] getLdapObjectclasses() { + return ldapObjectclasses; + } + + public void setLdapObjectclasses(String[] ldapObjectclasses) { + this.ldapObjectclasses = ldapObjectclasses; + } + + public boolean isUpdateChangesOnly() { + return updateChangesOnly; + } + + public void setUpdateChangesOnly(boolean updateChangesOnly) { + this.updateChangesOnly = updateChangesOnly; + } + + /** + * Return the tables this bean is dependant on. This implies that if any of + * these tables are modified then cached beans may be invalidated. + */ + public String[] getDependantTables() { + return dependantTables; + } + + /** + * Add a compound unique constraint. + */ + public void addCompoundUniqueConstraint(CompoundUniqueContraint c) { + if (compoundUniqueConstraints == null) { + compoundUniqueConstraints = new ArrayList(); + } + compoundUniqueConstraints.add(c); + } + + /** + * Return the compound unique constraints (can be null). + */ + public CompoundUniqueContraint[] getCompoundUniqueConstraints() { + if (compoundUniqueConstraints == null) { + return null; + } else { + return compoundUniqueConstraints.toArray(new CompoundUniqueContraint[compoundUniqueConstraints.size()]); + } + } + + /** + * Set the tables this bean is dependant on. This implies that if any of these + * tables are modified then cached beans may be invalidated. + */ + public void setDependantTables(String[] dependantTables) { + this.dependantTables = dependantTables; + } + + /** + * Return the beanFinder. Usually null unless overriding the finder. + */ + public BeanFinder getBeanFinder() { + return beanFinder; + } + + /** + * Set the BeanFinder to use for beans of this type. This is set to override + * the finding from the default. + */ + public void setBeanFinder(BeanFinder beanFinder) { + this.beanFinder = beanFinder; + } + + /** + * Return the BeanPersistController (could be a chain of them, 1 or null). + */ + public BeanPersistController getPersistController() { + if (persistControllers.size() == 0) { + return null; + } else if (persistControllers.size() == 1) { + return persistControllers.get(0); + } else { + return new ChainedBeanPersistController(persistControllers); + } + } + + /** + * Return the BeanPersistListener (could be a chain of them, 1 or null). + */ + public BeanPersistListener getPersistListener() { + if (persistListeners.size() == 0) { + return null; + } else if (persistListeners.size() == 1) { + return persistListeners.get(0); + } else { + return new ChainedBeanPersistListener(persistListeners); + } + } + + public BeanQueryAdapter getQueryAdapter() { + if (queryAdapters.size() == 0) { + return null; + } else if (queryAdapters.size() == 1) { + return queryAdapters.get(0); + } else { + return new ChainedBeanQueryAdapter(queryAdapters); + } + } + + /** + * Set the Controller. + */ + public void addPersistController(BeanPersistController controller) { + persistControllers.add(controller); + } + + public void addPersistListener(BeanPersistListener listener) { + persistListeners.add(listener); + } + + public void addQueryAdapter(BeanQueryAdapter queryAdapter) { + queryAdapters.add(queryAdapter); + } + + /** + * Return true if this bean type should use IdGeneration. + *

+ * If this is false and the Id is null it is assumed that a database auto + * increment feature is being used to populate the id. + *

+ */ + public boolean isUseIdGenerator() { + return idType == IdType.GENERATOR; + } + + /** + * Return the base table. Only properties mapped to the base table are by + * default persisted. + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Return the base table with full structure. + */ + public TableName getBaseTableFull() { + return baseTableFull; + } + + /** + * Set the base table. Only properties mapped to the base table are by default + * persisted. + */ + public void setBaseTable(TableName baseTableFull) { + this.baseTableFull = baseTableFull; + this.baseTable = baseTableFull == null ? null : baseTableFull.getQualifiedName(); + } + + public void sortProperties() { + + ArrayList list = new ArrayList(); + list.addAll(propMap.values()); + + Collections.sort(list, PROP_ORDER); + + propMap = new LinkedHashMap(list.size()); + for (int i = 0; i < list.size(); i++) { + addBeanProperty(list.get(i)); + } + } + + /** + * Add a bean property. + */ + public DeployBeanProperty addBeanProperty(DeployBeanProperty prop) { + return propMap.put(prop.getName(), prop); + } + + /** + * Get a BeanProperty by its name. + */ + public DeployBeanProperty getBeanProperty(String propName) { + return propMap.get(propName); + } + + public Map getExtraAttributeMap() { + return extraAttrMap; + } + + /** + * Get a named extra attribute. + */ + public String getExtraAttribute(String key) { + return (String) extraAttrMap.get(key); + } + + /** + * Set an extra attribute with a given name. + * + * @param key + * the name of the extra attribute + * @param value + * the value of the extra attribute + */ + public void setExtraAttribute(String key, String value) { + extraAttrMap.put(key, value); + } + + /** + * Return the bean class name this descriptor is used for. + *

+ * If this BeanDescriptor is for a table then this returns the table name + * instead. + *

+ */ + public String getFullName() { + return beanType.getName(); + } + + /** + * Return the bean short name. + */ + public String getName() { + return name; + } + + /** + * Set the bean shortName. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Return the identity generation type. + */ + public IdType getIdType() { + return idType; + } + + /** + * Set the identity generation type. + */ + public void setIdType(IdType idType) { + this.idType = idType; + } + + /** + * Return the DB sequence name (can be null). + */ + public String getSequenceName() { + return sequenceName; + } + + /** + * Set the DB sequence name. + */ + public void setSequenceName(String sequenceName) { + this.sequenceName = sequenceName; + } + + /** + * Return the SQL used to return the last inserted Id. + *

+ * Used with Identity columns where getGeneratedKeys is not supported. + *

+ */ + public String getSelectLastInsertedId() { + return selectLastInsertedId; + } + + /** + * Set the SQL used to return the last inserted Id. + */ + public void setSelectLastInsertedId(String selectLastInsertedId) { + this.selectLastInsertedId = selectLastInsertedId; + } + + /** + * Return the name of the IdGenerator that should be used with this type of + * bean. A null value could be used to specify the 'default' IdGenerator. + */ + public String getIdGeneratorName() { + return idGeneratorName; + } + + /** + * Set the name of the IdGenerator that should be used with this type of bean. + */ + public void setIdGeneratorName(String idGeneratorName) { + this.idGeneratorName = idGeneratorName; + } + + /** + * Return the actual IdGenerator for this bean type (can be null). + */ + public IdGenerator getIdGenerator() { + return idGenerator; + } + + /** + * Set the actual IdGenerator for this bean type. + */ + public void setIdGenerator(IdGenerator idGenerator) { + this.idGenerator = idGenerator; + if (idGenerator != null && idGenerator.isDbSequence()) { + setSequenceName(idGenerator.getName()); + } + } + + /** + * Return the includes for getReference(). + */ + public String getLazyFetchIncludes() { + return lazyFetchIncludes; + } + + /** + * Set includes to use for lazy loading by getReference(). Note queries also + * build references and includes on the actual association are used for those + * references. + */ + public void setLazyFetchIncludes(String lazyFetchIncludes) { + if (lazyFetchIncludes != null && lazyFetchIncludes.length() > 0) { + this.lazyFetchIncludes = lazyFetchIncludes; + } + } + + /** + * Summary description. + */ + public String toString() { + return getFullName(); + } + + /** + * Add a TableJoin to this type of bean. For Secondary table properties. + */ + public void addTableJoin(DeployTableJoin join) { + tableJoinList.add(join); + } + + public List getTableJoins() { + return tableJoinList; + } + + /** + * Return an Iterator of all BeanProperty. + */ + public Iterator propertiesAll() { + return propMap.values().iterator(); + } + + /** + * Return the defaultSelectClause using FetchType.LAZY and FetchType.EAGER. + */ + public String getDefaultSelectClause() { + + StringBuilder sb = new StringBuilder(); + + boolean hasLazyFetch = false; + + Iterator it = propMap.values().iterator(); + while (it.hasNext()) { + DeployBeanProperty prop = it.next(); + if (prop.isTransient()) { + // ignore transient props etc + } else if (prop instanceof DeployBeanPropertyAssocMany) { + // ignore the associated many properties + } else { + if (prop.isFetchEager()) { + sb.append(prop.getName()).append(","); + } else { + hasLazyFetch = true; + } + } + } + + if (!hasLazyFetch) { + return null; + } + String selectClause = sb.toString(); + return selectClause.substring(0, selectClause.length() - 1); + } + + /** + * Return an Array of properties to include in default fetch (for LDAP). + */ + public String[] getDefaultSelectDbArray(Set defaultSelect) { + + ArrayList list = new ArrayList(); + for (DeployBeanProperty p : propMap.values()) { + if (defaultSelect != null) { + if (defaultSelect.contains(p.getName())) { + // properties in defaultSelect + list.add(p.getDbColumn()); + } + } else if (!p.isTransient() && p.isDbRead()) { + // non transient db properties + list.add(p.getDbColumn()); + } + } + return list.toArray(new String[list.size()]); + } + + /** + * Parse the include separating by comma or semicolon. + */ + public Set parseDefaultSelectClause(String rawList) { + + if (rawList == null) { + return null; + } + + String[] res = rawList.split(","); + + LinkedHashSet set = new LinkedHashSet(res.length + 3); + + String temp = null; + for (int i = 0; i < res.length; i++) { + temp = res[i].trim(); + if (temp.length() > 0) { + set.add(temp); + } + } + return Collections.unmodifiableSet(set); + } + + /** + * Return the Primary Key column assuming it is a single column (not + * compound). This is for the purpose of defining a sequence name. + */ + public String getSinglePrimaryKeyColumn() { + List ids = propertiesId(); + if (ids.size() == 1) { + DeployBeanProperty p = ids.get(0); + if (p instanceof DeployBeanPropertyAssoc) { + // its a compound primary key + return null; + } else { + return p.getDbColumn(); + } + } + return null; + } + + /** + * Return the BeanProperty that make up the unique id. + *

+ * The order of these properties can be relied on to be consistent if the bean + * itself doesn't change or the xml deployment order does not change. + *

+ */ + public List propertiesId() { + + ArrayList list = new ArrayList(2); + + Iterator it = propMap.values().iterator(); + while (it.hasNext()) { + DeployBeanProperty prop = it.next(); + if (prop.isId()) { + list.add(prop); + } + } + + return list; + } + + public DeployBeanPropertyAssocOne findJoinToTable(String tableName) { + + List> assocOne = propertiesAssocOne(); + for (DeployBeanPropertyAssocOne prop : assocOne) { + DeployTableJoin tableJoin = prop.getTableJoin(); + if (tableJoin != null && tableJoin.getTable().equalsIgnoreCase(tableName)) { + return prop; + } + } + return null; + } + + /** + * Return an Iterator of BeanPropertyAssocOne that are not embedded. These are + * effectively joined beans. For ManyToOne and OneToOne associations. + */ + public List> propertiesAssocOne() { + + ArrayList> list = new ArrayList>(); + + Iterator it = propMap.values().iterator(); + while (it.hasNext()) { + DeployBeanProperty prop = it.next(); + if (prop instanceof DeployBeanPropertyAssocOne) { + if (!prop.isEmbedded()) { + list.add((DeployBeanPropertyAssocOne) prop); + } + } + } + + return list; + + } + + /** + * Return BeanPropertyAssocMany for this descriptor. + */ + public List> propertiesAssocMany() { + + ArrayList> list = new ArrayList>(); + + Iterator it = propMap.values().iterator(); + while (it.hasNext()) { + DeployBeanProperty prop = it.next(); + if (prop instanceof DeployBeanPropertyAssocMany) { + list.add((DeployBeanPropertyAssocMany) prop); + } + } + + return list; + } + + /** + * Returns 'Version' properties on this bean. These are 'Counter' or 'Update + * Timestamp' type properties. Note version properties can also be on embedded + * beans rather than on the bean itself. + */ + public List propertiesVersion() { + + ArrayList list = new ArrayList(); + + Iterator it = propMap.values().iterator(); + while (it.hasNext()) { + DeployBeanProperty prop = it.next(); + + if (prop instanceof DeployBeanPropertyAssoc) { + + } else { + if (!prop.isId() && prop.isVersionColumn()) { + list.add(prop); + } + } + } + + return list; + } + + /** + * base properties without the unique id properties. + */ + public List propertiesBase() { + + ArrayList list = new ArrayList(); + + Iterator it = propMap.values().iterator(); + while (it.hasNext()) { + DeployBeanProperty prop = it.next(); + + if (prop instanceof DeployBeanPropertyAssoc) { + + } else { + if (!prop.isId()) { + list.add(prop); + } + } + } + + return list; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanEmbedded.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanEmbedded.java index 49d0144b9..913f2c4f3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanEmbedded.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanEmbedded.java @@ -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.meta; - -import java.util.HashMap; -import java.util.Map; - -/** - * Collects Deployment information on Embedded beans. - *

- * Typically collects the overridden column names mapped - * to the Embedded bean. - *

- */ -public class DeployBeanEmbedded { - - /** - * A map of property names to dbColumns. - */ - Map propMap = new HashMap(); - - /** - * Set a property name to use a specific dbColumn. - */ - public void put(String propertyName, String dbCoumn){ - propMap.put(propertyName, dbCoumn); - } - - /** - * Set a Map of property names to dbColumns. - */ - public void putAll(Map propertyColumnMap){ - propMap.putAll(propertyColumnMap); - } - - /** - * Return a map of property names to dbColumns. - */ - public Map getPropertyColumnMap() { - return propMap; - } - - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import java.util.HashMap; +import java.util.Map; + +/** + * Collects Deployment information on Embedded beans. + *

+ * Typically collects the overridden column names mapped + * to the Embedded bean. + *

+ */ +public class DeployBeanEmbedded { + + /** + * A map of property names to dbColumns. + */ + Map propMap = new HashMap(); + + /** + * Set a property name to use a specific dbColumn. + */ + public void put(String propertyName, String dbCoumn){ + propMap.put(propertyName, dbCoumn); + } + + /** + * Set a Map of property names to dbColumns. + */ + public void putAll(Map propertyColumnMap){ + propMap.putAll(propertyColumnMap); + } + + /** + * Return a map of property names to dbColumns. + */ + public Map getPropertyColumnMap() { + return propMap; + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java index 103bbd709..14208890e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanProperty.java @@ -1,965 +1,946 @@ -/** - * 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.meta; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.sql.Types; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import javax.persistence.EmbeddedId; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.Version; - -import com.avaje.ebean.annotation.CreatedTimestamp; -import com.avaje.ebean.annotation.UpdatedTimestamp; -import com.avaje.ebean.config.ScalarTypeConverter; -import com.avaje.ebean.config.dbplatform.DbEncrypt; -import com.avaje.ebean.config.dbplatform.DbEncryptFunction; -import com.avaje.ebean.config.ldap.LdapAttributeAdapter; -import com.avaje.ebean.validation.factory.Validator; -import com.avaje.ebeaninternal.server.core.InternString; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; -import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter; -import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter; -import com.avaje.ebeaninternal.server.type.ScalarType; -import com.avaje.ebeaninternal.server.type.ScalarTypeEnum; -import com.avaje.ebeaninternal.server.type.ScalarTypeWrapper; - -/** - * Description of a property of a bean. Includes its deployment information such - * as database column mapping information. - */ -public class DeployBeanProperty { - - private static final int ID_ORDER = 1000000; - private static final int UNIDIRECTIONAL_ORDER = 100000; - private static final int AUDITCOLUMN_ORDER = -1000000; - private static final int VERSIONCOLUMN_ORDER = -1000000; - - /** - * Advanced bean deployment. To exclude this property from update where - * clause. - */ - public static final String EXCLUDE_FROM_UPDATE_WHERE = "EXCLUDE_FROM_UPDATE_WHERE"; - - /** - * Advanced bean deployment. To exclude this property from delete where - * clause. - */ - public static final String EXCLUDE_FROM_DELETE_WHERE = "EXCLUDE_FROM_DELETE_WHERE"; - - /** - * Advanced bean deployment. To exclude this property from insert. - */ - public static final String EXCLUDE_FROM_INSERT = "EXCLUDE_FROM_INSERT"; - - /** - * Advanced bean deployment. To exclude this property from update set - * clause. - */ - public static final String EXCLUDE_FROM_UPDATE = "EXCLUDE_FROM_UPDATE"; - - /** - * Flag to mark this at part of the unique id. - */ - private boolean id; - - /** - * Flag to mark the property as embedded. This could be on - * BeanPropertyAssocOne rather than here. Put it here for checking Id type - * (embedded or not). - */ - private boolean embedded; - - /** - * Flag indicating if this the version property. - */ - private boolean versionColumn; - - private boolean fetchEager = true; - - /** - * Set if this property is nullable. - */ - private boolean nullable = true; - - private boolean unique; - - private LdapAttributeAdapter ldapAttributeAdapter; - - /** - * The length or precision of the DB column. - */ - private int dbLength; - - private int dbScale; - - private String dbColumnDefn; - - private boolean isTransient; - - private boolean localEncrypted; - - private boolean dbEncrypted; - private DbEncryptFunction dbEncryptFunction; - - private int dbEncryptedType; - - private String dbBind = "?"; - - /** - * Is this property include in database resultSet. - */ - private boolean dbRead; - - /** - * Include this in DB insert. - */ - private boolean dbInsertable; - - /** - * Include this in a DB update. - */ - private boolean dbUpdateable; - - private DeployTableJoin secondaryTableJoin; - - private String secondaryTableJoinPrefix; - - /** - * Set to true if this property is based on a secondary table. - */ - private String secondaryTable; - - /** - * The type that owns this property. - */ - private Class owningType; - - /** - * True if the property is a Clob, Blob LongVarchar or LongVarbinary. - */ - private boolean lob; - - private boolean naturalKey; - - /** - * The logical bean property name. - */ - private String name; - - /** - * The reflected field. - */ - private Field field; - - /** - * The bean type. - */ - private Class propertyType; - - /** - * Set for Non-JDBC types to provide logical to db type conversion. - */ - private ScalarType scalarType; - - /** - * The database column. This can include quoted identifiers. - */ - private String dbColumn; - - private String sqlFormulaSelect; - private String sqlFormulaJoin; - - /** - * The jdbc data type this maps to. - */ - private int dbType; - - /** - * The default value to insert if null. - */ - private Object defaultValue; - - /** - * Extra deployment parameters. - */ - private HashMap extraAttributeMap = new HashMap(); - - /** - * The method used to read the property. - */ - private Method readMethod; - - /** - * The method used to write the property. - */ - private Method writeMethod; - - private BeanReflectGetter getter; - - private BeanReflectSetter setter; - - /** - * Generator for insert or update timestamp etc. - */ - private GeneratedProperty generatedProperty; - - private List validators = new ArrayList(); - - private final DeployBeanDescriptor desc; - - private boolean undirectionalShadow; - - private int sortOrder; - - public DeployBeanProperty(DeployBeanDescriptor desc, Class propertyType, ScalarType scalarType, ScalarTypeConverter typeConverter) { - this.desc = desc; - this.propertyType = propertyType; - this.scalarType = wrapScalarType(propertyType, scalarType, typeConverter); - } - - /** - * Wrap the ScalarType using a ScalarTypeConverter. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - private ScalarType wrapScalarType(Class propertyType, ScalarType scalarType, ScalarTypeConverter typeConverter) { - if (typeConverter == null){ - return scalarType; - } - return new ScalarTypeWrapper(propertyType, scalarType, typeConverter); - } - - public int getSortOverride() { - if (field == null) { - return 0; - } - if (field.getAnnotation(Id.class) != null) { - return ID_ORDER; - } else if (field.getAnnotation(EmbeddedId.class) != null) { - return ID_ORDER; - } else if (undirectionalShadow){ - return UNIDIRECTIONAL_ORDER; - } else if (field.getAnnotation(CreatedTimestamp.class) != null) { - return AUDITCOLUMN_ORDER; - } else if (field.getAnnotation(UpdatedTimestamp.class) != null) { - return AUDITCOLUMN_ORDER; - } else if (field.getAnnotation(Version.class) != null) { - return VERSIONCOLUMN_ORDER; - } - return 0; - } - - /** - * Return true is this is a simple scalar property. - */ - public boolean isScalar() { - return true; - } - - public String getFullBeanName() { - return desc.getFullName() + "." + name; - } - - /** - * Return true if this is a primitive type with a nullable DB column. - *

- * This should log a WARNING as primitive types can't be null. - *

- */ - public boolean isNullablePrimitive() { - if (nullable && propertyType.isPrimitive()) { - return true; - } - return false; - } - - /** - * Return the DB column length for character columns. - *

- * Note if there is no length explicitly defined then the scalarType is - * checked to see if that has one (primarily to support putting a length on - * Enum types). - *

- */ - public int getDbLength() { - if (dbLength == 0 && scalarType != null) { - return scalarType.getLength(); - } - - return dbLength; - } - - /** - * Return the sortOrder for the properties. - */ - public int getSortOrder() { - return sortOrder; - } - - /** - * Set the sortOrder for the properties. - */ - public void setSortOrder(int sortOrder) { - this.sortOrder = sortOrder; - } - - /** - * Return true if this is a placeholder property for a unidirectional relationship. - */ - public boolean isUndirectionalShadow() { - return undirectionalShadow; - } - - /** - * Mark this property as a placeholder for a unidirectional relationship. - */ - public void setUndirectionalShadow(boolean undirectionalShadow) { - this.undirectionalShadow = undirectionalShadow; - } - - /** - * Return true if the property is encrypted in java rather than in the DB. - */ - public boolean isLocalEncrypted() { - return localEncrypted; - } - - /** - * Set to true when the property is encrypted in java rather than in the DB. - */ - public void setLocalEncrypted(boolean localEncrypted) { - this.localEncrypted = localEncrypted; - } - - /** - * Set the DB column length for character columns. - */ - public void setDbLength(int dbLength) { - this.dbLength = dbLength; - } - - /** - * Return the Db scale for numeric columns. - */ - public int getDbScale() { - return dbScale; - } - - /** - * Set the Db scale for numeric columns. - */ - public void setDbScale(int dbScale) { - this.dbScale = dbScale; - } - - /** - * Return the DB column definition if defined. - */ - public String getDbColumnDefn() { - return dbColumnDefn; - } - - /** - * Set a specific DB column definition. - */ - public void setDbColumnDefn(String dbColumnDefn) { - if (dbColumnDefn == null || dbColumnDefn.trim().length() == 0) { - this.dbColumnDefn = null; - } else { - this.dbColumnDefn = InternString.intern(dbColumnDefn); - } - } - - public String getDbConstraintExpression() { - if (scalarType instanceof ScalarTypeEnum) { - // create a check constraint for the enum - ScalarTypeEnum etype = (ScalarTypeEnum) scalarType; - - // check dbColName IN ('A', 'I', 'D') - return "check (" + dbColumn + " in " + etype.getContraintInValues() + ")"; - } - return null; - } - - /** - * Add a validator to this property. - */ - public void addValidator(Validator validator) { - validators.add(validator); - } - - /** - * Return true if the property contains a validator of a given type. - *

- * Used to detect if a validator has already been assigned when trying to - * automatically add validators such as Length and NotNull. - *

- */ - public boolean containsValidatorType(Class type) { - - Iterator it = validators.iterator(); - while (it.hasNext()) { - Validator validator = (Validator) it.next(); - if (validator.getClass().equals(type)) { - return true; - } - } - return false; - } - - /** - * Return the validators for this property. - */ - public Validator[] getValidators() { - return validators.toArray(new Validator[validators.size()]); - } - - /** - * Return the scalarType. This returns null for native JDBC types, otherwise - * it is used to convert between logical types and jdbc types. - */ - public ScalarType getScalarType() { - return scalarType; - } - - public void setScalarType(ScalarType scalarType) { - this.scalarType = scalarType; - } - - public BeanReflectGetter getGetter() { - return getter; - } - - public BeanReflectSetter getSetter() { - return setter; - } - - /** - * Return the getter method. - */ - public Method getReadMethod() { - return readMethod; - } - - /** - * Return the setter method. - */ - public Method getWriteMethod() { - return writeMethod; - } - - /** - * Set to the owning type form a Inheritance heirarchy. - */ - public void setOwningType(Class owningType) { - this.owningType = owningType; - } - - public Class getOwningType() { - return owningType; - } - - /** - * Return true if this is local to this type - aka not from a super type. - */ - public boolean isLocal() { - return owningType == null || owningType.equals(desc.getBeanType()); - } - - /** - * Set the getter used to read the property value from a bean. - */ - public void setGetter(BeanReflectGetter getter) { - this.getter = getter; - } - - /** - * Set the setter used to set the property value to a bean. - */ - public void setSetter(BeanReflectSetter setter) { - this.setter = setter; - } - - /** - * Return the name of the property. - */ - public String getName() { - return name; - } - - /** - * Set the name of the property. - */ - public void setName(String name) { - this.name = InternString.intern(name); - } - - /** - * Return the bean Field associated with this property. - */ - public Field getField() { - return field; - } - - /** - * Set the bean Field associated with this property. - */ - public void setField(Field field) { - this.field = field; - } - - public boolean isNaturalKey() { - return naturalKey; - } - - public void setNaturalKey(boolean naturalKey) { - this.naturalKey = naturalKey; - } - - /** - * Return true if this is a generated property like update timestamp and - * create timestamp. - */ - public boolean isGenerated() { - return generatedProperty != null; - } - - /** - * Return the GeneratedValue. Used to generate update timestamp etc. - */ - public GeneratedProperty getGeneratedProperty() { - return generatedProperty; - } - - /** - * Set the GeneratedValue. Used to generate update timestamp etc. - */ - public void setGeneratedProperty(GeneratedProperty generatedValue) { - this.generatedProperty = generatedValue; - } - - /** - * Return true if this property is mandatory. - */ - public boolean isNullable() { - return nullable; - } - - /** - * Set the not nullable of this property. - */ - public void setNullable(boolean isNullable) { - this.nullable = isNullable; - } - - /** - * Return true if the DB column is unique. - */ - public boolean isUnique() { - return unique; - } - - /** - * Set to true if the DB column is unique. - */ - public void setUnique(boolean unique) { - this.unique = unique; - } - - /** - * Return the LdapAttributeAdapter. - */ - public LdapAttributeAdapter getLdapAttributeAdapter() { - return ldapAttributeAdapter; - } - - /** - * Set the LdapAttributeAdapter. - */ - public void setLdapAttributeAdapter(LdapAttributeAdapter ldapAttributeAdapter) { - this.ldapAttributeAdapter = ldapAttributeAdapter; - } - - /** - * Return true if this is a version column used for concurrency checking. - */ - public boolean isVersionColumn() { - return versionColumn; - } - - /** - * Set if this is a version column used for concurrency checking. - */ - public void setVersionColumn(boolean isVersionColumn) { - this.versionColumn = isVersionColumn; - } - - /** - * Return true if this should be eager fetched by default. - */ - public boolean isFetchEager() { - return fetchEager; - } - - /** - * Set the default fetch type for this property. - */ - public void setFetchType(FetchType fetchType) { - this.fetchEager = FetchType.EAGER.equals(fetchType); - } - - /** - * Return the formula this property is based on. - */ - public String getSqlFormulaSelect() { - return sqlFormulaSelect; - } - - public String getSqlFormulaJoin() { - return sqlFormulaJoin; - } - - /** - * The property is based on a formula. - */ - public void setSqlFormula(String formulaSelect, String formulaJoin) { - this.sqlFormulaSelect = formulaSelect; - this.sqlFormulaJoin = formulaJoin.equals("") ? null : formulaJoin; - this.dbRead = true; - this.dbInsertable = false; - this.dbUpdateable = false; - } - - public String getElPlaceHolder(EntityType et) { - if (sqlFormulaSelect != null) { - return sqlFormulaSelect; - } else if (EntityType.LDAP.equals(et)){ - return getDbColumn(); - } else { - if (secondaryTableJoinPrefix != null){ - return "${"+secondaryTableJoinPrefix+"}"+getDbColumn(); - } - // prepend table alias placeholder - return ElPropertyValue.ROOT_ELPREFIX + getDbColumn(); - } - } - - /** - * The database column name this is mapped to. - */ - public String getDbColumn() { - if (sqlFormulaSelect != null) { - return sqlFormulaSelect; - } - return dbColumn; - } - - /** - * Set the database column name this is mapped to. - */ - public void setDbColumn(String dbColumn) { - this.dbColumn = InternString.intern(dbColumn); - } - - /** - * Return the database jdbc data type this is mapped to. - */ - public int getDbType() { - return dbType; - } - - /** - * Set the database jdbc data type this is mapped to. - */ - public void setDbType(int dbType) { - this.dbType = dbType; - this.lob = isLobType(dbType); - } - - /** - * Return true if this is mapped to a Clob Blob LongVarchar or - * LongVarbinary. - */ - public boolean isLob() { - return lob; - } - - private boolean isLobType(int type) { - switch (type) { - case Types.CLOB: - return true; - case Types.BLOB: - return true; - case Types.LONGVARBINARY: - return true; - case Types.LONGVARCHAR: - return true; - - default: - return false; - } - } - - /** - * Return true if this property is based on a secondary table. - */ - public boolean isSecondaryTable() { - return secondaryTable != null; - } - - /** - * Return the secondary table this property is associated with. - */ - public String getSecondaryTable() { - return secondaryTable; - } - - /** - * Set to true if this property is included in persisting. - */ - public void setSecondaryTable(String secondaryTable) { - this.secondaryTable = secondaryTable; - this.dbInsertable = false; - this.dbUpdateable = false; - } - - /** - * - */ - public String getSecondaryTableJoinPrefix() { - return secondaryTableJoinPrefix; - } - - public DeployTableJoin getSecondaryTableJoin() { - return secondaryTableJoin; - } - - public void setSecondaryTableJoin(DeployTableJoin secondaryTableJoin, String prefix) { - this.secondaryTableJoin = secondaryTableJoin; - this.secondaryTableJoinPrefix = prefix; - } - - /** - * Return the DB Bind parameter. Typically is "?" but can be different for - * encrypted bind. - */ - public String getDbBind() { - return dbBind; - } - - /** - * Set the DB bind parameter (if different from "?"). - */ - public void setDbBind(String dbBind) { - this.dbBind = dbBind; - } - - /** - * Return true if this property is encrypted in the DB. - */ - public boolean isDbEncrypted() { - return dbEncrypted; - } - -// /** -// * Set true if this property should be encrypted in the DB. -// */ -// public void setDbEncrypted(boolean dbEncrypted) { -// this.dbEncrypted = dbEncrypted; -// } - - public DbEncryptFunction getDbEncryptFunction() { - return dbEncryptFunction; - } - - public void setDbEncryptFunction(DbEncryptFunction dbEncryptFunction, DbEncrypt dbEncrypt, int dbLen) { - this.dbEncryptFunction = dbEncryptFunction; - this.dbEncrypted = true; - this.dbBind = dbEncryptFunction.getEncryptBindSql(); - - this.dbEncryptedType = isLob() ? Types.BLOB : dbEncrypt.getEncryptDbType(); - if (dbLen > 0){ - setDbLength(dbLen); - } - } - - /** - * Return the DB type for the encrypted property. This can differ from the - * logical type (String encrypted and stored in a VARBINARY) - */ - public int getDbEncryptedType() { - return dbEncryptedType; - } - - /** - * Set the DB type used to store the encrypted value. - */ - public void setDbEncryptedType(int dbEncryptedType) { - this.dbEncryptedType = dbEncryptedType; - } - - /** - * Return true if this property is included in database queries. - */ - public boolean isDbRead() { - return dbRead; - } - - /** - * Set to true if this property is included in database queries. - */ - public void setDbRead(boolean isDBRead) { - this.dbRead = isDBRead; - } - - public boolean isDbInsertable() { - return dbInsertable; - } - - public void setDbInsertable(boolean insertable) { - this.dbInsertable = insertable; - } - - public boolean isDbUpdateable() { - return dbUpdateable; - } - - public void setDbUpdateable(boolean updateable) { - this.dbUpdateable = updateable; - } - - /** - * Return true if the property is transient. - */ - public boolean isTransient() { - return isTransient; - } - - /** - * Mark the property explicitly as a transient property. - */ - public void setTransient(boolean isTransient) { - this.isTransient = isTransient; - } - - /** - * Set the bean read method. - *

- * NB: That a BeanReflectGetter is used to actually perform the getting of - * property values from a bean. This is due to performance considerations. - *

- */ - public void setReadMethod(Method readMethod) { - this.readMethod = readMethod; - } - - /** - * Set the bean write method. - *

- * NB: That a BeanReflectSetter is used to actually perform the setting of - * property values to a bean. This is due to performance considerations. - *

- */ - public void setWriteMethod(Method writeMethod) { - this.writeMethod = writeMethod; - } - - /** - * Return the property type. - */ - public Class getPropertyType() { - return propertyType; - } - - /** - * Return true if this is included in the unique id. - */ - public boolean isId() { - return id; - } - - /** - * Set to true if this is included in the unique id. - */ - public void setId(boolean id) { - this.id = id; - } - - /** - * Return true if this is an Embedded property. In this case it shares the - * table and pk of its owner object. - */ - public boolean isEmbedded() { - return embedded; - } - - /** - * Set to true if this is an embedded property. - */ - public void setEmbedded(boolean embedded) { - this.embedded = embedded; - } - - public Map getExtraAttributeMap() { - return extraAttributeMap; - } - - /** - * Return an extra attribute set on this property. - */ - public String getExtraAttribute(String key) { - return (String) extraAttributeMap.get(key); - } - - /** - * Set an extra attribute set on this property. - */ - public void setExtraAttribute(String key, String value) { - extraAttributeMap.put(key, value); - } - - /** - * Return the default value. - */ - public Object getDefaultValue() { - return defaultValue; - } - - /** - * Set the default value. Inserted if the value is null. - */ - public void setDefaultValue(Object defaultValue) { - this.defaultValue = defaultValue; - } - - public String toString() { - return desc.getFullName() + "." + name; - } - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.sql.Types; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.persistence.EmbeddedId; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.Version; + +import com.avaje.ebean.annotation.CreatedTimestamp; +import com.avaje.ebean.annotation.UpdatedTimestamp; +import com.avaje.ebean.config.ScalarTypeConverter; +import com.avaje.ebean.config.dbplatform.DbEncrypt; +import com.avaje.ebean.config.dbplatform.DbEncryptFunction; +import com.avaje.ebean.config.ldap.LdapAttributeAdapter; +import com.avaje.ebean.validation.factory.Validator; +import com.avaje.ebeaninternal.server.core.InternString; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; +import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter; +import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter; +import com.avaje.ebeaninternal.server.type.ScalarType; +import com.avaje.ebeaninternal.server.type.ScalarTypeEnum; +import com.avaje.ebeaninternal.server.type.ScalarTypeWrapper; + +/** + * Description of a property of a bean. Includes its deployment information such + * as database column mapping information. + */ +public class DeployBeanProperty { + + private static final int ID_ORDER = 1000000; + private static final int UNIDIRECTIONAL_ORDER = 100000; + private static final int AUDITCOLUMN_ORDER = -1000000; + private static final int VERSIONCOLUMN_ORDER = -1000000; + + /** + * Advanced bean deployment. To exclude this property from update where + * clause. + */ + public static final String EXCLUDE_FROM_UPDATE_WHERE = "EXCLUDE_FROM_UPDATE_WHERE"; + + /** + * Advanced bean deployment. To exclude this property from delete where + * clause. + */ + public static final String EXCLUDE_FROM_DELETE_WHERE = "EXCLUDE_FROM_DELETE_WHERE"; + + /** + * Advanced bean deployment. To exclude this property from insert. + */ + public static final String EXCLUDE_FROM_INSERT = "EXCLUDE_FROM_INSERT"; + + /** + * Advanced bean deployment. To exclude this property from update set + * clause. + */ + public static final String EXCLUDE_FROM_UPDATE = "EXCLUDE_FROM_UPDATE"; + + /** + * Flag to mark this at part of the unique id. + */ + private boolean id; + + /** + * Flag to mark the property as embedded. This could be on + * BeanPropertyAssocOne rather than here. Put it here for checking Id type + * (embedded or not). + */ + private boolean embedded; + + /** + * Flag indicating if this the version property. + */ + private boolean versionColumn; + + private boolean fetchEager = true; + + /** + * Set if this property is nullable. + */ + private boolean nullable = true; + + private boolean unique; + + private LdapAttributeAdapter ldapAttributeAdapter; + + /** + * The length or precision of the DB column. + */ + private int dbLength; + + private int dbScale; + + private String dbColumnDefn; + + private boolean isTransient; + + private boolean localEncrypted; + + private boolean dbEncrypted; + private DbEncryptFunction dbEncryptFunction; + + private int dbEncryptedType; + + private String dbBind = "?"; + + /** + * Is this property include in database resultSet. + */ + private boolean dbRead; + + /** + * Include this in DB insert. + */ + private boolean dbInsertable; + + /** + * Include this in a DB update. + */ + private boolean dbUpdateable; + + private DeployTableJoin secondaryTableJoin; + + private String secondaryTableJoinPrefix; + + /** + * Set to true if this property is based on a secondary table. + */ + private String secondaryTable; + + /** + * The type that owns this property. + */ + private Class owningType; + + /** + * True if the property is a Clob, Blob LongVarchar or LongVarbinary. + */ + private boolean lob; + + private boolean naturalKey; + + /** + * The logical bean property name. + */ + private String name; + + /** + * The reflected field. + */ + private Field field; + + /** + * The bean type. + */ + private Class propertyType; + + /** + * Set for Non-JDBC types to provide logical to db type conversion. + */ + private ScalarType scalarType; + + /** + * The database column. This can include quoted identifiers. + */ + private String dbColumn; + + private String sqlFormulaSelect; + private String sqlFormulaJoin; + + /** + * The jdbc data type this maps to. + */ + private int dbType; + + /** + * The default value to insert if null. + */ + private Object defaultValue; + + /** + * Extra deployment parameters. + */ + private HashMap extraAttributeMap = new HashMap(); + + /** + * The method used to read the property. + */ + private Method readMethod; + + /** + * The method used to write the property. + */ + private Method writeMethod; + + private BeanReflectGetter getter; + + private BeanReflectSetter setter; + + /** + * Generator for insert or update timestamp etc. + */ + private GeneratedProperty generatedProperty; + + private List validators = new ArrayList(); + + private final DeployBeanDescriptor desc; + + private boolean undirectionalShadow; + + private int sortOrder; + + public DeployBeanProperty(DeployBeanDescriptor desc, Class propertyType, ScalarType scalarType, ScalarTypeConverter typeConverter) { + this.desc = desc; + this.propertyType = propertyType; + this.scalarType = wrapScalarType(propertyType, scalarType, typeConverter); + } + + /** + * Wrap the ScalarType using a ScalarTypeConverter. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + private ScalarType wrapScalarType(Class propertyType, ScalarType scalarType, ScalarTypeConverter typeConverter) { + if (typeConverter == null){ + return scalarType; + } + return new ScalarTypeWrapper(propertyType, scalarType, typeConverter); + } + + public int getSortOverride() { + if (field == null) { + return 0; + } + if (field.getAnnotation(Id.class) != null) { + return ID_ORDER; + } else if (field.getAnnotation(EmbeddedId.class) != null) { + return ID_ORDER; + } else if (undirectionalShadow){ + return UNIDIRECTIONAL_ORDER; + } else if (field.getAnnotation(CreatedTimestamp.class) != null) { + return AUDITCOLUMN_ORDER; + } else if (field.getAnnotation(UpdatedTimestamp.class) != null) { + return AUDITCOLUMN_ORDER; + } else if (field.getAnnotation(Version.class) != null) { + return VERSIONCOLUMN_ORDER; + } + return 0; + } + + /** + * Return true is this is a simple scalar property. + */ + public boolean isScalar() { + return true; + } + + public String getFullBeanName() { + return desc.getFullName() + "." + name; + } + + /** + * Return true if this is a primitive type with a nullable DB column. + *

+ * This should log a WARNING as primitive types can't be null. + *

+ */ + public boolean isNullablePrimitive() { + if (nullable && propertyType.isPrimitive()) { + return true; + } + return false; + } + + /** + * Return the DB column length for character columns. + *

+ * Note if there is no length explicitly defined then the scalarType is + * checked to see if that has one (primarily to support putting a length on + * Enum types). + *

+ */ + public int getDbLength() { + if (dbLength == 0 && scalarType != null) { + return scalarType.getLength(); + } + + return dbLength; + } + + /** + * Return the sortOrder for the properties. + */ + public int getSortOrder() { + return sortOrder; + } + + /** + * Set the sortOrder for the properties. + */ + public void setSortOrder(int sortOrder) { + this.sortOrder = sortOrder; + } + + /** + * Return true if this is a placeholder property for a unidirectional relationship. + */ + public boolean isUndirectionalShadow() { + return undirectionalShadow; + } + + /** + * Mark this property as a placeholder for a unidirectional relationship. + */ + public void setUndirectionalShadow(boolean undirectionalShadow) { + this.undirectionalShadow = undirectionalShadow; + } + + /** + * Return true if the property is encrypted in java rather than in the DB. + */ + public boolean isLocalEncrypted() { + return localEncrypted; + } + + /** + * Set to true when the property is encrypted in java rather than in the DB. + */ + public void setLocalEncrypted(boolean localEncrypted) { + this.localEncrypted = localEncrypted; + } + + /** + * Set the DB column length for character columns. + */ + public void setDbLength(int dbLength) { + this.dbLength = dbLength; + } + + /** + * Return the Db scale for numeric columns. + */ + public int getDbScale() { + return dbScale; + } + + /** + * Set the Db scale for numeric columns. + */ + public void setDbScale(int dbScale) { + this.dbScale = dbScale; + } + + /** + * Return the DB column definition if defined. + */ + public String getDbColumnDefn() { + return dbColumnDefn; + } + + /** + * Set a specific DB column definition. + */ + public void setDbColumnDefn(String dbColumnDefn) { + if (dbColumnDefn == null || dbColumnDefn.trim().length() == 0) { + this.dbColumnDefn = null; + } else { + this.dbColumnDefn = InternString.intern(dbColumnDefn); + } + } + + public String getDbConstraintExpression() { + if (scalarType instanceof ScalarTypeEnum) { + // create a check constraint for the enum + ScalarTypeEnum etype = (ScalarTypeEnum) scalarType; + + // check dbColName IN ('A', 'I', 'D') + return "check (" + dbColumn + " in " + etype.getContraintInValues() + ")"; + } + return null; + } + + /** + * Add a validator to this property. + */ + public void addValidator(Validator validator) { + validators.add(validator); + } + + /** + * Return true if the property contains a validator of a given type. + *

+ * Used to detect if a validator has already been assigned when trying to + * automatically add validators such as Length and NotNull. + *

+ */ + public boolean containsValidatorType(Class type) { + + Iterator it = validators.iterator(); + while (it.hasNext()) { + Validator validator = (Validator) it.next(); + if (validator.getClass().equals(type)) { + return true; + } + } + return false; + } + + /** + * Return the validators for this property. + */ + public Validator[] getValidators() { + return validators.toArray(new Validator[validators.size()]); + } + + /** + * Return the scalarType. This returns null for native JDBC types, otherwise + * it is used to convert between logical types and jdbc types. + */ + public ScalarType getScalarType() { + return scalarType; + } + + public void setScalarType(ScalarType scalarType) { + this.scalarType = scalarType; + } + + public BeanReflectGetter getGetter() { + return getter; + } + + public BeanReflectSetter getSetter() { + return setter; + } + + /** + * Return the getter method. + */ + public Method getReadMethod() { + return readMethod; + } + + /** + * Return the setter method. + */ + public Method getWriteMethod() { + return writeMethod; + } + + /** + * Set to the owning type form a Inheritance heirarchy. + */ + public void setOwningType(Class owningType) { + this.owningType = owningType; + } + + public Class getOwningType() { + return owningType; + } + + /** + * Return true if this is local to this type - aka not from a super type. + */ + public boolean isLocal() { + return owningType == null || owningType.equals(desc.getBeanType()); + } + + /** + * Set the getter used to read the property value from a bean. + */ + public void setGetter(BeanReflectGetter getter) { + this.getter = getter; + } + + /** + * Set the setter used to set the property value to a bean. + */ + public void setSetter(BeanReflectSetter setter) { + this.setter = setter; + } + + /** + * Return the name of the property. + */ + public String getName() { + return name; + } + + /** + * Set the name of the property. + */ + public void setName(String name) { + this.name = InternString.intern(name); + } + + /** + * Return the bean Field associated with this property. + */ + public Field getField() { + return field; + } + + /** + * Set the bean Field associated with this property. + */ + public void setField(Field field) { + this.field = field; + } + + public boolean isNaturalKey() { + return naturalKey; + } + + public void setNaturalKey(boolean naturalKey) { + this.naturalKey = naturalKey; + } + + /** + * Return true if this is a generated property like update timestamp and + * create timestamp. + */ + public boolean isGenerated() { + return generatedProperty != null; + } + + /** + * Return the GeneratedValue. Used to generate update timestamp etc. + */ + public GeneratedProperty getGeneratedProperty() { + return generatedProperty; + } + + /** + * Set the GeneratedValue. Used to generate update timestamp etc. + */ + public void setGeneratedProperty(GeneratedProperty generatedValue) { + this.generatedProperty = generatedValue; + } + + /** + * Return true if this property is mandatory. + */ + public boolean isNullable() { + return nullable; + } + + /** + * Set the not nullable of this property. + */ + public void setNullable(boolean isNullable) { + this.nullable = isNullable; + } + + /** + * Return true if the DB column is unique. + */ + public boolean isUnique() { + return unique; + } + + /** + * Set to true if the DB column is unique. + */ + public void setUnique(boolean unique) { + this.unique = unique; + } + + /** + * Return the LdapAttributeAdapter. + */ + public LdapAttributeAdapter getLdapAttributeAdapter() { + return ldapAttributeAdapter; + } + + /** + * Set the LdapAttributeAdapter. + */ + public void setLdapAttributeAdapter(LdapAttributeAdapter ldapAttributeAdapter) { + this.ldapAttributeAdapter = ldapAttributeAdapter; + } + + /** + * Return true if this is a version column used for concurrency checking. + */ + public boolean isVersionColumn() { + return versionColumn; + } + + /** + * Set if this is a version column used for concurrency checking. + */ + public void setVersionColumn(boolean isVersionColumn) { + this.versionColumn = isVersionColumn; + } + + /** + * Return true if this should be eager fetched by default. + */ + public boolean isFetchEager() { + return fetchEager; + } + + /** + * Set the default fetch type for this property. + */ + public void setFetchType(FetchType fetchType) { + this.fetchEager = FetchType.EAGER.equals(fetchType); + } + + /** + * Return the formula this property is based on. + */ + public String getSqlFormulaSelect() { + return sqlFormulaSelect; + } + + public String getSqlFormulaJoin() { + return sqlFormulaJoin; + } + + /** + * The property is based on a formula. + */ + public void setSqlFormula(String formulaSelect, String formulaJoin) { + this.sqlFormulaSelect = formulaSelect; + this.sqlFormulaJoin = formulaJoin.equals("") ? null : formulaJoin; + this.dbRead = true; + this.dbInsertable = false; + this.dbUpdateable = false; + } + + public String getElPlaceHolder(EntityType et) { + if (sqlFormulaSelect != null) { + return sqlFormulaSelect; + } else if (EntityType.LDAP.equals(et)){ + return getDbColumn(); + } else { + if (secondaryTableJoinPrefix != null){ + return "${"+secondaryTableJoinPrefix+"}"+getDbColumn(); + } + // prepend table alias placeholder + return ElPropertyValue.ROOT_ELPREFIX + getDbColumn(); + } + } + + /** + * The database column name this is mapped to. + */ + public String getDbColumn() { + if (sqlFormulaSelect != null) { + return sqlFormulaSelect; + } + return dbColumn; + } + + /** + * Set the database column name this is mapped to. + */ + public void setDbColumn(String dbColumn) { + this.dbColumn = InternString.intern(dbColumn); + } + + /** + * Return the database jdbc data type this is mapped to. + */ + public int getDbType() { + return dbType; + } + + /** + * Set the database jdbc data type this is mapped to. + */ + public void setDbType(int dbType) { + this.dbType = dbType; + this.lob = isLobType(dbType); + } + + /** + * Return true if this is mapped to a Clob Blob LongVarchar or + * LongVarbinary. + */ + public boolean isLob() { + return lob; + } + + private boolean isLobType(int type) { + switch (type) { + case Types.CLOB: + return true; + case Types.BLOB: + return true; + case Types.LONGVARBINARY: + return true; + case Types.LONGVARCHAR: + return true; + + default: + return false; + } + } + + /** + * Return true if this property is based on a secondary table. + */ + public boolean isSecondaryTable() { + return secondaryTable != null; + } + + /** + * Return the secondary table this property is associated with. + */ + public String getSecondaryTable() { + return secondaryTable; + } + + /** + * Set to true if this property is included in persisting. + */ + public void setSecondaryTable(String secondaryTable) { + this.secondaryTable = secondaryTable; + this.dbInsertable = false; + this.dbUpdateable = false; + } + + /** + * + */ + public String getSecondaryTableJoinPrefix() { + return secondaryTableJoinPrefix; + } + + public DeployTableJoin getSecondaryTableJoin() { + return secondaryTableJoin; + } + + public void setSecondaryTableJoin(DeployTableJoin secondaryTableJoin, String prefix) { + this.secondaryTableJoin = secondaryTableJoin; + this.secondaryTableJoinPrefix = prefix; + } + + /** + * Return the DB Bind parameter. Typically is "?" but can be different for + * encrypted bind. + */ + public String getDbBind() { + return dbBind; + } + + /** + * Set the DB bind parameter (if different from "?"). + */ + public void setDbBind(String dbBind) { + this.dbBind = dbBind; + } + + /** + * Return true if this property is encrypted in the DB. + */ + public boolean isDbEncrypted() { + return dbEncrypted; + } + +// /** +// * Set true if this property should be encrypted in the DB. +// */ +// public void setDbEncrypted(boolean dbEncrypted) { +// this.dbEncrypted = dbEncrypted; +// } + + public DbEncryptFunction getDbEncryptFunction() { + return dbEncryptFunction; + } + + public void setDbEncryptFunction(DbEncryptFunction dbEncryptFunction, DbEncrypt dbEncrypt, int dbLen) { + this.dbEncryptFunction = dbEncryptFunction; + this.dbEncrypted = true; + this.dbBind = dbEncryptFunction.getEncryptBindSql(); + + this.dbEncryptedType = isLob() ? Types.BLOB : dbEncrypt.getEncryptDbType(); + if (dbLen > 0){ + setDbLength(dbLen); + } + } + + /** + * Return the DB type for the encrypted property. This can differ from the + * logical type (String encrypted and stored in a VARBINARY) + */ + public int getDbEncryptedType() { + return dbEncryptedType; + } + + /** + * Set the DB type used to store the encrypted value. + */ + public void setDbEncryptedType(int dbEncryptedType) { + this.dbEncryptedType = dbEncryptedType; + } + + /** + * Return true if this property is included in database queries. + */ + public boolean isDbRead() { + return dbRead; + } + + /** + * Set to true if this property is included in database queries. + */ + public void setDbRead(boolean isDBRead) { + this.dbRead = isDBRead; + } + + public boolean isDbInsertable() { + return dbInsertable; + } + + public void setDbInsertable(boolean insertable) { + this.dbInsertable = insertable; + } + + public boolean isDbUpdateable() { + return dbUpdateable; + } + + public void setDbUpdateable(boolean updateable) { + this.dbUpdateable = updateable; + } + + /** + * Return true if the property is transient. + */ + public boolean isTransient() { + return isTransient; + } + + /** + * Mark the property explicitly as a transient property. + */ + public void setTransient(boolean isTransient) { + this.isTransient = isTransient; + } + + /** + * Set the bean read method. + *

+ * NB: That a BeanReflectGetter is used to actually perform the getting of + * property values from a bean. This is due to performance considerations. + *

+ */ + public void setReadMethod(Method readMethod) { + this.readMethod = readMethod; + } + + /** + * Set the bean write method. + *

+ * NB: That a BeanReflectSetter is used to actually perform the setting of + * property values to a bean. This is due to performance considerations. + *

+ */ + public void setWriteMethod(Method writeMethod) { + this.writeMethod = writeMethod; + } + + /** + * Return the property type. + */ + public Class getPropertyType() { + return propertyType; + } + + /** + * Return true if this is included in the unique id. + */ + public boolean isId() { + return id; + } + + /** + * Set to true if this is included in the unique id. + */ + public void setId(boolean id) { + this.id = id; + } + + /** + * Return true if this is an Embedded property. In this case it shares the + * table and pk of its owner object. + */ + public boolean isEmbedded() { + return embedded; + } + + /** + * Set to true if this is an embedded property. + */ + public void setEmbedded(boolean embedded) { + this.embedded = embedded; + } + + public Map getExtraAttributeMap() { + return extraAttributeMap; + } + + /** + * Return an extra attribute set on this property. + */ + public String getExtraAttribute(String key) { + return (String) extraAttributeMap.get(key); + } + + /** + * Set an extra attribute set on this property. + */ + public void setExtraAttribute(String key, String value) { + extraAttributeMap.put(key, value); + } + + /** + * Return the default value. + */ + public Object getDefaultValue() { + return defaultValue; + } + + /** + * Set the default value. Inserted if the value is null. + */ + public void setDefaultValue(Object defaultValue) { + this.defaultValue = defaultValue; + } + + public String toString() { + return desc.getFullName() + "." + name; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java index 841035770..f06be9264 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssoc.java @@ -1,175 +1,156 @@ -/** - * 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.meta; - -import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo; -import com.avaje.ebeaninternal.server.deploy.BeanTable; - -/** - * Abstract base for properties mapped to an associated bean, list, set or map. - */ -public abstract class DeployBeanPropertyAssoc extends DeployBeanProperty { - - /** - * The type of the joined bean. - */ - Class targetType; - - /** - * Persist settings. - */ - BeanCascadeInfo cascadeInfo = new BeanCascadeInfo(); - - /** - * The join table information. - */ - BeanTable beanTable; - - /** - * Join between the beans. - */ - DeployTableJoin tableJoin = new DeployTableJoin(); - - /** - * Whether the associated join type should be an outer join. - */ - boolean isOuterJoin = false; - - /** - * Literal added to where clause of lazy loading query. - */ - String extraWhere; - - /** - * From the deployment mappedBy attribute. - */ - String mappedBy; - - /** - * Construct the property. - */ - public DeployBeanPropertyAssoc(DeployBeanDescriptor desc, Class targetType) { - super(desc, targetType, null, null); - this.targetType = targetType; - } - - /** - * Return false. - */ - @Override - public boolean isScalar() { - return false; - } - - /** - * Return the type of the target. - *

- * This is the class of the associated bean, or beans contained in a list, - * set or map. - *

- */ - public Class getTargetType() { - return targetType; - } - - /** - * Return if this association should use an Outer join. - */ - public boolean isOuterJoin() { - return isOuterJoin; - } - - /** - * Specify that this bean should use an outer join. - */ - public void setOuterJoin(boolean isOuterJoin) { - this.isOuterJoin = isOuterJoin; - } - - /** - * Return a literal expression that is added to the query that lazy loads - * the collection. - */ - public String getExtraWhere() { - return extraWhere; - } - - /** - * Set a literal expression to add to the query that lazy loads the - * collection. - */ - public void setExtraWhere(String extraWhere) { - this.extraWhere = extraWhere; - } - - /** - * return the join to use for the bean. - */ - public DeployTableJoin getTableJoin() { - return tableJoin; - } - - /** - * Return the BeanTable for this association. - *

- * This has the table name which is used to determine the relationship for - * this association. - *

- */ - public BeanTable getBeanTable() { - return beanTable; - } - - /** - * Set the bean table. - */ - public void setBeanTable(BeanTable beanTable) { - this.beanTable = beanTable; - getTableJoin().setTable(beanTable.getBaseTable()); - } - - /** - * Get the persist info. - */ - public BeanCascadeInfo getCascadeInfo() { - return cascadeInfo; - } - - - /** - * Return the mappedBy deployment attribute. - *

- * This is the name of the property in the 'detail' bean that maps back to - * this 'master' bean. - *

- */ - public String getMappedBy() { - return mappedBy; - } - - /** - * Set mappedBy deployment attribute. - */ - public void setMappedBy(String mappedBy) { - if (!"".equals(mappedBy)) { - this.mappedBy = mappedBy; - } - } -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo; +import com.avaje.ebeaninternal.server.deploy.BeanTable; + +/** + * Abstract base for properties mapped to an associated bean, list, set or map. + */ +public abstract class DeployBeanPropertyAssoc extends DeployBeanProperty { + + /** + * The type of the joined bean. + */ + Class targetType; + + /** + * Persist settings. + */ + BeanCascadeInfo cascadeInfo = new BeanCascadeInfo(); + + /** + * The join table information. + */ + BeanTable beanTable; + + /** + * Join between the beans. + */ + DeployTableJoin tableJoin = new DeployTableJoin(); + + /** + * Whether the associated join type should be an outer join. + */ + boolean isOuterJoin = false; + + /** + * Literal added to where clause of lazy loading query. + */ + String extraWhere; + + /** + * From the deployment mappedBy attribute. + */ + String mappedBy; + + /** + * Construct the property. + */ + public DeployBeanPropertyAssoc(DeployBeanDescriptor desc, Class targetType) { + super(desc, targetType, null, null); + this.targetType = targetType; + } + + /** + * Return false. + */ + @Override + public boolean isScalar() { + return false; + } + + /** + * Return the type of the target. + *

+ * This is the class of the associated bean, or beans contained in a list, + * set or map. + *

+ */ + public Class getTargetType() { + return targetType; + } + + /** + * Return if this association should use an Outer join. + */ + public boolean isOuterJoin() { + return isOuterJoin; + } + + /** + * Specify that this bean should use an outer join. + */ + public void setOuterJoin(boolean isOuterJoin) { + this.isOuterJoin = isOuterJoin; + } + + /** + * Return a literal expression that is added to the query that lazy loads + * the collection. + */ + public String getExtraWhere() { + return extraWhere; + } + + /** + * Set a literal expression to add to the query that lazy loads the + * collection. + */ + public void setExtraWhere(String extraWhere) { + this.extraWhere = extraWhere; + } + + /** + * return the join to use for the bean. + */ + public DeployTableJoin getTableJoin() { + return tableJoin; + } + + /** + * Return the BeanTable for this association. + *

+ * This has the table name which is used to determine the relationship for + * this association. + *

+ */ + public BeanTable getBeanTable() { + return beanTable; + } + + /** + * Set the bean table. + */ + public void setBeanTable(BeanTable beanTable) { + this.beanTable = beanTable; + getTableJoin().setTable(beanTable.getBaseTable()); + } + + /** + * Get the persist info. + */ + public BeanCascadeInfo getCascadeInfo() { + return cascadeInfo; + } + + + /** + * Return the mappedBy deployment attribute. + *

+ * This is the name of the property in the 'detail' bean that maps back to + * this 'master' bean. + *

+ */ + public String getMappedBy() { + return mappedBy; + } + + /** + * Set mappedBy deployment attribute. + */ + public void setMappedBy(String mappedBy) { + if (!"".equals(mappedBy)) { + this.mappedBy = mappedBy; + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocMany.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocMany.java index 693fa1808..711683ca6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocMany.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocMany.java @@ -1,213 +1,194 @@ -/** - * 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.meta; - -import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; -import com.avaje.ebeaninternal.server.deploy.ManyType; -import com.avaje.ebeaninternal.server.deploy.TableJoin; - -/** - * Property mapped to a List Set or Map. - */ -public class DeployBeanPropertyAssocMany extends DeployBeanPropertyAssoc { - - ModifyListenMode modifyListenMode = ModifyListenMode.NONE; - - /** - * Flag to indicate manyToMany relationship. - */ - boolean manyToMany; - - /** - * Flag to indicate this is a unidirectional relationship. - */ - boolean unidirectional; - - /** - * Join for manyToMany intersection table. - */ - DeployTableJoin intersectionJoin; - - /** - * For ManyToMany this is the Inverse join used to build reference queries. - */ - DeployTableJoin inverseJoin; - - String fetchOrderBy; - - String mapKey; - - /** - * The type of the many, set, list or map. - */ - ManyType manyType; - - /** - * Create this property. - */ - public DeployBeanPropertyAssocMany(DeployBeanDescriptor desc, Class targetType, ManyType manyType) { - super(desc, targetType); - this.manyType = manyType; - } - - /** - * When generics is not used for manyType you can specify via annotations. - *

- * Really only expect this for Scala due to a Scala compiler bug at the moment. - * Otherwise I'd probably not bother support this. - *

- */ - @SuppressWarnings("unchecked") - public void setTargetType(Class cls){ - this.targetType = (Class)cls; - } - - - /** - * Return the many type. - */ - public ManyType getManyType() { - return manyType; - } - - /** - * Return true if this is many to many. - */ - public boolean isManyToMany() { - return manyToMany; - } - - /** - * Set to true if this is a many to many. - */ - public void setManyToMany(boolean isManyToMany) { - this.manyToMany = isManyToMany; - } - - /** - * Return the mode for listening to changes to the List Set or Map. - */ - public ModifyListenMode getModifyListenMode() { - return modifyListenMode; - } - - /** - * Set the mode for listening to changes to the List Set or Map. - */ - public void setModifyListenMode(ModifyListenMode modifyListenMode) { - this.modifyListenMode = modifyListenMode; - } - - /** - * Return true if this is a unidirectional relationship. - */ - public boolean isUnidirectional() { - return unidirectional; - } - - /** - * Set to true if this is a unidirectional relationship. - */ - public void setUnidirectional(boolean unidirectional) { - this.unidirectional = unidirectional; - } - - /** - * Create the immutable version of the intersection join. - */ - public TableJoin createIntersectionTableJoin() { - if (intersectionJoin != null){ - return new TableJoin(intersectionJoin, null); - } else { - return null; - } - } - - /** - * Create the immutable version of the inverse join. - */ - public TableJoin createInverseTableJoin() { - if (inverseJoin != null){ - return new TableJoin(inverseJoin, null); - } else { - return null; - } - } - - /** - * ManyToMany only, join from local table to intersection table. - */ - public DeployTableJoin getIntersectionJoin() { - return intersectionJoin; - } - - public DeployTableJoin getInverseJoin() { - return inverseJoin; - } - - /** - * ManyToMany only, join from local table to intersection table. - */ - public void setIntersectionJoin(DeployTableJoin intersectionJoin) { - this.intersectionJoin = intersectionJoin; - } - - /** - * ManyToMany only, join from foreign table to intersection table. - */ - public void setInverseJoin(DeployTableJoin inverseJoin) { - this.inverseJoin = inverseJoin; - } - - /** - * Return the order by clause used to order the fetching of the data for - * this list, set or map. - */ - public String getFetchOrderBy() { - return fetchOrderBy; - } - - /** - * Return the default mapKey when returning a Map. - */ - public String getMapKey() { - return mapKey; - } - - /** - * Set the default mapKey to use when returning a Map. - */ - public void setMapKey(String mapKey) { - if (mapKey != null && mapKey.length() > 0) { - this.mapKey = mapKey; - } - } - - /** - * Set the order by clause used to order the fetching or the data for this - * list, set or map. - */ - public void setFetchOrderBy(String orderBy) { - if (orderBy != null && orderBy.length() > 0) { - fetchOrderBy = orderBy; - } - } - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; +import com.avaje.ebeaninternal.server.deploy.ManyType; +import com.avaje.ebeaninternal.server.deploy.TableJoin; + +/** + * Property mapped to a List Set or Map. + */ +public class DeployBeanPropertyAssocMany extends DeployBeanPropertyAssoc { + + ModifyListenMode modifyListenMode = ModifyListenMode.NONE; + + /** + * Flag to indicate manyToMany relationship. + */ + boolean manyToMany; + + /** + * Flag to indicate this is a unidirectional relationship. + */ + boolean unidirectional; + + /** + * Join for manyToMany intersection table. + */ + DeployTableJoin intersectionJoin; + + /** + * For ManyToMany this is the Inverse join used to build reference queries. + */ + DeployTableJoin inverseJoin; + + String fetchOrderBy; + + String mapKey; + + /** + * The type of the many, set, list or map. + */ + ManyType manyType; + + /** + * Create this property. + */ + public DeployBeanPropertyAssocMany(DeployBeanDescriptor desc, Class targetType, ManyType manyType) { + super(desc, targetType); + this.manyType = manyType; + } + + /** + * When generics is not used for manyType you can specify via annotations. + *

+ * Really only expect this for Scala due to a Scala compiler bug at the moment. + * Otherwise I'd probably not bother support this. + *

+ */ + @SuppressWarnings("unchecked") + public void setTargetType(Class cls){ + this.targetType = (Class)cls; + } + + + /** + * Return the many type. + */ + public ManyType getManyType() { + return manyType; + } + + /** + * Return true if this is many to many. + */ + public boolean isManyToMany() { + return manyToMany; + } + + /** + * Set to true if this is a many to many. + */ + public void setManyToMany(boolean isManyToMany) { + this.manyToMany = isManyToMany; + } + + /** + * Return the mode for listening to changes to the List Set or Map. + */ + public ModifyListenMode getModifyListenMode() { + return modifyListenMode; + } + + /** + * Set the mode for listening to changes to the List Set or Map. + */ + public void setModifyListenMode(ModifyListenMode modifyListenMode) { + this.modifyListenMode = modifyListenMode; + } + + /** + * Return true if this is a unidirectional relationship. + */ + public boolean isUnidirectional() { + return unidirectional; + } + + /** + * Set to true if this is a unidirectional relationship. + */ + public void setUnidirectional(boolean unidirectional) { + this.unidirectional = unidirectional; + } + + /** + * Create the immutable version of the intersection join. + */ + public TableJoin createIntersectionTableJoin() { + if (intersectionJoin != null){ + return new TableJoin(intersectionJoin, null); + } else { + return null; + } + } + + /** + * Create the immutable version of the inverse join. + */ + public TableJoin createInverseTableJoin() { + if (inverseJoin != null){ + return new TableJoin(inverseJoin, null); + } else { + return null; + } + } + + /** + * ManyToMany only, join from local table to intersection table. + */ + public DeployTableJoin getIntersectionJoin() { + return intersectionJoin; + } + + public DeployTableJoin getInverseJoin() { + return inverseJoin; + } + + /** + * ManyToMany only, join from local table to intersection table. + */ + public void setIntersectionJoin(DeployTableJoin intersectionJoin) { + this.intersectionJoin = intersectionJoin; + } + + /** + * ManyToMany only, join from foreign table to intersection table. + */ + public void setInverseJoin(DeployTableJoin inverseJoin) { + this.inverseJoin = inverseJoin; + } + + /** + * Return the order by clause used to order the fetching of the data for + * this list, set or map. + */ + public String getFetchOrderBy() { + return fetchOrderBy; + } + + /** + * Return the default mapKey when returning a Map. + */ + public String getMapKey() { + return mapKey; + } + + /** + * Set the default mapKey to use when returning a Map. + */ + public void setMapKey(String mapKey) { + if (mapKey != null && mapKey.length() > 0) { + this.mapKey = mapKey; + } + } + + /** + * Set the order by clause used to order the fetching or the data for this + * list, set or map. + */ + public void setFetchOrderBy(String orderBy) { + if (orderBy != null && orderBy.length() > 0) { + fetchOrderBy = orderBy; + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocOne.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocOne.java index 723a152a6..524cc7b7c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocOne.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyAssocOne.java @@ -1,114 +1,95 @@ -/** - * 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.meta; - -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; - - -/** - * Property mapped to a joined bean. - */ -public class DeployBeanPropertyAssocOne extends DeployBeanPropertyAssoc { - - boolean oneToOne; - - boolean oneToOneExported; - - boolean importedPrimaryKey; - - DeployBeanEmbedded deployEmbedded; - - /** - * Create the property. - */ - public DeployBeanPropertyAssocOne(DeployBeanDescriptor desc, Class targetType) { - super(desc, targetType); - } - - /** - * Return the deploy information specifically for the deployment - * of Embedded beans. - */ - public DeployBeanEmbedded getDeployEmbedded() { - // deployment should be single threaded - if (deployEmbedded == null){ - deployEmbedded = new DeployBeanEmbedded(); - } - return deployEmbedded; - } - - @Override - public String getDbColumn() { - DeployTableJoinColumn[] columns = tableJoin.columns(); - if (columns.length == 1){ - return columns[0].getLocalDbColumn(); - } - return super.getDbColumn(); - } - - @Override - public String getElPlaceHolder(EntityType et) { - return super.getElPlaceHolder(et); - } - - /** - * Return true if this a OneToOne property. Otherwise assumed ManyToOne. - */ - public boolean isOneToOne() { - return oneToOne; - } - - /** - * Set to true if this is a OneToOne. - */ - public void setOneToOne(boolean oneToOne) { - this.oneToOne = oneToOne; - } - - /** - * Return true if this is the exported side of a OneToOne. - */ - public boolean isOneToOneExported() { - return oneToOneExported; - } - - /** - * Set to true if this is the exported side of a OneToOne. This means - * it doesn't 'own' the foreign key column. A OneToMany without the many. - */ - public void setOneToOneExported(boolean oneToOneExported) { - this.oneToOneExported = oneToOneExported; - } - - /** - * If true this bean maps to the primary key. - */ - public boolean isImportedPrimaryKey() { - return importedPrimaryKey; - } - - /** - * Set to true if the bean maps to the primary key. - */ - public void setImportedPrimaryKey(boolean importedPrimaryKey) { - this.importedPrimaryKey = importedPrimaryKey; - } - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; + + +/** + * Property mapped to a joined bean. + */ +public class DeployBeanPropertyAssocOne extends DeployBeanPropertyAssoc { + + boolean oneToOne; + + boolean oneToOneExported; + + boolean importedPrimaryKey; + + DeployBeanEmbedded deployEmbedded; + + /** + * Create the property. + */ + public DeployBeanPropertyAssocOne(DeployBeanDescriptor desc, Class targetType) { + super(desc, targetType); + } + + /** + * Return the deploy information specifically for the deployment + * of Embedded beans. + */ + public DeployBeanEmbedded getDeployEmbedded() { + // deployment should be single threaded + if (deployEmbedded == null){ + deployEmbedded = new DeployBeanEmbedded(); + } + return deployEmbedded; + } + + @Override + public String getDbColumn() { + DeployTableJoinColumn[] columns = tableJoin.columns(); + if (columns.length == 1){ + return columns[0].getLocalDbColumn(); + } + return super.getDbColumn(); + } + + @Override + public String getElPlaceHolder(EntityType et) { + return super.getElPlaceHolder(et); + } + + /** + * Return true if this a OneToOne property. Otherwise assumed ManyToOne. + */ + public boolean isOneToOne() { + return oneToOne; + } + + /** + * Set to true if this is a OneToOne. + */ + public void setOneToOne(boolean oneToOne) { + this.oneToOne = oneToOne; + } + + /** + * Return true if this is the exported side of a OneToOne. + */ + public boolean isOneToOneExported() { + return oneToOneExported; + } + + /** + * Set to true if this is the exported side of a OneToOne. This means + * it doesn't 'own' the foreign key column. A OneToMany without the many. + */ + public void setOneToOneExported(boolean oneToOneExported) { + this.oneToOneExported = oneToOneExported; + } + + /** + * If true this bean maps to the primary key. + */ + public boolean isImportedPrimaryKey() { + return importedPrimaryKey; + } + + /** + * Set to true if the bean maps to the primary key. + */ + public void setImportedPrimaryKey(boolean importedPrimaryKey) { + this.importedPrimaryKey = importedPrimaryKey; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyCompound.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyCompound.java index 1947d0331..a14ddb2b0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyCompound.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyCompound.java @@ -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.deploy.meta; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map.Entry; - -import com.avaje.ebean.config.ScalarTypeConverter; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorMap; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompoundRoot; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompoundScalar; -import com.avaje.ebeaninternal.server.type.CtCompoundProperty; -import com.avaje.ebeaninternal.server.type.CtCompoundType; -import com.avaje.ebeaninternal.server.type.CtCompoundTypeScalarList; -import com.avaje.ebeaninternal.server.type.ScalarType; - - -/** - * Property mapped to a joined bean. - */ -public class DeployBeanPropertyCompound extends DeployBeanProperty { - - final CtCompoundType compoundType; - - final ScalarTypeConverter typeConverter; - - DeployBeanEmbedded deployEmbedded; - - /** - * Create the property. - */ - public DeployBeanPropertyCompound(DeployBeanDescriptor desc, Class targetType, - CtCompoundType compoundType, ScalarTypeConverter typeConverter) { - - super(desc, targetType, null, null); - this.compoundType = compoundType; - this.typeConverter = typeConverter; - } - - public BeanPropertyCompoundRoot getFlatProperties(BeanDescriptorMap owner, BeanDescriptor descriptor) { - - // get a 'flat' list of all the scalar types, their relative property names - // and also set their matching dbColumn - - // represents the root property - BeanPropertyCompoundRoot rootProperty = new BeanPropertyCompoundRoot(this); - - // Walk the tree of a compound type collecting the - // scalar types and non-scalar properties - CtCompoundTypeScalarList ctMeta = new CtCompoundTypeScalarList(); - - compoundType.accumulateScalarTypes(null, ctMeta); - - List beanPropertyList = new ArrayList(); - - - // for each of the scalar types inside a compound value object - // build a BeanPropertyCompoundScalar with appropriate deployment - // information. - - for (Entry> entry : ctMeta.entries()) { - - String relativePropertyName = entry.getKey(); - ScalarType scalarType = entry.getValue(); - - CtCompoundProperty ctProp = ctMeta.getCompoundType(relativePropertyName); - - - String dbColumn = relativePropertyName.replace(".", "_"); - dbColumn = getDbColumn(relativePropertyName, dbColumn); - - DeployBeanProperty deploy = new DeployBeanProperty(null, scalarType.getType(), scalarType, null); - deploy.setScalarType(scalarType); - deploy.setDbColumn(dbColumn); - deploy.setName(relativePropertyName); - deploy.setDbInsertable(true); - deploy.setDbUpdateable(true); - deploy.setDbRead(true); - - BeanPropertyCompoundScalar bp = new BeanPropertyCompoundScalar(rootProperty, deploy, ctProp, typeConverter); - beanPropertyList.add(bp); - - rootProperty.register(bp); - } - - rootProperty.setNonScalarProperties(ctMeta.getNonScalarProperties()); - return rootProperty; - } - - private String getDbColumn(String propName, String defaultDbColumn){ - if (deployEmbedded == null){ - return defaultDbColumn; - } - String dbColumn = deployEmbedded.getPropertyColumnMap().get(propName); - return dbColumn == null ? defaultDbColumn : dbColumn; - } - - /** - * Return the deploy information specifically for the deployment - * of Embedded beans. - */ - public DeployBeanEmbedded getDeployEmbedded() { - // deployment should be single threaded - if (deployEmbedded == null){ - deployEmbedded = new DeployBeanEmbedded(); - } - return deployEmbedded; - } - - public ScalarTypeConverter getTypeConverter() { - return typeConverter; - } - - public CtCompoundType getCompoundType() { - return compoundType; - } - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; + +import com.avaje.ebean.config.ScalarTypeConverter; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorMap; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompoundRoot; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompoundScalar; +import com.avaje.ebeaninternal.server.type.CtCompoundProperty; +import com.avaje.ebeaninternal.server.type.CtCompoundType; +import com.avaje.ebeaninternal.server.type.CtCompoundTypeScalarList; +import com.avaje.ebeaninternal.server.type.ScalarType; + + +/** + * Property mapped to a joined bean. + */ +public class DeployBeanPropertyCompound extends DeployBeanProperty { + + final CtCompoundType compoundType; + + final ScalarTypeConverter typeConverter; + + DeployBeanEmbedded deployEmbedded; + + /** + * Create the property. + */ + public DeployBeanPropertyCompound(DeployBeanDescriptor desc, Class targetType, + CtCompoundType compoundType, ScalarTypeConverter typeConverter) { + + super(desc, targetType, null, null); + this.compoundType = compoundType; + this.typeConverter = typeConverter; + } + + public BeanPropertyCompoundRoot getFlatProperties(BeanDescriptorMap owner, BeanDescriptor descriptor) { + + // get a 'flat' list of all the scalar types, their relative property names + // and also set their matching dbColumn + + // represents the root property + BeanPropertyCompoundRoot rootProperty = new BeanPropertyCompoundRoot(this); + + // Walk the tree of a compound type collecting the + // scalar types and non-scalar properties + CtCompoundTypeScalarList ctMeta = new CtCompoundTypeScalarList(); + + compoundType.accumulateScalarTypes(null, ctMeta); + + List beanPropertyList = new ArrayList(); + + + // for each of the scalar types inside a compound value object + // build a BeanPropertyCompoundScalar with appropriate deployment + // information. + + for (Entry> entry : ctMeta.entries()) { + + String relativePropertyName = entry.getKey(); + ScalarType scalarType = entry.getValue(); + + CtCompoundProperty ctProp = ctMeta.getCompoundType(relativePropertyName); + + + String dbColumn = relativePropertyName.replace(".", "_"); + dbColumn = getDbColumn(relativePropertyName, dbColumn); + + DeployBeanProperty deploy = new DeployBeanProperty(null, scalarType.getType(), scalarType, null); + deploy.setScalarType(scalarType); + deploy.setDbColumn(dbColumn); + deploy.setName(relativePropertyName); + deploy.setDbInsertable(true); + deploy.setDbUpdateable(true); + deploy.setDbRead(true); + + BeanPropertyCompoundScalar bp = new BeanPropertyCompoundScalar(rootProperty, deploy, ctProp, typeConverter); + beanPropertyList.add(bp); + + rootProperty.register(bp); + } + + rootProperty.setNonScalarProperties(ctMeta.getNonScalarProperties()); + return rootProperty; + } + + private String getDbColumn(String propName, String defaultDbColumn){ + if (deployEmbedded == null){ + return defaultDbColumn; + } + String dbColumn = deployEmbedded.getPropertyColumnMap().get(propName); + return dbColumn == null ? defaultDbColumn : dbColumn; + } + + /** + * Return the deploy information specifically for the deployment + * of Embedded beans. + */ + public DeployBeanEmbedded getDeployEmbedded() { + // deployment should be single threaded + if (deployEmbedded == null){ + deployEmbedded = new DeployBeanEmbedded(); + } + return deployEmbedded; + } + + public ScalarTypeConverter getTypeConverter() { + return typeConverter; + } + + public CtCompoundType getCompoundType() { + return compoundType; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java index ff62d6ec7..d039d9006 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java @@ -1,403 +1,384 @@ -/** - * 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.meta; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; - -import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; -import com.avaje.ebean.validation.factory.Validator; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorMap; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; -import com.avaje.ebeaninternal.server.deploy.BeanPropertySimpleCollection; -import com.avaje.ebeaninternal.server.deploy.TableJoin; - -/** - * Helper object to classify BeanProperties into appropriate lists. - */ -public class DeployBeanPropertyLists { - - private BeanProperty derivedFirstVersionProp; - - private final BeanDescriptor desc; - - private final LinkedHashMap propertyMap; - - private final ArrayList ids = new ArrayList(); - - private final ArrayList version = new ArrayList(); - - private final ArrayList local = new ArrayList(); - - private final ArrayList manys = new ArrayList(); - private final ArrayList nonManys = new ArrayList(); - - private final ArrayList ones = new ArrayList(); - - private final ArrayList onesExported = new ArrayList(); - - private final ArrayList onesImported = new ArrayList(); - - private final ArrayList embedded = new ArrayList(); - - private final ArrayList baseScalar = new ArrayList(); - - private final ArrayList baseCompound = new ArrayList(); - - private final ArrayList transients = new ArrayList(); - - private final ArrayList nonTransients = new ArrayList(); - - private final TableJoin[] tableJoins; - - private final BeanPropertyAssocOne unidirectional; - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public DeployBeanPropertyLists(BeanDescriptorMap owner, BeanDescriptor desc, DeployBeanDescriptor deploy) { - this.desc = desc; - - DeployBeanPropertyAssocOne deployUnidirectional = deploy.getUnidirectional(); - if (deployUnidirectional == null) { - unidirectional = null; - } else { - unidirectional = new BeanPropertyAssocOne(owner, desc, deployUnidirectional); - } - - this.propertyMap = new LinkedHashMap(); - - Iterator deployIt = deploy.propertiesAll(); - while (deployIt.hasNext()) { - DeployBeanProperty deployProp = deployIt.next(); - BeanProperty beanProp = createBeanProperty(owner, deployProp); - propertyMap.put(beanProp.getName(), beanProp); - } - - Iterator it = propertyMap.values().iterator(); - - int order = 0; - while (it.hasNext()) { - BeanProperty prop = it.next(); - prop.setDeployOrder(order++); - allocateToList(prop); - } - - List deployTableJoins = deploy.getTableJoins(); - tableJoins = new TableJoin[deployTableJoins.size()]; - for (int i = 0; i < deployTableJoins.size(); i++) { - tableJoins[i] = new TableJoin(deployTableJoins.get(i), propertyMap); - } - - } - - /** - * Return the unidirectional. - */ - public BeanPropertyAssocOne getUnidirectional() { - return unidirectional; - } - - /** - * Allocate the property to a list. - */ - private void allocateToList(BeanProperty prop) { - if (prop.isTransient()) { - transients.add(prop); - return; - } - if (prop.isId()) { - ids.add(prop); - return; - } else { - nonTransients.add(prop); - } - - if (desc.getInheritInfo() != null && prop.isLocal()) { - local.add(prop); - } - - if (prop instanceof BeanPropertyAssocMany) { - manys.add(prop); - - } else { - nonManys.add(prop); - if (prop instanceof BeanPropertyAssocOne) { - if (prop.isEmbedded()) { - embedded.add(prop); - } else { - ones.add(prop); - BeanPropertyAssocOne assocOne = (BeanPropertyAssocOne) prop; - if (assocOne.isOneToOneExported()) { - onesExported.add(prop); - } else { - onesImported.add(prop); - } - } - } else { - // its a "base" property... - if (prop.isVersion()) { - version.add(prop); - if (derivedFirstVersionProp == null) { - derivedFirstVersionProp = prop; - } - } - if (prop instanceof BeanPropertyCompound) { - baseCompound.add((BeanPropertyCompound) prop); - } else { - baseScalar.add(prop); - } - } - } - } - - public BeanProperty getFirstVersion() { - return derivedFirstVersionProp; - } - - public BeanProperty[] getPropertiesWithValidators(boolean recurse) { - - ArrayList list = new ArrayList(); - Iterator it = propertyMap.values().iterator(); - while (it.hasNext()) { - BeanProperty property = (BeanProperty) it.next(); - if (property.hasValidationRules(recurse)) { - list.add(property); - } - } - return list.toArray(new BeanProperty[list.size()]); - } - - public Validator[] getBeanValidators() { - return new Validator[0]; - } - - public LinkedHashMap getPropertyMap() { - return propertyMap; - } - - public TableJoin[] getTableJoin() { - return tableJoins; - } - - /** - * Return the base scalar properties (excludes Id and secondary table - * properties). - */ - public BeanProperty[] getBaseScalar() { - return (BeanProperty[]) baseScalar.toArray(new BeanProperty[baseScalar.size()]); - } - - public BeanPropertyCompound[] getBaseCompound() { - return (BeanPropertyCompound[]) baseCompound.toArray(new BeanPropertyCompound[baseCompound.size()]); - } - - public BeanProperty getNaturalKey() { - String naturalKey = desc.getCacheOptions().getNaturalKey(); - if (naturalKey != null){ - return propertyMap.get(naturalKey); - } - return null; - } - - public BeanProperty[] getId() { - return (BeanProperty[]) ids.toArray(new BeanProperty[ids.size()]); - } - - public BeanProperty[] getNonTransients() { - return (BeanProperty[]) nonTransients.toArray(new BeanProperty[nonTransients.size()]); - } - - public BeanProperty[] getTransients() { - return (BeanProperty[]) transients.toArray(new BeanProperty[transients.size()]); - } - - public BeanProperty[] getVersion() { - return (BeanProperty[]) version.toArray(new BeanProperty[version.size()]); - } - - public BeanProperty[] getLocal() { - return (BeanProperty[]) local.toArray(new BeanProperty[local.size()]); - } - - public BeanPropertyAssocOne[] getEmbedded() { - return (BeanPropertyAssocOne[]) embedded.toArray(new BeanPropertyAssocOne[embedded.size()]); - } - - public BeanPropertyAssocOne[] getOneExported() { - return (BeanPropertyAssocOne[]) onesExported.toArray(new BeanPropertyAssocOne[onesExported.size()]); - } - - public BeanPropertyAssocOne[] getOneImported() { - return (BeanPropertyAssocOne[]) onesImported.toArray(new BeanPropertyAssocOne[onesImported.size()]); - } - - public BeanPropertyAssocOne[] getOnes() { - return (BeanPropertyAssocOne[]) ones.toArray(new BeanPropertyAssocOne[ones.size()]); - } - - public BeanPropertyAssocOne[] getOneExportedSave() { - return getOne(false, Mode.Save); - } - - public BeanPropertyAssocOne[] getOneExportedDelete() { - return getOne(false, Mode.Delete); - } - - public BeanPropertyAssocOne[] getOneImportedSave() { - return getOne(true, Mode.Save); - } - - public BeanPropertyAssocOne[] getOneImportedDelete() { - return getOne(true, Mode.Delete); - } - - public BeanProperty[] getNonMany() { - return (BeanProperty[]) nonManys.toArray(new BeanProperty[nonManys.size()]); - } - - public BeanPropertyAssocMany[] getMany() { - return (BeanPropertyAssocMany[]) manys.toArray(new BeanPropertyAssocMany[manys.size()]); - } - - public BeanPropertyAssocMany[] getManySave() { - return getMany(Mode.Save); - } - - public BeanPropertyAssocMany[] getManyDelete() { - return getMany(Mode.Delete); - } - - public BeanPropertyAssocMany[] getManyToMany() { - return getMany2Many(); - } - - /** - * Mode used to determine which BeanPropertyAssoc to include. - */ - private enum Mode { - Save, Delete, Validate; - } - - private BeanPropertyAssocOne[] getOne(boolean imported, Mode mode) { - ArrayList> list = new ArrayList>(); - for (int i = 0; i < ones.size(); i++) { - BeanPropertyAssocOne prop = (BeanPropertyAssocOne) ones.get(i); - if (imported != prop.isOneToOneExported()) { - switch (mode) { - case Save: - if (prop.getCascadeInfo().isSave()) { - list.add(prop); - } - break; - case Delete: - if (prop.getCascadeInfo().isDelete()) { - list.add(prop); - } - break; - case Validate: - if (prop.getCascadeInfo().isValidate()) { - list.add(prop); - } - break; - default: - break; - } - } - } - - return (BeanPropertyAssocOne[]) list.toArray(new BeanPropertyAssocOne[list.size()]); - } - - private BeanPropertyAssocMany[] getMany2Many() { - ArrayList> list = new ArrayList>(); - for (int i = 0; i < manys.size(); i++) { - BeanPropertyAssocMany prop = (BeanPropertyAssocMany) manys.get(i); - if (prop.isManyToMany()) { - list.add(prop); - } - } - - return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[list.size()]); - } - - private BeanPropertyAssocMany[] getMany(Mode mode) { - ArrayList> list = new ArrayList>(); - for (int i = 0; i < manys.size(); i++) { - BeanPropertyAssocMany prop = (BeanPropertyAssocMany) manys.get(i); - - switch (mode) { - case Save: - if (prop.getCascadeInfo().isSave() || prop.isManyToMany() - || ModifyListenMode.REMOVALS.equals(prop.getModifyListenMode())) { - // Note ManyToMany always included as we always 'save' - // the relationship via insert/delete of intersection table - // REMOVALS means including PrivateOwned relationships - list.add(prop); - } - break; - case Delete: - if (prop.getCascadeInfo().isDelete() - || ModifyListenMode.REMOVALS.equals(prop.getModifyListenMode())) { - // REMOVALS means including PrivateOwned relationships - list.add(prop); - } - break; - case Validate: - if (prop.getCascadeInfo().isValidate()) { - list.add(prop); - } - break; - default: - break; - } - - } - - return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[list.size()]); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private BeanProperty createBeanProperty(BeanDescriptorMap owner, DeployBeanProperty deployProp) { - - if (deployProp instanceof DeployBeanPropertyAssocOne) { - - return new BeanPropertyAssocOne(owner, desc, (DeployBeanPropertyAssocOne) deployProp); - } - if (deployProp instanceof DeployBeanPropertySimpleCollection) { - - return new BeanPropertySimpleCollection(owner, desc, (DeployBeanPropertySimpleCollection)deployProp); - } - if (deployProp instanceof DeployBeanPropertyAssocMany) { - - return new BeanPropertyAssocMany(owner, desc, (DeployBeanPropertyAssocMany) deployProp); - } - if (deployProp instanceof DeployBeanPropertyCompound) { - - return new BeanPropertyCompound(owner, desc, (DeployBeanPropertyCompound) deployProp); - } - - return new BeanProperty(owner, desc, deployProp); - } -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; + +import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; +import com.avaje.ebean.validation.factory.Validator; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorMap; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.BeanPropertySimpleCollection; +import com.avaje.ebeaninternal.server.deploy.TableJoin; + +/** + * Helper object to classify BeanProperties into appropriate lists. + */ +public class DeployBeanPropertyLists { + + private BeanProperty derivedFirstVersionProp; + + private final BeanDescriptor desc; + + private final LinkedHashMap propertyMap; + + private final ArrayList ids = new ArrayList(); + + private final ArrayList version = new ArrayList(); + + private final ArrayList local = new ArrayList(); + + private final ArrayList manys = new ArrayList(); + private final ArrayList nonManys = new ArrayList(); + + private final ArrayList ones = new ArrayList(); + + private final ArrayList onesExported = new ArrayList(); + + private final ArrayList onesImported = new ArrayList(); + + private final ArrayList embedded = new ArrayList(); + + private final ArrayList baseScalar = new ArrayList(); + + private final ArrayList baseCompound = new ArrayList(); + + private final ArrayList transients = new ArrayList(); + + private final ArrayList nonTransients = new ArrayList(); + + private final TableJoin[] tableJoins; + + private final BeanPropertyAssocOne unidirectional; + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public DeployBeanPropertyLists(BeanDescriptorMap owner, BeanDescriptor desc, DeployBeanDescriptor deploy) { + this.desc = desc; + + DeployBeanPropertyAssocOne deployUnidirectional = deploy.getUnidirectional(); + if (deployUnidirectional == null) { + unidirectional = null; + } else { + unidirectional = new BeanPropertyAssocOne(owner, desc, deployUnidirectional); + } + + this.propertyMap = new LinkedHashMap(); + + Iterator deployIt = deploy.propertiesAll(); + while (deployIt.hasNext()) { + DeployBeanProperty deployProp = deployIt.next(); + BeanProperty beanProp = createBeanProperty(owner, deployProp); + propertyMap.put(beanProp.getName(), beanProp); + } + + Iterator it = propertyMap.values().iterator(); + + int order = 0; + while (it.hasNext()) { + BeanProperty prop = it.next(); + prop.setDeployOrder(order++); + allocateToList(prop); + } + + List deployTableJoins = deploy.getTableJoins(); + tableJoins = new TableJoin[deployTableJoins.size()]; + for (int i = 0; i < deployTableJoins.size(); i++) { + tableJoins[i] = new TableJoin(deployTableJoins.get(i), propertyMap); + } + + } + + /** + * Return the unidirectional. + */ + public BeanPropertyAssocOne getUnidirectional() { + return unidirectional; + } + + /** + * Allocate the property to a list. + */ + private void allocateToList(BeanProperty prop) { + if (prop.isTransient()) { + transients.add(prop); + return; + } + if (prop.isId()) { + ids.add(prop); + return; + } else { + nonTransients.add(prop); + } + + if (desc.getInheritInfo() != null && prop.isLocal()) { + local.add(prop); + } + + if (prop instanceof BeanPropertyAssocMany) { + manys.add(prop); + + } else { + nonManys.add(prop); + if (prop instanceof BeanPropertyAssocOne) { + if (prop.isEmbedded()) { + embedded.add(prop); + } else { + ones.add(prop); + BeanPropertyAssocOne assocOne = (BeanPropertyAssocOne) prop; + if (assocOne.isOneToOneExported()) { + onesExported.add(prop); + } else { + onesImported.add(prop); + } + } + } else { + // its a "base" property... + if (prop.isVersion()) { + version.add(prop); + if (derivedFirstVersionProp == null) { + derivedFirstVersionProp = prop; + } + } + if (prop instanceof BeanPropertyCompound) { + baseCompound.add((BeanPropertyCompound) prop); + } else { + baseScalar.add(prop); + } + } + } + } + + public BeanProperty getFirstVersion() { + return derivedFirstVersionProp; + } + + public BeanProperty[] getPropertiesWithValidators(boolean recurse) { + + ArrayList list = new ArrayList(); + Iterator it = propertyMap.values().iterator(); + while (it.hasNext()) { + BeanProperty property = (BeanProperty) it.next(); + if (property.hasValidationRules(recurse)) { + list.add(property); + } + } + return list.toArray(new BeanProperty[list.size()]); + } + + public Validator[] getBeanValidators() { + return new Validator[0]; + } + + public LinkedHashMap getPropertyMap() { + return propertyMap; + } + + public TableJoin[] getTableJoin() { + return tableJoins; + } + + /** + * Return the base scalar properties (excludes Id and secondary table + * properties). + */ + public BeanProperty[] getBaseScalar() { + return (BeanProperty[]) baseScalar.toArray(new BeanProperty[baseScalar.size()]); + } + + public BeanPropertyCompound[] getBaseCompound() { + return (BeanPropertyCompound[]) baseCompound.toArray(new BeanPropertyCompound[baseCompound.size()]); + } + + public BeanProperty getNaturalKey() { + String naturalKey = desc.getCacheOptions().getNaturalKey(); + if (naturalKey != null){ + return propertyMap.get(naturalKey); + } + return null; + } + + public BeanProperty[] getId() { + return (BeanProperty[]) ids.toArray(new BeanProperty[ids.size()]); + } + + public BeanProperty[] getNonTransients() { + return (BeanProperty[]) nonTransients.toArray(new BeanProperty[nonTransients.size()]); + } + + public BeanProperty[] getTransients() { + return (BeanProperty[]) transients.toArray(new BeanProperty[transients.size()]); + } + + public BeanProperty[] getVersion() { + return (BeanProperty[]) version.toArray(new BeanProperty[version.size()]); + } + + public BeanProperty[] getLocal() { + return (BeanProperty[]) local.toArray(new BeanProperty[local.size()]); + } + + public BeanPropertyAssocOne[] getEmbedded() { + return (BeanPropertyAssocOne[]) embedded.toArray(new BeanPropertyAssocOne[embedded.size()]); + } + + public BeanPropertyAssocOne[] getOneExported() { + return (BeanPropertyAssocOne[]) onesExported.toArray(new BeanPropertyAssocOne[onesExported.size()]); + } + + public BeanPropertyAssocOne[] getOneImported() { + return (BeanPropertyAssocOne[]) onesImported.toArray(new BeanPropertyAssocOne[onesImported.size()]); + } + + public BeanPropertyAssocOne[] getOnes() { + return (BeanPropertyAssocOne[]) ones.toArray(new BeanPropertyAssocOne[ones.size()]); + } + + public BeanPropertyAssocOne[] getOneExportedSave() { + return getOne(false, Mode.Save); + } + + public BeanPropertyAssocOne[] getOneExportedDelete() { + return getOne(false, Mode.Delete); + } + + public BeanPropertyAssocOne[] getOneImportedSave() { + return getOne(true, Mode.Save); + } + + public BeanPropertyAssocOne[] getOneImportedDelete() { + return getOne(true, Mode.Delete); + } + + public BeanProperty[] getNonMany() { + return (BeanProperty[]) nonManys.toArray(new BeanProperty[nonManys.size()]); + } + + public BeanPropertyAssocMany[] getMany() { + return (BeanPropertyAssocMany[]) manys.toArray(new BeanPropertyAssocMany[manys.size()]); + } + + public BeanPropertyAssocMany[] getManySave() { + return getMany(Mode.Save); + } + + public BeanPropertyAssocMany[] getManyDelete() { + return getMany(Mode.Delete); + } + + public BeanPropertyAssocMany[] getManyToMany() { + return getMany2Many(); + } + + /** + * Mode used to determine which BeanPropertyAssoc to include. + */ + private enum Mode { + Save, Delete, Validate; + } + + private BeanPropertyAssocOne[] getOne(boolean imported, Mode mode) { + ArrayList> list = new ArrayList>(); + for (int i = 0; i < ones.size(); i++) { + BeanPropertyAssocOne prop = (BeanPropertyAssocOne) ones.get(i); + if (imported != prop.isOneToOneExported()) { + switch (mode) { + case Save: + if (prop.getCascadeInfo().isSave()) { + list.add(prop); + } + break; + case Delete: + if (prop.getCascadeInfo().isDelete()) { + list.add(prop); + } + break; + case Validate: + if (prop.getCascadeInfo().isValidate()) { + list.add(prop); + } + break; + default: + break; + } + } + } + + return (BeanPropertyAssocOne[]) list.toArray(new BeanPropertyAssocOne[list.size()]); + } + + private BeanPropertyAssocMany[] getMany2Many() { + ArrayList> list = new ArrayList>(); + for (int i = 0; i < manys.size(); i++) { + BeanPropertyAssocMany prop = (BeanPropertyAssocMany) manys.get(i); + if (prop.isManyToMany()) { + list.add(prop); + } + } + + return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[list.size()]); + } + + private BeanPropertyAssocMany[] getMany(Mode mode) { + ArrayList> list = new ArrayList>(); + for (int i = 0; i < manys.size(); i++) { + BeanPropertyAssocMany prop = (BeanPropertyAssocMany) manys.get(i); + + switch (mode) { + case Save: + if (prop.getCascadeInfo().isSave() || prop.isManyToMany() + || ModifyListenMode.REMOVALS.equals(prop.getModifyListenMode())) { + // Note ManyToMany always included as we always 'save' + // the relationship via insert/delete of intersection table + // REMOVALS means including PrivateOwned relationships + list.add(prop); + } + break; + case Delete: + if (prop.getCascadeInfo().isDelete() + || ModifyListenMode.REMOVALS.equals(prop.getModifyListenMode())) { + // REMOVALS means including PrivateOwned relationships + list.add(prop); + } + break; + case Validate: + if (prop.getCascadeInfo().isValidate()) { + list.add(prop); + } + break; + default: + break; + } + + } + + return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[list.size()]); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private BeanProperty createBeanProperty(BeanDescriptorMap owner, DeployBeanProperty deployProp) { + + if (deployProp instanceof DeployBeanPropertyAssocOne) { + + return new BeanPropertyAssocOne(owner, desc, (DeployBeanPropertyAssocOne) deployProp); + } + if (deployProp instanceof DeployBeanPropertySimpleCollection) { + + return new BeanPropertySimpleCollection(owner, desc, (DeployBeanPropertySimpleCollection)deployProp); + } + if (deployProp instanceof DeployBeanPropertyAssocMany) { + + return new BeanPropertyAssocMany(owner, desc, (DeployBeanPropertyAssocMany) deployProp); + } + if (deployProp instanceof DeployBeanPropertyCompound) { + + return new BeanPropertyCompound(owner, desc, (DeployBeanPropertyCompound) deployProp); + } + + return new BeanProperty(owner, desc, deployProp); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertySimpleCollection.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertySimpleCollection.java index 1739217ba..52be73af4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertySimpleCollection.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanPropertySimpleCollection.java @@ -1,60 +1,41 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.deploy.meta; - -import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; -import com.avaje.ebeaninternal.server.deploy.ManyType; -import com.avaje.ebeaninternal.server.type.ScalarType; - -public class DeployBeanPropertySimpleCollection extends DeployBeanPropertyAssocMany { - - private final ScalarType collectionScalarType; - - public DeployBeanPropertySimpleCollection(DeployBeanDescriptor desc, Class targetType, ScalarType scalarType, ManyType manyType) { - super(desc, targetType, manyType); - this.collectionScalarType = scalarType; - this.modifyListenMode = ModifyListenMode.ALL; - } - - /** - * Return the scalarType of the collection elements. - */ - public ScalarType getCollectionScalarType() { - return collectionScalarType; - } - - /** - * Returns false as never a ManyToMany. - */ - @Override - public boolean isManyToMany() { - return false; - } - - /** - * Returns true as always Unidirectional. - */ - @Override - public boolean isUnidirectional() { - return true; - } - - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; +import com.avaje.ebeaninternal.server.deploy.ManyType; +import com.avaje.ebeaninternal.server.type.ScalarType; + +public class DeployBeanPropertySimpleCollection extends DeployBeanPropertyAssocMany { + + private final ScalarType collectionScalarType; + + public DeployBeanPropertySimpleCollection(DeployBeanDescriptor desc, Class targetType, ScalarType scalarType, ManyType manyType) { + super(desc, targetType, manyType); + this.collectionScalarType = scalarType; + this.modifyListenMode = ModifyListenMode.ALL; + } + + /** + * Return the scalarType of the collection elements. + */ + public ScalarType getCollectionScalarType() { + return collectionScalarType; + } + + /** + * Returns false as never a ManyToMany. + */ + @Override + public boolean isManyToMany() { + return false; + } + + /** + * Returns true as always Unidirectional. + */ + @Override + public boolean isUnidirectional() { + return true; + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanTable.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanTable.java index 1876c4094..c2de543ea 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanTable.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanTable.java @@ -1,109 +1,90 @@ -/** - * 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.meta; - -import java.util.List; - -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorMap; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; - - -/** - * 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. - *

- * It holds the main deployment information and not all the detail that is held - * in a BeanDescriptor. - *

- */ -public class DeployBeanTable { - - private final Class beanType; - - /** - * The base table. - */ - private String baseTable; - - private List idProperties; - - /** - * Create the BeanTable. - */ - public DeployBeanTable(Class beanType) { - this.beanType = beanType; - } - - /** - * Return the base table for this BeanTable. - * This is used to determine the join information - * for associations. - */ - public String getBaseTable() { - return baseTable; - } - - /** - * Set the base table for this BeanTable. - */ - public void setBaseTable(String baseTable) { - this.baseTable = baseTable; - } - - /** - * Return the id properties. - */ - public BeanProperty[] createIdProperties(BeanDescriptorMap owner) { - BeanProperty[] props = new BeanProperty[idProperties.size()]; - for (int i = 0; i < idProperties.size(); i++) { - props[i] = createProperty(owner, idProperties.get(i)); - } - return props; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private BeanProperty createProperty(BeanDescriptorMap owner, DeployBeanProperty prop){ - - if (prop instanceof DeployBeanPropertyAssocOne){ - return new BeanPropertyAssocOne(owner, (DeployBeanPropertyAssocOne)prop); - - } else { - return new BeanProperty(prop); - } - - } - - /** - * Set the Id properties. - */ - public void setIdProperties(List idProperties) { - this.idProperties = idProperties; - } - - /** - * Return the class for this beanTable. - */ - public Class getBeanType() { - return beanType; - } - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import java.util.List; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorMap; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; + + +/** + * 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. + *

+ * It holds the main deployment information and not all the detail that is held + * in a BeanDescriptor. + *

+ */ +public class DeployBeanTable { + + private final Class beanType; + + /** + * The base table. + */ + private String baseTable; + + private List idProperties; + + /** + * Create the BeanTable. + */ + public DeployBeanTable(Class beanType) { + this.beanType = beanType; + } + + /** + * Return the base table for this BeanTable. + * This is used to determine the join information + * for associations. + */ + public String getBaseTable() { + return baseTable; + } + + /** + * Set the base table for this BeanTable. + */ + public void setBaseTable(String baseTable) { + this.baseTable = baseTable; + } + + /** + * Return the id properties. + */ + public BeanProperty[] createIdProperties(BeanDescriptorMap owner) { + BeanProperty[] props = new BeanProperty[idProperties.size()]; + for (int i = 0; i < idProperties.size(); i++) { + props[i] = createProperty(owner, idProperties.get(i)); + } + return props; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private BeanProperty createProperty(BeanDescriptorMap owner, DeployBeanProperty prop){ + + if (prop instanceof DeployBeanPropertyAssocOne){ + return new BeanPropertyAssocOne(owner, (DeployBeanPropertyAssocOne)prop); + + } else { + return new BeanProperty(prop); + } + + } + + /** + * Set the Id properties. + */ + public void setIdProperties(List idProperties) { + this.idProperties = idProperties; + } + + /** + * Return the class for this beanTable. + */ + public Class getBeanType() { + return beanType; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoin.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoin.java index 900039701..bfbc3b82d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoin.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoin.java @@ -1,227 +1,208 @@ -/** - * 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.meta; - -import java.util.ArrayList; - -import javax.persistence.JoinColumn; - -import com.avaje.ebeaninternal.server.core.Message; -import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo; -import com.avaje.ebeaninternal.server.deploy.BeanTable; -import com.avaje.ebeaninternal.server.deploy.TableJoin; - -/** - * Represents a join to another table during deployment phase. - *

- * This gets converted into a immutable TableJoin when complete. - *

- */ -public class DeployTableJoin { - - /** - * Flag set when the imported key maps to the primary key. - * This occurs for intersection tables (ManyToMany). - */ - private boolean importedPrimaryKey; - - /** - * The joined table. - */ - private String table; - - /** - * The type of join. LEFT OUTER etc. - */ - private String type = TableJoin.JOIN; - - /** - * The list of properties mapped to this joined table. - */ - private ArrayList properties = new ArrayList(); - - /** - * The list of join column pairs. Used to generate the on clause. - */ - private ArrayList columns = new ArrayList(); - - /** - * The persist cascade info. - */ - private BeanCascadeInfo cascadeInfo = new BeanCascadeInfo(); - - - /** - * Create a DeployTableJoin. - */ - public DeployTableJoin() { - } - - public String toString() { - return type + " " + table + " " + columns; - } - - /** - * Return true if the imported foreign key maps to the primary key. - */ - public boolean isImportedPrimaryKey() { - return importedPrimaryKey; - } - - /** - * Flag set when the imported key maps to the primary key. - * This occurs for intersection tables (ManyToMany). - */ - public void setImportedPrimaryKey(boolean importedPrimaryKey) { - this.importedPrimaryKey = importedPrimaryKey; - } - - /** - * Return true if the JoinOnPair have been set. - */ - public boolean hasJoinColumns() { - return columns.size() > 0; - } - - /** - * Return the persist info. - */ - public BeanCascadeInfo getCascadeInfo() { - return cascadeInfo; - } - - - /** - * Copy all the columns to this join potentially reversing the columns. - */ - public void setColumns(DeployTableJoinColumn[] cols, boolean reverse) { - columns = new ArrayList(); - for (int i = 0; i < cols.length; i++) { - addJoinColumn(cols[i].copy(reverse)); - } - } - - /** - * Add a join pair - */ - public void addJoinColumn(DeployTableJoinColumn pair) { - columns.add(pair); - } - - /** - * Add a JoinColumn - *

- * The order is generally true for OneToMany and false for ManyToOne relationships. - *

- */ - public void addJoinColumn(boolean order, JoinColumn jc, BeanTable beanTable) { - if (!"".equals(jc.table())) { - setTable(jc.table()); - } - addJoinColumn(new DeployTableJoinColumn(order, jc, beanTable)); - } - - /** - * Add a JoinColumn array. - */ - public void addJoinColumn(boolean order, JoinColumn[] jcArray, BeanTable beanTable) { - for (int i = 0; i < jcArray.length; i++) { - addJoinColumn(order, jcArray[i], beanTable); - } - } - - /** - * Return the join columns. - */ - public DeployTableJoinColumn[] columns() { - return (DeployTableJoinColumn[])columns.toArray(new DeployTableJoinColumn[columns.size()]); - } - - - /** - * For secondary table joins returns the properties mapped to that table. - */ - public DeployBeanProperty[] properties() { - return (DeployBeanProperty[])properties.toArray(new DeployBeanProperty[properties.size()]); - } - - /** - * Return the joined table name. - */ - public String getTable() { - return table; - } - - /** - * set the joined table name. - */ - public void setTable(String table) { - this.table = table; - } - - /** - * Return the type of join. LEFT OUTER JOIN etc. - */ - public String getType() { - return type; - } - - /** - * Return true if this join is a left outer join. - */ - public boolean isOuterJoin() { - return type.equals(TableJoin.LEFT_OUTER); - } - - /** - * Set the type of join. - */ - public void setType(String joinType) { - joinType = joinType.toUpperCase(); - if (joinType.equalsIgnoreCase(TableJoin.JOIN)) { - type = TableJoin.JOIN; - } else if (joinType.indexOf("LEFT") > -1) { - type = TableJoin.LEFT_OUTER; - } else if (joinType.indexOf("OUTER") > -1) { - type = TableJoin.LEFT_OUTER; - } else if (joinType.indexOf("INNER") > -1) { - type = TableJoin.JOIN; - } else { - throw new RuntimeException(Message.msg("join.type.unknown", joinType)); - } - } - - public DeployTableJoin createInverse(String tableName) { - - DeployTableJoin inverse = new DeployTableJoin(); - - return copyTo(inverse, true, tableName); - - } - - public DeployTableJoin copyTo(DeployTableJoin destJoin, boolean reverse, String tableName) { - - destJoin.setTable(tableName); - destJoin.setType(type); - destJoin.setColumns(columns(), reverse); - - return destJoin; - } -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import java.util.ArrayList; + +import javax.persistence.JoinColumn; + +import com.avaje.ebeaninternal.server.core.Message; +import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo; +import com.avaje.ebeaninternal.server.deploy.BeanTable; +import com.avaje.ebeaninternal.server.deploy.TableJoin; + +/** + * Represents a join to another table during deployment phase. + *

+ * This gets converted into a immutable TableJoin when complete. + *

+ */ +public class DeployTableJoin { + + /** + * Flag set when the imported key maps to the primary key. + * This occurs for intersection tables (ManyToMany). + */ + private boolean importedPrimaryKey; + + /** + * The joined table. + */ + private String table; + + /** + * The type of join. LEFT OUTER etc. + */ + private String type = TableJoin.JOIN; + + /** + * The list of properties mapped to this joined table. + */ + private ArrayList properties = new ArrayList(); + + /** + * The list of join column pairs. Used to generate the on clause. + */ + private ArrayList columns = new ArrayList(); + + /** + * The persist cascade info. + */ + private BeanCascadeInfo cascadeInfo = new BeanCascadeInfo(); + + + /** + * Create a DeployTableJoin. + */ + public DeployTableJoin() { + } + + public String toString() { + return type + " " + table + " " + columns; + } + + /** + * Return true if the imported foreign key maps to the primary key. + */ + public boolean isImportedPrimaryKey() { + return importedPrimaryKey; + } + + /** + * Flag set when the imported key maps to the primary key. + * This occurs for intersection tables (ManyToMany). + */ + public void setImportedPrimaryKey(boolean importedPrimaryKey) { + this.importedPrimaryKey = importedPrimaryKey; + } + + /** + * Return true if the JoinOnPair have been set. + */ + public boolean hasJoinColumns() { + return columns.size() > 0; + } + + /** + * Return the persist info. + */ + public BeanCascadeInfo getCascadeInfo() { + return cascadeInfo; + } + + + /** + * Copy all the columns to this join potentially reversing the columns. + */ + public void setColumns(DeployTableJoinColumn[] cols, boolean reverse) { + columns = new ArrayList(); + for (int i = 0; i < cols.length; i++) { + addJoinColumn(cols[i].copy(reverse)); + } + } + + /** + * Add a join pair + */ + public void addJoinColumn(DeployTableJoinColumn pair) { + columns.add(pair); + } + + /** + * Add a JoinColumn + *

+ * The order is generally true for OneToMany and false for ManyToOne relationships. + *

+ */ + public void addJoinColumn(boolean order, JoinColumn jc, BeanTable beanTable) { + if (!"".equals(jc.table())) { + setTable(jc.table()); + } + addJoinColumn(new DeployTableJoinColumn(order, jc, beanTable)); + } + + /** + * Add a JoinColumn array. + */ + public void addJoinColumn(boolean order, JoinColumn[] jcArray, BeanTable beanTable) { + for (int i = 0; i < jcArray.length; i++) { + addJoinColumn(order, jcArray[i], beanTable); + } + } + + /** + * Return the join columns. + */ + public DeployTableJoinColumn[] columns() { + return (DeployTableJoinColumn[])columns.toArray(new DeployTableJoinColumn[columns.size()]); + } + + + /** + * For secondary table joins returns the properties mapped to that table. + */ + public DeployBeanProperty[] properties() { + return (DeployBeanProperty[])properties.toArray(new DeployBeanProperty[properties.size()]); + } + + /** + * Return the joined table name. + */ + public String getTable() { + return table; + } + + /** + * set the joined table name. + */ + public void setTable(String table) { + this.table = table; + } + + /** + * Return the type of join. LEFT OUTER JOIN etc. + */ + public String getType() { + return type; + } + + /** + * Return true if this join is a left outer join. + */ + public boolean isOuterJoin() { + return type.equals(TableJoin.LEFT_OUTER); + } + + /** + * Set the type of join. + */ + public void setType(String joinType) { + joinType = joinType.toUpperCase(); + if (joinType.equalsIgnoreCase(TableJoin.JOIN)) { + type = TableJoin.JOIN; + } else if (joinType.indexOf("LEFT") > -1) { + type = TableJoin.LEFT_OUTER; + } else if (joinType.indexOf("OUTER") > -1) { + type = TableJoin.LEFT_OUTER; + } else if (joinType.indexOf("INNER") > -1) { + type = TableJoin.JOIN; + } else { + throw new RuntimeException(Message.msg("join.type.unknown", joinType)); + } + } + + public DeployTableJoin createInverse(String tableName) { + + DeployTableJoin inverse = new DeployTableJoin(); + + return copyTo(inverse, true, tableName); + + } + + public DeployTableJoin copyTo(DeployTableJoin destJoin, boolean reverse, String tableName) { + + destJoin.setTable(tableName); + destJoin.setType(type); + destJoin.setColumns(columns(), reverse); + + return destJoin; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoinColumn.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoinColumn.java index ac8811781..8a9664605 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoinColumn.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployTableJoinColumn.java @@ -1,197 +1,178 @@ -/** - * 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.meta; - -import javax.persistence.JoinColumn; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanTable; - -/** - * A join pair of local and foreign properties. - */ -public class DeployTableJoinColumn { - - /** - * The local database column name. - */ - String localDbColumn; - - /** - * The foreign database column name. - */ - String foreignDbColumn; - - boolean insertable; - - boolean updateable; - - /** - * Construct when automatically determining the join. - *

- * Assume that we want the foreign key to be insertable and updateable. - *

- */ - public DeployTableJoinColumn(String localDbColumn, String foreignDbColumn) { - this(localDbColumn, foreignDbColumn, true, true); - } - - /** - * Construct with explicit insertable and updateable flags. - */ - public DeployTableJoinColumn(String localDbColumn, String foreignDbColumn, boolean insertable, boolean updateable) { - this.localDbColumn = nullEmptyString(localDbColumn); - this.foreignDbColumn = nullEmptyString(foreignDbColumn); - this.insertable = insertable; - this.updateable = updateable; - } - - public DeployTableJoinColumn(boolean order, JoinColumn jc, BeanTable beanTable) { - this(jc.referencedColumnName(), jc.name(), jc.insertable(), jc.updatable()); - setReferencedColumn(beanTable); - if (!order){ - reverse(); - } - } - - private void setReferencedColumn(BeanTable beanTable){ - if (localDbColumn == null){ - BeanProperty[] idProperties = beanTable.getIdProperties(); - if (idProperties.length == 1){ - localDbColumn = idProperties[0].getDbColumn(); - } - } - } - - /** - * Reverse the direction of the join. - */ - public DeployTableJoinColumn reverse() { - String temp = localDbColumn; - localDbColumn = foreignDbColumn; - foreignDbColumn = temp; - return this; - } - - /** - * Helper method to null out empty strings. - */ - private String nullEmptyString(String s){ - if ("".equals(s)){ - return null; - } - return s; - } - - - public DeployTableJoinColumn copy(boolean reverse) { - // Note that the insertable and updateable are just copied - // which may not always be the correct thing to do - // but will leave it like this for now - if (reverse){ - return new DeployTableJoinColumn(foreignDbColumn, localDbColumn, insertable, updateable); - - } else { - return new DeployTableJoinColumn(localDbColumn, foreignDbColumn, insertable, updateable); - } - } - - public String toString() { - return localDbColumn + " = " + foreignDbColumn; - } - - /** - * Return true if either the local or foreign column is null. - *

- * Both columns need to be defined. If one is null then typically it is - * derived as the primary key column. - *

- */ - public boolean hasNullColumn() { - return localDbColumn == null || foreignDbColumn == null; - } - - /** - * When only ONE column has been set by deployment information return that one. - *

- * Used with hasNullColumn() to set the foreignDbColumn for OneToMany joins. - *

- */ - public String getNonNullColumn() { - if (localDbColumn == null && foreignDbColumn == null) { - throw new IllegalStateException("expecting only one null column?"); - - } else if (localDbColumn != null && foreignDbColumn != null) { - throw new IllegalStateException("expecting one null column?"); - } - if (localDbColumn != null) { - return localDbColumn; - } else { - return foreignDbColumn; - } - } - - /** - * Return true if this column should be insertable. - */ - public boolean isInsertable() { - return insertable; - } - - /** - * Return true if this column should be updateable. - */ - public boolean isUpdateable() { - return updateable; - } - - /** - * Return the foreign database column name. - */ - public String getForeignDbColumn() { - return foreignDbColumn; - } - - /** - * Set the foreign database column name. - *

- * Used when this is derived from Primary Key and not set explicitly in the - * deployment information. - *

- */ - public void setForeignDbColumn(String foreignDbColumn) { - this.foreignDbColumn = foreignDbColumn; - } - - /** - * Return the local database column name. - */ - public String getLocalDbColumn() { - return localDbColumn; - } - - /** - * Set the local database column name. - */ - public void setLocalDbColumn(String localDbColumn) { - this.localDbColumn = localDbColumn; - } - -} +package com.avaje.ebeaninternal.server.deploy.meta; + +import javax.persistence.JoinColumn; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanTable; + +/** + * A join pair of local and foreign properties. + */ +public class DeployTableJoinColumn { + + /** + * The local database column name. + */ + String localDbColumn; + + /** + * The foreign database column name. + */ + String foreignDbColumn; + + boolean insertable; + + boolean updateable; + + /** + * Construct when automatically determining the join. + *

+ * Assume that we want the foreign key to be insertable and updateable. + *

+ */ + public DeployTableJoinColumn(String localDbColumn, String foreignDbColumn) { + this(localDbColumn, foreignDbColumn, true, true); + } + + /** + * Construct with explicit insertable and updateable flags. + */ + public DeployTableJoinColumn(String localDbColumn, String foreignDbColumn, boolean insertable, boolean updateable) { + this.localDbColumn = nullEmptyString(localDbColumn); + this.foreignDbColumn = nullEmptyString(foreignDbColumn); + this.insertable = insertable; + this.updateable = updateable; + } + + public DeployTableJoinColumn(boolean order, JoinColumn jc, BeanTable beanTable) { + this(jc.referencedColumnName(), jc.name(), jc.insertable(), jc.updatable()); + setReferencedColumn(beanTable); + if (!order){ + reverse(); + } + } + + private void setReferencedColumn(BeanTable beanTable){ + if (localDbColumn == null){ + BeanProperty[] idProperties = beanTable.getIdProperties(); + if (idProperties.length == 1){ + localDbColumn = idProperties[0].getDbColumn(); + } + } + } + + /** + * Reverse the direction of the join. + */ + public DeployTableJoinColumn reverse() { + String temp = localDbColumn; + localDbColumn = foreignDbColumn; + foreignDbColumn = temp; + return this; + } + + /** + * Helper method to null out empty strings. + */ + private String nullEmptyString(String s){ + if ("".equals(s)){ + return null; + } + return s; + } + + + public DeployTableJoinColumn copy(boolean reverse) { + // Note that the insertable and updateable are just copied + // which may not always be the correct thing to do + // but will leave it like this for now + if (reverse){ + return new DeployTableJoinColumn(foreignDbColumn, localDbColumn, insertable, updateable); + + } else { + return new DeployTableJoinColumn(localDbColumn, foreignDbColumn, insertable, updateable); + } + } + + public String toString() { + return localDbColumn + " = " + foreignDbColumn; + } + + /** + * Return true if either the local or foreign column is null. + *

+ * Both columns need to be defined. If one is null then typically it is + * derived as the primary key column. + *

+ */ + public boolean hasNullColumn() { + return localDbColumn == null || foreignDbColumn == null; + } + + /** + * When only ONE column has been set by deployment information return that one. + *

+ * Used with hasNullColumn() to set the foreignDbColumn for OneToMany joins. + *

+ */ + public String getNonNullColumn() { + if (localDbColumn == null && foreignDbColumn == null) { + throw new IllegalStateException("expecting only one null column?"); + + } else if (localDbColumn != null && foreignDbColumn != null) { + throw new IllegalStateException("expecting one null column?"); + } + if (localDbColumn != null) { + return localDbColumn; + } else { + return foreignDbColumn; + } + } + + /** + * Return true if this column should be insertable. + */ + public boolean isInsertable() { + return insertable; + } + + /** + * Return true if this column should be updateable. + */ + public boolean isUpdateable() { + return updateable; + } + + /** + * Return the foreign database column name. + */ + public String getForeignDbColumn() { + return foreignDbColumn; + } + + /** + * Set the foreign database column name. + *

+ * Used when this is derived from Primary Key and not set explicitly in the + * deployment information. + *

+ */ + public void setForeignDbColumn(String foreignDbColumn) { + this.foreignDbColumn = foreignDbColumn; + } + + /** + * Return the local database column name. + */ + public String getLocalDbColumn() { + return localDbColumn; + } + + /** + * Set the local database column name. + */ + public void setLocalDbColumn(String localDbColumn) { + this.localDbColumn = localDbColumn; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java index 3a4bc2126..757855fc6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocManys.java @@ -1,348 +1,329 @@ -/** - * 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.parse; - -import java.util.Iterator; - -import javax.persistence.JoinColumn; -import javax.persistence.JoinColumns; -import javax.persistence.JoinTable; -import javax.persistence.ManyToMany; -import javax.persistence.MapKey; -import javax.persistence.OneToMany; -import javax.persistence.OrderBy; - -import com.avaje.ebean.annotation.LdapAttribute; -import com.avaje.ebean.annotation.PrivateOwned; -import com.avaje.ebean.annotation.Where; -import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; -import com.avaje.ebean.config.NamingConvention; -import com.avaje.ebean.config.TableName; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanTable; -import com.avaje.ebeaninternal.server.deploy.TableJoin; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; -import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; - -/** - * Read the deployment annotation for Assoc Many beans. - */ -public class AnnotationAssocManys extends AnnotationParser { - - private final BeanDescriptorManager factory; - - /** - * Create with the DeployInfo. - */ - public AnnotationAssocManys(DeployBeanInfo info, BeanDescriptorManager factory) { - super(info); - this.factory = factory; - } - - /** - * Parse the annotations. - */ - public void parse() { - Iterator it = descriptor.propertiesAll(); - while (it.hasNext()) { - DeployBeanProperty prop = it.next(); - if (prop instanceof DeployBeanPropertyAssocMany) { - read((DeployBeanPropertyAssocMany) prop); - } - } - } - - private void read(DeployBeanPropertyAssocMany prop) { - - OneToMany oneToMany = get(prop, OneToMany.class); - if (oneToMany != null) { - readToOne(oneToMany, prop); - PrivateOwned privateOwned = get(prop, PrivateOwned.class); - if (privateOwned != null){ - prop.setModifyListenMode(ModifyListenMode.REMOVALS); - prop.getCascadeInfo().setDelete(privateOwned.cascadeRemove()); - } - } - ManyToMany manyToMany = get(prop, ManyToMany.class); - if (manyToMany != null) { - readToMany(manyToMany, prop); - } - - OrderBy orderBy = get(prop, OrderBy.class); - if (orderBy != null) { - prop.setFetchOrderBy(orderBy.value()); - } - - MapKey mapKey = get(prop, MapKey.class); - if (mapKey != null) { - prop.setMapKey(mapKey.name()); - } - - Where where = get(prop, Where.class); - if (where != null) { - prop.setExtraWhere(where.clause()); - } - - // check for manually defined joins - BeanTable beanTable = prop.getBeanTable(); - JoinColumn joinColumn = get(prop, JoinColumn.class); - if (joinColumn != null) { - prop.getTableJoin().addJoinColumn(true, joinColumn, beanTable); - } - - JoinColumns joinColumns = get(prop, JoinColumns.class); - if (joinColumns != null) { - prop.getTableJoin().addJoinColumn(true, joinColumns.value(), beanTable); - } - - JoinTable joinTable = get(prop, JoinTable.class); - if (joinTable != null) { - if (prop.isManyToMany()){ - // expected this - readJoinTable(joinTable, prop); - - } else { - // OneToMany in theory - prop.getTableJoin().addJoinColumn(true, joinTable.joinColumns(), beanTable); - } - } - LdapAttribute ldapAttribute = get(prop, LdapAttribute.class); - if (ldapAttribute != null) { - // read ldap specific property settings - readLdapAttribute(ldapAttribute, prop); - } - - if (prop.getMappedBy() != null){ - // the join is derived by reversing the join information - // from the mapped by property. - // Refer BeanDescriptorManager.readEntityRelationships() - return; - } - - if (prop.isManyToMany()){ - manyToManyDefaultJoins(prop); - return; - } - - - if (!prop.getTableJoin().hasJoinColumns() && beanTable != null){ - - // use naming convention to define join (based on the bean name for this side of relationship) - // A unidirectional OneToMany or OneToMany with no mappedBy property - - NamingConvention nc = factory.getNamingConvention(); - - String fkeyPrefix = null; - if (nc.isUseForeignKeyPrefix()){ - fkeyPrefix = nc.getColumnFromProperty(descriptor.getBeanType(), descriptor.getName()); - } - - // Use the owning bean table to define the join - BeanTable owningBeanTable = factory.getBeanTable(descriptor.getBeanType()); - owningBeanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), false); - } - } - - /** - * Define the joins for a ManyToMany relationship. - *

- * This includes joins to the intersection table and from the intersection table - * to the other side of the ManyToMany. - *

- */ - private void readJoinTable(JoinTable joinTable, DeployBeanPropertyAssocMany prop) { - - String intTableName = getFullTableName(joinTable); - // set the intersection table - DeployTableJoin intJoin = new DeployTableJoin(); - intJoin.setTable(intTableName); - - // add the source to intersection join columns - intJoin.addJoinColumn(true, joinTable.joinColumns(), prop.getBeanTable()); - - // set the intersection to dest table join columns - DeployTableJoin destJoin = prop.getTableJoin(); - destJoin.addJoinColumn(false, joinTable.inverseJoinColumns(), prop.getBeanTable()); - - intJoin.setType(TableJoin.LEFT_OUTER); - - // reverse join from dest back to intersection - DeployTableJoin inverseDest = destJoin.createInverse(intTableName); - prop.setIntersectionJoin(intJoin); - prop.setInverseJoin(inverseDest); - } - - /** - * Return the full table name - * @param joinTable - * @return - */ - private String getFullTableName(JoinTable joinTable) { - StringBuilder sb = new StringBuilder(); - if (!StringHelper.isNull(joinTable.catalog())){ - sb.append(joinTable.catalog()).append("."); - } - if (!StringHelper.isNull(joinTable.schema())){ - sb.append(joinTable.schema()).append("."); - } - sb.append(joinTable.name()); - return sb.toString(); - } - - /** - * Define intersection table and foreign key columns for ManyToMany. - *

- * Some of these (maybe all) have been already defined via @JoinTable - * and @JoinColumns etc. - *

- */ - private void manyToManyDefaultJoins(DeployBeanPropertyAssocMany prop) { - - String intTableName = null; - - DeployTableJoin intJoin = prop.getIntersectionJoin(); - if (intJoin == null){ - intJoin = new DeployTableJoin(); - prop.setIntersectionJoin(intJoin); - } else { - // intersection table already defined (by @JoinTable) - intTableName = intJoin.getTable(); - } - - BeanTable localTable = factory.getBeanTable(descriptor.getBeanType()); - BeanTable otherTable = factory.getBeanTable(prop.getTargetType()); - - final String localTableName = localTable.getUnqualifiedBaseTable(); - final String otherTableName = otherTable.getUnqualifiedBaseTable(); - - if (intTableName == null){ - // define intersection table name - intTableName = getM2MJoinTableName(localTable, otherTable); - - intJoin.setTable(intTableName); - intJoin.setType(TableJoin.LEFT_OUTER); - } - - DeployTableJoin destJoin = prop.getTableJoin(); - - - if (intJoin.hasJoinColumns() && destJoin.hasJoinColumns()){ - // already defined the foreign key columns etc - return; - } - if (!intJoin.hasJoinColumns()){ - // define foreign key columns - BeanProperty[] localIds = localTable.getIdProperties(); - for (int i = 0; i < localIds.length; i++) { - // add the source to intersection join columns - String fkCol = localTableName+"_"+localIds[i].getDbColumn(); - intJoin.addJoinColumn(new DeployTableJoinColumn(localIds[i].getDbColumn(), fkCol)); - } - } - - if (!destJoin.hasJoinColumns()){ - // define inverse foreign key columns - BeanProperty[] otherIds = otherTable.getIdProperties(); - for (int i = 0; i < otherIds.length; i++) { - // set the intersection to dest table join columns - final String fkCol = otherTableName+"_"+otherIds[i].getDbColumn(); - destJoin.addJoinColumn(new DeployTableJoinColumn(fkCol, otherIds[i].getDbColumn())); - } - } - - // reverse join from dest back to intersection - DeployTableJoin inverseDest = destJoin.createInverse(intTableName); - prop.setInverseJoin(inverseDest); - } - - - - private String errorMsgMissingBeanTable(Class type, String from) { - return "Error with association to ["+type+"] from ["+from+"]. Is "+type+" registered?"; - } - - private void readToMany(ManyToMany propAnn, DeployBeanPropertyAssocMany manyProp) { - - manyProp.setMappedBy(propAnn.mappedBy()); - manyProp.setFetchType(propAnn.fetch()); - - setCascadeTypes(propAnn.cascade(), manyProp.getCascadeInfo()); - - Class targetType = propAnn.targetEntity(); - if (targetType.equals(void.class)) { - // via reflection of generics type - targetType = manyProp.getTargetType(); - } else { - manyProp.setTargetType(targetType); - } - - // find the other many table (not intersection) - BeanTable assoc = factory.getBeanTable(targetType); - if (assoc == null) { - String msg = errorMsgMissingBeanTable(targetType, manyProp.getFullBeanName()); - throw new RuntimeException(msg); - } - - manyProp.setManyToMany(true); - manyProp.setModifyListenMode(ModifyListenMode.ALL); - manyProp.setBeanTable(assoc); - manyProp.getTableJoin().setType(TableJoin.LEFT_OUTER); - } - - private void readToOne(OneToMany propAnn, DeployBeanPropertyAssocMany manyProp) { - - manyProp.setMappedBy(propAnn.mappedBy()); - manyProp.setFetchType(propAnn.fetch()); - - setCascadeTypes(propAnn.cascade(), manyProp.getCascadeInfo()); - - Class targetType = propAnn.targetEntity(); - if (targetType.equals(void.class)) { - // via reflection of generics type - targetType = manyProp.getTargetType(); - } else { - manyProp.setTargetType(targetType); - } - - BeanTable assoc = factory.getBeanTable(targetType); - if (assoc == null) { - String msg = errorMsgMissingBeanTable(targetType, manyProp.getFullBeanName()); - throw new RuntimeException(msg); - } - - manyProp.setBeanTable(assoc); - manyProp.getTableJoin().setType(TableJoin.LEFT_OUTER); - } - - - private String getM2MJoinTableName(BeanTable lhsTable, BeanTable rhsTable){ - - TableName lhs = new TableName(lhsTable.getBaseTable()); - TableName rhs = new TableName(rhsTable.getBaseTable()); - - TableName joinTable = namingConvention.getM2MJoinTableName(lhs, rhs); - - return joinTable.getQualifiedName(); - } -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.util.Iterator; + +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.MapKey; +import javax.persistence.OneToMany; +import javax.persistence.OrderBy; + +import com.avaje.ebean.annotation.LdapAttribute; +import com.avaje.ebean.annotation.PrivateOwned; +import com.avaje.ebean.annotation.Where; +import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; +import com.avaje.ebean.config.NamingConvention; +import com.avaje.ebean.config.TableName; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanTable; +import com.avaje.ebeaninternal.server.deploy.TableJoin; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; +import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoinColumn; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; + +/** + * Read the deployment annotation for Assoc Many beans. + */ +public class AnnotationAssocManys extends AnnotationParser { + + private final BeanDescriptorManager factory; + + /** + * Create with the DeployInfo. + */ + public AnnotationAssocManys(DeployBeanInfo info, BeanDescriptorManager factory) { + super(info); + this.factory = factory; + } + + /** + * Parse the annotations. + */ + public void parse() { + Iterator it = descriptor.propertiesAll(); + while (it.hasNext()) { + DeployBeanProperty prop = it.next(); + if (prop instanceof DeployBeanPropertyAssocMany) { + read((DeployBeanPropertyAssocMany) prop); + } + } + } + + private void read(DeployBeanPropertyAssocMany prop) { + + OneToMany oneToMany = get(prop, OneToMany.class); + if (oneToMany != null) { + readToOne(oneToMany, prop); + PrivateOwned privateOwned = get(prop, PrivateOwned.class); + if (privateOwned != null){ + prop.setModifyListenMode(ModifyListenMode.REMOVALS); + prop.getCascadeInfo().setDelete(privateOwned.cascadeRemove()); + } + } + ManyToMany manyToMany = get(prop, ManyToMany.class); + if (manyToMany != null) { + readToMany(manyToMany, prop); + } + + OrderBy orderBy = get(prop, OrderBy.class); + if (orderBy != null) { + prop.setFetchOrderBy(orderBy.value()); + } + + MapKey mapKey = get(prop, MapKey.class); + if (mapKey != null) { + prop.setMapKey(mapKey.name()); + } + + Where where = get(prop, Where.class); + if (where != null) { + prop.setExtraWhere(where.clause()); + } + + // check for manually defined joins + BeanTable beanTable = prop.getBeanTable(); + JoinColumn joinColumn = get(prop, JoinColumn.class); + if (joinColumn != null) { + prop.getTableJoin().addJoinColumn(true, joinColumn, beanTable); + } + + JoinColumns joinColumns = get(prop, JoinColumns.class); + if (joinColumns != null) { + prop.getTableJoin().addJoinColumn(true, joinColumns.value(), beanTable); + } + + JoinTable joinTable = get(prop, JoinTable.class); + if (joinTable != null) { + if (prop.isManyToMany()){ + // expected this + readJoinTable(joinTable, prop); + + } else { + // OneToMany in theory + prop.getTableJoin().addJoinColumn(true, joinTable.joinColumns(), beanTable); + } + } + LdapAttribute ldapAttribute = get(prop, LdapAttribute.class); + if (ldapAttribute != null) { + // read ldap specific property settings + readLdapAttribute(ldapAttribute, prop); + } + + if (prop.getMappedBy() != null){ + // the join is derived by reversing the join information + // from the mapped by property. + // Refer BeanDescriptorManager.readEntityRelationships() + return; + } + + if (prop.isManyToMany()){ + manyToManyDefaultJoins(prop); + return; + } + + + if (!prop.getTableJoin().hasJoinColumns() && beanTable != null){ + + // use naming convention to define join (based on the bean name for this side of relationship) + // A unidirectional OneToMany or OneToMany with no mappedBy property + + NamingConvention nc = factory.getNamingConvention(); + + String fkeyPrefix = null; + if (nc.isUseForeignKeyPrefix()){ + fkeyPrefix = nc.getColumnFromProperty(descriptor.getBeanType(), descriptor.getName()); + } + + // Use the owning bean table to define the join + BeanTable owningBeanTable = factory.getBeanTable(descriptor.getBeanType()); + owningBeanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), false); + } + } + + /** + * Define the joins for a ManyToMany relationship. + *

+ * This includes joins to the intersection table and from the intersection table + * to the other side of the ManyToMany. + *

+ */ + private void readJoinTable(JoinTable joinTable, DeployBeanPropertyAssocMany prop) { + + String intTableName = getFullTableName(joinTable); + // set the intersection table + DeployTableJoin intJoin = new DeployTableJoin(); + intJoin.setTable(intTableName); + + // add the source to intersection join columns + intJoin.addJoinColumn(true, joinTable.joinColumns(), prop.getBeanTable()); + + // set the intersection to dest table join columns + DeployTableJoin destJoin = prop.getTableJoin(); + destJoin.addJoinColumn(false, joinTable.inverseJoinColumns(), prop.getBeanTable()); + + intJoin.setType(TableJoin.LEFT_OUTER); + + // reverse join from dest back to intersection + DeployTableJoin inverseDest = destJoin.createInverse(intTableName); + prop.setIntersectionJoin(intJoin); + prop.setInverseJoin(inverseDest); + } + + /** + * Return the full table name + * @param joinTable + * @return + */ + private String getFullTableName(JoinTable joinTable) { + StringBuilder sb = new StringBuilder(); + if (!StringHelper.isNull(joinTable.catalog())){ + sb.append(joinTable.catalog()).append("."); + } + if (!StringHelper.isNull(joinTable.schema())){ + sb.append(joinTable.schema()).append("."); + } + sb.append(joinTable.name()); + return sb.toString(); + } + + /** + * Define intersection table and foreign key columns for ManyToMany. + *

+ * Some of these (maybe all) have been already defined via @JoinTable + * and @JoinColumns etc. + *

+ */ + private void manyToManyDefaultJoins(DeployBeanPropertyAssocMany prop) { + + String intTableName = null; + + DeployTableJoin intJoin = prop.getIntersectionJoin(); + if (intJoin == null){ + intJoin = new DeployTableJoin(); + prop.setIntersectionJoin(intJoin); + } else { + // intersection table already defined (by @JoinTable) + intTableName = intJoin.getTable(); + } + + BeanTable localTable = factory.getBeanTable(descriptor.getBeanType()); + BeanTable otherTable = factory.getBeanTable(prop.getTargetType()); + + final String localTableName = localTable.getUnqualifiedBaseTable(); + final String otherTableName = otherTable.getUnqualifiedBaseTable(); + + if (intTableName == null){ + // define intersection table name + intTableName = getM2MJoinTableName(localTable, otherTable); + + intJoin.setTable(intTableName); + intJoin.setType(TableJoin.LEFT_OUTER); + } + + DeployTableJoin destJoin = prop.getTableJoin(); + + + if (intJoin.hasJoinColumns() && destJoin.hasJoinColumns()){ + // already defined the foreign key columns etc + return; + } + if (!intJoin.hasJoinColumns()){ + // define foreign key columns + BeanProperty[] localIds = localTable.getIdProperties(); + for (int i = 0; i < localIds.length; i++) { + // add the source to intersection join columns + String fkCol = localTableName+"_"+localIds[i].getDbColumn(); + intJoin.addJoinColumn(new DeployTableJoinColumn(localIds[i].getDbColumn(), fkCol)); + } + } + + if (!destJoin.hasJoinColumns()){ + // define inverse foreign key columns + BeanProperty[] otherIds = otherTable.getIdProperties(); + for (int i = 0; i < otherIds.length; i++) { + // set the intersection to dest table join columns + final String fkCol = otherTableName+"_"+otherIds[i].getDbColumn(); + destJoin.addJoinColumn(new DeployTableJoinColumn(fkCol, otherIds[i].getDbColumn())); + } + } + + // reverse join from dest back to intersection + DeployTableJoin inverseDest = destJoin.createInverse(intTableName); + prop.setInverseJoin(inverseDest); + } + + + + private String errorMsgMissingBeanTable(Class type, String from) { + return "Error with association to ["+type+"] from ["+from+"]. Is "+type+" registered?"; + } + + private void readToMany(ManyToMany propAnn, DeployBeanPropertyAssocMany manyProp) { + + manyProp.setMappedBy(propAnn.mappedBy()); + manyProp.setFetchType(propAnn.fetch()); + + setCascadeTypes(propAnn.cascade(), manyProp.getCascadeInfo()); + + Class targetType = propAnn.targetEntity(); + if (targetType.equals(void.class)) { + // via reflection of generics type + targetType = manyProp.getTargetType(); + } else { + manyProp.setTargetType(targetType); + } + + // find the other many table (not intersection) + BeanTable assoc = factory.getBeanTable(targetType); + if (assoc == null) { + String msg = errorMsgMissingBeanTable(targetType, manyProp.getFullBeanName()); + throw new RuntimeException(msg); + } + + manyProp.setManyToMany(true); + manyProp.setModifyListenMode(ModifyListenMode.ALL); + manyProp.setBeanTable(assoc); + manyProp.getTableJoin().setType(TableJoin.LEFT_OUTER); + } + + private void readToOne(OneToMany propAnn, DeployBeanPropertyAssocMany manyProp) { + + manyProp.setMappedBy(propAnn.mappedBy()); + manyProp.setFetchType(propAnn.fetch()); + + setCascadeTypes(propAnn.cascade(), manyProp.getCascadeInfo()); + + Class targetType = propAnn.targetEntity(); + if (targetType.equals(void.class)) { + // via reflection of generics type + targetType = manyProp.getTargetType(); + } else { + manyProp.setTargetType(targetType); + } + + BeanTable assoc = factory.getBeanTable(targetType); + if (assoc == null) { + String msg = errorMsgMissingBeanTable(targetType, manyProp.getFullBeanName()); + throw new RuntimeException(msg); + } + + manyProp.setBeanTable(assoc); + manyProp.getTableJoin().setType(TableJoin.LEFT_OUTER); + } + + + private String getM2MJoinTableName(BeanTable lhsTable, BeanTable rhsTable){ + + TableName lhs = new TableName(lhsTable.getBaseTable()); + TableName rhs = new TableName(rhsTable.getBaseTable()); + + TableName joinTable = namingConvention.getM2MJoinTableName(lhs, rhs); + + return joinTable.getQualifiedName(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java index cf6ca1aa2..38dad3e22 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationAssocOnes.java @@ -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.deploy.parse; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import javax.persistence.AttributeOverride; -import javax.persistence.AttributeOverrides; -import javax.persistence.Column; -import javax.persistence.Embedded; -import javax.persistence.EmbeddedId; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.JoinColumns; -import javax.persistence.JoinTable; -import javax.persistence.ManyToOne; -import javax.persistence.OneToOne; - -import com.avaje.ebean.annotation.EmbeddedColumns; -import com.avaje.ebean.annotation.Where; -import com.avaje.ebean.config.NamingConvention; -import com.avaje.ebean.validation.NotNull; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; -import com.avaje.ebeaninternal.server.deploy.BeanTable; -import com.avaje.ebeaninternal.server.deploy.TableJoin; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; - -/** - * Read the deployment annotations for Associated One beans. - */ -public class AnnotationAssocOnes extends AnnotationParser { - - private final BeanDescriptorManager factory; - - /** - * Create with the deploy Info. - */ - public AnnotationAssocOnes(DeployBeanInfo info, BeanDescriptorManager factory) { - super(info); - this.factory = factory; - } - - /** - * Parse the annotation. - */ - public void parse() { - - Iterator it = descriptor.propertiesAll(); - while (it.hasNext()) { - DeployBeanProperty prop = it.next(); - if (prop instanceof DeployBeanPropertyAssocOne) { - readAssocOne((DeployBeanPropertyAssocOne) prop); - } - } - } - - private void readAssocOne(DeployBeanPropertyAssocOne prop) { - - ManyToOne manyToOne = get(prop, ManyToOne.class); - if (manyToOne != null) { - readManyToOne(manyToOne, prop); - } - OneToOne oneToOne = get(prop, OneToOne.class); - if (oneToOne != null) { - readOneToOne(oneToOne, prop); - } - Embedded embedded = get(prop, Embedded.class); - if (embedded != null) { - readEmbedded(embedded, prop); - } - EmbeddedId emId = get(prop, EmbeddedId.class); - if (emId != null) { - prop.setEmbedded(true); - prop.setId(true); - prop.setNullable(false); - } - Column column = get(prop, Column.class); - if (column != null && !isEmpty(column.name())) { - // have this in for AssocOnes used on - // Sql based beans... - prop.setDbColumn(column.name()); - } - - // May as well check for Id. Makes sense to me. - Id id = get(prop, Id.class); - if (id != null) { - prop.setEmbedded(true); - prop.setId(true); - prop.setNullable(false); - } - - Where where = get(prop, Where.class); - if (where != null) { - // not expecting this to be used on assoc one properties - prop.setExtraWhere(where.clause()); - } - - NotNull notNull = get(prop, NotNull.class); - if (notNull != null) { - prop.setNullable(false); - // overrides optional attribute of ManyToOne etc - prop.getTableJoin().setType(TableJoin.JOIN); - } - - // check for manually defined joins - BeanTable beanTable = prop.getBeanTable(); - JoinColumn joinColumn = get(prop, JoinColumn.class); - if (joinColumn != null) { - prop.getTableJoin().addJoinColumn(false, joinColumn, beanTable); - if (!joinColumn.updatable()){ - prop.setDbUpdateable(false); - } - } - - JoinColumns joinColumns = get(prop, JoinColumns.class); - if (joinColumns != null) { - prop.getTableJoin().addJoinColumn(false, joinColumns.value(), beanTable); - } - - JoinTable joinTable = get(prop, JoinTable.class); - if (joinTable != null) { - prop.getTableJoin().addJoinColumn(false, joinTable.joinColumns(), beanTable); - } - - info.setBeanJoinType(prop, prop.isNullable()); - - if (!prop.getTableJoin().hasJoinColumns() && beanTable != null) { - - if (prop.getMappedBy() != null) { - // the join is derived by reversing the join information - // from the mapped by property. - // Refer BeanDescriptorManager.readEntityRelationships() - - } else { - // use naming convention to define join. - NamingConvention nc = factory.getNamingConvention(); - - String fkeyPrefix = null; - if (nc.isUseForeignKeyPrefix()){ - fkeyPrefix = nc.getColumnFromProperty(beanType, prop.getName()); - } - - beanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), true); - } - } - } - - private String errorMsgMissingBeanTable(Class type, String from) { - return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered?"; - } - - private void readManyToOne(ManyToOne propAnn, DeployBeanProperty prop) { - - DeployBeanPropertyAssocOne beanProp = (DeployBeanPropertyAssocOne) prop; - - setCascadeTypes(propAnn.cascade(), beanProp.getCascadeInfo()); - - BeanTable assoc = factory.getBeanTable(beanProp.getPropertyType()); - if (assoc == null) { - String msg = errorMsgMissingBeanTable(beanProp.getPropertyType(), prop.getFullBeanName()); - throw new RuntimeException(msg); - } - beanProp.setBeanTable(assoc); - beanProp.setDbInsertable(true); - beanProp.setDbUpdateable(true); - beanProp.setNullable(propAnn.optional()); - beanProp.setFetchType(propAnn.fetch()); - } - - private void readOneToOne(OneToOne propAnn, DeployBeanPropertyAssocOne prop) { - - prop.setOneToOne(true); - prop.setDbInsertable(true); - prop.setDbUpdateable(true); - prop.setNullable(propAnn.optional()); - prop.setFetchType(propAnn.fetch()); - prop.setMappedBy(propAnn.mappedBy()); - if (!"".equals(propAnn.mappedBy())) { - prop.setOneToOneExported(true); - } - - setCascadeTypes(propAnn.cascade(), prop.getCascadeInfo()); - - BeanTable assoc = factory.getBeanTable(prop.getPropertyType()); - if (assoc == null) { - String msg = errorMsgMissingBeanTable(prop.getPropertyType(), prop.getFullBeanName()); - throw new RuntimeException(msg); - } - - prop.setBeanTable(assoc); - } - - private void readEmbedded(Embedded propAnn, DeployBeanPropertyAssocOne prop) { - - prop.setEmbedded(true); - prop.setDbInsertable(true); - prop.setDbUpdateable(true); - - EmbeddedColumns columns = get(prop, EmbeddedColumns.class); - if (columns != null) { - - // convert into a Map - String propColumns = columns.columns(); - Map propMap = StringHelper.delimitedToMap(propColumns, ",", "="); - - prop.getDeployEmbedded().putAll(propMap); - } - - AttributeOverrides attrOverrides = get(prop, AttributeOverrides.class); - if (attrOverrides != null) { - HashMap propMap = new HashMap(); - AttributeOverride[] aoArray = attrOverrides.value(); - for (int i = 0; i < aoArray.length; i++) { - String propName = aoArray[i].name(); - String columnName = aoArray[i].column().name(); - - propMap.put(propName, columnName); - } - - prop.getDeployEmbedded().putAll(propMap); - } - - } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.persistence.AttributeOverride; +import javax.persistence.AttributeOverrides; +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.EmbeddedId; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.JoinTable; +import javax.persistence.ManyToOne; +import javax.persistence.OneToOne; + +import com.avaje.ebean.annotation.EmbeddedColumns; +import com.avaje.ebean.annotation.Where; +import com.avaje.ebean.config.NamingConvention; +import com.avaje.ebean.validation.NotNull; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; +import com.avaje.ebeaninternal.server.deploy.BeanTable; +import com.avaje.ebeaninternal.server.deploy.TableJoin; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; + +/** + * Read the deployment annotations for Associated One beans. + */ +public class AnnotationAssocOnes extends AnnotationParser { + + private final BeanDescriptorManager factory; + + /** + * Create with the deploy Info. + */ + public AnnotationAssocOnes(DeployBeanInfo info, BeanDescriptorManager factory) { + super(info); + this.factory = factory; + } + + /** + * Parse the annotation. + */ + public void parse() { + + Iterator it = descriptor.propertiesAll(); + while (it.hasNext()) { + DeployBeanProperty prop = it.next(); + if (prop instanceof DeployBeanPropertyAssocOne) { + readAssocOne((DeployBeanPropertyAssocOne) prop); + } + } + } + + private void readAssocOne(DeployBeanPropertyAssocOne prop) { + + ManyToOne manyToOne = get(prop, ManyToOne.class); + if (manyToOne != null) { + readManyToOne(manyToOne, prop); + } + OneToOne oneToOne = get(prop, OneToOne.class); + if (oneToOne != null) { + readOneToOne(oneToOne, prop); + } + Embedded embedded = get(prop, Embedded.class); + if (embedded != null) { + readEmbedded(embedded, prop); + } + EmbeddedId emId = get(prop, EmbeddedId.class); + if (emId != null) { + prop.setEmbedded(true); + prop.setId(true); + prop.setNullable(false); + } + Column column = get(prop, Column.class); + if (column != null && !isEmpty(column.name())) { + // have this in for AssocOnes used on + // Sql based beans... + prop.setDbColumn(column.name()); + } + + // May as well check for Id. Makes sense to me. + Id id = get(prop, Id.class); + if (id != null) { + prop.setEmbedded(true); + prop.setId(true); + prop.setNullable(false); + } + + Where where = get(prop, Where.class); + if (where != null) { + // not expecting this to be used on assoc one properties + prop.setExtraWhere(where.clause()); + } + + NotNull notNull = get(prop, NotNull.class); + if (notNull != null) { + prop.setNullable(false); + // overrides optional attribute of ManyToOne etc + prop.getTableJoin().setType(TableJoin.JOIN); + } + + // check for manually defined joins + BeanTable beanTable = prop.getBeanTable(); + JoinColumn joinColumn = get(prop, JoinColumn.class); + if (joinColumn != null) { + prop.getTableJoin().addJoinColumn(false, joinColumn, beanTable); + if (!joinColumn.updatable()){ + prop.setDbUpdateable(false); + } + } + + JoinColumns joinColumns = get(prop, JoinColumns.class); + if (joinColumns != null) { + prop.getTableJoin().addJoinColumn(false, joinColumns.value(), beanTable); + } + + JoinTable joinTable = get(prop, JoinTable.class); + if (joinTable != null) { + prop.getTableJoin().addJoinColumn(false, joinTable.joinColumns(), beanTable); + } + + info.setBeanJoinType(prop, prop.isNullable()); + + if (!prop.getTableJoin().hasJoinColumns() && beanTable != null) { + + if (prop.getMappedBy() != null) { + // the join is derived by reversing the join information + // from the mapped by property. + // Refer BeanDescriptorManager.readEntityRelationships() + + } else { + // use naming convention to define join. + NamingConvention nc = factory.getNamingConvention(); + + String fkeyPrefix = null; + if (nc.isUseForeignKeyPrefix()){ + fkeyPrefix = nc.getColumnFromProperty(beanType, prop.getName()); + } + + beanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), true); + } + } + } + + private String errorMsgMissingBeanTable(Class type, String from) { + return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered?"; + } + + private void readManyToOne(ManyToOne propAnn, DeployBeanProperty prop) { + + DeployBeanPropertyAssocOne beanProp = (DeployBeanPropertyAssocOne) prop; + + setCascadeTypes(propAnn.cascade(), beanProp.getCascadeInfo()); + + BeanTable assoc = factory.getBeanTable(beanProp.getPropertyType()); + if (assoc == null) { + String msg = errorMsgMissingBeanTable(beanProp.getPropertyType(), prop.getFullBeanName()); + throw new RuntimeException(msg); + } + beanProp.setBeanTable(assoc); + beanProp.setDbInsertable(true); + beanProp.setDbUpdateable(true); + beanProp.setNullable(propAnn.optional()); + beanProp.setFetchType(propAnn.fetch()); + } + + private void readOneToOne(OneToOne propAnn, DeployBeanPropertyAssocOne prop) { + + prop.setOneToOne(true); + prop.setDbInsertable(true); + prop.setDbUpdateable(true); + prop.setNullable(propAnn.optional()); + prop.setFetchType(propAnn.fetch()); + prop.setMappedBy(propAnn.mappedBy()); + if (!"".equals(propAnn.mappedBy())) { + prop.setOneToOneExported(true); + } + + setCascadeTypes(propAnn.cascade(), prop.getCascadeInfo()); + + BeanTable assoc = factory.getBeanTable(prop.getPropertyType()); + if (assoc == null) { + String msg = errorMsgMissingBeanTable(prop.getPropertyType(), prop.getFullBeanName()); + throw new RuntimeException(msg); + } + + prop.setBeanTable(assoc); + } + + private void readEmbedded(Embedded propAnn, DeployBeanPropertyAssocOne prop) { + + prop.setEmbedded(true); + prop.setDbInsertable(true); + prop.setDbUpdateable(true); + + EmbeddedColumns columns = get(prop, EmbeddedColumns.class); + if (columns != null) { + + // convert into a Map + String propColumns = columns.columns(); + Map propMap = StringHelper.delimitedToMap(propColumns, ",", "="); + + prop.getDeployEmbedded().putAll(propMap); + } + + AttributeOverrides attrOverrides = get(prop, AttributeOverrides.class); + if (attrOverrides != null) { + HashMap propMap = new HashMap(); + AttributeOverride[] aoArray = attrOverrides.value(); + for (int i = 0; i < aoArray.length; i++) { + String propName = aoArray[i].name(); + String columnName = aoArray[i].column().name(); + + propMap.put(propName, columnName); + } + + prop.getDeployEmbedded().putAll(propMap); + } + + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBeanTable.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBeanTable.java index cb8544c1c..1fe7494bd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBeanTable.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationBeanTable.java @@ -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.parse; - -import com.avaje.ebean.config.TableName; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable; - -/** - * Read the annotations for BeanTable. - *

- * Refer to BeanTable but basically determining base table, table alias - * and the unique id properties. - *

- */ -public class AnnotationBeanTable extends AnnotationBase { - - final DeployBeanTable beanTable; - - public AnnotationBeanTable(DeployUtil util, DeployBeanTable beanTable){ - super(util); - this.beanTable = beanTable; - } - - /** - * Parse the annotations. - */ - public void parse() { - - TableName tableName = namingConvention.getTableName(beanTable.getBeanType()); - - beanTable.setBaseTable(tableName.getQualifiedName()); - } -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import com.avaje.ebean.config.TableName; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable; + +/** + * Read the annotations for BeanTable. + *

+ * Refer to BeanTable but basically determining base table, table alias + * and the unique id properties. + *

+ */ +public class AnnotationBeanTable extends AnnotationBase { + + final DeployBeanTable beanTable; + + public AnnotationBeanTable(DeployUtil util, DeployBeanTable beanTable){ + super(util); + this.beanTable = beanTable; + } + + /** + * Parse the annotations. + */ + public void parse() { + + TableName tableName = namingConvention.getTableName(beanTable.getBeanType()); + + beanTable.setBaseTable(tableName.getQualifiedName()); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java index c95eafa19..172d5bd13 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java @@ -1,244 +1,225 @@ -/** - * 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.parse; - -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlType; - -import com.avaje.ebean.Query.UseIndex; -import com.avaje.ebean.annotation.CacheStrategy; -import com.avaje.ebean.annotation.LdapDomain; -import com.avaje.ebean.annotation.NamedUpdate; -import com.avaje.ebean.annotation.NamedUpdates; -import com.avaje.ebean.annotation.UpdateMode; -import com.avaje.ebean.config.TableName; -import com.avaje.ebeaninternal.server.core.CacheOptions; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; -import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; -import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; -import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; - -/** - * Read the class level deployment annotations. - */ -public class AnnotationClass extends AnnotationParser { - - public AnnotationClass(DeployBeanInfo info) { - super(info); - } - - /** - * Read the class level deployment annotations. - */ - public void parse() { - read(descriptor.getBeanType()); - setTableName(); - } - - /** - * Set the table name if it has not already been set. - */ - private void setTableName() { - - if (descriptor.isBaseTableType()) { - - // default the TableName using NamingConvention. - TableName tableName = namingConvention.getTableName(descriptor.getBeanType()); - - descriptor.setBaseTable(tableName); - } - } - - private String[] parseLdapObjectclasses(String objectclasses) { - - if (objectclasses == null || objectclasses.length() == 0){ - return null; - } - return objectclasses.split(","); - } - - private boolean isXmlElement(Class cls) { - XmlRootElement rootElement = cls.getAnnotation(XmlRootElement.class); - if (rootElement != null){ - return true; - } - XmlType xmlType = cls.getAnnotation(XmlType.class); - if (xmlType != null){ - return true; - } - return false; - } - - private void read(Class cls) { - - LdapDomain ldapDomain = cls.getAnnotation(LdapDomain.class); - if (ldapDomain != null) { - descriptor.setName(cls.getSimpleName()); - descriptor.setEntityType(EntityType.LDAP); - descriptor.setLdapBaseDn(ldapDomain.baseDn()); - descriptor.setLdapObjectclasses(parseLdapObjectclasses(ldapDomain.objectclass())); - } - - Entity entity = cls.getAnnotation(Entity.class); - if (entity != null){ - //checkDefaultConstructor(); - if (entity.name().equals("")) { - descriptor.setName(cls.getSimpleName()); - - } else { - descriptor.setName(entity.name()); - } - } else if (isXmlElement(cls)) { - descriptor.setName(cls.getSimpleName()); - descriptor.setEntityType(EntityType.XMLELEMENT); - } - - Embeddable embeddable = cls.getAnnotation(Embeddable.class); - if (embeddable != null){ - descriptor.setEntityType(EntityType.EMBEDDED); - descriptor.setName("Embeddable:"+cls.getSimpleName()); - } - - UniqueConstraint uc = cls.getAnnotation(UniqueConstraint.class); - if (uc != null){ - descriptor.addCompoundUniqueConstraint(new CompoundUniqueContraint(uc.columnNames())); - } - - Table table = cls.getAnnotation(Table.class); - if (table != null){ - UniqueConstraint[] uniqueConstraints = table.uniqueConstraints(); - if (uniqueConstraints != null){ - for (UniqueConstraint c : uniqueConstraints) { - descriptor.addCompoundUniqueConstraint(new CompoundUniqueContraint(c.columnNames())); - } - } - } - - UpdateMode updateMode = cls.getAnnotation(UpdateMode.class); - if (updateMode != null){ - descriptor.setUpdateChangesOnly(updateMode.updateChangesOnly()); - } - - NamedQueries namedQueries = cls.getAnnotation(NamedQueries.class); - if (namedQueries != null){ - readNamedQueries(namedQueries); - } - NamedQuery namedQuery = cls.getAnnotation(NamedQuery.class); - if (namedQuery != null){ - readNamedQuery(namedQuery); - } - - NamedUpdates namedUpdates = cls.getAnnotation(NamedUpdates.class); - if (namedUpdates != null){ - readNamedUpdates(namedUpdates); - } - - NamedUpdate namedUpdate = cls.getAnnotation(NamedUpdate.class); - if (namedUpdate != null){ - readNamedUpdate(namedUpdate); - } - - CacheStrategy cacheStrategy = cls.getAnnotation(CacheStrategy.class); - if (cacheStrategy != null){ - readCacheStrategy(cacheStrategy); - } - } - - private void readCacheStrategy(CacheStrategy cacheStrategy){ - - CacheOptions cacheOptions = descriptor.getCacheOptions(); - cacheOptions.setUseCache(cacheStrategy.useBeanCache()); - cacheOptions.setReadOnly(cacheStrategy.readOnly()); - cacheOptions.setWarmingQuery(cacheStrategy.warmingQuery()); - if (cacheStrategy.naturalKey().length() > 0){ - String propName = cacheStrategy.naturalKey().trim(); - DeployBeanProperty beanProperty = descriptor.getBeanProperty(propName); - if (beanProperty != null){ - beanProperty.setNaturalKey(true); - cacheOptions.setNaturalKey(propName); - } - } - - if (!UseIndex.DEFAULT.equals(cacheStrategy.useIndex())){ - // a specific text index strategy has been defined - descriptor.setUseIndex(cacheStrategy.useIndex()); - } - } - - private void readNamedQueries(NamedQueries namedQueries) { - NamedQuery[] queries = namedQueries.value(); - for (int i = 0; i < queries.length; i++) { - readNamedQuery(queries[i]); - } - } - - private void readNamedQuery(NamedQuery namedQuery) { - DeployNamedQuery q = new DeployNamedQuery(namedQuery); - descriptor.add(q); - } - - private void readNamedUpdates(NamedUpdates updates) { - NamedUpdate[] updateArray = updates.value(); - for (int i = 0; i < updateArray.length; i++) { - readNamedUpdate(updateArray[i]); - } - } - - private void readNamedUpdate(NamedUpdate update) { - DeployNamedUpdate upd = new DeployNamedUpdate(update); - descriptor.add(upd); - } - -// /** -// * Check to see if the Entity bean has a default constructor. -// *

-// * If it does not then it is expected that this entity bean has an -// * associated BeanFinder. -// *

-// */ -// private void checkDefaultConstructor() { -// -// Class beanType = descriptor.getBeanType(); -// -// Constructor defaultConstructor; -// try { -// defaultConstructor = beanType.getConstructor((Class[]) null); -// if (defaultConstructor == null) { -// String m = "No default constructor on "+beanType; -// throw new PersistenceException(m); -// } -// } catch (SecurityException e) { -// String m = "Error checking for default constructor on "+beanType; -// throw new PersistenceException(m, e); -// -// } catch (NoSuchMethodException e) { -// String m = "No default constructor on "+beanType; -// throw new PersistenceException(m); -// } -// } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import javax.persistence.Embeddable; +import javax.persistence.Entity; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +import com.avaje.ebean.Query.UseIndex; +import com.avaje.ebean.annotation.CacheStrategy; +import com.avaje.ebean.annotation.LdapDomain; +import com.avaje.ebean.annotation.NamedUpdate; +import com.avaje.ebean.annotation.NamedUpdates; +import com.avaje.ebean.annotation.UpdateMode; +import com.avaje.ebean.config.TableName; +import com.avaje.ebeaninternal.server.core.CacheOptions; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; +import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; + +/** + * Read the class level deployment annotations. + */ +public class AnnotationClass extends AnnotationParser { + + public AnnotationClass(DeployBeanInfo info) { + super(info); + } + + /** + * Read the class level deployment annotations. + */ + public void parse() { + read(descriptor.getBeanType()); + setTableName(); + } + + /** + * Set the table name if it has not already been set. + */ + private void setTableName() { + + if (descriptor.isBaseTableType()) { + + // default the TableName using NamingConvention. + TableName tableName = namingConvention.getTableName(descriptor.getBeanType()); + + descriptor.setBaseTable(tableName); + } + } + + private String[] parseLdapObjectclasses(String objectclasses) { + + if (objectclasses == null || objectclasses.length() == 0){ + return null; + } + return objectclasses.split(","); + } + + private boolean isXmlElement(Class cls) { + XmlRootElement rootElement = cls.getAnnotation(XmlRootElement.class); + if (rootElement != null){ + return true; + } + XmlType xmlType = cls.getAnnotation(XmlType.class); + if (xmlType != null){ + return true; + } + return false; + } + + private void read(Class cls) { + + LdapDomain ldapDomain = cls.getAnnotation(LdapDomain.class); + if (ldapDomain != null) { + descriptor.setName(cls.getSimpleName()); + descriptor.setEntityType(EntityType.LDAP); + descriptor.setLdapBaseDn(ldapDomain.baseDn()); + descriptor.setLdapObjectclasses(parseLdapObjectclasses(ldapDomain.objectclass())); + } + + Entity entity = cls.getAnnotation(Entity.class); + if (entity != null){ + //checkDefaultConstructor(); + if (entity.name().equals("")) { + descriptor.setName(cls.getSimpleName()); + + } else { + descriptor.setName(entity.name()); + } + } else if (isXmlElement(cls)) { + descriptor.setName(cls.getSimpleName()); + descriptor.setEntityType(EntityType.XMLELEMENT); + } + + Embeddable embeddable = cls.getAnnotation(Embeddable.class); + if (embeddable != null){ + descriptor.setEntityType(EntityType.EMBEDDED); + descriptor.setName("Embeddable:"+cls.getSimpleName()); + } + + UniqueConstraint uc = cls.getAnnotation(UniqueConstraint.class); + if (uc != null){ + descriptor.addCompoundUniqueConstraint(new CompoundUniqueContraint(uc.columnNames())); + } + + Table table = cls.getAnnotation(Table.class); + if (table != null){ + UniqueConstraint[] uniqueConstraints = table.uniqueConstraints(); + if (uniqueConstraints != null){ + for (UniqueConstraint c : uniqueConstraints) { + descriptor.addCompoundUniqueConstraint(new CompoundUniqueContraint(c.columnNames())); + } + } + } + + UpdateMode updateMode = cls.getAnnotation(UpdateMode.class); + if (updateMode != null){ + descriptor.setUpdateChangesOnly(updateMode.updateChangesOnly()); + } + + NamedQueries namedQueries = cls.getAnnotation(NamedQueries.class); + if (namedQueries != null){ + readNamedQueries(namedQueries); + } + NamedQuery namedQuery = cls.getAnnotation(NamedQuery.class); + if (namedQuery != null){ + readNamedQuery(namedQuery); + } + + NamedUpdates namedUpdates = cls.getAnnotation(NamedUpdates.class); + if (namedUpdates != null){ + readNamedUpdates(namedUpdates); + } + + NamedUpdate namedUpdate = cls.getAnnotation(NamedUpdate.class); + if (namedUpdate != null){ + readNamedUpdate(namedUpdate); + } + + CacheStrategy cacheStrategy = cls.getAnnotation(CacheStrategy.class); + if (cacheStrategy != null){ + readCacheStrategy(cacheStrategy); + } + } + + private void readCacheStrategy(CacheStrategy cacheStrategy){ + + CacheOptions cacheOptions = descriptor.getCacheOptions(); + cacheOptions.setUseCache(cacheStrategy.useBeanCache()); + cacheOptions.setReadOnly(cacheStrategy.readOnly()); + cacheOptions.setWarmingQuery(cacheStrategy.warmingQuery()); + if (cacheStrategy.naturalKey().length() > 0){ + String propName = cacheStrategy.naturalKey().trim(); + DeployBeanProperty beanProperty = descriptor.getBeanProperty(propName); + if (beanProperty != null){ + beanProperty.setNaturalKey(true); + cacheOptions.setNaturalKey(propName); + } + } + + if (!UseIndex.DEFAULT.equals(cacheStrategy.useIndex())){ + // a specific text index strategy has been defined + descriptor.setUseIndex(cacheStrategy.useIndex()); + } + } + + private void readNamedQueries(NamedQueries namedQueries) { + NamedQuery[] queries = namedQueries.value(); + for (int i = 0; i < queries.length; i++) { + readNamedQuery(queries[i]); + } + } + + private void readNamedQuery(NamedQuery namedQuery) { + DeployNamedQuery q = new DeployNamedQuery(namedQuery); + descriptor.add(q); + } + + private void readNamedUpdates(NamedUpdates updates) { + NamedUpdate[] updateArray = updates.value(); + for (int i = 0; i < updateArray.length; i++) { + readNamedUpdate(updateArray[i]); + } + } + + private void readNamedUpdate(NamedUpdate update) { + DeployNamedUpdate upd = new DeployNamedUpdate(update); + descriptor.add(upd); + } + +// /** +// * Check to see if the Entity bean has a default constructor. +// *

+// * If it does not then it is expected that this entity bean has an +// * associated BeanFinder. +// *

+// */ +// private void checkDefaultConstructor() { +// +// Class beanType = descriptor.getBeanType(); +// +// Constructor defaultConstructor; +// try { +// defaultConstructor = beanType.getConstructor((Class[]) null); +// if (defaultConstructor == null) { +// String m = "No default constructor on "+beanType; +// throw new PersistenceException(m); +// } +// } catch (SecurityException e) { +// String m = "Error checking for default constructor on "+beanType; +// throw new PersistenceException(m, e); +// +// } catch (NoSuchMethodException e) { +// String m = "No default constructor on "+beanType; +// throw new PersistenceException(m); +// } +// } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java index 6adeb27af..ff3f83f05 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java @@ -1,527 +1,508 @@ -/** - * 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.parse; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.sql.Types; -import java.util.Iterator; -import java.util.Map; -import java.util.UUID; - -import javax.persistence.Basic; -import javax.persistence.Column; -import javax.persistence.EmbeddedId; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Lob; -import javax.persistence.PersistenceException; -import javax.persistence.SequenceGenerator; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Transient; -import javax.persistence.Version; - -import com.avaje.ebean.annotation.CreatedTimestamp; -import com.avaje.ebean.annotation.EmbeddedColumns; -import com.avaje.ebean.annotation.Encrypted; -import com.avaje.ebean.annotation.Formula; -import com.avaje.ebean.annotation.LdapAttribute; -import com.avaje.ebean.annotation.LdapId; -import com.avaje.ebean.annotation.UpdatedTimestamp; -import com.avaje.ebean.config.EncryptDeploy; -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebean.config.EncryptDeploy.Mode; -import com.avaje.ebean.config.dbplatform.DbEncrypt; -import com.avaje.ebean.config.dbplatform.DbEncryptFunction; -import com.avaje.ebean.config.dbplatform.IdType; -import com.avaje.ebean.validation.Length; -import com.avaje.ebean.validation.NotNull; -import com.avaje.ebean.validation.Pattern; -import com.avaje.ebean.validation.Patterns; -import com.avaje.ebean.validation.ValidatorMeta; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; -import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; -import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; -import com.avaje.ebeaninternal.server.type.CtCompoundType; -import com.avaje.ebeaninternal.server.type.DataEncryptSupport; -import com.avaje.ebeaninternal.server.type.ScalarType; -import com.avaje.ebeaninternal.server.type.ScalarTypeBytesBase; -import com.avaje.ebeaninternal.server.type.ScalarTypeBytesEncrypted; -import com.avaje.ebeaninternal.server.type.ScalarTypeEncryptedWrapper; -import com.avaje.ebeaninternal.server.type.ScalarTypeLdapBoolean; -import com.avaje.ebeaninternal.server.type.ScalarTypeLdapDate; -import com.avaje.ebeaninternal.server.type.ScalarTypeLdapTimestamp; - -/** - * Read the field level deployment annotations. - */ -public class AnnotationFields extends AnnotationParser { - - /** - * By default we lazy load Lob properties. - */ - private FetchType defaultLobFetchType = FetchType.LAZY; - - private GeneratedPropertyFactory generatedPropFactory = new GeneratedPropertyFactory(); - - public AnnotationFields(DeployBeanInfo info) { - super(info); - - if (GlobalProperties.getBoolean("ebean.lobEagerFetch", false)) { - defaultLobFetchType = FetchType.EAGER; - } - } - - /** - * Read the field level deployment annotations. - */ - public void parse() { - - Iterator it = descriptor.propertiesAll(); - while (it.hasNext()) { - DeployBeanProperty prop = it.next(); - if (prop instanceof DeployBeanPropertyAssoc) { - readAssocOne(prop); - } else { - readField(prop); - } - - readValidations(prop); - } - } - - /** - * Read the Id marker annotations on EmbeddedId properties. - */ - private void readAssocOne(DeployBeanProperty prop) { - - Id id = get(prop, Id.class); - if (id != null) { - prop.setId(true); - prop.setNullable(false); - } - - EmbeddedId embeddedId = get(prop, EmbeddedId.class); - if (embeddedId != null) { - prop.setId(true); - prop.setNullable(false); - prop.setEmbedded(true); - } - - } - - private void readField(DeployBeanProperty prop) { - - // all Enums will have a ScalarType assigned... - boolean isEnum = prop.getPropertyType().isEnum(); - Enumerated enumerated = get(prop, Enumerated.class); - if (isEnum || enumerated != null) { - util.setEnumScalarType(enumerated, prop); - } - - // its persistent and assumed to be on the base table - // rather than on a secondary table - prop.setDbRead(true); - prop.setDbInsertable(true); - prop.setDbUpdateable(true); - - Column column = get(prop, Column.class); - if (column != null) { - readColumn(column, prop); - } - LdapAttribute ldapAttribute = get(prop, LdapAttribute.class); - if (ldapAttribute != null) { - // read ldap specific property settings - readLdapAttribute(ldapAttribute, prop); - } - - if (prop.getDbColumn() == null){ - if (EntityType.LDAP.equals(descriptor.getEntityType())) { - // just use matching for now. Could consider an LdapNamingConvention later. - prop.setDbColumn(prop.getName()); - } else { - // No @Column annotation or @Column.name() not set - // Use the NamingConvention to set the DB column name - String dbColumn = namingConvention.getColumnFromProperty(beanType, prop.getName()); - prop.setDbColumn(dbColumn); - } - } - - GeneratedValue gen = get(prop, GeneratedValue.class); - if (gen != null) { - readGenValue(gen, prop); - } - - Id id = (Id) get(prop, Id.class); - if (id != null) { - readId(id, prop); - } - LdapId ldapId = (LdapId)get(prop, LdapId.class); - if (ldapId != null) { - prop.setId(true); - prop.setNullable(false); - } - - - // determine the JDBC type using Lob/Temporal - // otherwise based on the property Class - Lob lob = get(prop, Lob.class); - Temporal temporal = get(prop, Temporal.class); - if (temporal != null) { - readTemporal(temporal, prop); - - } else if (lob != null) { - util.setLobType(prop); - } - - Formula formula = get(prop, Formula.class); - if (formula != null) { - prop.setSqlFormula(formula.select(), formula.join()); - } - - Version version = get(prop, Version.class); - if (version != null) { - // explicitly specify a version column - prop.setVersionColumn(true); - generatedPropFactory.setVersion(prop); - } - - Basic basic = get(prop, Basic.class); - if (basic != null) { - prop.setFetchType(basic.fetch()); - if (!basic.optional()) { - prop.setNullable(false); - } - } else if (prop.isLob()){ - // use the default Lob fetchType - prop.setFetchType(defaultLobFetchType); - } - - CreatedTimestamp ct = get(prop, CreatedTimestamp.class); - if (ct != null) { - generatedPropFactory.setInsertTimestamp(prop); - } - - UpdatedTimestamp ut = get(prop, UpdatedTimestamp.class); - if (ut != null) { - generatedPropFactory.setUpdateTimestamp(prop); - } - - NotNull notNull = get(prop, NotNull.class); - if (notNull != null) { - // explicitly specify a version column - prop.setNullable(false); - } - - Length length = get(prop, Length.class); - if (length != null) { - if (length.max() < Integer.MAX_VALUE){ - // explicitly specify a version column - prop.setDbLength(length.max()); - } - } - - EmbeddedColumns columns = get(prop, EmbeddedColumns.class); - if (columns != null) { - if (prop instanceof DeployBeanPropertyCompound){ - DeployBeanPropertyCompound p = (DeployBeanPropertyCompound)prop; - - // convert into a Map - String propColumns = columns.columns(); - Map propMap = StringHelper.delimitedToMap(propColumns, ",", "="); - - p.getDeployEmbedded().putAll(propMap); - - CtCompoundType compoundType = p.getCompoundType(); - if (compoundType == null){ - throw new RuntimeException("No registered CtCompoundType for "+p.getPropertyType()); - } - - } else { - throw new RuntimeException("Can't use EmbeddedColumns on ScalarType "+prop.getFullBeanName()); - } - } - - // Want to process last so we can use with @Formula - Transient t = get(prop, Transient.class); - if (t != null) { - // it is not a persistent property. - prop.setDbRead(false); - prop.setDbInsertable(false); - prop.setDbUpdateable(false); - prop.setTransient(true); - } - - if (!prop.isTransient()){ - - EncryptDeploy encryptDeploy = util.getEncryptDeploy(info.getDescriptor().getBaseTableFull(), prop.getDbColumn()); - if (encryptDeploy == null || encryptDeploy.getMode().equals(Mode.MODE_ANNOTATION)){ - Encrypted encrypted = get(prop, Encrypted.class); - if (encrypted != null) { - setEncryption(prop, encrypted.dbEncryption(), encrypted.dbLength()); - } - } else if (Mode.MODE_ENCRYPT.equals(encryptDeploy.getMode())) { - setEncryption(prop, encryptDeploy.isDbEncrypt(), encryptDeploy.getDbLength()); - } - } - - if (EntityType.LDAP.equals(descriptor.getEntityType())){ - adjustTypesForLdap(prop); - } - } - - private static final ScalarTypeLdapBoolean LDAP_BOOLEAN_SCALARTYPE = new ScalarTypeLdapBoolean(); - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private void adjustTypesForLdap(DeployBeanProperty prop) { - - Class pt = prop.getPropertyType(); - if (boolean.class.equals(pt) || Boolean.class.equals(pt)){ - prop.setScalarType(LDAP_BOOLEAN_SCALARTYPE); - - } else { - ScalarType sqlScalarType = prop.getScalarType(); - int sqlType = sqlScalarType.getJdbcType(); - if (sqlType == Types.TIMESTAMP){ - // Use LDAP Timestamp String format - prop.setScalarType(new ScalarTypeLdapTimestamp(sqlScalarType)); - - } else if (sqlType == Types.DATE){ - // Use LDAP Timestamp String format - prop.setScalarType(new ScalarTypeLdapDate(sqlScalarType)); - - } else { - // Just using string parsing for all other types - } - } - } - - private void setEncryption(DeployBeanProperty prop, boolean dbEncString, int dbLen) { - - util.checkEncryptKeyManagerDefined(prop.getFullBeanName()); - - ScalarType st = prop.getScalarType(); - if (byte[].class.equals(st.getType())){ - // Always using Java client encryption rather than DB for encryption - // of binary data (partially as this is not supported on all db's etc) - // This could be reviewed at a later stage. - ScalarTypeBytesBase baseType = (ScalarTypeBytesBase)st; - DataEncryptSupport support = createDataEncryptSupport(prop); - ScalarTypeBytesEncrypted encryptedScalarType = new ScalarTypeBytesEncrypted(baseType, support); - prop.setScalarType(encryptedScalarType); - prop.setLocalEncrypted(true); - return; - - } - if (dbEncString){ - - DbEncrypt dbEncrypt = util.getDbPlatform().getDbEncrypt(); - - if (dbEncrypt != null){ - // check if we have a DB encryption function for this type - int jdbcType = prop.getScalarType().getJdbcType(); - DbEncryptFunction dbEncryptFunction = dbEncrypt.getDbEncryptFunction(jdbcType); - if (dbEncryptFunction != null){ - // Use DB functions to encrypt and decrypt - prop.setDbEncryptFunction(dbEncryptFunction, dbEncrypt, dbLen); - return; - } - } - } - - prop.setScalarType(createScalarType(prop, st)); - prop.setLocalEncrypted(true); - if (dbLen > 0){ - prop.setDbLength(dbLen); - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private ScalarTypeEncryptedWrapper createScalarType(DeployBeanProperty prop, ScalarType st ) { - - // Use Java Encryptor wrapping the logical scalar type - DataEncryptSupport support = createDataEncryptSupport(prop); - ScalarTypeBytesBase byteType = getDbEncryptType(prop); - - return new ScalarTypeEncryptedWrapper(st, byteType, support); - } - - private ScalarTypeBytesBase getDbEncryptType(DeployBeanProperty prop) { - int dbType = prop.isLob() ? Types.BLOB : Types.VARBINARY; - return (ScalarTypeBytesBase)util.getTypeManager().getScalarType(dbType); - } - - private DataEncryptSupport createDataEncryptSupport(DeployBeanProperty prop) { - - String table = info.getDescriptor().getBaseTable(); - String column = prop.getDbColumn(); - - return util.createDataEncryptSupport(table, column); - } - - - private void readId(Id id, DeployBeanProperty prop) { - - prop.setId(true); - prop.setNullable(false); - - if (prop.getPropertyType().equals(UUID.class)){ - // An Id of type UUID - if (descriptor.getIdGeneratorName() == null){ - // Without a generator explicitly specified - // so will use the default one AUTO_UUID - descriptor.setIdGeneratorName(UuidIdGenerator.AUTO_UUID); - descriptor.setIdType(IdType.GENERATOR); - } - } - } - - private void readGenValue(GeneratedValue gen, DeployBeanProperty prop) { - - String genName = gen.generator(); - - SequenceGenerator sequenceGenerator = find(prop, SequenceGenerator.class); - if (sequenceGenerator != null) { - if (sequenceGenerator.name().equals(genName)) { - genName = sequenceGenerator.sequenceName(); - } - } - - GenerationType strategy = gen.strategy(); - - if (strategy == GenerationType.IDENTITY) { - descriptor.setIdType(IdType.IDENTITY); - - } else if (strategy == GenerationType.SEQUENCE) { - descriptor.setIdType(IdType.SEQUENCE); - if (genName != null && genName.length() > 0) { - descriptor.setIdGeneratorName(genName); - } - - } else if (strategy == GenerationType.AUTO) { - if (prop.getPropertyType().equals(UUID.class)){ - descriptor.setIdGeneratorName(UuidIdGenerator.AUTO_UUID); - descriptor.setIdType(IdType.GENERATOR); - - } else { - // use DatabasePlatform defaults - } - } - } - - private void readTemporal(Temporal temporal, DeployBeanProperty prop) { - - TemporalType type = temporal.value(); - if (type.equals(TemporalType.DATE)) { - prop.setDbType(Types.DATE); - - } else if (type.equals(TemporalType.TIMESTAMP)) { - prop.setDbType(Types.TIMESTAMP); - - } else if (type.equals(TemporalType.TIME)) { - prop.setDbType(Types.TIME); - - } else { - throw new PersistenceException("Unhandled type " + type); - } - } - - - private void readColumn(Column columnAnn, DeployBeanProperty prop) { - - if (!isEmpty(columnAnn.name())){ - String dbColumn = databasePlatform.convertQuotedIdentifiers(columnAnn.name()); - prop.setDbColumn(dbColumn); - } - - prop.setDbInsertable(columnAnn.insertable()); - prop.setDbUpdateable(columnAnn.updatable()); - prop.setNullable(columnAnn.nullable()); - prop.setUnique(columnAnn.unique()); - if (columnAnn.precision() > 0){ - prop.setDbLength(columnAnn.precision()); - } else if (columnAnn.length() != 255){ - // set default 255 on DbTypeMap - prop.setDbLength(columnAnn.length()); - } - prop.setDbScale(columnAnn.scale()); - prop.setDbColumnDefn(columnAnn.columnDefinition()); - - String baseTable = descriptor.getBaseTable(); - String tableName = columnAnn.table(); - if (tableName.equals("") || tableName.equalsIgnoreCase(baseTable)) { - // its a base table property... - } else { - // its on a secondary table... - prop.setSecondaryTable(tableName); - //DeployTableJoin tableJoin = info.getTableJoin(tableName); - //tableJoin.addProperty(prop); - } - } - - - - private void readValidations(DeployBeanProperty prop) { - - Field field = prop.getField(); - if (field != null) { - Annotation[] fieldAnnotations = field.getAnnotations(); - for (int i = 0; i < fieldAnnotations.length; i++) { - readValidations(prop, fieldAnnotations[i]); - } - } - - Method readMethod = prop.getReadMethod(); - if (readMethod != null) { - Annotation[] methAnnotations = readMethod.getAnnotations(); - for (int i = 0; i < methAnnotations.length; i++) { - readValidations(prop, methAnnotations[i]); - } - } - } - - private void readValidations(DeployBeanProperty prop, Annotation ann) { - Class type = ann.annotationType(); - if (type.equals(Patterns.class)){ - // treating this as a special case for now... - Patterns patterns = (Patterns)ann; - Pattern[] patternsArray = patterns.patterns(); - for (int i = 0; i < patternsArray.length; i++) { - util.createValidator(prop, patternsArray[i]); - } - - } else { - - ValidatorMeta meta = type.getAnnotation(ValidatorMeta.class); - if (meta != null) { - util.createValidator(prop, ann); - } - } - } -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.sql.Types; +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; + +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.EmbeddedId; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Lob; +import javax.persistence.PersistenceException; +import javax.persistence.SequenceGenerator; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.persistence.Transient; +import javax.persistence.Version; + +import com.avaje.ebean.annotation.CreatedTimestamp; +import com.avaje.ebean.annotation.EmbeddedColumns; +import com.avaje.ebean.annotation.Encrypted; +import com.avaje.ebean.annotation.Formula; +import com.avaje.ebean.annotation.LdapAttribute; +import com.avaje.ebean.annotation.LdapId; +import com.avaje.ebean.annotation.UpdatedTimestamp; +import com.avaje.ebean.config.EncryptDeploy; +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebean.config.EncryptDeploy.Mode; +import com.avaje.ebean.config.dbplatform.DbEncrypt; +import com.avaje.ebean.config.dbplatform.DbEncryptFunction; +import com.avaje.ebean.config.dbplatform.IdType; +import com.avaje.ebean.validation.Length; +import com.avaje.ebean.validation.NotNull; +import com.avaje.ebean.validation.Pattern; +import com.avaje.ebean.validation.Patterns; +import com.avaje.ebean.validation.ValidatorMeta; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; +import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; +import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import com.avaje.ebeaninternal.server.type.CtCompoundType; +import com.avaje.ebeaninternal.server.type.DataEncryptSupport; +import com.avaje.ebeaninternal.server.type.ScalarType; +import com.avaje.ebeaninternal.server.type.ScalarTypeBytesBase; +import com.avaje.ebeaninternal.server.type.ScalarTypeBytesEncrypted; +import com.avaje.ebeaninternal.server.type.ScalarTypeEncryptedWrapper; +import com.avaje.ebeaninternal.server.type.ScalarTypeLdapBoolean; +import com.avaje.ebeaninternal.server.type.ScalarTypeLdapDate; +import com.avaje.ebeaninternal.server.type.ScalarTypeLdapTimestamp; + +/** + * Read the field level deployment annotations. + */ +public class AnnotationFields extends AnnotationParser { + + /** + * By default we lazy load Lob properties. + */ + private FetchType defaultLobFetchType = FetchType.LAZY; + + private GeneratedPropertyFactory generatedPropFactory = new GeneratedPropertyFactory(); + + public AnnotationFields(DeployBeanInfo info) { + super(info); + + if (GlobalProperties.getBoolean("ebean.lobEagerFetch", false)) { + defaultLobFetchType = FetchType.EAGER; + } + } + + /** + * Read the field level deployment annotations. + */ + public void parse() { + + Iterator it = descriptor.propertiesAll(); + while (it.hasNext()) { + DeployBeanProperty prop = it.next(); + if (prop instanceof DeployBeanPropertyAssoc) { + readAssocOne(prop); + } else { + readField(prop); + } + + readValidations(prop); + } + } + + /** + * Read the Id marker annotations on EmbeddedId properties. + */ + private void readAssocOne(DeployBeanProperty prop) { + + Id id = get(prop, Id.class); + if (id != null) { + prop.setId(true); + prop.setNullable(false); + } + + EmbeddedId embeddedId = get(prop, EmbeddedId.class); + if (embeddedId != null) { + prop.setId(true); + prop.setNullable(false); + prop.setEmbedded(true); + } + + } + + private void readField(DeployBeanProperty prop) { + + // all Enums will have a ScalarType assigned... + boolean isEnum = prop.getPropertyType().isEnum(); + Enumerated enumerated = get(prop, Enumerated.class); + if (isEnum || enumerated != null) { + util.setEnumScalarType(enumerated, prop); + } + + // its persistent and assumed to be on the base table + // rather than on a secondary table + prop.setDbRead(true); + prop.setDbInsertable(true); + prop.setDbUpdateable(true); + + Column column = get(prop, Column.class); + if (column != null) { + readColumn(column, prop); + } + LdapAttribute ldapAttribute = get(prop, LdapAttribute.class); + if (ldapAttribute != null) { + // read ldap specific property settings + readLdapAttribute(ldapAttribute, prop); + } + + if (prop.getDbColumn() == null){ + if (EntityType.LDAP.equals(descriptor.getEntityType())) { + // just use matching for now. Could consider an LdapNamingConvention later. + prop.setDbColumn(prop.getName()); + } else { + // No @Column annotation or @Column.name() not set + // Use the NamingConvention to set the DB column name + String dbColumn = namingConvention.getColumnFromProperty(beanType, prop.getName()); + prop.setDbColumn(dbColumn); + } + } + + GeneratedValue gen = get(prop, GeneratedValue.class); + if (gen != null) { + readGenValue(gen, prop); + } + + Id id = (Id) get(prop, Id.class); + if (id != null) { + readId(id, prop); + } + LdapId ldapId = (LdapId)get(prop, LdapId.class); + if (ldapId != null) { + prop.setId(true); + prop.setNullable(false); + } + + + // determine the JDBC type using Lob/Temporal + // otherwise based on the property Class + Lob lob = get(prop, Lob.class); + Temporal temporal = get(prop, Temporal.class); + if (temporal != null) { + readTemporal(temporal, prop); + + } else if (lob != null) { + util.setLobType(prop); + } + + Formula formula = get(prop, Formula.class); + if (formula != null) { + prop.setSqlFormula(formula.select(), formula.join()); + } + + Version version = get(prop, Version.class); + if (version != null) { + // explicitly specify a version column + prop.setVersionColumn(true); + generatedPropFactory.setVersion(prop); + } + + Basic basic = get(prop, Basic.class); + if (basic != null) { + prop.setFetchType(basic.fetch()); + if (!basic.optional()) { + prop.setNullable(false); + } + } else if (prop.isLob()){ + // use the default Lob fetchType + prop.setFetchType(defaultLobFetchType); + } + + CreatedTimestamp ct = get(prop, CreatedTimestamp.class); + if (ct != null) { + generatedPropFactory.setInsertTimestamp(prop); + } + + UpdatedTimestamp ut = get(prop, UpdatedTimestamp.class); + if (ut != null) { + generatedPropFactory.setUpdateTimestamp(prop); + } + + NotNull notNull = get(prop, NotNull.class); + if (notNull != null) { + // explicitly specify a version column + prop.setNullable(false); + } + + Length length = get(prop, Length.class); + if (length != null) { + if (length.max() < Integer.MAX_VALUE){ + // explicitly specify a version column + prop.setDbLength(length.max()); + } + } + + EmbeddedColumns columns = get(prop, EmbeddedColumns.class); + if (columns != null) { + if (prop instanceof DeployBeanPropertyCompound){ + DeployBeanPropertyCompound p = (DeployBeanPropertyCompound)prop; + + // convert into a Map + String propColumns = columns.columns(); + Map propMap = StringHelper.delimitedToMap(propColumns, ",", "="); + + p.getDeployEmbedded().putAll(propMap); + + CtCompoundType compoundType = p.getCompoundType(); + if (compoundType == null){ + throw new RuntimeException("No registered CtCompoundType for "+p.getPropertyType()); + } + + } else { + throw new RuntimeException("Can't use EmbeddedColumns on ScalarType "+prop.getFullBeanName()); + } + } + + // Want to process last so we can use with @Formula + Transient t = get(prop, Transient.class); + if (t != null) { + // it is not a persistent property. + prop.setDbRead(false); + prop.setDbInsertable(false); + prop.setDbUpdateable(false); + prop.setTransient(true); + } + + if (!prop.isTransient()){ + + EncryptDeploy encryptDeploy = util.getEncryptDeploy(info.getDescriptor().getBaseTableFull(), prop.getDbColumn()); + if (encryptDeploy == null || encryptDeploy.getMode().equals(Mode.MODE_ANNOTATION)){ + Encrypted encrypted = get(prop, Encrypted.class); + if (encrypted != null) { + setEncryption(prop, encrypted.dbEncryption(), encrypted.dbLength()); + } + } else if (Mode.MODE_ENCRYPT.equals(encryptDeploy.getMode())) { + setEncryption(prop, encryptDeploy.isDbEncrypt(), encryptDeploy.getDbLength()); + } + } + + if (EntityType.LDAP.equals(descriptor.getEntityType())){ + adjustTypesForLdap(prop); + } + } + + private static final ScalarTypeLdapBoolean LDAP_BOOLEAN_SCALARTYPE = new ScalarTypeLdapBoolean(); + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void adjustTypesForLdap(DeployBeanProperty prop) { + + Class pt = prop.getPropertyType(); + if (boolean.class.equals(pt) || Boolean.class.equals(pt)){ + prop.setScalarType(LDAP_BOOLEAN_SCALARTYPE); + + } else { + ScalarType sqlScalarType = prop.getScalarType(); + int sqlType = sqlScalarType.getJdbcType(); + if (sqlType == Types.TIMESTAMP){ + // Use LDAP Timestamp String format + prop.setScalarType(new ScalarTypeLdapTimestamp(sqlScalarType)); + + } else if (sqlType == Types.DATE){ + // Use LDAP Timestamp String format + prop.setScalarType(new ScalarTypeLdapDate(sqlScalarType)); + + } else { + // Just using string parsing for all other types + } + } + } + + private void setEncryption(DeployBeanProperty prop, boolean dbEncString, int dbLen) { + + util.checkEncryptKeyManagerDefined(prop.getFullBeanName()); + + ScalarType st = prop.getScalarType(); + if (byte[].class.equals(st.getType())){ + // Always using Java client encryption rather than DB for encryption + // of binary data (partially as this is not supported on all db's etc) + // This could be reviewed at a later stage. + ScalarTypeBytesBase baseType = (ScalarTypeBytesBase)st; + DataEncryptSupport support = createDataEncryptSupport(prop); + ScalarTypeBytesEncrypted encryptedScalarType = new ScalarTypeBytesEncrypted(baseType, support); + prop.setScalarType(encryptedScalarType); + prop.setLocalEncrypted(true); + return; + + } + if (dbEncString){ + + DbEncrypt dbEncrypt = util.getDbPlatform().getDbEncrypt(); + + if (dbEncrypt != null){ + // check if we have a DB encryption function for this type + int jdbcType = prop.getScalarType().getJdbcType(); + DbEncryptFunction dbEncryptFunction = dbEncrypt.getDbEncryptFunction(jdbcType); + if (dbEncryptFunction != null){ + // Use DB functions to encrypt and decrypt + prop.setDbEncryptFunction(dbEncryptFunction, dbEncrypt, dbLen); + return; + } + } + } + + prop.setScalarType(createScalarType(prop, st)); + prop.setLocalEncrypted(true); + if (dbLen > 0){ + prop.setDbLength(dbLen); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private ScalarTypeEncryptedWrapper createScalarType(DeployBeanProperty prop, ScalarType st ) { + + // Use Java Encryptor wrapping the logical scalar type + DataEncryptSupport support = createDataEncryptSupport(prop); + ScalarTypeBytesBase byteType = getDbEncryptType(prop); + + return new ScalarTypeEncryptedWrapper(st, byteType, support); + } + + private ScalarTypeBytesBase getDbEncryptType(DeployBeanProperty prop) { + int dbType = prop.isLob() ? Types.BLOB : Types.VARBINARY; + return (ScalarTypeBytesBase)util.getTypeManager().getScalarType(dbType); + } + + private DataEncryptSupport createDataEncryptSupport(DeployBeanProperty prop) { + + String table = info.getDescriptor().getBaseTable(); + String column = prop.getDbColumn(); + + return util.createDataEncryptSupport(table, column); + } + + + private void readId(Id id, DeployBeanProperty prop) { + + prop.setId(true); + prop.setNullable(false); + + if (prop.getPropertyType().equals(UUID.class)){ + // An Id of type UUID + if (descriptor.getIdGeneratorName() == null){ + // Without a generator explicitly specified + // so will use the default one AUTO_UUID + descriptor.setIdGeneratorName(UuidIdGenerator.AUTO_UUID); + descriptor.setIdType(IdType.GENERATOR); + } + } + } + + private void readGenValue(GeneratedValue gen, DeployBeanProperty prop) { + + String genName = gen.generator(); + + SequenceGenerator sequenceGenerator = find(prop, SequenceGenerator.class); + if (sequenceGenerator != null) { + if (sequenceGenerator.name().equals(genName)) { + genName = sequenceGenerator.sequenceName(); + } + } + + GenerationType strategy = gen.strategy(); + + if (strategy == GenerationType.IDENTITY) { + descriptor.setIdType(IdType.IDENTITY); + + } else if (strategy == GenerationType.SEQUENCE) { + descriptor.setIdType(IdType.SEQUENCE); + if (genName != null && genName.length() > 0) { + descriptor.setIdGeneratorName(genName); + } + + } else if (strategy == GenerationType.AUTO) { + if (prop.getPropertyType().equals(UUID.class)){ + descriptor.setIdGeneratorName(UuidIdGenerator.AUTO_UUID); + descriptor.setIdType(IdType.GENERATOR); + + } else { + // use DatabasePlatform defaults + } + } + } + + private void readTemporal(Temporal temporal, DeployBeanProperty prop) { + + TemporalType type = temporal.value(); + if (type.equals(TemporalType.DATE)) { + prop.setDbType(Types.DATE); + + } else if (type.equals(TemporalType.TIMESTAMP)) { + prop.setDbType(Types.TIMESTAMP); + + } else if (type.equals(TemporalType.TIME)) { + prop.setDbType(Types.TIME); + + } else { + throw new PersistenceException("Unhandled type " + type); + } + } + + + private void readColumn(Column columnAnn, DeployBeanProperty prop) { + + if (!isEmpty(columnAnn.name())){ + String dbColumn = databasePlatform.convertQuotedIdentifiers(columnAnn.name()); + prop.setDbColumn(dbColumn); + } + + prop.setDbInsertable(columnAnn.insertable()); + prop.setDbUpdateable(columnAnn.updatable()); + prop.setNullable(columnAnn.nullable()); + prop.setUnique(columnAnn.unique()); + if (columnAnn.precision() > 0){ + prop.setDbLength(columnAnn.precision()); + } else if (columnAnn.length() != 255){ + // set default 255 on DbTypeMap + prop.setDbLength(columnAnn.length()); + } + prop.setDbScale(columnAnn.scale()); + prop.setDbColumnDefn(columnAnn.columnDefinition()); + + String baseTable = descriptor.getBaseTable(); + String tableName = columnAnn.table(); + if (tableName.equals("") || tableName.equalsIgnoreCase(baseTable)) { + // its a base table property... + } else { + // its on a secondary table... + prop.setSecondaryTable(tableName); + //DeployTableJoin tableJoin = info.getTableJoin(tableName); + //tableJoin.addProperty(prop); + } + } + + + + private void readValidations(DeployBeanProperty prop) { + + Field field = prop.getField(); + if (field != null) { + Annotation[] fieldAnnotations = field.getAnnotations(); + for (int i = 0; i < fieldAnnotations.length; i++) { + readValidations(prop, fieldAnnotations[i]); + } + } + + Method readMethod = prop.getReadMethod(); + if (readMethod != null) { + Annotation[] methAnnotations = readMethod.getAnnotations(); + for (int i = 0; i < methAnnotations.length; i++) { + readValidations(prop, methAnnotations[i]); + } + } + } + + private void readValidations(DeployBeanProperty prop, Annotation ann) { + Class type = ann.annotationType(); + if (type.equals(Patterns.class)){ + // treating this as a special case for now... + Patterns patterns = (Patterns)ann; + Pattern[] patternsArray = patterns.patterns(); + for (int i = 0; i < patternsArray.length; i++) { + util.createValidator(prop, patternsArray[i]); + } + + } else { + + ValidatorMeta meta = type.getAnnotation(ValidatorMeta.class); + if (meta != null) { + util.createValidator(prop, ann); + } + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java index 92011c95f..0c2fb9236 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationParser.java @@ -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.parse; - -import javax.persistence.CascadeType; -import javax.persistence.PersistenceException; - -import com.avaje.ebean.annotation.LdapAttribute; -import com.avaje.ebean.config.ldap.LdapAttributeAdapter; -import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; - -/** - * Base class for reading deployment annotations. - */ -public abstract class AnnotationParser extends AnnotationBase { - - protected final DeployBeanInfo info; - - protected final DeployBeanDescriptor descriptor; - - protected final Class beanType; - - public AnnotationParser(DeployBeanInfo info){ - super(info.getUtil()); - this.info = info; - this.beanType = info.getDescriptor().getBeanType(); - this.descriptor = info.getDescriptor(); - } - - /** - * read the deployment annotations. - */ - public abstract void parse(); - - /** - * Helper method to set cascade types to the CascadeInfo on BeanProperty. - */ - protected void setCascadeTypes(CascadeType[] cascadeTypes, BeanCascadeInfo cascadeInfo) { - if (cascadeTypes != null && cascadeTypes.length > 0) { - cascadeInfo.setTypes(cascadeTypes); - } - } - - protected void readLdapAttribute(LdapAttribute ldapAttribute, DeployBeanProperty prop) { - - if (!isEmpty(ldapAttribute.name())){ - prop.setDbColumn(ldapAttribute.name()); - } - prop.setDbInsertable(ldapAttribute.insertable()); - prop.setDbUpdateable(ldapAttribute.updatable()); - - Class adapterCls = ldapAttribute.adapter(); - - if (adapterCls != null && !void.class.equals(adapterCls)){ - try { - LdapAttributeAdapter adapter = (LdapAttributeAdapter)adapterCls.newInstance(); - prop.setLdapAttributeAdapter(adapter); - } catch (Exception e){ - String msg= "Error creating LdapAttributeAdapter for ["+prop.getFullBeanName()+"] " - +"with class ["+adapterCls+"] using the default constructor."; - throw new PersistenceException(msg, e); - } - } - - } -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import javax.persistence.CascadeType; +import javax.persistence.PersistenceException; + +import com.avaje.ebean.annotation.LdapAttribute; +import com.avaje.ebean.config.ldap.LdapAttributeAdapter; +import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; + +/** + * Base class for reading deployment annotations. + */ +public abstract class AnnotationParser extends AnnotationBase { + + protected final DeployBeanInfo info; + + protected final DeployBeanDescriptor descriptor; + + protected final Class beanType; + + public AnnotationParser(DeployBeanInfo info){ + super(info.getUtil()); + this.info = info; + this.beanType = info.getDescriptor().getBeanType(); + this.descriptor = info.getDescriptor(); + } + + /** + * read the deployment annotations. + */ + public abstract void parse(); + + /** + * Helper method to set cascade types to the CascadeInfo on BeanProperty. + */ + protected void setCascadeTypes(CascadeType[] cascadeTypes, BeanCascadeInfo cascadeInfo) { + if (cascadeTypes != null && cascadeTypes.length > 0) { + cascadeInfo.setTypes(cascadeTypes); + } + } + + protected void readLdapAttribute(LdapAttribute ldapAttribute, DeployBeanProperty prop) { + + if (!isEmpty(ldapAttribute.name())){ + prop.setDbColumn(ldapAttribute.name()); + } + prop.setDbInsertable(ldapAttribute.insertable()); + prop.setDbUpdateable(ldapAttribute.updatable()); + + Class adapterCls = ldapAttribute.adapter(); + + if (adapterCls != null && !void.class.equals(adapterCls)){ + try { + LdapAttributeAdapter adapter = (LdapAttributeAdapter)adapterCls.newInstance(); + prop.setLdapAttributeAdapter(adapter); + } catch (Exception e){ + String msg= "Error creating LdapAttributeAdapter for ["+prop.getFullBeanName()+"] " + +"with class ["+adapterCls+"] using the default constructor."; + throw new PersistenceException(msg, e); + } + } + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java index b524439eb..660503e34 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationSql.java @@ -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.parse; - -import com.avaje.ebean.annotation.Sql; -import com.avaje.ebean.annotation.SqlSelect; -import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta; - -/** - * Read the class level deployment annotations. - */ -public class AnnotationSql extends AnnotationParser { - - public AnnotationSql(DeployBeanInfo info) { - super(info); - } - - public void parse() { - Class cls = descriptor.getBeanType(); - Sql sql = cls.getAnnotation(Sql.class); - if (sql != null){ - setSql(sql); - } - - - SqlSelect sqlSelect = cls.getAnnotation(SqlSelect.class); - if (sqlSelect != null){ - setSqlSelect(sqlSelect); - } - } - - private void setSql(Sql sql) { - SqlSelect[] select = sql.select(); - for (int i = 0; i < select.length; i++) { - setSqlSelect(select[i]); - } - } - - private void setSqlSelect(SqlSelect sqlSelect) { - - DRawSqlMeta rawSqlMeta = new DRawSqlMeta(sqlSelect); - descriptor.add(rawSqlMeta); - } -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import com.avaje.ebean.annotation.Sql; +import com.avaje.ebean.annotation.SqlSelect; +import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta; + +/** + * Read the class level deployment annotations. + */ +public class AnnotationSql extends AnnotationParser { + + public AnnotationSql(DeployBeanInfo info) { + super(info); + } + + public void parse() { + Class cls = descriptor.getBeanType(); + Sql sql = cls.getAnnotation(Sql.class); + if (sql != null){ + setSql(sql); + } + + + SqlSelect sqlSelect = cls.getAnnotation(SqlSelect.class); + if (sqlSelect != null){ + setSqlSelect(sqlSelect); + } + } + + private void setSql(Sql sql) { + SqlSelect[] select = sql.select(); + for (int i = 0; i < select.length; i++) { + setSqlSelect(select[i]); + } + } + + private void setSqlSelect(SqlSelect sqlSelect) { + + DRawSqlMeta rawSqlMeta = new DRawSqlMeta(sqlSelect); + descriptor.add(rawSqlMeta); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployBeanInfo.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployBeanInfo.java index 497818650..914437f80 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployBeanInfo.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployBeanInfo.java @@ -1,102 +1,83 @@ -/** - * 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.parse; - -import java.util.HashMap; - -import com.avaje.ebeaninternal.server.deploy.TableJoin; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; - -/** - * Wraps information about a bean during deployment parsing. - */ -public class DeployBeanInfo { - - /** - * Holds TableJoins for secondary table properties. - */ - private final HashMap tableJoinMap = new HashMap(); - - private final DeployUtil util; - - private final DeployBeanDescriptor descriptor; - - /** - * Create with a DeployUtil and BeanDescriptor. - */ - public DeployBeanInfo(DeployUtil util, DeployBeanDescriptor descriptor) { - this.util = util; - this.descriptor = descriptor; - } - - public String toString() { - return ""+descriptor; - } - - /** - * Return the BeanDescriptor currently being processed. - */ - public DeployBeanDescriptor getDescriptor() { - return descriptor; - } - - /** - * Return the DeployUtil we are using. - */ - public DeployUtil getUtil() { - return util; - } - - /** - * Appropriate TableJoin for a property mapped to a secondary table. - */ - public DeployTableJoin getTableJoin(String tableName) { - - String key = tableName.toLowerCase(); - - DeployTableJoin tableJoin = (DeployTableJoin) tableJoinMap.get(key); - if (tableJoin == null) { - tableJoin = new DeployTableJoin(); - tableJoin.setTable(tableName); - tableJoin.setType(TableJoin.JOIN); - descriptor.addTableJoin(tableJoin); - - tableJoinMap.put(key, tableJoin); - } - return tableJoin; - } - - /** - * Set a the join alias for a assoc one property. - */ - public void setBeanJoinType(DeployBeanPropertyAssocOne beanProp, boolean outerJoin) { - - String joinType = TableJoin.JOIN; - if (outerJoin){// && util.isUseOneToOneOptional()) { - joinType = TableJoin.LEFT_OUTER; - } - - DeployTableJoin tableJoin = beanProp.getTableJoin(); - tableJoin.setType(joinType); - } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.util.HashMap; + +import com.avaje.ebeaninternal.server.deploy.TableJoin; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin; + +/** + * Wraps information about a bean during deployment parsing. + */ +public class DeployBeanInfo { + + /** + * Holds TableJoins for secondary table properties. + */ + private final HashMap tableJoinMap = new HashMap(); + + private final DeployUtil util; + + private final DeployBeanDescriptor descriptor; + + /** + * Create with a DeployUtil and BeanDescriptor. + */ + public DeployBeanInfo(DeployUtil util, DeployBeanDescriptor descriptor) { + this.util = util; + this.descriptor = descriptor; + } + + public String toString() { + return ""+descriptor; + } + + /** + * Return the BeanDescriptor currently being processed. + */ + public DeployBeanDescriptor getDescriptor() { + return descriptor; + } + + /** + * Return the DeployUtil we are using. + */ + public DeployUtil getUtil() { + return util; + } + + /** + * Appropriate TableJoin for a property mapped to a secondary table. + */ + public DeployTableJoin getTableJoin(String tableName) { + + String key = tableName.toLowerCase(); + + DeployTableJoin tableJoin = (DeployTableJoin) tableJoinMap.get(key); + if (tableJoin == null) { + tableJoin = new DeployTableJoin(); + tableJoin.setTable(tableName); + tableJoin.setType(TableJoin.JOIN); + descriptor.addTableJoin(tableJoin); + + tableJoinMap.put(key, tableJoin); + } + return tableJoin; + } + + /** + * Set a the join alias for a assoc one property. + */ + public void setBeanJoinType(DeployBeanPropertyAssocOne beanProp, boolean outerJoin) { + + String joinType = TableJoin.JOIN; + if (outerJoin){// && util.isUseOneToOneOptional()) { + joinType = TableJoin.LEFT_OUTER; + } + + DeployTableJoin tableJoin = beanProp.getTableJoin(); + tableJoin.setType(joinType); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployCreateProperties.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployCreateProperties.java index 41d6d2060..0722cdc98 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployCreateProperties.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployCreateProperties.java @@ -1,435 +1,416 @@ -/** - * 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.parse; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Iterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; -import javax.persistence.Transient; - -import com.avaje.ebean.config.ScalarTypeConverter; -import com.avaje.ebeaninternal.server.core.Message; -import com.avaje.ebeaninternal.server.deploy.DetermineManyType; -import com.avaje.ebeaninternal.server.deploy.ManyType; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection; -import com.avaje.ebeaninternal.server.type.CtCompoundType; -import com.avaje.ebeaninternal.server.type.ScalaOptionTypeConverter; -import com.avaje.ebeaninternal.server.type.ScalarType; -import com.avaje.ebeaninternal.server.type.TypeManager; -import com.avaje.ebeaninternal.server.type.reflect.CheckImmutableResponse; - -/** - * Create the properties for a bean. - *

- * This also needs to determine if the property is a associated many, associated - * one or normal scalar property. - *

- */ -public class DeployCreateProperties { - - private static final Logger logger = Logger.getLogger(DeployCreateProperties.class.getName()); - - private final Class scalaOptionClass; - /** - * Use to wrap and unwrap Scala Option. - */ - @SuppressWarnings("rawtypes") - private final ScalarTypeConverter scalaOptionTypeConverter; - - private final DetermineManyType determineManyType; - - private final TypeManager typeManager; - - @SuppressWarnings("rawtypes") - public DeployCreateProperties(TypeManager typeManager) { - this.typeManager = typeManager; - - Class tmpOptionClass = DetectScala.getScalaOptionClass(); - - if (tmpOptionClass == null){ - scalaOptionClass = null; - scalaOptionTypeConverter = null; - } else { - scalaOptionClass = tmpOptionClass; - scalaOptionTypeConverter = new ScalaOptionTypeConverter(); - } - - this.determineManyType = new DetermineManyType(tmpOptionClass != null); - } - - /** - * Create the appropriate properties for a bean. - */ - public void createProperties(DeployBeanDescriptor desc) { - - createProperties(desc, desc.getBeanType(), 0); - desc.sortProperties(); - - // check the transient properties... - Iterator it = desc.propertiesAll(); - - while (it.hasNext()) { - DeployBeanProperty prop = it.next(); - if (prop.isTransient()){ - if (prop.getWriteMethod() == null || prop.getReadMethod() == null){ - // Typically a helper method ... this is expected - logger.finest("... transient: "+prop.getFullBeanName()); - } else { - // dubious, possible error... - String msg = Message.msg("deploy.property.nofield", desc.getFullName(), prop.getName()); - logger.warning(msg); - } - } - } - } - - /** - * Return true if we should ignore this field. - *

- * We want to ignore ebean internal fields and some others as well. - *

- */ - private boolean ignoreFieldByName(String fieldName) { - if (fieldName.startsWith("_ebean_")){ - // ignore Ebean internal fields - return true; - } - if (fieldName.startsWith("ajc$instance$")) { - // ignore AspectJ internal fields - return true; - } - - // we are interested in this field - return false; - } - - /** - * reflect the bean properties from Class. Some of these properties may not - * map to database columns. - */ - private void createProperties(DeployBeanDescriptor desc, Class beanType, int level) { - - boolean scalaObject = desc.isScalaObject(); - - try { - Method[] declaredMethods = beanType.getDeclaredMethods(); - Field[] fields = beanType.getDeclaredFields(); - - for (int i = 0; i < fields.length; i++) { - - Field field = fields[i]; - if (Modifier.isStatic(field.getModifiers())) { - // not interested in static fields - - } else if (Modifier.isTransient(field.getModifiers())) { - // not interested in transient fields - logger.finer("Skipping transient field "+field.getName()+" in "+beanType.getName()); - - } else if (ignoreFieldByName(field.getName())) { - // not interested this field (ebean or aspectJ field) - - } else { - - String fieldName = getFieldName(field, beanType); - String initFieldName = initCap(fieldName); - - Method getter = findGetter(field, initFieldName, declaredMethods, scalaObject); - Method setter = findSetter(field, initFieldName, declaredMethods, scalaObject); - - DeployBeanProperty prop = createProp(level, desc, field, beanType, getter, setter); - if (prop == null){ - // transient annotation on unsupported type - - } else { - // set a order that gives priority to inherited properties - // push Id/EmbeddedId up and CreatedTimestamp/UpdatedTimestamp down - int sortOverride = prop.getSortOverride(); - prop.setSortOrder((level*10000+100-i + sortOverride)); - - DeployBeanProperty replaced = desc.addBeanProperty(prop); - if (replaced != null){ - if (replaced.isTransient()) { - // expected for inheritance... - } else { - String msg = "Huh??? property "+prop.getFullBeanName()+" being defined twice"; - msg += " but replaced property was not transient? This is not expected?"; - logger.warning(msg); - } - } - } - } - } - - Class superClass = beanType.getSuperclass(); - - if (!superClass.equals(Object.class)) { - // recursively add any properties in the inheritance heirarchy - // up to the Object.class level... - createProperties(desc, superClass, level + 1); - } - - } catch (PersistenceException ex) { - throw ex; - - } catch (Exception ex) { - throw new PersistenceException(ex); - } - } - - /** - * Make the first letter of the string upper case. - */ - private String initCap(String str){ - if (str.length() > 1){ - return Character.toUpperCase(str.charAt(0))+str.substring(1); - } else { - // only a single char - return str.toUpperCase(); - } - } - - /** - * Return the bean spec field name (trim of "is" from boolean types) - */ - private String getFieldName(Field field, Class beanType){ - - String name = field.getName(); - - if ((Boolean.class.equals(field.getType()) || boolean.class.equals(field.getType())) - && name.startsWith("is") && name.length() > 2){ - - // it is a boolean type field starting with "is" - char c = name.charAt(2); - if (Character.isUpperCase(c)){ - String msg = "trimming off 'is' from boolean field name "+name+" in class "+beanType.getName(); - logger.log(Level.INFO, msg); - - return name.substring(2); - } - } - return name; - } - - /** - * Find a public non-static getter method that matches this field (according to bean-spec rules). - */ - private Method findGetter(Field field, String initFieldName, Method[] declaredMethods, boolean scalaObject){ - - String methGetName = "get"+initFieldName; - String methIsName = "is"+initFieldName; - String scalaGet = field.getName(); - - for (int i = 0; i < declaredMethods.length; i++) { - Method m = declaredMethods[i]; - if ((scalaObject && m.getName().equals(scalaGet)) - || m.getName().equals(methGetName) || m.getName().equals(methIsName)){ - - Class[] params = m.getParameterTypes(); - if (params.length == 0){ - if (field.getType().equals(m.getReturnType())){ - int modifiers = m.getModifiers(); - if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { - // we find it... - return m; - } - } - } - } - } - return null; - } - - /** - * Find a public non-static setter method that matches this field (according to bean-spec rules). - */ - private Method findSetter(Field field, String initFieldName, Method[] declaredMethods, boolean scalaObject){ - - String methSetName = "set"+initFieldName; - String scalaSetName = field.getName()+"_$eq"; - - for (int i = 0; i < declaredMethods.length; i++) { - Method m = declaredMethods[i]; - - if ((scalaObject && m.getName().equals(scalaSetName)) - || m.getName().equals(methSetName)){ - - Class[] params = m.getParameterTypes(); - if (params.length == 1 && field.getType().equals(params[0])){ - if (void.class.equals(m.getReturnType())){ - int modifiers = m.getModifiers(); - if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { - return m; - } - } - } - } - } - return null; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private DeployBeanProperty createManyType(DeployBeanDescriptor desc, Class targetType, ManyType manyType) { - - ScalarType scalarType = typeManager.getScalarType(targetType); - if (scalarType != null) { - return new DeployBeanPropertySimpleCollection(desc, targetType, scalarType, manyType); - } - //TODO: Handle Collection of CompoundType and Embedded Type - return new DeployBeanPropertyAssocMany(desc, targetType, manyType); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private DeployBeanProperty createProp(DeployBeanDescriptor desc, Field field) { - - Class propertyType = field.getType(); - Class innerType = propertyType; - ScalarTypeConverter typeConverter = null; - - if (propertyType.equals(scalaOptionClass)){ - innerType = determineTargetType(field); - typeConverter = scalaOptionTypeConverter; - } - - // check for Collection type (list, set or map) - ManyType manyType = determineManyType.getManyType(propertyType); - - if (manyType != null) { - // List, Set or Map based object - Class targetType = determineTargetType(field); - if (targetType == null){ - Transient transAnnotation = field.getAnnotation(Transient.class); - if (transAnnotation != null) { - // not supporting this field (generic type used) - return null; - } - logger.warning("Could not find parameter type (via reflection) on "+desc.getFullName()+" "+field.getName()); - } - return createManyType(desc, targetType, manyType); - } - - if (innerType.isEnum() || innerType.isPrimitive()){ - return new DeployBeanProperty(desc, propertyType, null, typeConverter); - } - - ScalarType scalarType = typeManager.getScalarType(innerType); - if (scalarType != null) { - return new DeployBeanProperty(desc, propertyType, scalarType, typeConverter); - } - - CtCompoundType compoundType = typeManager.getCompoundType(innerType); - if (compoundType != null) { - return new DeployBeanPropertyCompound(desc, propertyType, compoundType, typeConverter); - } - - if (!isTransientField(field)){ - try { - CheckImmutableResponse checkImmutable = typeManager.checkImmutable(innerType); - if (checkImmutable.isImmutable()){ - if (checkImmutable.isCompoundType()){ - // use reflection to support compound immutable value objects - typeManager.recursiveCreateScalarDataReader(innerType); - compoundType = typeManager.getCompoundType(innerType); - if (compoundType != null) { - return new DeployBeanPropertyCompound(desc, propertyType, compoundType, typeConverter); - } - - } else { - // use reflection to support simple immutable value objects - scalarType = typeManager.recursiveCreateScalarTypes(innerType); - return new DeployBeanProperty(desc, propertyType, scalarType, typeConverter); - } - } - } catch (Exception e){ - logger.log(Level.SEVERE, "Error with "+desc+" field:"+field.getName(), e); - } - } - - return new DeployBeanPropertyAssocOne(desc, propertyType); - } - - private boolean isTransientField(Field field) { - - Transient t = field.getAnnotation(Transient.class); - return (t != null); - } - - private DeployBeanProperty createProp(int level, DeployBeanDescriptor desc, Field field, Class beanType, Method getter, Method setter) { - - DeployBeanProperty prop = createProp(desc, field); - if (prop == null){ - // transient annotation on unsupported type - return null; - } else { - prop.setOwningType(beanType); - prop.setName(field.getName()); - - // the getter or setter could be null if we are using - // javaagent type enhancement. If we are using subclass - // generation then we do need to find the getter and setter - prop.setReadMethod(getter); - prop.setWriteMethod(setter); - prop.setField(field); - return prop; - } - } - - /** - * Determine the type of the List,Set or Map. Not been set explicitly so - * determine this from ParameterizedType. - */ - private Class determineTargetType(Field field) { - - Type genType = field.getGenericType(); - if (genType instanceof ParameterizedType) { - ParameterizedType ptype = (ParameterizedType) genType; - - Type[] typeArgs = ptype.getActualTypeArguments(); - if (typeArgs.length == 1) { - // probably a Set or List - if (typeArgs[0] instanceof Class){ - return (Class) typeArgs[0]; - } - throw new RuntimeException("Unexpected Parameterised Type? "+typeArgs[0]); - } - if (typeArgs.length == 2) { - // this is probably a Map - if (typeArgs[1] instanceof ParameterizedType) { - // not supporting ParameterizedType on Map. - return null; - } - return (Class) typeArgs[1]; - } - } - // if targetType is null, then must be set in annotations - return null; - } -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Iterator; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; +import javax.persistence.Transient; + +import com.avaje.ebean.config.ScalarTypeConverter; +import com.avaje.ebeaninternal.server.core.Message; +import com.avaje.ebeaninternal.server.deploy.DetermineManyType; +import com.avaje.ebeaninternal.server.deploy.ManyType; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection; +import com.avaje.ebeaninternal.server.type.CtCompoundType; +import com.avaje.ebeaninternal.server.type.ScalaOptionTypeConverter; +import com.avaje.ebeaninternal.server.type.ScalarType; +import com.avaje.ebeaninternal.server.type.TypeManager; +import com.avaje.ebeaninternal.server.type.reflect.CheckImmutableResponse; + +/** + * Create the properties for a bean. + *

+ * This also needs to determine if the property is a associated many, associated + * one or normal scalar property. + *

+ */ +public class DeployCreateProperties { + + private static final Logger logger = Logger.getLogger(DeployCreateProperties.class.getName()); + + private final Class scalaOptionClass; + /** + * Use to wrap and unwrap Scala Option. + */ + @SuppressWarnings("rawtypes") + private final ScalarTypeConverter scalaOptionTypeConverter; + + private final DetermineManyType determineManyType; + + private final TypeManager typeManager; + + @SuppressWarnings("rawtypes") + public DeployCreateProperties(TypeManager typeManager) { + this.typeManager = typeManager; + + Class tmpOptionClass = DetectScala.getScalaOptionClass(); + + if (tmpOptionClass == null){ + scalaOptionClass = null; + scalaOptionTypeConverter = null; + } else { + scalaOptionClass = tmpOptionClass; + scalaOptionTypeConverter = new ScalaOptionTypeConverter(); + } + + this.determineManyType = new DetermineManyType(tmpOptionClass != null); + } + + /** + * Create the appropriate properties for a bean. + */ + public void createProperties(DeployBeanDescriptor desc) { + + createProperties(desc, desc.getBeanType(), 0); + desc.sortProperties(); + + // check the transient properties... + Iterator it = desc.propertiesAll(); + + while (it.hasNext()) { + DeployBeanProperty prop = it.next(); + if (prop.isTransient()){ + if (prop.getWriteMethod() == null || prop.getReadMethod() == null){ + // Typically a helper method ... this is expected + logger.finest("... transient: "+prop.getFullBeanName()); + } else { + // dubious, possible error... + String msg = Message.msg("deploy.property.nofield", desc.getFullName(), prop.getName()); + logger.warning(msg); + } + } + } + } + + /** + * Return true if we should ignore this field. + *

+ * We want to ignore ebean internal fields and some others as well. + *

+ */ + private boolean ignoreFieldByName(String fieldName) { + if (fieldName.startsWith("_ebean_")){ + // ignore Ebean internal fields + return true; + } + if (fieldName.startsWith("ajc$instance$")) { + // ignore AspectJ internal fields + return true; + } + + // we are interested in this field + return false; + } + + /** + * reflect the bean properties from Class. Some of these properties may not + * map to database columns. + */ + private void createProperties(DeployBeanDescriptor desc, Class beanType, int level) { + + boolean scalaObject = desc.isScalaObject(); + + try { + Method[] declaredMethods = beanType.getDeclaredMethods(); + Field[] fields = beanType.getDeclaredFields(); + + for (int i = 0; i < fields.length; i++) { + + Field field = fields[i]; + if (Modifier.isStatic(field.getModifiers())) { + // not interested in static fields + + } else if (Modifier.isTransient(field.getModifiers())) { + // not interested in transient fields + logger.finer("Skipping transient field "+field.getName()+" in "+beanType.getName()); + + } else if (ignoreFieldByName(field.getName())) { + // not interested this field (ebean or aspectJ field) + + } else { + + String fieldName = getFieldName(field, beanType); + String initFieldName = initCap(fieldName); + + Method getter = findGetter(field, initFieldName, declaredMethods, scalaObject); + Method setter = findSetter(field, initFieldName, declaredMethods, scalaObject); + + DeployBeanProperty prop = createProp(level, desc, field, beanType, getter, setter); + if (prop == null){ + // transient annotation on unsupported type + + } else { + // set a order that gives priority to inherited properties + // push Id/EmbeddedId up and CreatedTimestamp/UpdatedTimestamp down + int sortOverride = prop.getSortOverride(); + prop.setSortOrder((level*10000+100-i + sortOverride)); + + DeployBeanProperty replaced = desc.addBeanProperty(prop); + if (replaced != null){ + if (replaced.isTransient()) { + // expected for inheritance... + } else { + String msg = "Huh??? property "+prop.getFullBeanName()+" being defined twice"; + msg += " but replaced property was not transient? This is not expected?"; + logger.warning(msg); + } + } + } + } + } + + Class superClass = beanType.getSuperclass(); + + if (!superClass.equals(Object.class)) { + // recursively add any properties in the inheritance heirarchy + // up to the Object.class level... + createProperties(desc, superClass, level + 1); + } + + } catch (PersistenceException ex) { + throw ex; + + } catch (Exception ex) { + throw new PersistenceException(ex); + } + } + + /** + * Make the first letter of the string upper case. + */ + private String initCap(String str){ + if (str.length() > 1){ + return Character.toUpperCase(str.charAt(0))+str.substring(1); + } else { + // only a single char + return str.toUpperCase(); + } + } + + /** + * Return the bean spec field name (trim of "is" from boolean types) + */ + private String getFieldName(Field field, Class beanType){ + + String name = field.getName(); + + if ((Boolean.class.equals(field.getType()) || boolean.class.equals(field.getType())) + && name.startsWith("is") && name.length() > 2){ + + // it is a boolean type field starting with "is" + char c = name.charAt(2); + if (Character.isUpperCase(c)){ + String msg = "trimming off 'is' from boolean field name "+name+" in class "+beanType.getName(); + logger.log(Level.INFO, msg); + + return name.substring(2); + } + } + return name; + } + + /** + * Find a public non-static getter method that matches this field (according to bean-spec rules). + */ + private Method findGetter(Field field, String initFieldName, Method[] declaredMethods, boolean scalaObject){ + + String methGetName = "get"+initFieldName; + String methIsName = "is"+initFieldName; + String scalaGet = field.getName(); + + for (int i = 0; i < declaredMethods.length; i++) { + Method m = declaredMethods[i]; + if ((scalaObject && m.getName().equals(scalaGet)) + || m.getName().equals(methGetName) || m.getName().equals(methIsName)){ + + Class[] params = m.getParameterTypes(); + if (params.length == 0){ + if (field.getType().equals(m.getReturnType())){ + int modifiers = m.getModifiers(); + if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { + // we find it... + return m; + } + } + } + } + } + return null; + } + + /** + * Find a public non-static setter method that matches this field (according to bean-spec rules). + */ + private Method findSetter(Field field, String initFieldName, Method[] declaredMethods, boolean scalaObject){ + + String methSetName = "set"+initFieldName; + String scalaSetName = field.getName()+"_$eq"; + + for (int i = 0; i < declaredMethods.length; i++) { + Method m = declaredMethods[i]; + + if ((scalaObject && m.getName().equals(scalaSetName)) + || m.getName().equals(methSetName)){ + + Class[] params = m.getParameterTypes(); + if (params.length == 1 && field.getType().equals(params[0])){ + if (void.class.equals(m.getReturnType())){ + int modifiers = m.getModifiers(); + if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { + return m; + } + } + } + } + } + return null; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private DeployBeanProperty createManyType(DeployBeanDescriptor desc, Class targetType, ManyType manyType) { + + ScalarType scalarType = typeManager.getScalarType(targetType); + if (scalarType != null) { + return new DeployBeanPropertySimpleCollection(desc, targetType, scalarType, manyType); + } + //TODO: Handle Collection of CompoundType and Embedded Type + return new DeployBeanPropertyAssocMany(desc, targetType, manyType); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private DeployBeanProperty createProp(DeployBeanDescriptor desc, Field field) { + + Class propertyType = field.getType(); + Class innerType = propertyType; + ScalarTypeConverter typeConverter = null; + + if (propertyType.equals(scalaOptionClass)){ + innerType = determineTargetType(field); + typeConverter = scalaOptionTypeConverter; + } + + // check for Collection type (list, set or map) + ManyType manyType = determineManyType.getManyType(propertyType); + + if (manyType != null) { + // List, Set or Map based object + Class targetType = determineTargetType(field); + if (targetType == null){ + Transient transAnnotation = field.getAnnotation(Transient.class); + if (transAnnotation != null) { + // not supporting this field (generic type used) + return null; + } + logger.warning("Could not find parameter type (via reflection) on "+desc.getFullName()+" "+field.getName()); + } + return createManyType(desc, targetType, manyType); + } + + if (innerType.isEnum() || innerType.isPrimitive()){ + return new DeployBeanProperty(desc, propertyType, null, typeConverter); + } + + ScalarType scalarType = typeManager.getScalarType(innerType); + if (scalarType != null) { + return new DeployBeanProperty(desc, propertyType, scalarType, typeConverter); + } + + CtCompoundType compoundType = typeManager.getCompoundType(innerType); + if (compoundType != null) { + return new DeployBeanPropertyCompound(desc, propertyType, compoundType, typeConverter); + } + + if (!isTransientField(field)){ + try { + CheckImmutableResponse checkImmutable = typeManager.checkImmutable(innerType); + if (checkImmutable.isImmutable()){ + if (checkImmutable.isCompoundType()){ + // use reflection to support compound immutable value objects + typeManager.recursiveCreateScalarDataReader(innerType); + compoundType = typeManager.getCompoundType(innerType); + if (compoundType != null) { + return new DeployBeanPropertyCompound(desc, propertyType, compoundType, typeConverter); + } + + } else { + // use reflection to support simple immutable value objects + scalarType = typeManager.recursiveCreateScalarTypes(innerType); + return new DeployBeanProperty(desc, propertyType, scalarType, typeConverter); + } + } + } catch (Exception e){ + logger.log(Level.SEVERE, "Error with "+desc+" field:"+field.getName(), e); + } + } + + return new DeployBeanPropertyAssocOne(desc, propertyType); + } + + private boolean isTransientField(Field field) { + + Transient t = field.getAnnotation(Transient.class); + return (t != null); + } + + private DeployBeanProperty createProp(int level, DeployBeanDescriptor desc, Field field, Class beanType, Method getter, Method setter) { + + DeployBeanProperty prop = createProp(desc, field); + if (prop == null){ + // transient annotation on unsupported type + return null; + } else { + prop.setOwningType(beanType); + prop.setName(field.getName()); + + // the getter or setter could be null if we are using + // javaagent type enhancement. If we are using subclass + // generation then we do need to find the getter and setter + prop.setReadMethod(getter); + prop.setWriteMethod(setter); + prop.setField(field); + return prop; + } + } + + /** + * Determine the type of the List,Set or Map. Not been set explicitly so + * determine this from ParameterizedType. + */ + private Class determineTargetType(Field field) { + + Type genType = field.getGenericType(); + if (genType instanceof ParameterizedType) { + ParameterizedType ptype = (ParameterizedType) genType; + + Type[] typeArgs = ptype.getActualTypeArguments(); + if (typeArgs.length == 1) { + // probably a Set or List + if (typeArgs[0] instanceof Class){ + return (Class) typeArgs[0]; + } + throw new RuntimeException("Unexpected Parameterised Type? "+typeArgs[0]); + } + if (typeArgs.length == 2) { + // this is probably a Map + if (typeArgs[1] instanceof ParameterizedType) { + // not supporting ParameterizedType on Map. + return null; + } + return (Class) typeArgs[1]; + } + } + // if targetType is null, then must be set in annotations + return null; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInherit.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInherit.java index bda0809bf..ec0d5411e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInherit.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInherit.java @@ -1,196 +1,177 @@ -/** - * 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.parse; - -import java.lang.annotation.Annotation; -import java.sql.Types; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import javax.persistence.DiscriminatorColumn; -import javax.persistence.DiscriminatorType; -import javax.persistence.DiscriminatorValue; -import javax.persistence.Inheritance; - -import com.avaje.ebeaninternal.server.core.BootupClasses; -import com.avaje.ebeaninternal.server.deploy.InheritInfo; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; - -/** - * Builds the InheritInfo deployment information. - */ -public class DeployInherit { - - private final Map, DeployInheritInfo> deployMap = new LinkedHashMap, DeployInheritInfo>(); - - private final Map, InheritInfo> finalMap = new LinkedHashMap, InheritInfo>(); - - private final BootupClasses bootupClasses; - - /** - * Create the InheritInfoDeploy. - */ - public DeployInherit(BootupClasses bootupClasses) { - this.bootupClasses = bootupClasses; - initialise(); - } - - public void process(DeployBeanDescriptor desc) { - InheritInfo inheritInfo = finalMap.get(desc.getBeanType()); - desc.setInheritInfo(inheritInfo); - } - - private void initialise() { - List> entityList = bootupClasses.getEntities(); - - findInheritClasses(entityList); - buildDeployTree(); - buildFinalTree(); - } - - private void findInheritClasses(List> entityList) { - - // go through each class and initialise the info object... - Iterator> it = entityList.iterator(); - while (it.hasNext()) { - Class cls = (Class) it.next(); - if (isInheritanceClass(cls)) { - DeployInheritInfo info = createInfo(cls); - deployMap.put(cls, info); - } - } - } - - private void buildDeployTree() { - Iterator it = deployMap.values().iterator(); - while (it.hasNext()) { - DeployInheritInfo info = it.next(); - if (!info.isRoot()) { - DeployInheritInfo parent = getInfo(info.getParent()); - parent.addChild(info); - } - } - } - - private void buildFinalTree() { - - Iterator it = deployMap.values().iterator(); - while (it.hasNext()) { - DeployInheritInfo deploy = it.next(); - if (deploy.isRoot()) { - // build tree top down... - createFinalInfo(null, null, deploy); - - } - } - } - - private InheritInfo createFinalInfo(InheritInfo root, InheritInfo parent, - DeployInheritInfo deploy) { - - InheritInfo node = new InheritInfo(root, parent, deploy); - if (parent != null) { - parent.addChild(node); - } - finalMap.put(node.getType(), node); - - if (root == null) { - root = node; - } - - // buildFinalChildren(root, child, deploy); - - Iterator it = deploy.children(); - - while (it.hasNext()) { - DeployInheritInfo childDeploy = it.next(); - - createFinalInfo(root, node, childDeploy); - } - - return node; - } - - /** - * Build the InheritInfo for a given class. - */ - private DeployInheritInfo getInfo(Class cls) { - return deployMap.get(cls); - } - - private DeployInheritInfo createInfo(Class cls) { - - DeployInheritInfo info = new DeployInheritInfo(cls); - - Class parent = findParent(cls); - if (parent != null) { - info.setParent(parent); - } else { - // its the root of inheritance tree... - } - - Inheritance ia = (Inheritance) cls.getAnnotation(Inheritance.class); - if (ia != null) { - ia.strategy(); - } - DiscriminatorColumn da = (DiscriminatorColumn) cls.getAnnotation(DiscriminatorColumn.class); - if (da != null) { - info.setDiscriminatorColumn(da.name()); - DiscriminatorType discriminatorType = da.discriminatorType(); - if (discriminatorType.equals(DiscriminatorType.INTEGER)){ - info.setDiscriminatorType(Types.INTEGER); - } else { - info.setDiscriminatorType(Types.VARCHAR); - } - info.setDiscriminatorLength(da.length()); - } - - DiscriminatorValue dv = (DiscriminatorValue) cls.getAnnotation(DiscriminatorValue.class); - if (dv != null) { - info.setDiscriminatorValue(dv.value()); - } - - return info; - } - - private Class findParent(Class cls) { - Class superCls = cls.getSuperclass(); - if (isInheritanceClass(superCls)) { - return superCls; - } else { - return null; - } - } - - private boolean isInheritanceClass(Class cls) { - if (cls.equals(Object.class)) { - return false; - } - Annotation a = cls.getAnnotation(Inheritance.class); - if (a != null) { - return true; - } - // search up the inheritance heirarchy - return isInheritanceClass(cls.getSuperclass()); - } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.lang.annotation.Annotation; +import java.sql.Types; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorType; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Inheritance; + +import com.avaje.ebeaninternal.server.core.BootupClasses; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; + +/** + * Builds the InheritInfo deployment information. + */ +public class DeployInherit { + + private final Map, DeployInheritInfo> deployMap = new LinkedHashMap, DeployInheritInfo>(); + + private final Map, InheritInfo> finalMap = new LinkedHashMap, InheritInfo>(); + + private final BootupClasses bootupClasses; + + /** + * Create the InheritInfoDeploy. + */ + public DeployInherit(BootupClasses bootupClasses) { + this.bootupClasses = bootupClasses; + initialise(); + } + + public void process(DeployBeanDescriptor desc) { + InheritInfo inheritInfo = finalMap.get(desc.getBeanType()); + desc.setInheritInfo(inheritInfo); + } + + private void initialise() { + List> entityList = bootupClasses.getEntities(); + + findInheritClasses(entityList); + buildDeployTree(); + buildFinalTree(); + } + + private void findInheritClasses(List> entityList) { + + // go through each class and initialise the info object... + Iterator> it = entityList.iterator(); + while (it.hasNext()) { + Class cls = (Class) it.next(); + if (isInheritanceClass(cls)) { + DeployInheritInfo info = createInfo(cls); + deployMap.put(cls, info); + } + } + } + + private void buildDeployTree() { + Iterator it = deployMap.values().iterator(); + while (it.hasNext()) { + DeployInheritInfo info = it.next(); + if (!info.isRoot()) { + DeployInheritInfo parent = getInfo(info.getParent()); + parent.addChild(info); + } + } + } + + private void buildFinalTree() { + + Iterator it = deployMap.values().iterator(); + while (it.hasNext()) { + DeployInheritInfo deploy = it.next(); + if (deploy.isRoot()) { + // build tree top down... + createFinalInfo(null, null, deploy); + + } + } + } + + private InheritInfo createFinalInfo(InheritInfo root, InheritInfo parent, + DeployInheritInfo deploy) { + + InheritInfo node = new InheritInfo(root, parent, deploy); + if (parent != null) { + parent.addChild(node); + } + finalMap.put(node.getType(), node); + + if (root == null) { + root = node; + } + + // buildFinalChildren(root, child, deploy); + + Iterator it = deploy.children(); + + while (it.hasNext()) { + DeployInheritInfo childDeploy = it.next(); + + createFinalInfo(root, node, childDeploy); + } + + return node; + } + + /** + * Build the InheritInfo for a given class. + */ + private DeployInheritInfo getInfo(Class cls) { + return deployMap.get(cls); + } + + private DeployInheritInfo createInfo(Class cls) { + + DeployInheritInfo info = new DeployInheritInfo(cls); + + Class parent = findParent(cls); + if (parent != null) { + info.setParent(parent); + } else { + // its the root of inheritance tree... + } + + Inheritance ia = (Inheritance) cls.getAnnotation(Inheritance.class); + if (ia != null) { + ia.strategy(); + } + DiscriminatorColumn da = (DiscriminatorColumn) cls.getAnnotation(DiscriminatorColumn.class); + if (da != null) { + info.setDiscriminatorColumn(da.name()); + DiscriminatorType discriminatorType = da.discriminatorType(); + if (discriminatorType.equals(DiscriminatorType.INTEGER)){ + info.setDiscriminatorType(Types.INTEGER); + } else { + info.setDiscriminatorType(Types.VARCHAR); + } + info.setDiscriminatorLength(da.length()); + } + + DiscriminatorValue dv = (DiscriminatorValue) cls.getAnnotation(DiscriminatorValue.class); + if (dv != null) { + info.setDiscriminatorValue(dv.value()); + } + + return info; + } + + private Class findParent(Class cls) { + Class superCls = cls.getSuperclass(); + if (isInheritanceClass(superCls)) { + return superCls; + } else { + return null; + } + } + + private boolean isInheritanceClass(Class cls) { + if (cls.equals(Object.class)) { + return false; + } + Annotation a = cls.getAnnotation(Inheritance.class); + if (a != null) { + return true; + } + // search up the inheritance heirarchy + return isInheritanceClass(cls.getSuperclass()); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInheritInfo.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInheritInfo.java index e885dd36d..d522e1941 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInheritInfo.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployInheritInfo.java @@ -1,283 +1,264 @@ -/** - * 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.parse; - -import java.sql.Types; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import com.avaje.ebeaninternal.server.deploy.InheritInfo; - -/** - * Represents a node in the Inheritance tree. - * Holds information regarding Super Subclass support. - */ -public class DeployInheritInfo { - - /** - * the default discriminator column according to the JPA 1.0 spec. - */ - private static final String JPA_DEFAULT_DISCRIM_COLUMN = "dtype"; - - private int discriminatorLength; - - private int discriminatorType; - - private String discriminatorStringValue; - private Object discriminatorObjectValue; - - private String discriminatorColumn; - - private String discriminatorWhere; - - private Class type; - - private Class parent; - - private ArrayList children = new ArrayList(); - - /** - * Create for a given type. - */ - public DeployInheritInfo(Class type){ - this.type = type; - } - - /** - * return the type. - */ - public Class getType() { - return type; - } - - /** - * Return the type of the root object. - */ - public Class getParent() { - return parent; - } - - /** - * Set the type of the root object. - */ - public void setParent(Class parent) { - this.parent = parent; - } - - /** - * Return true if this is abstract node. - */ - public boolean isAbstract() { - return (discriminatorObjectValue == null); - } - - /** - * Return true if this is the root node. - */ - public boolean isRoot(){ - return parent == null; - } - - /** - * Return the child nodes. - */ - public Iterator children() { - return children.iterator(); - } - - /** - * Add a child node. - */ - public void addChild(DeployInheritInfo childInfo){ - children.add(childInfo); - } - - /** - * Return the derived where for the discriminator. - */ - public String getDiscriminatorWhere() { - return discriminatorWhere; - } - - /** - * Set the derived where for the discriminator. - */ - public void setDiscriminatorWhere(String discriminatorWhere) { - this.discriminatorWhere = discriminatorWhere; - } - - /** - * Return the column name of the discriminator. - */ - public String getDiscriminatorColumn(InheritInfo parent) { - if (discriminatorColumn == null){ - if (parent == null){ - discriminatorColumn = JPA_DEFAULT_DISCRIM_COLUMN; - } else { - discriminatorColumn = parent.getDiscriminatorColumn(); - } - } - return discriminatorColumn; - } - - /** - * Set the column name of the discriminator. - */ - public void setDiscriminatorColumn(String discriminatorColumn) { - this.discriminatorColumn = discriminatorColumn; - } - - public int getDiscriminatorLength(InheritInfo parent) { - if (discriminatorLength == 0){ - if (parent == null){ - discriminatorLength = 10; - } else { - discriminatorLength = parent.getDiscriminatorLength(); - } - } - return discriminatorLength; - } - - /** - * Return the sql type of the discriminator value. - */ - public int getDiscriminatorType(InheritInfo parent) { - if (discriminatorType == 0){ - if (parent == null){ - discriminatorType = Types.VARCHAR; - } else { - discriminatorType = parent.getDiscriminatorType(); - } - } - return discriminatorType; - } - - /** - * Set the sql type of the discriminator. - */ - public void setDiscriminatorType(int discriminatorType) { - this.discriminatorType = discriminatorType; - } - - /** - * Return the length of the discriminator column. - */ - public int getDiscriminatorLength() { - return discriminatorLength; - } - - /** - * Set the length of the discriminator column. - */ - public void setDiscriminatorLength(int discriminatorLength) { - this.discriminatorLength = discriminatorLength; - } - - /** - * Return the discriminator value for this node. - */ - public Object getDiscriminatorObjectValue() { - return discriminatorObjectValue; - } - - public String getDiscriminatorStringValue() { - return discriminatorStringValue; - } - - /** - * Set the discriminator value for this node. - */ - public void setDiscriminatorValue(String value) { - if (value != null){ - value = value.trim(); - if (value.length() == 0){ - value = null; - } else { - discriminatorStringValue = value; - // convert the value if desired - if (discriminatorType == Types.INTEGER){ - this.discriminatorObjectValue = Integer.valueOf(value.toString()); - } else { - this.discriminatorObjectValue = value; - } - } - } - } - - public String getWhere() { - - List discList = new ArrayList(); - - appendDiscriminator(discList); - - return buildWhereLiteral(discList); - } - - private void appendDiscriminator(List list) { - if (discriminatorObjectValue != null){ - list.add(discriminatorObjectValue); - } - for (DeployInheritInfo child : children) { - child.appendDiscriminator(list); - } - } - - private String buildWhereLiteral(List discList) { - int size = discList.size(); - if (size == 0){ - return ""; - } - StringBuilder sb = new StringBuilder(); - sb.append(discriminatorColumn); - if (size == 1){ - sb.append(" = "); - } else { - sb.append(" in ("); - } - for (int i = 0; i < discList.size(); i++) { - appendSqlLiteralValue(i, discList.get(i), sb); - } - if (size > 1){ - sb.append(")"); - } - return sb.toString(); - } - - private void appendSqlLiteralValue(int count, Object value, StringBuilder sb) { - if (count > 0){ - sb.append(","); - } - if (value instanceof String){ - sb.append("'").append(value).append("'"); - } else { - sb.append(value); - } - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("InheritInfo[").append(type.getName()).append("]"); - sb.append(" root[").append(parent.getName()).append("]"); - sb.append(" disValue[").append(discriminatorStringValue).append("]"); - return sb.toString(); - } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.sql.Types; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import com.avaje.ebeaninternal.server.deploy.InheritInfo; + +/** + * Represents a node in the Inheritance tree. + * Holds information regarding Super Subclass support. + */ +public class DeployInheritInfo { + + /** + * the default discriminator column according to the JPA 1.0 spec. + */ + private static final String JPA_DEFAULT_DISCRIM_COLUMN = "dtype"; + + private int discriminatorLength; + + private int discriminatorType; + + private String discriminatorStringValue; + private Object discriminatorObjectValue; + + private String discriminatorColumn; + + private String discriminatorWhere; + + private Class type; + + private Class parent; + + private ArrayList children = new ArrayList(); + + /** + * Create for a given type. + */ + public DeployInheritInfo(Class type){ + this.type = type; + } + + /** + * return the type. + */ + public Class getType() { + return type; + } + + /** + * Return the type of the root object. + */ + public Class getParent() { + return parent; + } + + /** + * Set the type of the root object. + */ + public void setParent(Class parent) { + this.parent = parent; + } + + /** + * Return true if this is abstract node. + */ + public boolean isAbstract() { + return (discriminatorObjectValue == null); + } + + /** + * Return true if this is the root node. + */ + public boolean isRoot(){ + return parent == null; + } + + /** + * Return the child nodes. + */ + public Iterator children() { + return children.iterator(); + } + + /** + * Add a child node. + */ + public void addChild(DeployInheritInfo childInfo){ + children.add(childInfo); + } + + /** + * Return the derived where for the discriminator. + */ + public String getDiscriminatorWhere() { + return discriminatorWhere; + } + + /** + * Set the derived where for the discriminator. + */ + public void setDiscriminatorWhere(String discriminatorWhere) { + this.discriminatorWhere = discriminatorWhere; + } + + /** + * Return the column name of the discriminator. + */ + public String getDiscriminatorColumn(InheritInfo parent) { + if (discriminatorColumn == null){ + if (parent == null){ + discriminatorColumn = JPA_DEFAULT_DISCRIM_COLUMN; + } else { + discriminatorColumn = parent.getDiscriminatorColumn(); + } + } + return discriminatorColumn; + } + + /** + * Set the column name of the discriminator. + */ + public void setDiscriminatorColumn(String discriminatorColumn) { + this.discriminatorColumn = discriminatorColumn; + } + + public int getDiscriminatorLength(InheritInfo parent) { + if (discriminatorLength == 0){ + if (parent == null){ + discriminatorLength = 10; + } else { + discriminatorLength = parent.getDiscriminatorLength(); + } + } + return discriminatorLength; + } + + /** + * Return the sql type of the discriminator value. + */ + public int getDiscriminatorType(InheritInfo parent) { + if (discriminatorType == 0){ + if (parent == null){ + discriminatorType = Types.VARCHAR; + } else { + discriminatorType = parent.getDiscriminatorType(); + } + } + return discriminatorType; + } + + /** + * Set the sql type of the discriminator. + */ + public void setDiscriminatorType(int discriminatorType) { + this.discriminatorType = discriminatorType; + } + + /** + * Return the length of the discriminator column. + */ + public int getDiscriminatorLength() { + return discriminatorLength; + } + + /** + * Set the length of the discriminator column. + */ + public void setDiscriminatorLength(int discriminatorLength) { + this.discriminatorLength = discriminatorLength; + } + + /** + * Return the discriminator value for this node. + */ + public Object getDiscriminatorObjectValue() { + return discriminatorObjectValue; + } + + public String getDiscriminatorStringValue() { + return discriminatorStringValue; + } + + /** + * Set the discriminator value for this node. + */ + public void setDiscriminatorValue(String value) { + if (value != null){ + value = value.trim(); + if (value.length() == 0){ + value = null; + } else { + discriminatorStringValue = value; + // convert the value if desired + if (discriminatorType == Types.INTEGER){ + this.discriminatorObjectValue = Integer.valueOf(value.toString()); + } else { + this.discriminatorObjectValue = value; + } + } + } + } + + public String getWhere() { + + List discList = new ArrayList(); + + appendDiscriminator(discList); + + return buildWhereLiteral(discList); + } + + private void appendDiscriminator(List list) { + if (discriminatorObjectValue != null){ + list.add(discriminatorObjectValue); + } + for (DeployInheritInfo child : children) { + child.appendDiscriminator(list); + } + } + + private String buildWhereLiteral(List discList) { + int size = discList.size(); + if (size == 0){ + return ""; + } + StringBuilder sb = new StringBuilder(); + sb.append(discriminatorColumn); + if (size == 1){ + sb.append(" = "); + } else { + sb.append(" in ("); + } + for (int i = 0; i < discList.size(); i++) { + appendSqlLiteralValue(i, discList.get(i), sb); + } + if (size > 1){ + sb.append(")"); + } + return sb.toString(); + } + + private void appendSqlLiteralValue(int count, Object value, StringBuilder sb) { + if (count > 0){ + sb.append(","); + } + if (value instanceof String){ + sb.append("'").append(value).append("'"); + } else { + sb.append(value); + } + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("InheritInfo[").append(type.getName()).append("]"); + sb.append(" root[").append(parent.getName()).append("]"); + sb.append(" disValue[").append(discriminatorStringValue).append("]"); + return sb.toString(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java index b122e1e2b..8038c36a4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java @@ -1,266 +1,247 @@ -/** - * 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.parse; - -import java.lang.annotation.Annotation; -import java.sql.Types; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.PersistenceException; - -import com.avaje.ebean.config.EncryptDeploy; -import com.avaje.ebean.config.EncryptDeployManager; -import com.avaje.ebean.config.EncryptKeyManager; -import com.avaje.ebean.config.Encryptor; -import com.avaje.ebean.config.NamingConvention; -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebean.config.TableName; -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebean.validation.factory.Validator; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; -import com.avaje.ebeaninternal.server.type.DataEncryptSupport; -import com.avaje.ebeaninternal.server.type.ScalarType; -import com.avaje.ebeaninternal.server.type.ScalarTypeEnumStandard; -import com.avaje.ebeaninternal.server.type.SimpleAesEncryptor; -import com.avaje.ebeaninternal.server.type.TypeManager; - -/** - * Utility object to help processing deployment information. - */ -public class DeployUtil { - - private static final Logger logger = Logger.getLogger(DeployUtil.class.getName()); - - - - /** - * Assumes CLOB rather than LONGVARCHAR. - */ - private static final int dbCLOBType = Types.CLOB; - - /** - * Assumes BLOB rather than LONGVARBINARY. This should probably be - * configurable. - */ - private static final int dbBLOBType = Types.BLOB; - - private final NamingConvention namingConvention; - - private final TypeManager typeManager; - - private final ValidatorFactoryManager validatorFactoryManager; - - private final String manyToManyAlias; - - private final DatabasePlatform dbPlatform; - - private final EncryptDeployManager encryptDeployManager; - - private final EncryptKeyManager encryptKeyManager; - - private final Encryptor bytesEncryptor; - - public DeployUtil(TypeManager typeMgr, ServerConfig serverConfig) { - - this.typeManager = typeMgr; - this.namingConvention = serverConfig.getNamingConvention(); - this.dbPlatform = serverConfig.getDatabasePlatform(); - this.encryptDeployManager = serverConfig.getEncryptDeployManager(); - this.encryptKeyManager = serverConfig.getEncryptKeyManager(); - - Encryptor be = serverConfig.getEncryptor(); - this.bytesEncryptor = be != null ? be : new SimpleAesEncryptor(); - - // this alias is used for ManyToMany lazy loading queries - this.manyToManyAlias = "zzzzzz"; - - this.validatorFactoryManager = new ValidatorFactoryManager(); - } - - public TypeManager getTypeManager() { - return typeManager; - } - - public DatabasePlatform getDbPlatform() { - return dbPlatform; - } - - public NamingConvention getNamingConvention() { - return namingConvention; - } - - /** - * Check that the EncryptKeyManager has been defined. - */ - public void checkEncryptKeyManagerDefined(String fullPropName) { - if (encryptKeyManager == null){ - String msg = "Using encryption on "+fullPropName+" but no EncryptKeyManager defined!"; - throw new PersistenceException(msg); - } - } - - public EncryptDeploy getEncryptDeploy(TableName table, String column) { - if (encryptDeployManager == null){ - return EncryptDeploy.ANNOTATION; - } - return encryptDeployManager.getEncryptDeploy(table, column); - } - - public DataEncryptSupport createDataEncryptSupport(String table, String column) { - return new DataEncryptSupport(encryptKeyManager, bytesEncryptor, table, column); - } - - /** - * Return the table alias used for ManyToMany joins. - */ - public String getManyToManyAlias() { - return manyToManyAlias; - } - - public void createValidator(DeployBeanProperty prop, Annotation ann) { - try { - Validator validator = validatorFactoryManager.create(ann, prop.getPropertyType()); - if (validator != null){ - prop.addValidator(validator); - } - } catch (Exception e){ - String msg = "Error creating a validator on "+prop.getFullBeanName(); - logger.log(Level.SEVERE, msg, e); - } - } - - public ScalarType setEnumScalarType(Enumerated enumerated, DeployBeanProperty prop) { - - Class enumType = prop.getPropertyType(); - if (!enumType.isEnum()) { - throw new IllegalArgumentException("Class ["+enumType+"] is Not a Enum?"); - } - ScalarType scalarType = typeManager.getScalarType(enumType); - if (scalarType == null) { - // see if it has a Mapping in avaje.properties - scalarType = typeManager.createEnumScalarType(enumType); - if (scalarType == null){ - // use JPA normal Enum type (without mapping) - EnumType type = enumerated != null? enumerated.value(): null; - scalarType = createEnumScalarTypePerSpec(enumType, type, prop.getDbType()); - } - - typeManager.add(scalarType); - } - prop.setScalarType(scalarType); - prop.setDbType(scalarType.getJdbcType()); - return scalarType; - } - - private ScalarType createEnumScalarTypePerSpec(Class enumType, EnumType type, int dbType) { - - if (type == null) { - // default as per spec is ORDINAL - return new ScalarTypeEnumStandard.OrdinalEnum(enumType); - - } else if (type == EnumType.ORDINAL) { - return new ScalarTypeEnumStandard.OrdinalEnum(enumType); - - } else { - return new ScalarTypeEnumStandard.StringEnum(enumType); - } - } - - /** - * Find the ScalarType for this property. - *

- * This determines if there is a conversion required from the logical (bean) - * type to a DB (jdbc) type. This is the case for java.util.Date etc. - *

- */ - public void setScalarType(DeployBeanProperty property) { - - if (property.getScalarType() != null){ - // already has a ScalarType assigned. - // this will be an Enum type... - return; - } - if (property instanceof DeployBeanPropertyCompound){ - // compound properties have a CvoInternalType instead - return; - } - - ScalarType scalarType = getScalarType(property); - if (scalarType != null){ - // set the jdbc type this maps to - - property.setDbType(scalarType.getJdbcType()); - property.setScalarType(scalarType); - } - } - - private ScalarType getScalarType(DeployBeanProperty property) { - - // Note that Temporal types already have dbType - // set via annotations - Class propType = property.getPropertyType(); - ScalarType scalarType = typeManager.getScalarType(propType, property.getDbType()); - if (scalarType != null) { - return scalarType; - } - - String msg = property.getFullBeanName()+" has no ScalarType - type[" + propType.getName() + "]"; - if (!property.isTransient()){ - throw new PersistenceException(msg); - - } else { - // this is ok... - logger.finest("... transient property "+msg); - return null; - } - } - - /** - * This property is marked as a Lob object. - */ - public void setLobType(DeployBeanProperty prop) { - - // is String or byte[] ? used to determine if its a CLOB or BLOB - Class type = prop.getPropertyType(); - - // this also sets the lob flag on DeployBeanProperty - int lobType = isClobType(type) ? dbCLOBType : dbBLOBType; - - ScalarType scalarType = typeManager.getScalarType(type, lobType); - if (scalarType == null) { - // this should never occur actually - throw new RuntimeException("No ScalarType for LOB type ["+type+"] ["+lobType+"]"); - } - prop.setDbType(lobType); - prop.setScalarType(scalarType); - } - - public boolean isClobType(Class type){ - if (type.equals(String.class)){ - return true; - } - return false; - } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.lang.annotation.Annotation; +import java.sql.Types; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.PersistenceException; + +import com.avaje.ebean.config.EncryptDeploy; +import com.avaje.ebean.config.EncryptDeployManager; +import com.avaje.ebean.config.EncryptKeyManager; +import com.avaje.ebean.config.Encryptor; +import com.avaje.ebean.config.NamingConvention; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.config.TableName; +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.validation.factory.Validator; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyCompound; +import com.avaje.ebeaninternal.server.type.DataEncryptSupport; +import com.avaje.ebeaninternal.server.type.ScalarType; +import com.avaje.ebeaninternal.server.type.ScalarTypeEnumStandard; +import com.avaje.ebeaninternal.server.type.SimpleAesEncryptor; +import com.avaje.ebeaninternal.server.type.TypeManager; + +/** + * Utility object to help processing deployment information. + */ +public class DeployUtil { + + private static final Logger logger = Logger.getLogger(DeployUtil.class.getName()); + + + + /** + * Assumes CLOB rather than LONGVARCHAR. + */ + private static final int dbCLOBType = Types.CLOB; + + /** + * Assumes BLOB rather than LONGVARBINARY. This should probably be + * configurable. + */ + private static final int dbBLOBType = Types.BLOB; + + private final NamingConvention namingConvention; + + private final TypeManager typeManager; + + private final ValidatorFactoryManager validatorFactoryManager; + + private final String manyToManyAlias; + + private final DatabasePlatform dbPlatform; + + private final EncryptDeployManager encryptDeployManager; + + private final EncryptKeyManager encryptKeyManager; + + private final Encryptor bytesEncryptor; + + public DeployUtil(TypeManager typeMgr, ServerConfig serverConfig) { + + this.typeManager = typeMgr; + this.namingConvention = serverConfig.getNamingConvention(); + this.dbPlatform = serverConfig.getDatabasePlatform(); + this.encryptDeployManager = serverConfig.getEncryptDeployManager(); + this.encryptKeyManager = serverConfig.getEncryptKeyManager(); + + Encryptor be = serverConfig.getEncryptor(); + this.bytesEncryptor = be != null ? be : new SimpleAesEncryptor(); + + // this alias is used for ManyToMany lazy loading queries + this.manyToManyAlias = "zzzzzz"; + + this.validatorFactoryManager = new ValidatorFactoryManager(); + } + + public TypeManager getTypeManager() { + return typeManager; + } + + public DatabasePlatform getDbPlatform() { + return dbPlatform; + } + + public NamingConvention getNamingConvention() { + return namingConvention; + } + + /** + * Check that the EncryptKeyManager has been defined. + */ + public void checkEncryptKeyManagerDefined(String fullPropName) { + if (encryptKeyManager == null){ + String msg = "Using encryption on "+fullPropName+" but no EncryptKeyManager defined!"; + throw new PersistenceException(msg); + } + } + + public EncryptDeploy getEncryptDeploy(TableName table, String column) { + if (encryptDeployManager == null){ + return EncryptDeploy.ANNOTATION; + } + return encryptDeployManager.getEncryptDeploy(table, column); + } + + public DataEncryptSupport createDataEncryptSupport(String table, String column) { + return new DataEncryptSupport(encryptKeyManager, bytesEncryptor, table, column); + } + + /** + * Return the table alias used for ManyToMany joins. + */ + public String getManyToManyAlias() { + return manyToManyAlias; + } + + public void createValidator(DeployBeanProperty prop, Annotation ann) { + try { + Validator validator = validatorFactoryManager.create(ann, prop.getPropertyType()); + if (validator != null){ + prop.addValidator(validator); + } + } catch (Exception e){ + String msg = "Error creating a validator on "+prop.getFullBeanName(); + logger.log(Level.SEVERE, msg, e); + } + } + + public ScalarType setEnumScalarType(Enumerated enumerated, DeployBeanProperty prop) { + + Class enumType = prop.getPropertyType(); + if (!enumType.isEnum()) { + throw new IllegalArgumentException("Class ["+enumType+"] is Not a Enum?"); + } + ScalarType scalarType = typeManager.getScalarType(enumType); + if (scalarType == null) { + // see if it has a Mapping in avaje.properties + scalarType = typeManager.createEnumScalarType(enumType); + if (scalarType == null){ + // use JPA normal Enum type (without mapping) + EnumType type = enumerated != null? enumerated.value(): null; + scalarType = createEnumScalarTypePerSpec(enumType, type, prop.getDbType()); + } + + typeManager.add(scalarType); + } + prop.setScalarType(scalarType); + prop.setDbType(scalarType.getJdbcType()); + return scalarType; + } + + private ScalarType createEnumScalarTypePerSpec(Class enumType, EnumType type, int dbType) { + + if (type == null) { + // default as per spec is ORDINAL + return new ScalarTypeEnumStandard.OrdinalEnum(enumType); + + } else if (type == EnumType.ORDINAL) { + return new ScalarTypeEnumStandard.OrdinalEnum(enumType); + + } else { + return new ScalarTypeEnumStandard.StringEnum(enumType); + } + } + + /** + * Find the ScalarType for this property. + *

+ * This determines if there is a conversion required from the logical (bean) + * type to a DB (jdbc) type. This is the case for java.util.Date etc. + *

+ */ + public void setScalarType(DeployBeanProperty property) { + + if (property.getScalarType() != null){ + // already has a ScalarType assigned. + // this will be an Enum type... + return; + } + if (property instanceof DeployBeanPropertyCompound){ + // compound properties have a CvoInternalType instead + return; + } + + ScalarType scalarType = getScalarType(property); + if (scalarType != null){ + // set the jdbc type this maps to + + property.setDbType(scalarType.getJdbcType()); + property.setScalarType(scalarType); + } + } + + private ScalarType getScalarType(DeployBeanProperty property) { + + // Note that Temporal types already have dbType + // set via annotations + Class propType = property.getPropertyType(); + ScalarType scalarType = typeManager.getScalarType(propType, property.getDbType()); + if (scalarType != null) { + return scalarType; + } + + String msg = property.getFullBeanName()+" has no ScalarType - type[" + propType.getName() + "]"; + if (!property.isTransient()){ + throw new PersistenceException(msg); + + } else { + // this is ok... + logger.finest("... transient property "+msg); + return null; + } + } + + /** + * This property is marked as a Lob object. + */ + public void setLobType(DeployBeanProperty prop) { + + // is String or byte[] ? used to determine if its a CLOB or BLOB + Class type = prop.getPropertyType(); + + // this also sets the lob flag on DeployBeanProperty + int lobType = isClobType(type) ? dbCLOBType : dbBLOBType; + + ScalarType scalarType = typeManager.getScalarType(type, lobType); + if (scalarType == null) { + // this should never occur actually + throw new RuntimeException("No ScalarType for LOB type ["+type+"] ["+lobType+"]"); + } + prop.setDbType(lobType); + prop.setScalarType(scalarType); + } + + public boolean isClobType(Class type){ + if (type.equals(String.class)){ + return true; + } + return false; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DetectScala.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DetectScala.java index 35e106924..9511da836 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DetectScala.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DetectScala.java @@ -1,62 +1,43 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.deploy.parse; - -import java.util.logging.Logger; - -import com.avaje.ebeaninternal.api.ClassUtil; - -/** - * Used to detected if Scala support is required. - * - * @author rbygrave - */ -public class DetectScala { - - private static final Logger logger = Logger.getLogger(DetectScala.class.getName()); - - private static Class scalaOptionClass = initScalaOptionClass(); - - private static boolean hasScalaSupport = scalaOptionClass != null; - - private static Class initScalaOptionClass() { - try { - return ClassUtil.forName("scala.Option"); - } catch (ClassNotFoundException e) { - // scala not in the classpath... - logger.fine("Scala type 'scala.Option' not found. Scala Support disabled."); - return null; - } - } - - /** - * Return true if scala is in the classpath. - */ - public static boolean hasScalaSupport() { - return hasScalaSupport; - } - - /** - * Return the scala.Option class or null if scala is not in the classpath. - */ - public static Class getScalaOptionClass() { - return scalaOptionClass; - } -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.util.logging.Logger; + +import com.avaje.ebeaninternal.api.ClassUtil; + +/** + * Used to detected if Scala support is required. + * + * @author rbygrave + */ +public class DetectScala { + + private static final Logger logger = Logger.getLogger(DetectScala.class.getName()); + + private static Class scalaOptionClass = initScalaOptionClass(); + + private static boolean hasScalaSupport = scalaOptionClass != null; + + private static Class initScalaOptionClass() { + try { + return ClassUtil.forName("scala.Option"); + } catch (ClassNotFoundException e) { + // scala not in the classpath... + logger.fine("Scala type 'scala.Option' not found. Scala Support disabled."); + return null; + } + } + + /** + * Return true if scala is in the classpath. + */ + public static boolean hasScalaSupport() { + return hasScalaSupport; + } + + /** + * Return the scala.Option class or null if scala is not in the classpath. + */ + public static Class getScalaOptionClass() { + return scalaOptionClass; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/ReadAnnotations.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/ReadAnnotations.java index 35e62c3bb..4b9799b71 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/ReadAnnotations.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/ReadAnnotations.java @@ -1,77 +1,58 @@ -/** - * 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.parse; - -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; - - -/** - * Read the deployment annotations for the bean. - */ -public class ReadAnnotations { - - /** - * Read the initial non-relationship annotations included Id and EmbeddedId. - *

- * We then have enough to create BeanTables which are used in readAssociations - * to resolve the relationships etc. - *

- */ - public void readInitial(DeployBeanInfo info){ - - try { - new AnnotationClass(info).parse(); - new AnnotationFields(info).parse(); - - } catch (RuntimeException e){ - String msg = "Error reading annotations for "+info; - throw new RuntimeException(msg, e); - } - } - - - /** - * Read and process the associated relationship annotations. - *

- * These can only be processed after the BeanTables have been created - *

- *

- * This uses the factory as a call back to get the BeanTable for a given - * associated bean. - *

- */ - public void readAssociations(DeployBeanInfo info, BeanDescriptorManager factory){ - - try { - - new AnnotationAssocOnes(info, factory).parse(); - new AnnotationAssocManys(info, factory).parse(); - - // read the Sql annotations last because they may be - // dependent on field level annotations - new AnnotationSql(info).parse(); - - } catch (RuntimeException e){ - String msg = "Error reading annotations for "+info; - throw new RuntimeException(msg, e); - } - } - -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; + + +/** + * Read the deployment annotations for the bean. + */ +public class ReadAnnotations { + + /** + * Read the initial non-relationship annotations included Id and EmbeddedId. + *

+ * We then have enough to create BeanTables which are used in readAssociations + * to resolve the relationships etc. + *

+ */ + public void readInitial(DeployBeanInfo info){ + + try { + new AnnotationClass(info).parse(); + new AnnotationFields(info).parse(); + + } catch (RuntimeException e){ + String msg = "Error reading annotations for "+info; + throw new RuntimeException(msg, e); + } + } + + + /** + * Read and process the associated relationship annotations. + *

+ * These can only be processed after the BeanTables have been created + *

+ *

+ * This uses the factory as a call back to get the BeanTable for a given + * associated bean. + *

+ */ + public void readAssociations(DeployBeanInfo info, BeanDescriptorManager factory){ + + try { + + new AnnotationAssocOnes(info, factory).parse(); + new AnnotationAssocManys(info, factory).parse(); + + // read the Sql annotations last because they may be + // dependent on field level annotations + new AnnotationSql(info).parse(); + + } catch (RuntimeException e){ + String msg = "Error reading annotations for "+info; + throw new RuntimeException(msg, e); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/TransientProperties.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/TransientProperties.java index e4b3c76d2..7e078373f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/TransientProperties.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/TransientProperties.java @@ -1,70 +1,51 @@ -/** - * 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.parse; - -import java.util.List; - -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; - -/** - * Mark transient properties. - */ -public class TransientProperties { - - public TransientProperties() { - } - - /** - * Mark any additional properties as transient. - */ - public void process(DeployBeanDescriptor desc) { - - List props = desc.propertiesBase(); - for (int i = 0; i < props.size(); i++) { - DeployBeanProperty prop = props.get(i); - if (!prop.isDbRead() && !prop.isDbInsertable() && !prop.isDbUpdateable()) { - // non-transient... - prop.setTransient(true); - } - } - - List> ones = desc.propertiesAssocOne(); - for (int i = 0; i < ones.size(); i++) { - DeployBeanPropertyAssocOne prop = ones.get(i); - if (prop.getBeanTable() == null) { - if (!prop.isEmbedded()) { - prop.setTransient(true); - } - } - } - - List> manys = desc.propertiesAssocMany(); - for (int i = 0; i < manys.size(); i++) { - DeployBeanPropertyAssocMany prop = manys.get(i); - if (prop.getBeanTable() == null) { - prop.setTransient(true); - } - } - - } -} +package com.avaje.ebeaninternal.server.deploy.parse; + +import java.util.List; + +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne; + +/** + * Mark transient properties. + */ +public class TransientProperties { + + public TransientProperties() { + } + + /** + * Mark any additional properties as transient. + */ + public void process(DeployBeanDescriptor desc) { + + List props = desc.propertiesBase(); + for (int i = 0; i < props.size(); i++) { + DeployBeanProperty prop = props.get(i); + if (!prop.isDbRead() && !prop.isDbInsertable() && !prop.isDbUpdateable()) { + // non-transient... + prop.setTransient(true); + } + } + + List> ones = desc.propertiesAssocOne(); + for (int i = 0; i < ones.size(); i++) { + DeployBeanPropertyAssocOne prop = ones.get(i); + if (prop.getBeanTable() == null) { + if (!prop.isEmbedded()) { + prop.setTransient(true); + } + } + } + + List> manys = desc.propertiesAssocMany(); + for (int i = 0; i < manys.size(); i++) { + DeployBeanPropertyAssocMany prop = manys.get(i); + if (prop.getBeanTable() == null) { + prop.setTransient(true); + } + } + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/el/CharMatch.java b/src/main/java/com/avaje/ebeaninternal/server/el/CharMatch.java index 932ac0db0..48848b5ee 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/el/CharMatch.java +++ b/src/main/java/com/avaje/ebeaninternal/server/el/CharMatch.java @@ -1,78 +1,59 @@ -/** - * 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.el; - -/** - * Case insensitive string matching. - *

- * Provides an alternative to using regular expressions. - *

- */ -public final class CharMatch { - - private final char[] upperChars; - - private final int maxLength; - - public CharMatch(String s) { - this.upperChars = s.toUpperCase().toCharArray(); - this.maxLength = upperChars.length; - } - - public boolean startsWith(String other) { - - if (other == null || other.length() < maxLength){ - return false; - } - - char ta[] = other.toCharArray(); - - int pos = -1; - while (++pos < maxLength) { - char c1 = upperChars[pos]; - char c2 = Character.toUpperCase(ta[pos]); - if (c1 != c2) { - return false; - } - } - return true; - } - - public boolean endsWith(String other) { - - if (other == null || other.length() < maxLength){ - return false; - } - - char ta[] = other.toCharArray(); - - int offset = ta.length - maxLength; - int pos = maxLength; - while (--pos >= 0) { - char c1 = upperChars[pos]; - char c2 = Character.toUpperCase(ta[offset+pos]); - if (c1 != c2) { - return false; - } - } - return true; - } - -} +package com.avaje.ebeaninternal.server.el; + +/** + * Case insensitive string matching. + *

+ * Provides an alternative to using regular expressions. + *

+ */ +public final class CharMatch { + + private final char[] upperChars; + + private final int maxLength; + + public CharMatch(String s) { + this.upperChars = s.toUpperCase().toCharArray(); + this.maxLength = upperChars.length; + } + + public boolean startsWith(String other) { + + if (other == null || other.length() < maxLength){ + return false; + } + + char ta[] = other.toCharArray(); + + int pos = -1; + while (++pos < maxLength) { + char c1 = upperChars[pos]; + char c2 = Character.toUpperCase(ta[pos]); + if (c1 != c2) { + return false; + } + } + return true; + } + + public boolean endsWith(String other) { + + if (other == null || other.length() < maxLength){ + return false; + } + + char ta[] = other.toCharArray(); + + int offset = ta.length - maxLength; + int pos = maxLength; + while (--pos >= 0) { + char c1 = upperChars[pos]; + char c2 = Character.toUpperCase(ta[offset+pos]); + if (c1 != c2) { + return false; + } + } + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/el/ElComparator.java b/src/main/java/com/avaje/ebeaninternal/server/el/ElComparator.java index 8dc93af91..13edc5d76 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/el/ElComparator.java +++ b/src/main/java/com/avaje/ebeaninternal/server/el/ElComparator.java @@ -1,39 +1,20 @@ -/** - * 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.el; - -import java.util.Comparator; - -/** - * Comparator for use with the expression objects. - */ -public interface ElComparator extends Comparator { - - /** - * Compare given 2 beans. - */ - public int compare(T o1, T o2); - - /** - * Compare with a fixed value to a given bean. - */ - public int compareValue(Object value, T o2); - +package com.avaje.ebeaninternal.server.el; + +import java.util.Comparator; + +/** + * Comparator for use with the expression objects. + */ +public interface ElComparator extends Comparator { + + /** + * Compare given 2 beans. + */ + public int compare(T o1, T o2); + + /** + * Compare with a fixed value to a given bean. + */ + public int compareValue(Object value, T o2); + } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/el/ElComparatorCompound.java b/src/main/java/com/avaje/ebeaninternal/server/el/ElComparatorCompound.java index 57e29b428..eabe3fb43 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/el/ElComparatorCompound.java +++ b/src/main/java/com/avaje/ebeaninternal/server/el/ElComparatorCompound.java @@ -1,67 +1,48 @@ -/** - * 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.el; - -import java.util.Comparator; - -/** - * Comparator based on multiple ordered comparators. - *

- * eg. "name, orderDate desc, id" - *

- */ -public final class ElComparatorCompound implements Comparator, ElComparator { - - private final ElComparator[] array; - - public ElComparatorCompound(ElComparator[] array) { - this.array = array; - } - - public int compare(T o1, T o2) { - - for (int i = 0; i < array.length; i++) { - int ret = array[i].compare(o1, o2); - if (ret != 0){ - return ret; - } - } - - return 0; - } - - public int compareValue(Object value, T o2) { - - for (int i = 0; i < array.length; i++) { - int ret = array[i].compareValue(value, o2); - if (ret != 0){ - return ret; - } - } - - return 0; - } - - - - - - -} +package com.avaje.ebeaninternal.server.el; + +import java.util.Comparator; + +/** + * Comparator based on multiple ordered comparators. + *

+ * eg. "name, orderDate desc, id" + *

+ */ +public final class ElComparatorCompound implements Comparator, ElComparator { + + private final ElComparator[] array; + + public ElComparatorCompound(ElComparator[] array) { + this.array = array; + } + + public int compare(T o1, T o2) { + + for (int i = 0; i < array.length; i++) { + int ret = array[i].compare(o1, o2); + if (ret != 0){ + return ret; + } + } + + return 0; + } + + public int compareValue(Object value, T o2) { + + for (int i = 0; i < array.length; i++) { + int ret = array[i].compareValue(value, o2); + if (ret != 0){ + return ret; + } + } + + return 0; + } + + + + + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/el/ElComparatorProperty.java b/src/main/java/com/avaje/ebeaninternal/server/el/ElComparatorProperty.java index 99d1efd2b..4c5ac1e1c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/el/ElComparatorProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/el/ElComparatorProperty.java @@ -1,70 +1,51 @@ -/** - * 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.el; - -import java.util.Comparator; - -/** - * Comparator based on a ElGetValue. - */ -public final class ElComparatorProperty implements Comparator, ElComparator { - - private final ElPropertyValue elGetValue; - - private final int nullOrder; - - private final int asc; - - public ElComparatorProperty(ElPropertyValue elGetValue, boolean ascending, boolean nullsHigh) { - this.elGetValue = elGetValue; - this.asc = ascending ? 1 : -1; - this.nullOrder = asc * (nullsHigh ? 1 : -1); - } - - public int compare(T o1, T o2) { - - Object val1 = elGetValue.elGetValue(o1); - Object val2 = elGetValue.elGetValue(o2); - - return compareValues(val1, val2); - } - - public int compareValue(Object value, T o2) { - - Object val2 = elGetValue.elGetValue(o2); - - return compareValues(value, val2); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public int compareValues(Object val1, Object val2){ - - if (val1 == null){ - return val2 == null ? 0 : nullOrder; - } - if (val2 == null){ - return -1 * nullOrder; - } - Comparable c = (Comparable)val1; - return asc * c.compareTo(val2); - } - - -} +package com.avaje.ebeaninternal.server.el; + +import java.util.Comparator; + +/** + * Comparator based on a ElGetValue. + */ +public final class ElComparatorProperty implements Comparator, ElComparator { + + private final ElPropertyValue elGetValue; + + private final int nullOrder; + + private final int asc; + + public ElComparatorProperty(ElPropertyValue elGetValue, boolean ascending, boolean nullsHigh) { + this.elGetValue = elGetValue; + this.asc = ascending ? 1 : -1; + this.nullOrder = asc * (nullsHigh ? 1 : -1); + } + + public int compare(T o1, T o2) { + + Object val1 = elGetValue.elGetValue(o1); + Object val2 = elGetValue.elGetValue(o2); + + return compareValues(val1, val2); + } + + public int compareValue(Object value, T o2) { + + Object val2 = elGetValue.elGetValue(o2); + + return compareValues(value, val2); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public int compareValues(Object val1, Object val2){ + + if (val1 == null){ + return val2 == null ? 0 : nullOrder; + } + if (val2 == null){ + return -1 * nullOrder; + } + Comparable c = (Comparable)val1; + return asc * c.compareTo(val2); + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/el/ElFilter.java b/src/main/java/com/avaje/ebeaninternal/server/el/ElFilter.java index 9cec00687..1372e7d4c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/el/ElFilter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/el/ElFilter.java @@ -1,271 +1,252 @@ -/** - * 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.el; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.regex.Pattern; - -import com.avaje.ebean.Filter; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; - -/** - * Default implementation of the Filter interface. - */ -public final class ElFilter implements Filter { - - private final BeanDescriptor beanDescriptor; - - private ArrayList> matches = new ArrayList>(); - - private int maxRows; - - private String sortByClause; - - public ElFilter(BeanDescriptor beanDescriptor) { - this.beanDescriptor = beanDescriptor; - } - - private Object convertValue(String propertyName, Object value) { - // convert type of value to match expected type - ElPropertyValue elGetValue = beanDescriptor.getElGetValue(propertyName); - return elGetValue.elConvertType(value); - } - - private ElComparator getElComparator(String propertyName) { - - return beanDescriptor.getElComparator(propertyName); - } - - private ElPropertyValue getElGetValue(String propertyName) { - - return beanDescriptor.getElGetValue(propertyName); - } - - public Filter sort(String sortByClause) { - this.sortByClause = sortByClause; - return this; - } - - protected boolean isMatch(T bean) { - for (int i = 0; i < matches.size(); i++) { - ElMatcher matcher = matches.get(i); - if (!matcher.isMatch(bean)){ - return false; - } - } - return true; - } - - - public Filter in(String propertyName, Set matchingValues) { - - ElPropertyValue elGetValue = getElGetValue(propertyName); - - matches.add(new ElMatchBuilder.InSet(matchingValues, elGetValue)); - return this; - } - - public Filter eq(String propertyName, Object value) { - - value = convertValue(propertyName, value); - ElComparator comparator = getElComparator(propertyName); - - matches.add(new ElMatchBuilder.Eq(value, comparator)); - return this; - } - - - public Filter ne(String propertyName, Object value) { - - value = convertValue(propertyName, value); - ElComparator comparator = getElComparator(propertyName); - - matches.add(new ElMatchBuilder.Ne(value, comparator)); - return this; - } - - public Filter between(String propertyName, Object min, Object max) { - - ElPropertyValue elGetValue = getElGetValue(propertyName); - min = elGetValue.elConvertType(min); - max = elGetValue.elConvertType(max); - - ElComparator elComparator = getElComparator(propertyName); - - matches.add(new ElMatchBuilder.Between(min, max, elComparator)); - return this; - } - - - public Filter gt(String propertyName, Object value) { - - value = convertValue(propertyName, value); - ElComparator comparator = getElComparator(propertyName); - - matches.add(new ElMatchBuilder.Gt(value, comparator)); - return this; - } - - public Filter ge(String propertyName, Object value) { - - value = convertValue(propertyName, value); - ElComparator comparator = getElComparator(propertyName); - - matches.add(new ElMatchBuilder.Ge(value, comparator)); - return this; - } - - public Filter ieq(String propertyName, String value) { - - ElPropertyValue elGetValue = getElGetValue(propertyName); - - matches.add(new ElMatchBuilder.Ieq(elGetValue, value)); - return this; - } - - - public Filter isNotNull(String propertyName) { - - ElPropertyValue elGetValue = getElGetValue(propertyName); - - matches.add(new ElMatchBuilder.IsNotNull(elGetValue)); - return this; - } - - - public Filter isNull(String propertyName) { - - ElPropertyValue elGetValue = getElGetValue(propertyName); - - matches.add(new ElMatchBuilder.IsNull(elGetValue)); - return this; - } - - - public Filter le(String propertyName, Object value) { - - value = convertValue(propertyName, value); - ElComparator comparator = getElComparator(propertyName); - - matches.add(new ElMatchBuilder.Le(value, comparator)); - return this; - } - - - public Filter lt(String propertyName, Object value) { - - value = convertValue(propertyName, value); - ElComparator comparator = getElComparator(propertyName); - - matches.add(new ElMatchBuilder.Lt(value, comparator)); - return this; - } - - - public Filter regex(String propertyName, String regEx) { - return regex(propertyName, regEx, 0); - } - - public Filter regex(String propertyName, String regEx, int options) { - - ElPropertyValue elGetValue = getElGetValue(propertyName); - - matches.add(new ElMatchBuilder.RegularExpr(elGetValue, regEx, options)); - return this; - } - - public Filter contains(String propertyName, String value) { - - String quote = ".*"+Pattern.quote(value)+".*"; - - ElPropertyValue elGetValue = getElGetValue(propertyName); - matches.add(new ElMatchBuilder.RegularExpr(elGetValue, quote, 0)); - return this; - } - - public Filter icontains(String propertyName, String value) { - - String quote = ".*"+Pattern.quote(value)+".*"; - - ElPropertyValue elGetValue = getElGetValue(propertyName); - matches.add(new ElMatchBuilder.RegularExpr(elGetValue, quote, Pattern.CASE_INSENSITIVE)); - return this; - } - - - public Filter endsWith(String propertyName, String value) { - - ElPropertyValue elGetValue = getElGetValue(propertyName); - matches.add(new ElMatchBuilder.EndsWith(elGetValue, value)); - return this; - } - - public Filter startsWith(String propertyName, String value) { - - ElPropertyValue elGetValue = getElGetValue(propertyName); - matches.add(new ElMatchBuilder.StartsWith(elGetValue, value)); - return this; - } - - public Filter iendsWith(String propertyName, String value) { - - ElPropertyValue elGetValue = getElGetValue(propertyName); - matches.add(new ElMatchBuilder.IEndsWith(elGetValue, value)); - return this; - } - - public Filter istartsWith(String propertyName, String value) { - - ElPropertyValue elGetValue = getElGetValue(propertyName); - matches.add(new ElMatchBuilder.IStartsWith(elGetValue, value)); - return this; - } - - public Filter maxRows(int maxRows) { - this.maxRows = maxRows; - return this; - } - - public List filter(List list) { - - if (sortByClause != null){ - // create shallow copy and sort - list = new ArrayList(list); - beanDescriptor.sort(list, sortByClause); - } - - ArrayList filterList = new ArrayList(); - - for (int i = 0; i < list.size(); i++) { - T t = list.get(i); - if (isMatch(t)) { - filterList.add(t); - if (maxRows > 0 && filterList.size() >= maxRows){ - break; - } - } - } - - return filterList; - } - -} +package com.avaje.ebeaninternal.server.el; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +import com.avaje.ebean.Filter; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Default implementation of the Filter interface. + */ +public final class ElFilter implements Filter { + + private final BeanDescriptor beanDescriptor; + + private ArrayList> matches = new ArrayList>(); + + private int maxRows; + + private String sortByClause; + + public ElFilter(BeanDescriptor beanDescriptor) { + this.beanDescriptor = beanDescriptor; + } + + private Object convertValue(String propertyName, Object value) { + // convert type of value to match expected type + ElPropertyValue elGetValue = beanDescriptor.getElGetValue(propertyName); + return elGetValue.elConvertType(value); + } + + private ElComparator getElComparator(String propertyName) { + + return beanDescriptor.getElComparator(propertyName); + } + + private ElPropertyValue getElGetValue(String propertyName) { + + return beanDescriptor.getElGetValue(propertyName); + } + + public Filter sort(String sortByClause) { + this.sortByClause = sortByClause; + return this; + } + + protected boolean isMatch(T bean) { + for (int i = 0; i < matches.size(); i++) { + ElMatcher matcher = matches.get(i); + if (!matcher.isMatch(bean)){ + return false; + } + } + return true; + } + + + public Filter in(String propertyName, Set matchingValues) { + + ElPropertyValue elGetValue = getElGetValue(propertyName); + + matches.add(new ElMatchBuilder.InSet(matchingValues, elGetValue)); + return this; + } + + public Filter eq(String propertyName, Object value) { + + value = convertValue(propertyName, value); + ElComparator comparator = getElComparator(propertyName); + + matches.add(new ElMatchBuilder.Eq(value, comparator)); + return this; + } + + + public Filter ne(String propertyName, Object value) { + + value = convertValue(propertyName, value); + ElComparator comparator = getElComparator(propertyName); + + matches.add(new ElMatchBuilder.Ne(value, comparator)); + return this; + } + + public Filter between(String propertyName, Object min, Object max) { + + ElPropertyValue elGetValue = getElGetValue(propertyName); + min = elGetValue.elConvertType(min); + max = elGetValue.elConvertType(max); + + ElComparator elComparator = getElComparator(propertyName); + + matches.add(new ElMatchBuilder.Between(min, max, elComparator)); + return this; + } + + + public Filter gt(String propertyName, Object value) { + + value = convertValue(propertyName, value); + ElComparator comparator = getElComparator(propertyName); + + matches.add(new ElMatchBuilder.Gt(value, comparator)); + return this; + } + + public Filter ge(String propertyName, Object value) { + + value = convertValue(propertyName, value); + ElComparator comparator = getElComparator(propertyName); + + matches.add(new ElMatchBuilder.Ge(value, comparator)); + return this; + } + + public Filter ieq(String propertyName, String value) { + + ElPropertyValue elGetValue = getElGetValue(propertyName); + + matches.add(new ElMatchBuilder.Ieq(elGetValue, value)); + return this; + } + + + public Filter isNotNull(String propertyName) { + + ElPropertyValue elGetValue = getElGetValue(propertyName); + + matches.add(new ElMatchBuilder.IsNotNull(elGetValue)); + return this; + } + + + public Filter isNull(String propertyName) { + + ElPropertyValue elGetValue = getElGetValue(propertyName); + + matches.add(new ElMatchBuilder.IsNull(elGetValue)); + return this; + } + + + public Filter le(String propertyName, Object value) { + + value = convertValue(propertyName, value); + ElComparator comparator = getElComparator(propertyName); + + matches.add(new ElMatchBuilder.Le(value, comparator)); + return this; + } + + + public Filter lt(String propertyName, Object value) { + + value = convertValue(propertyName, value); + ElComparator comparator = getElComparator(propertyName); + + matches.add(new ElMatchBuilder.Lt(value, comparator)); + return this; + } + + + public Filter regex(String propertyName, String regEx) { + return regex(propertyName, regEx, 0); + } + + public Filter regex(String propertyName, String regEx, int options) { + + ElPropertyValue elGetValue = getElGetValue(propertyName); + + matches.add(new ElMatchBuilder.RegularExpr(elGetValue, regEx, options)); + return this; + } + + public Filter contains(String propertyName, String value) { + + String quote = ".*"+Pattern.quote(value)+".*"; + + ElPropertyValue elGetValue = getElGetValue(propertyName); + matches.add(new ElMatchBuilder.RegularExpr(elGetValue, quote, 0)); + return this; + } + + public Filter icontains(String propertyName, String value) { + + String quote = ".*"+Pattern.quote(value)+".*"; + + ElPropertyValue elGetValue = getElGetValue(propertyName); + matches.add(new ElMatchBuilder.RegularExpr(elGetValue, quote, Pattern.CASE_INSENSITIVE)); + return this; + } + + + public Filter endsWith(String propertyName, String value) { + + ElPropertyValue elGetValue = getElGetValue(propertyName); + matches.add(new ElMatchBuilder.EndsWith(elGetValue, value)); + return this; + } + + public Filter startsWith(String propertyName, String value) { + + ElPropertyValue elGetValue = getElGetValue(propertyName); + matches.add(new ElMatchBuilder.StartsWith(elGetValue, value)); + return this; + } + + public Filter iendsWith(String propertyName, String value) { + + ElPropertyValue elGetValue = getElGetValue(propertyName); + matches.add(new ElMatchBuilder.IEndsWith(elGetValue, value)); + return this; + } + + public Filter istartsWith(String propertyName, String value) { + + ElPropertyValue elGetValue = getElGetValue(propertyName); + matches.add(new ElMatchBuilder.IStartsWith(elGetValue, value)); + return this; + } + + public Filter maxRows(int maxRows) { + this.maxRows = maxRows; + return this; + } + + public List filter(List list) { + + if (sortByClause != null){ + // create shallow copy and sort + list = new ArrayList(list); + beanDescriptor.sort(list, sortByClause); + } + + ArrayList filterList = new ArrayList(); + + for (int i = 0; i < list.size(); i++) { + T t = list.get(i); + if (isMatch(t)) { + filterList.add(t); + if (maxRows > 0 && filterList.size() >= maxRows){ + break; + } + } + } + + return filterList; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/el/ElMatchBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/el/ElMatchBuilder.java index 49c0e6b93..c7e6ab36a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/el/ElMatchBuilder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/el/ElMatchBuilder.java @@ -1,305 +1,286 @@ -/** - * 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.el; - -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Pattern; - - -/** - * Contains the various ElMatcher implementations. - */ -class ElMatchBuilder { - - /** - * Case insensitive equals. - */ - static class RegularExpr implements ElMatcher { - - final ElPropertyValue elGetValue; - final String value; - final Pattern pattern; - - RegularExpr(ElPropertyValue elGetValue, String value, int options){ - this.elGetValue = elGetValue; - this.value = value; - this.pattern = Pattern.compile(value, options); - } - - public boolean isMatch(T bean) { - String v = (String)elGetValue.elGetValue(bean); - return pattern.matcher(v).matches(); - } - } - - /** - * Case insensitive equals. - */ - static abstract class BaseString implements ElMatcher { - - final ElPropertyValue elGetValue; - final String value; - - public BaseString(ElPropertyValue elGetValue, String value){ - this.elGetValue = elGetValue; - this.value = value; - } - - public abstract boolean isMatch(T bean); - } - - static class Ieq extends BaseString { - Ieq(ElPropertyValue elGetValue, String value) { - super(elGetValue, value); - } - - public boolean isMatch(T bean) { - String v = (String)elGetValue.elGetValue(bean); - return value.equalsIgnoreCase(v); - } - } - - /** - * Case insensitive starts with matcher. - */ - static class IStartsWith implements ElMatcher { - - final ElPropertyValue elGetValue; - final CharMatch charMatch; - - IStartsWith(ElPropertyValue elGetValue, String value) { - this.elGetValue = elGetValue; - this.charMatch = new CharMatch(value); - } - - public boolean isMatch(T bean) { - - String v = (String)elGetValue.elGetValue(bean); - return charMatch.startsWith(v); - } - } - - /** - * Case insensitive ends with matcher. - */ - static class IEndsWith implements ElMatcher { - - final ElPropertyValue elGetValue; - final CharMatch charMatch; - - IEndsWith(ElPropertyValue elGetValue, String value) { - this.elGetValue = elGetValue; - this.charMatch = new CharMatch(value); - } - - public boolean isMatch(T bean) { - - String v = (String)elGetValue.elGetValue(bean); - return charMatch.endsWith(v); - } - } - - static class StartsWith extends BaseString { - StartsWith(ElPropertyValue elGetValue, String value) { - super(elGetValue, value); - } - - public boolean isMatch(T bean) { - String v = (String)elGetValue.elGetValue(bean); - return value.startsWith(v); - } - } - - static class EndsWith extends BaseString { - EndsWith(ElPropertyValue elGetValue, String value) { - super(elGetValue, value); - } - - public boolean isMatch(T bean) { - String v = (String)elGetValue.elGetValue(bean); - return value.endsWith(v); - } - } - - static class IsNull implements ElMatcher { - - final ElPropertyValue elGetValue; - - public IsNull(ElPropertyValue elGetValue){ - this.elGetValue = elGetValue; - } - - public boolean isMatch(T bean) { - return (null == elGetValue.elGetValue(bean)); - } - } - - static class IsNotNull implements ElMatcher { - - final ElPropertyValue elGetValue; - - public IsNotNull(ElPropertyValue elGetValue){ - this.elGetValue = elGetValue; - } - - public boolean isMatch(T bean) { - return (null != elGetValue.elGetValue(bean)); - } - } - - static abstract class Base implements ElMatcher { - - final Object filterValue; - - final ElComparator comparator; - - public Base(Object filterValue, ElComparator comparator){ - this.filterValue = filterValue; - this.comparator = comparator; - } - - public abstract boolean isMatch(T value); - } - - static class InSet implements ElMatcher { - - final Set set; - final ElPropertyValue elGetValue; - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public InSet(Set set, ElPropertyValue elGetValue){ - this.set = new HashSet(set); - this.elGetValue = elGetValue; - } - - public boolean isMatch(T bean) { - - Object value = elGetValue.elGetValue(bean); - if (value == null){ - return false; - } - - return set.contains(value); - } - - } - /** - * Equal To. - */ - static class Eq extends Base { - - public Eq(Object filterValue, ElComparator comparator){ - super(filterValue, comparator); - } - - public boolean isMatch(T value) { - return comparator.compareValue(filterValue, value) == 0; - } - } - - /** - * Not Equal To. - */ - static class Ne extends Base { - - public Ne(Object filterValue, ElComparator comparator){ - super(filterValue, comparator); - } - - public boolean isMatch(T value) { - return comparator.compareValue(filterValue, value) != 0; - } - } - - /** - * Between. - */ - static class Between implements ElMatcher { - - final Object min; - final Object max; - final ElComparator comparator; - - Between(Object min, Object max, ElComparator comparator){ - this.min = min; - this.max = max; - this.comparator = comparator; - } - - public boolean isMatch(T value) { - - return (comparator.compareValue(min, value) <= 0 - && comparator.compareValue(max, value) >= 0); - } - } - - /** - * Greater Than. - */ - static class Gt extends Base { - Gt(Object filterValue, ElComparator comparator){ - super(filterValue, comparator); - } - - public boolean isMatch(T value) { - return comparator.compareValue(filterValue, value) == -1; - } - } - - /** - * Greater Than or Equal To. - */ - static class Ge extends Base { - Ge(Object filterValue, ElComparator comparator){ - super(filterValue, comparator); - } - - public boolean isMatch(T value) { - return comparator.compareValue(filterValue, value) >= 0; - } - } - - /** - * Less Than or Equal To. - */ - static class Le extends Base { - Le(Object filterValue, ElComparator comparator){ - super(filterValue, comparator); - } - - public boolean isMatch(T value) { - return comparator.compareValue(filterValue, value) <= 0; - } - } - - /** - * Less Than. - */ - static class Lt extends Base { - Lt(Object filterValue, ElComparator comparator){ - super(filterValue, comparator); - } - - public boolean isMatch(T value) { - return comparator.compareValue(filterValue, value) == 1; - } - } -} +package com.avaje.ebeaninternal.server.el; + +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; + + +/** + * Contains the various ElMatcher implementations. + */ +class ElMatchBuilder { + + /** + * Case insensitive equals. + */ + static class RegularExpr implements ElMatcher { + + final ElPropertyValue elGetValue; + final String value; + final Pattern pattern; + + RegularExpr(ElPropertyValue elGetValue, String value, int options){ + this.elGetValue = elGetValue; + this.value = value; + this.pattern = Pattern.compile(value, options); + } + + public boolean isMatch(T bean) { + String v = (String)elGetValue.elGetValue(bean); + return pattern.matcher(v).matches(); + } + } + + /** + * Case insensitive equals. + */ + static abstract class BaseString implements ElMatcher { + + final ElPropertyValue elGetValue; + final String value; + + public BaseString(ElPropertyValue elGetValue, String value){ + this.elGetValue = elGetValue; + this.value = value; + } + + public abstract boolean isMatch(T bean); + } + + static class Ieq extends BaseString { + Ieq(ElPropertyValue elGetValue, String value) { + super(elGetValue, value); + } + + public boolean isMatch(T bean) { + String v = (String)elGetValue.elGetValue(bean); + return value.equalsIgnoreCase(v); + } + } + + /** + * Case insensitive starts with matcher. + */ + static class IStartsWith implements ElMatcher { + + final ElPropertyValue elGetValue; + final CharMatch charMatch; + + IStartsWith(ElPropertyValue elGetValue, String value) { + this.elGetValue = elGetValue; + this.charMatch = new CharMatch(value); + } + + public boolean isMatch(T bean) { + + String v = (String)elGetValue.elGetValue(bean); + return charMatch.startsWith(v); + } + } + + /** + * Case insensitive ends with matcher. + */ + static class IEndsWith implements ElMatcher { + + final ElPropertyValue elGetValue; + final CharMatch charMatch; + + IEndsWith(ElPropertyValue elGetValue, String value) { + this.elGetValue = elGetValue; + this.charMatch = new CharMatch(value); + } + + public boolean isMatch(T bean) { + + String v = (String)elGetValue.elGetValue(bean); + return charMatch.endsWith(v); + } + } + + static class StartsWith extends BaseString { + StartsWith(ElPropertyValue elGetValue, String value) { + super(elGetValue, value); + } + + public boolean isMatch(T bean) { + String v = (String)elGetValue.elGetValue(bean); + return value.startsWith(v); + } + } + + static class EndsWith extends BaseString { + EndsWith(ElPropertyValue elGetValue, String value) { + super(elGetValue, value); + } + + public boolean isMatch(T bean) { + String v = (String)elGetValue.elGetValue(bean); + return value.endsWith(v); + } + } + + static class IsNull implements ElMatcher { + + final ElPropertyValue elGetValue; + + public IsNull(ElPropertyValue elGetValue){ + this.elGetValue = elGetValue; + } + + public boolean isMatch(T bean) { + return (null == elGetValue.elGetValue(bean)); + } + } + + static class IsNotNull implements ElMatcher { + + final ElPropertyValue elGetValue; + + public IsNotNull(ElPropertyValue elGetValue){ + this.elGetValue = elGetValue; + } + + public boolean isMatch(T bean) { + return (null != elGetValue.elGetValue(bean)); + } + } + + static abstract class Base implements ElMatcher { + + final Object filterValue; + + final ElComparator comparator; + + public Base(Object filterValue, ElComparator comparator){ + this.filterValue = filterValue; + this.comparator = comparator; + } + + public abstract boolean isMatch(T value); + } + + static class InSet implements ElMatcher { + + final Set set; + final ElPropertyValue elGetValue; + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public InSet(Set set, ElPropertyValue elGetValue){ + this.set = new HashSet(set); + this.elGetValue = elGetValue; + } + + public boolean isMatch(T bean) { + + Object value = elGetValue.elGetValue(bean); + if (value == null){ + return false; + } + + return set.contains(value); + } + + } + /** + * Equal To. + */ + static class Eq extends Base { + + public Eq(Object filterValue, ElComparator comparator){ + super(filterValue, comparator); + } + + public boolean isMatch(T value) { + return comparator.compareValue(filterValue, value) == 0; + } + } + + /** + * Not Equal To. + */ + static class Ne extends Base { + + public Ne(Object filterValue, ElComparator comparator){ + super(filterValue, comparator); + } + + public boolean isMatch(T value) { + return comparator.compareValue(filterValue, value) != 0; + } + } + + /** + * Between. + */ + static class Between implements ElMatcher { + + final Object min; + final Object max; + final ElComparator comparator; + + Between(Object min, Object max, ElComparator comparator){ + this.min = min; + this.max = max; + this.comparator = comparator; + } + + public boolean isMatch(T value) { + + return (comparator.compareValue(min, value) <= 0 + && comparator.compareValue(max, value) >= 0); + } + } + + /** + * Greater Than. + */ + static class Gt extends Base { + Gt(Object filterValue, ElComparator comparator){ + super(filterValue, comparator); + } + + public boolean isMatch(T value) { + return comparator.compareValue(filterValue, value) == -1; + } + } + + /** + * Greater Than or Equal To. + */ + static class Ge extends Base { + Ge(Object filterValue, ElComparator comparator){ + super(filterValue, comparator); + } + + public boolean isMatch(T value) { + return comparator.compareValue(filterValue, value) >= 0; + } + } + + /** + * Less Than or Equal To. + */ + static class Le extends Base { + Le(Object filterValue, ElComparator comparator){ + super(filterValue, comparator); + } + + public boolean isMatch(T value) { + return comparator.compareValue(filterValue, value) <= 0; + } + } + + /** + * Less Than. + */ + static class Lt extends Base { + Lt(Object filterValue, ElComparator comparator){ + super(filterValue, comparator); + } + + public boolean isMatch(T value) { + return comparator.compareValue(filterValue, value) == 1; + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/el/ElMatcher.java b/src/main/java/com/avaje/ebeaninternal/server/el/ElMatcher.java index 228a78d76..cac78e208 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/el/ElMatcher.java +++ b/src/main/java/com/avaje/ebeaninternal/server/el/ElMatcher.java @@ -1,31 +1,12 @@ -/** - * 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.el; - -/** - * Interface for defining matches for filter expressions. - */ -public interface ElMatcher { - - /** - * Return true if the bean matches the expression. - */ - public boolean isMatch(T bean); -} +package com.avaje.ebeaninternal.server.el; + +/** + * Interface for defining matches for filter expressions. + */ +public interface ElMatcher { + + /** + * Return true if the bean matches the expression. + */ + public boolean isMatch(T bean); +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChain.java b/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChain.java index 25f0cdefc..e81a296d1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChain.java +++ b/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChain.java @@ -1,331 +1,312 @@ -/** - * 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.el; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.text.StringFormatter; -import com.avaje.ebean.text.StringParser; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; -import com.avaje.ebeaninternal.server.query.SplitName; -import com.avaje.ebeaninternal.server.type.ScalarType; - - -/** - * A ElGetValue based on a chain of properties. - *

- * Used to get the value for an compound expression like customer.name or - * customer.shippingAddress.city etc. - *

- *

- * Note that if any element in the chain returns null, then null is returned and - * no further processing of the chain occurs. - *

- */ -public class ElPropertyChain implements ElPropertyValue { - - private final String prefix; - - private final String placeHolder; - private final String placeHolderEncrypted; - - private final String name; - - private final String expression; - - private final boolean containsMany; - - private final ElPropertyValue[] chain; - - private final boolean assocId; - private final int last; - private final BeanProperty lastBeanProperty; - private final ScalarType scalarType; - - private final ElPropertyValue lastElPropertyValue; - - public ElPropertyChain(boolean containsMany, boolean embedded, String expression, ElPropertyValue[] chain) { - - this.containsMany = containsMany; - this.chain = chain; - this.expression = expression; - int dotPos = expression.lastIndexOf('.'); - if (dotPos > -1){ - this.name = expression.substring(dotPos+1); - if (embedded){ - int embPos = expression.lastIndexOf('.',dotPos-1); - this.prefix = embPos == -1 ? null : expression.substring(0, embPos); - - } else { - this.prefix = expression.substring(0, dotPos); - } - } else { - this.prefix = null; - this.name = expression; - } - - this.assocId = chain[chain.length-1].isAssocId(); - - this.last = chain.length-1; - this.lastBeanProperty = chain[chain.length-1].getBeanProperty(); - if (lastBeanProperty != null){ - this.scalarType = lastBeanProperty.getScalarType(); - } else { - // case for nested compound type (non-scalar) - this.scalarType = null; - } - this.lastElPropertyValue = chain[chain.length-1]; - this.placeHolder = getElPlaceHolder(prefix, lastElPropertyValue, false); - this.placeHolderEncrypted = getElPlaceHolder(prefix, lastElPropertyValue, true); - } - - private String getElPlaceHolder(String prefix, ElPropertyValue lastElPropertyValue, boolean encrypted) { - if (prefix == null){ - return lastElPropertyValue.getElPlaceholder(encrypted); - } - - String el = lastElPropertyValue.getElPlaceholder(encrypted); - - if (!el.contains("${}")){ - // typically a secondary table property - return StringHelper.replaceString(el, "${", "${"+prefix+"."); - } else { - return StringHelper.replaceString(el, ROOT_ELPREFIX, "${"+prefix+"}"); - } - } - - /** - * Full ElGetValue support. - */ - public boolean isDeployOnly() { - return false; - } - - /** - * Return true if there is a many property from sinceProperty to - * the end of this chain. - */ - public boolean containsManySince(String sinceProperty) { - if (sinceProperty == null){ - return containsMany; - } - if (!expression.startsWith(sinceProperty)){ - return containsMany; - } - - int i = 1 + SplitName.count('.', sinceProperty); - - for (; i < chain.length; i++) { - if (chain[i].getBeanProperty().containsMany()) { - return true; - } - } - - return false; - } - - public boolean containsMany() { - return containsMany; - } - - public String getElPrefix() { - return prefix; - } - - public String getName() { - return name; - } - - public String getElName() { - return expression; - } - - public String getElPlaceholder(boolean encrypted) { - return encrypted ? placeHolderEncrypted : placeHolder; - } - - public boolean isDbEncrypted() { - return lastElPropertyValue.isDbEncrypted(); - } - - public boolean isLocalEncrypted() { - return lastElPropertyValue.isLocalEncrypted(); - } - - public Object[] getAssocOneIdValues(Object bean) { - // Don't navigate the object graph as bean - // is assumed to be the appropriate type - return lastElPropertyValue.getAssocOneIdValues(bean); - } - - public String getAssocOneIdExpr(String prefix, String operator) { - return lastElPropertyValue.getAssocOneIdExpr(expression, operator); - } - - public String getAssocIdInExpr(String prefix) { - return lastElPropertyValue.getAssocIdInExpr(prefix); - } - - public String getAssocIdInValueExpr(int size) { - return lastElPropertyValue.getAssocIdInValueExpr(size); - } - - public int getDeployOrder() { - int i = lastBeanProperty.getDeployOrder(); - int max = chain.length-1; - for (int j = 0; j < max; j++) { - int xtra = ((max-j)*1000) * chain[j].getDeployOrder(); - i += xtra; - } - return i; - } - - public boolean isAssocId() { - return assocId; - } - - public boolean isAssocProperty() { - for (int i = 0; i < chain.length; i++) { - if (chain[i].isAssocProperty()){ - return true; - } - } - return false; - } - - public String getDbColumn() { - return lastElPropertyValue.getDbColumn(); - } - - public BeanProperty getBeanProperty() { - return lastBeanProperty; - } - - - public boolean isDateTimeCapable() { - return scalarType != null && scalarType.isDateTimeCapable(); - } - - public int getJdbcType() { - return scalarType == null ? 0 : scalarType.getJdbcType(); - } - - public Object parseDateTime(long systemTimeMillis) { - return scalarType.parseDateTime(systemTimeMillis); - } - - public StringParser getStringParser() { - return scalarType; - } - - public StringFormatter getStringFormatter() { - return scalarType; - } - - public Object elConvertType(Object value){ - // just convert using the last one in the chain - return lastElPropertyValue.elConvertType(value); - } - - public Object elGetValue(Object bean) { - - for (int i = 0; i < chain.length; i++) { - bean = chain[i].elGetValue(bean); - if (bean == null) { - return null; - } - } - - return bean; - } - - public Object elGetReference(Object bean) { - - Object prevBean = bean; - for (int i = 0; i < last; i++) { - // always return non null prevBean - prevBean = chain[i].elGetReference(prevBean); - } - // try the last step in the chain - bean = chain[last].elGetValue(prevBean); - - return bean; - } - - - public void elSetLoaded(Object bean) { - - for (int i = 0; i < last; i++) { - bean = chain[i].elGetValue(bean); - if (bean == null){ - break; - } - } - if (bean != null){ - ((EntityBean)bean)._ebean_getIntercept().setLoaded(); - } - } - - public void elSetReference(Object bean) { - - for (int i = 0; i < last; i++) { - bean = chain[i].elGetValue(bean); - if (bean == null){ - break; - } - } - if (bean != null){ - ((EntityBean)bean)._ebean_getIntercept().setReference(); - } - } - - public void elSetValue(Object bean, Object value, boolean populate, boolean reference){ - - Object prevBean = bean; - if (populate){ - for (int i = 0; i < last; i++) { - // always return non null prevBean - prevBean = chain[i].elGetReference(prevBean); - } - } else { - for (int i = 0; i < last; i++) { - // always return non null prevBean - prevBean = chain[i].elGetValue(prevBean); - if (prevBean == null){ - break; - } - } - } - if (prevBean != null){ - if (lastBeanProperty != null){ - // last chain element maps to a real scalar property - lastBeanProperty.setValueIntercept(prevBean, value); - if (reference){ - ((EntityBean)prevBean)._ebean_getIntercept().setReference(); - } - } else { - // a non-scalar property of a Compound value object - lastElPropertyValue.elSetValue(prevBean, value, populate, reference); - } - } - } - - -} +package com.avaje.ebeaninternal.server.el; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.text.StringFormatter; +import com.avaje.ebean.text.StringParser; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import com.avaje.ebeaninternal.server.query.SplitName; +import com.avaje.ebeaninternal.server.type.ScalarType; + + +/** + * A ElGetValue based on a chain of properties. + *

+ * Used to get the value for an compound expression like customer.name or + * customer.shippingAddress.city etc. + *

+ *

+ * Note that if any element in the chain returns null, then null is returned and + * no further processing of the chain occurs. + *

+ */ +public class ElPropertyChain implements ElPropertyValue { + + private final String prefix; + + private final String placeHolder; + private final String placeHolderEncrypted; + + private final String name; + + private final String expression; + + private final boolean containsMany; + + private final ElPropertyValue[] chain; + + private final boolean assocId; + private final int last; + private final BeanProperty lastBeanProperty; + private final ScalarType scalarType; + + private final ElPropertyValue lastElPropertyValue; + + public ElPropertyChain(boolean containsMany, boolean embedded, String expression, ElPropertyValue[] chain) { + + this.containsMany = containsMany; + this.chain = chain; + this.expression = expression; + int dotPos = expression.lastIndexOf('.'); + if (dotPos > -1){ + this.name = expression.substring(dotPos+1); + if (embedded){ + int embPos = expression.lastIndexOf('.',dotPos-1); + this.prefix = embPos == -1 ? null : expression.substring(0, embPos); + + } else { + this.prefix = expression.substring(0, dotPos); + } + } else { + this.prefix = null; + this.name = expression; + } + + this.assocId = chain[chain.length-1].isAssocId(); + + this.last = chain.length-1; + this.lastBeanProperty = chain[chain.length-1].getBeanProperty(); + if (lastBeanProperty != null){ + this.scalarType = lastBeanProperty.getScalarType(); + } else { + // case for nested compound type (non-scalar) + this.scalarType = null; + } + this.lastElPropertyValue = chain[chain.length-1]; + this.placeHolder = getElPlaceHolder(prefix, lastElPropertyValue, false); + this.placeHolderEncrypted = getElPlaceHolder(prefix, lastElPropertyValue, true); + } + + private String getElPlaceHolder(String prefix, ElPropertyValue lastElPropertyValue, boolean encrypted) { + if (prefix == null){ + return lastElPropertyValue.getElPlaceholder(encrypted); + } + + String el = lastElPropertyValue.getElPlaceholder(encrypted); + + if (!el.contains("${}")){ + // typically a secondary table property + return StringHelper.replaceString(el, "${", "${"+prefix+"."); + } else { + return StringHelper.replaceString(el, ROOT_ELPREFIX, "${"+prefix+"}"); + } + } + + /** + * Full ElGetValue support. + */ + public boolean isDeployOnly() { + return false; + } + + /** + * Return true if there is a many property from sinceProperty to + * the end of this chain. + */ + public boolean containsManySince(String sinceProperty) { + if (sinceProperty == null){ + return containsMany; + } + if (!expression.startsWith(sinceProperty)){ + return containsMany; + } + + int i = 1 + SplitName.count('.', sinceProperty); + + for (; i < chain.length; i++) { + if (chain[i].getBeanProperty().containsMany()) { + return true; + } + } + + return false; + } + + public boolean containsMany() { + return containsMany; + } + + public String getElPrefix() { + return prefix; + } + + public String getName() { + return name; + } + + public String getElName() { + return expression; + } + + public String getElPlaceholder(boolean encrypted) { + return encrypted ? placeHolderEncrypted : placeHolder; + } + + public boolean isDbEncrypted() { + return lastElPropertyValue.isDbEncrypted(); + } + + public boolean isLocalEncrypted() { + return lastElPropertyValue.isLocalEncrypted(); + } + + public Object[] getAssocOneIdValues(Object bean) { + // Don't navigate the object graph as bean + // is assumed to be the appropriate type + return lastElPropertyValue.getAssocOneIdValues(bean); + } + + public String getAssocOneIdExpr(String prefix, String operator) { + return lastElPropertyValue.getAssocOneIdExpr(expression, operator); + } + + public String getAssocIdInExpr(String prefix) { + return lastElPropertyValue.getAssocIdInExpr(prefix); + } + + public String getAssocIdInValueExpr(int size) { + return lastElPropertyValue.getAssocIdInValueExpr(size); + } + + public int getDeployOrder() { + int i = lastBeanProperty.getDeployOrder(); + int max = chain.length-1; + for (int j = 0; j < max; j++) { + int xtra = ((max-j)*1000) * chain[j].getDeployOrder(); + i += xtra; + } + return i; + } + + public boolean isAssocId() { + return assocId; + } + + public boolean isAssocProperty() { + for (int i = 0; i < chain.length; i++) { + if (chain[i].isAssocProperty()){ + return true; + } + } + return false; + } + + public String getDbColumn() { + return lastElPropertyValue.getDbColumn(); + } + + public BeanProperty getBeanProperty() { + return lastBeanProperty; + } + + + public boolean isDateTimeCapable() { + return scalarType != null && scalarType.isDateTimeCapable(); + } + + public int getJdbcType() { + return scalarType == null ? 0 : scalarType.getJdbcType(); + } + + public Object parseDateTime(long systemTimeMillis) { + return scalarType.parseDateTime(systemTimeMillis); + } + + public StringParser getStringParser() { + return scalarType; + } + + public StringFormatter getStringFormatter() { + return scalarType; + } + + public Object elConvertType(Object value){ + // just convert using the last one in the chain + return lastElPropertyValue.elConvertType(value); + } + + public Object elGetValue(Object bean) { + + for (int i = 0; i < chain.length; i++) { + bean = chain[i].elGetValue(bean); + if (bean == null) { + return null; + } + } + + return bean; + } + + public Object elGetReference(Object bean) { + + Object prevBean = bean; + for (int i = 0; i < last; i++) { + // always return non null prevBean + prevBean = chain[i].elGetReference(prevBean); + } + // try the last step in the chain + bean = chain[last].elGetValue(prevBean); + + return bean; + } + + + public void elSetLoaded(Object bean) { + + for (int i = 0; i < last; i++) { + bean = chain[i].elGetValue(bean); + if (bean == null){ + break; + } + } + if (bean != null){ + ((EntityBean)bean)._ebean_getIntercept().setLoaded(); + } + } + + public void elSetReference(Object bean) { + + for (int i = 0; i < last; i++) { + bean = chain[i].elGetValue(bean); + if (bean == null){ + break; + } + } + if (bean != null){ + ((EntityBean)bean)._ebean_getIntercept().setReference(); + } + } + + public void elSetValue(Object bean, Object value, boolean populate, boolean reference){ + + Object prevBean = bean; + if (populate){ + for (int i = 0; i < last; i++) { + // always return non null prevBean + prevBean = chain[i].elGetReference(prevBean); + } + } else { + for (int i = 0; i < last; i++) { + // always return non null prevBean + prevBean = chain[i].elGetValue(prevBean); + if (prevBean == null){ + break; + } + } + } + if (prevBean != null){ + if (lastBeanProperty != null){ + // last chain element maps to a real scalar property + lastBeanProperty.setValueIntercept(prevBean, value); + if (reference){ + ((EntityBean)prevBean)._ebean_getIntercept().setReference(); + } + } else { + // a non-scalar property of a Compound value object + lastElPropertyValue.elSetValue(prevBean, value, populate, reference); + } + } + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChainBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChainBuilder.java index 72b453fdc..49387b63b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChainBuilder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyChainBuilder.java @@ -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.el; - -import java.util.ArrayList; -import java.util.List; - -/** - * Utility object used to build a ElPropertyChain. - *

- * Builds a ElPropertyChain based on a chain of properties with dot separators. - *

- *

- * This can navigate an object graph based on dot notation such as - * order.customer.name. - *

- */ -public class ElPropertyChainBuilder { - - private final String expression; - - private final List chain = new ArrayList(); - - private final boolean embedded; - - private boolean containsMany; - - /** - * Create with the original expression. - */ - public ElPropertyChainBuilder(boolean embedded, String expression) { - this.embedded = embedded; - this.expression = expression; - } - - public boolean isContainsMany() { - return containsMany; - } - - public void setContainsMany(boolean containsMany) { - this.containsMany = containsMany; - } - - public String getExpression() { - return expression; - } - - /** - * Add a ElGetValue element to the chain. - */ - public ElPropertyChainBuilder add(ElPropertyValue element) { - if (element == null){ - throw new NullPointerException("element null in expression "+expression); - } - chain.add(element); - return this; - } - - /** - * Build the immutable ElGetChain from the build information. - */ - public ElPropertyChain build() { - return new ElPropertyChain(containsMany, embedded, expression, chain.toArray(new ElPropertyValue[chain.size()])); - } - -} +package com.avaje.ebeaninternal.server.el; + +import java.util.ArrayList; +import java.util.List; + +/** + * Utility object used to build a ElPropertyChain. + *

+ * Builds a ElPropertyChain based on a chain of properties with dot separators. + *

+ *

+ * This can navigate an object graph based on dot notation such as + * order.customer.name. + *

+ */ +public class ElPropertyChainBuilder { + + private final String expression; + + private final List chain = new ArrayList(); + + private final boolean embedded; + + private boolean containsMany; + + /** + * Create with the original expression. + */ + public ElPropertyChainBuilder(boolean embedded, String expression) { + this.embedded = embedded; + this.expression = expression; + } + + public boolean isContainsMany() { + return containsMany; + } + + public void setContainsMany(boolean containsMany) { + this.containsMany = containsMany; + } + + public String getExpression() { + return expression; + } + + /** + * Add a ElGetValue element to the chain. + */ + public ElPropertyChainBuilder add(ElPropertyValue element) { + if (element == null){ + throw new NullPointerException("element null in expression "+expression); + } + chain.add(element); + return this; + } + + /** + * Build the immutable ElGetChain from the build information. + */ + public ElPropertyChain build() { + return new ElPropertyChain(containsMany, embedded, expression, chain.toArray(new ElPropertyValue[chain.size()])); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyValue.java b/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyValue.java index 9659b142b..bfe9de67d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyValue.java +++ b/src/main/java/com/avaje/ebeaninternal/server/el/ElPropertyValue.java @@ -1,140 +1,121 @@ -/** - * 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.el; - -import com.avaje.ebean.text.StringFormatter; -import com.avaje.ebean.text.StringParser; - -/** - * The expression language object that can get values. - *

- * This can be used for local sorting and filtering. - *

- */ -public interface ElPropertyValue extends ElPropertyDeploy { - - /** - * Return the Id values for the given bean value. - */ - public Object[] getAssocOneIdValues(Object bean); - - /** - * Return the Id expression string. - *

- * Typically used to produce id = ? expression strings. - *

- */ - public String getAssocOneIdExpr(String prefix, String operator); - - /** - * Return the logical id value expression taking into account embedded id's. - */ - public String getAssocIdInValueExpr(int size); - - /** - * Return the logical id in expression taking into account embedded id's. - */ - public String getAssocIdInExpr(String prefix); - - /** - * Return true if this is an ManyToOne or OneToOne associated bean property. - */ - public boolean isAssocId(); - - /** - * Return true if any path of this path contains a Associated One or Many. - */ - public boolean isAssocProperty(); - - /** - * Return true if the property is encrypted via Java. - */ - public boolean isLocalEncrypted(); - - /** - * Return true if the property is encrypted in the DB. - */ - public boolean isDbEncrypted(); - - /** - * Return the deploy order for the property. - */ - public int getDeployOrder(); - - /** - * Return the default StringParser for the scalar property. - */ - public StringParser getStringParser(); - - /** - * Return the default StringFormatter for the scalar property. - */ - public StringFormatter getStringFormatter(); - - /** - * Return true if the last type is "DateTime capable" - can support - * {@link #parseDateTime(long)}. - */ - public boolean isDateTimeCapable(); - - /** - * Return the underlying JDBC type or 0 if this is not a scalar type. - */ - public int getJdbcType(); - - /** - * For DateTime capable scalar types convert the long systemTimeMillis into - * an appropriate java time (Date,Timestamp,Time,Calendar, JODA type etc). - */ - public Object parseDateTime(long systemTimeMillis); - - /** - * Return the value from a given entity bean. - */ - public Object elGetValue(Object bean); - - /** - * Return the value ensuring objects prior to the top scalar property are - * automatically populated. - */ - public Object elGetReference(Object bean); - - /** - * Set a value given a root level bean. - *

- * If populate then - *

- */ - public void elSetValue(Object bean, Object value, boolean populate, boolean reference); - - /** - * Make the owning bean of this property a reference (as in not new/dirty). - */ - public void elSetReference(Object bean); - - /** - * Convert the value to the expected type. - *

- * Typically useful for converting strings to the appropriate number type - * etc. - *

- */ - public Object elConvertType(Object value); -} +package com.avaje.ebeaninternal.server.el; + +import com.avaje.ebean.text.StringFormatter; +import com.avaje.ebean.text.StringParser; + +/** + * The expression language object that can get values. + *

+ * This can be used for local sorting and filtering. + *

+ */ +public interface ElPropertyValue extends ElPropertyDeploy { + + /** + * Return the Id values for the given bean value. + */ + public Object[] getAssocOneIdValues(Object bean); + + /** + * Return the Id expression string. + *

+ * Typically used to produce id = ? expression strings. + *

+ */ + public String getAssocOneIdExpr(String prefix, String operator); + + /** + * Return the logical id value expression taking into account embedded id's. + */ + public String getAssocIdInValueExpr(int size); + + /** + * Return the logical id in expression taking into account embedded id's. + */ + public String getAssocIdInExpr(String prefix); + + /** + * Return true if this is an ManyToOne or OneToOne associated bean property. + */ + public boolean isAssocId(); + + /** + * Return true if any path of this path contains a Associated One or Many. + */ + public boolean isAssocProperty(); + + /** + * Return true if the property is encrypted via Java. + */ + public boolean isLocalEncrypted(); + + /** + * Return true if the property is encrypted in the DB. + */ + public boolean isDbEncrypted(); + + /** + * Return the deploy order for the property. + */ + public int getDeployOrder(); + + /** + * Return the default StringParser for the scalar property. + */ + public StringParser getStringParser(); + + /** + * Return the default StringFormatter for the scalar property. + */ + public StringFormatter getStringFormatter(); + + /** + * Return true if the last type is "DateTime capable" - can support + * {@link #parseDateTime(long)}. + */ + public boolean isDateTimeCapable(); + + /** + * Return the underlying JDBC type or 0 if this is not a scalar type. + */ + public int getJdbcType(); + + /** + * For DateTime capable scalar types convert the long systemTimeMillis into + * an appropriate java time (Date,Timestamp,Time,Calendar, JODA type etc). + */ + public Object parseDateTime(long systemTimeMillis); + + /** + * Return the value from a given entity bean. + */ + public Object elGetValue(Object bean); + + /** + * Return the value ensuring objects prior to the top scalar property are + * automatically populated. + */ + public Object elGetReference(Object bean); + + /** + * Set a value given a root level bean. + *

+ * If populate then + *

+ */ + public void elSetValue(Object bean, Object value, boolean populate, boolean reference); + + /** + * Make the owning bean of this property a reference (as in not new/dirty). + */ + public void elSetReference(Object bean); + + /** + * Convert the value to the expected type. + *

+ * Typically useful for converting strings to the appropriate number type + * etc. + *

+ */ + public Object elConvertType(Object value); +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/el/ElSetValue.java b/src/main/java/com/avaje/ebeaninternal/server/el/ElSetValue.java index fcee1c9c7..0a14caabc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/el/ElSetValue.java +++ b/src/main/java/com/avaje/ebeaninternal/server/el/ElSetValue.java @@ -1,29 +1,10 @@ -/** - * 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.el; - -public interface ElSetValue { - - /** - * Set the value to the bean. - */ - public void elSetValue(Object bean, Object value); - -} +package com.avaje.ebeaninternal.server.el; + +public interface ElSetValue { + + /** + * Set the value to the bean. + */ + public void elSetValue(Object bean, Object value); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/el/package-info.java b/src/main/java/com/avaje/ebeaninternal/server/el/package-info.java index 22a02b845..5040e1ac6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/el/package-info.java +++ b/src/main/java/com/avaje/ebeaninternal/server/el/package-info.java @@ -1,4 +1 @@ -/** - * Expression language. - */ package com.avaje.ebeaninternal.server.el; \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/AbstractExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/AbstractExpression.java index 01b3ebbb7..a1c74a641 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/expression/AbstractExpression.java +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/AbstractExpression.java @@ -1,76 +1,57 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.expression; - -import com.avaje.ebeaninternal.api.ManyWhereJoins; -import com.avaje.ebeaninternal.api.SpiExpression; -import com.avaje.ebeaninternal.api.SpiExpressionRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.el.ElPropertyDeploy; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; - -/** - * Base class for simple expressions. - * - * @author rbygrave - */ -public abstract class AbstractExpression implements SpiExpression { - - private static final long serialVersionUID = 4072786211853856174L; - - protected final String propName; - - protected final FilterExprPath pathPrefix; - - protected AbstractExpression(FilterExprPath pathPrefix, String propName) { - this.pathPrefix = pathPrefix; - this.propName = propName; - } - - public String getPropertyName() { - if (pathPrefix == null){ - return propName; - } else { - String path = pathPrefix.getPath(); - if (path == null || path.length() == 0){ - return propName; - } else { - return path+"."+propName; - } - } - } - - public void containsMany(BeanDescriptor desc, ManyWhereJoins manyWhereJoin) { - - String propertyName = getPropertyName(); - if (propertyName != null){ - ElPropertyDeploy elProp = desc.getElPropertyDeploy(propertyName); - if (elProp != null && elProp.containsMany()){ - manyWhereJoin.add(elProp); - } - } - } - - protected ElPropertyValue getElProp(SpiExpressionRequest request) { - - String propertyName = getPropertyName(); - return request.getBeanDescriptor().getElGetValue(propertyName); - } -} +package com.avaje.ebeaninternal.server.expression; + +import com.avaje.ebeaninternal.api.ManyWhereJoins; +import com.avaje.ebeaninternal.api.SpiExpression; +import com.avaje.ebeaninternal.api.SpiExpressionRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.el.ElPropertyDeploy; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; + +/** + * Base class for simple expressions. + * + * @author rbygrave + */ +public abstract class AbstractExpression implements SpiExpression { + + private static final long serialVersionUID = 4072786211853856174L; + + protected final String propName; + + protected final FilterExprPath pathPrefix; + + protected AbstractExpression(FilterExprPath pathPrefix, String propName) { + this.pathPrefix = pathPrefix; + this.propName = propName; + } + + public String getPropertyName() { + if (pathPrefix == null){ + return propName; + } else { + String path = pathPrefix.getPath(); + if (path == null || path.length() == 0){ + return propName; + } else { + return path+"."+propName; + } + } + } + + public void containsMany(BeanDescriptor desc, ManyWhereJoins manyWhereJoin) { + + String propertyName = getPropertyName(); + if (propertyName != null){ + ElPropertyDeploy elProp = desc.getElPropertyDeploy(propertyName); + if (elProp != null && elProp.containsMany()){ + manyWhereJoin.add(elProp); + } + } + } + + protected ElPropertyValue getElProp(SpiExpressionRequest request) { + + String propertyName = getPropertyName(); + return request.getBeanDescriptor().getElGetValue(propertyName); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/BetweenPropertyExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/BetweenPropertyExpression.java index ddff6726b..7a2f14b66 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/expression/BetweenPropertyExpression.java +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/BetweenPropertyExpression.java @@ -1,101 +1,82 @@ -/** - * 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.expression; - -import com.avaje.ebean.event.BeanQueryRequest; -import com.avaje.ebeaninternal.api.ManyWhereJoins; -import com.avaje.ebeaninternal.api.SpiExpression; -import com.avaje.ebeaninternal.api.SpiExpressionRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.el.ElPropertyDeploy; - -/** - * Between expression where a value is between two properties. - * - * @author rbygrave - */ -class BetweenPropertyExpression implements SpiExpression { - - private static final long serialVersionUID = 2078918165221454910L; - - private static final String BETWEEN = " between "; - - private final FilterExprPath pathPrefix; - private final String lowProperty; - private final String highProperty; - private final Object value; - - BetweenPropertyExpression(FilterExprPath pathPrefix, String lowProperty, String highProperty, Object value) { - this.pathPrefix = pathPrefix; - this.lowProperty = lowProperty; - this.highProperty = highProperty; - this.value = value; - } - - protected String name(String propName) { - if (pathPrefix == null) { - return propName; - } else { - String path = pathPrefix.getPath(); - if (path == null || path.length() == 0) { - return propName; - } else { - return path + "." + propName; - } - } - } - - public void containsMany(BeanDescriptor desc, ManyWhereJoins manyWhereJoin) { - - ElPropertyDeploy elProp = desc.getElPropertyDeploy(name(lowProperty)); - if (elProp != null && elProp.containsMany()) { - manyWhereJoin.add(elProp); - } - - elProp = desc.getElPropertyDeploy(name(highProperty)); - if (elProp != null && elProp.containsMany()) { - manyWhereJoin.add(elProp); - } - } - - public void addBindValues(SpiExpressionRequest request) { - request.addBindValue(value); - } - - public void addSql(SpiExpressionRequest request) { - - request.append(" ? ").append(BETWEEN).append(name(lowProperty)).append(" and ").append(name(highProperty)); - } - - public int queryAutoFetchHash() { - int hc = BetweenPropertyExpression.class.getName().hashCode(); - hc = hc * 31 + lowProperty.hashCode(); - hc = hc * 31 + highProperty.hashCode(); - return hc; - } - - public int queryPlanHash(BeanQueryRequest request) { - return queryAutoFetchHash(); - } - - public int queryBindHash() { - return value.hashCode(); - } -} +package com.avaje.ebeaninternal.server.expression; + +import com.avaje.ebean.event.BeanQueryRequest; +import com.avaje.ebeaninternal.api.ManyWhereJoins; +import com.avaje.ebeaninternal.api.SpiExpression; +import com.avaje.ebeaninternal.api.SpiExpressionRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.el.ElPropertyDeploy; + +/** + * Between expression where a value is between two properties. + * + * @author rbygrave + */ +class BetweenPropertyExpression implements SpiExpression { + + private static final long serialVersionUID = 2078918165221454910L; + + private static final String BETWEEN = " between "; + + private final FilterExprPath pathPrefix; + private final String lowProperty; + private final String highProperty; + private final Object value; + + BetweenPropertyExpression(FilterExprPath pathPrefix, String lowProperty, String highProperty, Object value) { + this.pathPrefix = pathPrefix; + this.lowProperty = lowProperty; + this.highProperty = highProperty; + this.value = value; + } + + protected String name(String propName) { + if (pathPrefix == null) { + return propName; + } else { + String path = pathPrefix.getPath(); + if (path == null || path.length() == 0) { + return propName; + } else { + return path + "." + propName; + } + } + } + + public void containsMany(BeanDescriptor desc, ManyWhereJoins manyWhereJoin) { + + ElPropertyDeploy elProp = desc.getElPropertyDeploy(name(lowProperty)); + if (elProp != null && elProp.containsMany()) { + manyWhereJoin.add(elProp); + } + + elProp = desc.getElPropertyDeploy(name(highProperty)); + if (elProp != null && elProp.containsMany()) { + manyWhereJoin.add(elProp); + } + } + + public void addBindValues(SpiExpressionRequest request) { + request.addBindValue(value); + } + + public void addSql(SpiExpressionRequest request) { + + request.append(" ? ").append(BETWEEN).append(name(lowProperty)).append(" and ").append(name(highProperty)); + } + + public int queryAutoFetchHash() { + int hc = BetweenPropertyExpression.class.getName().hashCode(); + hc = hc * 31 + lowProperty.hashCode(); + hc = hc * 31 + highProperty.hashCode(); + return hc; + } + + public int queryPlanHash(BeanQueryRequest request) { + return queryAutoFetchHash(); + } + + public int queryBindHash() { + return value.hashCode(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/FilterExprPath.java b/src/main/java/com/avaje/ebeaninternal/server/expression/FilterExprPath.java index 15f0dcb7f..3ef049285 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/expression/FilterExprPath.java +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/FilterExprPath.java @@ -1,61 +1,42 @@ -/** - * 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.expression; - -import java.io.Serializable; - -/** - * This is the path prefix for filterMany. - *

- * The actual path can change due to FetchConfig query joins that proceed - * the query that includes the filterMany. - *

- * - * @author rbygrave - */ -public class FilterExprPath implements Serializable { - - private static final long serialVersionUID = -6420905565372842018L; - - /** - * The path of the filterMany. - */ - private String path; - - public FilterExprPath(String path){ - this.path = path; - } - - /** - * Trim off leading part of the path due to a - * proceeding (earlier) query join etc. - */ - public void trimPath(int prefixTrim) { - path = path.substring(prefixTrim); - } - - /** - * Return the path. This is a prefix used in the filterMany expressions. - */ - public String getPath() { - return path; - } - -} +package com.avaje.ebeaninternal.server.expression; + +import java.io.Serializable; + +/** + * This is the path prefix for filterMany. + *

+ * The actual path can change due to FetchConfig query joins that proceed + * the query that includes the filterMany. + *

+ * + * @author rbygrave + */ +public class FilterExprPath implements Serializable { + + private static final long serialVersionUID = -6420905565372842018L; + + /** + * The path of the filterMany. + */ + private String path; + + public FilterExprPath(String path){ + this.path = path; + } + + /** + * Trim off leading part of the path due to a + * proceeding (earlier) query join etc. + */ + public void trimPath(int prefixTrim) { + path = path.substring(prefixTrim); + } + + /** + * Return the path. This is a prefix used in the filterMany expressions. + */ + public String getPath() { + return path; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/LuceneAwareExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/LuceneAwareExpression.java index 3a344cfa8..f80115e4e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/expression/LuceneAwareExpression.java +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/LuceneAwareExpression.java @@ -1,29 +1,10 @@ -/** - * 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.expression; - -/** - * Marker interface for lucene aware expressions. - * - * @author rbygrave - */ -public interface LuceneAwareExpression { - -} +package com.avaje.ebeaninternal.server.expression; + +/** + * Marker interface for lucene aware expressions. + * + * @author rbygrave + */ +public interface LuceneAwareExpression { + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/PersistenceLuceneParseException.java b/src/main/java/com/avaje/ebeaninternal/server/expression/PersistenceLuceneParseException.java index b3fecffb2..f5a8e6364 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/expression/PersistenceLuceneParseException.java +++ b/src/main/java/com/avaje/ebeaninternal/server/expression/PersistenceLuceneParseException.java @@ -1,35 +1,16 @@ -/** - * 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.expression; - -import javax.persistence.PersistenceException; - -/** - * Exception used to wrap Lucene parsing exceptions. - */ -public class PersistenceLuceneParseException extends PersistenceException { - - private static final long serialVersionUID = 838790249273928392L; - - public PersistenceLuceneParseException(Throwable e){ - super(e); - } - -} +package com.avaje.ebeaninternal.server.expression; + +import javax.persistence.PersistenceException; + +/** + * Exception used to wrap Lucene parsing exceptions. + */ +public class PersistenceLuceneParseException extends PersistenceException { + + private static final long serialVersionUID = 838790249273928392L; + + public PersistenceLuceneParseException(Throwable e){ + super(e); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/jdbc/OraclePstmtBatch.java b/src/main/java/com/avaje/ebeaninternal/server/jdbc/OraclePstmtBatch.java index 7f9c670b2..7990bb31d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/jdbc/OraclePstmtBatch.java +++ b/src/main/java/com/avaje/ebeaninternal/server/jdbc/OraclePstmtBatch.java @@ -1,140 +1,121 @@ -/** - * 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.jdbc; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.sql.PreparedStatement; -import java.sql.SQLException; - -import javax.persistence.OptimisticLockException; -import javax.persistence.PersistenceException; - -import com.avaje.ebean.config.PstmtDelegate; -import com.avaje.ebeaninternal.api.ClassUtil; -import com.avaje.ebeaninternal.server.core.PstmtBatch; - - -/** - * Oracle specific handling of JDBC batching. - *

- * I guess some people don't need to follow the jdbc specification. - *

- * - * @author rbygrave - * @author imario - */ -public class OraclePstmtBatch implements PstmtBatch { - - private final PstmtDelegate pstmtDelegate; - - /** - * OraclePreparedStatement.setExecuteBatch() method. - */ - private static final Method METHOD_SET_EXECUTE_BATCH; - - /** - * OraclePreparedStatement.sendBatch() method. - */ - private static final Method METHOD_SEND_BATCH; - - private static final RuntimeException INIT_EXCEPTION; - - static { - RuntimeException initException = null; - Method mSetExecuteBatch = null; - Method mSendBatch = null; - - try { - Class ops = ClassUtil.forName("oracle.jdbc.OraclePreparedStatement"); - - mSetExecuteBatch = ops.getMethod("setExecuteBatch", new Class[] { int.class }); - mSendBatch = ops.getMethod("sendBatch"); - - } catch (NoSuchMethodException e) { - initException = new RuntimeException("problems initializing oracle reflection", e); - initException.fillInStackTrace(); - - } catch (ClassNotFoundException e) { - initException = new RuntimeException("problems initializing oracle reflection", e); - initException.fillInStackTrace(); - } - - INIT_EXCEPTION = initException; - METHOD_SET_EXECUTE_BATCH = mSetExecuteBatch; - METHOD_SEND_BATCH = mSendBatch; - } - - public OraclePstmtBatch(PstmtDelegate pstmtDelegate) { - this.pstmtDelegate = pstmtDelegate; - } - - public void setBatchSize(PreparedStatement pstmt, int batchSize) { - if (INIT_EXCEPTION != null) { - throw INIT_EXCEPTION; - } - - try { - // invoke setExecuteBatch(batchSize+1); - METHOD_SET_EXECUTE_BATCH.invoke(pstmtDelegate.unwrap(pstmt), batchSize+1); - } catch (IllegalAccessException e) { - String m = "Error with Oracle setExecuteBatch "+(batchSize+1); - throw new RuntimeException(m, e); - } catch (InvocationTargetException e) { - String m = "Error with Oracle setExecuteBatch "+(batchSize+1); - throw new RuntimeException(m, e); - } - } - - /** - * Simply calls standard pstmt.addBatch(). - */ - public void addBatch(PreparedStatement pstmt) throws SQLException { - pstmt.executeUpdate(); - } - - public int executeBatch(PreparedStatement pstmt, int expectedRows, String sql, boolean occCheck) throws SQLException { - - if (INIT_EXCEPTION != null) { - throw INIT_EXCEPTION; - } - - int rows; - try { - // invoke sendBatch(); - rows = ((Integer) METHOD_SEND_BATCH.invoke(pstmtDelegate.unwrap(pstmt))).intValue(); - - } catch (IllegalAccessException e) { - String msg = "Error invoking Oracle sendBatch method via reflection"; - throw new PersistenceException(msg,e); - - } catch (InvocationTargetException e) { - String msg = "Error invoking Oracle sendBatch method via reflection"; - throw new PersistenceException(msg, e); - } - if (occCheck && rows != expectedRows) { - String msg = "Batch execution expected "+expectedRows+" but got "+rows+" sql:"+sql; - throw new OptimisticLockException(msg); - } - - return rows; - } - -} +package com.avaje.ebeaninternal.server.jdbc; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import javax.persistence.OptimisticLockException; +import javax.persistence.PersistenceException; + +import com.avaje.ebean.config.PstmtDelegate; +import com.avaje.ebeaninternal.api.ClassUtil; +import com.avaje.ebeaninternal.server.core.PstmtBatch; + + +/** + * Oracle specific handling of JDBC batching. + *

+ * I guess some people don't need to follow the jdbc specification. + *

+ * + * @author rbygrave + * @author imario + */ +public class OraclePstmtBatch implements PstmtBatch { + + private final PstmtDelegate pstmtDelegate; + + /** + * OraclePreparedStatement.setExecuteBatch() method. + */ + private static final Method METHOD_SET_EXECUTE_BATCH; + + /** + * OraclePreparedStatement.sendBatch() method. + */ + private static final Method METHOD_SEND_BATCH; + + private static final RuntimeException INIT_EXCEPTION; + + static { + RuntimeException initException = null; + Method mSetExecuteBatch = null; + Method mSendBatch = null; + + try { + Class ops = ClassUtil.forName("oracle.jdbc.OraclePreparedStatement"); + + mSetExecuteBatch = ops.getMethod("setExecuteBatch", new Class[] { int.class }); + mSendBatch = ops.getMethod("sendBatch"); + + } catch (NoSuchMethodException e) { + initException = new RuntimeException("problems initializing oracle reflection", e); + initException.fillInStackTrace(); + + } catch (ClassNotFoundException e) { + initException = new RuntimeException("problems initializing oracle reflection", e); + initException.fillInStackTrace(); + } + + INIT_EXCEPTION = initException; + METHOD_SET_EXECUTE_BATCH = mSetExecuteBatch; + METHOD_SEND_BATCH = mSendBatch; + } + + public OraclePstmtBatch(PstmtDelegate pstmtDelegate) { + this.pstmtDelegate = pstmtDelegate; + } + + public void setBatchSize(PreparedStatement pstmt, int batchSize) { + if (INIT_EXCEPTION != null) { + throw INIT_EXCEPTION; + } + + try { + // invoke setExecuteBatch(batchSize+1); + METHOD_SET_EXECUTE_BATCH.invoke(pstmtDelegate.unwrap(pstmt), batchSize+1); + } catch (IllegalAccessException e) { + String m = "Error with Oracle setExecuteBatch "+(batchSize+1); + throw new RuntimeException(m, e); + } catch (InvocationTargetException e) { + String m = "Error with Oracle setExecuteBatch "+(batchSize+1); + throw new RuntimeException(m, e); + } + } + + /** + * Simply calls standard pstmt.addBatch(). + */ + public void addBatch(PreparedStatement pstmt) throws SQLException { + pstmt.executeUpdate(); + } + + public int executeBatch(PreparedStatement pstmt, int expectedRows, String sql, boolean occCheck) throws SQLException { + + if (INIT_EXCEPTION != null) { + throw INIT_EXCEPTION; + } + + int rows; + try { + // invoke sendBatch(); + rows = ((Integer) METHOD_SEND_BATCH.invoke(pstmtDelegate.unwrap(pstmt))).intValue(); + + } catch (IllegalAccessException e) { + String msg = "Error invoking Oracle sendBatch method via reflection"; + throw new PersistenceException(msg,e); + + } catch (InvocationTargetException e) { + String msg = "Error invoking Oracle sendBatch method via reflection"; + throw new PersistenceException(msg, e); + } + if (occCheck && rows != expectedRows) { + String msg = "Batch execution expected "+expectedRows+" but got "+rows+" sql:"+sql; + throw new OptimisticLockException(msg); + } + + return rows; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/jdbc/StandardPstmtDelegate.java b/src/main/java/com/avaje/ebeaninternal/server/jdbc/StandardPstmtDelegate.java index 3506e87cc..86738bca3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/jdbc/StandardPstmtDelegate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/jdbc/StandardPstmtDelegate.java @@ -1,42 +1,23 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.jdbc; - -import java.sql.PreparedStatement; - -import com.avaje.ebean.config.PstmtDelegate; -import com.avaje.ebeaninternal.server.lib.sql.ExtendedPreparedStatement; - -/** - * Implementation of PstmtDelegate from Ebean's own - * DataSource. - * - * @author rbygrave - */ -public class StandardPstmtDelegate implements PstmtDelegate { - - /** - * Unwrap the PreparedStatement from Ebean's DataSource implementation. - */ - public PreparedStatement unwrap(PreparedStatement pstmt) { - - return ((ExtendedPreparedStatement)pstmt).getDelegate(); - } -} +package com.avaje.ebeaninternal.server.jdbc; + +import java.sql.PreparedStatement; + +import com.avaje.ebean.config.PstmtDelegate; +import com.avaje.ebeaninternal.server.lib.sql.ExtendedPreparedStatement; + +/** + * Implementation of PstmtDelegate from Ebean's own + * DataSource. + * + * @author rbygrave + */ +public class StandardPstmtDelegate implements PstmtDelegate { + + /** + * Unwrap the PreparedStatement from Ebean's DataSource implementation. + */ + public PreparedStatement unwrap(PreparedStatement pstmt) { + + return ((ExtendedPreparedStatement)pstmt).getDelegate(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/jdbc/package-info.java b/src/main/java/com/avaje/ebeaninternal/server/jdbc/package-info.java index cca31a385..634231d8f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/jdbc/package-info.java +++ b/src/main/java/com/avaje/ebeaninternal/server/jdbc/package-info.java @@ -1,4 +1 @@ -/** - * Fixes for Oracle JDBC driver issues etc. - */ package com.avaje.ebeaninternal.server.jdbc; \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminLogging.java b/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminLogging.java index d3ab6c1a3..f052fbd4f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminLogging.java +++ b/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminLogging.java @@ -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.jmx; - -import com.avaje.ebean.AdminLogging; -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.LogLevel; -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebeaninternal.server.transaction.TransactionManager; - -/** - * Implementation of the LogControl. - *

- * This is accessible via {@link EbeanServer#getAdminLogging()} or via JMX MBean. - *

- */ -public class MAdminLogging implements MAdminLoggingMBean, AdminLogging { - - private final TransactionManager transactionManager; - - private boolean debugSql; - private boolean debugLazyLoad; - - /** - * Configure from serverConfig properties. - */ - public MAdminLogging(ServerConfig serverConfig, TransactionManager txManager) { - - this.transactionManager = txManager; - this.debugSql = serverConfig.isDebugSql(); - this.debugLazyLoad = serverConfig.isDebugLazyLoad(); - } - - public void setLogLevel(LogLevel logLevel){ - transactionManager.setTransactionLogLevel(logLevel); - } - - public LogLevel getLogLevel() { - return transactionManager.getTransactionLogLevel(); - } - - public boolean isDebugGeneratedSql() { - return debugSql; - } - - public void setDebugGeneratedSql(boolean debugSql) { - this.debugSql = debugSql; - } - - public boolean isDebugLazyLoad() { - return debugLazyLoad; - } - - public void setDebugLazyLoad(boolean debugLazyLoad) { - this.debugLazyLoad = debugLazyLoad; - } - -} +package com.avaje.ebeaninternal.server.jmx; + +import com.avaje.ebean.AdminLogging; +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.LogLevel; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebeaninternal.server.transaction.TransactionManager; + +/** + * Implementation of the LogControl. + *

+ * This is accessible via {@link EbeanServer#getAdminLogging()} or via JMX MBean. + *

+ */ +public class MAdminLogging implements MAdminLoggingMBean, AdminLogging { + + private final TransactionManager transactionManager; + + private boolean debugSql; + private boolean debugLazyLoad; + + /** + * Configure from serverConfig properties. + */ + public MAdminLogging(ServerConfig serverConfig, TransactionManager txManager) { + + this.transactionManager = txManager; + this.debugSql = serverConfig.isDebugSql(); + this.debugLazyLoad = serverConfig.isDebugLazyLoad(); + } + + public void setLogLevel(LogLevel logLevel){ + transactionManager.setTransactionLogLevel(logLevel); + } + + public LogLevel getLogLevel() { + return transactionManager.getTransactionLogLevel(); + } + + public boolean isDebugGeneratedSql() { + return debugSql; + } + + public void setDebugGeneratedSql(boolean debugSql) { + this.debugSql = debugSql; + } + + public boolean isDebugLazyLoad() { + return debugLazyLoad; + } + + public void setDebugLazyLoad(boolean debugLazyLoad) { + this.debugLazyLoad = debugLazyLoad; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/jmx/package-info.java b/src/main/java/com/avaje/ebeaninternal/server/jmx/package-info.java index a706f9346..edaa2ad21 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/jmx/package-info.java +++ b/src/main/java/com/avaje/ebeaninternal/server/jmx/package-info.java @@ -1,4 +1 @@ -/** - * JMX MBeans. - */ package com.avaje.ebeaninternal.server.jmx; \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/ldap/DefaultLdapOrmQuery.java b/src/main/java/com/avaje/ebeaninternal/server/ldap/DefaultLdapOrmQuery.java index ecc9e7194..94caec158 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ldap/DefaultLdapOrmQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ldap/DefaultLdapOrmQuery.java @@ -1,33 +1,14 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.ldap; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.ExpressionFactory; -import com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery; - -public class DefaultLdapOrmQuery extends DefaultOrmQuery { - - private static final long serialVersionUID = -4344629258591773124L; - - public DefaultLdapOrmQuery(Class beanType, EbeanServer server, ExpressionFactory expressionFactory, String query) { - super(beanType, server, expressionFactory, query); - } -} +package com.avaje.ebeaninternal.server.ldap; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.ExpressionFactory; +import com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery; + +public class DefaultLdapOrmQuery extends DefaultOrmQuery { + + private static final long serialVersionUID = -4344629258591773124L; + + public DefaultLdapOrmQuery(Class beanType, EbeanServer server, ExpressionFactory expressionFactory, String query) { + super(beanType, server, expressionFactory, query); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/ldap/DefaultLdapPersister.java b/src/main/java/com/avaje/ebeaninternal/server/ldap/DefaultLdapPersister.java index 843455505..54c6a3ff6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ldap/DefaultLdapPersister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ldap/DefaultLdapPersister.java @@ -1,160 +1,141 @@ -/** - * 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.ldap; - -import java.util.Iterator; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.naming.Name; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; -import javax.naming.directory.BasicAttributes; -import javax.naming.directory.DirContext; - -import com.avaje.ebean.config.ldap.LdapContextFactory; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -public class DefaultLdapPersister { - - private static final Logger logger = Logger.getLogger(DefaultLdapPersister.class.getName()); - - private final LdapContextFactory contextFactory; - - public DefaultLdapPersister(LdapContextFactory dirContextFactory) { - this.contextFactory = dirContextFactory; - } - - public int persist(LdapPersistBeanRequest request) { - - switch (request.getType()) { - case INSERT: - return insert(request); - case UPDATE: - return update(request); - case DELETE: - return delete(request); - - default: - throw new LdapPersistenceException("Invalid type " + request.getType()); - } - } - - private int insert(LdapPersistBeanRequest request) { - - DirContext dc = contextFactory.createContext(); - - Name name = request.createLdapName(); - Attributes attrs = createAttributes(request, false, request.getLoadedProperties()); - - if (logger.isLoggable(Level.FINE)) { - logger.fine("Ldap Insert Name:" + name + " Attrs:" + attrs); - } - try { - dc.bind(name, null, attrs); - return 1; - - } catch (NamingException e) { - throw new LdapPersistenceException(e); - } - } - - private int delete(LdapPersistBeanRequest request) { - - DirContext dc = contextFactory.createContext(); - Name name = request.createLdapName(); - - if (logger.isLoggable(Level.FINE)) { - logger.fine("Ldap Delete Name:" + name); - } - - try { - dc.unbind(name); - return 1; - - } catch (NamingException e) { - throw new LdapPersistenceException(e); - } - } - - private int update(LdapPersistBeanRequest request) { - - Name name = request.createLdapName(); - - Set updatedProperties = request.getUpdatedProperties(); - if (updatedProperties == null || updatedProperties.isEmpty()) { - logger.info("Ldap Update has no changed properties? Name:" + name); - return 0; - } - - DirContext dc = contextFactory.createContext(); - Attributes attrs = createAttributes(request, true, updatedProperties); - - if (logger.isLoggable(Level.FINE)) { - logger.fine("Ldap Update Name:" + name + " Attrs:" + attrs); - } - - try { - dc.modifyAttributes(name, DirContext.REPLACE_ATTRIBUTE, attrs); - return 1; - - } catch (NamingException e) { - throw new LdapPersistenceException(e); - } - } - - private Attributes createAttributes(LdapPersistBeanRequest request, boolean update, Set props) { - - BeanDescriptor desc = request.getBeanDescriptor(); - - Attributes attrs = desc.createAttributes(); - if (update) { - attrs = new BasicAttributes(true); - } else { - attrs = desc.createAttributes(); - } - - Object bean = request.getBean(); - - if (props != null) { - for (String propName : props) { - BeanProperty p = desc.getBeanPropertyFromPath(propName); - Attribute attr = p.createAttribute(bean); - if (attr != null) { - attrs.put(attr); - } - } - } else { - Iterator it = desc.propertiesAll(); - while (it.hasNext()) { - BeanProperty p = it.next(); - Attribute attr = p.createAttribute(bean); - if (attr != null) { - attrs.put(attr); - } - } - } - - return attrs; - } -} +package com.avaje.ebeaninternal.server.ldap; + +import java.util.Iterator; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.naming.Name; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; + +import com.avaje.ebean.config.ldap.LdapContextFactory; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +public class DefaultLdapPersister { + + private static final Logger logger = Logger.getLogger(DefaultLdapPersister.class.getName()); + + private final LdapContextFactory contextFactory; + + public DefaultLdapPersister(LdapContextFactory dirContextFactory) { + this.contextFactory = dirContextFactory; + } + + public int persist(LdapPersistBeanRequest request) { + + switch (request.getType()) { + case INSERT: + return insert(request); + case UPDATE: + return update(request); + case DELETE: + return delete(request); + + default: + throw new LdapPersistenceException("Invalid type " + request.getType()); + } + } + + private int insert(LdapPersistBeanRequest request) { + + DirContext dc = contextFactory.createContext(); + + Name name = request.createLdapName(); + Attributes attrs = createAttributes(request, false, request.getLoadedProperties()); + + if (logger.isLoggable(Level.FINE)) { + logger.fine("Ldap Insert Name:" + name + " Attrs:" + attrs); + } + try { + dc.bind(name, null, attrs); + return 1; + + } catch (NamingException e) { + throw new LdapPersistenceException(e); + } + } + + private int delete(LdapPersistBeanRequest request) { + + DirContext dc = contextFactory.createContext(); + Name name = request.createLdapName(); + + if (logger.isLoggable(Level.FINE)) { + logger.fine("Ldap Delete Name:" + name); + } + + try { + dc.unbind(name); + return 1; + + } catch (NamingException e) { + throw new LdapPersistenceException(e); + } + } + + private int update(LdapPersistBeanRequest request) { + + Name name = request.createLdapName(); + + Set updatedProperties = request.getUpdatedProperties(); + if (updatedProperties == null || updatedProperties.isEmpty()) { + logger.info("Ldap Update has no changed properties? Name:" + name); + return 0; + } + + DirContext dc = contextFactory.createContext(); + Attributes attrs = createAttributes(request, true, updatedProperties); + + if (logger.isLoggable(Level.FINE)) { + logger.fine("Ldap Update Name:" + name + " Attrs:" + attrs); + } + + try { + dc.modifyAttributes(name, DirContext.REPLACE_ATTRIBUTE, attrs); + return 1; + + } catch (NamingException e) { + throw new LdapPersistenceException(e); + } + } + + private Attributes createAttributes(LdapPersistBeanRequest request, boolean update, Set props) { + + BeanDescriptor desc = request.getBeanDescriptor(); + + Attributes attrs = desc.createAttributes(); + if (update) { + attrs = new BasicAttributes(true); + } else { + attrs = desc.createAttributes(); + } + + Object bean = request.getBean(); + + if (props != null) { + for (String propName : props) { + BeanProperty p = desc.getBeanPropertyFromPath(propName); + Attribute attr = p.createAttribute(bean); + if (attr != null) { + attrs.put(attr); + } + } + } else { + Iterator it = desc.propertiesAll(); + while (it.hasNext()) { + BeanProperty p = it.next(); + Attribute attr = p.createAttribute(bean); + if (attr != null) { + attrs.put(attr); + } + } + } + + return attrs; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapBeanBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapBeanBuilder.java index cbd6c1aa1..df53635e2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapBeanBuilder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapBeanBuilder.java @@ -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.ldap; - -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.logging.Logger; - -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.event.BeanPersistController; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -public class LdapBeanBuilder { - - private static final Logger logger = Logger.getLogger(LdapBeanBuilder.class.getName()); - - private final BeanDescriptor beanDescriptor; - - private final boolean vanillaMode; - - private Set loadedProps; - - public LdapBeanBuilder(BeanDescriptor beanDescriptor, boolean vanillaMode) { - this.beanDescriptor = beanDescriptor; - this.vanillaMode = vanillaMode; - } - - @SuppressWarnings("unchecked") - public T readAttributes(Attributes attributes) throws NamingException { - - Object bean = beanDescriptor.createBean(vanillaMode); - - NamingEnumeration all = attributes.getAll(); - - boolean setLoadedProps = false; - if (loadedProps == null) { - setLoadedProps = true; - loadedProps = new LinkedHashSet(); - } - - while (all.hasMoreElements()) { - Attribute attr = all.nextElement(); - String attrName = attr.getID(); - - BeanProperty prop = beanDescriptor.getBeanPropertyFromDbColumn(attrName); - if (prop == null) { - if ("objectclass".equalsIgnoreCase(attrName)) { - // this is expected - } else { - logger.info("... hmm, no property to map to attribute[" + attrName + "] value["+attr.get()+"]"); - } - - } else { - prop.setAttributeValue(bean, attr); - if (setLoadedProps) { - loadedProps.add(prop.getName()); - } - } - } - - if (bean instanceof EntityBean) { - EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept(); - ebi.setLoadedProps(loadedProps); - ebi.setLoaded(); - } - - BeanPersistController persistController = beanDescriptor.getPersistController(); - if (persistController != null) { - persistController.postLoad(bean, loadedProps); - } - - return (T)bean; - } - -} +package com.avaje.ebeaninternal.server.ldap; + +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.logging.Logger; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.event.BeanPersistController; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +public class LdapBeanBuilder { + + private static final Logger logger = Logger.getLogger(LdapBeanBuilder.class.getName()); + + private final BeanDescriptor beanDescriptor; + + private final boolean vanillaMode; + + private Set loadedProps; + + public LdapBeanBuilder(BeanDescriptor beanDescriptor, boolean vanillaMode) { + this.beanDescriptor = beanDescriptor; + this.vanillaMode = vanillaMode; + } + + @SuppressWarnings("unchecked") + public T readAttributes(Attributes attributes) throws NamingException { + + Object bean = beanDescriptor.createBean(vanillaMode); + + NamingEnumeration all = attributes.getAll(); + + boolean setLoadedProps = false; + if (loadedProps == null) { + setLoadedProps = true; + loadedProps = new LinkedHashSet(); + } + + while (all.hasMoreElements()) { + Attribute attr = all.nextElement(); + String attrName = attr.getID(); + + BeanProperty prop = beanDescriptor.getBeanPropertyFromDbColumn(attrName); + if (prop == null) { + if ("objectclass".equalsIgnoreCase(attrName)) { + // this is expected + } else { + logger.info("... hmm, no property to map to attribute[" + attrName + "] value["+attr.get()+"]"); + } + + } else { + prop.setAttributeValue(bean, attr); + if (setLoadedProps) { + loadedProps.add(prop.getName()); + } + } + } + + if (bean instanceof EntityBean) { + EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept(); + ebi.setLoadedProps(loadedProps); + ebi.setLoaded(); + } + + BeanPersistController persistController = beanDescriptor.getPersistController(); + if (persistController != null) { + persistController.postLoad(bean, loadedProps); + } + + return (T)bean; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapOrmQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapOrmQueryEngine.java index e1ff07733..9891af4cb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapOrmQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapOrmQueryEngine.java @@ -1,54 +1,35 @@ -/** - * 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.ldap; - -import java.util.List; - -import javax.naming.directory.DirContext; - -import com.avaje.ebean.config.ldap.LdapContextFactory; - -public class LdapOrmQueryEngine { - - private final boolean defaultVanillaMode; - - private final LdapContextFactory contextFactory; - - public LdapOrmQueryEngine(boolean defaultVanillaMode, LdapContextFactory contextFactory) { - this.defaultVanillaMode = defaultVanillaMode; - this.contextFactory = contextFactory; - } - - public T findId(LdapOrmQueryRequest request) { - DirContext dc = contextFactory.createContext(); - LdapOrmQueryExecute exe = new LdapOrmQueryExecute(request, defaultVanillaMode, dc); - - return exe.findId(); - } - - public List findList(LdapOrmQueryRequest request) { - - DirContext dc = contextFactory.createContext(); - - LdapOrmQueryExecute exe = new LdapOrmQueryExecute(request, defaultVanillaMode, dc); - - return exe.findList(); - } -} +package com.avaje.ebeaninternal.server.ldap; + +import java.util.List; + +import javax.naming.directory.DirContext; + +import com.avaje.ebean.config.ldap.LdapContextFactory; + +public class LdapOrmQueryEngine { + + private final boolean defaultVanillaMode; + + private final LdapContextFactory contextFactory; + + public LdapOrmQueryEngine(boolean defaultVanillaMode, LdapContextFactory contextFactory) { + this.defaultVanillaMode = defaultVanillaMode; + this.contextFactory = contextFactory; + } + + public T findId(LdapOrmQueryRequest request) { + DirContext dc = contextFactory.createContext(); + LdapOrmQueryExecute exe = new LdapOrmQueryExecute(request, defaultVanillaMode, dc); + + return exe.findId(); + } + + public List findList(LdapOrmQueryRequest request) { + + DirContext dc = contextFactory.createContext(); + + LdapOrmQueryExecute exe = new LdapOrmQueryExecute(request, defaultVanillaMode, dc); + + return exe.findList(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapOrmQueryExecute.java b/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapOrmQueryExecute.java index 6ba43def2..a835bc093 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapOrmQueryExecute.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapOrmQueryExecute.java @@ -1,148 +1,129 @@ -/** - * 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.ldap; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.Attributes; -import javax.naming.directory.DirContext; -import javax.naming.directory.SearchControls; -import javax.naming.directory.SearchResult; -import javax.naming.ldap.LdapName; - -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; - -public class LdapOrmQueryExecute { - - private static final Logger logger = Logger.getLogger(LdapOrmQueryExecute.class.getName()); - - private final SpiQuery query; - - private final BeanDescriptor beanDescriptor; - - private final DirContext dc; - - private final LdapBeanBuilder beanBuilder; - - private final String filterExpr; - - private final Object[] filterValues; - - private final String[] selectProps; - - public LdapOrmQueryExecute(LdapOrmQueryRequest request, boolean defaultVanillaMode, DirContext dc) { - - this.query = request.getQuery(); - this.beanDescriptor = request.getBeanDescriptor(); - this.dc = dc; - - boolean vanillaMode = query.isVanillaMode(defaultVanillaMode); - this.beanBuilder = new LdapBeanBuilder(beanDescriptor, vanillaMode); - - LdapQueryDeployHelper deployHelper = new LdapQueryDeployHelper(request); - this.selectProps = deployHelper.getSelectedProperties(); - this.filterExpr = deployHelper.getFilterExpr(); - this.filterValues = deployHelper.getFilterValues(); - } - - public T findId() { - - Object id = query.getId(); - - try { - LdapName dn = beanDescriptor.createLdapNameById(id); - - String[] findAttrs = selectProps; - if (findAttrs == null){ - findAttrs = beanDescriptor.getDefaultSelectDbArray(); - } - - // build a string describing the query - String debugQuery = "Name:"+dn+" attrs:"+Arrays.toString(findAttrs); - - Attributes attrs = dc.getAttributes(dn, findAttrs); - - T bean = beanBuilder.readAttributes(attrs); - - query.setGeneratedSql(debugQuery); - return bean; - - } catch (NamingException e) { - throw new LdapPersistenceException(e); - } - } - - public List findList() { - - SearchControls sc = new SearchControls(); - sc.setSearchScope(SearchControls.ONELEVEL_SCOPE); - - List list = new ArrayList(); - - try { - LdapName dn = beanDescriptor.createLdapName(null); - - // build a string describing the query - String debugQuery = "Name:"+dn; - - if (selectProps != null) { - sc.setReturningAttributes(selectProps); - debugQuery += " select:"+Arrays.toString(selectProps); - } - - if (logger.isLoggable(Level.INFO)){ - logger.info("Ldap Query Name:"+dn+" filterExpr:"+filterExpr); - } - - debugQuery += " filterExpr:"+filterExpr; - - NamingEnumeration result; - if (filterValues == null || filterValues.length == 0) { - result = dc.search(dn, filterExpr, sc); - } else { - debugQuery += " filterValues:"+Arrays.toString(filterValues); - result = dc.search(dn, filterExpr, filterValues, sc); - } - - query.setGeneratedSql(debugQuery); - - if (result != null){ - while (result.hasMoreElements()) { - SearchResult row = result.nextElement(); - T bean = beanBuilder.readAttributes(row.getAttributes()); - list.add(bean); - } - } - - return list; - - } catch (NamingException e) { - throw new LdapPersistenceException(e); - } - } - -} +package com.avaje.ebeaninternal.server.ldap; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.naming.ldap.LdapName; + +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +public class LdapOrmQueryExecute { + + private static final Logger logger = Logger.getLogger(LdapOrmQueryExecute.class.getName()); + + private final SpiQuery query; + + private final BeanDescriptor beanDescriptor; + + private final DirContext dc; + + private final LdapBeanBuilder beanBuilder; + + private final String filterExpr; + + private final Object[] filterValues; + + private final String[] selectProps; + + public LdapOrmQueryExecute(LdapOrmQueryRequest request, boolean defaultVanillaMode, DirContext dc) { + + this.query = request.getQuery(); + this.beanDescriptor = request.getBeanDescriptor(); + this.dc = dc; + + boolean vanillaMode = query.isVanillaMode(defaultVanillaMode); + this.beanBuilder = new LdapBeanBuilder(beanDescriptor, vanillaMode); + + LdapQueryDeployHelper deployHelper = new LdapQueryDeployHelper(request); + this.selectProps = deployHelper.getSelectedProperties(); + this.filterExpr = deployHelper.getFilterExpr(); + this.filterValues = deployHelper.getFilterValues(); + } + + public T findId() { + + Object id = query.getId(); + + try { + LdapName dn = beanDescriptor.createLdapNameById(id); + + String[] findAttrs = selectProps; + if (findAttrs == null){ + findAttrs = beanDescriptor.getDefaultSelectDbArray(); + } + + // build a string describing the query + String debugQuery = "Name:"+dn+" attrs:"+Arrays.toString(findAttrs); + + Attributes attrs = dc.getAttributes(dn, findAttrs); + + T bean = beanBuilder.readAttributes(attrs); + + query.setGeneratedSql(debugQuery); + return bean; + + } catch (NamingException e) { + throw new LdapPersistenceException(e); + } + } + + public List findList() { + + SearchControls sc = new SearchControls(); + sc.setSearchScope(SearchControls.ONELEVEL_SCOPE); + + List list = new ArrayList(); + + try { + LdapName dn = beanDescriptor.createLdapName(null); + + // build a string describing the query + String debugQuery = "Name:"+dn; + + if (selectProps != null) { + sc.setReturningAttributes(selectProps); + debugQuery += " select:"+Arrays.toString(selectProps); + } + + if (logger.isLoggable(Level.INFO)){ + logger.info("Ldap Query Name:"+dn+" filterExpr:"+filterExpr); + } + + debugQuery += " filterExpr:"+filterExpr; + + NamingEnumeration result; + if (filterValues == null || filterValues.length == 0) { + result = dc.search(dn, filterExpr, sc); + } else { + debugQuery += " filterValues:"+Arrays.toString(filterValues); + result = dc.search(dn, filterExpr, filterValues, sc); + } + + query.setGeneratedSql(debugQuery); + + if (result != null){ + while (result.hasMoreElements()) { + SearchResult row = result.nextElement(); + T bean = beanBuilder.readAttributes(row.getAttributes()); + list.add(bean); + } + } + + return list; + + } catch (NamingException e) { + throw new LdapPersistenceException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapOrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapOrmQueryRequest.java index 3352d7475..aaad3da57 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapOrmQueryRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapOrmQueryRequest.java @@ -1,105 +1,86 @@ -/** - * 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.ldap; - -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.core.SpiOrmQueryRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; - -public class LdapOrmQueryRequest implements SpiOrmQueryRequest { - - private final SpiQuery query; - private final BeanDescriptor desc; - private final LdapOrmQueryEngine queryEngine; - - public LdapOrmQueryRequest(SpiQuery query, BeanDescriptor desc, LdapOrmQueryEngine queryEngine) { - this.query = query; - this.desc = desc; - this.queryEngine = queryEngine; - } - - public BeanDescriptor getBeanDescriptor() { - return desc; - } - - public SpiQuery getQuery() { - return query; - } - - public Object findId() { - return queryEngine.findId(this); - } - - public List findIds() { - throw new RuntimeException("Not Implemented yet"); - } - - public List findList() { - return queryEngine.findList(this); - } - - public void findVisit(QueryResultVisitor visitor) { - throw new RuntimeException("Not Implemented yet"); - } - - public QueryIterator findIterate() { - throw new RuntimeException("Not Implemented yet"); - } - - public Map findMap() { - throw new RuntimeException("Not Implemented yet"); - } - - public int findRowCount() { - throw new RuntimeException("Not Implemented yet"); - } - - public Set findSet() { - throw new RuntimeException("Not Implemented yet"); - } - - public T getFromPersistenceContextOrCache() { - return null; - } - - public BeanCollection getFromQueryCache() { - return null; - } - - public void initTransIfRequired() { - // nothing to do here - } - - public void rollbackTransIfRequired() { - // nothing to do here - } - - public void endTransIfRequired() { - // nothing to do here - } - -} +package com.avaje.ebeaninternal.server.ldap; + +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.core.SpiOrmQueryRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +public class LdapOrmQueryRequest implements SpiOrmQueryRequest { + + private final SpiQuery query; + private final BeanDescriptor desc; + private final LdapOrmQueryEngine queryEngine; + + public LdapOrmQueryRequest(SpiQuery query, BeanDescriptor desc, LdapOrmQueryEngine queryEngine) { + this.query = query; + this.desc = desc; + this.queryEngine = queryEngine; + } + + public BeanDescriptor getBeanDescriptor() { + return desc; + } + + public SpiQuery getQuery() { + return query; + } + + public Object findId() { + return queryEngine.findId(this); + } + + public List findIds() { + throw new RuntimeException("Not Implemented yet"); + } + + public List findList() { + return queryEngine.findList(this); + } + + public void findVisit(QueryResultVisitor visitor) { + throw new RuntimeException("Not Implemented yet"); + } + + public QueryIterator findIterate() { + throw new RuntimeException("Not Implemented yet"); + } + + public Map findMap() { + throw new RuntimeException("Not Implemented yet"); + } + + public int findRowCount() { + throw new RuntimeException("Not Implemented yet"); + } + + public Set findSet() { + throw new RuntimeException("Not Implemented yet"); + } + + public T getFromPersistenceContextOrCache() { + return null; + } + + public BeanCollection getFromQueryCache() { + return null; + } + + public void initTransIfRequired() { + // nothing to do here + } + + public void rollbackTransIfRequired() { + // nothing to do here + } + + public void endTransIfRequired() { + // nothing to do here + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapPersistBeanRequest.java b/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapPersistBeanRequest.java index b35c663cb..7035e5dbc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapPersistBeanRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapPersistBeanRequest.java @@ -1,79 +1,60 @@ -/** - * 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.ldap; - -import java.util.Set; - -import javax.naming.ldap.LdapName; - -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.server.core.ConcurrencyMode; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanManager; - -public class LdapPersistBeanRequest extends PersistRequestBean { - - private final DefaultLdapPersister persister; - - public LdapPersistBeanRequest(SpiEbeanServer server, T bean, Object parentBean, BeanManager mgr, DefaultLdapPersister persister) { - - super(server, bean, parentBean, mgr, null, null); - this.persister = persister; - } - - public LdapPersistBeanRequest(SpiEbeanServer server, T bean, Object parentBean, BeanManager mgr, DefaultLdapPersister persister, - Set updateProps, ConcurrencyMode concurrencyMode) { - - super(server, bean, parentBean, mgr, null, null, updateProps, concurrencyMode); - this.persister = persister; - } - - public LdapName createLdapName() { - return beanDescriptor.createLdapName(bean); - } - - @Override - public int executeNow() { - - return persister.persist(this); - } - - @Override - public int executeOrQueue() { - return executeNow(); - } - - @Override - public void initTransIfRequired() { - // no transaction at this stage for Ldap - } - - @Override - public void commitTransIfRequired() { - // no transaction at this stage for Ldap - } - - @Override - public void rollbackTransIfRequired() { - // no transaction at this stage for Ldap - } - - -} +package com.avaje.ebeaninternal.server.ldap; + +import java.util.Set; + +import javax.naming.ldap.LdapName; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.core.ConcurrencyMode; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanManager; + +public class LdapPersistBeanRequest extends PersistRequestBean { + + private final DefaultLdapPersister persister; + + public LdapPersistBeanRequest(SpiEbeanServer server, T bean, Object parentBean, BeanManager mgr, DefaultLdapPersister persister) { + + super(server, bean, parentBean, mgr, null, null); + this.persister = persister; + } + + public LdapPersistBeanRequest(SpiEbeanServer server, T bean, Object parentBean, BeanManager mgr, DefaultLdapPersister persister, + Set updateProps, ConcurrencyMode concurrencyMode) { + + super(server, bean, parentBean, mgr, null, null, updateProps, concurrencyMode); + this.persister = persister; + } + + public LdapName createLdapName() { + return beanDescriptor.createLdapName(bean); + } + + @Override + public int executeNow() { + + return persister.persist(this); + } + + @Override + public int executeOrQueue() { + return executeNow(); + } + + @Override + public void initTransIfRequired() { + // no transaction at this stage for Ldap + } + + @Override + public void commitTransIfRequired() { + // no transaction at this stage for Ldap + } + + @Override + public void rollbackTransIfRequired() { + // no transaction at this stage for Ldap + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapPersistenceException.java b/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapPersistenceException.java index 4a2358b7e..a843899be 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapPersistenceException.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapPersistenceException.java @@ -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.ldap; - -import javax.persistence.PersistenceException; - -public class LdapPersistenceException extends PersistenceException { - - private static final long serialVersionUID = -3170359404117927668L; - - public LdapPersistenceException(Throwable e){ - super(e); - } - - public LdapPersistenceException(String msg, Throwable e){ - super(msg, e); - } - - public LdapPersistenceException(String msg){ - super(msg); - } - -} +package com.avaje.ebeaninternal.server.ldap; + +import javax.persistence.PersistenceException; + +public class LdapPersistenceException extends PersistenceException { + + private static final long serialVersionUID = -3170359404117927668L; + + public LdapPersistenceException(Throwable e){ + super(e); + } + + public LdapPersistenceException(String msg, Throwable e){ + super(msg, e); + } + + public LdapPersistenceException(String msg){ + super(msg); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapQueryDeployHelper.java b/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapQueryDeployHelper.java index 7e9ac9dd7..1f3b99d79 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapQueryDeployHelper.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ldap/LdapQueryDeployHelper.java @@ -1,110 +1,91 @@ -/** - * 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.ldap; - -import java.util.ArrayList; -import java.util.Iterator; - -import com.avaje.ebeaninternal.api.SpiExpressionList; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.DeployPropertyParser; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties; -import com.avaje.ebeaninternal.util.DefaultExpressionRequest; - -public class LdapQueryDeployHelper { - - private final LdapOrmQueryRequest request; - private final SpiQuery query; - private final BeanDescriptor desc; - - private String filterExpr; - private Object[] filterValues; - - public LdapQueryDeployHelper(LdapOrmQueryRequest request) { - this.request = request; - this.query = request.getQuery(); - this.desc = request.getBeanDescriptor(); - - parse(); - } - - public String[] getSelectedProperties() { - - OrmQueryProperties chunk = query.getDetail().getChunk(null, false); - if (chunk.allProperties()) { - return null; - } - - // convert to array of String[] for setReturningAttributes(); - ArrayList ldapSelectProps = new ArrayList(); - - Iterator selectProperties = chunk.getSelectProperties(); - while (selectProperties.hasNext()) { - String propName = selectProperties.next(); - BeanProperty p = desc.getBeanProperty(propName); - if (p != null) { - propName = p.getDbColumn(); - } - ldapSelectProps.add(propName); - } - return ldapSelectProps.toArray(new String[ldapSelectProps.size()]); - - } - - private void parse() { - - DeployPropertyParser deployParser = desc.createDeployPropertyParser(); - - String baseWhere = query.getAdditionalWhere(); - if (baseWhere != null){ - baseWhere = deployParser.parse(baseWhere); - } - - - SpiExpressionList whereExp = query.getWhereExpressions(); - if (whereExp != null) { - - DefaultExpressionRequest expReq = new DefaultExpressionRequest(request, deployParser); - - ArrayList bindValues = whereExp.buildBindValues(expReq); - filterValues = bindValues.toArray(new Object[bindValues.size()]); - String exprWhere = whereExp.buildSql(expReq); - - if (baseWhere != null){ - filterExpr = "(&"+baseWhere +exprWhere+")"; - } else { - filterExpr = exprWhere; - } - } else { - filterExpr = baseWhere; - } - } - - public String getFilterExpr() { - return filterExpr; - } - - public Object[] getFilterValues() { - return filterValues; - } - -} +package com.avaje.ebeaninternal.server.ldap; + +import java.util.ArrayList; +import java.util.Iterator; + +import com.avaje.ebeaninternal.api.SpiExpressionList; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.DeployPropertyParser; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties; +import com.avaje.ebeaninternal.util.DefaultExpressionRequest; + +public class LdapQueryDeployHelper { + + private final LdapOrmQueryRequest request; + private final SpiQuery query; + private final BeanDescriptor desc; + + private String filterExpr; + private Object[] filterValues; + + public LdapQueryDeployHelper(LdapOrmQueryRequest request) { + this.request = request; + this.query = request.getQuery(); + this.desc = request.getBeanDescriptor(); + + parse(); + } + + public String[] getSelectedProperties() { + + OrmQueryProperties chunk = query.getDetail().getChunk(null, false); + if (chunk.allProperties()) { + return null; + } + + // convert to array of String[] for setReturningAttributes(); + ArrayList ldapSelectProps = new ArrayList(); + + Iterator selectProperties = chunk.getSelectProperties(); + while (selectProperties.hasNext()) { + String propName = selectProperties.next(); + BeanProperty p = desc.getBeanProperty(propName); + if (p != null) { + propName = p.getDbColumn(); + } + ldapSelectProps.add(propName); + } + return ldapSelectProps.toArray(new String[ldapSelectProps.size()]); + + } + + private void parse() { + + DeployPropertyParser deployParser = desc.createDeployPropertyParser(); + + String baseWhere = query.getAdditionalWhere(); + if (baseWhere != null){ + baseWhere = deployParser.parse(baseWhere); + } + + + SpiExpressionList whereExp = query.getWhereExpressions(); + if (whereExp != null) { + + DefaultExpressionRequest expReq = new DefaultExpressionRequest(request, deployParser); + + ArrayList bindValues = whereExp.buildBindValues(expReq); + filterValues = bindValues.toArray(new Object[bindValues.size()]); + String exprWhere = whereExp.buildSql(expReq); + + if (baseWhere != null){ + filterExpr = "(&"+baseWhere +exprWhere+")"; + } else { + filterExpr = exprWhere; + } + } else { + filterExpr = baseWhere; + } + } + + public String getFilterExpr() { + return filterExpr; + } + + public Object[] getFilterValues() { + return filterValues; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/ldap/expression/LdAbstractExpression.java b/src/main/java/com/avaje/ebeaninternal/server/ldap/expression/LdAbstractExpression.java index c74e69d0a..e7421ec6e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ldap/expression/LdAbstractExpression.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ldap/expression/LdAbstractExpression.java @@ -1,64 +1,45 @@ -/** - * 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.ldap.expression; - -import com.avaje.ebeaninternal.api.ManyWhereJoins; -import com.avaje.ebeaninternal.api.SpiExpression; -import com.avaje.ebeaninternal.api.SpiExpressionRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.el.ElPropertyDeploy; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; - -/** - * Base class for simple expressions. - * - * @author rbygrave - */ -public abstract class LdAbstractExpression implements SpiExpression { - - private static final long serialVersionUID = 4072786211853856174L; - - protected final String propertyName; - - protected LdAbstractExpression(String propertyName) { - this.propertyName = propertyName; - } - - protected String nextParam(SpiExpressionRequest request) { - - int pos = request.nextParameter(); - return "{"+(pos-1)+"}"; - } - - public void containsMany(BeanDescriptor desc, ManyWhereJoins manyWhereJoin) { - - if (propertyName != null){ - ElPropertyDeploy elProp = desc.getElPropertyDeploy(propertyName); - if (elProp != null && elProp.containsMany()){ - manyWhereJoin.add(elProp); - } - } - } - - protected ElPropertyValue getElProp(SpiExpressionRequest request) { - - return request.getBeanDescriptor().getElGetValue(propertyName); - } -} +package com.avaje.ebeaninternal.server.ldap.expression; + +import com.avaje.ebeaninternal.api.ManyWhereJoins; +import com.avaje.ebeaninternal.api.SpiExpression; +import com.avaje.ebeaninternal.api.SpiExpressionRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.el.ElPropertyDeploy; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; + +/** + * Base class for simple expressions. + * + * @author rbygrave + */ +public abstract class LdAbstractExpression implements SpiExpression { + + private static final long serialVersionUID = 4072786211853856174L; + + protected final String propertyName; + + protected LdAbstractExpression(String propertyName) { + this.propertyName = propertyName; + } + + protected String nextParam(SpiExpressionRequest request) { + + int pos = request.nextParameter(); + return "{"+(pos-1)+"}"; + } + + public void containsMany(BeanDescriptor desc, ManyWhereJoins manyWhereJoin) { + + if (propertyName != null){ + ElPropertyDeploy elProp = desc.getElPropertyDeploy(propertyName); + if (elProp != null && elProp.containsMany()){ + manyWhereJoin.add(elProp); + } + } + } + + protected ElPropertyValue getElProp(SpiExpressionRequest request) { + + return request.getBeanDescriptor().getElGetValue(propertyName); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/ldap/expression/LdapExpressionFactory.java b/src/main/java/com/avaje/ebeaninternal/server/ldap/expression/LdapExpressionFactory.java index 5dbf04bb0..4e3605910 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ldap/expression/LdapExpressionFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ldap/expression/LdapExpressionFactory.java @@ -1,268 +1,249 @@ -/** - * 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.ldap.expression; - -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import com.avaje.ebean.ExampleExpression; -import com.avaje.ebean.Expression; -import com.avaje.ebean.ExpressionFactory; -import com.avaje.ebean.ExpressionList; -import com.avaje.ebean.Junction; -import com.avaje.ebean.LikeType; -import com.avaje.ebean.Query; -import com.avaje.ebeaninternal.server.ldap.LdapPersistenceException; -import com.avaje.ebeaninternal.server.ldap.expression.LdSimpleExpression.Op; - -public class LdapExpressionFactory implements ExpressionFactory { - - public String getLang() { - return "ldap"; - } - - public ExpressionFactory createExpressionFactory(String path) { - return new LdapExpressionFactory(); - } - - @SuppressWarnings({ "rawtypes" }) - public Expression allEq(Map propertyMap) { - - Junction conjunction = new LdJunctionExpression.Conjunction(this); - - Iterator> it = propertyMap.entrySet().iterator(); - while (it.hasNext()) { - Entry entry = it.next(); - conjunction.add(eq(entry.getKey(), entry.getValue())); - } - return conjunction; - } - - public Expression and(Expression expOne, Expression expTwo) { - return new LdLogicExpression.And(expOne, expTwo); - } - - public Expression between(String propertyName, Object value1, Object value2) { - Expression e1 = gt(propertyName, value1); - Expression e2 = lt(propertyName, value2); - return and(e1, e2); - } - - public Expression betweenProperties(String lowProperty, String highProperty, Object value) { - throw new RuntimeException("Not Implemented"); - } - - public Expression contains(String propertyName, String value) { - if (!value.endsWith("*")){ - value = "*"+value+"*"; - } - return new LdSimpleExpression(propertyName, Op.EQ, value); - } - - public Junction conjunction(Query query) { - return new LdJunctionExpression.Conjunction(query, query.where()); - } - - - public Junction disjunction(Query query) { - return new LdJunctionExpression.Disjunction(query, query.where()); - } - - public Junction conjunction(Query query, ExpressionList parent) { - return new LdJunctionExpression.Conjunction(query, parent); - } - - public Junction disjunction(Query query, ExpressionList parent) { - return new LdJunctionExpression.Disjunction(query, parent); - } - - public Expression endsWith(String propertyName, String value) { - if (!value.startsWith("*")){ - value = "*"+value; - } - return new LdLikeExpression(propertyName, value); - } - - public Expression eq(String propertyName, Object value) { - return new LdSimpleExpression(propertyName, Op.EQ, value); - } - - public Expression lucene(String propertyName, String value) { - throw new RuntimeException("Not Implemented"); - } - - public Expression lucene(String value) { - throw new RuntimeException("Not Implemented"); - } - - public ExampleExpression exampleLike(Object example, boolean caseInsensitive, LikeType likeType) { - throw new RuntimeException("Not Implemented"); - } - - public ExampleExpression exampleLike(Object example) { - throw new RuntimeException("Not Implemented"); - } - - public Expression ge(String propertyName, Object value) { - return new LdSimpleExpression(propertyName, Op.GT_EQ, value); - } - - public Expression gt(String propertyName, Object value) { - return new LdSimpleExpression(propertyName, Op.GT, value); - } - - public Expression icontains(String propertyName, String value) { - if (!value.endsWith("*")){ - value = "*"+value+"*"; - } - return new LdLikeExpression(propertyName, value); - } - - public Expression idEq(Object value) { - // TODO Auto-generated method stub - return null; - } - - public Expression idIn(List idList) { - throw new RuntimeException("Not Implemented"); - } - - public Expression iendsWith(String propertyName, String value) { - if (!value.startsWith("*")){ - value = "*"+value; - } - return new LdLikeExpression(propertyName, value); - } - - public Expression ieq(String propertyName, String value) { - return new LdSimpleExpression(propertyName, Op.EQ, value); - } - - public ExampleExpression iexampleLike(Object example) { - throw new RuntimeException("Not Implemented"); - } - - public Expression ilike(String propertyName, String value) { - return new LdLikeExpression(propertyName, value); - } - - @SuppressWarnings({ "rawtypes" }) - public Expression in(String propertyName, Collection values) { - - if (values == null || values.isEmpty()){ - throw new LdapPersistenceException("collection can't be empty for Ldap"); - } - - Junction disjunction = new LdJunctionExpression.Disjunction(this); - for (Object v : values) { - disjunction.add(eq(propertyName, v)); - } - - return disjunction; - } - - @SuppressWarnings("rawtypes") - public Expression in(String propertyName, Object[] values) { - - if (values == null || values.length == 0){ - throw new LdapPersistenceException("values can't be empty for Ldap"); - } - - Junction disjunction = new LdJunctionExpression.Disjunction(this); - for (Object v : values) { - disjunction.add(eq(propertyName, v)); - } - - return disjunction; - } - - public Expression in(String propertyName, Query subQuery) { - throw new RuntimeException("Not Implemented"); - } - - public Expression isNotNull(String propertyName) { - return new LdPresentExpression(propertyName); - } - - public Expression isNull(String propertyName) { - LdPresentExpression exp = new LdPresentExpression(propertyName); - return new LdNotExpression(exp); - } - - public Expression istartsWith(String propertyName, String value) { - if (!value.endsWith("*")){ - value = value+"*"; - } - return new LdLikeExpression(propertyName, value); - } - - public Expression le(String propertyName, Object value) { - return new LdSimpleExpression(propertyName, Op.LT_EQ, value); - } - - public Expression like(String propertyName, String value) { - return new LdLikeExpression(propertyName, value); - } - - public Expression lt(String propertyName, Object value) { - return new LdSimpleExpression(propertyName, Op.LT, value); - } - - public Expression ne(String propertyName, Object value) { - return new LdSimpleExpression(propertyName, Op.NOT_EQ, value); - } - - public Expression not(Expression exp) { - return new LdNotExpression(exp); - } - - public Expression or(Expression expOne, Expression expTwo) { - return new LdLogicExpression.Or(expOne, expTwo); - } - - public Expression raw(String raw, Object value) { - if (value != null){ - return new LdRawExpression(raw, new Object[]{value}); - - } else { - return new LdRawExpression(raw, null); - } - } - - public Expression raw(String raw, Object[] values) { - return new LdRawExpression(raw, values); - } - - public Expression raw(String raw) { - return new LdRawExpression(raw, null); - } - - public Expression startsWith(String propertyName, String value) { - if (!value.endsWith("*")){ - value = value+"*"; - } - return new LdLikeExpression(propertyName, value); - } - - -} +package com.avaje.ebeaninternal.server.ldap.expression; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import com.avaje.ebean.ExampleExpression; +import com.avaje.ebean.Expression; +import com.avaje.ebean.ExpressionFactory; +import com.avaje.ebean.ExpressionList; +import com.avaje.ebean.Junction; +import com.avaje.ebean.LikeType; +import com.avaje.ebean.Query; +import com.avaje.ebeaninternal.server.ldap.LdapPersistenceException; +import com.avaje.ebeaninternal.server.ldap.expression.LdSimpleExpression.Op; + +public class LdapExpressionFactory implements ExpressionFactory { + + public String getLang() { + return "ldap"; + } + + public ExpressionFactory createExpressionFactory(String path) { + return new LdapExpressionFactory(); + } + + @SuppressWarnings({ "rawtypes" }) + public Expression allEq(Map propertyMap) { + + Junction conjunction = new LdJunctionExpression.Conjunction(this); + + Iterator> it = propertyMap.entrySet().iterator(); + while (it.hasNext()) { + Entry entry = it.next(); + conjunction.add(eq(entry.getKey(), entry.getValue())); + } + return conjunction; + } + + public Expression and(Expression expOne, Expression expTwo) { + return new LdLogicExpression.And(expOne, expTwo); + } + + public Expression between(String propertyName, Object value1, Object value2) { + Expression e1 = gt(propertyName, value1); + Expression e2 = lt(propertyName, value2); + return and(e1, e2); + } + + public Expression betweenProperties(String lowProperty, String highProperty, Object value) { + throw new RuntimeException("Not Implemented"); + } + + public Expression contains(String propertyName, String value) { + if (!value.endsWith("*")){ + value = "*"+value+"*"; + } + return new LdSimpleExpression(propertyName, Op.EQ, value); + } + + public Junction conjunction(Query query) { + return new LdJunctionExpression.Conjunction(query, query.where()); + } + + + public Junction disjunction(Query query) { + return new LdJunctionExpression.Disjunction(query, query.where()); + } + + public Junction conjunction(Query query, ExpressionList parent) { + return new LdJunctionExpression.Conjunction(query, parent); + } + + public Junction disjunction(Query query, ExpressionList parent) { + return new LdJunctionExpression.Disjunction(query, parent); + } + + public Expression endsWith(String propertyName, String value) { + if (!value.startsWith("*")){ + value = "*"+value; + } + return new LdLikeExpression(propertyName, value); + } + + public Expression eq(String propertyName, Object value) { + return new LdSimpleExpression(propertyName, Op.EQ, value); + } + + public Expression lucene(String propertyName, String value) { + throw new RuntimeException("Not Implemented"); + } + + public Expression lucene(String value) { + throw new RuntimeException("Not Implemented"); + } + + public ExampleExpression exampleLike(Object example, boolean caseInsensitive, LikeType likeType) { + throw new RuntimeException("Not Implemented"); + } + + public ExampleExpression exampleLike(Object example) { + throw new RuntimeException("Not Implemented"); + } + + public Expression ge(String propertyName, Object value) { + return new LdSimpleExpression(propertyName, Op.GT_EQ, value); + } + + public Expression gt(String propertyName, Object value) { + return new LdSimpleExpression(propertyName, Op.GT, value); + } + + public Expression icontains(String propertyName, String value) { + if (!value.endsWith("*")){ + value = "*"+value+"*"; + } + return new LdLikeExpression(propertyName, value); + } + + public Expression idEq(Object value) { + // TODO Auto-generated method stub + return null; + } + + public Expression idIn(List idList) { + throw new RuntimeException("Not Implemented"); + } + + public Expression iendsWith(String propertyName, String value) { + if (!value.startsWith("*")){ + value = "*"+value; + } + return new LdLikeExpression(propertyName, value); + } + + public Expression ieq(String propertyName, String value) { + return new LdSimpleExpression(propertyName, Op.EQ, value); + } + + public ExampleExpression iexampleLike(Object example) { + throw new RuntimeException("Not Implemented"); + } + + public Expression ilike(String propertyName, String value) { + return new LdLikeExpression(propertyName, value); + } + + @SuppressWarnings({ "rawtypes" }) + public Expression in(String propertyName, Collection values) { + + if (values == null || values.isEmpty()){ + throw new LdapPersistenceException("collection can't be empty for Ldap"); + } + + Junction disjunction = new LdJunctionExpression.Disjunction(this); + for (Object v : values) { + disjunction.add(eq(propertyName, v)); + } + + return disjunction; + } + + @SuppressWarnings("rawtypes") + public Expression in(String propertyName, Object[] values) { + + if (values == null || values.length == 0){ + throw new LdapPersistenceException("values can't be empty for Ldap"); + } + + Junction disjunction = new LdJunctionExpression.Disjunction(this); + for (Object v : values) { + disjunction.add(eq(propertyName, v)); + } + + return disjunction; + } + + public Expression in(String propertyName, Query subQuery) { + throw new RuntimeException("Not Implemented"); + } + + public Expression isNotNull(String propertyName) { + return new LdPresentExpression(propertyName); + } + + public Expression isNull(String propertyName) { + LdPresentExpression exp = new LdPresentExpression(propertyName); + return new LdNotExpression(exp); + } + + public Expression istartsWith(String propertyName, String value) { + if (!value.endsWith("*")){ + value = value+"*"; + } + return new LdLikeExpression(propertyName, value); + } + + public Expression le(String propertyName, Object value) { + return new LdSimpleExpression(propertyName, Op.LT_EQ, value); + } + + public Expression like(String propertyName, String value) { + return new LdLikeExpression(propertyName, value); + } + + public Expression lt(String propertyName, Object value) { + return new LdSimpleExpression(propertyName, Op.LT, value); + } + + public Expression ne(String propertyName, Object value) { + return new LdSimpleExpression(propertyName, Op.NOT_EQ, value); + } + + public Expression not(Expression exp) { + return new LdNotExpression(exp); + } + + public Expression or(Expression expOne, Expression expTwo) { + return new LdLogicExpression.Or(expOne, expTwo); + } + + public Expression raw(String raw, Object value) { + if (value != null){ + return new LdRawExpression(raw, new Object[]{value}); + + } else { + return new LdRawExpression(raw, null); + } + } + + public Expression raw(String raw, Object[] values) { + return new LdRawExpression(raw, values); + } + + public Expression raw(String raw) { + return new LdRawExpression(raw, null); + } + + public Expression startsWith(String propertyName, String value) { + if (!value.endsWith("*")){ + value = value+"*"; + } + return new LdLikeExpression(propertyName, value); + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/ldap/expression/package-info.java b/src/main/java/com/avaje/ebeaninternal/server/ldap/expression/package-info.java index bbc12426f..e91415b66 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ldap/expression/package-info.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ldap/expression/package-info.java @@ -1,4 +1 @@ -/** - * LDAP query expressions. - */ package com.avaje.ebeaninternal.server.ldap.expression; \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/ldap/package-info.java b/src/main/java/com/avaje/ebeaninternal/server/ldap/package-info.java index 9388fe56a..63789ed19 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/ldap/package-info.java +++ b/src/main/java/com/avaje/ebeaninternal/server/ldap/package-info.java @@ -1,4 +1 @@ -/** - * LDAP query and persist implementation. - */ package com.avaje.ebeaninternal.server.ldap; \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/BackgroundRunnable.java b/src/main/java/com/avaje/ebeaninternal/server/lib/BackgroundRunnable.java index c59287589..cc861633a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/BackgroundRunnable.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/BackgroundRunnable.java @@ -1,164 +1,147 @@ -/** - * 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.lib; - -/** - * Wraps a Runnable that is registed with BackgroundThread. - * @see BackgroundThread - */ -public class BackgroundRunnable { - - /** - * The task to run. - */ - Runnable runnable; - - /** - * The frequency to run the task. - */ - int freqInSecs; - - /** - * The number of times the task has run. - */ - int runCount = 0; - - /** - * The total time taken to run the task. - */ - long totalRunTime = 0; - - /** - * The start time the task was started. - */ - long startTimeTemp; - - long startAfter; - - /** - * Used to disable/enable a task. - */ - boolean isActive = true; - - public BackgroundRunnable(Runnable runnable, int freqInSecs){ - this(runnable, freqInSecs, System.currentTimeMillis()+1000*(freqInSecs+10)); - } - - public BackgroundRunnable(Runnable runnable, int freqInSecs, long startAfter){ - this.runnable = runnable; - this.freqInSecs = freqInSecs; - this.startAfter = startAfter; - } - - /** - * Return true if this can be run now. - *

- * This is used to stop jobs firing immediately. - *

- */ - public boolean runNow(long now){ - return now > startAfter; - } - - /** - * Returns true if the task is currently enabled. - */ - public boolean isActive() { - return isActive; - } - - /** - * Set this to false to stop this task from running. - * Useful to temporarily disable a particular task. - */ - public void setActive(boolean isActive) { - this.isActive = isActive; - } - - /** - * Mark the start time of a task run. - */ - protected void runStart() { - startTimeTemp = System.currentTimeMillis(); - } - - /** - * Mark the end time of a task run. - */ - protected void runEnd(){ - runCount++; - long exeTime = System.currentTimeMillis() - startTimeTemp; - totalRunTime = totalRunTime + exeTime; - } - - /** - * Return the number of times this task was run. - */ - public int getRunCount() { - return runCount; - } - - /** - * Return the average time this task takes to run. - */ - public long getAverageRunTime() { - if (runCount == 0){ - return 0; - } - return totalRunTime/runCount; - } - - /** - * Return the frequency in seconds that this task runs. - */ - public int getFreqInSecs() { - return freqInSecs; - } - - /** - * Set the frequency in seconds that this task runs. - */ - public void setFreqInSecs(int freqInSecs) { - this.freqInSecs = freqInSecs; - } - - /** - * Return the underlying runnable. - */ - public Runnable getRunnable() { - return runnable; - } - - /** - * Set the underlying runnable. - */ - public void setRunnable(Runnable runnable) { - this.runnable = runnable; - } - - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append("["); - sb.append(runnable.getClass().getName()); - sb.append(" freq:").append(freqInSecs); - sb.append(" count:").append(getRunCount()); - sb.append(" avgTime:").append(getAverageRunTime()); - sb.append("]"); - return sb.toString(); - } -} +package com.avaje.ebeaninternal.server.lib; + +/** + * Wraps a Runnable that is registed with BackgroundThread. + * @see BackgroundThread + */ +public class BackgroundRunnable { + + /** + * The task to run. + */ + Runnable runnable; + + /** + * The frequency to run the task. + */ + int freqInSecs; + + /** + * The number of times the task has run. + */ + int runCount = 0; + + /** + * The total time taken to run the task. + */ + long totalRunTime = 0; + + /** + * The start time the task was started. + */ + long startTimeTemp; + + long startAfter; + + /** + * Used to disable/enable a task. + */ + boolean isActive = true; + + public BackgroundRunnable(Runnable runnable, int freqInSecs){ + this(runnable, freqInSecs, System.currentTimeMillis()+1000*(freqInSecs+10)); + } + + public BackgroundRunnable(Runnable runnable, int freqInSecs, long startAfter){ + this.runnable = runnable; + this.freqInSecs = freqInSecs; + this.startAfter = startAfter; + } + + /** + * Return true if this can be run now. + *

+ * This is used to stop jobs firing immediately. + *

+ */ + public boolean runNow(long now){ + return now > startAfter; + } + + /** + * Returns true if the task is currently enabled. + */ + public boolean isActive() { + return isActive; + } + + /** + * Set this to false to stop this task from running. + * Useful to temporarily disable a particular task. + */ + public void setActive(boolean isActive) { + this.isActive = isActive; + } + + /** + * Mark the start time of a task run. + */ + protected void runStart() { + startTimeTemp = System.currentTimeMillis(); + } + + /** + * Mark the end time of a task run. + */ + protected void runEnd(){ + runCount++; + long exeTime = System.currentTimeMillis() - startTimeTemp; + totalRunTime = totalRunTime + exeTime; + } + + /** + * Return the number of times this task was run. + */ + public int getRunCount() { + return runCount; + } + + /** + * Return the average time this task takes to run. + */ + public long getAverageRunTime() { + if (runCount == 0){ + return 0; + } + return totalRunTime/runCount; + } + + /** + * Return the frequency in seconds that this task runs. + */ + public int getFreqInSecs() { + return freqInSecs; + } + + /** + * Set the frequency in seconds that this task runs. + */ + public void setFreqInSecs(int freqInSecs) { + this.freqInSecs = freqInSecs; + } + + /** + * Return the underlying runnable. + */ + public Runnable getRunnable() { + return runnable; + } + + /** + * Set the underlying runnable. + */ + public void setRunnable(Runnable runnable) { + this.runnable = runnable; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("["); + sb.append(runnable.getClass().getName()); + sb.append(" freq:").append(freqInSecs); + sb.append(" count:").append(getRunCount()); + sb.append(" avgTime:").append(getAverageRunTime()); + sb.append("]"); + return sb.toString(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/BackgroundThread.java b/src/main/java/com/avaje/ebeaninternal/server/lib/BackgroundThread.java index 07283757b..e38f67516 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/BackgroundThread.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/BackgroundThread.java @@ -1,236 +1,219 @@ -/** - * 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.lib; - -import java.util.Iterator; -import java.util.Vector; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A general background thread that runs registered tasks periodically. - *

- * Several features such as CacheManager, DataSourceManager, ThreadPoolManager - * require tasks to be undertaken periodically. Instead of each having their own - * background thread they register runnables with this one. - *

- *

- * SystemProperties:
- * - *


- *   ## initially sleep for 5 seconds before starting 
- *   backgroundthread.initialsleep=5
- * 
- * - *

- * - * @see BackgroundRunnable - */ -public final class BackgroundThread { - - private static final Logger logger = Logger.getLogger(BackgroundThread.class.getName()); - - private static final BackgroundThread me = new BackgroundThread(); - - /** - * The list of Runnable tasks. - */ - private Vector list = new Vector(); - - /** - * Used to synchronize the list. - */ - private final Object monitor = new Object(); - - /** - * The underlying background thread. - */ - private final Thread thread; - - /** - * Wakes every second to look for tasks to run. - */ - private long sleepTime = 1000; - - /** - * The number of times a task is run. - */ - private long count; - - /** - * The time it takes to run the tasks. - */ - private long exeTime; - - /** - * Set when shutting down. - */ - private boolean stopped; - - /** - * Used to shutdown nicely. - */ - private Object threadMonitor = new Object(); - - private BackgroundThread() { - - thread = new Thread(new Runner(), "EbeanBackgroundThread"); - thread.setDaemon(true); - thread.start(); - } - - /** - * Register a Runnable to execute every freqInSecs seconds. - */ - public static void add(int freqInSecs, Runnable runnable) { - add(new BackgroundRunnable(runnable, freqInSecs)); - } - - /** - * Register a Runnable to execute every freqInSecs seconds. - */ - public static void add(BackgroundRunnable backgroundRunnable) { - me.addTask(backgroundRunnable); - } - - /** - * Stop the service. - */ - public static void shutdown() { - me.stop(); - } - - /** - * Return the registered BackgroundRunnable objects. - */ - public static Iterator runnables() { - synchronized (me.monitor) { - return me.list.iterator(); - } - } - - private void addTask(BackgroundRunnable backgroundRunnable) { - synchronized (monitor) { - list.add(backgroundRunnable); - } - } - - /** - * Stop the thread nicely. This will wait a maximum of 10 seconds for - * current work to be finished. - */ - private void stop() { - - stopped = true; - synchronized (threadMonitor) { - try { - threadMonitor.wait(10000); - } catch (InterruptedException e) { - ; - } - } - // thread = null; - } - - private class Runner implements Runnable { - - /** - * Run the registered tasks periodically. - */ - public void run() { - - if (ShutdownManager.isStopping()) { - return; - } - - while (!stopped) { - try { - - long actualSleep = sleepTime - exeTime; - if (actualSleep < 0) { - actualSleep = sleepTime; - } - Thread.sleep(actualSleep); - synchronized (monitor) { - runJobs(); - } - - } catch (InterruptedException e) { - logger.log(Level.SEVERE, null, e); - } - } - - // Tell Stop() we have shut ourselves down successfully - synchronized (threadMonitor) { - threadMonitor.notifyAll(); - } - } - - private void runJobs() { - - long startTime = System.currentTimeMillis(); - - // call trim on each cache - Iterator it = list.iterator(); - while (it.hasNext()) { - BackgroundRunnable bgr = (BackgroundRunnable) it.next(); - if (bgr.isActive()) { - - int freqInSecs = bgr.getFreqInSecs(); - - if (count % freqInSecs == 0) { - Runnable runable = bgr.getRunnable(); - if (bgr.runNow(startTime)){ - bgr.runStart(); - if (logger.isLoggable(Level.FINER)) { - String msg = count + " BGRunnable running [" - + runable.getClass().getName() + "]"; - logger.finer(msg); - } - - runable.run(); - bgr.runEnd(); - } - } - } - } - exeTime = System.currentTimeMillis() - startTime; - count++; - - if (count == 86400) { - // reset count back to zero every day - count = 0; - } - } - } - - public String toString() { - synchronized (monitor) { - StringBuffer sb = new StringBuffer(); - - Iterator it = runnables(); - while (it.hasNext()) { - BackgroundRunnable bgr = it.next(); - sb.append(bgr); - } - - return sb.toString(); - } - } - -} +package com.avaje.ebeaninternal.server.lib; + +import java.util.Iterator; +import java.util.Vector; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A general background thread that runs registered tasks periodically. + *

+ * Several features such as CacheManager, DataSourceManager, ThreadPoolManager + * require tasks to be undertaken periodically. Instead of each having their own + * background thread they register runnables with this one. + *

+ *

+ * SystemProperties:
+ * + *


+ *   ## initially sleep for 5 seconds before starting 
+ *   backgroundthread.initialsleep=5
+ * 
+ * + *

+ * + * @see BackgroundRunnable + */ +public final class BackgroundThread { + + private static final Logger logger = Logger.getLogger(BackgroundThread.class.getName()); + + private static final BackgroundThread me = new BackgroundThread(); + + /** + * The list of Runnable tasks. + */ + private Vector list = new Vector(); + + /** + * Used to synchronize the list. + */ + private final Object monitor = new Object(); + + /** + * The underlying background thread. + */ + private final Thread thread; + + /** + * Wakes every second to look for tasks to run. + */ + private long sleepTime = 1000; + + /** + * The number of times a task is run. + */ + private long count; + + /** + * The time it takes to run the tasks. + */ + private long exeTime; + + /** + * Set when shutting down. + */ + private boolean stopped; + + /** + * Used to shutdown nicely. + */ + private Object threadMonitor = new Object(); + + private BackgroundThread() { + + thread = new Thread(new Runner(), "EbeanBackgroundThread"); + thread.setDaemon(true); + thread.start(); + } + + /** + * Register a Runnable to execute every freqInSecs seconds. + */ + public static void add(int freqInSecs, Runnable runnable) { + add(new BackgroundRunnable(runnable, freqInSecs)); + } + + /** + * Register a Runnable to execute every freqInSecs seconds. + */ + public static void add(BackgroundRunnable backgroundRunnable) { + me.addTask(backgroundRunnable); + } + + /** + * Stop the service. + */ + public static void shutdown() { + me.stop(); + } + + /** + * Return the registered BackgroundRunnable objects. + */ + public static Iterator runnables() { + synchronized (me.monitor) { + return me.list.iterator(); + } + } + + private void addTask(BackgroundRunnable backgroundRunnable) { + synchronized (monitor) { + list.add(backgroundRunnable); + } + } + + /** + * Stop the thread nicely. This will wait a maximum of 10 seconds for + * current work to be finished. + */ + private void stop() { + + stopped = true; + synchronized (threadMonitor) { + try { + threadMonitor.wait(10000); + } catch (InterruptedException e) { + ; + } + } + // thread = null; + } + + private class Runner implements Runnable { + + /** + * Run the registered tasks periodically. + */ + public void run() { + + if (ShutdownManager.isStopping()) { + return; + } + + while (!stopped) { + try { + + long actualSleep = sleepTime - exeTime; + if (actualSleep < 0) { + actualSleep = sleepTime; + } + Thread.sleep(actualSleep); + synchronized (monitor) { + runJobs(); + } + + } catch (InterruptedException e) { + logger.log(Level.SEVERE, null, e); + } + } + + // Tell Stop() we have shut ourselves down successfully + synchronized (threadMonitor) { + threadMonitor.notifyAll(); + } + } + + private void runJobs() { + + long startTime = System.currentTimeMillis(); + + // call trim on each cache + Iterator it = list.iterator(); + while (it.hasNext()) { + BackgroundRunnable bgr = (BackgroundRunnable) it.next(); + if (bgr.isActive()) { + + int freqInSecs = bgr.getFreqInSecs(); + + if (count % freqInSecs == 0) { + Runnable runable = bgr.getRunnable(); + if (bgr.runNow(startTime)){ + bgr.runStart(); + if (logger.isLoggable(Level.FINER)) { + String msg = count + " BGRunnable running [" + + runable.getClass().getName() + "]"; + logger.finer(msg); + } + + runable.run(); + bgr.runEnd(); + } + } + } + } + exeTime = System.currentTimeMillis() - startTime; + count++; + + if (count == 86400) { + // reset count back to zero every day + count = 0; + } + } + } + + public String toString() { + synchronized (monitor) { + StringBuffer sb = new StringBuffer(); + + Iterator it = runnables(); + while (it.hasNext()) { + BackgroundRunnable bgr = it.next(); + sb.append(bgr); + } + + return sb.toString(); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonScheduleThreadPool.java b/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonScheduleThreadPool.java index 52b426420..b0f48f221 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonScheduleThreadPool.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonScheduleThreadPool.java @@ -1,97 +1,78 @@ -/** - * 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.lib; - - -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebeaninternal.api.Monitor; - -/** - * Daemon based ScheduleThreadPool. - *

- * Uses Daemon threads and hooks into shutdown event. - *

- * - * @author rbygrave - */ -public final class DaemonScheduleThreadPool extends ScheduledThreadPoolExecutor { - - private static final Logger logger = Logger.getLogger(DaemonScheduleThreadPool.class.getName()); - - private final Monitor monitor = new Monitor(); - - private int shutdownWaitSeconds; - - /** - * Construct the DaemonScheduleThreadPool. - */ - public DaemonScheduleThreadPool(int coreSize, int shutdownWaitSeconds, String namePrefix) { - super(coreSize, new DaemonThreadFactory(namePrefix)); - this.shutdownWaitSeconds = shutdownWaitSeconds; - - // we want to shutdown nicely when either the web application stops. - // Adding the JVM shutdown hook as a safety (and when not run in tomcat) - Runtime.getRuntime().addShutdownHook(new ShutdownHook()); - } - - /** - * Shutdown this thread pool nicely if possible. - *

- * This will wait a maximum of 20 seconds before terminating any threads - * still working. - *

- */ - public void shutdown() { - synchronized (monitor) { - if (super.isShutdown()) { - logger.fine("... DaemonScheduleThreadPool already shut down"); - return; - } - try { - logger.fine("DaemonScheduleThreadPool shutting down..."); - super.shutdown(); - if (!super.awaitTermination(shutdownWaitSeconds, TimeUnit.SECONDS)) { - logger.info("ScheduleService shut down timeout exceeded. Terminating running threads."); - super.shutdownNow(); - } - - } catch (Exception e) { - String msg = "Error during shutdown"; - logger.log(Level.SEVERE, msg, e); - e.printStackTrace(); - } - } - } - - /** - * Fired by the JVM Runtime shutdown. - */ - private class ShutdownHook extends Thread { - @Override - public void run() { - shutdown(); - } - }; -} - +package com.avaje.ebeaninternal.server.lib; + + +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebeaninternal.api.Monitor; + +/** + * Daemon based ScheduleThreadPool. + *

+ * Uses Daemon threads and hooks into shutdown event. + *

+ * + * @author rbygrave + */ +public final class DaemonScheduleThreadPool extends ScheduledThreadPoolExecutor { + + private static final Logger logger = Logger.getLogger(DaemonScheduleThreadPool.class.getName()); + + private final Monitor monitor = new Monitor(); + + private int shutdownWaitSeconds; + + /** + * Construct the DaemonScheduleThreadPool. + */ + public DaemonScheduleThreadPool(int coreSize, int shutdownWaitSeconds, String namePrefix) { + super(coreSize, new DaemonThreadFactory(namePrefix)); + this.shutdownWaitSeconds = shutdownWaitSeconds; + + // we want to shutdown nicely when either the web application stops. + // Adding the JVM shutdown hook as a safety (and when not run in tomcat) + Runtime.getRuntime().addShutdownHook(new ShutdownHook()); + } + + /** + * Shutdown this thread pool nicely if possible. + *

+ * This will wait a maximum of 20 seconds before terminating any threads + * still working. + *

+ */ + public void shutdown() { + synchronized (monitor) { + if (super.isShutdown()) { + logger.fine("... DaemonScheduleThreadPool already shut down"); + return; + } + try { + logger.fine("DaemonScheduleThreadPool shutting down..."); + super.shutdown(); + if (!super.awaitTermination(shutdownWaitSeconds, TimeUnit.SECONDS)) { + logger.info("ScheduleService shut down timeout exceeded. Terminating running threads."); + super.shutdownNow(); + } + + } catch (Exception e) { + String msg = "Error during shutdown"; + logger.log(Level.SEVERE, msg, e); + e.printStackTrace(); + } + } + } + + /** + * Fired by the JVM Runtime shutdown. + */ + private class ShutdownHook extends Thread { + @Override + public void run() { + shutdown(); + } + }; +} + diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadFactory.java b/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadFactory.java index 5f2684b32..d96ce19bc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadFactory.java @@ -1,68 +1,49 @@ -/** - * 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.lib; - - -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * ThreadFactory for Daemon threads. - *

- * Daemon threads do not stop a JVM stopping. If an application only has Daemon - * threads left it will shutdown. - *

- *

- * In using Daemon threads you need to either not care about being interrupted - * on shutdown or register with the JVM shutdown hook to perform a nice shutdown - * of the daemon threads etc. - *

- * - * @author rbygrave - */ -public class DaemonThreadFactory implements ThreadFactory { - - private static final AtomicInteger poolNumber = new AtomicInteger(1); - - private final ThreadGroup group; - - private final AtomicInteger threadNumber = new AtomicInteger(1); - - private final String namePrefix; - - public DaemonThreadFactory(String namePrefix) { - SecurityManager s = System.getSecurityManager(); - this.group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); - this.namePrefix = namePrefix != null ? namePrefix : "pool-" + poolNumber.getAndIncrement() + "-thread-"; - } - - public Thread newThread(Runnable r) { - - Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); - - t.setDaemon(true); - - if (t.getPriority() != Thread.NORM_PRIORITY) { - t.setPriority(Thread.NORM_PRIORITY); - } - - return t; - } +package com.avaje.ebeaninternal.server.lib; + + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * ThreadFactory for Daemon threads. + *

+ * Daemon threads do not stop a JVM stopping. If an application only has Daemon + * threads left it will shutdown. + *

+ *

+ * In using Daemon threads you need to either not care about being interrupted + * on shutdown or register with the JVM shutdown hook to perform a nice shutdown + * of the daemon threads etc. + *

+ * + * @author rbygrave + */ +public class DaemonThreadFactory implements ThreadFactory { + + private static final AtomicInteger poolNumber = new AtomicInteger(1); + + private final ThreadGroup group; + + private final AtomicInteger threadNumber = new AtomicInteger(1); + + private final String namePrefix; + + public DaemonThreadFactory(String namePrefix) { + SecurityManager s = System.getSecurityManager(); + this.group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); + this.namePrefix = namePrefix != null ? namePrefix : "pool-" + poolNumber.getAndIncrement() + "-thread-"; + } + + public Thread newThread(Runnable r) { + + Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); + + t.setDaemon(true); + + if (t.getPriority() != Thread.NORM_PRIORITY) { + t.setPriority(Thread.NORM_PRIORITY); + } + + return t; + } } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadPool.java b/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadPool.java index cd6cf8c6f..6dfa0da51 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadPool.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadPool.java @@ -1,105 +1,86 @@ -/** - * 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.lib; - - -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebeaninternal.api.Monitor; - -/** - * The Thread Pool based on Daemon threads. - * - * @author rbygrave - */ -public final class DaemonThreadPool extends ThreadPoolExecutor { - - private static final Logger logger = Logger.getLogger(DaemonThreadPool.class.getName()); - - private final Monitor monitor = new Monitor(); - - private final String namePrefix; - - private int shutdownWaitSeconds; - - /** - * Construct the DaemonThreadPool. - * - * @param coreSize - * 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 DaemonThreadPool(int coreSize, long keepAliveSecs, int shutdownWaitSeconds, String namePrefix) { - super(coreSize, coreSize, keepAliveSecs, TimeUnit.SECONDS, new LinkedBlockingQueue(), new DaemonThreadFactory(namePrefix)); - this.shutdownWaitSeconds = shutdownWaitSeconds; - this.namePrefix = namePrefix; - // we want to shutdown nicely when either the web application stops. - // Adding the JVM shutdown hook as a safety (and when not run in tomcat) - Runtime.getRuntime().addShutdownHook(new ShutdownHook()); - } - - /** - * Shutdown this thread pool nicely if possible. - *

- * This will wait a maximum of 20 seconds before terminating any threads - * still working. - *

- */ - public void shutdown() { - synchronized (monitor) { - if (super.isShutdown()) { - logger.fine("... DaemonThreadPool["+namePrefix+"] already shut down"); - return; - } - try { - logger.fine("DaemonThreadPool["+namePrefix+"] shutting down..."); - super.shutdown(); - if (!super.awaitTermination(shutdownWaitSeconds, TimeUnit.SECONDS)) { - logger.info("DaemonThreadPool["+namePrefix+"] shut down timeout exceeded. Terminating running threads."); - super.shutdownNow(); - } - - } catch (Exception e) { - String msg = "Error during shutdown of DaemonThreadPool["+namePrefix+"]"; - logger.log(Level.SEVERE, msg, e); - e.printStackTrace(); - } - } - } - - /** - * Fired by the JVM Runtime shutdown. - */ - private class ShutdownHook extends Thread { - @Override - public void run() { - shutdown(); - } - }; -} - +package com.avaje.ebeaninternal.server.lib; + + +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebeaninternal.api.Monitor; + +/** + * The Thread Pool based on Daemon threads. + * + * @author rbygrave + */ +public final class DaemonThreadPool extends ThreadPoolExecutor { + + private static final Logger logger = Logger.getLogger(DaemonThreadPool.class.getName()); + + private final Monitor monitor = new Monitor(); + + private final String namePrefix; + + private int shutdownWaitSeconds; + + /** + * Construct the DaemonThreadPool. + * + * @param coreSize + * 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 DaemonThreadPool(int coreSize, long keepAliveSecs, int shutdownWaitSeconds, String namePrefix) { + super(coreSize, coreSize, keepAliveSecs, TimeUnit.SECONDS, new LinkedBlockingQueue(), new DaemonThreadFactory(namePrefix)); + this.shutdownWaitSeconds = shutdownWaitSeconds; + this.namePrefix = namePrefix; + // we want to shutdown nicely when either the web application stops. + // Adding the JVM shutdown hook as a safety (and when not run in tomcat) + Runtime.getRuntime().addShutdownHook(new ShutdownHook()); + } + + /** + * Shutdown this thread pool nicely if possible. + *

+ * This will wait a maximum of 20 seconds before terminating any threads + * still working. + *

+ */ + public void shutdown() { + synchronized (monitor) { + if (super.isShutdown()) { + logger.fine("... DaemonThreadPool["+namePrefix+"] already shut down"); + return; + } + try { + logger.fine("DaemonThreadPool["+namePrefix+"] shutting down..."); + super.shutdown(); + if (!super.awaitTermination(shutdownWaitSeconds, TimeUnit.SECONDS)) { + logger.info("DaemonThreadPool["+namePrefix+"] shut down timeout exceeded. Terminating running threads."); + super.shutdownNow(); + } + + } catch (Exception e) { + String msg = "Error during shutdown of DaemonThreadPool["+namePrefix+"]"; + logger.log(Level.SEVERE, msg, e); + e.printStackTrace(); + } + } + } + + /** + * Fired by the JVM Runtime shutdown. + */ + private class ShutdownHook extends Thread { + @Override + public void run() { + shutdown(); + } + }; +} + diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownHook.java b/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownHook.java index b55f7ea32..d3a7e453e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownHook.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownHook.java @@ -1,50 +1,33 @@ -/** - * 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.lib; - - - -/** - * This is the ShutdownHook that gets added to Runtime. - * It will try to shutdown the system cleanly when the JVM exits. - * It is best to add your own shutdown hooks to StartStop. - * - */ -class ShutdownHook extends Thread { - - ShutdownHook() { - } - -// /** -// * Register this as a Shutdown hook with the Runtime. -// */ -// public static void registerWithRuntime() { -// -// ShutdownHook hook = new ShutdownHook(); -// Runtime.getRuntime().addShutdownHook(hook); -// } - - - /** - * Fired by the JVM Runtime on shutdown. - */ - public void run() { - ShutdownManager.shutdown(); - } - -}; +package com.avaje.ebeaninternal.server.lib; + + + +/** + * This is the ShutdownHook that gets added to Runtime. + * It will try to shutdown the system cleanly when the JVM exits. + * It is best to add your own shutdown hooks to StartStop. + * + */ +class ShutdownHook extends Thread { + + ShutdownHook() { + } + +// /** +// * Register this as a Shutdown hook with the Runtime. +// */ +// public static void registerWithRuntime() { +// +// ShutdownHook hook = new ShutdownHook(); +// Runtime.getRuntime().addShutdownHook(hook); +// } + + + /** + * Fired by the JVM Runtime on shutdown. + */ + public void run() { + ShutdownManager.shutdown(); + } + +}; diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownManager.java b/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownManager.java index e9824a512..9e667dca3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownManager.java @@ -1,232 +1,215 @@ -/** - * 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.lib; - -import java.sql.Driver; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.util.Enumeration; -import java.util.Vector; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebean.common.BootupEbeanManager; -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebeaninternal.api.ClassUtil; -import com.avaje.ebeaninternal.server.lib.sql.DataSourceGlobalManager; -import com.avaje.ebeaninternal.server.lib.thread.ThreadPoolManager; - -/** - * Manages the shutdown of the Runtime. - *

- * Makes sure all the resources are shutdown properly and in order. - *

- */ -public final class ShutdownManager { - - private static final Logger logger = Logger.getLogger(BackgroundThread.class.getName()); - - static final Vector runnables = new Vector(); - - static boolean stopping; - - static BootupEbeanManager serverFactory; - - static final ShutdownHook shutdownHook = new ShutdownHook(); - - static boolean whyShutdown; - - static { - // Register the Shutdown hook - register(); - whyShutdown = GlobalProperties.getBoolean("debug.shutdown.why",false); - } - - /** - * Disallow construction. - */ - private ShutdownManager() { - } - - public static void registerServerFactory(BootupEbeanManager factory){ - serverFactory = factory; - } - /** - * Make sure the ShutdownManager is activated. - */ - public static void touch() { - - } - - /** - * Return true if the system is in the process of stopping. - */ - public static boolean isStopping() { - synchronized (runnables) { - return stopping; - } - } - - /** - * Deregister the Shutdown hook. - *

- * For running in a Servlet Container a redeploy will cause a shutdown, and - * for that case we need to make sure the shutdown hook is deregistered. - *

- */ - private static void deregister() { - synchronized (runnables) { - try { - Runtime.getRuntime().removeShutdownHook(shutdownHook); - } catch (IllegalStateException ex) { - if (!ex.getMessage().equals("Shutdown in progress")) { - throw ex; - } - } - } - } - - /** - * Register the shutdown hook with the Runtime. - */ - private static void register() { - synchronized (runnables) { - try { - Runtime.getRuntime().addShutdownHook(shutdownHook); - } catch (IllegalStateException ex) { - if (!ex.getMessage().equals("Shutdown in progress")) { - throw ex; - } - } - } - } - - /** - * cleanup any resources as Runtime is stopping. - *

- *

    - *
  • Run the application specific shutdown runnable - *
  • Run any other registered shutdown runnable - *
  • Deregister from the cluster if required - *
  • Shutdown Thread pools - *
  • Shutdown any database connection pools - *
- *

- */ - public static void shutdown() { - synchronized (runnables) { - if (stopping) { - // Already run shutdown... - return; - } - - if (whyShutdown){ - try { - throw new RuntimeException("debug.shutdown.why=true ..."); - } catch(Throwable e){ - logger.log(Level.WARNING, "Stacktrace showing why shutdown was fired", e); - } - } - - stopping = true; - //logger.info("Stopping [" + SystemProperties.getContextName() + "]"); - - deregister(); - - // stop the BackgroundThread - BackgroundThread.shutdown(); - - String shutdownRunner = GlobalProperties.get("system.shutdown.runnable", null); - if (shutdownRunner != null) { - try { - Runnable r = (Runnable)ClassUtil.newInstance(shutdownRunner); - r.run(); - } catch (Exception e) { - logger.log(Level.SEVERE, null, e); - } - } - - // shutdown any registered runnable - - Enumeration e = runnables.elements(); - while (e.hasMoreElements()) { - try { - Runnable r = (Runnable) e.nextElement(); - r.run(); - } catch (Exception ex) { - logger.log(Level.SEVERE, null, ex); - ex.printStackTrace(); - } - } - try { - // shutdown order is important! - // CronManager is ok - - if (serverFactory != null){ - serverFactory.shutdown(); - } - - ThreadPoolManager.shutdown(); - - DataSourceGlobalManager.shutdown(); - - boolean dereg = GlobalProperties.getBoolean("datasource.deregisterAllDrivers", false); - if (dereg){ - deregisterAllJdbcDrivers(); - } - - } catch (Exception ex) { - String msg = "Shutdown Exception: "+ ex.getMessage(); - System.err.println(msg); - ex.printStackTrace(); - try { - logger.log(Level.SEVERE, null, ex); - } catch (Exception exc) { - String ms = "Error Logging error to the Log. It may be shutting down."; - System.err.println(ms); - exc.printStackTrace(); - } - } - } - } - - private static void deregisterAllJdbcDrivers() { - // This manually deregisters JDBC driver, which prevents Tomcat 7 from complaining about memory leaks wrto this class - Enumeration drivers = DriverManager.getDrivers(); - while (drivers.hasMoreElements()) { - Driver driver = drivers.nextElement(); - try { - DriverManager.deregisterDriver(driver); - logger.log(Level.INFO, String.format("Deregistering jdbc driver: %s", driver)); - } catch (SQLException e) { - logger.log(Level.SEVERE, String.format("Error deregistering driver %s", driver), e); - } - } - } - - /** - * Register a runnable to be executed when the system is shutdown. Note that - * runnables registered here are shutdown before any thread pools or - * DataSource pools are shutdown. - */ - public static void register(Runnable runnable) { - synchronized (runnables) { - runnables.add(runnable); - } - } -} +package com.avaje.ebeaninternal.server.lib; + +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Enumeration; +import java.util.Vector; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebean.common.BootupEbeanManager; +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebeaninternal.api.ClassUtil; +import com.avaje.ebeaninternal.server.lib.sql.DataSourceGlobalManager; +import com.avaje.ebeaninternal.server.lib.thread.ThreadPoolManager; + +/** + * Manages the shutdown of the Runtime. + *

+ * Makes sure all the resources are shutdown properly and in order. + *

+ */ +public final class ShutdownManager { + + private static final Logger logger = Logger.getLogger(BackgroundThread.class.getName()); + + static final Vector runnables = new Vector(); + + static boolean stopping; + + static BootupEbeanManager serverFactory; + + static final ShutdownHook shutdownHook = new ShutdownHook(); + + static boolean whyShutdown; + + static { + // Register the Shutdown hook + register(); + whyShutdown = GlobalProperties.getBoolean("debug.shutdown.why",false); + } + + /** + * Disallow construction. + */ + private ShutdownManager() { + } + + public static void registerServerFactory(BootupEbeanManager factory){ + serverFactory = factory; + } + /** + * Make sure the ShutdownManager is activated. + */ + public static void touch() { + + } + + /** + * Return true if the system is in the process of stopping. + */ + public static boolean isStopping() { + synchronized (runnables) { + return stopping; + } + } + + /** + * Deregister the Shutdown hook. + *

+ * For running in a Servlet Container a redeploy will cause a shutdown, and + * for that case we need to make sure the shutdown hook is deregistered. + *

+ */ + private static void deregister() { + synchronized (runnables) { + try { + Runtime.getRuntime().removeShutdownHook(shutdownHook); + } catch (IllegalStateException ex) { + if (!ex.getMessage().equals("Shutdown in progress")) { + throw ex; + } + } + } + } + + /** + * Register the shutdown hook with the Runtime. + */ + private static void register() { + synchronized (runnables) { + try { + Runtime.getRuntime().addShutdownHook(shutdownHook); + } catch (IllegalStateException ex) { + if (!ex.getMessage().equals("Shutdown in progress")) { + throw ex; + } + } + } + } + + /** + * cleanup any resources as Runtime is stopping. + *

+ *

    + *
  • Run the application specific shutdown runnable + *
  • Run any other registered shutdown runnable + *
  • Deregister from the cluster if required + *
  • Shutdown Thread pools + *
  • Shutdown any database connection pools + *
+ *

+ */ + public static void shutdown() { + synchronized (runnables) { + if (stopping) { + // Already run shutdown... + return; + } + + if (whyShutdown){ + try { + throw new RuntimeException("debug.shutdown.why=true ..."); + } catch(Throwable e){ + logger.log(Level.WARNING, "Stacktrace showing why shutdown was fired", e); + } + } + + stopping = true; + //logger.info("Stopping [" + SystemProperties.getContextName() + "]"); + + deregister(); + + // stop the BackgroundThread + BackgroundThread.shutdown(); + + String shutdownRunner = GlobalProperties.get("system.shutdown.runnable", null); + if (shutdownRunner != null) { + try { + Runnable r = (Runnable)ClassUtil.newInstance(shutdownRunner); + r.run(); + } catch (Exception e) { + logger.log(Level.SEVERE, null, e); + } + } + + // shutdown any registered runnable + + Enumeration e = runnables.elements(); + while (e.hasMoreElements()) { + try { + Runnable r = (Runnable) e.nextElement(); + r.run(); + } catch (Exception ex) { + logger.log(Level.SEVERE, null, ex); + ex.printStackTrace(); + } + } + try { + // shutdown order is important! + // CronManager is ok + + if (serverFactory != null){ + serverFactory.shutdown(); + } + + ThreadPoolManager.shutdown(); + + DataSourceGlobalManager.shutdown(); + + boolean dereg = GlobalProperties.getBoolean("datasource.deregisterAllDrivers", false); + if (dereg){ + deregisterAllJdbcDrivers(); + } + + } catch (Exception ex) { + String msg = "Shutdown Exception: "+ ex.getMessage(); + System.err.println(msg); + ex.printStackTrace(); + try { + logger.log(Level.SEVERE, null, ex); + } catch (Exception exc) { + String ms = "Error Logging error to the Log. It may be shutting down."; + System.err.println(ms); + exc.printStackTrace(); + } + } + } + } + + private static void deregisterAllJdbcDrivers() { + // This manually deregisters JDBC driver, which prevents Tomcat 7 from complaining about memory leaks wrto this class + Enumeration drivers = DriverManager.getDrivers(); + while (drivers.hasMoreElements()) { + Driver driver = drivers.nextElement(); + try { + DriverManager.deregisterDriver(driver); + logger.log(Level.INFO, String.format("Deregistering jdbc driver: %s", driver)); + } catch (SQLException e) { + logger.log(Level.SEVERE, String.format("Error deregistering driver %s", driver), e); + } + } + } + + /** + * Register a runnable to be executed when the system is shutdown. Note that + * runnables registered here are shutdown before any thread pools or + * DataSource pools are shutdown. + */ + public static void register(Runnable runnable) { + synchronized (runnables) { + runnables.add(runnable); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/DirectoryFinder.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/DirectoryFinder.java index dc2aa92ea..9fb489fc5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/DirectoryFinder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/DirectoryFinder.java @@ -1,124 +1,107 @@ -/** - * 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.lib.resource; - -import java.io.File; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Helper object used to find directories typically from the current working - * directory. - */ -public class DirectoryFinder { - - private static final Logger logger = Logger.getLogger(DirectoryFinder.class.getName()); - - /** - * Find a directory by search through subdirectories. - *

- * For example, used to find the WEB-INF directory starting from the current - * working directory. - *

- * - *
-	 * 
-	 * // search to a depth of 3 from the current working directory
-	 * // looking for a directory WEB-INF that contains the subdirectory
-	 * // data
-	 * 
-	 * File dir = DirectoryFinder.find(null, "WEB-INF/data", 3);
-	 * if (dir != null) {
-	 * 	//found the directory
-	 * }
-	 * 
- */ - public static File find(File startDir, String match, int maxDepth) { - - String matchSub = null; - int slashPos = match.indexOf('/'); - if (slashPos > -1) { - // match has sub directories - matchSub = match.substring(slashPos + 1); - match = match.substring(0, slashPos); - } - - // search for the directory - File found = find(startDir, match, matchSub, 0, maxDepth); - - if (found != null && matchSub != null) { - // match has sub directories - return new File(found, matchSub); - } - return found; - } - - private static File find(File dir, String match, String matchSub, int depth, int maxDepth) { - - if (dir == null) { - String curDir = System.getProperty("user.dir"); - dir = new File(curDir); - } - - if (dir.exists()) { - File[] list = dir.listFiles(); - if (list != null){ - for (int i = 0; i < list.length; i++) { - if (isMatch(list[i], match, matchSub)) { - return list[i]; - } - } - - // go through the directories again - // Aka *NOT* a depth first search - if (depth < maxDepth) { - for (int i = 0; i < list.length; i++) { - if (list[i].isDirectory()) { - File found = find(list[i], match, matchSub, depth + 1, maxDepth); - if (found != null) { - return found; - } - } - } - } - } - } - return null; - } - - private static boolean isMatch(File f, String match, String matchSub) { - if (f == null) { - return false; - } - if (!f.isDirectory()) { - return false; - } - if (!f.getName().equalsIgnoreCase(match)) { - return false; - } - if (matchSub == null) { - return true; - } - File sub = new File(f, matchSub); - if (logger.isLoggable(Level.FINEST)){ - logger.finest("search; " + f.getPath()); - } - return sub.exists(); - - } -} +package com.avaje.ebeaninternal.server.lib.resource; + +import java.io.File; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Helper object used to find directories typically from the current working + * directory. + */ +public class DirectoryFinder { + + private static final Logger logger = Logger.getLogger(DirectoryFinder.class.getName()); + + /** + * Find a directory by search through subdirectories. + *

+ * For example, used to find the WEB-INF directory starting from the current + * working directory. + *

+ * + *
+	 * 
+	 * // search to a depth of 3 from the current working directory
+	 * // looking for a directory WEB-INF that contains the subdirectory
+	 * // data
+	 * 
+	 * File dir = DirectoryFinder.find(null, "WEB-INF/data", 3);
+	 * if (dir != null) {
+	 * 	//found the directory
+	 * }
+	 * 
+ */ + public static File find(File startDir, String match, int maxDepth) { + + String matchSub = null; + int slashPos = match.indexOf('/'); + if (slashPos > -1) { + // match has sub directories + matchSub = match.substring(slashPos + 1); + match = match.substring(0, slashPos); + } + + // search for the directory + File found = find(startDir, match, matchSub, 0, maxDepth); + + if (found != null && matchSub != null) { + // match has sub directories + return new File(found, matchSub); + } + return found; + } + + private static File find(File dir, String match, String matchSub, int depth, int maxDepth) { + + if (dir == null) { + String curDir = System.getProperty("user.dir"); + dir = new File(curDir); + } + + if (dir.exists()) { + File[] list = dir.listFiles(); + if (list != null){ + for (int i = 0; i < list.length; i++) { + if (isMatch(list[i], match, matchSub)) { + return list[i]; + } + } + + // go through the directories again + // Aka *NOT* a depth first search + if (depth < maxDepth) { + for (int i = 0; i < list.length; i++) { + if (list[i].isDirectory()) { + File found = find(list[i], match, matchSub, depth + 1, maxDepth); + if (found != null) { + return found; + } + } + } + } + } + } + return null; + } + + private static boolean isMatch(File f, String match, String matchSub) { + if (f == null) { + return false; + } + if (!f.isDirectory()) { + return false; + } + if (!f.getName().equalsIgnoreCase(match)) { + return false; + } + if (matchSub == null) { + return true; + } + File sub = new File(f, matchSub); + if (logger.isLoggable(Level.FINEST)){ + logger.finest("search; " + f.getPath()); + } + return sub.exists(); + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceContent.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceContent.java index ad8554684..893603aff 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceContent.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceContent.java @@ -1,88 +1,71 @@ -/** - * 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.lib.resource; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Date; - -/** - * Content from a file system file. - */ -public class FileResourceContent implements ResourceContent { - - /** - * The underlying file. - */ - File file; - - String entryName; - - /** - * Create with a File and the entryName. - */ - public FileResourceContent(File file, String entryName) { - this.file = file; - this.entryName = entryName; - } - - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append("[").append(getName()); - sb.append("] size[").append(size()); - sb.append("] lastModified[").append(new Date(lastModified())); - sb.append("]"); - return sb.toString(); - } - - /** - * Returns the entry name which contains the path from the base directory. - *

- * This does not return the full path of the file, but the path relative to - * the FileIoSource directory. - *

- */ - public String getName() { - return entryName; - } - - /** - * Return the time the file was last modified. - */ - public long lastModified() { - return file.lastModified(); - } - - /** - * Return the size of the file. - */ - public long size() { - return file.length(); - } - - /** - * Return the input stream for this file. - */ - public InputStream getInputStream() throws IOException { - - FileInputStream is = new FileInputStream(file); - return is; - } -} +package com.avaje.ebeaninternal.server.lib.resource; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Date; + +/** + * Content from a file system file. + */ +public class FileResourceContent implements ResourceContent { + + /** + * The underlying file. + */ + File file; + + String entryName; + + /** + * Create with a File and the entryName. + */ + public FileResourceContent(File file, String entryName) { + this.file = file; + this.entryName = entryName; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("[").append(getName()); + sb.append("] size[").append(size()); + sb.append("] lastModified[").append(new Date(lastModified())); + sb.append("]"); + return sb.toString(); + } + + /** + * Returns the entry name which contains the path from the base directory. + *

+ * This does not return the full path of the file, but the path relative to + * the FileIoSource directory. + *

+ */ + public String getName() { + return entryName; + } + + /** + * Return the time the file was last modified. + */ + public long lastModified() { + return file.lastModified(); + } + + /** + * Return the size of the file. + */ + public long size() { + return file.length(); + } + + /** + * Return the input stream for this file. + */ + public InputStream getInputStream() throws IOException { + + FileInputStream is = new FileInputStream(file); + return is; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceSource.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceSource.java index 963936e38..1a9a9aeda 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceSource.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceSource.java @@ -1,68 +1,51 @@ -/** - * 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.lib.resource; - -import java.io.File; - -/** - * A file system directory represented as a FileSource. - */ -public class FileResourceSource extends AbstractResourceSource implements ResourceSource { - - /** - * The directory name. - */ - String directory; - - String baseDir; - - /** - * Create the source based on a directory name. - */ - public FileResourceSource(String directory){ - this.directory = directory; - this.baseDir = directory+File.separator; - } - - /** - * Create the source based on a directory file. - */ - public FileResourceSource(File dir){ - this(dir.getPath()); - } - - - public String getRealPath() { - return directory; - } - - /** - * Search for the given file and return as IoContent. - */ - public ResourceContent getContent(String entry) { - - String fullPath = baseDir+entry; - - File f = new File(fullPath); - if (f.exists()){ - FileResourceContent content = new FileResourceContent(f, entry); - return content; - } - return null; - } -} +package com.avaje.ebeaninternal.server.lib.resource; + +import java.io.File; + +/** + * A file system directory represented as a FileSource. + */ +public class FileResourceSource extends AbstractResourceSource implements ResourceSource { + + /** + * The directory name. + */ + String directory; + + String baseDir; + + /** + * Create the source based on a directory name. + */ + public FileResourceSource(String directory){ + this.directory = directory; + this.baseDir = directory+File.separator; + } + + /** + * Create the source based on a directory file. + */ + public FileResourceSource(File dir){ + this(dir.getPath()); + } + + + public String getRealPath() { + return directory; + } + + /** + * Search for the given file and return as IoContent. + */ + public ResourceContent getContent(String entry) { + + String fullPath = baseDir+entry; + + File f = new File(fullPath); + if (f.exists()){ + FileResourceContent content = new FileResourceContent(f, entry); + return content; + } + return null; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceContent.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceContent.java index 8343ff3b5..42b6811e6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceContent.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceContent.java @@ -1,51 +1,34 @@ -/** - * 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.lib.resource; - -import java.io.IOException; -import java.io.InputStream; - -/** - * Represents content that can be read via the ResourceManager. - *

- * Typically either content from a File or a URL. - *

- */ -public interface ResourceContent { - - /** - * The name of the content. - */ - public String getName(); - - /** - * The size of the content in bytes. - */ - public long size(); - - /** - * The last modified timestamp of the content. - */ - public long lastModified(); - - /** - * The content itself. - */ - public InputStream getInputStream() throws IOException; - -} +package com.avaje.ebeaninternal.server.lib.resource; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Represents content that can be read via the ResourceManager. + *

+ * Typically either content from a File or a URL. + *

+ */ +public interface ResourceContent { + + /** + * The name of the content. + */ + public String getName(); + + /** + * The size of the content in bytes. + */ + public long size(); + + /** + * The last modified timestamp of the content. + */ + public long lastModified(); + + /** + * The content itself. + */ + public InputStream getInputStream() throws IOException; + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceSource.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceSource.java index 5630270a4..c0cca33f4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceSource.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceSource.java @@ -1,54 +1,37 @@ -/** - * 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.lib.resource; - -import java.io.IOException; - -/** - * A Source for ResourceManager. - *

- * Typically a File System Directory based source or a ServletContext URL - * resource based source (for Servlet WAR files). - *

- */ -public interface ResourceSource { - - /** - * Return the File System path of the root of the ResourceSource. - *

- * This will return null IF the ResourceSource is an unpacked WAR file. - *

- */ - public String getRealPath(); - - /** - * Find the content with a given entry name. This will return null if no - * matching content was found. - */ - public ResourceContent getContent(String entry); - - /** - * Return the content as a String. - */ - public String readString(ResourceContent content, int bufSize) throws IOException; - - /** - * Return the content as a byte[]. - */ - public byte[] readBytes(ResourceContent content, int bufSize) throws IOException; -} +package com.avaje.ebeaninternal.server.lib.resource; + +import java.io.IOException; + +/** + * A Source for ResourceManager. + *

+ * Typically a File System Directory based source or a ServletContext URL + * resource based source (for Servlet WAR files). + *

+ */ +public interface ResourceSource { + + /** + * Return the File System path of the root of the ResourceSource. + *

+ * This will return null IF the ResourceSource is an unpacked WAR file. + *

+ */ + public String getRealPath(); + + /** + * Find the content with a given entry name. This will return null if no + * matching content was found. + */ + public ResourceContent getContent(String entry); + + /** + * Return the content as a String. + */ + public String readString(ResourceContent content, int bufSize) throws IOException; + + /** + * Return the content as a byte[]. + */ + public byte[] readBytes(ResourceContent content, int bufSize) throws IOException; +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceContent.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceContent.java index ad23d32ad..0dac4c904 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceContent.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceContent.java @@ -1,94 +1,77 @@ -/** - * 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.lib.resource; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; -import java.util.Date; - -/** - * Content from a URL Resource. - */ -public class UrlResourceContent implements ResourceContent { - - /** - * The underlying resource. - */ - //URL url; - - String entryName; - - URLConnection con; - - /** - * Create with a File and the entryName. - */ - public UrlResourceContent(URL url, String entryName) { - //this.url = url; - this.entryName = entryName; - try { - con = url.openConnection(); - } catch (IOException ex){ - throw new RuntimeException(ex); - } - } - - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append("[").append(getName()); - sb.append("] size[").append(size()); - sb.append("] lastModified[").append(new Date(lastModified())); - sb.append("]"); - return sb.toString(); - } - - /** - * Returns the entry name which contains the path from the base directory. - *

- * This does not return the full path of the file, but the path relative to - * the FileIoSource directory. - *

- */ - public String getName() { - return entryName; - } - - /** - * Return the time the file was last modified. - */ - public long lastModified() { - return con.getLastModified(); - } - - /** - * Return the size of the file. - */ - public long size() { - return con.getContentLength(); - } - - /** - * Return the input stream for this file. - */ - public InputStream getInputStream() throws IOException { - - return con.getInputStream(); - } -} +package com.avaje.ebeaninternal.server.lib.resource; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Date; + +/** + * Content from a URL Resource. + */ +public class UrlResourceContent implements ResourceContent { + + /** + * The underlying resource. + */ + //URL url; + + String entryName; + + URLConnection con; + + /** + * Create with a File and the entryName. + */ + public UrlResourceContent(URL url, String entryName) { + //this.url = url; + this.entryName = entryName; + try { + con = url.openConnection(); + } catch (IOException ex){ + throw new RuntimeException(ex); + } + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("[").append(getName()); + sb.append("] size[").append(size()); + sb.append("] lastModified[").append(new Date(lastModified())); + sb.append("]"); + return sb.toString(); + } + + /** + * Returns the entry name which contains the path from the base directory. + *

+ * This does not return the full path of the file, but the path relative to + * the FileIoSource directory. + *

+ */ + public String getName() { + return entryName; + } + + /** + * Return the time the file was last modified. + */ + public long lastModified() { + return con.getLastModified(); + } + + /** + * Return the size of the file. + */ + public long size() { + return con.getContentLength(); + } + + /** + * Return the input stream for this file. + */ + public InputStream getInputStream() throws IOException { + + return con.getInputStream(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceSource.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceSource.java index 9903a7b09..5be64a80b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceSource.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceSource.java @@ -1,81 +1,64 @@ -/** - * 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.lib.resource; - -import java.net.MalformedURLException; -import java.net.URL; - -import javax.servlet.ServletContext; - -import com.avaje.ebeaninternal.server.lib.util.GeneralException; - -/** - * A file system directory represented as a FileSource. - */ -public class UrlResourceSource extends AbstractResourceSource implements ResourceSource { - - - ServletContext sc; - - String basePath; - - String realPath; - - /** - * Create the source based on a directory name. - */ - public UrlResourceSource(ServletContext sc, String basePath){ - this.sc = sc; - if (basePath == null){ - this.basePath = "/"; - } else { - this.basePath = "/"+basePath+"/"; - } - this.realPath = sc.getRealPath(basePath); - } - - /** - * Returns the "real path" from the ServletContext root. - *

- * This can be null for unpacked WAR deployment. - *

- */ - public String getRealPath() { - return realPath; - } - - /** - * Search for the given URL resource and return as ResourceContent. - *

- * Returns null if the resource is not found. - *

- */ - public ResourceContent getContent(String entry) { - - try { - URL url = sc.getResource(basePath+entry); - if (url != null){ - return new UrlResourceContent(url, entry); - } - return null; - - } catch (MalformedURLException ex){ - throw new GeneralException(ex); - } - } -} +package com.avaje.ebeaninternal.server.lib.resource; + +import java.net.MalformedURLException; +import java.net.URL; + +import javax.servlet.ServletContext; + +import com.avaje.ebeaninternal.server.lib.util.GeneralException; + +/** + * A file system directory represented as a FileSource. + */ +public class UrlResourceSource extends AbstractResourceSource implements ResourceSource { + + + ServletContext sc; + + String basePath; + + String realPath; + + /** + * Create the source based on a directory name. + */ + public UrlResourceSource(ServletContext sc, String basePath){ + this.sc = sc; + if (basePath == null){ + this.basePath = "/"; + } else { + this.basePath = "/"+basePath+"/"; + } + this.realPath = sc.getRealPath(basePath); + } + + /** + * Returns the "real path" from the ServletContext root. + *

+ * This can be null for unpacked WAR deployment. + *

+ */ + public String getRealPath() { + return realPath; + } + + /** + * Search for the given URL resource and return as ResourceContent. + *

+ * Returns null if the resource is not found. + *

+ */ + public ResourceContent getContent(String entry) { + + try { + URL url = sc.getResource(basePath+entry); + if (url != null){ + return new UrlResourceContent(url, entry); + } + return null; + + } catch (MalformedURLException ex){ + throw new GeneralException(ex); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/BusyConnectionBuffer.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/BusyConnectionBuffer.java index 36b77a9ac..dc5a314c2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/BusyConnectionBuffer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/BusyConnectionBuffer.java @@ -1,155 +1,136 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.lib.sql; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * A buffer especially designed for Busy PooledConnections. - *

- * All thread safety controlled externally (by PooledConnectionQueue). - *

- *

- * It has a set of 'slots' and PooledConnections know which slot they went into - * and this allows for fast addition and removal (by slotId without looping). - * The capacity will increase on demand by the 'growBy' amount. - *

- * - * @author rbygrave - * - */ -class BusyConnectionBuffer { - - private PooledConnection[] slots; - - private int growBy; - - private int size; - - private int pos = -1; - - /** - * Create the buffer with an initial capacity and fixed growBy. - * We generally do not want the buffer to grow very often. - * - * @param capacity - * the initial capacity - * @param growBy - * the fixed amount to grow the buffer by. - */ - protected BusyConnectionBuffer(int capacity, int growBy) { - this.slots = new PooledConnection[capacity]; - this.growBy = growBy; - } - - /** - * We can only grow (not shrink) the capacity. - */ - private void setCapacity(int newCapacity) { - if (newCapacity > slots.length){ - PooledConnection[] current = this.slots; - this.slots = new PooledConnection[newCapacity]; - System.arraycopy(current, 0, this.slots, 0, current.length); - } - } - - public String toString() { - return Arrays.toString(slots); - } - - protected int getCapacity() { - return slots.length; - } - - protected int size(){ - return size; - } - - protected boolean isEmpty(){ - return size == 0; - } - - protected int add(PooledConnection pc){ - if (size == slots.length){ - // grow the capacity - setCapacity(slots.length + growBy); - } - ++size; - int slot = nextEmptySlot(); - pc.setSlotId(slot); - slots[slot] = pc; - return size; - } - - protected boolean remove(PooledConnection pc) { - --size; - int slotId = pc.getSlotId(); - if (slots[slotId] != pc){ - return false; - } - slots[slotId] = null; - return true; - } - - - /** - * Get a shallow read only List of the busy connections. - *

- * Note that the {@link #remove(PooledConnection)} MUST be used to remove PooledConnections. - *

- * @return - */ - protected List getShallowCopy() { - ArrayList tmp = new ArrayList(); - for (int i = 0; i < slots.length; i++) { - if (slots[i] != null){ - tmp.add(slots[i]); - } - } - return Collections.unmodifiableList(tmp); - } - - /** - * Return the position of the next empty slot. - */ - private int nextEmptySlot() { - - // search forward - while(++pos < slots.length) { - if (slots[pos] == null){ - return pos; - } - } - // search from beginning - pos = -1; - while(++pos < slots.length) { - if (slots[pos] == null){ - return pos; - } - } - - // not expecting this - throw new RuntimeException("No Empty Slot Found?"); - } - -} +package com.avaje.ebeaninternal.server.lib.sql; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * A buffer especially designed for Busy PooledConnections. + *

+ * All thread safety controlled externally (by PooledConnectionQueue). + *

+ *

+ * It has a set of 'slots' and PooledConnections know which slot they went into + * and this allows for fast addition and removal (by slotId without looping). + * The capacity will increase on demand by the 'growBy' amount. + *

+ * + * @author rbygrave + * + */ +class BusyConnectionBuffer { + + private PooledConnection[] slots; + + private int growBy; + + private int size; + + private int pos = -1; + + /** + * Create the buffer with an initial capacity and fixed growBy. + * We generally do not want the buffer to grow very often. + * + * @param capacity + * the initial capacity + * @param growBy + * the fixed amount to grow the buffer by. + */ + protected BusyConnectionBuffer(int capacity, int growBy) { + this.slots = new PooledConnection[capacity]; + this.growBy = growBy; + } + + /** + * We can only grow (not shrink) the capacity. + */ + private void setCapacity(int newCapacity) { + if (newCapacity > slots.length){ + PooledConnection[] current = this.slots; + this.slots = new PooledConnection[newCapacity]; + System.arraycopy(current, 0, this.slots, 0, current.length); + } + } + + public String toString() { + return Arrays.toString(slots); + } + + protected int getCapacity() { + return slots.length; + } + + protected int size(){ + return size; + } + + protected boolean isEmpty(){ + return size == 0; + } + + protected int add(PooledConnection pc){ + if (size == slots.length){ + // grow the capacity + setCapacity(slots.length + growBy); + } + ++size; + int slot = nextEmptySlot(); + pc.setSlotId(slot); + slots[slot] = pc; + return size; + } + + protected boolean remove(PooledConnection pc) { + --size; + int slotId = pc.getSlotId(); + if (slots[slotId] != pc){ + return false; + } + slots[slotId] = null; + return true; + } + + + /** + * Get a shallow read only List of the busy connections. + *

+ * Note that the {@link #remove(PooledConnection)} MUST be used to remove PooledConnections. + *

+ * @return + */ + protected List getShallowCopy() { + ArrayList tmp = new ArrayList(); + for (int i = 0; i < slots.length; i++) { + if (slots[i] != null){ + tmp.add(slots[i]); + } + } + return Collections.unmodifiableList(tmp); + } + + /** + * Return the position of the next empty slot. + */ + private int nextEmptySlot() { + + // search forward + while(++pos < slots.length) { + if (slots[pos] == null){ + return pos; + } + } + // search from beginning + pos = -1; + while(++pos < slots.length) { + if (slots[pos] == null){ + return pos; + } + } + + // not expecting this + throw new RuntimeException("No Empty Slot Found?"); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceAlertListener.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceAlertListener.java index ea64e9026..97655000c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceAlertListener.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceAlertListener.java @@ -1,43 +1,26 @@ -/** - * 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.lib.sql; - - -/** - * Listens for alerting events such as DataSource down. - */ -public interface DataSourceAlertListener { - - /** - * Send an Alert saying the dataSource is down. - */ - public void dataSourceDown(String dataSourceName); - - /** - * Send an Alert saying the dataSource is back up. - */ - public void dataSourceUp(String dataSourceName); - - /** - * Send an Alert saying the dataSource has reached a high - * number of connections. - */ - public void warning(String subject, String msg); - - -} +package com.avaje.ebeaninternal.server.lib.sql; + + +/** + * Listens for alerting events such as DataSource down. + */ +public interface DataSourceAlertListener { + + /** + * Send an Alert saying the dataSource is down. + */ + public void dataSourceDown(String dataSourceName); + + /** + * Send an Alert saying the dataSource is back up. + */ + public void dataSourceUp(String dataSourceName); + + /** + * Send an Alert saying the dataSource has reached a high + * number of connections. + */ + public void warning(String subject, String msg); + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceException.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceException.java index f5a6ead63..9dc198aaf 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceException.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceException.java @@ -1,41 +1,24 @@ -/** - * 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.lib.sql; - - - -/** - * A general DataSource exception. - */ -public class DataSourceException extends RuntimeException -{ - static final long serialVersionUID = 7061559938704539844L; - - public DataSourceException(Exception cause) { - super(cause); - } - - public DataSourceException(String s, Exception cause) { - super(s, cause); - } - - public DataSourceException(String s) { - super(s); - } - -} +package com.avaje.ebeaninternal.server.lib.sql; + + + +/** + * A general DataSource exception. + */ +public class DataSourceException extends RuntimeException +{ + static final long serialVersionUID = 7061559938704539844L; + + public DataSourceException(Exception cause) { + super(cause); + } + + public DataSourceException(String s, Exception cause) { + super(s, cause); + } + + public DataSourceException(String s) { + super(s); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceGlobalManager.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceGlobalManager.java index eceff6ccb..df84f3e85 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceGlobalManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceGlobalManager.java @@ -1,67 +1,50 @@ -/** - * 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.lib.sql; - -import java.util.List; - -import com.avaje.ebean.config.DataSourceConfig; - -/** - * Manages access to named DataSources using singleton scope. - */ -public final class DataSourceGlobalManager { - - private static final DataSourceManager manager = new DataSourceManager(); - - private DataSourceGlobalManager() { - } - - /** - * Return true when the dataSource is shutting down. - */ - public static boolean isShuttingDown() { - return manager.isShuttingDown(); - } - - /** - * Shutdown the dataSources. - */ - public static void shutdown() { - manager.shutdown(); - } - - /** - * Return the list of DataSourcePool's. - */ - public static List getPools() { - return manager.getPools(); - } - - /** - * Return a DataSource pool by its name. - */ - public static DataSourcePool getDataSource(String name) { - return manager.getDataSource(name); - } - - public static DataSourcePool getDataSource(String name, DataSourceConfig dsConfig) { - return manager.getDataSource(name, dsConfig); - } - - -} +package com.avaje.ebeaninternal.server.lib.sql; + +import java.util.List; + +import com.avaje.ebean.config.DataSourceConfig; + +/** + * Manages access to named DataSources using singleton scope. + */ +public final class DataSourceGlobalManager { + + private static final DataSourceManager manager = new DataSourceManager(); + + private DataSourceGlobalManager() { + } + + /** + * Return true when the dataSource is shutting down. + */ + public static boolean isShuttingDown() { + return manager.isShuttingDown(); + } + + /** + * Shutdown the dataSources. + */ + public static void shutdown() { + manager.shutdown(); + } + + /** + * Return the list of DataSourcePool's. + */ + public static List getPools() { + return manager.getPools(); + } + + /** + * Return a DataSource pool by its name. + */ + public static DataSourcePool getDataSource(String name) { + return manager.getDataSource(name); + } + + public static DataSourcePool getDataSource(String name, DataSourceConfig dsConfig) { + return manager.getDataSource(name, dsConfig); + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceManager.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceManager.java index 8c6b55608..37f490313 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceManager.java @@ -1,250 +1,233 @@ -/** - * 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.lib.sql; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebean.config.DataSourceConfig; -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebeaninternal.api.ClassUtil; -import com.avaje.ebeaninternal.server.lib.BackgroundRunnable; -import com.avaje.ebeaninternal.server.lib.BackgroundThread; - - -/** - * Manages access to named DataSources. - */ -public class DataSourceManager implements DataSourceNotify { - - private static final Logger logger = Logger.getLogger(DataSourceManager.class.getName()); - - /** - * An alerter that notifies when the database has problems. - */ - private final DataSourceAlertListener alertlistener; - - /** - * Cache of the named DataSources. - */ - private final Hashtable dsMap = new Hashtable(); - - /** - * Monitor for creating dataSources. - */ - private final Object monitor = new Object(); - - /** - * The database checker registered with BackgroundThread. - */ - private final BackgroundRunnable dbChecker; - - /** - * The frequency to test db while it is up. - */ - private final int dbUpFreqInSecs; - - /** - * The frequency to test db while it is down. - */ - private final int dbDownFreqInSecs; - - /** - * Set to true when shutting down. - */ - private boolean shuttingDown; - - private boolean deregisterDriver; - - /** - * Construct with explicit ConfigProperties. - */ - public DataSourceManager() { - - this.alertlistener = createAlertListener(); - - // perform heart beat every 30 seconds by default - this.dbUpFreqInSecs = GlobalProperties.getInt("datasource.heartbeatfreq",30); - this.dbDownFreqInSecs = GlobalProperties.getInt("datasource.deadbeatfreq",10); - this.dbChecker = new BackgroundRunnable(new Checker(), dbUpFreqInSecs); - this.deregisterDriver = GlobalProperties.getBoolean("datasource.deregisterDriver", true); - - try { - BackgroundThread.add(dbChecker); - - } catch (Exception e) { - logger.log(Level.SEVERE, null, e); - } - } - - private DataSourceAlertListener createAlertListener() throws DataSourceException { - - String alertCN = GlobalProperties.get("datasource.alert.class", null); - if (alertCN == null){ - return new SimpleAlerter(); - - } else { - try { - return (DataSourceAlertListener)ClassUtil.newInstance(alertCN, this.getClass()); - - } catch (Exception ex){ - throw new DataSourceException(ex); - } - } - } - - /** - * Send an alert to say the dataSource is back up. - */ - public void notifyDataSourceUp(String dataSourceName){ - - dbChecker.setFreqInSecs(dbUpFreqInSecs); - - if (alertlistener != null){ - alertlistener.dataSourceUp(dataSourceName); - } - } - - /** - * Send an alert to say the dataSource is down. - */ - public void notifyDataSourceDown(String dataSourceName){ - - dbChecker.setFreqInSecs(dbDownFreqInSecs); - - if (alertlistener != null){ - alertlistener.dataSourceDown(dataSourceName); - } - } - - /** - * Send an alert to say the dataSource is getting close to its max size. - */ - public void notifyWarning(String subject, String msg){ - if (alertlistener != null){ - alertlistener.warning(subject, msg); - } - } - - /** - * Return true when the dataSource is shutting down. - */ - public boolean isShuttingDown() { - synchronized(monitor) { - return shuttingDown; - } - } - - /** - * Shutdown the dataSources. - */ - public void shutdown() { - - synchronized(monitor) { - - this.shuttingDown = true; - - Collection values = dsMap.values(); - for (DataSourcePool ds : values) { - try { - ds.shutdown(); - } catch (DataSourceException e) { - // should never be thrown as the DataSources are all created... - logger.log(Level.SEVERE, null, e); - } - } - if (deregisterDriver){ - for (DataSourcePool ds : values) { - ds.deregisterDriver(); - } - } - } - } - - /** - * Return the DataSourcePool's. - */ - public List getPools() { - synchronized(monitor) { - // create a copy of the DataSourcePool's - ArrayList list = new ArrayList(); - list.addAll(dsMap.values()); - return list; - } - } - - /** - * Get the dataSource using the default ConfigProperties. - */ - public DataSourcePool getDataSource(String name) { - return getDataSource(name, null); - } - - - public DataSourcePool getDataSource(String name, DataSourceConfig dsConfig){ - - if (name == null){ - throw new IllegalArgumentException("name not defined"); - } - - synchronized(monitor){ - DataSourcePool pool = dsMap.get(name); - if (pool == null){ - if (dsConfig == null){ - dsConfig = new DataSourceConfig(); - dsConfig.loadSettings(name); - } - pool = new DataSourcePool(this, name, dsConfig); - dsMap.put(name, pool); - } - return pool; - } - } - - /** - * Check that the database is up by performing a simple query. This should - * be done periodically. By default every 30 seconds. - */ - private void checkDataSource() { - - synchronized (monitor) { - if (!isShuttingDown()) { - Iterator it = dsMap.values().iterator(); - while (it.hasNext()) { - DataSourcePool ds = it.next(); - ds.checkDataSource(); - } - } - } - } - - /** - * Runs every dbUpFreqInSecs to make sure dataSource is up. - */ - private final class Checker implements Runnable { - - public void run() { - checkDataSource(); - } - } -} +package com.avaje.ebeaninternal.server.lib.sql; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebean.config.DataSourceConfig; +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebeaninternal.api.ClassUtil; +import com.avaje.ebeaninternal.server.lib.BackgroundRunnable; +import com.avaje.ebeaninternal.server.lib.BackgroundThread; + + +/** + * Manages access to named DataSources. + */ +public class DataSourceManager implements DataSourceNotify { + + private static final Logger logger = Logger.getLogger(DataSourceManager.class.getName()); + + /** + * An alerter that notifies when the database has problems. + */ + private final DataSourceAlertListener alertlistener; + + /** + * Cache of the named DataSources. + */ + private final Hashtable dsMap = new Hashtable(); + + /** + * Monitor for creating dataSources. + */ + private final Object monitor = new Object(); + + /** + * The database checker registered with BackgroundThread. + */ + private final BackgroundRunnable dbChecker; + + /** + * The frequency to test db while it is up. + */ + private final int dbUpFreqInSecs; + + /** + * The frequency to test db while it is down. + */ + private final int dbDownFreqInSecs; + + /** + * Set to true when shutting down. + */ + private boolean shuttingDown; + + private boolean deregisterDriver; + + /** + * Construct with explicit ConfigProperties. + */ + public DataSourceManager() { + + this.alertlistener = createAlertListener(); + + // perform heart beat every 30 seconds by default + this.dbUpFreqInSecs = GlobalProperties.getInt("datasource.heartbeatfreq",30); + this.dbDownFreqInSecs = GlobalProperties.getInt("datasource.deadbeatfreq",10); + this.dbChecker = new BackgroundRunnable(new Checker(), dbUpFreqInSecs); + this.deregisterDriver = GlobalProperties.getBoolean("datasource.deregisterDriver", true); + + try { + BackgroundThread.add(dbChecker); + + } catch (Exception e) { + logger.log(Level.SEVERE, null, e); + } + } + + private DataSourceAlertListener createAlertListener() throws DataSourceException { + + String alertCN = GlobalProperties.get("datasource.alert.class", null); + if (alertCN == null){ + return new SimpleAlerter(); + + } else { + try { + return (DataSourceAlertListener)ClassUtil.newInstance(alertCN, this.getClass()); + + } catch (Exception ex){ + throw new DataSourceException(ex); + } + } + } + + /** + * Send an alert to say the dataSource is back up. + */ + public void notifyDataSourceUp(String dataSourceName){ + + dbChecker.setFreqInSecs(dbUpFreqInSecs); + + if (alertlistener != null){ + alertlistener.dataSourceUp(dataSourceName); + } + } + + /** + * Send an alert to say the dataSource is down. + */ + public void notifyDataSourceDown(String dataSourceName){ + + dbChecker.setFreqInSecs(dbDownFreqInSecs); + + if (alertlistener != null){ + alertlistener.dataSourceDown(dataSourceName); + } + } + + /** + * Send an alert to say the dataSource is getting close to its max size. + */ + public void notifyWarning(String subject, String msg){ + if (alertlistener != null){ + alertlistener.warning(subject, msg); + } + } + + /** + * Return true when the dataSource is shutting down. + */ + public boolean isShuttingDown() { + synchronized(monitor) { + return shuttingDown; + } + } + + /** + * Shutdown the dataSources. + */ + public void shutdown() { + + synchronized(monitor) { + + this.shuttingDown = true; + + Collection values = dsMap.values(); + for (DataSourcePool ds : values) { + try { + ds.shutdown(); + } catch (DataSourceException e) { + // should never be thrown as the DataSources are all created... + logger.log(Level.SEVERE, null, e); + } + } + if (deregisterDriver){ + for (DataSourcePool ds : values) { + ds.deregisterDriver(); + } + } + } + } + + /** + * Return the DataSourcePool's. + */ + public List getPools() { + synchronized(monitor) { + // create a copy of the DataSourcePool's + ArrayList list = new ArrayList(); + list.addAll(dsMap.values()); + return list; + } + } + + /** + * Get the dataSource using the default ConfigProperties. + */ + public DataSourcePool getDataSource(String name) { + return getDataSource(name, null); + } + + + public DataSourcePool getDataSource(String name, DataSourceConfig dsConfig){ + + if (name == null){ + throw new IllegalArgumentException("name not defined"); + } + + synchronized(monitor){ + DataSourcePool pool = dsMap.get(name); + if (pool == null){ + if (dsConfig == null){ + dsConfig = new DataSourceConfig(); + dsConfig.loadSettings(name); + } + pool = new DataSourcePool(this, name, dsConfig); + dsMap.put(name, pool); + } + return pool; + } + } + + /** + * Check that the database is up by performing a simple query. This should + * be done periodically. By default every 30 seconds. + */ + private void checkDataSource() { + + synchronized (monitor) { + if (!isShuttingDown()) { + Iterator it = dsMap.values().iterator(); + while (it.hasNext()) { + DataSourcePool ds = it.next(); + ds.checkDataSource(); + } + } + } + } + + /** + * Runs every dbUpFreqInSecs to make sure dataSource is up. + */ + private final class Checker implements Runnable { + + public void run() { + checkDataSource(); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourcePool.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourcePool.java index 570703404..ee114d1fb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourcePool.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourcePool.java @@ -1,927 +1,910 @@ -/** - * 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.lib.sql; - -import java.io.PrintWriter; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Properties; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; -import javax.sql.DataSource; - -import com.avaje.ebean.config.DataSourceConfig; -import com.avaje.ebeaninternal.api.ClassUtil; - -/** - * A robust DataSource. - *

- *

    - *
  • Manages the number of connections closing connections that have been idle - * for some time. - *
  • Notifies when the datasource goes down and comes back up. - *
  • Checks for expected downtime which is useful for schedule db backups. - *
  • Provides PreparedStatement caching - *
  • Knows the busy connections - *
  • Traces connections that have been leaked - *
- *

- */ -public class DataSourcePool implements DataSource { - - private static final Logger logger = Logger.getLogger(DataSourcePool.class.getName()); - - /** - * The name given to this dataSource. - */ - private final String name; - - /** - * Used to notify of changes to the DataSource status. - */ - private final DataSourceNotify notify; - - /** - * Optional listener that can be notified when connections are got from and - * put back into the pool. - */ - private final DataSourcePoolListener poolListener; - - /** - * Properties used to create a Connection. - */ - private final Properties connectionProps; - - /** - * The jdbc connection url. - */ - private final String databaseUrl; - - /** - * The jdbc driver. - */ - private final String databaseDriver; - - /** - * The sql used to test a connection. - */ - private final String heartbeatsql; - - /** - * The transaction isolation level as per java.sql.Connection. - */ - private final int transactionIsolation; - - /** - * The default autoCommit setting for Connections in this pool. - */ - private final boolean autoCommit; - - /** - * Flag set to true to capture stackTraces (can be expensive). - */ - private boolean captureStackTrace; - - /** - * The max size of the stack trace to report. - */ - private int maxStackTraceSize; - - /** - * flag to indicate we have sent an alert message. - */ - private boolean dataSourceDownAlertSent; - - /** - * The time the pool was last trimmed. - */ - private long lastTrimTime; - - /** - * Assume that the DataSource is up. heartBeat checking will discover when - * it goes down, and comes back up again. - */ - private boolean dataSourceUp = true; - - /** - * The current alert. - */ - private boolean inWarningMode; - - /** - * The minimum number of connections this pool will maintain. - */ - private int minConnections; - - /** - * The maximum number of connections this pool will grow to. - */ - private int maxConnections; - - /** - * The number of connections to exceed before a warning Alert is fired. - */ - private int warningSize; - - /** - * The time a thread will wait for a connection to become available. - */ - private int waitTimeoutMillis; - - /** - * The size of the preparedStatement cache; - */ - private int pstmtCacheSize; - - /** - * By default trim connections that are inactive for longer than this time. - */ - private int maxInactiveTimeSecs; - - private final PooledConnectionQueue queue; - - /** - * Used to find and close() leaked connections. Leaked connections are - * thought to be busy but have not been used for some time. Each time a - * connection is used it sets it's lastUsedTime. - */ - private long leakTimeMinutes; - - public DataSourcePool(DataSourceNotify notify, String name, DataSourceConfig params) { - - this.notify = notify; - this.name = name; - this.poolListener = createPoolListener(params.getPoolListener()); - - this.autoCommit = false; - this.transactionIsolation = params.getIsolationLevel(); - - this.maxInactiveTimeSecs = params.getMaxInactiveTimeSecs(); - this.leakTimeMinutes = params.getLeakTimeMinutes(); - this.captureStackTrace = params.isCaptureStackTrace(); - this.maxStackTraceSize = params.getMaxStackTraceSize(); - this.databaseDriver = params.getDriver(); - this.databaseUrl = params.getUrl(); - this.pstmtCacheSize = params.getPstmtCacheSize(); - - this.minConnections = params.getMinConnections(); - this.maxConnections = params.getMaxConnections(); - this.waitTimeoutMillis = params.getWaitTimeoutMillis(); - this.heartbeatsql = params.getHeartbeatSql(); - - queue = new PooledConnectionQueue(this); - - String un = params.getUsername(); - String pw = params.getPassword(); - if (un == null) { - throw new RuntimeException("DataSource user is null?"); - } - if (pw == null) { - throw new RuntimeException("DataSource password is null?"); - } - this.connectionProps = new Properties(); - this.connectionProps.setProperty("user", un); - this.connectionProps.setProperty("password", pw); - - Map customProperties = params.getCustomProperties(); - if (customProperties != null){ - Set> entrySet = customProperties.entrySet(); - for (Entry entry : entrySet) { - this.connectionProps.setProperty(entry.getKey(), entry.getValue()); - } - } - - try { - initialise(); - } catch (SQLException ex) { - throw new DataSourceException(ex); - } - } - - /** - * Create the DataSourcePoolListener if there is one. - */ - private DataSourcePoolListener createPoolListener(String cn) { - if (cn == null) { - return null; - } - try { - return (DataSourcePoolListener)ClassUtil.newInstance(cn, this.getClass()); - } catch (Exception e) { - throw new DataSourceException(e); - } - } - - private void initialise() throws SQLException { - - // Ensure database driver is loaded - try { - ClassUtil.forName(this.databaseDriver, this.getClass()); - } catch (Throwable e) { - throw new PersistenceException("Problem loading Database Driver [" + this.databaseDriver + "]: " - + e.getMessage(), e); - } - - String transIsolation = TransactionIsolation.getLevelDescription(transactionIsolation); - StringBuilder sb = new StringBuilder(); - sb.append("DataSourcePool [").append(name); - sb.append("] autoCommit[").append(autoCommit); - sb.append("] transIsolation[").append(transIsolation); - sb.append("] min[").append(minConnections); - sb.append("] max[").append(maxConnections).append("]"); - - logger.info(sb.toString()); - - queue.ensureMinimumConnections(); - } - - /** - * Returns false. - */ - public boolean isWrapperFor(Class arg0) throws SQLException { - return false; - } - - /** - * Not Implemented. - */ - public T unwrap(Class arg0) throws SQLException { - throw new SQLException("Not Implemented"); - } - - /** - * Return the dataSource name. - */ - public String getName() { - return name; - } - - /** - * Return the max size of stack traces used when trying to find connection pool leaks. - *

- * This is only used when {@link #isCaptureStackTrace()} is true. - *

- */ - public int getMaxStackTraceSize() { - return maxStackTraceSize; - } - - /** - * Returns false when the dataSource is down. - */ - public boolean isDataSourceUp() { - return dataSourceUp; - } - - /** - * Called when the pool hits the warning level. - */ - protected void notifyWarning(String msg) { - - if (!inWarningMode) { - // send an Error to the event log... - inWarningMode = true; - logger.warning(msg); - if (notify != null) { - String subject = "DataSourcePool [" + name + "] warning"; - notify.notifyWarning(subject, msg); - } - } - } - - private void notifyDataSourceIsDown(SQLException ex) { - - if (!dataSourceDownAlertSent) { - String msg = "FATAL: DataSourcePool [" + name + "] is down!!!"; - logger.log(Level.SEVERE, msg, ex); - if (notify != null) { - notify.notifyDataSourceDown(name); - } - dataSourceDownAlertSent = true; - - } - if (dataSourceUp) { - reset(); - } - dataSourceUp = false; - } - - private void notifyDataSourceIsUp() { - if (dataSourceDownAlertSent) { - String msg = "RESOLVED FATAL: DataSourcePool [" + name + "] is back up!"; - logger.log(Level.SEVERE, msg); - if (notify != null) { - notify.notifyDataSourceUp(name); - } - dataSourceDownAlertSent = false; - - } else if (!dataSourceUp) { - logger.log(Level.WARNING, "DataSourcePool [" + name + "] is back up!"); - } - - if (!dataSourceUp) { - dataSourceUp = true; - reset(); - } - } - - /** - * Check the dataSource is up. Trim connections. - */ - protected void checkDataSource() { - Connection conn = null; - try { - // test to see if we can create a new connection... - conn = getConnection(); - testConnection(conn); - - notifyDataSourceIsUp(); - - if (System.currentTimeMillis() > (lastTrimTime + (maxInactiveTimeSecs * 1000))) { - queue.trim(maxInactiveTimeSecs); - lastTrimTime = System.currentTimeMillis(); - } - - } catch (SQLException ex) { - notifyDataSourceIsDown(ex); - } finally { - try { - if (conn != null) { - conn.close(); - } - } catch (SQLException ex) { - logger.log(Level.WARNING, "Can't close connection in checkDataSource!"); - } - } - } - - /** - * Create a Connection that will not be part of the connection pool. - * - *

- * When this connection is closed it will not go back into the pool. - *

- * - *

- * If withDefaults is true then the Connection will have the autoCommit and - * transaction isolation set to the defaults for the pool. - *

- */ - public Connection createUnpooledConnection() throws SQLException { - - try { - Connection conn = DriverManager.getConnection(databaseUrl, connectionProps); - conn.setAutoCommit(autoCommit); - conn.setTransactionIsolation(transactionIsolation); - return conn; - - } catch (SQLException ex) { - notifyDataSourceIsDown(null); - throw ex; - } - } - - /** - * Set a new maximum size. The pool should respect this new maximum - * immediately and not require a restart. You may want to increase the - * maxConnections if the pool gets large and hits the warning level. - */ - public void setMaxSize(int max) { - queue.setMaxSize(max); - this.maxConnections = max; - } - - /** - * Return the max size this pool can grow to. - */ - public int getMaxSize() { - return maxConnections; - } - - /** - * Set the min size this pool should maintain. - */ - public void setMinSize(int min) { - queue.setMinSize(min); - this.minConnections = min; - } - - /** - * Return the min size this pool should maintain. - */ - public int getMinSize() { - return minConnections; - } - - /** - * Set a new maximum size. The pool should respect this new maximum - * immediately and not require a restart. You may want to increase the - * maxConnections if the pool gets large and hits the warning and or alert - * levels. - */ - public void setWarningSize(int warningSize) { - queue.setWarningSize(warningSize); - this.warningSize = warningSize; - } - - /** - * Return the warning size. When the pool hits this size it can send a - * notify message to an administrator. - */ - public int getWarningSize() { - return warningSize; - } - - /** - * Return the time in millis that threads will wait when the pool has hit - * the max size. These threads wait for connections to be returned by the - * busy connections. - */ - public int getWaitTimeoutMillis() { - return waitTimeoutMillis; - } - - /** - * Set the time after which inactive connections are trimmed. - */ - public void setMaxInactiveTimeSecs(int maxInactiveTimeSecs) { - this.maxInactiveTimeSecs = maxInactiveTimeSecs; - } - - /** - * Return the time after which inactive connections are trimmed. - */ - public int getMaxInactiveTimeSecs() { - return maxInactiveTimeSecs; - } - - private void testConnection(Connection conn) throws SQLException { - - if (heartbeatsql == null) { - return; - } - Statement stmt = null; - ResultSet rset = null; - try { - // It should only error IF the DataSource is down ? (or a network - // issue?) - stmt = conn.createStatement(); - rset = stmt.executeQuery(heartbeatsql); - conn.commit(); - - } finally { - try { - if (rset != null) { - rset.close(); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - try { - if (stmt != null) { - stmt.close(); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - } - } - - /** - * Make sure the connection is still ok to use. If not then remove it from - * the pool. - */ - protected boolean validateConnection(PooledConnection conn) { - try { - if (heartbeatsql == null) { - logger.info("Can not test connection as heartbeatsql is not set"); - return false; - } - - testConnection(conn); - return true; - - } catch (Exception e) { - String desc = "heartbeatsql test failed on connection[" + conn.getName() + "]"; - logger.warning(desc); - return false; - } - } - - /** - * Called by the PooledConnection themselves, returning themselves to the - * pool when they have been finished with. - *

- * Note that connections may not be added back to the pool if returnToPool - * is false or if they where created before the recycleTime. In both of - * these cases the connection is fully closed and not pooled. - *

- * - * @param pooledConnection - * the returning connection - * - */ - protected void returnConnection(PooledConnection pooledConnection) { - - if (poolListener != null) { - poolListener.onBeforeReturnConnection(pooledConnection); - } - queue.returnPooledConnection(pooledConnection); - } - - /** - * Returns information describing connections that are currently being used. - */ - public String getBusyConnectionInformation() { - - return queue.getBusyConnectionInformation(); - } - - /** - * Dumps the busy connection information to the logs. - *

- * This includes the stackTrace elements if they are being captured. This is - * useful when needing to look a potential connection pool leaks. - *

- */ - public void dumpBusyConnectionInformation() { - - queue.dumpBusyConnectionInformation(); - } - - /** - * Close any busy connections that have not been used for some time. - *

- * These connections are considered to have leaked from the connection pool. - *

- *

- * Connection leaks occur when code doesn't ensure that connections are - * closed() after they have been finished with. There should be an - * appropriate try catch finally block to ensure connections are always - * closed and put back into the pool. - *

- */ - public void closeBusyConnections(long leakTimeMinutes) { - - queue.closeBusyConnections(leakTimeMinutes); - } - - /** - * Grow the pool by creating a new connection. The connection can either be - * added to the available list, or returned. - *

- * This method is protected by synchronisation in calling methods. - *

- */ - protected PooledConnection createConnectionForQueue(int connId) throws SQLException { - - try { - Connection c = createUnpooledConnection(); - - PooledConnection pc = new PooledConnection(this, connId, c); - pc.resetForUse(); - - if (!dataSourceUp) { - notifyDataSourceIsUp(); - } - return pc; - - } catch (SQLException ex) { - notifyDataSourceIsDown(ex); - throw ex; - } - - } - - /** - * Close all the connections in the pool. - *

- *

    - *
  • Checks that the database is up. - *
  • Resets the Alert level. - *
  • Closes busy connections that have not been used for some time (aka - * leaks). - *
  • This closes all the currently available connections. - *
  • Busy connections are closed when they are returned to the pool. - *
- *

- */ - public void reset() { - queue.reset(leakTimeMinutes); - inWarningMode = false; - } - - /** - * Return a pooled connection. - */ - public Connection getConnection() throws SQLException { - return getPooledConnection(); - } - - /** - * Get a connection from the pool. - *

- * This will grow the pool if all the current connections are busy. This - * will go into a wait if the pool has hit its maximum size. - *

- */ - public PooledConnection getPooledConnection() throws SQLException { - - PooledConnection c = queue.getPooledConnection(); - - if (captureStackTrace) { - c.setStackTrace(Thread.currentThread().getStackTrace()); - } - - if (poolListener != null) { - poolListener.onAfterBorrowConnection(c); - } - return c; - } - - /** - * Send a message to the DataSourceAlertListener to test it. This is so that - * you can make sure the alerter is configured correctly etc. - */ - public void testAlert() { - - String subject = "Test DataSourcePool [" + name + "]"; - String msg = "Just testing if alert message is sent successfully."; - - if (notify != null) { - notify.notifyWarning(subject, msg); - } - } - - /** - * This will close all the free connections, and then go into a wait loop, - * waiting for the busy connections to be freed. - * - *

- * The DataSources's should be shutdown AFTER thread pools. Leaked - * Connections are not waited on, as that would hang the server. - *

- */ - public void shutdown() { - queue.shutdown(); - } - - /** - * Return the default autoCommit setting Connections in this pool will use. - * - * @return true if the pool defaults autoCommit to true - */ - public boolean getAutoCommit() { - return autoCommit; - } - - /** - * Return the default transaction isolation level connections in this pool - * should have. - * - * @return the default transaction isolation level - */ - public int getTransactionIsolation() { - return transactionIsolation; - } - - /** - * Return true if the connection pool is currently capturing the StackTrace - * when connections are 'got' from the pool. - *

- * This is set to true to help diagnose connection pool leaks. - *

- */ - public boolean isCaptureStackTrace() { - return captureStackTrace; - } - - /** - * Set this to true means that the StackElements are captured every time a - * connection is retrieved from the pool. This can be used to identify - * connection pool leaks. - */ - public void setCaptureStackTrace(boolean captureStackTrace) { - this.captureStackTrace = captureStackTrace; - } - - /** - * Not implemented and shouldn't be used. - */ - public Connection getConnection(String username, String password) throws SQLException { - throw new SQLException("Method not supported"); - } - - /** - * Not implemented and shouldn't be used. - */ - public int getLoginTimeout() throws SQLException { - throw new SQLException("Method not supported"); - } - - /** - * Not implemented and shouldn't be used. - */ - public void setLoginTimeout(int seconds) throws SQLException { - throw new SQLException("Method not supported"); - } - - /** - * Returns null. - */ - public PrintWriter getLogWriter() { - return null; - } - - /** - * Not implemented. - */ - public void setLogWriter(PrintWriter writer) throws SQLException { - throw new SQLException("Method not supported"); - } - - /** - * For detecting and closing leaked connections. Connections that have been - * busy for more than leakTimeMinutes are considered leaks and will be - * closed on a reset(). - *

- * If you want to use a connection for that longer then you should consider - * creating an unpooled connection or setting longRunning to true on that - * connection. - *

- */ - public void setLeakTimeMinutes(long leakTimeMinutes) { - this.leakTimeMinutes = leakTimeMinutes; - } - - /** - * Return the number of minutes after which a busy connection could be - * considered leaked from the connection pool. - */ - public long getLeakTimeMinutes() { - return leakTimeMinutes; - } - - /** - * Return the preparedStatement cache size. - */ - public int getPstmtCacheSize() { - return pstmtCacheSize; - } - - /** - * Set the preparedStatement cache size. - */ - public void setPstmtCacheSize(int pstmtCacheSize) { - this.pstmtCacheSize = pstmtCacheSize; - } - - /** - * Return the current status of the connection pool. - *

- * If you pass reset = true then the counters such as - * hitCount, waitCount and highWaterMark are reset. - *

- */ - public Status getStatus(boolean reset) { - return queue.getStatus(reset); - } - - /** - * Deregister the JDBC driver. - */ - public void deregisterDriver() { - try { - DriverManager.deregisterDriver(DriverManager.getDriver(this.databaseUrl)); - String msg = "Deregistered the JDBC driver "+this.databaseDriver; - logger.log(Level.FINE, msg); - } catch (SQLException e) { - String msg = "Error trying to deregister the JDBC driver "+this.databaseDriver; - logger.log(Level.WARNING, msg, e); - } - } - - public static class Status { - - private final String name; - private final int minSize; - private final int maxSize; - private final int free; - private final int busy; - private final int waiting; - private final int highWaterMark; - private final int waitCount; - private final int hitCount; - - protected Status(String name, int minSize, int maxSize, int free, int busy, int waiting, int highWaterMark, - int waitCount, int hitCount) { - this.name = name; - this.minSize = minSize; - this.maxSize = maxSize; - this.free = free; - this.busy = busy; - this.waiting = waiting; - this.highWaterMark = highWaterMark; - this.waitCount = waitCount; - this.hitCount = hitCount; - } - - public String toString() { - return "min:" + minSize + " max:" + maxSize + " free:" + free + " busy:" + busy + " waiting:" + waiting - + " highWaterMark:" + highWaterMark + " waitCount:" + waitCount + " hitCount:" + hitCount; - } - - /** - * Return the DataSource name. - */ - public String getName() { - return name; - } - - /** - * Return the min pool size. - */ - public int getMinSize() { - return minSize; - } - - /** - * Return the max pool size. - */ - public int getMaxSize() { - return maxSize; - } - - /** - * Return the current number of free connections in the pool. - */ - public int getFree() { - return free; - } - - /** - * Return the current number of busy connections in the pool. - */ - public int getBusy() { - return busy; - } - - /** - * Return the current number of threads waiting for a connection. - */ - public int getWaiting() { - return waiting; - } - - /** - * Return the high water mark of busy connections. - */ - public int getHighWaterMark() { - return highWaterMark; - } - - /** - * Return the total number of times a thread had to wait. - */ - public int getWaitCount() { - return waitCount; - } - - /** - * Return the total number of times there was an attempt to get a - * connection. - *

- * If the attempt to get a connection failed with a timeout or other - * exception those attempts are still included in this hit count. - *

- */ - public int getHitCount() { - return hitCount; - } - - } - -} +package com.avaje.ebeaninternal.server.lib.sql; + +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; +import javax.sql.DataSource; + +import com.avaje.ebean.config.DataSourceConfig; +import com.avaje.ebeaninternal.api.ClassUtil; + +/** + * A robust DataSource. + *

+ *

    + *
  • Manages the number of connections closing connections that have been idle + * for some time. + *
  • Notifies when the datasource goes down and comes back up. + *
  • Checks for expected downtime which is useful for schedule db backups. + *
  • Provides PreparedStatement caching + *
  • Knows the busy connections + *
  • Traces connections that have been leaked + *
+ *

+ */ +public class DataSourcePool implements DataSource { + + private static final Logger logger = Logger.getLogger(DataSourcePool.class.getName()); + + /** + * The name given to this dataSource. + */ + private final String name; + + /** + * Used to notify of changes to the DataSource status. + */ + private final DataSourceNotify notify; + + /** + * Optional listener that can be notified when connections are got from and + * put back into the pool. + */ + private final DataSourcePoolListener poolListener; + + /** + * Properties used to create a Connection. + */ + private final Properties connectionProps; + + /** + * The jdbc connection url. + */ + private final String databaseUrl; + + /** + * The jdbc driver. + */ + private final String databaseDriver; + + /** + * The sql used to test a connection. + */ + private final String heartbeatsql; + + /** + * The transaction isolation level as per java.sql.Connection. + */ + private final int transactionIsolation; + + /** + * The default autoCommit setting for Connections in this pool. + */ + private final boolean autoCommit; + + /** + * Flag set to true to capture stackTraces (can be expensive). + */ + private boolean captureStackTrace; + + /** + * The max size of the stack trace to report. + */ + private int maxStackTraceSize; + + /** + * flag to indicate we have sent an alert message. + */ + private boolean dataSourceDownAlertSent; + + /** + * The time the pool was last trimmed. + */ + private long lastTrimTime; + + /** + * Assume that the DataSource is up. heartBeat checking will discover when + * it goes down, and comes back up again. + */ + private boolean dataSourceUp = true; + + /** + * The current alert. + */ + private boolean inWarningMode; + + /** + * The minimum number of connections this pool will maintain. + */ + private int minConnections; + + /** + * The maximum number of connections this pool will grow to. + */ + private int maxConnections; + + /** + * The number of connections to exceed before a warning Alert is fired. + */ + private int warningSize; + + /** + * The time a thread will wait for a connection to become available. + */ + private int waitTimeoutMillis; + + /** + * The size of the preparedStatement cache; + */ + private int pstmtCacheSize; + + /** + * By default trim connections that are inactive for longer than this time. + */ + private int maxInactiveTimeSecs; + + private final PooledConnectionQueue queue; + + /** + * Used to find and close() leaked connections. Leaked connections are + * thought to be busy but have not been used for some time. Each time a + * connection is used it sets it's lastUsedTime. + */ + private long leakTimeMinutes; + + public DataSourcePool(DataSourceNotify notify, String name, DataSourceConfig params) { + + this.notify = notify; + this.name = name; + this.poolListener = createPoolListener(params.getPoolListener()); + + this.autoCommit = false; + this.transactionIsolation = params.getIsolationLevel(); + + this.maxInactiveTimeSecs = params.getMaxInactiveTimeSecs(); + this.leakTimeMinutes = params.getLeakTimeMinutes(); + this.captureStackTrace = params.isCaptureStackTrace(); + this.maxStackTraceSize = params.getMaxStackTraceSize(); + this.databaseDriver = params.getDriver(); + this.databaseUrl = params.getUrl(); + this.pstmtCacheSize = params.getPstmtCacheSize(); + + this.minConnections = params.getMinConnections(); + this.maxConnections = params.getMaxConnections(); + this.waitTimeoutMillis = params.getWaitTimeoutMillis(); + this.heartbeatsql = params.getHeartbeatSql(); + + queue = new PooledConnectionQueue(this); + + String un = params.getUsername(); + String pw = params.getPassword(); + if (un == null) { + throw new RuntimeException("DataSource user is null?"); + } + if (pw == null) { + throw new RuntimeException("DataSource password is null?"); + } + this.connectionProps = new Properties(); + this.connectionProps.setProperty("user", un); + this.connectionProps.setProperty("password", pw); + + Map customProperties = params.getCustomProperties(); + if (customProperties != null){ + Set> entrySet = customProperties.entrySet(); + for (Entry entry : entrySet) { + this.connectionProps.setProperty(entry.getKey(), entry.getValue()); + } + } + + try { + initialise(); + } catch (SQLException ex) { + throw new DataSourceException(ex); + } + } + + /** + * Create the DataSourcePoolListener if there is one. + */ + private DataSourcePoolListener createPoolListener(String cn) { + if (cn == null) { + return null; + } + try { + return (DataSourcePoolListener)ClassUtil.newInstance(cn, this.getClass()); + } catch (Exception e) { + throw new DataSourceException(e); + } + } + + private void initialise() throws SQLException { + + // Ensure database driver is loaded + try { + ClassUtil.forName(this.databaseDriver, this.getClass()); + } catch (Throwable e) { + throw new PersistenceException("Problem loading Database Driver [" + this.databaseDriver + "]: " + + e.getMessage(), e); + } + + String transIsolation = TransactionIsolation.getLevelDescription(transactionIsolation); + StringBuilder sb = new StringBuilder(); + sb.append("DataSourcePool [").append(name); + sb.append("] autoCommit[").append(autoCommit); + sb.append("] transIsolation[").append(transIsolation); + sb.append("] min[").append(minConnections); + sb.append("] max[").append(maxConnections).append("]"); + + logger.info(sb.toString()); + + queue.ensureMinimumConnections(); + } + + /** + * Returns false. + */ + public boolean isWrapperFor(Class arg0) throws SQLException { + return false; + } + + /** + * Not Implemented. + */ + public T unwrap(Class arg0) throws SQLException { + throw new SQLException("Not Implemented"); + } + + /** + * Return the dataSource name. + */ + public String getName() { + return name; + } + + /** + * Return the max size of stack traces used when trying to find connection pool leaks. + *

+ * This is only used when {@link #isCaptureStackTrace()} is true. + *

+ */ + public int getMaxStackTraceSize() { + return maxStackTraceSize; + } + + /** + * Returns false when the dataSource is down. + */ + public boolean isDataSourceUp() { + return dataSourceUp; + } + + /** + * Called when the pool hits the warning level. + */ + protected void notifyWarning(String msg) { + + if (!inWarningMode) { + // send an Error to the event log... + inWarningMode = true; + logger.warning(msg); + if (notify != null) { + String subject = "DataSourcePool [" + name + "] warning"; + notify.notifyWarning(subject, msg); + } + } + } + + private void notifyDataSourceIsDown(SQLException ex) { + + if (!dataSourceDownAlertSent) { + String msg = "FATAL: DataSourcePool [" + name + "] is down!!!"; + logger.log(Level.SEVERE, msg, ex); + if (notify != null) { + notify.notifyDataSourceDown(name); + } + dataSourceDownAlertSent = true; + + } + if (dataSourceUp) { + reset(); + } + dataSourceUp = false; + } + + private void notifyDataSourceIsUp() { + if (dataSourceDownAlertSent) { + String msg = "RESOLVED FATAL: DataSourcePool [" + name + "] is back up!"; + logger.log(Level.SEVERE, msg); + if (notify != null) { + notify.notifyDataSourceUp(name); + } + dataSourceDownAlertSent = false; + + } else if (!dataSourceUp) { + logger.log(Level.WARNING, "DataSourcePool [" + name + "] is back up!"); + } + + if (!dataSourceUp) { + dataSourceUp = true; + reset(); + } + } + + /** + * Check the dataSource is up. Trim connections. + */ + protected void checkDataSource() { + Connection conn = null; + try { + // test to see if we can create a new connection... + conn = getConnection(); + testConnection(conn); + + notifyDataSourceIsUp(); + + if (System.currentTimeMillis() > (lastTrimTime + (maxInactiveTimeSecs * 1000))) { + queue.trim(maxInactiveTimeSecs); + lastTrimTime = System.currentTimeMillis(); + } + + } catch (SQLException ex) { + notifyDataSourceIsDown(ex); + } finally { + try { + if (conn != null) { + conn.close(); + } + } catch (SQLException ex) { + logger.log(Level.WARNING, "Can't close connection in checkDataSource!"); + } + } + } + + /** + * Create a Connection that will not be part of the connection pool. + * + *

+ * When this connection is closed it will not go back into the pool. + *

+ * + *

+ * If withDefaults is true then the Connection will have the autoCommit and + * transaction isolation set to the defaults for the pool. + *

+ */ + public Connection createUnpooledConnection() throws SQLException { + + try { + Connection conn = DriverManager.getConnection(databaseUrl, connectionProps); + conn.setAutoCommit(autoCommit); + conn.setTransactionIsolation(transactionIsolation); + return conn; + + } catch (SQLException ex) { + notifyDataSourceIsDown(null); + throw ex; + } + } + + /** + * Set a new maximum size. The pool should respect this new maximum + * immediately and not require a restart. You may want to increase the + * maxConnections if the pool gets large and hits the warning level. + */ + public void setMaxSize(int max) { + queue.setMaxSize(max); + this.maxConnections = max; + } + + /** + * Return the max size this pool can grow to. + */ + public int getMaxSize() { + return maxConnections; + } + + /** + * Set the min size this pool should maintain. + */ + public void setMinSize(int min) { + queue.setMinSize(min); + this.minConnections = min; + } + + /** + * Return the min size this pool should maintain. + */ + public int getMinSize() { + return minConnections; + } + + /** + * Set a new maximum size. The pool should respect this new maximum + * immediately and not require a restart. You may want to increase the + * maxConnections if the pool gets large and hits the warning and or alert + * levels. + */ + public void setWarningSize(int warningSize) { + queue.setWarningSize(warningSize); + this.warningSize = warningSize; + } + + /** + * Return the warning size. When the pool hits this size it can send a + * notify message to an administrator. + */ + public int getWarningSize() { + return warningSize; + } + + /** + * Return the time in millis that threads will wait when the pool has hit + * the max size. These threads wait for connections to be returned by the + * busy connections. + */ + public int getWaitTimeoutMillis() { + return waitTimeoutMillis; + } + + /** + * Set the time after which inactive connections are trimmed. + */ + public void setMaxInactiveTimeSecs(int maxInactiveTimeSecs) { + this.maxInactiveTimeSecs = maxInactiveTimeSecs; + } + + /** + * Return the time after which inactive connections are trimmed. + */ + public int getMaxInactiveTimeSecs() { + return maxInactiveTimeSecs; + } + + private void testConnection(Connection conn) throws SQLException { + + if (heartbeatsql == null) { + return; + } + Statement stmt = null; + ResultSet rset = null; + try { + // It should only error IF the DataSource is down ? (or a network + // issue?) + stmt = conn.createStatement(); + rset = stmt.executeQuery(heartbeatsql); + conn.commit(); + + } finally { + try { + if (rset != null) { + rset.close(); + } + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + try { + if (stmt != null) { + stmt.close(); + } + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + } + } + + /** + * Make sure the connection is still ok to use. If not then remove it from + * the pool. + */ + protected boolean validateConnection(PooledConnection conn) { + try { + if (heartbeatsql == null) { + logger.info("Can not test connection as heartbeatsql is not set"); + return false; + } + + testConnection(conn); + return true; + + } catch (Exception e) { + String desc = "heartbeatsql test failed on connection[" + conn.getName() + "]"; + logger.warning(desc); + return false; + } + } + + /** + * Called by the PooledConnection themselves, returning themselves to the + * pool when they have been finished with. + *

+ * Note that connections may not be added back to the pool if returnToPool + * is false or if they where created before the recycleTime. In both of + * these cases the connection is fully closed and not pooled. + *

+ * + * @param pooledConnection + * the returning connection + * + */ + protected void returnConnection(PooledConnection pooledConnection) { + + if (poolListener != null) { + poolListener.onBeforeReturnConnection(pooledConnection); + } + queue.returnPooledConnection(pooledConnection); + } + + /** + * Returns information describing connections that are currently being used. + */ + public String getBusyConnectionInformation() { + + return queue.getBusyConnectionInformation(); + } + + /** + * Dumps the busy connection information to the logs. + *

+ * This includes the stackTrace elements if they are being captured. This is + * useful when needing to look a potential connection pool leaks. + *

+ */ + public void dumpBusyConnectionInformation() { + + queue.dumpBusyConnectionInformation(); + } + + /** + * Close any busy connections that have not been used for some time. + *

+ * These connections are considered to have leaked from the connection pool. + *

+ *

+ * Connection leaks occur when code doesn't ensure that connections are + * closed() after they have been finished with. There should be an + * appropriate try catch finally block to ensure connections are always + * closed and put back into the pool. + *

+ */ + public void closeBusyConnections(long leakTimeMinutes) { + + queue.closeBusyConnections(leakTimeMinutes); + } + + /** + * Grow the pool by creating a new connection. The connection can either be + * added to the available list, or returned. + *

+ * This method is protected by synchronisation in calling methods. + *

+ */ + protected PooledConnection createConnectionForQueue(int connId) throws SQLException { + + try { + Connection c = createUnpooledConnection(); + + PooledConnection pc = new PooledConnection(this, connId, c); + pc.resetForUse(); + + if (!dataSourceUp) { + notifyDataSourceIsUp(); + } + return pc; + + } catch (SQLException ex) { + notifyDataSourceIsDown(ex); + throw ex; + } + + } + + /** + * Close all the connections in the pool. + *

+ *

    + *
  • Checks that the database is up. + *
  • Resets the Alert level. + *
  • Closes busy connections that have not been used for some time (aka + * leaks). + *
  • This closes all the currently available connections. + *
  • Busy connections are closed when they are returned to the pool. + *
+ *

+ */ + public void reset() { + queue.reset(leakTimeMinutes); + inWarningMode = false; + } + + /** + * Return a pooled connection. + */ + public Connection getConnection() throws SQLException { + return getPooledConnection(); + } + + /** + * Get a connection from the pool. + *

+ * This will grow the pool if all the current connections are busy. This + * will go into a wait if the pool has hit its maximum size. + *

+ */ + public PooledConnection getPooledConnection() throws SQLException { + + PooledConnection c = queue.getPooledConnection(); + + if (captureStackTrace) { + c.setStackTrace(Thread.currentThread().getStackTrace()); + } + + if (poolListener != null) { + poolListener.onAfterBorrowConnection(c); + } + return c; + } + + /** + * Send a message to the DataSourceAlertListener to test it. This is so that + * you can make sure the alerter is configured correctly etc. + */ + public void testAlert() { + + String subject = "Test DataSourcePool [" + name + "]"; + String msg = "Just testing if alert message is sent successfully."; + + if (notify != null) { + notify.notifyWarning(subject, msg); + } + } + + /** + * This will close all the free connections, and then go into a wait loop, + * waiting for the busy connections to be freed. + * + *

+ * The DataSources's should be shutdown AFTER thread pools. Leaked + * Connections are not waited on, as that would hang the server. + *

+ */ + public void shutdown() { + queue.shutdown(); + } + + /** + * Return the default autoCommit setting Connections in this pool will use. + * + * @return true if the pool defaults autoCommit to true + */ + public boolean getAutoCommit() { + return autoCommit; + } + + /** + * Return the default transaction isolation level connections in this pool + * should have. + * + * @return the default transaction isolation level + */ + public int getTransactionIsolation() { + return transactionIsolation; + } + + /** + * Return true if the connection pool is currently capturing the StackTrace + * when connections are 'got' from the pool. + *

+ * This is set to true to help diagnose connection pool leaks. + *

+ */ + public boolean isCaptureStackTrace() { + return captureStackTrace; + } + + /** + * Set this to true means that the StackElements are captured every time a + * connection is retrieved from the pool. This can be used to identify + * connection pool leaks. + */ + public void setCaptureStackTrace(boolean captureStackTrace) { + this.captureStackTrace = captureStackTrace; + } + + /** + * Not implemented and shouldn't be used. + */ + public Connection getConnection(String username, String password) throws SQLException { + throw new SQLException("Method not supported"); + } + + /** + * Not implemented and shouldn't be used. + */ + public int getLoginTimeout() throws SQLException { + throw new SQLException("Method not supported"); + } + + /** + * Not implemented and shouldn't be used. + */ + public void setLoginTimeout(int seconds) throws SQLException { + throw new SQLException("Method not supported"); + } + + /** + * Returns null. + */ + public PrintWriter getLogWriter() { + return null; + } + + /** + * Not implemented. + */ + public void setLogWriter(PrintWriter writer) throws SQLException { + throw new SQLException("Method not supported"); + } + + /** + * For detecting and closing leaked connections. Connections that have been + * busy for more than leakTimeMinutes are considered leaks and will be + * closed on a reset(). + *

+ * If you want to use a connection for that longer then you should consider + * creating an unpooled connection or setting longRunning to true on that + * connection. + *

+ */ + public void setLeakTimeMinutes(long leakTimeMinutes) { + this.leakTimeMinutes = leakTimeMinutes; + } + + /** + * Return the number of minutes after which a busy connection could be + * considered leaked from the connection pool. + */ + public long getLeakTimeMinutes() { + return leakTimeMinutes; + } + + /** + * Return the preparedStatement cache size. + */ + public int getPstmtCacheSize() { + return pstmtCacheSize; + } + + /** + * Set the preparedStatement cache size. + */ + public void setPstmtCacheSize(int pstmtCacheSize) { + this.pstmtCacheSize = pstmtCacheSize; + } + + /** + * Return the current status of the connection pool. + *

+ * If you pass reset = true then the counters such as + * hitCount, waitCount and highWaterMark are reset. + *

+ */ + public Status getStatus(boolean reset) { + return queue.getStatus(reset); + } + + /** + * Deregister the JDBC driver. + */ + public void deregisterDriver() { + try { + DriverManager.deregisterDriver(DriverManager.getDriver(this.databaseUrl)); + String msg = "Deregistered the JDBC driver "+this.databaseDriver; + logger.log(Level.FINE, msg); + } catch (SQLException e) { + String msg = "Error trying to deregister the JDBC driver "+this.databaseDriver; + logger.log(Level.WARNING, msg, e); + } + } + + public static class Status { + + private final String name; + private final int minSize; + private final int maxSize; + private final int free; + private final int busy; + private final int waiting; + private final int highWaterMark; + private final int waitCount; + private final int hitCount; + + protected Status(String name, int minSize, int maxSize, int free, int busy, int waiting, int highWaterMark, + int waitCount, int hitCount) { + this.name = name; + this.minSize = minSize; + this.maxSize = maxSize; + this.free = free; + this.busy = busy; + this.waiting = waiting; + this.highWaterMark = highWaterMark; + this.waitCount = waitCount; + this.hitCount = hitCount; + } + + public String toString() { + return "min:" + minSize + " max:" + maxSize + " free:" + free + " busy:" + busy + " waiting:" + waiting + + " highWaterMark:" + highWaterMark + " waitCount:" + waitCount + " hitCount:" + hitCount; + } + + /** + * Return the DataSource name. + */ + public String getName() { + return name; + } + + /** + * Return the min pool size. + */ + public int getMinSize() { + return minSize; + } + + /** + * Return the max pool size. + */ + public int getMaxSize() { + return maxSize; + } + + /** + * Return the current number of free connections in the pool. + */ + public int getFree() { + return free; + } + + /** + * Return the current number of busy connections in the pool. + */ + public int getBusy() { + return busy; + } + + /** + * Return the current number of threads waiting for a connection. + */ + public int getWaiting() { + return waiting; + } + + /** + * Return the high water mark of busy connections. + */ + public int getHighWaterMark() { + return highWaterMark; + } + + /** + * Return the total number of times a thread had to wait. + */ + public int getWaitCount() { + return waitCount; + } + + /** + * Return the total number of times there was an attempt to get a + * connection. + *

+ * If the attempt to get a connection failed with a timeout or other + * exception those attempts are still included in this hit count. + *

+ */ + public int getHitCount() { + return hitCount; + } + + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedPreparedStatement.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedPreparedStatement.java index 6d6204832..3b3b40344 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedPreparedStatement.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedPreparedStatement.java @@ -1,407 +1,390 @@ -/** - * 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.lib.sql; - -import java.io.InputStream; -import java.io.Reader; -import java.math.BigDecimal; -import java.net.URL; -import java.sql.Array; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.Date; -import java.sql.ParameterMetaData; -import java.sql.PreparedStatement; -import java.sql.Ref; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Time; -import java.sql.Timestamp; -import java.util.Calendar; - -/** - * Extended PreparedStatement that supports caching. - *

- * Designed so that it can be cached by the PooledConnection. It additionally - * notes any Exceptions that occur and this is used to ensure bad connections - * are removed from the connection pool. - *

- */ -public class ExtendedPreparedStatement extends ExtendedStatement implements PreparedStatement { - - /** - * The SQL used to create the underlying PreparedStatement. - */ - final String sql; - - /** - * The key used to cache this in the connection. - */ - final String cacheKey; - - /** - * Create a wrapped PreparedStatement that can be cached. - */ - public ExtendedPreparedStatement(PooledConnection pooledConnection, PreparedStatement pstmt, - String sql, String cacheKey) { - super(pooledConnection, pstmt); - this.sql = sql; - this.cacheKey = cacheKey; - } - - public PreparedStatement getDelegate() { - return pstmt; - } - - /** - * Return the key used to cache this on the Connection. - */ - public String getCacheKey() { - return cacheKey; - } - - /** - * Return the SQL used to create this PreparedStatement. - */ - public String getSql() { - return sql; - } - - /** - * Fully close the underlying PreparedStatement. After this we can no longer - * reuse the PreparedStatement. - */ - public void closeDestroy() throws SQLException { - pstmt.close(); - } - - /** - * Returns the PreparedStatement back into the cache. This doesn't fully - * close the underlying PreparedStatement. - */ - public void close() throws SQLException { - // return the connection back into the cache. - pooledConnection.returnPreparedStatement(this); - } - - /** - * Add the last binding for batch execution. - */ - public void addBatch() throws SQLException { - try { - pstmt.addBatch(); - } catch (SQLException e) { - // we got an error... need to check this - // connection before returning it - pooledConnection.addError(e); - throw e; - } - } - - /** - * Clear parameters. - */ - public void clearParameters() throws SQLException { - try { - pstmt.clearParameters(); - } catch (SQLException e) { - // we got an error... need to check - // this connection before returning it - pooledConnection.addError(e); - throw e; - } - } - - /** - * execute the statement. - */ - public boolean execute() throws SQLException { - try { - return pstmt.execute(); - } catch (SQLException e) { - // we got an error... need to check - // this connection before returning it - pooledConnection.addError(e); - throw e; - } - } - - /** - * Execute teh query. - */ - public ResultSet executeQuery() throws SQLException { - try { - return pstmt.executeQuery(); - } catch (SQLException e) { - // we got an error... need to check - // this connection before returning it - pooledConnection.addError(e); - throw e; - } - } - - /** - * Execute the dml statement. - */ - public int executeUpdate() throws SQLException { - try { - return pstmt.executeUpdate(); - } catch (SQLException e) { - // we got an error... need to check - // this connection before returning it - pooledConnection.addError(e); - throw e; - } - } - - /** - * Return the MetaData for the query. - */ - public ResultSetMetaData getMetaData() throws SQLException { - try { - return pstmt.getMetaData(); - } catch (SQLException e) { - // we got an error... need to check - // this connection before returning it - pooledConnection.addError(e); - throw e; - } - } - - /** - * Standard PreparedStatement method execution. - */ - public ParameterMetaData getParameterMetaData() throws SQLException { - return pstmt.getParameterMetaData(); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setArray(int i, Array x) throws SQLException { - pstmt.setArray(i, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { - pstmt.setAsciiStream(parameterIndex, x, length); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { - pstmt.setBigDecimal(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { - pstmt.setBinaryStream(parameterIndex, x, length); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setBlob(int i, Blob x) throws SQLException { - pstmt.setBlob(i, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setBoolean(int parameterIndex, boolean x) throws SQLException { - pstmt.setBoolean(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setByte(int parameterIndex, byte x) throws SQLException { - pstmt.setByte(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setBytes(int parameterIndex, byte[] x) throws SQLException { - pstmt.setBytes(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setCharacterStream(int parameterIndex, Reader reader, int length) - throws SQLException { - pstmt.setCharacterStream(parameterIndex, reader, length); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setClob(int i, Clob x) throws SQLException { - pstmt.setClob(i, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setDate(int parameterIndex, Date x) throws SQLException { - pstmt.setDate(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { - pstmt.setDate(parameterIndex, x, cal); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setDouble(int parameterIndex, double x) throws SQLException { - pstmt.setDouble(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setFloat(int parameterIndex, float x) throws SQLException { - pstmt.setFloat(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setInt(int parameterIndex, int x) throws SQLException { - pstmt.setInt(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setLong(int parameterIndex, long x) throws SQLException { - pstmt.setLong(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setNull(int parameterIndex, int sqlType) throws SQLException { - pstmt.setNull(parameterIndex, sqlType); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setNull(int paramIndex, int sqlType, String typeName) throws SQLException { - pstmt.setNull(paramIndex, sqlType, typeName); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setObject(int parameterIndex, Object x) throws SQLException { - pstmt.setObject(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { - pstmt.setObject(parameterIndex, x, targetSqlType); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) - throws SQLException { - pstmt.setObject(parameterIndex, x, targetSqlType, scale); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setRef(int i, Ref x) throws SQLException { - pstmt.setRef(i, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setShort(int parameterIndex, short x) throws SQLException { - pstmt.setShort(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setString(int parameterIndex, String x) throws SQLException { - pstmt.setString(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setTime(int parameterIndex, Time x) throws SQLException { - pstmt.setTime(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { - pstmt.setTime(parameterIndex, x, cal); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { - pstmt.setTimestamp(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { - pstmt.setTimestamp(parameterIndex, x, cal); - } - - /** - * Standard PreparedStatement method execution. - * @deprecated - */ - public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { - pstmt.setUnicodeStream(parameterIndex, x, length); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setURL(int parameterIndex, URL x) throws SQLException { - pstmt.setURL(parameterIndex, x); - } - -} +package com.avaje.ebeaninternal.server.lib.sql; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; + +/** + * Extended PreparedStatement that supports caching. + *

+ * Designed so that it can be cached by the PooledConnection. It additionally + * notes any Exceptions that occur and this is used to ensure bad connections + * are removed from the connection pool. + *

+ */ +public class ExtendedPreparedStatement extends ExtendedStatement implements PreparedStatement { + + /** + * The SQL used to create the underlying PreparedStatement. + */ + final String sql; + + /** + * The key used to cache this in the connection. + */ + final String cacheKey; + + /** + * Create a wrapped PreparedStatement that can be cached. + */ + public ExtendedPreparedStatement(PooledConnection pooledConnection, PreparedStatement pstmt, + String sql, String cacheKey) { + super(pooledConnection, pstmt); + this.sql = sql; + this.cacheKey = cacheKey; + } + + public PreparedStatement getDelegate() { + return pstmt; + } + + /** + * Return the key used to cache this on the Connection. + */ + public String getCacheKey() { + return cacheKey; + } + + /** + * Return the SQL used to create this PreparedStatement. + */ + public String getSql() { + return sql; + } + + /** + * Fully close the underlying PreparedStatement. After this we can no longer + * reuse the PreparedStatement. + */ + public void closeDestroy() throws SQLException { + pstmt.close(); + } + + /** + * Returns the PreparedStatement back into the cache. This doesn't fully + * close the underlying PreparedStatement. + */ + public void close() throws SQLException { + // return the connection back into the cache. + pooledConnection.returnPreparedStatement(this); + } + + /** + * Add the last binding for batch execution. + */ + public void addBatch() throws SQLException { + try { + pstmt.addBatch(); + } catch (SQLException e) { + // we got an error... need to check this + // connection before returning it + pooledConnection.addError(e); + throw e; + } + } + + /** + * Clear parameters. + */ + public void clearParameters() throws SQLException { + try { + pstmt.clearParameters(); + } catch (SQLException e) { + // we got an error... need to check + // this connection before returning it + pooledConnection.addError(e); + throw e; + } + } + + /** + * execute the statement. + */ + public boolean execute() throws SQLException { + try { + return pstmt.execute(); + } catch (SQLException e) { + // we got an error... need to check + // this connection before returning it + pooledConnection.addError(e); + throw e; + } + } + + /** + * Execute teh query. + */ + public ResultSet executeQuery() throws SQLException { + try { + return pstmt.executeQuery(); + } catch (SQLException e) { + // we got an error... need to check + // this connection before returning it + pooledConnection.addError(e); + throw e; + } + } + + /** + * Execute the dml statement. + */ + public int executeUpdate() throws SQLException { + try { + return pstmt.executeUpdate(); + } catch (SQLException e) { + // we got an error... need to check + // this connection before returning it + pooledConnection.addError(e); + throw e; + } + } + + /** + * Return the MetaData for the query. + */ + public ResultSetMetaData getMetaData() throws SQLException { + try { + return pstmt.getMetaData(); + } catch (SQLException e) { + // we got an error... need to check + // this connection before returning it + pooledConnection.addError(e); + throw e; + } + } + + /** + * Standard PreparedStatement method execution. + */ + public ParameterMetaData getParameterMetaData() throws SQLException { + return pstmt.getParameterMetaData(); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setArray(int i, Array x) throws SQLException { + pstmt.setArray(i, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + pstmt.setAsciiStream(parameterIndex, x, length); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { + pstmt.setBigDecimal(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + pstmt.setBinaryStream(parameterIndex, x, length); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setBlob(int i, Blob x) throws SQLException { + pstmt.setBlob(i, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setBoolean(int parameterIndex, boolean x) throws SQLException { + pstmt.setBoolean(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setByte(int parameterIndex, byte x) throws SQLException { + pstmt.setByte(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setBytes(int parameterIndex, byte[] x) throws SQLException { + pstmt.setBytes(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setCharacterStream(int parameterIndex, Reader reader, int length) + throws SQLException { + pstmt.setCharacterStream(parameterIndex, reader, length); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setClob(int i, Clob x) throws SQLException { + pstmt.setClob(i, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setDate(int parameterIndex, Date x) throws SQLException { + pstmt.setDate(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { + pstmt.setDate(parameterIndex, x, cal); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setDouble(int parameterIndex, double x) throws SQLException { + pstmt.setDouble(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setFloat(int parameterIndex, float x) throws SQLException { + pstmt.setFloat(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setInt(int parameterIndex, int x) throws SQLException { + pstmt.setInt(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setLong(int parameterIndex, long x) throws SQLException { + pstmt.setLong(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setNull(int parameterIndex, int sqlType) throws SQLException { + pstmt.setNull(parameterIndex, sqlType); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setNull(int paramIndex, int sqlType, String typeName) throws SQLException { + pstmt.setNull(paramIndex, sqlType, typeName); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setObject(int parameterIndex, Object x) throws SQLException { + pstmt.setObject(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + pstmt.setObject(parameterIndex, x, targetSqlType); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) + throws SQLException { + pstmt.setObject(parameterIndex, x, targetSqlType, scale); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setRef(int i, Ref x) throws SQLException { + pstmt.setRef(i, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setShort(int parameterIndex, short x) throws SQLException { + pstmt.setShort(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setString(int parameterIndex, String x) throws SQLException { + pstmt.setString(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setTime(int parameterIndex, Time x) throws SQLException { + pstmt.setTime(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { + pstmt.setTime(parameterIndex, x, cal); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { + pstmt.setTimestamp(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { + pstmt.setTimestamp(parameterIndex, x, cal); + } + + /** + * Standard PreparedStatement method execution. + * @deprecated + */ + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + pstmt.setUnicodeStream(parameterIndex, x, length); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setURL(int parameterIndex, URL x) throws SQLException { + pstmt.setURL(parameterIndex, x); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedStatement.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedStatement.java index 8d3ab949a..cc22e1c22 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedStatement.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedStatement.java @@ -1,345 +1,328 @@ -/** - * 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.lib.sql; - -import com.avaje.ebeaninternal.jdbc.PreparedStatementDelegator; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.SQLWarning; - -/** - * Implements the Statement methods for ExtendedPreparedStatement. - *

- * PreparedStatements should always be used and the intention is that there - * should be no use of Statement at all. The implementation here is generally - * for the case where someone uses the Statement api on an ExtendedPreparedStatement. - *

- */ -public abstract class ExtendedStatement extends PreparedStatementDelegator -{ - - /** - * The pooled connection this Statement belongs to. - */ - protected final PooledConnection pooledConnection; - - /** - * The underlying Statement that this object wraps. - */ - protected final PreparedStatement pstmt; - - /** - * Create the ExtendedStatement for a given pooledConnection. - */ - public ExtendedStatement(PooledConnection pooledConnection, PreparedStatement pstmt) { - super(pstmt); - - this.pooledConnection = pooledConnection; - this.pstmt = pstmt; - } - - /** - * Put the statement back into the statement cache. - */ - public abstract void close() throws SQLException; - - /** - * Return the underlying connection. - */ - public Connection getConnection() throws SQLException { - try { - return pstmt.getConnection(); - } catch (SQLException e) { - pooledConnection.addError(e); - throw e; - } - } - - /** - * Add the sql for batch execution. - */ - public void addBatch(String sql) throws SQLException { - try { - pooledConnection.setLastStatement(sql); - pstmt.addBatch(sql); - } catch (SQLException e) { - pooledConnection.addError(e); - throw e; - } - } - - /** - * Execute the sql. - */ - public boolean execute(String sql) throws SQLException { - try { - pooledConnection.setLastStatement(sql); - return pstmt.execute(sql); - } catch (SQLException e) { - pooledConnection.addError(e); - throw e; - } - } - - /** - * Execute the query. - */ - public ResultSet executeQuery(String sql) throws SQLException { - try { - pooledConnection.setLastStatement(sql); - return pstmt.executeQuery(sql); - } catch (SQLException e) { - pooledConnection.addError(e); - throw e; - } - } - - /** - * Execute the dml sql. - */ - public int executeUpdate(String sql) throws SQLException { - try { - pooledConnection.setLastStatement(sql); - return pstmt.executeUpdate(sql); - } catch (SQLException e) { - pooledConnection.addError(e); - throw e; - } - } - - /** - * Standard Statement method call. - */ - public int[] executeBatch() throws SQLException { - return pstmt.executeBatch(); - } - - /** - * Standard Statement method call. - */ - public void cancel() throws SQLException { - pstmt.cancel(); - } - - /** - * Standard Statement method call. - */ - public void clearBatch() throws SQLException { - pstmt.clearBatch(); - } - - /** - * Standard Statement method call. - */ - public void clearWarnings() throws SQLException { - pstmt.clearWarnings(); - } - - /** - * Standard Statement method call. - */ - public int getFetchDirection() throws SQLException { - return pstmt.getFetchDirection(); - } - - /** - * Standard Statement method call. - */ - public int getFetchSize() throws SQLException { - return pstmt.getFetchSize(); - } - - /** - * Standard Statement method call. - */ - public int getMaxFieldSize() throws SQLException { - return pstmt.getMaxFieldSize(); - } - - /** - * Standard Statement method call. - */ - public int getMaxRows() throws SQLException { - return pstmt.getMaxRows(); - } - - /** - * Standard Statement method call. - */ - public boolean getMoreResults() throws SQLException { - return pstmt.getMoreResults(); - } - - /** - * Standard Statement method call. - */ - public int getQueryTimeout() throws SQLException { - return pstmt.getQueryTimeout(); - } - - /** - * Standard Statement method call. - */ - public ResultSet getResultSet() throws SQLException { - return pstmt.getResultSet(); - } - - /** - * Standard Statement method call. - */ - public int getResultSetConcurrency() throws SQLException { - return pstmt.getResultSetConcurrency(); - } - - /** - * Standard Statement method call. - */ - public int getResultSetType() throws SQLException { - return pstmt.getResultSetType(); - } - - /** - * Standard Statement method call. - */ - public int getUpdateCount() throws SQLException { - return pstmt.getUpdateCount(); - } - - /** - * Standard Statement method call. - */ - public SQLWarning getWarnings() throws SQLException { - return pstmt.getWarnings(); - } - - /** - * Standard Statement method call. - */ - public void setCursorName(String name) throws SQLException { - pstmt.setCursorName(name); - } - - /** - * Standard Statement method call. - */ - public void setEscapeProcessing(boolean enable) throws SQLException { - pstmt.setEscapeProcessing(enable); - } - - /** - * Standard Statement method call. - */ - public void setFetchDirection(int direction) throws SQLException { - pstmt.setFetchDirection(direction); - } - - /** - * Standard Statement method call. - */ - public void setFetchSize(int rows) throws SQLException { - pstmt.setFetchSize(rows); - } - - /** - * Standard Statement method call. - */ - public void setMaxFieldSize(int max) throws SQLException { - pstmt.setMaxFieldSize(max); - } - - /** - * Standard Statement method call. - */ - public void setMaxRows(int max) throws SQLException { - pstmt.setMaxRows(max); - } - - /** - * Standard Statement method call. - */ - public void setQueryTimeout(int seconds) throws SQLException { - pstmt.setQueryTimeout(seconds); - } - - /** - * Standard Statement method call. - */ - public boolean getMoreResults(int i) throws SQLException { - return pstmt.getMoreResults(i); - } - - /** - * Standard Statement method call. - */ - public ResultSet getGeneratedKeys() throws SQLException { - return pstmt.getGeneratedKeys(); - } - - /** - * Standard Statement method call. - */ - public int executeUpdate(String s, int i) throws SQLException { - return pstmt.executeUpdate(s, i); - } - - /** - * Standard Statement method call. - */ - public int executeUpdate(String s, int[] i) throws SQLException { - return pstmt.executeUpdate(s, i); - } - - /** - * Standard Statement method call. - */ - public int executeUpdate(String s, String[] i) throws SQLException { - return pstmt.executeUpdate(s, i); - } - - /** - * Standard Statement method call. - */ - public boolean execute(String s, int i) throws SQLException { - return pstmt.execute(s, i); - } - - /** - * Standard Statement method call. - */ - public boolean execute(String s, int[] i) throws SQLException { - return pstmt.execute(s, i); - } - - /** - * Standard Statement method call. - */ - public boolean execute(String s, String[] i) throws SQLException { - return pstmt.execute(s, i); - } - - /** - * Standard Statement method call. - */ - public int getResultSetHoldability() throws SQLException { - return pstmt.getResultSetHoldability(); - } - -} +package com.avaje.ebeaninternal.server.lib.sql; + +import com.avaje.ebeaninternal.jdbc.PreparedStatementDelegator; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLWarning; + +/** + * Implements the Statement methods for ExtendedPreparedStatement. + *

+ * PreparedStatements should always be used and the intention is that there + * should be no use of Statement at all. The implementation here is generally + * for the case where someone uses the Statement api on an ExtendedPreparedStatement. + *

+ */ +public abstract class ExtendedStatement extends PreparedStatementDelegator +{ + + /** + * The pooled connection this Statement belongs to. + */ + protected final PooledConnection pooledConnection; + + /** + * The underlying Statement that this object wraps. + */ + protected final PreparedStatement pstmt; + + /** + * Create the ExtendedStatement for a given pooledConnection. + */ + public ExtendedStatement(PooledConnection pooledConnection, PreparedStatement pstmt) { + super(pstmt); + + this.pooledConnection = pooledConnection; + this.pstmt = pstmt; + } + + /** + * Put the statement back into the statement cache. + */ + public abstract void close() throws SQLException; + + /** + * Return the underlying connection. + */ + public Connection getConnection() throws SQLException { + try { + return pstmt.getConnection(); + } catch (SQLException e) { + pooledConnection.addError(e); + throw e; + } + } + + /** + * Add the sql for batch execution. + */ + public void addBatch(String sql) throws SQLException { + try { + pooledConnection.setLastStatement(sql); + pstmt.addBatch(sql); + } catch (SQLException e) { + pooledConnection.addError(e); + throw e; + } + } + + /** + * Execute the sql. + */ + public boolean execute(String sql) throws SQLException { + try { + pooledConnection.setLastStatement(sql); + return pstmt.execute(sql); + } catch (SQLException e) { + pooledConnection.addError(e); + throw e; + } + } + + /** + * Execute the query. + */ + public ResultSet executeQuery(String sql) throws SQLException { + try { + pooledConnection.setLastStatement(sql); + return pstmt.executeQuery(sql); + } catch (SQLException e) { + pooledConnection.addError(e); + throw e; + } + } + + /** + * Execute the dml sql. + */ + public int executeUpdate(String sql) throws SQLException { + try { + pooledConnection.setLastStatement(sql); + return pstmt.executeUpdate(sql); + } catch (SQLException e) { + pooledConnection.addError(e); + throw e; + } + } + + /** + * Standard Statement method call. + */ + public int[] executeBatch() throws SQLException { + return pstmt.executeBatch(); + } + + /** + * Standard Statement method call. + */ + public void cancel() throws SQLException { + pstmt.cancel(); + } + + /** + * Standard Statement method call. + */ + public void clearBatch() throws SQLException { + pstmt.clearBatch(); + } + + /** + * Standard Statement method call. + */ + public void clearWarnings() throws SQLException { + pstmt.clearWarnings(); + } + + /** + * Standard Statement method call. + */ + public int getFetchDirection() throws SQLException { + return pstmt.getFetchDirection(); + } + + /** + * Standard Statement method call. + */ + public int getFetchSize() throws SQLException { + return pstmt.getFetchSize(); + } + + /** + * Standard Statement method call. + */ + public int getMaxFieldSize() throws SQLException { + return pstmt.getMaxFieldSize(); + } + + /** + * Standard Statement method call. + */ + public int getMaxRows() throws SQLException { + return pstmt.getMaxRows(); + } + + /** + * Standard Statement method call. + */ + public boolean getMoreResults() throws SQLException { + return pstmt.getMoreResults(); + } + + /** + * Standard Statement method call. + */ + public int getQueryTimeout() throws SQLException { + return pstmt.getQueryTimeout(); + } + + /** + * Standard Statement method call. + */ + public ResultSet getResultSet() throws SQLException { + return pstmt.getResultSet(); + } + + /** + * Standard Statement method call. + */ + public int getResultSetConcurrency() throws SQLException { + return pstmt.getResultSetConcurrency(); + } + + /** + * Standard Statement method call. + */ + public int getResultSetType() throws SQLException { + return pstmt.getResultSetType(); + } + + /** + * Standard Statement method call. + */ + public int getUpdateCount() throws SQLException { + return pstmt.getUpdateCount(); + } + + /** + * Standard Statement method call. + */ + public SQLWarning getWarnings() throws SQLException { + return pstmt.getWarnings(); + } + + /** + * Standard Statement method call. + */ + public void setCursorName(String name) throws SQLException { + pstmt.setCursorName(name); + } + + /** + * Standard Statement method call. + */ + public void setEscapeProcessing(boolean enable) throws SQLException { + pstmt.setEscapeProcessing(enable); + } + + /** + * Standard Statement method call. + */ + public void setFetchDirection(int direction) throws SQLException { + pstmt.setFetchDirection(direction); + } + + /** + * Standard Statement method call. + */ + public void setFetchSize(int rows) throws SQLException { + pstmt.setFetchSize(rows); + } + + /** + * Standard Statement method call. + */ + public void setMaxFieldSize(int max) throws SQLException { + pstmt.setMaxFieldSize(max); + } + + /** + * Standard Statement method call. + */ + public void setMaxRows(int max) throws SQLException { + pstmt.setMaxRows(max); + } + + /** + * Standard Statement method call. + */ + public void setQueryTimeout(int seconds) throws SQLException { + pstmt.setQueryTimeout(seconds); + } + + /** + * Standard Statement method call. + */ + public boolean getMoreResults(int i) throws SQLException { + return pstmt.getMoreResults(i); + } + + /** + * Standard Statement method call. + */ + public ResultSet getGeneratedKeys() throws SQLException { + return pstmt.getGeneratedKeys(); + } + + /** + * Standard Statement method call. + */ + public int executeUpdate(String s, int i) throws SQLException { + return pstmt.executeUpdate(s, i); + } + + /** + * Standard Statement method call. + */ + public int executeUpdate(String s, int[] i) throws SQLException { + return pstmt.executeUpdate(s, i); + } + + /** + * Standard Statement method call. + */ + public int executeUpdate(String s, String[] i) throws SQLException { + return pstmt.executeUpdate(s, i); + } + + /** + * Standard Statement method call. + */ + public boolean execute(String s, int i) throws SQLException { + return pstmt.execute(s, i); + } + + /** + * Standard Statement method call. + */ + public boolean execute(String s, int[] i) throws SQLException { + return pstmt.execute(s, i); + } + + /** + * Standard Statement method call. + */ + public boolean execute(String s, String[] i) throws SQLException { + return pstmt.execute(s, i); + } + + /** + * Standard Statement method call. + */ + public int getResultSetHoldability() throws SQLException { + return pstmt.getResultSetHoldability(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/FreeConnectionBuffer.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/FreeConnectionBuffer.java index b97cd387f..23f160091 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/FreeConnectionBuffer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/FreeConnectionBuffer.java @@ -1,157 +1,138 @@ -/** - * 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.lib.sql; - -import java.util.ArrayList; -import java.util.List; - -/** - * A buffer designed especially to hold free pooled connections. - *

- * It is circular in nature. - *

- *

- * All thread safety controlled externally (by PooledConnectionQueue). - *

- * - * @author rbygrave - * - */ -class FreeConnectionBuffer { - - private PooledConnection[] conns; - - private int removeIndex; - - private int addIndex; - - /** - * The current number of connections in the buffer - */ - private int size; - - protected FreeConnectionBuffer(int capacity) { - this.conns = new PooledConnection[capacity]; - } - - protected int getCapacity() { - return conns.length; - } - - protected int size() { - return size; - } - - protected boolean isEmpty() { - return size == 0; - } - - /** - * Add at connection. - */ - protected void add(PooledConnection pc) { - conns[addIndex] = pc; - addIndex = inc(addIndex); - ++size; - } - - /** - * Remove a connection at current remove position. - */ - protected PooledConnection remove() { - final PooledConnection[] items = this.conns; - PooledConnection pc = items[removeIndex]; - items[removeIndex] = null; - removeIndex = inc(removeIndex); - --size; - return pc; - } - - /** - * Return a shallow copy of the free connections. - */ - protected List getShallowCopy() { - - List copy = new ArrayList(conns.length); - for (int i = 0; i < conns.length; i++) { - if (conns[i] != null){ - copy.add(conns[i]); - } - } - return copy; - } - - /** - * Set the free list to be the connections in this copy. This is done after - * unused connections have been trimmed. - *

- * Not a particularly performant approach but this should not be called very - * often - *

- */ - protected void setShallowCopy(List copy) { - - // reset to empty state - this.removeIndex = 0; - this.addIndex = 0; - this.size = 0; - - // null all the current connections - for (int i = 0; i < conns.length; i++) { - conns[i] = null; - } - - // add connections from the copy - for (int i = 0; i < copy.size(); i++) { - add(copy.get(i)); - } - } - - /** - * Increase the capacity of the buffer. This is a relatively expensive - * operation but should occur very infrequently. - */ - protected void setCapacity(int newCapacity) { - if (newCapacity > conns.length){ - - List copy = getShallowCopy(); - - // reset to empty state - this.removeIndex = 0; - this.addIndex = 0; - this.size = 0; - - this.conns = new PooledConnection[newCapacity]; - - // add the connections back from the copy - for (int i = 0; i < copy.size(); i++) { - add(copy.get(i)); - } - } - } - - /** - * Circularly increment i. - */ - private final int inc(int i) { - return (++i == conns.length)? 0 : i; - } - -} +package com.avaje.ebeaninternal.server.lib.sql; + +import java.util.ArrayList; +import java.util.List; + +/** + * A buffer designed especially to hold free pooled connections. + *

+ * It is circular in nature. + *

+ *

+ * All thread safety controlled externally (by PooledConnectionQueue). + *

+ * + * @author rbygrave + * + */ +class FreeConnectionBuffer { + + private PooledConnection[] conns; + + private int removeIndex; + + private int addIndex; + + /** + * The current number of connections in the buffer + */ + private int size; + + protected FreeConnectionBuffer(int capacity) { + this.conns = new PooledConnection[capacity]; + } + + protected int getCapacity() { + return conns.length; + } + + protected int size() { + return size; + } + + protected boolean isEmpty() { + return size == 0; + } + + /** + * Add at connection. + */ + protected void add(PooledConnection pc) { + conns[addIndex] = pc; + addIndex = inc(addIndex); + ++size; + } + + /** + * Remove a connection at current remove position. + */ + protected PooledConnection remove() { + final PooledConnection[] items = this.conns; + PooledConnection pc = items[removeIndex]; + items[removeIndex] = null; + removeIndex = inc(removeIndex); + --size; + return pc; + } + + /** + * Return a shallow copy of the free connections. + */ + protected List getShallowCopy() { + + List copy = new ArrayList(conns.length); + for (int i = 0; i < conns.length; i++) { + if (conns[i] != null){ + copy.add(conns[i]); + } + } + return copy; + } + + /** + * Set the free list to be the connections in this copy. This is done after + * unused connections have been trimmed. + *

+ * Not a particularly performant approach but this should not be called very + * often + *

+ */ + protected void setShallowCopy(List copy) { + + // reset to empty state + this.removeIndex = 0; + this.addIndex = 0; + this.size = 0; + + // null all the current connections + for (int i = 0; i < conns.length; i++) { + conns[i] = null; + } + + // add connections from the copy + for (int i = 0; i < copy.size(); i++) { + add(copy.get(i)); + } + } + + /** + * Increase the capacity of the buffer. This is a relatively expensive + * operation but should occur very infrequently. + */ + protected void setCapacity(int newCapacity) { + if (newCapacity > conns.length){ + + List copy = getShallowCopy(); + + // reset to empty state + this.removeIndex = 0; + this.addIndex = 0; + this.size = 0; + + this.conns = new PooledConnection[newCapacity]; + + // add the connections back from the copy + for (int i = 0; i < copy.size(); i++) { + add(copy.get(i)); + } + } + } + + /** + * Circularly increment i. + */ + private final int inc(int i) { + return (++i == conns.length)? 0 : i; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PooledConnection.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PooledConnection.java index 44189ccd1..17c87718a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PooledConnection.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PooledConnection.java @@ -1,961 +1,944 @@ -/** - * 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.lib.sql; - -import com.avaje.ebeaninternal.jdbc.ConnectionDelegator; - -import java.sql.CallableStatement; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.Savepoint; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Is a connection that belongs to a DataSourcePool. - * - *

- * It is designed to be part of DataSourcePool. Closing the connection puts it - * back into the pool. - *

- * - *

- * It defaults autoCommit and Transaction Isolation to the defaults of the - * DataSourcePool. - *

- * - *

- * It has caching of Statements and PreparedStatements. Remembers the last - * statement that was executed. Keeps statistics on how long it is in use. - *

- */ -public class PooledConnection extends ConnectionDelegator -{ - - private static final Logger logger = Logger.getLogger(PooledConnection.class.getName()); - - private static String IDLE_CONNECTION_ACCESSED_ERROR = "Pooled Connection has been accessed whilst idle in the pool, via method: "; - - /** - * Set when connection is idle in the pool. In general when in the pool the - * connection should not be modified. - */ - static final int STATUS_IDLE = 88; - - /** - * Set when connection given to client. - */ - static final int STATUS_ACTIVE = 89; - - /** - * Set when commit() or rollback() called. - */ - static final int STATUS_ENDED = 87; - - /** - * Name used to identify the PooledConnection for logging. - */ - final String name; - - /** - * The pool this connection belongs to. - */ - final DataSourcePool pool; - - /** - * The underlying connection. - */ - final Connection connection; - - /** - * The time this connection was created. - */ - final long creationTime; - - /** - * Cache of the PreparedStatements - */ - final PstmtCache pstmtCache; - - final Object pstmtMonitor = new Object(); - - /** - * The status of the connection. IDLE, ACTIVE or ENDED. - */ - int status = STATUS_IDLE; - - /** - * Set this to true if the connection will be busy for a long time. - *

- * This means it should skip the suspected connection pool leak checking. - *

- */ - boolean longRunning; - - /** - * Flag to indicate that this connection had errors and should be checked to - * make sure it is okay. - */ - boolean hadErrors; - - /** - * The last start time. When the connection was given to a thread. - */ - long startUseTime; - - /** - * The last end time of this connection. This is to calculate the usage - * time. - */ - long lastUseTime; - - /** - * The last statement executed by this connection. - */ - String lastStatement; - - /** - * The number of hits against the preparedStatement cache. - */ - int pstmtHitCounter; - - /** - * The number of misses against the preparedStatement cache. - */ - int pstmtMissCounter; - - /** - * The non avaje method that created the connection. - */ - String createdByMethod; - - /** - * Used to find connection pool leaks. - */ - StackTraceElement[] stackTrace; - - int maxStackTrace; - - /** - * Slot position in the BusyConnectionBuffer. - */ - int slotId; - - /** - * Construct the connection that can refer back to the pool it belongs to. - *

- * close() will return the connection back to the pool , while - * closeDestroy() will close() the underlining connection properly. - *

- */ - public PooledConnection(DataSourcePool pool, int uniqueId, Connection connection) throws SQLException { - super(connection); - - this.pool = pool; - this.connection = connection; - this.name = pool.getName() + "." + uniqueId; - this.pstmtCache = new PstmtCache(name, pool.getPstmtCacheSize()); - this.maxStackTrace = pool.getMaxStackTraceSize(); - this.creationTime = System.currentTimeMillis(); - this.lastUseTime = creationTime; - } - - /** - * For testing the pool without real connections. - */ - protected PooledConnection(String name) { - super(null); - this.name = name; - this.pool = null; - this.connection = null; - this.pstmtCache = null; - this.maxStackTrace = 0; - this.creationTime = System.currentTimeMillis(); - this.lastUseTime = creationTime; - } - - /** - * Return the slot position in the busy buffer. - */ - public int getSlotId() { - return slotId; - } - - /** - * Set the slot position in the busy buffer. - */ - public void setSlotId(int slotId) { - this.slotId = slotId; - } - - /** - * Return the DataSourcePool that this connection belongs to. - */ - public DataSourcePool getDataSourcePool() { - return pool; - } - - /** - * Return the time the connection was created. - */ - public long getCreationTime() { - return creationTime; - } - - /** - * Return a string to identify the connection. - */ - public String getName() { - return name; - } - - public String toString() { - return name; - } - - public String getDescription() { - return "name["+name+"] startTime["+getStartUseTime()+"] stmt["+getLastStatement()+"] createdBy["+getCreatedByMethod()+"]"; - } - - public String getStatistics() { - return "name["+name+"] startTime["+getStartUseTime()+"] pstmtHits["+pstmtHitCounter+"] pstmtMiss["+pstmtMissCounter+"] "+pstmtCache.getDescription(); - } - - /** - * Return true if the connection should be treated as long running (skip connection pool leak check). - */ - public boolean isLongRunning() { - return longRunning; - } - - /** - * Set this to true if the connection is a long running connection and should skip the - * 'suspected connection pool leak' checking. - */ - public void setLongRunning(boolean longRunning) { - this.longRunning = longRunning; - } - - /** - * Close the connection fully NOT putting in back into the pool. - *

- * The logErrors parameter exists so that expected errors are not logged - * such as when the database is known to be down. - *

- * - * @param logErrors - * if false then don't log errors when closing - */ - public void closeConnectionFully(boolean logErrors) { - - //pool.removeConnection(this); - - String msg = "Closing Connection[" + getName() + "]" + " psReuse[" + pstmtHitCounter - + "] psCreate[" + pstmtMissCounter + "] psSize[" + pstmtCache.size() + "]"; - - logger.info(msg); - - try { - if (connection.isClosed()) { - msg = "Closing Connection[" + getName() + "] that is already closed?"; - logger.log(Level.SEVERE, msg); - return; - } - } catch (SQLException ex) { - if (logErrors) { - msg = "Error when fully closing connection [" + getName() + "]"; - logger.log(Level.SEVERE, msg, ex); - } - } - - try { - Iterator psi = pstmtCache.values().iterator(); - while (psi.hasNext()) { - ExtendedPreparedStatement ps = (ExtendedPreparedStatement) psi.next(); - ps.closeDestroy(); - } - - } catch (SQLException ex) { - if (logErrors) { - logger.log(Level.WARNING, "Error when closing connection Statements", ex); - } - } - - try { - connection.close(); - - } catch (SQLException ex) { - if (logErrors) { - msg = "Error when fully closing connection [" + getName() + "]"; - logger.log(Level.SEVERE, msg, ex); - } - } - } - - /** - * A Least Recently used cache of PreparedStatements. - */ - public PstmtCache getPstmtCache() { - return pstmtCache; - } - - /** - * Creates a wrapper ExtendedStatement so that I can get the executed sql. I - * want to do this so that I can get the slowest query statments etc, and - * log that information. - */ - public Statement createStatement() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "createStatement()"); - } - try { - return connection.createStatement(); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public Statement createStatement(int resultSetType, int resultSetConcurreny) - throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "createStatement()"); - } - try { - return connection.createStatement(resultSetType, resultSetConcurreny); - - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - /** - * Return a PreparedStatement back into the cache. - */ - protected void returnPreparedStatement(ExtendedPreparedStatement pstmt) { - - synchronized (pstmtMonitor) { - ExtendedPreparedStatement alreadyInCache = pstmtCache.get(pstmt.getCacheKey()); - - if (alreadyInCache == null) { - // add the returning prepared statement to the cache. - // Note that the LRUCache will automatically close fully old unused - // PStmts when the cache has hit its maximum size. - pstmtCache.put(pstmt.getCacheKey(), pstmt); - - } else { - try { - // if a entry in the cache exists for the exact same SQL... - // then remove it from the cache and close it fully. - // Only having one PreparedStatement per unique SQL - // statement - pstmt.closeDestroy(); - - } catch (SQLException e) { - logger.log(Level.SEVERE, "Error closing Pstmt", e); - } - } - } - } - - /** - * This will try to use a cache of PreparedStatements. - */ - public PreparedStatement prepareStatement(String sql, int returnKeysFlag) throws SQLException { - String cacheKey = sql + returnKeysFlag; - return prepareStatement(sql, true, returnKeysFlag, cacheKey); - } - - /** - * This will try to use a cache of PreparedStatements. - */ - public PreparedStatement prepareStatement(String sql) throws SQLException { - return prepareStatement(sql, false, 0, sql); - } - - /** - * This will try to use a cache of PreparedStatements. - */ - private PreparedStatement prepareStatement(String sql, boolean useFlag, int flag, String cacheKey) throws SQLException { - - if (status == STATUS_IDLE) { - String m = IDLE_CONNECTION_ACCESSED_ERROR + "prepareStatement()"; - throw new SQLException(m); - } - try { - synchronized (pstmtMonitor) { - lastStatement = sql; - - // try to get a matching cached PStmt from the cache. - ExtendedPreparedStatement pstmt = pstmtCache.remove(cacheKey); - - if (pstmt != null) { - pstmtHitCounter++; - return pstmt; - } - - // create a new PreparedStatement - pstmtMissCounter++; - PreparedStatement actualPstmt; - if (useFlag) { - actualPstmt = connection.prepareStatement(sql, flag); - } else { - actualPstmt = connection.prepareStatement(sql); - } - return new ExtendedPreparedStatement(this, actualPstmt, sql, cacheKey); - } - - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurreny) - throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareStatement()"); - } - try { - // no caching when creating PreparedStatements this way - pstmtMissCounter++; - lastStatement = sql; - return connection.prepareStatement(sql, resultSetType, resultSetConcurreny); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - /** - * Reset the connection for returning to the client. Resets the status, - * startUseTime and hadErrors. - */ - protected void resetForUse() { - this.status = STATUS_ACTIVE; - this.startUseTime = System.currentTimeMillis(); - this.createdByMethod = null; - this.lastStatement = null; - this.hadErrors = false; - this.longRunning = false; - } - - /** - * When an error occurs during use add it the connection. - *

- * Any PooledConnection that has an error is checked to make sure it works - * before it is placed back into the connection pool. - *

- */ - public void addError(Throwable e) { - hadErrors = true; - } - - /** - * Returns true if the connect threw any errors during use. - *

- * Connections with errors are testing to make sure they are still good - * before putting them back into the pool. - *

- */ - public boolean hadErrors() { - return hadErrors; - } - - /** - * close the connection putting it back into the connection pool. - *

- * Note that to ensure that the next transaction starts at the correct time - * a commit() or rollback() should be called. If neither has occured at this - * time then a rollback() is used (to end the transaction). - *

- *

- * To close the connection fully use closeConnectionFully(). - *

- */ - public void close() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "close()"); - } - - if (hadErrors) { - if (!pool.validateConnection(this)) { - // the connection is BAD, close it and test the pool - closeConnectionFully(false); - pool.checkDataSource(); - return; - } - } - - try { - // reset the autoCommit back if client code changed it - if (connection.getAutoCommit() != pool.getAutoCommit()) { - connection.setAutoCommit(pool.getAutoCommit()); - } - // Generally resetting Isolation level seems expensive. - // Hence using resetIsolationReadOnlyRequired flag - // performance reasons. - if (resetIsolationReadOnlyRequired) { - resetIsolationReadOnly(); - resetIsolationReadOnlyRequired = false; - } - - // the connection is assumed GOOD so put it back in the pool - lastUseTime = System.currentTimeMillis(); - // connection.clearWarnings(); - status = STATUS_IDLE; - pool.returnConnection(this); - - } catch (Exception ex) { - // the connection is BAD, close it and test the pool - closeConnectionFully(false); - pool.checkDataSource(); - } - } - - private void resetIsolationReadOnly() throws SQLException { - // reset the transaction isolation if the client code changed it - if (connection.getTransactionIsolation() != pool.getTransactionIsolation()) { - connection.setTransactionIsolation(pool.getTransactionIsolation()); - } - // reset readonly to false - if (connection.isReadOnly()) { - connection.setReadOnly(false); - } - } - - protected void finalize() throws Throwable { - try { - if (connection != null && !connection.isClosed()) { - // connect leak? - String msg = "Closing Connection[" + getName() + "] on finalize()."; - logger.warning(msg); - closeConnectionFully(false); - } - } catch (Exception e) { - logger.log(Level.SEVERE, null, e); - } - super.finalize(); - } - - /** - * Return the time the connection was passed to the client code. - *

- * Used to detect busy connections that could be leaks. - *

- */ - public long getStartUseTime() { - return startUseTime; - } - - /** - * Returns the time the connection was last used. - *

- * Used to close connections that have been idle for some time. Typically 5 - * minutes. - *

- */ - public long getLastUsedTime() { - return lastUseTime; - } - - /** - * Returns the last sql statement executed. - */ - public String getLastStatement() { - return lastStatement; - } - - /** - * Called by ExtendedStatement to trace the sql being executed. - *

- * Note with addBatch() this will not really work. - *

- */ - protected void setLastStatement(String lastStatement) { - this.lastStatement = lastStatement; - if (logger.isLoggable(Level.FINER)) { - logger.finer(".setLastStatement[" + lastStatement + "]"); - } - } - - boolean resetIsolationReadOnlyRequired = false; - - /** - * Also note the read only status needs to be reset when put back into the - * pool. - */ - public void setReadOnly(boolean readOnly) throws SQLException { - // A bit loose not checking for STATUS_IDLE - // if (status == STATUS_IDLE) { - // throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + - // "setReadOnly()"); - // } - resetIsolationReadOnlyRequired = true; - connection.setReadOnly(readOnly); - } - - /** - * Also note the Isolation level needs to be reset when put back into the - * pool. - */ - public void setTransactionIsolation(int level) throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setTransactionIsolation()"); - } - try { - resetIsolationReadOnlyRequired = true; - connection.setTransactionIsolation(level); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - // - // - // Simple wrapper methods which pass a method call onto the acutal - // connection object. These methods are safe-guarded to prevent use of - // the methods whilst the connection is in the connection pool. - // - // - public void clearWarnings() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "clearWarnings()"); - } - connection.clearWarnings(); - } - - public void commit() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "commit()"); - } - try { - status = STATUS_ENDED; - connection.commit(); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public boolean getAutoCommit() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getAutoCommit()"); - } - return connection.getAutoCommit(); - } - - public String getCatalog() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getCatalog()"); - } - return connection.getCatalog(); - } - - public DatabaseMetaData getMetaData() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getMetaData()"); - } - return connection.getMetaData(); - } - - public int getTransactionIsolation() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getTransactionIsolation()"); - } - return connection.getTransactionIsolation(); - } - - public Map> getTypeMap() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getTypeMap()"); - } - return connection.getTypeMap(); - } - - public SQLWarning getWarnings() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getWarnings()"); - } - return connection.getWarnings(); - } - - public boolean isClosed() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "isClosed()"); - } - return connection.isClosed(); - } - - public boolean isReadOnly() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "isReadOnly()"); - } - return connection.isReadOnly(); - } - - public String nativeSQL(String sql) throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "nativeSQL()"); - } - lastStatement = sql; - return connection.nativeSQL(sql); - } - - public CallableStatement prepareCall(String sql) throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareCall()"); - } - lastStatement = sql; - return connection.prepareCall(sql); - } - - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurreny) - throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareCall()"); - } - lastStatement = sql; - return connection.prepareCall(sql, resultSetType, resultSetConcurreny); - } - - public void rollback() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "rollback()"); - } - try { - status = STATUS_ENDED; - connection.rollback(); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public void setAutoCommit(boolean autoCommit) throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setAutoCommit()"); - } - try { - connection.setAutoCommit(autoCommit); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public void setCatalog(String catalog) throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setCatalog()"); - } - connection.setCatalog(catalog); - } - - public void setTypeMap(Map> map) throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setTypeMap()"); - } - connection.setTypeMap(map); - } - - public Savepoint setSavepoint() throws SQLException { - try { - return connection.setSavepoint(); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public Savepoint setSavepoint(String savepointName) throws SQLException { - try { - return connection.setSavepoint(savepointName); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public void rollback(Savepoint sp) throws SQLException { - try { - connection.rollback(sp); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public void releaseSavepoint(Savepoint sp) throws SQLException { - try { - connection.releaseSavepoint(sp); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public void setHoldability(int i) throws SQLException { - try { - connection.setHoldability(i); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public int getHoldability() throws SQLException { - try { - return connection.getHoldability(); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public Statement createStatement(int i, int x, int y) throws SQLException { - try { - return connection.createStatement(i, x, y); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public PreparedStatement prepareStatement(String s, int i, int x, int y) throws SQLException { - try { - return connection.prepareStatement(s, i, x, y); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public PreparedStatement prepareStatement(String s, int[] i) throws SQLException { - try { - return connection.prepareStatement(s, i); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public PreparedStatement prepareStatement(String s, String[] s2) throws SQLException { - try { - return connection.prepareStatement(s, s2); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public CallableStatement prepareCall(String s, int i, int x, int y) throws SQLException { - try { - return connection.prepareCall(s, i, x, y); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - /** - * Returns the method that created the connection. - *

- * Used to help finding connection pool leaks. - *

- */ - public String getCreatedByMethod() { - if (createdByMethod != null) { - return createdByMethod; - } - if (stackTrace == null) { - return null; - } - - for (int j = 0; j < stackTrace.length; j++) { - String methodLine = stackTrace[j].toString(); - if (skipElement(methodLine)) { - // ignore these methods... - } else { - createdByMethod = methodLine; - return createdByMethod; - } - } - - return null; - } - - private boolean skipElement(String methodLine) { - if (methodLine.startsWith("java.lang.")) { - return true; - } else if (methodLine.startsWith("java.util.")) { - return true; - } else if (methodLine.startsWith("com.avaje.ebeaninternal.server.query.CallableQuery.")) { - // creating connection on future... - return true; - } else if (methodLine.startsWith("com.avaje.ebeaninternal.server.query.Callable")) { - // it is a future task being executed... - return false; - } else if (methodLine.startsWith("com.avaje.ebeaninternal")) { - return true; - } else { - return false; - } - } - - /** - * Set the stack trace to help find connection pool leaks. - */ - protected void setStackTrace(StackTraceElement[] stackTrace) { - this.stackTrace = stackTrace; - } - - /** - * Return the full stack trace that got the connection from the pool. You - * could use this if getCreatedByMethod() doesn't work for you. - */ - public StackTraceElement[] getStackTrace() { - - if (stackTrace == null){ - return null; - } - - // filter off the top of the stack that we are not interested in - ArrayList filteredList = new ArrayList(); - boolean include = false; - for (int i = 0; i < stackTrace.length; i++) { - if (!include && !skipElement(stackTrace[i].toString())){ - include = true; - } - if (include && filteredList.size() < maxStackTrace){ - filteredList.add(stackTrace[i]); - } - } - return filteredList.toArray(new StackTraceElement[filteredList.size()]); - - } - -} +package com.avaje.ebeaninternal.server.lib.sql; + +import com.avaje.ebeaninternal.jdbc.ConnectionDelegator; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Savepoint; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Is a connection that belongs to a DataSourcePool. + * + *

+ * It is designed to be part of DataSourcePool. Closing the connection puts it + * back into the pool. + *

+ * + *

+ * It defaults autoCommit and Transaction Isolation to the defaults of the + * DataSourcePool. + *

+ * + *

+ * It has caching of Statements and PreparedStatements. Remembers the last + * statement that was executed. Keeps statistics on how long it is in use. + *

+ */ +public class PooledConnection extends ConnectionDelegator +{ + + private static final Logger logger = Logger.getLogger(PooledConnection.class.getName()); + + private static String IDLE_CONNECTION_ACCESSED_ERROR = "Pooled Connection has been accessed whilst idle in the pool, via method: "; + + /** + * Set when connection is idle in the pool. In general when in the pool the + * connection should not be modified. + */ + static final int STATUS_IDLE = 88; + + /** + * Set when connection given to client. + */ + static final int STATUS_ACTIVE = 89; + + /** + * Set when commit() or rollback() called. + */ + static final int STATUS_ENDED = 87; + + /** + * Name used to identify the PooledConnection for logging. + */ + final String name; + + /** + * The pool this connection belongs to. + */ + final DataSourcePool pool; + + /** + * The underlying connection. + */ + final Connection connection; + + /** + * The time this connection was created. + */ + final long creationTime; + + /** + * Cache of the PreparedStatements + */ + final PstmtCache pstmtCache; + + final Object pstmtMonitor = new Object(); + + /** + * The status of the connection. IDLE, ACTIVE or ENDED. + */ + int status = STATUS_IDLE; + + /** + * Set this to true if the connection will be busy for a long time. + *

+ * This means it should skip the suspected connection pool leak checking. + *

+ */ + boolean longRunning; + + /** + * Flag to indicate that this connection had errors and should be checked to + * make sure it is okay. + */ + boolean hadErrors; + + /** + * The last start time. When the connection was given to a thread. + */ + long startUseTime; + + /** + * The last end time of this connection. This is to calculate the usage + * time. + */ + long lastUseTime; + + /** + * The last statement executed by this connection. + */ + String lastStatement; + + /** + * The number of hits against the preparedStatement cache. + */ + int pstmtHitCounter; + + /** + * The number of misses against the preparedStatement cache. + */ + int pstmtMissCounter; + + /** + * The non avaje method that created the connection. + */ + String createdByMethod; + + /** + * Used to find connection pool leaks. + */ + StackTraceElement[] stackTrace; + + int maxStackTrace; + + /** + * Slot position in the BusyConnectionBuffer. + */ + int slotId; + + /** + * Construct the connection that can refer back to the pool it belongs to. + *

+ * close() will return the connection back to the pool , while + * closeDestroy() will close() the underlining connection properly. + *

+ */ + public PooledConnection(DataSourcePool pool, int uniqueId, Connection connection) throws SQLException { + super(connection); + + this.pool = pool; + this.connection = connection; + this.name = pool.getName() + "." + uniqueId; + this.pstmtCache = new PstmtCache(name, pool.getPstmtCacheSize()); + this.maxStackTrace = pool.getMaxStackTraceSize(); + this.creationTime = System.currentTimeMillis(); + this.lastUseTime = creationTime; + } + + /** + * For testing the pool without real connections. + */ + protected PooledConnection(String name) { + super(null); + this.name = name; + this.pool = null; + this.connection = null; + this.pstmtCache = null; + this.maxStackTrace = 0; + this.creationTime = System.currentTimeMillis(); + this.lastUseTime = creationTime; + } + + /** + * Return the slot position in the busy buffer. + */ + public int getSlotId() { + return slotId; + } + + /** + * Set the slot position in the busy buffer. + */ + public void setSlotId(int slotId) { + this.slotId = slotId; + } + + /** + * Return the DataSourcePool that this connection belongs to. + */ + public DataSourcePool getDataSourcePool() { + return pool; + } + + /** + * Return the time the connection was created. + */ + public long getCreationTime() { + return creationTime; + } + + /** + * Return a string to identify the connection. + */ + public String getName() { + return name; + } + + public String toString() { + return name; + } + + public String getDescription() { + return "name["+name+"] startTime["+getStartUseTime()+"] stmt["+getLastStatement()+"] createdBy["+getCreatedByMethod()+"]"; + } + + public String getStatistics() { + return "name["+name+"] startTime["+getStartUseTime()+"] pstmtHits["+pstmtHitCounter+"] pstmtMiss["+pstmtMissCounter+"] "+pstmtCache.getDescription(); + } + + /** + * Return true if the connection should be treated as long running (skip connection pool leak check). + */ + public boolean isLongRunning() { + return longRunning; + } + + /** + * Set this to true if the connection is a long running connection and should skip the + * 'suspected connection pool leak' checking. + */ + public void setLongRunning(boolean longRunning) { + this.longRunning = longRunning; + } + + /** + * Close the connection fully NOT putting in back into the pool. + *

+ * The logErrors parameter exists so that expected errors are not logged + * such as when the database is known to be down. + *

+ * + * @param logErrors + * if false then don't log errors when closing + */ + public void closeConnectionFully(boolean logErrors) { + + //pool.removeConnection(this); + + String msg = "Closing Connection[" + getName() + "]" + " psReuse[" + pstmtHitCounter + + "] psCreate[" + pstmtMissCounter + "] psSize[" + pstmtCache.size() + "]"; + + logger.info(msg); + + try { + if (connection.isClosed()) { + msg = "Closing Connection[" + getName() + "] that is already closed?"; + logger.log(Level.SEVERE, msg); + return; + } + } catch (SQLException ex) { + if (logErrors) { + msg = "Error when fully closing connection [" + getName() + "]"; + logger.log(Level.SEVERE, msg, ex); + } + } + + try { + Iterator psi = pstmtCache.values().iterator(); + while (psi.hasNext()) { + ExtendedPreparedStatement ps = (ExtendedPreparedStatement) psi.next(); + ps.closeDestroy(); + } + + } catch (SQLException ex) { + if (logErrors) { + logger.log(Level.WARNING, "Error when closing connection Statements", ex); + } + } + + try { + connection.close(); + + } catch (SQLException ex) { + if (logErrors) { + msg = "Error when fully closing connection [" + getName() + "]"; + logger.log(Level.SEVERE, msg, ex); + } + } + } + + /** + * A Least Recently used cache of PreparedStatements. + */ + public PstmtCache getPstmtCache() { + return pstmtCache; + } + + /** + * Creates a wrapper ExtendedStatement so that I can get the executed sql. I + * want to do this so that I can get the slowest query statments etc, and + * log that information. + */ + public Statement createStatement() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "createStatement()"); + } + try { + return connection.createStatement(); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public Statement createStatement(int resultSetType, int resultSetConcurreny) + throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "createStatement()"); + } + try { + return connection.createStatement(resultSetType, resultSetConcurreny); + + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + /** + * Return a PreparedStatement back into the cache. + */ + protected void returnPreparedStatement(ExtendedPreparedStatement pstmt) { + + synchronized (pstmtMonitor) { + ExtendedPreparedStatement alreadyInCache = pstmtCache.get(pstmt.getCacheKey()); + + if (alreadyInCache == null) { + // add the returning prepared statement to the cache. + // Note that the LRUCache will automatically close fully old unused + // PStmts when the cache has hit its maximum size. + pstmtCache.put(pstmt.getCacheKey(), pstmt); + + } else { + try { + // if a entry in the cache exists for the exact same SQL... + // then remove it from the cache and close it fully. + // Only having one PreparedStatement per unique SQL + // statement + pstmt.closeDestroy(); + + } catch (SQLException e) { + logger.log(Level.SEVERE, "Error closing Pstmt", e); + } + } + } + } + + /** + * This will try to use a cache of PreparedStatements. + */ + public PreparedStatement prepareStatement(String sql, int returnKeysFlag) throws SQLException { + String cacheKey = sql + returnKeysFlag; + return prepareStatement(sql, true, returnKeysFlag, cacheKey); + } + + /** + * This will try to use a cache of PreparedStatements. + */ + public PreparedStatement prepareStatement(String sql) throws SQLException { + return prepareStatement(sql, false, 0, sql); + } + + /** + * This will try to use a cache of PreparedStatements. + */ + private PreparedStatement prepareStatement(String sql, boolean useFlag, int flag, String cacheKey) throws SQLException { + + if (status == STATUS_IDLE) { + String m = IDLE_CONNECTION_ACCESSED_ERROR + "prepareStatement()"; + throw new SQLException(m); + } + try { + synchronized (pstmtMonitor) { + lastStatement = sql; + + // try to get a matching cached PStmt from the cache. + ExtendedPreparedStatement pstmt = pstmtCache.remove(cacheKey); + + if (pstmt != null) { + pstmtHitCounter++; + return pstmt; + } + + // create a new PreparedStatement + pstmtMissCounter++; + PreparedStatement actualPstmt; + if (useFlag) { + actualPstmt = connection.prepareStatement(sql, flag); + } else { + actualPstmt = connection.prepareStatement(sql); + } + return new ExtendedPreparedStatement(this, actualPstmt, sql, cacheKey); + } + + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurreny) + throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareStatement()"); + } + try { + // no caching when creating PreparedStatements this way + pstmtMissCounter++; + lastStatement = sql; + return connection.prepareStatement(sql, resultSetType, resultSetConcurreny); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + /** + * Reset the connection for returning to the client. Resets the status, + * startUseTime and hadErrors. + */ + protected void resetForUse() { + this.status = STATUS_ACTIVE; + this.startUseTime = System.currentTimeMillis(); + this.createdByMethod = null; + this.lastStatement = null; + this.hadErrors = false; + this.longRunning = false; + } + + /** + * When an error occurs during use add it the connection. + *

+ * Any PooledConnection that has an error is checked to make sure it works + * before it is placed back into the connection pool. + *

+ */ + public void addError(Throwable e) { + hadErrors = true; + } + + /** + * Returns true if the connect threw any errors during use. + *

+ * Connections with errors are testing to make sure they are still good + * before putting them back into the pool. + *

+ */ + public boolean hadErrors() { + return hadErrors; + } + + /** + * close the connection putting it back into the connection pool. + *

+ * Note that to ensure that the next transaction starts at the correct time + * a commit() or rollback() should be called. If neither has occured at this + * time then a rollback() is used (to end the transaction). + *

+ *

+ * To close the connection fully use closeConnectionFully(). + *

+ */ + public void close() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "close()"); + } + + if (hadErrors) { + if (!pool.validateConnection(this)) { + // the connection is BAD, close it and test the pool + closeConnectionFully(false); + pool.checkDataSource(); + return; + } + } + + try { + // reset the autoCommit back if client code changed it + if (connection.getAutoCommit() != pool.getAutoCommit()) { + connection.setAutoCommit(pool.getAutoCommit()); + } + // Generally resetting Isolation level seems expensive. + // Hence using resetIsolationReadOnlyRequired flag + // performance reasons. + if (resetIsolationReadOnlyRequired) { + resetIsolationReadOnly(); + resetIsolationReadOnlyRequired = false; + } + + // the connection is assumed GOOD so put it back in the pool + lastUseTime = System.currentTimeMillis(); + // connection.clearWarnings(); + status = STATUS_IDLE; + pool.returnConnection(this); + + } catch (Exception ex) { + // the connection is BAD, close it and test the pool + closeConnectionFully(false); + pool.checkDataSource(); + } + } + + private void resetIsolationReadOnly() throws SQLException { + // reset the transaction isolation if the client code changed it + if (connection.getTransactionIsolation() != pool.getTransactionIsolation()) { + connection.setTransactionIsolation(pool.getTransactionIsolation()); + } + // reset readonly to false + if (connection.isReadOnly()) { + connection.setReadOnly(false); + } + } + + protected void finalize() throws Throwable { + try { + if (connection != null && !connection.isClosed()) { + // connect leak? + String msg = "Closing Connection[" + getName() + "] on finalize()."; + logger.warning(msg); + closeConnectionFully(false); + } + } catch (Exception e) { + logger.log(Level.SEVERE, null, e); + } + super.finalize(); + } + + /** + * Return the time the connection was passed to the client code. + *

+ * Used to detect busy connections that could be leaks. + *

+ */ + public long getStartUseTime() { + return startUseTime; + } + + /** + * Returns the time the connection was last used. + *

+ * Used to close connections that have been idle for some time. Typically 5 + * minutes. + *

+ */ + public long getLastUsedTime() { + return lastUseTime; + } + + /** + * Returns the last sql statement executed. + */ + public String getLastStatement() { + return lastStatement; + } + + /** + * Called by ExtendedStatement to trace the sql being executed. + *

+ * Note with addBatch() this will not really work. + *

+ */ + protected void setLastStatement(String lastStatement) { + this.lastStatement = lastStatement; + if (logger.isLoggable(Level.FINER)) { + logger.finer(".setLastStatement[" + lastStatement + "]"); + } + } + + boolean resetIsolationReadOnlyRequired = false; + + /** + * Also note the read only status needs to be reset when put back into the + * pool. + */ + public void setReadOnly(boolean readOnly) throws SQLException { + // A bit loose not checking for STATUS_IDLE + // if (status == STATUS_IDLE) { + // throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + + // "setReadOnly()"); + // } + resetIsolationReadOnlyRequired = true; + connection.setReadOnly(readOnly); + } + + /** + * Also note the Isolation level needs to be reset when put back into the + * pool. + */ + public void setTransactionIsolation(int level) throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setTransactionIsolation()"); + } + try { + resetIsolationReadOnlyRequired = true; + connection.setTransactionIsolation(level); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + // + // + // Simple wrapper methods which pass a method call onto the acutal + // connection object. These methods are safe-guarded to prevent use of + // the methods whilst the connection is in the connection pool. + // + // + public void clearWarnings() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "clearWarnings()"); + } + connection.clearWarnings(); + } + + public void commit() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "commit()"); + } + try { + status = STATUS_ENDED; + connection.commit(); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public boolean getAutoCommit() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getAutoCommit()"); + } + return connection.getAutoCommit(); + } + + public String getCatalog() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getCatalog()"); + } + return connection.getCatalog(); + } + + public DatabaseMetaData getMetaData() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getMetaData()"); + } + return connection.getMetaData(); + } + + public int getTransactionIsolation() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getTransactionIsolation()"); + } + return connection.getTransactionIsolation(); + } + + public Map> getTypeMap() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getTypeMap()"); + } + return connection.getTypeMap(); + } + + public SQLWarning getWarnings() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getWarnings()"); + } + return connection.getWarnings(); + } + + public boolean isClosed() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "isClosed()"); + } + return connection.isClosed(); + } + + public boolean isReadOnly() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "isReadOnly()"); + } + return connection.isReadOnly(); + } + + public String nativeSQL(String sql) throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "nativeSQL()"); + } + lastStatement = sql; + return connection.nativeSQL(sql); + } + + public CallableStatement prepareCall(String sql) throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareCall()"); + } + lastStatement = sql; + return connection.prepareCall(sql); + } + + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurreny) + throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareCall()"); + } + lastStatement = sql; + return connection.prepareCall(sql, resultSetType, resultSetConcurreny); + } + + public void rollback() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "rollback()"); + } + try { + status = STATUS_ENDED; + connection.rollback(); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public void setAutoCommit(boolean autoCommit) throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setAutoCommit()"); + } + try { + connection.setAutoCommit(autoCommit); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public void setCatalog(String catalog) throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setCatalog()"); + } + connection.setCatalog(catalog); + } + + public void setTypeMap(Map> map) throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setTypeMap()"); + } + connection.setTypeMap(map); + } + + public Savepoint setSavepoint() throws SQLException { + try { + return connection.setSavepoint(); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public Savepoint setSavepoint(String savepointName) throws SQLException { + try { + return connection.setSavepoint(savepointName); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public void rollback(Savepoint sp) throws SQLException { + try { + connection.rollback(sp); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public void releaseSavepoint(Savepoint sp) throws SQLException { + try { + connection.releaseSavepoint(sp); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public void setHoldability(int i) throws SQLException { + try { + connection.setHoldability(i); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public int getHoldability() throws SQLException { + try { + return connection.getHoldability(); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public Statement createStatement(int i, int x, int y) throws SQLException { + try { + return connection.createStatement(i, x, y); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public PreparedStatement prepareStatement(String s, int i, int x, int y) throws SQLException { + try { + return connection.prepareStatement(s, i, x, y); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public PreparedStatement prepareStatement(String s, int[] i) throws SQLException { + try { + return connection.prepareStatement(s, i); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public PreparedStatement prepareStatement(String s, String[] s2) throws SQLException { + try { + return connection.prepareStatement(s, s2); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public CallableStatement prepareCall(String s, int i, int x, int y) throws SQLException { + try { + return connection.prepareCall(s, i, x, y); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + /** + * Returns the method that created the connection. + *

+ * Used to help finding connection pool leaks. + *

+ */ + public String getCreatedByMethod() { + if (createdByMethod != null) { + return createdByMethod; + } + if (stackTrace == null) { + return null; + } + + for (int j = 0; j < stackTrace.length; j++) { + String methodLine = stackTrace[j].toString(); + if (skipElement(methodLine)) { + // ignore these methods... + } else { + createdByMethod = methodLine; + return createdByMethod; + } + } + + return null; + } + + private boolean skipElement(String methodLine) { + if (methodLine.startsWith("java.lang.")) { + return true; + } else if (methodLine.startsWith("java.util.")) { + return true; + } else if (methodLine.startsWith("com.avaje.ebeaninternal.server.query.CallableQuery.")) { + // creating connection on future... + return true; + } else if (methodLine.startsWith("com.avaje.ebeaninternal.server.query.Callable")) { + // it is a future task being executed... + return false; + } else if (methodLine.startsWith("com.avaje.ebeaninternal")) { + return true; + } else { + return false; + } + } + + /** + * Set the stack trace to help find connection pool leaks. + */ + protected void setStackTrace(StackTraceElement[] stackTrace) { + this.stackTrace = stackTrace; + } + + /** + * Return the full stack trace that got the connection from the pool. You + * could use this if getCreatedByMethod() doesn't work for you. + */ + public StackTraceElement[] getStackTrace() { + + if (stackTrace == null){ + return null; + } + + // filter off the top of the stack that we are not interested in + ArrayList filteredList = new ArrayList(); + boolean include = false; + for (int i = 0; i < stackTrace.length; i++) { + if (!include && !skipElement(stackTrace[i].toString())){ + include = true; + } + if (include && filteredList.size() < maxStackTrace){ + filteredList.add(stackTrace[i]); + } + } + return filteredList.toArray(new StackTraceElement[filteredList.size()]); + + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PooledConnectionQueue.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PooledConnectionQueue.java index bbc268a4f..3ea813f4e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PooledConnectionQueue.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PooledConnectionQueue.java @@ -1,606 +1,587 @@ -/** - * 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.lib.sql; - -import java.sql.SQLException; -import java.util.Arrays; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.ReentrantLock; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool.Status; - -public class PooledConnectionQueue { - - private static final Logger logger = Logger.getLogger(PooledConnectionQueue.class.getName()); - - private static final TimeUnit MILLIS_TIME_UNIT = TimeUnit.MILLISECONDS; - - private final String name; - - private final DataSourcePool pool; - - /** - * A 'circular' buffer designed specifically for free connections. - */ - private final FreeConnectionBuffer freeList; - - /** - * A 'slots' buffer designed specifically for busy connections. - * Fast add remove based on slot id. - */ - private final BusyConnectionBuffer busyList; - - /** - * Main lock guarding all access - */ - private final ReentrantLock lock; - - /** - * Condition for threads waiting to take a connection - */ - private final Condition notEmpty; - - private int connectionId; - - private long waitTimeoutMillis; - - private long leakTimeMinutes; - - private int warningSize; - - private int maxSize; - - private int minSize; - - /** - * Number of threads in the wait queue. - */ - private int waitingThreads; - - /** - * Number of times a thread had to wait. - */ - private int waitCount; - - /** - * Number of times a connection was got from this queue. - */ - private int hitCount; - - /** - * The high water mark for the queue size. - */ - private int highWaterMark; - - /** - * Last time the pool was reset. Used to close busy connections as they are - * returned to the pool that where created prior to the lastResetTime. - */ - private long lastResetTime; - - private boolean doingShutdown; - - public PooledConnectionQueue(DataSourcePool pool) { - - this.pool = pool; - this.name = pool.getName(); - this.minSize = pool.getMinSize(); - this.maxSize = pool.getMaxSize(); - - this.warningSize = pool.getWarningSize(); - this.waitTimeoutMillis = pool.getWaitTimeoutMillis(); - this.leakTimeMinutes = pool.getLeakTimeMinutes(); - - this.busyList = new BusyConnectionBuffer(50,20); - this.freeList = new FreeConnectionBuffer(maxSize); - - this.lock = new ReentrantLock(true); - this.notEmpty = lock.newCondition(); - } - - private Status createStatus() { - return new Status(name, minSize, maxSize, freeList.size(), busyList.size(), waitingThreads, highWaterMark, waitCount, hitCount); - } - - public String toString() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - return createStatus().toString(); - } finally { - lock.unlock(); - } - } - - public Status getStatus(boolean reset) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - Status s = createStatus(); - if (reset){ - highWaterMark = busyList.size(); - hitCount = 0; - waitCount = 0; - } - return s; - } finally { - lock.unlock(); - } - } - - public void setMinSize(int minSize) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - if (minSize > this.maxSize){ - throw new IllegalArgumentException("minSize "+minSize+" > maxSize "+this.maxSize); - } - this.minSize = minSize; - } finally { - lock.unlock(); - } - } - - public void setMaxSize(int maxSize) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - if (maxSize < this.minSize){ - throw new IllegalArgumentException("maxSize "+maxSize+" < minSize "+this.minSize); - } - freeList.setCapacity(maxSize); - this.maxSize = maxSize; - } finally { - lock.unlock(); - } - } - - public void setWarningSize(int warningSize) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - if (warningSize > this.maxSize){ - throw new IllegalArgumentException("warningSize "+warningSize+" > maxSize "+this.maxSize); - } - this.warningSize = warningSize; - } finally { - lock.unlock(); - } - } - - private int totalConnections() { - return freeList.size() + busyList.size(); - } - - public void ensureMinimumConnections() throws SQLException { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - int add = minSize - totalConnections(); - if (add > 0){ - for (int i = 0; i < add; i++) { - PooledConnection c = pool.createConnectionForQueue(connectionId++); - freeList.add(c); - } - notEmpty.signal(); - } - - } finally { - lock.unlock(); - } - } - - /** - * Return a PooledConnection. - */ - protected void returnPooledConnection(PooledConnection c) { - - final ReentrantLock lock = this.lock; - lock.lock(); - try { - if (!busyList.remove(c)) { - logger.log(Level.SEVERE, "Connection [" + c + "] not found in BusyList? "); - } - if (c.getCreationTime() <= lastResetTime) { - c.closeConnectionFully(false); - } else { - freeList.add(c); - notEmpty.signal(); - } - } finally { - lock.unlock(); - } - } - - private PooledConnection extractFromFreeList() { - PooledConnection c = freeList.remove(); - registerBusyConnection(c); - return c; - } - - public PooledConnection getPooledConnection() throws SQLException { - - try { - PooledConnection pc = _getPooledConnection(); - pc.resetForUse(); - return pc; - - } catch (InterruptedException e) { - String msg = "Interrupted getting connection from pool "+e; - throw new SQLException(msg); - } - } - - /** - * Register the PooledConnection with the busyList. - */ - private int registerBusyConnection(PooledConnection c) { - int busySize = busyList.add(c); - if (busySize > highWaterMark){ - highWaterMark = busySize; - } - return busySize; - } - - private PooledConnection _getPooledConnection() throws InterruptedException, SQLException { - final ReentrantLock lock = this.lock; - lock.lockInterruptibly(); - try { - if (doingShutdown) { - throw new SQLException("Trying to access the Connection Pool when it is shutting down"); - } - - // this includes attempts that fail with InterruptedException - // or SQLException but that is ok as its only an indicator - hitCount++; - - // are other threads already waiting? (they get priority) - if (waitingThreads == 0){ - - int freeSize = freeList.size(); - if (freeSize > 0){ - // we have a free connection to return - return extractFromFreeList(); - } - - if (busyList.size() < maxSize){ - // grow the connection pool - PooledConnection c = pool.createConnectionForQueue(connectionId++); - int busySize = registerBusyConnection(c); - - String msg = "DataSourcePool [" + name + "] grow; id["+c.getName()+"] busy["+busySize+"] max["+maxSize+"]"; - logger.info(msg); - - checkForWarningSize(); - return c; - } - } - - try { - // The pool is at maximum size. We are going to go into - // a wait loop until connections are returned into the pool. - waitCount++; - waitingThreads++; - return _getPooledConnectionWaitLoop(); - } finally { - waitingThreads--; - } - - } finally { - lock.unlock(); - } - } - - /** - * Got into a loop waiting for connections to be returned to the pool. - */ - private PooledConnection _getPooledConnectionWaitLoop() throws SQLException, InterruptedException { - - long nanos = MILLIS_TIME_UNIT.toNanos(waitTimeoutMillis); - for (;;) { - - if (nanos <= 0) { - String msg = "Unsuccessfully waited ["+waitTimeoutMillis+"] millis for a connection to be returned." - + " No connections are free. You need to Increase the max connections of ["+maxSize+"]" - + " or look for a connection pool leak using datasource.xxx.capturestacktrace=true"; - if (pool.isCaptureStackTrace()) { - dumpBusyConnectionInformation(); - } - - throw new SQLException(msg); - } - - try { - nanos = notEmpty.awaitNanos(nanos); - if (!freeList.isEmpty()) { - // successfully waited - return extractFromFreeList(); - } - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to non-interrupted thread - throw ie; - } - } - } - - public void shutdown() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - doingShutdown = true; - Status status = createStatus(); - logger.info("DataSourcePool [" + name + "] shutdown: "+status); - - closeFreeConnections(true); - - if (!busyList.isEmpty()) { - String msg = "A potential connection leak was detected. Busy connections: "+ busyList.size(); - logger.warning(msg); - - dumpBusyConnectionInformation(); - closeBusyConnections(0); - } - } finally { - lock.unlock(); - } - } - - /** - * Close all the connections in the pool and any current busy connections - * when they are returned. New connections will be then created on demand. - *

- * This is typically done when a database down event occurs. - *

- */ - public void reset(long leakTimeMinutes) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - Status status = createStatus(); - logger.info("Reseting DataSourcePool [" + name + "] "+status); - lastResetTime = System.currentTimeMillis(); - - closeFreeConnections(false); - closeBusyConnections(leakTimeMinutes); - - String busyMsg = "Busy Connections:\r\n" + getBusyConnectionInformation(); - logger.info(busyMsg); - - } finally { - lock.unlock(); - } - } - - public void trim(int maxInactiveTimeSecs) throws SQLException { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - trimInactiveConnections(maxInactiveTimeSecs); - ensureMinimumConnections(); - - } finally { - lock.unlock(); - } - } - - /** - * Trim connections that have been not used for some time. - */ - private int trimInactiveConnections(int maxInactiveTimeSecs) { - - int maxTrim = freeList.size() - minSize; - if (maxTrim <= 0) { - return 0; - } - - int trimedCount = 0; - long usedSince = System.currentTimeMillis() - (maxInactiveTimeSecs * 1000); - - // get a shallow copy to manipulate - List freeListCopy = freeList.getShallowCopy(); - - Iterator it = freeListCopy.iterator(); - while (it.hasNext()) { - PooledConnection pc = it.next(); - if (pc.getLastUsedTime() < usedSince) { - // trim this connection as it hasn't been used in a while - trimedCount++; - it.remove(); - pc.closeConnectionFully(true); - if (trimedCount >= maxTrim) { - break; - } - } - } - - if (trimedCount > 0) { - - // rebuild the free list from the trimmed copy - freeList.setShallowCopy(freeListCopy); - - String msg = "DataSourcePool [" + name + "] trimmed [" + trimedCount + "] inactive connections. New size[" + totalConnections() + "]"; - logger.info(msg); - } - return trimedCount; - } - - /** - * Close all the connections that are in the free list. - */ - public void closeFreeConnections(boolean logErrors) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - while (!freeList.isEmpty()) { - PooledConnection c = freeList.remove(); - logger.info("PSTMT Statistics: "+c.getStatistics()); - c.closeConnectionFully(logErrors); - } - } finally { - lock.unlock(); - } - } - - /** - * Close any busy connections that have not been used for some time. - *

- * These connections are considered to have leaked from the connection pool. - *

- *

- * Connection leaks occur when code doesn't ensure that connections are - * closed() after they have been finished with. There should be an - * appropriate try catch finally block to ensure connections are always - * closed and put back into the pool. - *

- */ - public void closeBusyConnections(long leakTimeMinutes) { - - final ReentrantLock lock = this.lock; - lock.lock(); - try { - - long olderThanTime = System.currentTimeMillis() - (leakTimeMinutes*60000); - - List copy = busyList.getShallowCopy(); - for (int i = 0; i < copy.size(); i++) { - PooledConnection pc = copy.get(i); - if (pc.isLongRunning() || pc.getLastUsedTime() > olderThanTime) { - // PooledConnection has been used recently or - // expected to be longRunning so not closing... - } else { - busyList.remove(pc); - closeBusyConnection(pc); - } - } - - } finally { - lock.unlock(); - } - } - - private void closeBusyConnection(PooledConnection pc) { - try { - String methodLine = pc.getCreatedByMethod(); - - Date luDate = new Date(); - luDate.setTime(pc.getLastUsedTime()); - - String msg = "DataSourcePool closing leaked connection? " + " name[" - + pc.getName() + "] lastUsed[" + luDate + "] createdBy[" + methodLine - + "] lastStmt[" + pc.getLastStatement() + "]"; - - logger.warning(msg); - logStackElement(pc, "Possible Leaked Connection: "); - - System.out.println("CLOSING BUSY CONNECTION ??? "+pc); - pc.close(); - - } catch (SQLException ex) { - // this should never actually happen - logger.log(Level.SEVERE, null, ex); - } - } - - private void logStackElement(PooledConnection pc, String prefix) { - StackTraceElement[] stackTrace = pc.getStackTrace(); - if (stackTrace != null){ - String s = Arrays.toString(stackTrace); - String msg = prefix+" name["+pc.getName()+"] stackTrace: "+s; - logger.warning(msg); - // also send to syserr ... as the loggers get turned - // off early in JVM shutdown - System.err.println(msg); - } - } - - - /** - * As the pool grows it gets closer to the maxConnections limit. We can send - * an Alert (or warning) as we get close to this limit and hence an - * Administrator could increase the pool size if desired. - *

- * This is called whenever the pool grows in size (towards the max limit). - *

- */ - private void checkForWarningSize() { - - // the the total number of connections that we can add - // to the pool before it hits the maximum - int availableGrowth = (maxSize - totalConnections()); - - if (availableGrowth < warningSize) { - - closeBusyConnections(leakTimeMinutes); - - String msg = "DataSourcePool [" + name + "] is [" + availableGrowth+ "] connections from its maximum size."; - pool.notifyWarning(msg); - } - } - - public String getBusyConnectionInformation() { - return getBusyConnectionInformation(false); - } - - public void dumpBusyConnectionInformation() { - getBusyConnectionInformation(true); - } - - /** - * Returns information describing connections that are currently being used. - */ - private String getBusyConnectionInformation(boolean toLogger) { - - final ReentrantLock lock = this.lock; - lock.lock(); - try { - - if (toLogger) { - logger.info("Dumping busy connections: (Use datasource.xxx.capturestacktrace=true ... to get stackTraces)"); - } - - StringBuilder sb = new StringBuilder(); - - List copy = busyList.getShallowCopy(); - for (int i = 0; i < copy.size(); i++) { - PooledConnection pc = copy.get(i); - if (toLogger) { - logger.info(pc.getDescription()); - logStackElement(pc, "Busy Connection: "); - - } else { - sb.append(pc.getDescription()).append("\r\n"); - } - } - - return sb.toString(); - - } finally { - lock.unlock(); - } - } - -} - +package com.avaje.ebeaninternal.server.lib.sql; + +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool.Status; + +public class PooledConnectionQueue { + + private static final Logger logger = Logger.getLogger(PooledConnectionQueue.class.getName()); + + private static final TimeUnit MILLIS_TIME_UNIT = TimeUnit.MILLISECONDS; + + private final String name; + + private final DataSourcePool pool; + + /** + * A 'circular' buffer designed specifically for free connections. + */ + private final FreeConnectionBuffer freeList; + + /** + * A 'slots' buffer designed specifically for busy connections. + * Fast add remove based on slot id. + */ + private final BusyConnectionBuffer busyList; + + /** + * Main lock guarding all access + */ + private final ReentrantLock lock; + + /** + * Condition for threads waiting to take a connection + */ + private final Condition notEmpty; + + private int connectionId; + + private long waitTimeoutMillis; + + private long leakTimeMinutes; + + private int warningSize; + + private int maxSize; + + private int minSize; + + /** + * Number of threads in the wait queue. + */ + private int waitingThreads; + + /** + * Number of times a thread had to wait. + */ + private int waitCount; + + /** + * Number of times a connection was got from this queue. + */ + private int hitCount; + + /** + * The high water mark for the queue size. + */ + private int highWaterMark; + + /** + * Last time the pool was reset. Used to close busy connections as they are + * returned to the pool that where created prior to the lastResetTime. + */ + private long lastResetTime; + + private boolean doingShutdown; + + public PooledConnectionQueue(DataSourcePool pool) { + + this.pool = pool; + this.name = pool.getName(); + this.minSize = pool.getMinSize(); + this.maxSize = pool.getMaxSize(); + + this.warningSize = pool.getWarningSize(); + this.waitTimeoutMillis = pool.getWaitTimeoutMillis(); + this.leakTimeMinutes = pool.getLeakTimeMinutes(); + + this.busyList = new BusyConnectionBuffer(50,20); + this.freeList = new FreeConnectionBuffer(maxSize); + + this.lock = new ReentrantLock(true); + this.notEmpty = lock.newCondition(); + } + + private Status createStatus() { + return new Status(name, minSize, maxSize, freeList.size(), busyList.size(), waitingThreads, highWaterMark, waitCount, hitCount); + } + + public String toString() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return createStatus().toString(); + } finally { + lock.unlock(); + } + } + + public Status getStatus(boolean reset) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Status s = createStatus(); + if (reset){ + highWaterMark = busyList.size(); + hitCount = 0; + waitCount = 0; + } + return s; + } finally { + lock.unlock(); + } + } + + public void setMinSize(int minSize) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + if (minSize > this.maxSize){ + throw new IllegalArgumentException("minSize "+minSize+" > maxSize "+this.maxSize); + } + this.minSize = minSize; + } finally { + lock.unlock(); + } + } + + public void setMaxSize(int maxSize) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + if (maxSize < this.minSize){ + throw new IllegalArgumentException("maxSize "+maxSize+" < minSize "+this.minSize); + } + freeList.setCapacity(maxSize); + this.maxSize = maxSize; + } finally { + lock.unlock(); + } + } + + public void setWarningSize(int warningSize) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + if (warningSize > this.maxSize){ + throw new IllegalArgumentException("warningSize "+warningSize+" > maxSize "+this.maxSize); + } + this.warningSize = warningSize; + } finally { + lock.unlock(); + } + } + + private int totalConnections() { + return freeList.size() + busyList.size(); + } + + public void ensureMinimumConnections() throws SQLException { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + int add = minSize - totalConnections(); + if (add > 0){ + for (int i = 0; i < add; i++) { + PooledConnection c = pool.createConnectionForQueue(connectionId++); + freeList.add(c); + } + notEmpty.signal(); + } + + } finally { + lock.unlock(); + } + } + + /** + * Return a PooledConnection. + */ + protected void returnPooledConnection(PooledConnection c) { + + final ReentrantLock lock = this.lock; + lock.lock(); + try { + if (!busyList.remove(c)) { + logger.log(Level.SEVERE, "Connection [" + c + "] not found in BusyList? "); + } + if (c.getCreationTime() <= lastResetTime) { + c.closeConnectionFully(false); + } else { + freeList.add(c); + notEmpty.signal(); + } + } finally { + lock.unlock(); + } + } + + private PooledConnection extractFromFreeList() { + PooledConnection c = freeList.remove(); + registerBusyConnection(c); + return c; + } + + public PooledConnection getPooledConnection() throws SQLException { + + try { + PooledConnection pc = _getPooledConnection(); + pc.resetForUse(); + return pc; + + } catch (InterruptedException e) { + String msg = "Interrupted getting connection from pool "+e; + throw new SQLException(msg); + } + } + + /** + * Register the PooledConnection with the busyList. + */ + private int registerBusyConnection(PooledConnection c) { + int busySize = busyList.add(c); + if (busySize > highWaterMark){ + highWaterMark = busySize; + } + return busySize; + } + + private PooledConnection _getPooledConnection() throws InterruptedException, SQLException { + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try { + if (doingShutdown) { + throw new SQLException("Trying to access the Connection Pool when it is shutting down"); + } + + // this includes attempts that fail with InterruptedException + // or SQLException but that is ok as its only an indicator + hitCount++; + + // are other threads already waiting? (they get priority) + if (waitingThreads == 0){ + + int freeSize = freeList.size(); + if (freeSize > 0){ + // we have a free connection to return + return extractFromFreeList(); + } + + if (busyList.size() < maxSize){ + // grow the connection pool + PooledConnection c = pool.createConnectionForQueue(connectionId++); + int busySize = registerBusyConnection(c); + + String msg = "DataSourcePool [" + name + "] grow; id["+c.getName()+"] busy["+busySize+"] max["+maxSize+"]"; + logger.info(msg); + + checkForWarningSize(); + return c; + } + } + + try { + // The pool is at maximum size. We are going to go into + // a wait loop until connections are returned into the pool. + waitCount++; + waitingThreads++; + return _getPooledConnectionWaitLoop(); + } finally { + waitingThreads--; + } + + } finally { + lock.unlock(); + } + } + + /** + * Got into a loop waiting for connections to be returned to the pool. + */ + private PooledConnection _getPooledConnectionWaitLoop() throws SQLException, InterruptedException { + + long nanos = MILLIS_TIME_UNIT.toNanos(waitTimeoutMillis); + for (;;) { + + if (nanos <= 0) { + String msg = "Unsuccessfully waited ["+waitTimeoutMillis+"] millis for a connection to be returned." + + " No connections are free. You need to Increase the max connections of ["+maxSize+"]" + + " or look for a connection pool leak using datasource.xxx.capturestacktrace=true"; + if (pool.isCaptureStackTrace()) { + dumpBusyConnectionInformation(); + } + + throw new SQLException(msg); + } + + try { + nanos = notEmpty.awaitNanos(nanos); + if (!freeList.isEmpty()) { + // successfully waited + return extractFromFreeList(); + } + } catch (InterruptedException ie) { + notEmpty.signal(); // propagate to non-interrupted thread + throw ie; + } + } + } + + public void shutdown() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + doingShutdown = true; + Status status = createStatus(); + logger.info("DataSourcePool [" + name + "] shutdown: "+status); + + closeFreeConnections(true); + + if (!busyList.isEmpty()) { + String msg = "A potential connection leak was detected. Busy connections: "+ busyList.size(); + logger.warning(msg); + + dumpBusyConnectionInformation(); + closeBusyConnections(0); + } + } finally { + lock.unlock(); + } + } + + /** + * Close all the connections in the pool and any current busy connections + * when they are returned. New connections will be then created on demand. + *

+ * This is typically done when a database down event occurs. + *

+ */ + public void reset(long leakTimeMinutes) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Status status = createStatus(); + logger.info("Reseting DataSourcePool [" + name + "] "+status); + lastResetTime = System.currentTimeMillis(); + + closeFreeConnections(false); + closeBusyConnections(leakTimeMinutes); + + String busyMsg = "Busy Connections:\r\n" + getBusyConnectionInformation(); + logger.info(busyMsg); + + } finally { + lock.unlock(); + } + } + + public void trim(int maxInactiveTimeSecs) throws SQLException { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + trimInactiveConnections(maxInactiveTimeSecs); + ensureMinimumConnections(); + + } finally { + lock.unlock(); + } + } + + /** + * Trim connections that have been not used for some time. + */ + private int trimInactiveConnections(int maxInactiveTimeSecs) { + + int maxTrim = freeList.size() - minSize; + if (maxTrim <= 0) { + return 0; + } + + int trimedCount = 0; + long usedSince = System.currentTimeMillis() - (maxInactiveTimeSecs * 1000); + + // get a shallow copy to manipulate + List freeListCopy = freeList.getShallowCopy(); + + Iterator it = freeListCopy.iterator(); + while (it.hasNext()) { + PooledConnection pc = it.next(); + if (pc.getLastUsedTime() < usedSince) { + // trim this connection as it hasn't been used in a while + trimedCount++; + it.remove(); + pc.closeConnectionFully(true); + if (trimedCount >= maxTrim) { + break; + } + } + } + + if (trimedCount > 0) { + + // rebuild the free list from the trimmed copy + freeList.setShallowCopy(freeListCopy); + + String msg = "DataSourcePool [" + name + "] trimmed [" + trimedCount + "] inactive connections. New size[" + totalConnections() + "]"; + logger.info(msg); + } + return trimedCount; + } + + /** + * Close all the connections that are in the free list. + */ + public void closeFreeConnections(boolean logErrors) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + while (!freeList.isEmpty()) { + PooledConnection c = freeList.remove(); + logger.info("PSTMT Statistics: "+c.getStatistics()); + c.closeConnectionFully(logErrors); + } + } finally { + lock.unlock(); + } + } + + /** + * Close any busy connections that have not been used for some time. + *

+ * These connections are considered to have leaked from the connection pool. + *

+ *

+ * Connection leaks occur when code doesn't ensure that connections are + * closed() after they have been finished with. There should be an + * appropriate try catch finally block to ensure connections are always + * closed and put back into the pool. + *

+ */ + public void closeBusyConnections(long leakTimeMinutes) { + + final ReentrantLock lock = this.lock; + lock.lock(); + try { + + long olderThanTime = System.currentTimeMillis() - (leakTimeMinutes*60000); + + List copy = busyList.getShallowCopy(); + for (int i = 0; i < copy.size(); i++) { + PooledConnection pc = copy.get(i); + if (pc.isLongRunning() || pc.getLastUsedTime() > olderThanTime) { + // PooledConnection has been used recently or + // expected to be longRunning so not closing... + } else { + busyList.remove(pc); + closeBusyConnection(pc); + } + } + + } finally { + lock.unlock(); + } + } + + private void closeBusyConnection(PooledConnection pc) { + try { + String methodLine = pc.getCreatedByMethod(); + + Date luDate = new Date(); + luDate.setTime(pc.getLastUsedTime()); + + String msg = "DataSourcePool closing leaked connection? " + " name[" + + pc.getName() + "] lastUsed[" + luDate + "] createdBy[" + methodLine + + "] lastStmt[" + pc.getLastStatement() + "]"; + + logger.warning(msg); + logStackElement(pc, "Possible Leaked Connection: "); + + System.out.println("CLOSING BUSY CONNECTION ??? "+pc); + pc.close(); + + } catch (SQLException ex) { + // this should never actually happen + logger.log(Level.SEVERE, null, ex); + } + } + + private void logStackElement(PooledConnection pc, String prefix) { + StackTraceElement[] stackTrace = pc.getStackTrace(); + if (stackTrace != null){ + String s = Arrays.toString(stackTrace); + String msg = prefix+" name["+pc.getName()+"] stackTrace: "+s; + logger.warning(msg); + // also send to syserr ... as the loggers get turned + // off early in JVM shutdown + System.err.println(msg); + } + } + + + /** + * As the pool grows it gets closer to the maxConnections limit. We can send + * an Alert (or warning) as we get close to this limit and hence an + * Administrator could increase the pool size if desired. + *

+ * This is called whenever the pool grows in size (towards the max limit). + *

+ */ + private void checkForWarningSize() { + + // the the total number of connections that we can add + // to the pool before it hits the maximum + int availableGrowth = (maxSize - totalConnections()); + + if (availableGrowth < warningSize) { + + closeBusyConnections(leakTimeMinutes); + + String msg = "DataSourcePool [" + name + "] is [" + availableGrowth+ "] connections from its maximum size."; + pool.notifyWarning(msg); + } + } + + public String getBusyConnectionInformation() { + return getBusyConnectionInformation(false); + } + + public void dumpBusyConnectionInformation() { + getBusyConnectionInformation(true); + } + + /** + * Returns information describing connections that are currently being used. + */ + private String getBusyConnectionInformation(boolean toLogger) { + + final ReentrantLock lock = this.lock; + lock.lock(); + try { + + if (toLogger) { + logger.info("Dumping busy connections: (Use datasource.xxx.capturestacktrace=true ... to get stackTraces)"); + } + + StringBuilder sb = new StringBuilder(); + + List copy = busyList.getShallowCopy(); + for (int i = 0; i < copy.size(); i++) { + PooledConnection pc = copy.get(i); + if (toLogger) { + logger.info(pc.getDescription()); + logStackElement(pc, "Busy Connection: "); + + } else { + sb.append(pc.getDescription()).append("\r\n"); + } + } + + return sb.toString(); + + } finally { + lock.unlock(); + } + } + +} + diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/Prefix.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/Prefix.java index 7433aff54..c88546c4c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/Prefix.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/Prefix.java @@ -1,115 +1,98 @@ -/** - * 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.lib.sql; - -import java.util.Random; -import java.util.logging.Logger; - -/** - * Security mechanisim. - */ -public class Prefix { - - private static final Logger logger = Logger.getLogger(Prefix.class.getName()); - - private static final int[] oa = { 50, 12, 4, 6, 8, 10, 7, 23, 45, 23, 6, 9, 12, 2, 8, 34 }; - - public static String getProp(String prop) { - String v = dec(prop); - int p = v.indexOf(":"); - String r = v.substring(1, p); - return r; - } - - public static void main(String[] args) { - String m = e(args[0]); - logger.info("[" + m + "]"); - String o = getProp(m); - logger.info("[" + o + "]"); - } - - public static String e(String msg) { - msg = elen(msg, 40); - return enc(msg); - } - - public static byte az(byte c, int offset) { - - int z = c + offset; - if (z > 122) { - // dp("z> "+z); - z = z - 122 + 48 - 1; - } - // dp("z="+z+" c:"+(int)c); - return (byte) z; - } - - public static byte bz(byte c, int offset) { - int z = c - offset; - if (z < (48)) { - // dp("z< "+z); - z = z + 122 - 48 + 1; - } - return (byte) z; - } - - public static String enc(String msg) { - byte[] msgbytes = msg.getBytes(); - byte[] encbytes = new byte[msgbytes.length + 1]; - Random r = new Random(); - int key = r.nextInt(70); - - char k = (char) (key + 48); - - encbytes[0] = az((byte) k, oa[0]); - // dp("key:"+key+" encbytes[0]:"+(byte)encbytes[0]); - int ios = key; - for (int i = 1; i < (msgbytes.length + 1); i++) { - encbytes[i] = az(msgbytes[i - 1], (oa[(i + ios) % oa.length])); - } - return new String(encbytes); - } - - public static String dec(String msg) { - byte[] msgbytes = msg.getBytes(); - byte[] encbytes = new byte[msgbytes.length]; - - encbytes[0] = bz(msgbytes[0], oa[0]); - byte key = encbytes[0]; - int ios = (key - 48); - for (int i = 1; i < msgbytes.length; i++) { - encbytes[i] = bz(msgbytes[i], oa[(i + ios) % oa.length]); - } - return new String(encbytes); - } - - public static String elen(String msg, int len) { - Random r = new Random(); - if (msg.length() < len) { - int max = len - msg.length(); - StringBuilder sb = new StringBuilder(); - sb.append(msg).append(":"); - for (int i = 1; i < max; i++) { - int bc = r.nextInt(122 - 48); - sb.append(Character.toString((char) (bc + 48))); - } - return sb.toString(); - } - return msg; - } -} +package com.avaje.ebeaninternal.server.lib.sql; + +import java.util.Random; +import java.util.logging.Logger; + +/** + * Security mechanisim. + */ +public class Prefix { + + private static final Logger logger = Logger.getLogger(Prefix.class.getName()); + + private static final int[] oa = { 50, 12, 4, 6, 8, 10, 7, 23, 45, 23, 6, 9, 12, 2, 8, 34 }; + + public static String getProp(String prop) { + String v = dec(prop); + int p = v.indexOf(":"); + String r = v.substring(1, p); + return r; + } + + public static void main(String[] args) { + String m = e(args[0]); + logger.info("[" + m + "]"); + String o = getProp(m); + logger.info("[" + o + "]"); + } + + public static String e(String msg) { + msg = elen(msg, 40); + return enc(msg); + } + + public static byte az(byte c, int offset) { + + int z = c + offset; + if (z > 122) { + // dp("z> "+z); + z = z - 122 + 48 - 1; + } + // dp("z="+z+" c:"+(int)c); + return (byte) z; + } + + public static byte bz(byte c, int offset) { + int z = c - offset; + if (z < (48)) { + // dp("z< "+z); + z = z + 122 - 48 + 1; + } + return (byte) z; + } + + public static String enc(String msg) { + byte[] msgbytes = msg.getBytes(); + byte[] encbytes = new byte[msgbytes.length + 1]; + Random r = new Random(); + int key = r.nextInt(70); + + char k = (char) (key + 48); + + encbytes[0] = az((byte) k, oa[0]); + // dp("key:"+key+" encbytes[0]:"+(byte)encbytes[0]); + int ios = key; + for (int i = 1; i < (msgbytes.length + 1); i++) { + encbytes[i] = az(msgbytes[i - 1], (oa[(i + ios) % oa.length])); + } + return new String(encbytes); + } + + public static String dec(String msg) { + byte[] msgbytes = msg.getBytes(); + byte[] encbytes = new byte[msgbytes.length]; + + encbytes[0] = bz(msgbytes[0], oa[0]); + byte key = encbytes[0]; + int ios = (key - 48); + for (int i = 1; i < msgbytes.length; i++) { + encbytes[i] = bz(msgbytes[i], oa[(i + ios) % oa.length]); + } + return new String(encbytes); + } + + public static String elen(String msg, int len) { + Random r = new Random(); + if (msg.length() < len) { + int max = len - msg.length(); + StringBuilder sb = new StringBuilder(); + sb.append(msg).append(":"); + for (int i = 1; i < max; i++) { + int bc = r.nextInt(122 - 48); + sb.append(Character.toString((char) (bc + 48))); + } + return sb.toString(); + } + return msg; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PstmtCache.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PstmtCache.java index 2eaea32c9..7ce9fc5b7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PstmtCache.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PstmtCache.java @@ -1,181 +1,164 @@ -/** - * Copyright (C) 2006 Robin Bygrave - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.lib.sql; - -import java.sql.SQLException; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A LRU based cache for PreparedStatements. - */ -public class PstmtCache extends LinkedHashMap { - - private static final Logger logger = Logger.getLogger(PstmtCache.class.getName()); - - static final long serialVersionUID = -3096406924865550697L; - - /** - * The name of the cache, for tracing purposes. - */ - final String cacheName; - - /** - * The maximum size of the cache. When this is exceeded the oldest entry is removed. - */ - final int maxSize; - - /** - * The total number of entries removed from this cache. - */ - int removeCounter; - - /** - * The number of get hits. - */ - int hitCounter; - - /** - * The number of get() misses. - */ - int missCounter; - - /** - * The number of puts into this cache. - */ - int putCounter; - - public PstmtCache(String cacheName, int maxCacheSize) { - - // note = access ordered list. This is what gives it the LRU order - super(maxCacheSize*3, 0.75f, true); - this.cacheName = cacheName; - this.maxSize = maxCacheSize; - } - - /** - * Return a summary description of this cache. - */ - public String getDescription() { - return cacheName+" size:"+size()+" max:"+maxSize+" totalHits:"+hitCounter+" hitRatio:"+getHitRatio()+" removes:"+removeCounter; - } - - /** - * returns the current maximum size of the cache. - */ - public int getMaxSize() { - return maxSize; - } - - /** - * Gets the hit ratio. A number between 0 and 100 indicating the number of - * hits to misses. A number approaching 100 is desirable. - */ - public int getHitRatio() { - if (hitCounter == 0) { - return 0; - } else { - return hitCounter*100/(hitCounter+missCounter); - } - } - - /** - * The total number of hits against this cache. - */ - public int getHitCounter() { - return hitCounter; - } - - /** - * The total number of misses against this cache. - */ - public int getMissCounter() { - return missCounter; - } - - /** - * The total number of puts against this cache. - */ - public int getPutCounter() { - return putCounter; - } - - /** - * additionally maintains hit and miss statistics. - */ - public ExtendedPreparedStatement get(Object key) { - - ExtendedPreparedStatement o = super.get(key); - if (o == null) { - missCounter++; - } else { - hitCounter++; - } - return o; - } - - /** - * additionally maintains hit and miss statistics. - */ - public ExtendedPreparedStatement remove(Object key) { - - ExtendedPreparedStatement o = super.remove(key); - if (o == null) { - missCounter++; - } else { - hitCounter++; - } - return o; - } - - /** - * additionally maintains put counter statistics. - */ - public ExtendedPreparedStatement put(String key, ExtendedPreparedStatement value) { - - putCounter++; - return super.put(key, value); - } - - /** - * will check to see if we need to remove entries and - * if so call the cacheCleanup.cleanupEldestLRUCacheEntry() if - * one has been set. - */ - protected boolean removeEldestEntry(Map.Entry eldest) { - - if (size() < maxSize) { - return false; - } - - removeCounter++; - - try { - ExtendedPreparedStatement pstmt = eldest.getValue(); - pstmt.closeDestroy(); - } catch (SQLException e) { - logger.log(Level.SEVERE, "Error closing ExtendedPreparedStatement", e); - } - return true; - } - - -} - +package com.avaje.ebeaninternal.server.lib.sql; + +import java.sql.SQLException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A LRU based cache for PreparedStatements. + */ +public class PstmtCache extends LinkedHashMap { + + private static final Logger logger = Logger.getLogger(PstmtCache.class.getName()); + + static final long serialVersionUID = -3096406924865550697L; + + /** + * The name of the cache, for tracing purposes. + */ + final String cacheName; + + /** + * The maximum size of the cache. When this is exceeded the oldest entry is removed. + */ + final int maxSize; + + /** + * The total number of entries removed from this cache. + */ + int removeCounter; + + /** + * The number of get hits. + */ + int hitCounter; + + /** + * The number of get() misses. + */ + int missCounter; + + /** + * The number of puts into this cache. + */ + int putCounter; + + public PstmtCache(String cacheName, int maxCacheSize) { + + // note = access ordered list. This is what gives it the LRU order + super(maxCacheSize*3, 0.75f, true); + this.cacheName = cacheName; + this.maxSize = maxCacheSize; + } + + /** + * Return a summary description of this cache. + */ + public String getDescription() { + return cacheName+" size:"+size()+" max:"+maxSize+" totalHits:"+hitCounter+" hitRatio:"+getHitRatio()+" removes:"+removeCounter; + } + + /** + * returns the current maximum size of the cache. + */ + public int getMaxSize() { + return maxSize; + } + + /** + * Gets the hit ratio. A number between 0 and 100 indicating the number of + * hits to misses. A number approaching 100 is desirable. + */ + public int getHitRatio() { + if (hitCounter == 0) { + return 0; + } else { + return hitCounter*100/(hitCounter+missCounter); + } + } + + /** + * The total number of hits against this cache. + */ + public int getHitCounter() { + return hitCounter; + } + + /** + * The total number of misses against this cache. + */ + public int getMissCounter() { + return missCounter; + } + + /** + * The total number of puts against this cache. + */ + public int getPutCounter() { + return putCounter; + } + + /** + * additionally maintains hit and miss statistics. + */ + public ExtendedPreparedStatement get(Object key) { + + ExtendedPreparedStatement o = super.get(key); + if (o == null) { + missCounter++; + } else { + hitCounter++; + } + return o; + } + + /** + * additionally maintains hit and miss statistics. + */ + public ExtendedPreparedStatement remove(Object key) { + + ExtendedPreparedStatement o = super.remove(key); + if (o == null) { + missCounter++; + } else { + hitCounter++; + } + return o; + } + + /** + * additionally maintains put counter statistics. + */ + public ExtendedPreparedStatement put(String key, ExtendedPreparedStatement value) { + + putCounter++; + return super.put(key, value); + } + + /** + * will check to see if we need to remove entries and + * if so call the cacheCleanup.cleanupEldestLRUCacheEntry() if + * one has been set. + */ + protected boolean removeEldestEntry(Map.Entry eldest) { + + if (size() < maxSize) { + return false; + } + + removeCounter++; + + try { + ExtendedPreparedStatement pstmt = eldest.getValue(); + pstmt.closeDestroy(); + } catch (SQLException e) { + logger.log(Level.SEVERE, "Error closing ExtendedPreparedStatement", e); + } + return true; + } + + +} + diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/SimpleAlerter.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/SimpleAlerter.java index 3f610c26a..2d4313bd5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/SimpleAlerter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/SimpleAlerter.java @@ -1,125 +1,108 @@ -/** - * 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.lib.sql; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebeaninternal.server.lib.util.MailEvent; -import com.avaje.ebeaninternal.server.lib.util.MailListener; -import com.avaje.ebeaninternal.server.lib.util.MailMessage; -import com.avaje.ebeaninternal.server.lib.util.MailSender; - -/** - * A simple smtp email alert that sends a email message - * on dataSourceDown and dataSourceUp etc. - *
    - *
  • alert.fromuser = the from user name - *
  • alert.fromemail = the from email account - *
  • alert.toemail = comma delimited list of email accounts to email - *
  • alert.mailserver = the smpt server name - *
- */ -public class SimpleAlerter implements DataSourceAlertListener, MailListener { - - private static final Logger logger = Logger.getLogger(SimpleAlerter.class.getName()); - - //boolean sendInBackGround = true; - - /** - * Create a SimpleAlerter. - */ - public SimpleAlerter() { - } - - /** - * If the email failed then log the error. - */ - public void handleEvent(MailEvent event) { - Throwable e = event.getError(); - if (e != null){ - logger.log(Level.SEVERE, null, e); - } - } - - /** - * Send the dataSource down alert. - */ - public void dataSourceDown(String dataSourceName) { - String msg = getSubject(true, dataSourceName); - sendMessage(msg, msg); - } - - /** - * Send the dataSource up alert. - */ - public void dataSourceUp(String dataSourceName) { - String msg = getSubject(false, dataSourceName); - sendMessage(msg, msg); - } - - /** - * Send the warning message. - */ - public void warning(String subject, String msg) { - sendMessage(subject, msg); - } - - private String getSubject(boolean isDown, String dsName) { - String msg = "The DataSource "+dsName; - if (isDown){ - msg += " is DOWN!!"; - } else { - msg += " is UP."; - } - return msg; - } - - private void sendMessage(String subject, String msg){ - - String fromUser = GlobalProperties.get("alert.fromuser", null); - String fromEmail = GlobalProperties.get("alert.fromemail", null); - String mailServerName = GlobalProperties.get("alert.mailserver", null); - String toEmail = GlobalProperties.get("alert.toemail", null); - - if (mailServerName == null){ - //throw new RuntimeException("alert.mailserver not set..."); - return; - } - - MailMessage data = new MailMessage(); - data.setSender(fromUser, fromEmail); - data.addBodyLine(msg); - data.setSubject(subject); - - String[] toList = toEmail.split(","); - if (toList.length==0) { - throw new RuntimeException("alert.toemail has not been set?"); - } - for (int i = 0; i < toList.length; i++) { - data.addRecipient(null, toList[i].trim()); - } - - MailSender sender = new MailSender(mailServerName); - sender.setMailListener(this); - sender.sendInBackground(data); - } - - -} +package com.avaje.ebeaninternal.server.lib.sql; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebeaninternal.server.lib.util.MailEvent; +import com.avaje.ebeaninternal.server.lib.util.MailListener; +import com.avaje.ebeaninternal.server.lib.util.MailMessage; +import com.avaje.ebeaninternal.server.lib.util.MailSender; + +/** + * A simple smtp email alert that sends a email message + * on dataSourceDown and dataSourceUp etc. + *
    + *
  • alert.fromuser = the from user name + *
  • alert.fromemail = the from email account + *
  • alert.toemail = comma delimited list of email accounts to email + *
  • alert.mailserver = the smpt server name + *
+ */ +public class SimpleAlerter implements DataSourceAlertListener, MailListener { + + private static final Logger logger = Logger.getLogger(SimpleAlerter.class.getName()); + + //boolean sendInBackGround = true; + + /** + * Create a SimpleAlerter. + */ + public SimpleAlerter() { + } + + /** + * If the email failed then log the error. + */ + public void handleEvent(MailEvent event) { + Throwable e = event.getError(); + if (e != null){ + logger.log(Level.SEVERE, null, e); + } + } + + /** + * Send the dataSource down alert. + */ + public void dataSourceDown(String dataSourceName) { + String msg = getSubject(true, dataSourceName); + sendMessage(msg, msg); + } + + /** + * Send the dataSource up alert. + */ + public void dataSourceUp(String dataSourceName) { + String msg = getSubject(false, dataSourceName); + sendMessage(msg, msg); + } + + /** + * Send the warning message. + */ + public void warning(String subject, String msg) { + sendMessage(subject, msg); + } + + private String getSubject(boolean isDown, String dsName) { + String msg = "The DataSource "+dsName; + if (isDown){ + msg += " is DOWN!!"; + } else { + msg += " is UP."; + } + return msg; + } + + private void sendMessage(String subject, String msg){ + + String fromUser = GlobalProperties.get("alert.fromuser", null); + String fromEmail = GlobalProperties.get("alert.fromemail", null); + String mailServerName = GlobalProperties.get("alert.mailserver", null); + String toEmail = GlobalProperties.get("alert.toemail", null); + + if (mailServerName == null){ + //throw new RuntimeException("alert.mailserver not set..."); + return; + } + + MailMessage data = new MailMessage(); + data.setSender(fromUser, fromEmail); + data.addBodyLine(msg); + data.setSubject(subject); + + String[] toList = toEmail.split(","); + if (toList.length==0) { + throw new RuntimeException("alert.toemail has not been set?"); + } + for (int i = 0; i < toList.length; i++) { + data.addRecipient(null, toList[i].trim()); + } + + MailSender sender = new MailSender(mailServerName); + sender.setMailListener(this); + sender.sendInBackground(data); + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/TransactionIsolation.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/TransactionIsolation.java index 731b2d2b5..d637883f6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/TransactionIsolation.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/TransactionIsolation.java @@ -1,86 +1,69 @@ -/** - * 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.lib.sql; - -import java.sql.Connection; - -/** - * Helper object that can convert between transaction isolation descriptions and values. - * - */ -public class TransactionIsolation { - - - /** - * return the isolation level for a given string description. - */ - public static int getLevel(String level) { - level = level.toUpperCase(); - if (level.startsWith("TRANSACTION")){ - level = level.substring("TRANSACTION".length()); - } - level = level.replace("_", ""); - if ("NONE".equalsIgnoreCase(level)){ - return Connection.TRANSACTION_NONE; - } - if ("READCOMMITTED".equalsIgnoreCase(level)){ - return Connection.TRANSACTION_READ_COMMITTED; - } - if ("READUNCOMMITTED".equalsIgnoreCase(level)){ - return Connection.TRANSACTION_READ_UNCOMMITTED; - } - if ("REPEATABLEREAD".equalsIgnoreCase(level)){ - return Connection.TRANSACTION_REPEATABLE_READ; - } - if ("SERIALIZABLE".equalsIgnoreCase(level)){ - return Connection.TRANSACTION_SERIALIZABLE; - } - - throw new RuntimeException("Transaction Isolaction level [" + level + "] is not known."); - } - - /** - * Return the string description of the transaction isolation level specified. - *

Returned value is one of NONE, READ_COMMITTED,READ_UNCOMMITTED, - * REPEATABLE_READ or SERIALIZABLE.

- * - * @param level the transaction isolation level as per java.sql.Connection - * @return the level description as a string. - */ - public static String getLevelDescription(int level) { - switch (level) { - case Connection.TRANSACTION_NONE : - return "NONE"; - case Connection.TRANSACTION_READ_COMMITTED : - return "READ_COMMITTED"; - case Connection.TRANSACTION_READ_UNCOMMITTED : - return "READ_UNCOMMITTED"; - case Connection.TRANSACTION_REPEATABLE_READ : - return "REPEATABLE_READ"; - case Connection.TRANSACTION_SERIALIZABLE : - return "SERIALIZABLE"; - case -1 : - return "NotSet"; - default : - throw new RuntimeException("Transaction Isolaction level [" + level + "] is not defined."); - } - } - - - -} +package com.avaje.ebeaninternal.server.lib.sql; + +import java.sql.Connection; + +/** + * Helper object that can convert between transaction isolation descriptions and values. + * + */ +public class TransactionIsolation { + + + /** + * return the isolation level for a given string description. + */ + public static int getLevel(String level) { + level = level.toUpperCase(); + if (level.startsWith("TRANSACTION")){ + level = level.substring("TRANSACTION".length()); + } + level = level.replace("_", ""); + if ("NONE".equalsIgnoreCase(level)){ + return Connection.TRANSACTION_NONE; + } + if ("READCOMMITTED".equalsIgnoreCase(level)){ + return Connection.TRANSACTION_READ_COMMITTED; + } + if ("READUNCOMMITTED".equalsIgnoreCase(level)){ + return Connection.TRANSACTION_READ_UNCOMMITTED; + } + if ("REPEATABLEREAD".equalsIgnoreCase(level)){ + return Connection.TRANSACTION_REPEATABLE_READ; + } + if ("SERIALIZABLE".equalsIgnoreCase(level)){ + return Connection.TRANSACTION_SERIALIZABLE; + } + + throw new RuntimeException("Transaction Isolaction level [" + level + "] is not known."); + } + + /** + * Return the string description of the transaction isolation level specified. + *

Returned value is one of NONE, READ_COMMITTED,READ_UNCOMMITTED, + * REPEATABLE_READ or SERIALIZABLE.

+ * + * @param level the transaction isolation level as per java.sql.Connection + * @return the level description as a string. + */ + public static String getLevelDescription(int level) { + switch (level) { + case Connection.TRANSACTION_NONE : + return "NONE"; + case Connection.TRANSACTION_READ_COMMITTED : + return "READ_COMMITTED"; + case Connection.TRANSACTION_READ_UNCOMMITTED : + return "READ_UNCOMMITTED"; + case Connection.TRANSACTION_REPEATABLE_READ : + return "REPEATABLE_READ"; + case Connection.TRANSACTION_SERIALIZABLE : + return "SERIALIZABLE"; + case -1 : + return "NotSet"; + default : + throw new RuntimeException("Transaction Isolaction level [" + level + "] is not defined."); + } + } + + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/thread/PooledThread.java b/src/main/java/com/avaje/ebeaninternal/server/lib/thread/PooledThread.java index 2b6878038..b3caec558 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/thread/PooledThread.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/thread/PooledThread.java @@ -1,276 +1,259 @@ -/** - * 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.lib.thread; - -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A thread that belongs to a ThreadPool. It will return to the Threadpool when - * it has finished its assigned task. - */ -public class PooledThread implements Runnable { - - private static final Logger logger = Logger.getLogger(PooledThread.class.getName()); - - /** - * Create the PooledThread. - */ - protected PooledThread(ThreadPool threadPool, String name, boolean isDaemon, - Integer threadPriority) { - - this.name = name; - this.threadPool = threadPool; - this.lastUsedTime = System.currentTimeMillis(); - - thread = new Thread(this, name); - thread.setDaemon(isDaemon); - - if (threadPriority != null) { - thread.setPriority(threadPriority.intValue()); - } - //thread.start(); - } - - protected void start() { - thread.start(); - } - - /** - * Assign work to this thread. The thread will notify the listener when it - * has finished the work. - */ - public boolean assignWork(Work work) { - synchronized (workMonitor) { - this.work = work; - workMonitor.notifyAll(); - } - return true; - } - - /** - * process any assigned work until stopped or interrupted. - */ - public void run() { - // process assigned work until we receive a shutdown signal - synchronized (workMonitor) { - while (!isStopping) { - try { - if (work == null) { - workMonitor.wait(); - } - } catch (InterruptedException e) { - } - doTheWork(); - } - } - - // Tell stop() we have shut ourselves down successfully - synchronized (threadMonitor) { - threadMonitor.notifyAll(); - } - //Log.debug("PooledThread [" + getName() + "] finished "); - isStopped = true; - } - - /** - * Actually do the work and gather the appropriate measures. - */ - private void doTheWork() { - if (isStopping){ - return; - } - - long startTime = System.currentTimeMillis(); - if (work == null) { - // probably shutting down the thread - - } else { - try { - work.setStartTime(startTime); - work.getRunnable().run(); - - } catch (Throwable ex) { - logger.log(Level.SEVERE, null, ex); - - if (wasInterrupted) { - this.isStopping = true; - threadPool.removeThread(this); - logger.info("PooledThread [" + name + "] removed due to interrupt"); - try { - thread.interrupt(); - } catch (Exception e){ - String msg = "Error interrupting PooledThead["+name+"]"; - logger.log(Level.SEVERE, msg, e); - } - return; - } - } - } - lastUsedTime = System.currentTimeMillis(); - totalWorkCount++; - totalWorkExecutionTime = totalWorkExecutionTime + lastUsedTime - startTime; - this.work = null; - threadPool.returnThread(this); - - } - - /** - * Try to interrupt the thread. - *

- * If the Thread was interrupted then it will be removed from the pool. - *

- */ - public void interrupt() { - - // set a flag so doTheWork knows that it was interrupted - // and removes rather than returns - wasInterrupted = true; - try { - thread.interrupt(); - - } catch (SecurityException ex) { - wasInterrupted = false; - throw ex; - } - } - - /** - * Returns true if the thread has finished. - */ - public boolean isStopped() { - return isStopped; - } - - /** - * Stop the thread relatively nicely. It will wait a maximum of 10 seconds - * for it to complete any existing work. - */ - protected void stop() { - isStopping = true; - - synchronized (threadMonitor) { - - assignWork(null); - //trace("stop assigned null work..."); - try { - threadMonitor.wait(10000); - } catch (InterruptedException e) { - ; - } - - } - - thread = null; - threadPool.removeThread(this); - } - - /** - * return the name of the thread. - */ - public String getName() { - return name; - } - /** - * Returns the currently executing work, otherwise null. - */ - public Work getWork() { - return work; - } - - /** - * The total number of jobs this thread has run. - */ - public int getTotalWorkCount() { - return totalWorkCount; - } - - /** - * The total time for performing all assigned work. - */ - public long getTotalWorkExecutionTime() { - return totalWorkExecutionTime; - } - - /** - * Returns the time this thread was last used. - */ - public long getLastUsedTime() { - return lastUsedTime; - } - - /** - * Flag to indicate that the thread was interrupted. - */ - private boolean wasInterrupted = false; - - /** - * The time the thread was last used. - */ - private long lastUsedTime; - - /** - * The work to run - */ - private Work work = null; - - /** - * Set to indicate the thread is stopping. - */ - private boolean isStopping = false; - - /** - * Set when the thread has stopped. - */ - private boolean isStopped = false; - /** - * The background thread. - */ - private Thread thread = null; - - /** - * The pool this worker belongs to. - */ - private ThreadPool threadPool; - - /** - * The name of the Thread - */ - private String name = null; - - /** - * The thread synchronization object. - */ - private Object threadMonitor = new Object(); - - /** - * The monitor for work notification. - */ - private Object workMonitor = new Object(); - - /** - * Total number of work performed. - */ - private int totalWorkCount = 0; - - /** - * Total work execution time. - */ - private long totalWorkExecutionTime = 0; - -} +package com.avaje.ebeaninternal.server.lib.thread; + +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A thread that belongs to a ThreadPool. It will return to the Threadpool when + * it has finished its assigned task. + */ +public class PooledThread implements Runnable { + + private static final Logger logger = Logger.getLogger(PooledThread.class.getName()); + + /** + * Create the PooledThread. + */ + protected PooledThread(ThreadPool threadPool, String name, boolean isDaemon, + Integer threadPriority) { + + this.name = name; + this.threadPool = threadPool; + this.lastUsedTime = System.currentTimeMillis(); + + thread = new Thread(this, name); + thread.setDaemon(isDaemon); + + if (threadPriority != null) { + thread.setPriority(threadPriority.intValue()); + } + //thread.start(); + } + + protected void start() { + thread.start(); + } + + /** + * Assign work to this thread. The thread will notify the listener when it + * has finished the work. + */ + public boolean assignWork(Work work) { + synchronized (workMonitor) { + this.work = work; + workMonitor.notifyAll(); + } + return true; + } + + /** + * process any assigned work until stopped or interrupted. + */ + public void run() { + // process assigned work until we receive a shutdown signal + synchronized (workMonitor) { + while (!isStopping) { + try { + if (work == null) { + workMonitor.wait(); + } + } catch (InterruptedException e) { + } + doTheWork(); + } + } + + // Tell stop() we have shut ourselves down successfully + synchronized (threadMonitor) { + threadMonitor.notifyAll(); + } + //Log.debug("PooledThread [" + getName() + "] finished "); + isStopped = true; + } + + /** + * Actually do the work and gather the appropriate measures. + */ + private void doTheWork() { + if (isStopping){ + return; + } + + long startTime = System.currentTimeMillis(); + if (work == null) { + // probably shutting down the thread + + } else { + try { + work.setStartTime(startTime); + work.getRunnable().run(); + + } catch (Throwable ex) { + logger.log(Level.SEVERE, null, ex); + + if (wasInterrupted) { + this.isStopping = true; + threadPool.removeThread(this); + logger.info("PooledThread [" + name + "] removed due to interrupt"); + try { + thread.interrupt(); + } catch (Exception e){ + String msg = "Error interrupting PooledThead["+name+"]"; + logger.log(Level.SEVERE, msg, e); + } + return; + } + } + } + lastUsedTime = System.currentTimeMillis(); + totalWorkCount++; + totalWorkExecutionTime = totalWorkExecutionTime + lastUsedTime - startTime; + this.work = null; + threadPool.returnThread(this); + + } + + /** + * Try to interrupt the thread. + *

+ * If the Thread was interrupted then it will be removed from the pool. + *

+ */ + public void interrupt() { + + // set a flag so doTheWork knows that it was interrupted + // and removes rather than returns + wasInterrupted = true; + try { + thread.interrupt(); + + } catch (SecurityException ex) { + wasInterrupted = false; + throw ex; + } + } + + /** + * Returns true if the thread has finished. + */ + public boolean isStopped() { + return isStopped; + } + + /** + * Stop the thread relatively nicely. It will wait a maximum of 10 seconds + * for it to complete any existing work. + */ + protected void stop() { + isStopping = true; + + synchronized (threadMonitor) { + + assignWork(null); + //trace("stop assigned null work..."); + try { + threadMonitor.wait(10000); + } catch (InterruptedException e) { + ; + } + + } + + thread = null; + threadPool.removeThread(this); + } + + /** + * return the name of the thread. + */ + public String getName() { + return name; + } + /** + * Returns the currently executing work, otherwise null. + */ + public Work getWork() { + return work; + } + + /** + * The total number of jobs this thread has run. + */ + public int getTotalWorkCount() { + return totalWorkCount; + } + + /** + * The total time for performing all assigned work. + */ + public long getTotalWorkExecutionTime() { + return totalWorkExecutionTime; + } + + /** + * Returns the time this thread was last used. + */ + public long getLastUsedTime() { + return lastUsedTime; + } + + /** + * Flag to indicate that the thread was interrupted. + */ + private boolean wasInterrupted = false; + + /** + * The time the thread was last used. + */ + private long lastUsedTime; + + /** + * The work to run + */ + private Work work = null; + + /** + * Set to indicate the thread is stopping. + */ + private boolean isStopping = false; + + /** + * Set when the thread has stopped. + */ + private boolean isStopped = false; + /** + * The background thread. + */ + private Thread thread = null; + + /** + * The pool this worker belongs to. + */ + private ThreadPool threadPool; + + /** + * The name of the Thread + */ + private String name = null; + + /** + * The thread synchronization object. + */ + private Object threadMonitor = new Object(); + + /** + * The monitor for work notification. + */ + private Object workMonitor = new Object(); + + /** + * Total number of work performed. + */ + private int totalWorkCount = 0; + + /** + * Total work execution time. + */ + private long totalWorkExecutionTime = 0; + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/thread/ThreadPool.java b/src/main/java/com/avaje/ebeaninternal/server/lib/thread/ThreadPool.java index ef0ae72e5..6af40e9f1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/thread/ThreadPool.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/thread/ThreadPool.java @@ -1,489 +1,472 @@ -/** - * 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.lib.thread; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Vector; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * This is a pool of threads which can be assigned work. - *

- * The Pool will automatically grow as required up to its maximum pool size. The - * Pool will be automatically shrink by trimming threads that have been idle for - * some time. - *

- */ -public class ThreadPool { - - private static final Logger logger = Logger.getLogger(ThreadPool.class.getName()); - - /** - * The max idle time used to trim idle threads from the pool. - */ - private long maxIdleTime; - - /** - * The name of the pool - */ - private String poolName; - - /** - * The initial pool size. - */ - private int minSize; - - /** - * Whether or not the threads are going to be Daemon threads. - */ - private boolean isDaemon; - - /** - * Flag to indicate that the pool is being shutdown. - */ - private boolean isStopping = false; - - /** - * The priority or the threads. Can be null, in which case the threads have - * the default priority. - */ - private Integer threadPriority; - - /** - * Incrementing int for thread name. NB: currentThreadCount will go up and - * down as the pool grows and shrinks. - */ - private int uniqueThreadID; - - /** - * List of PooledThread that are free for work. - */ - private Vector freeList = new Vector(); - - /** - * List of PooledThread that are busy. - */ - private Vector busyList = new Vector(); - - /** - * List holding queued work. - */ - private Vector workOverflowQueue = new Vector(); - - /** - * The maximum number of threads to grow to. Hitting this limit will have - * performance ramifications. - */ - private int maxSize = 100; - - /** - * Flag that the pool should terminate all the threads and stop. - */ - private boolean stopThePool; - - - /** - * Create the ThreadPool. - */ - public ThreadPool(String poolName, boolean isDaemon, Integer threadPriority) { - - this.poolName = poolName; - this.stopThePool = false; - this.isDaemon = isDaemon; - this.threadPriority = threadPriority; - } - - /** - * Return true if the pool is shutting down. - */ - public boolean isStopping() { - return isStopping; - } - - /** - * Return the name of the thread pool. - */ - public String getName() { - return poolName; - } - - /** - * Set the minimum size the pool should try to maintain. - */ - public void setMinSize(int minSize) { - if (minSize > 0) { - if (minSize > maxSize) { - this.maxSize = minSize; - } - this.minSize = minSize; - maintainPoolSize(); - } - } - - /** - * Return the minimum size the pool should maintain. - */ - public int getMinSize() { - return minSize; - } - - /** - * Set the maximum size the pool should grow to. - */ - public void setMaxSize(int maxSize) { - if (maxSize > 0) { - if (minSize > maxSize) { - minSize = maxSize; - } - this.maxSize = maxSize; - maintainPoolSize(); - } - } - - /** - * Return the maximum size this pool can grow to. - */ - public int getMaxSize() { - return maxSize; - } - - /** - * Return the total number of busy and free threads in the pool. - */ - public int size() { - return busyList.size() + freeList.size(); - } - - /** - * Return the number of currently busy threads. - */ - public int getBusyCount() { - return busyList.size(); - } - - /** - * Assign a task to the thread pool, specifing the options to wait or queue - * the task if the pool is fully busy and can't grow. - *

- * When the pool is fully busy... - *

- *

- * addToQueue=true -> work is added to queue, returns false
- * addToQueue=false -> work is not done or queued, returns false
- *

- * - * @param work the runnable work to do. - * @param addToQueueIfFull If the pool is maxed out and this is true then it - * queues the Runnable. - */ - public boolean assign(Runnable work, boolean addToQueueIfFull) { - - if (stopThePool) { - throw new RuntimeException("Pool is stopping... no more work please."); - } - - Work runWork = new Work(work); - - // get the next available thread in the pool (block) - PooledThread thread = getNextAvailableThread(); - if (thread != null) { - // assign the work to that thread - busyList.add(thread); - thread.assignWork(runWork); - return true; - - } else { - if (addToQueueIfFull) { - runWork.setEnterQueueTime(System.currentTimeMillis()); - workOverflowQueue.add(runWork); - } - return false; - } - } - - /** - * Remove the thread from the pool. The thread should be stopped before it - * is removed. - */ - protected void removeThread(PooledThread thread) { - synchronized (freeList) { - busyList.remove(thread); - freeList.remove(thread); - freeList.notify(); - - // if (ThreadPoolManager.getDebugLevel()>0){ - //Log.debug("PooledThread stopped [" + getName() + "]"); - // } - } - } - - /** - * fired when a Thread from the pool has finished, and can be put back into - * the pool. - */ - protected void returnThread(PooledThread thread) { - - synchronized (freeList) { - - // deregister from the busyList - busyList.remove(thread); - - if (!workOverflowQueue.isEmpty()) { - // get the first bit of work off the queue - Work queuedWork = (Work) workOverflowQueue.remove(0); - - // work out the queue time and counts etc - queuedWork.setExitQueueTime(System.currentTimeMillis()); - busyList.add(thread); - thread.assignWork(queuedWork); - - } else { - // put the thread back onto the available list - freeList.add(thread); - // tell shutdown() one has returned - freeList.notify(); - } - } - } - - - /** - * Get the next available thread. Block until thread is available. NB: The - * dispatcher is blocked but work can still be assigned to the dispatcher in - * a non-blocking way - */ - private PooledThread getNextAvailableThread() { - - synchronized (freeList) { - if (!freeList.isEmpty()) { - return (PooledThread) freeList.remove(0); - } - if (size() < maxSize) { - return growPool(true); - } - return null; - } - } - - /** - * Return an Iterator of PooledThread that are currently running. You should - * only use this for display. Use the getPooledThread() or interrupt() - * methods to interrupt a particular thread. - * - * @return an Iterator of busy PooledThread's. - */ - public Iterator getBusyThreads() { - synchronized (freeList) { - return busyList.iterator(); - } - } - - /** - * Shutdown the threadpool stopping all the threads. This will - * wait until any busy threads have finished their assigned work. - */ - protected void shutdown() { - - synchronized (freeList) { - isStopping = true; - - int size = size(); - - if (size > 0){ - String msg = null; - msg = "ThreadPool [" + poolName + "] Shutting down; threadCount[" + size() - + "] busyCount[" + getBusyCount() + "]"; - - logger.info(msg); - } - - stopThePool = true; - - while (!freeList.isEmpty()) { - PooledThread thread = (PooledThread) freeList.remove(0); - thread.stop(); - } - - try { - while (getBusyCount() > 0) { - // synchronized (freeList) { - String msg = "ThreadPool [" + poolName + "] has [" + getBusyCount() - + "] busy threads, waiting for those to finish."; - logger.info(msg); - - Iterator busyThreads = getBusyThreads(); - while (busyThreads.hasNext()) { - PooledThread busyThread = (PooledThread) busyThreads.next(); - - String threadName = busyThread.getName(); - Work work = busyThread.getWork(); - - String busymsg = "Busy thread [" + threadName + "] work[" + work + "]"; - logger.info(busymsg); - } - // trace("wait for a busy thread to be put back into - // freeList"); - freeList.wait(); - PooledThread thread = (PooledThread) freeList.remove(0); - // trace("wait finished...now shut it down [" + - // thread.getName() + "]"); - if (thread != null) { - thread.stop(); - } - } - - - } catch (InterruptedException e) { - logger.log(Level.SEVERE, null, e); - } - } - } - - /** - * Trim or grow the pool leaving at least min free. - */ - protected void maintainPoolSize() { - synchronized (freeList) { - if (isStopping) { - // don't bother as the pool is shutting down - return; - } - - int numToStop = size() - minSize; - if (numToStop > 0) { - // should trim idle threads as we are over the minSize - long usedAfter = System.currentTimeMillis() - maxIdleTime; - ArrayList stopList = new ArrayList(); - Iterator it = freeList.iterator(); - while (it.hasNext() && numToStop > 0) { - PooledThread thread = (PooledThread) it.next(); - if (thread.getLastUsedTime() < usedAfter) { - stopList.add(thread); - numToStop--; - } - } - Iterator stopIt = stopList.iterator(); - while (stopIt.hasNext()) { - PooledThread thread = (PooledThread) stopIt.next(); - thread.stop(); - } - } - int numToAdd = minSize - size(); - if (numToAdd > 0) { - // should add some more to the pool - for (int i = 0; i < numToAdd; i++) { - growPool(false); - } - } - } - } - - /** - * Interrupt a named thread that is currently busy. - *

- * Returns the thread that was interrupted or null if the thread - * was not found. If the thread was interrupted then it will - * automatically be stopped and removed from the pool. - *

- *

- * Note that it may take some time to actually interrupt the thread so - * an immediate test to see if the thread stopped will probably be wrong. - *


-     * ThreadPool test = ThreadPoolManager.getThreadPool("test");
-     * PooledThread pt = test.interrupt("test.1");
-     * if (pt == null) {
-     *      // the thread was not found, perhaps finished?
-     * } else {
-     *      // give interrupt a little time to execute
-     *      Thread.sleep(1000);
-     *      boolean hasStopped = pt.isStopped();
-     *      //..
-     * }
-     * 
- *

- * @return the thread that was interrupted - */ - public PooledThread interrupt(String threadName) { - PooledThread thread = getBusyThread(threadName); - if (thread != null) { - thread.interrupt(); - return thread; - } - return null; - } - - /** - * Find a thread using its name from the busy list. Returns null if the - * thread is not found in the busy list. - */ - public PooledThread getBusyThread(String threadName) { - synchronized (freeList) { - Iterator it = getBusyThreads(); - while (it.hasNext()) { - PooledThread pt = (PooledThread) it.next(); - if (pt.getName().equals(threadName)) { - return pt; - } - } - return null; - } - } - - /** - * Grow the pool with the option of either putting it on the available list, - * or returning it. - */ - private PooledThread growPool(boolean andReturn) { - - synchronized (freeList) { - - String threadName = poolName + "." + uniqueThreadID++; - PooledThread bgw = new PooledThread(this, threadName, isDaemon, threadPriority); - bgw.start(); - - if (logger.isLoggable(Level.FINE)) { - logger.fine("ThreadPool grow created [" + threadName + "] size[" + size() + "]"); - } - if (andReturn) { - return bgw; - } else { - freeList.add(bgw); - return null; - } - } - } - - /** - * Return the maximum amount of time in millis that Threads can be idle - * before they are trimmed. - */ - public long getMaxIdleTime() { - return maxIdleTime; - } - - /** - * Set the maxiumium amount of time in millis that Threads can be idle - * before they are trimed. - */ - public void setMaxIdleTime(long maxIdleTime) { - this.maxIdleTime = maxIdleTime; - } - -} +package com.avaje.ebeaninternal.server.lib.thread; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Vector; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This is a pool of threads which can be assigned work. + *

+ * The Pool will automatically grow as required up to its maximum pool size. The + * Pool will be automatically shrink by trimming threads that have been idle for + * some time. + *

+ */ +public class ThreadPool { + + private static final Logger logger = Logger.getLogger(ThreadPool.class.getName()); + + /** + * The max idle time used to trim idle threads from the pool. + */ + private long maxIdleTime; + + /** + * The name of the pool + */ + private String poolName; + + /** + * The initial pool size. + */ + private int minSize; + + /** + * Whether or not the threads are going to be Daemon threads. + */ + private boolean isDaemon; + + /** + * Flag to indicate that the pool is being shutdown. + */ + private boolean isStopping = false; + + /** + * The priority or the threads. Can be null, in which case the threads have + * the default priority. + */ + private Integer threadPriority; + + /** + * Incrementing int for thread name. NB: currentThreadCount will go up and + * down as the pool grows and shrinks. + */ + private int uniqueThreadID; + + /** + * List of PooledThread that are free for work. + */ + private Vector freeList = new Vector(); + + /** + * List of PooledThread that are busy. + */ + private Vector busyList = new Vector(); + + /** + * List holding queued work. + */ + private Vector workOverflowQueue = new Vector(); + + /** + * The maximum number of threads to grow to. Hitting this limit will have + * performance ramifications. + */ + private int maxSize = 100; + + /** + * Flag that the pool should terminate all the threads and stop. + */ + private boolean stopThePool; + + + /** + * Create the ThreadPool. + */ + public ThreadPool(String poolName, boolean isDaemon, Integer threadPriority) { + + this.poolName = poolName; + this.stopThePool = false; + this.isDaemon = isDaemon; + this.threadPriority = threadPriority; + } + + /** + * Return true if the pool is shutting down. + */ + public boolean isStopping() { + return isStopping; + } + + /** + * Return the name of the thread pool. + */ + public String getName() { + return poolName; + } + + /** + * Set the minimum size the pool should try to maintain. + */ + public void setMinSize(int minSize) { + if (minSize > 0) { + if (minSize > maxSize) { + this.maxSize = minSize; + } + this.minSize = minSize; + maintainPoolSize(); + } + } + + /** + * Return the minimum size the pool should maintain. + */ + public int getMinSize() { + return minSize; + } + + /** + * Set the maximum size the pool should grow to. + */ + public void setMaxSize(int maxSize) { + if (maxSize > 0) { + if (minSize > maxSize) { + minSize = maxSize; + } + this.maxSize = maxSize; + maintainPoolSize(); + } + } + + /** + * Return the maximum size this pool can grow to. + */ + public int getMaxSize() { + return maxSize; + } + + /** + * Return the total number of busy and free threads in the pool. + */ + public int size() { + return busyList.size() + freeList.size(); + } + + /** + * Return the number of currently busy threads. + */ + public int getBusyCount() { + return busyList.size(); + } + + /** + * Assign a task to the thread pool, specifing the options to wait or queue + * the task if the pool is fully busy and can't grow. + *

+ * When the pool is fully busy... + *

+ *

+ * addToQueue=true -> work is added to queue, returns false
+ * addToQueue=false -> work is not done or queued, returns false
+ *

+ * + * @param work the runnable work to do. + * @param addToQueueIfFull If the pool is maxed out and this is true then it + * queues the Runnable. + */ + public boolean assign(Runnable work, boolean addToQueueIfFull) { + + if (stopThePool) { + throw new RuntimeException("Pool is stopping... no more work please."); + } + + Work runWork = new Work(work); + + // get the next available thread in the pool (block) + PooledThread thread = getNextAvailableThread(); + if (thread != null) { + // assign the work to that thread + busyList.add(thread); + thread.assignWork(runWork); + return true; + + } else { + if (addToQueueIfFull) { + runWork.setEnterQueueTime(System.currentTimeMillis()); + workOverflowQueue.add(runWork); + } + return false; + } + } + + /** + * Remove the thread from the pool. The thread should be stopped before it + * is removed. + */ + protected void removeThread(PooledThread thread) { + synchronized (freeList) { + busyList.remove(thread); + freeList.remove(thread); + freeList.notify(); + + // if (ThreadPoolManager.getDebugLevel()>0){ + //Log.debug("PooledThread stopped [" + getName() + "]"); + // } + } + } + + /** + * fired when a Thread from the pool has finished, and can be put back into + * the pool. + */ + protected void returnThread(PooledThread thread) { + + synchronized (freeList) { + + // deregister from the busyList + busyList.remove(thread); + + if (!workOverflowQueue.isEmpty()) { + // get the first bit of work off the queue + Work queuedWork = (Work) workOverflowQueue.remove(0); + + // work out the queue time and counts etc + queuedWork.setExitQueueTime(System.currentTimeMillis()); + busyList.add(thread); + thread.assignWork(queuedWork); + + } else { + // put the thread back onto the available list + freeList.add(thread); + // tell shutdown() one has returned + freeList.notify(); + } + } + } + + + /** + * Get the next available thread. Block until thread is available. NB: The + * dispatcher is blocked but work can still be assigned to the dispatcher in + * a non-blocking way + */ + private PooledThread getNextAvailableThread() { + + synchronized (freeList) { + if (!freeList.isEmpty()) { + return (PooledThread) freeList.remove(0); + } + if (size() < maxSize) { + return growPool(true); + } + return null; + } + } + + /** + * Return an Iterator of PooledThread that are currently running. You should + * only use this for display. Use the getPooledThread() or interrupt() + * methods to interrupt a particular thread. + * + * @return an Iterator of busy PooledThread's. + */ + public Iterator getBusyThreads() { + synchronized (freeList) { + return busyList.iterator(); + } + } + + /** + * Shutdown the threadpool stopping all the threads. This will + * wait until any busy threads have finished their assigned work. + */ + protected void shutdown() { + + synchronized (freeList) { + isStopping = true; + + int size = size(); + + if (size > 0){ + String msg = null; + msg = "ThreadPool [" + poolName + "] Shutting down; threadCount[" + size() + + "] busyCount[" + getBusyCount() + "]"; + + logger.info(msg); + } + + stopThePool = true; + + while (!freeList.isEmpty()) { + PooledThread thread = (PooledThread) freeList.remove(0); + thread.stop(); + } + + try { + while (getBusyCount() > 0) { + // synchronized (freeList) { + String msg = "ThreadPool [" + poolName + "] has [" + getBusyCount() + + "] busy threads, waiting for those to finish."; + logger.info(msg); + + Iterator busyThreads = getBusyThreads(); + while (busyThreads.hasNext()) { + PooledThread busyThread = (PooledThread) busyThreads.next(); + + String threadName = busyThread.getName(); + Work work = busyThread.getWork(); + + String busymsg = "Busy thread [" + threadName + "] work[" + work + "]"; + logger.info(busymsg); + } + // trace("wait for a busy thread to be put back into + // freeList"); + freeList.wait(); + PooledThread thread = (PooledThread) freeList.remove(0); + // trace("wait finished...now shut it down [" + + // thread.getName() + "]"); + if (thread != null) { + thread.stop(); + } + } + + + } catch (InterruptedException e) { + logger.log(Level.SEVERE, null, e); + } + } + } + + /** + * Trim or grow the pool leaving at least min free. + */ + protected void maintainPoolSize() { + synchronized (freeList) { + if (isStopping) { + // don't bother as the pool is shutting down + return; + } + + int numToStop = size() - minSize; + if (numToStop > 0) { + // should trim idle threads as we are over the minSize + long usedAfter = System.currentTimeMillis() - maxIdleTime; + ArrayList stopList = new ArrayList(); + Iterator it = freeList.iterator(); + while (it.hasNext() && numToStop > 0) { + PooledThread thread = (PooledThread) it.next(); + if (thread.getLastUsedTime() < usedAfter) { + stopList.add(thread); + numToStop--; + } + } + Iterator stopIt = stopList.iterator(); + while (stopIt.hasNext()) { + PooledThread thread = (PooledThread) stopIt.next(); + thread.stop(); + } + } + int numToAdd = minSize - size(); + if (numToAdd > 0) { + // should add some more to the pool + for (int i = 0; i < numToAdd; i++) { + growPool(false); + } + } + } + } + + /** + * Interrupt a named thread that is currently busy. + *

+ * Returns the thread that was interrupted or null if the thread + * was not found. If the thread was interrupted then it will + * automatically be stopped and removed from the pool. + *

+ *

+ * Note that it may take some time to actually interrupt the thread so + * an immediate test to see if the thread stopped will probably be wrong. + *


+     * ThreadPool test = ThreadPoolManager.getThreadPool("test");
+     * PooledThread pt = test.interrupt("test.1");
+     * if (pt == null) {
+     *      // the thread was not found, perhaps finished?
+     * } else {
+     *      // give interrupt a little time to execute
+     *      Thread.sleep(1000);
+     *      boolean hasStopped = pt.isStopped();
+     *      //..
+     * }
+     * 
+ *

+ * @return the thread that was interrupted + */ + public PooledThread interrupt(String threadName) { + PooledThread thread = getBusyThread(threadName); + if (thread != null) { + thread.interrupt(); + return thread; + } + return null; + } + + /** + * Find a thread using its name from the busy list. Returns null if the + * thread is not found in the busy list. + */ + public PooledThread getBusyThread(String threadName) { + synchronized (freeList) { + Iterator it = getBusyThreads(); + while (it.hasNext()) { + PooledThread pt = (PooledThread) it.next(); + if (pt.getName().equals(threadName)) { + return pt; + } + } + return null; + } + } + + /** + * Grow the pool with the option of either putting it on the available list, + * or returning it. + */ + private PooledThread growPool(boolean andReturn) { + + synchronized (freeList) { + + String threadName = poolName + "." + uniqueThreadID++; + PooledThread bgw = new PooledThread(this, threadName, isDaemon, threadPriority); + bgw.start(); + + if (logger.isLoggable(Level.FINE)) { + logger.fine("ThreadPool grow created [" + threadName + "] size[" + size() + "]"); + } + if (andReturn) { + return bgw; + } else { + freeList.add(bgw); + return null; + } + } + } + + /** + * Return the maximum amount of time in millis that Threads can be idle + * before they are trimmed. + */ + public long getMaxIdleTime() { + return maxIdleTime; + } + + /** + * Set the maxiumium amount of time in millis that Threads can be idle + * before they are trimed. + */ + public void setMaxIdleTime(long maxIdleTime) { + this.maxIdleTime = maxIdleTime; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/thread/ThreadPoolManager.java b/src/main/java/com/avaje/ebeaninternal/server/lib/thread/ThreadPoolManager.java index cc3915ec1..55c600153 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/thread/ThreadPoolManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/thread/ThreadPoolManager.java @@ -1,194 +1,177 @@ -/** - * 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.lib.thread; - -import java.util.Iterator; -import java.util.concurrent.ConcurrentHashMap; - -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebeaninternal.server.lib.BackgroundThread; - -/** - * Singleton that manages a list of named ThreadPools. - */ -public class ThreadPoolManager implements Runnable { - - private static final class Single { - private static final ThreadPoolManager me = new ThreadPoolManager(); - } - - private static int debugLevel = 0; - - /** - * set when the pools are being shutdown. - */ - private boolean isShuttingDown = false; - - /** - * Holds all the thread pools. - */ - private ConcurrentHashMap threadPoolCache = new ConcurrentHashMap(); - - /** - * The default time threads are idle before they are stopped and removed. - * This can occur when the pool grows larger than the min size and then goes idle - * for some time. - */ - private long defaultIdleTime; - - private ThreadPoolManager() { - initialise(); - } - - private void initialise() { - - debugLevel = GlobalProperties.getInt("threadpool.debugLevel", 0); - - defaultIdleTime = 1000 * GlobalProperties.getInt("threadpool.idletime", 60); - - int freqIsSecs = GlobalProperties.getInt("threadpool.sleeptime", 30); - - BackgroundThread.add(freqIsSecs, this); - } - - /** - * Set the debug level. - */ - public static void setDebugLevel(int level) { - debugLevel = level; - } - - /** - * Return the debug level. - */ - public static int getDebugLevel() { - return debugLevel; - } - - /** - * Periodically maintains the pool size. Stops threads that have - * been idle for too long and ensures the minimum number of threads. - *

- * To change this you can set the threadpool.idletime property:
- *
- * ## set threadpool idletime to 120 seconds
- * threadpool.idletime=120
- *

- */ - public void run() { - if (!isShuttingDown) { - maintainPoolSize(); - } - } - - /** - * Return the named thread pool. - */ - public static ThreadPool getThreadPool(String poolName) { - return Single.me.getPool(poolName); - } - - /** - * Return the named ThreadPool. If the ThreadPool doesn't exist it will be - * created. - */ - private ThreadPool getPool(String poolName) { - synchronized (this) { - ThreadPool threadPool = (ThreadPool) threadPoolCache.get(poolName); - if (threadPool == null) { - threadPool = createThreadPool(poolName); - threadPoolCache.put(poolName, threadPool); - } - return threadPool; - } - } - - /** - * Returns an iterator of ThreadPools. - *

- * Note that the ThreadPools should not be removed by the iterator. - *

- */ - public static Iterator pools() { - return Single.me.threadPoolCache.values().iterator(); - } - - /** - * Maintain the size of all the thread pools. - * Trims down to minimum size threads that have been idle for a while. - * Adds threads if it is short of the minimum size. - */ - private void maintainPoolSize() { - if (isShuttingDown){ - return; - } - synchronized (this) { - - Iterator e = pools(); - while (e.hasNext()) { - ThreadPool pool = (ThreadPool) e.next(); - pool.maintainPoolSize(); - } - } - } - - /** - * Shutdown all the ThreadPools nicely. - * This will wait for all currently runnable and queued work to finish. - */ - public static void shutdown() { - Single.me.shutdownPools(); - } - - private void shutdownPools() { - synchronized (this) { - isShuttingDown = true; - Iterator i = pools(); - while (i.hasNext()) { - ThreadPool pool = (ThreadPool) i.next(); - pool.shutdown(); - } - } - } - - private ThreadPool createThreadPool(String poolName) { - - int min = GlobalProperties.getInt("threadpool." + poolName + ".min", 0); - int max = GlobalProperties.getInt("threadpool." + poolName + ".max", 100); - - long idle = 1000 * GlobalProperties.getInt("threadpool." + poolName + ".idletime", -1); - if (idle < 0) { - idle = defaultIdleTime; - } - - boolean isDaemon = true; - Integer priority = null; - String threadPriority = GlobalProperties.get("threadpool." + poolName + ".priority", null); - if (threadPriority != null) { - priority = new Integer(threadPriority); - } - - ThreadPool newPool = new ThreadPool(poolName, isDaemon, priority); - newPool.setMaxSize(max); - newPool.setMinSize(min); - newPool.setMaxIdleTime(idle); - - return newPool; - } - -}; +package com.avaje.ebeaninternal.server.lib.thread; + +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; + +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebeaninternal.server.lib.BackgroundThread; + +/** + * Singleton that manages a list of named ThreadPools. + */ +public class ThreadPoolManager implements Runnable { + + private static final class Single { + private static final ThreadPoolManager me = new ThreadPoolManager(); + } + + private static int debugLevel = 0; + + /** + * set when the pools are being shutdown. + */ + private boolean isShuttingDown = false; + + /** + * Holds all the thread pools. + */ + private ConcurrentHashMap threadPoolCache = new ConcurrentHashMap(); + + /** + * The default time threads are idle before they are stopped and removed. + * This can occur when the pool grows larger than the min size and then goes idle + * for some time. + */ + private long defaultIdleTime; + + private ThreadPoolManager() { + initialise(); + } + + private void initialise() { + + debugLevel = GlobalProperties.getInt("threadpool.debugLevel", 0); + + defaultIdleTime = 1000 * GlobalProperties.getInt("threadpool.idletime", 60); + + int freqIsSecs = GlobalProperties.getInt("threadpool.sleeptime", 30); + + BackgroundThread.add(freqIsSecs, this); + } + + /** + * Set the debug level. + */ + public static void setDebugLevel(int level) { + debugLevel = level; + } + + /** + * Return the debug level. + */ + public static int getDebugLevel() { + return debugLevel; + } + + /** + * Periodically maintains the pool size. Stops threads that have + * been idle for too long and ensures the minimum number of threads. + *

+ * To change this you can set the threadpool.idletime property:
+ *
+ * ## set threadpool idletime to 120 seconds
+ * threadpool.idletime=120
+ *

+ */ + public void run() { + if (!isShuttingDown) { + maintainPoolSize(); + } + } + + /** + * Return the named thread pool. + */ + public static ThreadPool getThreadPool(String poolName) { + return Single.me.getPool(poolName); + } + + /** + * Return the named ThreadPool. If the ThreadPool doesn't exist it will be + * created. + */ + private ThreadPool getPool(String poolName) { + synchronized (this) { + ThreadPool threadPool = (ThreadPool) threadPoolCache.get(poolName); + if (threadPool == null) { + threadPool = createThreadPool(poolName); + threadPoolCache.put(poolName, threadPool); + } + return threadPool; + } + } + + /** + * Returns an iterator of ThreadPools. + *

+ * Note that the ThreadPools should not be removed by the iterator. + *

+ */ + public static Iterator pools() { + return Single.me.threadPoolCache.values().iterator(); + } + + /** + * Maintain the size of all the thread pools. + * Trims down to minimum size threads that have been idle for a while. + * Adds threads if it is short of the minimum size. + */ + private void maintainPoolSize() { + if (isShuttingDown){ + return; + } + synchronized (this) { + + Iterator e = pools(); + while (e.hasNext()) { + ThreadPool pool = (ThreadPool) e.next(); + pool.maintainPoolSize(); + } + } + } + + /** + * Shutdown all the ThreadPools nicely. + * This will wait for all currently runnable and queued work to finish. + */ + public static void shutdown() { + Single.me.shutdownPools(); + } + + private void shutdownPools() { + synchronized (this) { + isShuttingDown = true; + Iterator i = pools(); + while (i.hasNext()) { + ThreadPool pool = (ThreadPool) i.next(); + pool.shutdown(); + } + } + } + + private ThreadPool createThreadPool(String poolName) { + + int min = GlobalProperties.getInt("threadpool." + poolName + ".min", 0); + int max = GlobalProperties.getInt("threadpool." + poolName + ".max", 100); + + long idle = 1000 * GlobalProperties.getInt("threadpool." + poolName + ".idletime", -1); + if (idle < 0) { + idle = defaultIdleTime; + } + + boolean isDaemon = true; + Integer priority = null; + String threadPriority = GlobalProperties.get("threadpool." + poolName + ".priority", null); + if (threadPriority != null) { + priority = new Integer(threadPriority); + } + + ThreadPool newPool = new ThreadPool(poolName, isDaemon, priority); + newPool.setMaxSize(max); + newPool.setMinSize(min); + newPool.setMaxIdleTime(idle); + + return newPool; + } + +}; diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/thread/Work.java b/src/main/java/com/avaje/ebeaninternal/server/lib/thread/Work.java index f0b1db9fe..c6db111f9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/thread/Work.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/thread/Work.java @@ -1,113 +1,96 @@ -/** - * 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.lib.thread; - - -/** - * Used internally by the ThreadPool to wrap a Runnable that is - * going to be run. - * - *

Used to maintains some useful times about the Runnable in terms of when - * it was queued and then eventually run.

- */ -public class Work { - - /** - * Create a Runnable Work. - */ - public Work(Runnable runnable) { - this.runnable = runnable; - } - - /** - * Return the associated Runnable object. - */ - public Runnable getRunnable() { - return runnable; - } - - /** - * Return the time this work actually started. - */ - public long getStartTime() { - return startTime; - } - - /** - * Sets the time this work actually started. - */ - public void setStartTime(long startTime) { - this.startTime = startTime; - } - - /** - * Return the time this entered the queue. - */ - public long getEnterQueueTime() { - return enterQueueTime; - } - - /** - * Set the time this entered the queue. - */ - public void setEnterQueueTime(long enterQueueTime) { - this.enterQueueTime = enterQueueTime; - } - - /** - * Return the time this left the queue. - */ - public long getExitQueueTime() { - return exitQueueTime; - } - - /** - * Set the time this work left the queue. - */ - public void setExitQueueTime(long exitQueueTime) { - this.exitQueueTime = exitQueueTime; - } - - /** - * The same as getDescription(). - */ - public String toString() { - return getDescription(); - } - - /** - * Return a description of this work. - */ - public String getDescription() { - - StringBuffer sb = new StringBuffer(); - sb.append("Work["); - if (runnable != null){ - sb.append(runnable.toString()); - } - sb.append("]"); - return sb.toString(); - } - - private Runnable runnable; - - private long exitQueueTime; - private long enterQueueTime; - private long startTime; - -}; +package com.avaje.ebeaninternal.server.lib.thread; + + +/** + * Used internally by the ThreadPool to wrap a Runnable that is + * going to be run. + * + *

Used to maintains some useful times about the Runnable in terms of when + * it was queued and then eventually run.

+ */ +public class Work { + + /** + * Create a Runnable Work. + */ + public Work(Runnable runnable) { + this.runnable = runnable; + } + + /** + * Return the associated Runnable object. + */ + public Runnable getRunnable() { + return runnable; + } + + /** + * Return the time this work actually started. + */ + public long getStartTime() { + return startTime; + } + + /** + * Sets the time this work actually started. + */ + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + /** + * Return the time this entered the queue. + */ + public long getEnterQueueTime() { + return enterQueueTime; + } + + /** + * Set the time this entered the queue. + */ + public void setEnterQueueTime(long enterQueueTime) { + this.enterQueueTime = enterQueueTime; + } + + /** + * Return the time this left the queue. + */ + public long getExitQueueTime() { + return exitQueueTime; + } + + /** + * Set the time this work left the queue. + */ + public void setExitQueueTime(long exitQueueTime) { + this.exitQueueTime = exitQueueTime; + } + + /** + * The same as getDescription(). + */ + public String toString() { + return getDescription(); + } + + /** + * Return a description of this work. + */ + public String getDescription() { + + StringBuffer sb = new StringBuffer(); + sb.append("Work["); + if (runnable != null){ + sb.append(runnable.toString()); + } + sb.append("]"); + return sb.toString(); + } + + private Runnable runnable; + + private long exitQueueTime; + private long enterQueueTime; + private long startTime; + +}; diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/CreateObjectException.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/CreateObjectException.java index 436a713f5..62aeebeda 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/CreateObjectException.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/CreateObjectException.java @@ -1,40 +1,23 @@ -/** - * 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.lib.util; - - -/** - * A general exception when creating an Object. - */ -public class CreateObjectException extends RuntimeException -{ - static final long serialVersionUID = 7061559938704539736L; - - public CreateObjectException(Exception cause) { - super(cause); - } - - public CreateObjectException(String s, Exception cause) { - super(s, cause); - } - - public CreateObjectException(String s) { - super(s); - } - -} +package com.avaje.ebeaninternal.server.lib.util; + + +/** + * A general exception when creating an Object. + */ +public class CreateObjectException extends RuntimeException +{ + static final long serialVersionUID = 7061559938704539736L; + + public CreateObjectException(Exception cause) { + super(cause); + } + + public CreateObjectException(String s, Exception cause) { + super(s, cause); + } + + public CreateObjectException(String s) { + super(s); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/Dnode.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/Dnode.java index 9986b11fc..09458359a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/Dnode.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/Dnode.java @@ -1,345 +1,328 @@ -/** - * 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.lib.util; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; - -/** - * A lightweight tree structure for simple XML handling. - *

- * It removes support for nodes being mixed with content. That is, a node can - * only contain content or a list of one or more child nodes. It does not - * support mixing bits of content between the child nodes. - *

- *

- * Although designed to simplify XML in supported cases it can be used as a - * general tree structure with attributes of java Objects. - *

- */ -public class Dnode { - - int level; - - String nodeName; - - String nodeContent; - - ArrayList children; - - LinkedHashMap attrList = new LinkedHashMap(); - - /** - * Create a node. - */ - public Dnode() { - } - - /** - * Parse the raw XML string. - */ - public static Dnode parse(String s){ - DnodeReader r = new DnodeReader(); - return r.parseXml(s); - } - - /** - * Return the node as XML. - */ - public String toXml() { - StringBuilder sb = new StringBuilder(); - generate(sb); - return sb.toString(); - } - - /** - * Generate this node as xml to the buffer. - */ - public StringBuilder generate(StringBuilder sb) { - if (sb == null) { - sb = new StringBuilder(); - } - sb.append("<").append(nodeName); - Iterator it = attributeNames(); - while (it.hasNext()) { - String attr = it.next(); - Object attrValue = getAttribute(attr); - sb.append(" ").append(attr).append("=\""); - if (attrValue != null) { - sb.append(attrValue); - } - sb.append("\""); - } - - if (nodeContent == null && !hasChildren()) { - sb.append(" />"); - - } else { - sb.append(">"); - if (children != null && children.size() > 0) { - for (int i = 0; i < children.size(); i++) { - Dnode child = children.get(i); - child.generate(sb); - } - } - if (nodeContent != null) { - sb.append(nodeContent); - } - sb.append(""); - } - return sb; - } - - /** - * Return the node name. - */ - public String getNodeName() { - return nodeName; - } - - /** - * Set the node name. - */ - public void setNodeName(String nodeName) { - this.nodeName = nodeName; - } - - /** - * Return the node content. - */ - public String getNodeContent() { - return nodeContent; - } - - /** - * Set the node content. - */ - public void setNodeContent(String nodeContent) { - this.nodeContent = nodeContent; - } - - /** - * Return true if this node has children. - */ - public boolean hasChildren() { - return getChildrenCount() > 0; - } - - /** - * Return the number of children this node has. - */ - public int getChildrenCount() { - if (children == null) { - return 0; - } - return children.size(); - } - - /** - * Remove a ancestor node. - */ - public boolean remove(Dnode node) { - if (children == null) { - return false; - } - if (children.remove(node)) { - return true; - } - Iterator it = children.iterator(); - while (it.hasNext()) { - Dnode child = it.next(); - if (child.remove(node)) { - return true; - } - } - return false; - } - - /** - * List of children nodes. - */ - public List children() { - if (children == null) { - return null; - } - return children; - } - - /** - * Add a child. - */ - public void addChild(Dnode child) { - if (children == null) { - children = new ArrayList(); - } - children.add(child); - child.setLevel(level + 1); - } - - /** - * Return the level or depth of the node from the root. - */ - public int getLevel() { - return level; - } - - /** - * Set the level or depth of this node from the root. - */ - public void setLevel(int level) { - this.level = level; - if (children != null) { - for (int i = 0; i < children.size(); i++) { - Dnode child = children.get(i); - child.setLevel(level + 1); - } - } - } - - /** - * Find the first matching node using nodeName. This is a depth first tree - * search. - */ - public Dnode find(String nodeName) { - return find(nodeName, null, null); - } - - /** - * Find the first node matching nodeName and attribute value. This is a - * depth first tree search. - */ - public Dnode find(String nodeName, String attrName, Object value) { - - return find(nodeName, attrName, value, -1); - - } - - /** - * Search for a single node with control over maxLevel. Find the first node - * matching nodeName and attribute value. If attrName and value are null - * then this will just search using the nodeName. This is a depth first tree - * search. Once a matching node is found the search will stop. - */ - public Dnode find(String nodeName, String attrName, Object value, int maxLevel) { - - ArrayList list = new ArrayList(); - findByNode(list, nodeName, true, attrName, value, maxLevel); - if (list.size() >= 1) { - return list.get(0); - } - return null; - } - - /** - * Find all the nodes that match the nodeName. - * - */ - public List findAll(String nodeName, int maxLevel) { - int level = -1; - if (maxLevel > 0) { - level = this.level + maxLevel; - } - return findAll(nodeName, null, null, level); - } - - /** - * Find all the nodes that match the nodeName and attribute value. - */ - public List findAll(String nodeName, String attrName, Object value, int maxLevel) { - - if (nodeName == null && attrName == null) { - throw new RuntimeException("You can not have both nodeName and attrName null"); - } - ArrayList list = new ArrayList(); - findByNode(list, nodeName, false, attrName, value, maxLevel); - return list; - } - - /** - * Used for recursive calling. - */ - private void findByNode(List list, String node, boolean findOne,String attrName, Object value, int maxLevel) { - - if (findOne && list.size() == 1) { - return; - } - if (node == null || node.equals(nodeName)) { - if (attrName == null || value.equals(getAttribute(attrName))) { - list.add(this); - if (findOne) { - return; - } - } - } - if (maxLevel > 0 && level >= maxLevel) { - // hit max level - - } else if (children != null) { - // recursively search the children - for (int i = 0; i < children.size(); i++) { - Dnode child = children.get(i); - child.findByNode(list, node, findOne, attrName, value,maxLevel); - } - } - } - - /** - * The attribute names as strings. - */ - public Iterator attributeNames() { - return attrList.keySet().iterator(); - } - - /** - * Return the attribute for a given name. - */ - public String getAttribute(String name) { - return attrList.get(name); - } - - /** - * Returns an Attribute as a String. - *

- * Will throw a ClassCastException if the attribute is not a String. - *

- */ - public String getStringAttr(String name, String defaultValue) { - Object o = attrList.get(name); - if (o == null){ - return defaultValue; - } else { - return o.toString(); - } - } - - /** - * Set an attribute. - */ - public void setAttribute(String name, String value) { - attrList.put(name, value); - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("[").append(getNodeName()).append(" ").append(attrList).append("]"); - return sb.toString(); - } - -} +package com.avaje.ebeaninternal.server.lib.util; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; + +/** + * A lightweight tree structure for simple XML handling. + *

+ * It removes support for nodes being mixed with content. That is, a node can + * only contain content or a list of one or more child nodes. It does not + * support mixing bits of content between the child nodes. + *

+ *

+ * Although designed to simplify XML in supported cases it can be used as a + * general tree structure with attributes of java Objects. + *

+ */ +public class Dnode { + + int level; + + String nodeName; + + String nodeContent; + + ArrayList children; + + LinkedHashMap attrList = new LinkedHashMap(); + + /** + * Create a node. + */ + public Dnode() { + } + + /** + * Parse the raw XML string. + */ + public static Dnode parse(String s){ + DnodeReader r = new DnodeReader(); + return r.parseXml(s); + } + + /** + * Return the node as XML. + */ + public String toXml() { + StringBuilder sb = new StringBuilder(); + generate(sb); + return sb.toString(); + } + + /** + * Generate this node as xml to the buffer. + */ + public StringBuilder generate(StringBuilder sb) { + if (sb == null) { + sb = new StringBuilder(); + } + sb.append("<").append(nodeName); + Iterator it = attributeNames(); + while (it.hasNext()) { + String attr = it.next(); + Object attrValue = getAttribute(attr); + sb.append(" ").append(attr).append("=\""); + if (attrValue != null) { + sb.append(attrValue); + } + sb.append("\""); + } + + if (nodeContent == null && !hasChildren()) { + sb.append(" />"); + + } else { + sb.append(">"); + if (children != null && children.size() > 0) { + for (int i = 0; i < children.size(); i++) { + Dnode child = children.get(i); + child.generate(sb); + } + } + if (nodeContent != null) { + sb.append(nodeContent); + } + sb.append(""); + } + return sb; + } + + /** + * Return the node name. + */ + public String getNodeName() { + return nodeName; + } + + /** + * Set the node name. + */ + public void setNodeName(String nodeName) { + this.nodeName = nodeName; + } + + /** + * Return the node content. + */ + public String getNodeContent() { + return nodeContent; + } + + /** + * Set the node content. + */ + public void setNodeContent(String nodeContent) { + this.nodeContent = nodeContent; + } + + /** + * Return true if this node has children. + */ + public boolean hasChildren() { + return getChildrenCount() > 0; + } + + /** + * Return the number of children this node has. + */ + public int getChildrenCount() { + if (children == null) { + return 0; + } + return children.size(); + } + + /** + * Remove a ancestor node. + */ + public boolean remove(Dnode node) { + if (children == null) { + return false; + } + if (children.remove(node)) { + return true; + } + Iterator it = children.iterator(); + while (it.hasNext()) { + Dnode child = it.next(); + if (child.remove(node)) { + return true; + } + } + return false; + } + + /** + * List of children nodes. + */ + public List children() { + if (children == null) { + return null; + } + return children; + } + + /** + * Add a child. + */ + public void addChild(Dnode child) { + if (children == null) { + children = new ArrayList(); + } + children.add(child); + child.setLevel(level + 1); + } + + /** + * Return the level or depth of the node from the root. + */ + public int getLevel() { + return level; + } + + /** + * Set the level or depth of this node from the root. + */ + public void setLevel(int level) { + this.level = level; + if (children != null) { + for (int i = 0; i < children.size(); i++) { + Dnode child = children.get(i); + child.setLevel(level + 1); + } + } + } + + /** + * Find the first matching node using nodeName. This is a depth first tree + * search. + */ + public Dnode find(String nodeName) { + return find(nodeName, null, null); + } + + /** + * Find the first node matching nodeName and attribute value. This is a + * depth first tree search. + */ + public Dnode find(String nodeName, String attrName, Object value) { + + return find(nodeName, attrName, value, -1); + + } + + /** + * Search for a single node with control over maxLevel. Find the first node + * matching nodeName and attribute value. If attrName and value are null + * then this will just search using the nodeName. This is a depth first tree + * search. Once a matching node is found the search will stop. + */ + public Dnode find(String nodeName, String attrName, Object value, int maxLevel) { + + ArrayList list = new ArrayList(); + findByNode(list, nodeName, true, attrName, value, maxLevel); + if (list.size() >= 1) { + return list.get(0); + } + return null; + } + + /** + * Find all the nodes that match the nodeName. + * + */ + public List findAll(String nodeName, int maxLevel) { + int level = -1; + if (maxLevel > 0) { + level = this.level + maxLevel; + } + return findAll(nodeName, null, null, level); + } + + /** + * Find all the nodes that match the nodeName and attribute value. + */ + public List findAll(String nodeName, String attrName, Object value, int maxLevel) { + + if (nodeName == null && attrName == null) { + throw new RuntimeException("You can not have both nodeName and attrName null"); + } + ArrayList list = new ArrayList(); + findByNode(list, nodeName, false, attrName, value, maxLevel); + return list; + } + + /** + * Used for recursive calling. + */ + private void findByNode(List list, String node, boolean findOne,String attrName, Object value, int maxLevel) { + + if (findOne && list.size() == 1) { + return; + } + if (node == null || node.equals(nodeName)) { + if (attrName == null || value.equals(getAttribute(attrName))) { + list.add(this); + if (findOne) { + return; + } + } + } + if (maxLevel > 0 && level >= maxLevel) { + // hit max level + + } else if (children != null) { + // recursively search the children + for (int i = 0; i < children.size(); i++) { + Dnode child = children.get(i); + child.findByNode(list, node, findOne, attrName, value,maxLevel); + } + } + } + + /** + * The attribute names as strings. + */ + public Iterator attributeNames() { + return attrList.keySet().iterator(); + } + + /** + * Return the attribute for a given name. + */ + public String getAttribute(String name) { + return attrList.get(name); + } + + /** + * Returns an Attribute as a String. + *

+ * Will throw a ClassCastException if the attribute is not a String. + *

+ */ + public String getStringAttr(String name, String defaultValue) { + Object o = attrList.get(name); + if (o == null){ + return defaultValue; + } else { + return o.toString(); + } + } + + /** + * Set an attribute. + */ + public void setAttribute(String name, String value) { + attrList.put(name, value); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("[").append(getNodeName()).append(" ").append(attrList).append("]"); + return sb.toString(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/DnodeParser.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/DnodeParser.java index 57c1b0263..dd080edb1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/DnodeParser.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/DnodeParser.java @@ -1,219 +1,202 @@ -/** - * 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.lib.util; - -import java.util.Stack; - -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import com.avaje.ebeaninternal.server.lib.util.StringHelper; - -/** - * Parse an xml document into a Dnode tree. - */ -public class DnodeParser extends DefaultHandler { - - /** - * The root of the DContent tree. - */ - Dnode root; - - /** - * The current node being parsed. - */ - Dnode currentNode; - - /** - * The nodeContent buffer. - */ - StringBuilder buffer; - - /** - * Used to stack the nodes. - */ - Stack stack = new Stack(); - - /** - * The class used to construct new nodes. Should be Dnode or a subtype of - * Dnode. - */ - Class nodeClass = Dnode.class; - - int depth = 0; - - /** - * Trim whitespace from the content. - */ - boolean trimWhitespace = true; - - /** - * The name of the tag that contains html content - */ - String contentName; - - /** - * The depth of the tag that contains the html content - */ - int contentDepth; - - - /** - * If true then trim the whitespace from the content. - */ - public boolean isTrimWhitespace() { - return trimWhitespace; - } - - /** - * Set whether to trim whitespace from the content. - */ - public void setTrimWhitespace(boolean trimWhitespace) { - this.trimWhitespace = trimWhitespace; - } - - /** - * Return the root node of the DContent tree. - */ - public Dnode getRoot() { - return root; - } - - /** - * Set the type class of node to be created. - */ - public void setNodeClass(Class nodeClass) { - this.nodeClass = nodeClass; - } - - /** - * Create a new Dnode using the nodeClass. - */ - private Dnode createNewNode() { - try { - return (Dnode) nodeClass.newInstance(); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - - /** - * process a startElement. - */ - public void startElement(String uri, String localName, String qName, Attributes attributes) - throws SAXException { - - super.startElement(uri, localName, qName, attributes); - depth++; - - boolean isContent = (contentName != null); - - if (isContent){ - // must be html content... add the begin tag as content - buffer.append("<").append(localName); - for (int i = 0; i < attributes.getLength(); i++) { - String key = attributes.getLocalName(i); - String val = attributes.getValue(i); - buffer.append(" ").append(key).append("='").append(val).append("'"); - } - buffer.append(">"); - return; - - } - - buffer = new StringBuilder(); - Dnode node = createNewNode(); - node.setNodeName(localName); - for (int i = 0; i < attributes.getLength(); i++) { - String key = attributes.getLocalName(i); - String val = attributes.getValue(i); - node.setAttribute(key, val); - if ("type".equalsIgnoreCase(key) && "content".equalsIgnoreCase(val)) { - // this tag contains html content - // no more nodes until end tag is found - contentName = localName; - contentDepth = depth-1; - } - - } - if (root == null) { - root = node; - } - if (currentNode != null) { - currentNode.addChild(node); - } - stack.push(node); - currentNode = node; - - } - - /** - * append the node content. - */ - public void characters(char[] ch, int start, int length) throws SAXException { - super.characters(ch, start, length); - String s = new String(ch, start, length); - int p = s.indexOf('\r'); - int p2 = s.indexOf('\n'); - if (p == -1 && p2 > -1) { - // This is probably not an issue but tidys up content - // in my text editor - s = StringHelper.replaceString(s, "\n", "\r\n"); - } - buffer.append(s); - } - - /** - * process the endElement. - */ - public void endElement(String uri, String localName, String qName) throws SAXException { - super.endElement(uri, localName, qName); - depth--; - - if (contentName != null){ - // is this the end of the content? - if (contentName.equals(localName) && contentDepth == depth){ - contentName = null; - - } else { - // the html content end tag - buffer.append(""); - } - return; - } - String content = buffer.toString(); - buffer.setLength(0); - if (content.length() > 0) { - if (trimWhitespace) { - content = content.trim(); - } - if (content.length() > 0) { - currentNode.setNodeContent(content); - } - } - stack.pop(); - if (!stack.isEmpty()) { - // get the new currentNode - currentNode = (Dnode) stack.pop(); - stack.push(currentNode); - } - } - -} +package com.avaje.ebeaninternal.server.lib.util; + +import java.util.Stack; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import com.avaje.ebeaninternal.server.lib.util.StringHelper; + +/** + * Parse an xml document into a Dnode tree. + */ +public class DnodeParser extends DefaultHandler { + + /** + * The root of the DContent tree. + */ + Dnode root; + + /** + * The current node being parsed. + */ + Dnode currentNode; + + /** + * The nodeContent buffer. + */ + StringBuilder buffer; + + /** + * Used to stack the nodes. + */ + Stack stack = new Stack(); + + /** + * The class used to construct new nodes. Should be Dnode or a subtype of + * Dnode. + */ + Class nodeClass = Dnode.class; + + int depth = 0; + + /** + * Trim whitespace from the content. + */ + boolean trimWhitespace = true; + + /** + * The name of the tag that contains html content + */ + String contentName; + + /** + * The depth of the tag that contains the html content + */ + int contentDepth; + + + /** + * If true then trim the whitespace from the content. + */ + public boolean isTrimWhitespace() { + return trimWhitespace; + } + + /** + * Set whether to trim whitespace from the content. + */ + public void setTrimWhitespace(boolean trimWhitespace) { + this.trimWhitespace = trimWhitespace; + } + + /** + * Return the root node of the DContent tree. + */ + public Dnode getRoot() { + return root; + } + + /** + * Set the type class of node to be created. + */ + public void setNodeClass(Class nodeClass) { + this.nodeClass = nodeClass; + } + + /** + * Create a new Dnode using the nodeClass. + */ + private Dnode createNewNode() { + try { + return (Dnode) nodeClass.newInstance(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + + /** + * process a startElement. + */ + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + + super.startElement(uri, localName, qName, attributes); + depth++; + + boolean isContent = (contentName != null); + + if (isContent){ + // must be html content... add the begin tag as content + buffer.append("<").append(localName); + for (int i = 0; i < attributes.getLength(); i++) { + String key = attributes.getLocalName(i); + String val = attributes.getValue(i); + buffer.append(" ").append(key).append("='").append(val).append("'"); + } + buffer.append(">"); + return; + + } + + buffer = new StringBuilder(); + Dnode node = createNewNode(); + node.setNodeName(localName); + for (int i = 0; i < attributes.getLength(); i++) { + String key = attributes.getLocalName(i); + String val = attributes.getValue(i); + node.setAttribute(key, val); + if ("type".equalsIgnoreCase(key) && "content".equalsIgnoreCase(val)) { + // this tag contains html content + // no more nodes until end tag is found + contentName = localName; + contentDepth = depth-1; + } + + } + if (root == null) { + root = node; + } + if (currentNode != null) { + currentNode.addChild(node); + } + stack.push(node); + currentNode = node; + + } + + /** + * append the node content. + */ + public void characters(char[] ch, int start, int length) throws SAXException { + super.characters(ch, start, length); + String s = new String(ch, start, length); + int p = s.indexOf('\r'); + int p2 = s.indexOf('\n'); + if (p == -1 && p2 > -1) { + // This is probably not an issue but tidys up content + // in my text editor + s = StringHelper.replaceString(s, "\n", "\r\n"); + } + buffer.append(s); + } + + /** + * process the endElement. + */ + public void endElement(String uri, String localName, String qName) throws SAXException { + super.endElement(uri, localName, qName); + depth--; + + if (contentName != null){ + // is this the end of the content? + if (contentName.equals(localName) && contentDepth == depth){ + contentName = null; + + } else { + // the html content end tag + buffer.append(""); + } + return; + } + String content = buffer.toString(); + buffer.setLength(0); + if (content.length() > 0) { + if (trimWhitespace) { + content = content.trim(); + } + if (content.length() > 0) { + currentNode.setNodeContent(content); + } + } + stack.pop(); + if (!stack.isEmpty()) { + // get the new currentNode + currentNode = (Dnode) stack.pop(); + stack.push(currentNode); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/DnodeReader.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/DnodeReader.java index 182d93211..23da20240 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/DnodeReader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/DnodeReader.java @@ -1,87 +1,70 @@ -/** - * 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.lib.util; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStreamWriter; -import java.io.StringReader; - -import org.xml.sax.InputSource; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.XMLReaderFactory; - -/** - * Parses an XML inputstream returning a Dnode tree. - */ -public class DnodeReader { - - public Dnode parseXml(String str) { - - try { - ByteArrayOutputStream bao = new ByteArrayOutputStream(str.length()); - OutputStreamWriter osw = new OutputStreamWriter(bao); - - StringReader sr = new StringReader(str); - - int charBufferSize = 1024; - char[] buf = new char[charBufferSize]; - int len; - while ((len = sr.read(buf, 0, buf.length)) != -1) { - osw.write(buf, 0, len); - } - sr.close(); - osw.flush(); - osw.close(); - - bao.flush(); - bao.close(); - - InputStream is = new ByteArrayInputStream(bao.toByteArray()); - return parseXml(is); - - } catch (IOException ex){ - throw new RuntimeException(ex); - } - } - - /** - * Parse the XML inputstream returning the Dnode tree. - */ - public Dnode parseXml(InputStream in) { - - try { - InputSource inSource = new InputSource(in); - - DnodeParser parser = new DnodeParser(); - - XMLReader myReader = XMLReaderFactory.createXMLReader(); - myReader.setContentHandler(parser); - - myReader.parse(inSource); - - return parser.getRoot(); - - } catch (Exception e) { - throw new RuntimeException(e); - } - } - -} +package com.avaje.ebeaninternal.server.lib.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.StringReader; + +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; + +/** + * Parses an XML inputstream returning a Dnode tree. + */ +public class DnodeReader { + + public Dnode parseXml(String str) { + + try { + ByteArrayOutputStream bao = new ByteArrayOutputStream(str.length()); + OutputStreamWriter osw = new OutputStreamWriter(bao); + + StringReader sr = new StringReader(str); + + int charBufferSize = 1024; + char[] buf = new char[charBufferSize]; + int len; + while ((len = sr.read(buf, 0, buf.length)) != -1) { + osw.write(buf, 0, len); + } + sr.close(); + osw.flush(); + osw.close(); + + bao.flush(); + bao.close(); + + InputStream is = new ByteArrayInputStream(bao.toByteArray()); + return parseXml(is); + + } catch (IOException ex){ + throw new RuntimeException(ex); + } + } + + /** + * Parse the XML inputstream returning the Dnode tree. + */ + public Dnode parseXml(InputStream in) { + + try { + InputSource inSource = new InputSource(in); + + DnodeParser parser = new DnodeParser(); + + XMLReader myReader = XMLReaderFactory.createXMLReader(); + myReader.setContentHandler(parser); + + myReader.parse(inSource); + + return parser.getRoot(); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/GeneralException.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/GeneralException.java index be7b1d07a..e2377403e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/GeneralException.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/GeneralException.java @@ -1,39 +1,22 @@ -/** - * 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.lib.util; - -/** - * A general exception that can be used for multiple purposes. - */ -public class GeneralException extends RuntimeException { - - private static final long serialVersionUID = 5783084420007103280L; - - public GeneralException(Exception cause) { - super(cause); - } - - public GeneralException(String s, Exception cause) { - super(s, cause); - } - - public GeneralException(String s) { - super(s); - } - -} +package com.avaje.ebeaninternal.server.lib.util; + +/** + * A general exception that can be used for multiple purposes. + */ +public class GeneralException extends RuntimeException { + + private static final long serialVersionUID = 5783084420007103280L; + + public GeneralException(Exception cause) { + super(cause); + } + + public GeneralException(String s, Exception cause) { + super(s, cause); + } + + public GeneralException(String s) { + super(s); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/InvalidDataException.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/InvalidDataException.java index 7046d20db..c27a94245 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/InvalidDataException.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/InvalidDataException.java @@ -1,40 +1,23 @@ -/** - * 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.lib.util; - - -/** - * A general exception for invalid data. - */ -public class InvalidDataException extends RuntimeException -{ - static final long serialVersionUID = 7061559938704539846L; - - public InvalidDataException(Exception cause) { - super(cause); - } - - public InvalidDataException(String s, Exception cause) { - super(s, cause); - } - - public InvalidDataException(String s) { - super(s); - } - -} +package com.avaje.ebeaninternal.server.lib.util; + + +/** + * A general exception for invalid data. + */ +public class InvalidDataException extends RuntimeException +{ + static final long serialVersionUID = 7061559938704539846L; + + public InvalidDataException(Exception cause) { + super(cause); + } + + public InvalidDataException(String s, Exception cause) { + super(s, cause); + } + + public InvalidDataException(String s) { + super(s); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailAddress.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailAddress.java index e87f4b299..c6543db5c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailAddress.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailAddress.java @@ -1,62 +1,45 @@ -/** - * 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.lib.util; - -/** - * An Email address with an associated alias. - */ -public class MailAddress { - - - String alias; - - String emailAddress; - - /** - * Create an address with an optional alias. - */ - public MailAddress(String alias, String emailAddress){ - this.alias = alias; - this.emailAddress = emailAddress; - } - - /** - * Return the alias. - * If the alias is null this returns an empty string. - */ - public String getAlias() { - if (alias == null){ - return ""; - } - return alias; - } - - /** - * Return the email address. - */ - public String getEmailAddress(){ - return emailAddress; - } - - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append(getAlias()).append(" ").append("<").append(getEmailAddress()).append(">"); - return sb.toString(); - } - -} +package com.avaje.ebeaninternal.server.lib.util; + +/** + * An Email address with an associated alias. + */ +public class MailAddress { + + + String alias; + + String emailAddress; + + /** + * Create an address with an optional alias. + */ + public MailAddress(String alias, String emailAddress){ + this.alias = alias; + this.emailAddress = emailAddress; + } + + /** + * Return the alias. + * If the alias is null this returns an empty string. + */ + public String getAlias() { + if (alias == null){ + return ""; + } + return alias; + } + + /** + * Return the email address. + */ + public String getEmailAddress(){ + return emailAddress; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(getAlias()).append(" ").append("<").append(getEmailAddress()).append(">"); + return sb.toString(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailEvent.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailEvent.java index 444c5a8a4..ddeed0941 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailEvent.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailEvent.java @@ -1,66 +1,49 @@ -/** - * 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.lib.util; - -/** - * Represents the success or failure of a mail send. - */ -public class MailEvent { - - - /** - * The error indicating a send failure. - */ - Throwable error; - - /** - * The message that was sent. - */ - MailMessage message; - - - /** - * The message send failed with an error. - */ - public MailEvent(MailMessage message, Throwable error){ - this.message = message; - this.error = error; - } - - /** - * The message that we attempted to send. - */ - public MailMessage getMailMessage() { - return message; - } - - /** - * Returns true if the message was sent successfully. - */ - public boolean wasSuccessful() { - return (error == null); - } - - /** - * The error indicating the send failed. - */ - public Throwable getError() { - return error; - } - -} +package com.avaje.ebeaninternal.server.lib.util; + +/** + * Represents the success or failure of a mail send. + */ +public class MailEvent { + + + /** + * The error indicating a send failure. + */ + Throwable error; + + /** + * The message that was sent. + */ + MailMessage message; + + + /** + * The message send failed with an error. + */ + public MailEvent(MailMessage message, Throwable error){ + this.message = message; + this.error = error; + } + + /** + * The message that we attempted to send. + */ + public MailMessage getMailMessage() { + return message; + } + + /** + * Returns true if the message was sent successfully. + */ + public boolean wasSuccessful() { + return (error == null); + } + + /** + * The error indicating the send failed. + */ + public Throwable getError() { + return error; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailListener.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailListener.java index 0c682aeb8..43d47a61a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailListener.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailListener.java @@ -1,30 +1,13 @@ -/** - * 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.lib.util; - -/** - * Listens to see if the message was successfully sent. - */ -public interface MailListener { - - /** - * Handle the message event. - */ - public void handleEvent(MailEvent event); - -} +package com.avaje.ebeaninternal.server.lib.util; + +/** + * Listens to see if the message was successfully sent. + */ +public interface MailListener { + + /** + * Handle the message event. + */ + public void handleEvent(MailEvent event); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailMessage.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailMessage.java index 07fccd8ab..221aee7ff 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailMessage.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailMessage.java @@ -1,176 +1,159 @@ -/** - * 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.lib.util; - - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; - -/** - * A simple test message that can be sent via smtp. - */ -public class MailMessage { - -// /** -// * The subject text. -// */ -// String subject; - - /** - * The body content. - */ - ArrayList bodylines; - - /** - * The sender email address. - */ - MailAddress senderAddress; - - /** - * The headers. - */ - HashMap header = new HashMap(); - - /** - * the recipient of the email. - */ - MailAddress currentRecipient; - - /** - * The list of recipients. - */ - ArrayList recipientList = new ArrayList(); - - /** - * Create the message. - */ - public MailMessage() { - bodylines = new ArrayList(); - } - - /** - * Set the current recipient. - */ - public void setCurrentRecipient(MailAddress currentRecipient){ - this.currentRecipient = currentRecipient; - } - - /** - * Return the current recipient. - */ - public MailAddress getCurrentRecipient() { - return currentRecipient; - } - - /** - * Add a recipient. - */ - public void addRecipient(String alias, String emailAddress){ - recipientList.add(new MailAddress(alias, emailAddress)); - } - - /** - * Set the sender details. - */ - public void setSender(String alias, String senderEmail){ - this.senderAddress = new MailAddress(alias, senderEmail); - } - /** - * Return the sender address. - */ - public MailAddress getSender() { - return senderAddress; - } - - /** - * Return the recipient list. - */ - public Iterator getRecipientList() { - return recipientList.iterator(); - } - - /** - * add a header to the message. - */ - public void addHeader(String key, String val) { - header.put(key, val); - } - - /** - * Set the subject text. - */ - public void setSubject(String subject){ - addHeader("Subject", subject); - } - - /** - * Return the subject text. - */ - public String getSubject() { - return getHeader("Subject"); - } - - /** - * Add text to the body. - */ - public void addBodyLine(String line) { - bodylines.add(line); - } - - /** - * Return the body text. - */ - public Iterator getBodyLines() { - return bodylines.iterator(); - } - - /** - * Return the headers. - */ - public Iterator getHeaderFields() { - return header.keySet().iterator(); - } - - /** - * Return a given header. - */ - public String getHeader(String key) { - return header.get(key); - } - - public String toString() { - StringBuilder sb = new StringBuilder(100); - sb.append("Sender: " + senderAddress + "\tRecipient: " + recipientList + "\n"); - Iterator hi = header.keySet().iterator(); - while (hi.hasNext()) { - String key = hi.next(); - String hline = key + ": " + header.get(key) + "\n"; - sb.append(hline); - } - sb.append("\n"); - Iterator e = bodylines.iterator(); - while (e.hasNext()) { - sb.append(e.next()).append("\n"); - } - return sb.toString(); - } -} - - - +package com.avaje.ebeaninternal.server.lib.util; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +/** + * A simple test message that can be sent via smtp. + */ +public class MailMessage { + +// /** +// * The subject text. +// */ +// String subject; + + /** + * The body content. + */ + ArrayList bodylines; + + /** + * The sender email address. + */ + MailAddress senderAddress; + + /** + * The headers. + */ + HashMap header = new HashMap(); + + /** + * the recipient of the email. + */ + MailAddress currentRecipient; + + /** + * The list of recipients. + */ + ArrayList recipientList = new ArrayList(); + + /** + * Create the message. + */ + public MailMessage() { + bodylines = new ArrayList(); + } + + /** + * Set the current recipient. + */ + public void setCurrentRecipient(MailAddress currentRecipient){ + this.currentRecipient = currentRecipient; + } + + /** + * Return the current recipient. + */ + public MailAddress getCurrentRecipient() { + return currentRecipient; + } + + /** + * Add a recipient. + */ + public void addRecipient(String alias, String emailAddress){ + recipientList.add(new MailAddress(alias, emailAddress)); + } + + /** + * Set the sender details. + */ + public void setSender(String alias, String senderEmail){ + this.senderAddress = new MailAddress(alias, senderEmail); + } + /** + * Return the sender address. + */ + public MailAddress getSender() { + return senderAddress; + } + + /** + * Return the recipient list. + */ + public Iterator getRecipientList() { + return recipientList.iterator(); + } + + /** + * add a header to the message. + */ + public void addHeader(String key, String val) { + header.put(key, val); + } + + /** + * Set the subject text. + */ + public void setSubject(String subject){ + addHeader("Subject", subject); + } + + /** + * Return the subject text. + */ + public String getSubject() { + return getHeader("Subject"); + } + + /** + * Add text to the body. + */ + public void addBodyLine(String line) { + bodylines.add(line); + } + + /** + * Return the body text. + */ + public Iterator getBodyLines() { + return bodylines.iterator(); + } + + /** + * Return the headers. + */ + public Iterator getHeaderFields() { + return header.keySet().iterator(); + } + + /** + * Return a given header. + */ + public String getHeader(String key) { + return header.get(key); + } + + public String toString() { + StringBuilder sb = new StringBuilder(100); + sb.append("Sender: " + senderAddress + "\tRecipient: " + recipientList + "\n"); + Iterator hi = header.keySet().iterator(); + while (hi.hasNext()) { + String key = hi.next(); + String hline = key + ": " + header.get(key) + "\n"; + sb.append(hline); + } + sb.append("\n"); + Iterator e = bodylines.iterator(); + while (e.hasNext()) { + sb.append(e.next()).append("\n"); + } + return sb.toString(); + } +} + + + diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailSender.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailSender.java index 3225e0d81..b10d5c307 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailSender.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/MailSender.java @@ -1,231 +1,214 @@ -/** - * 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.lib.util; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.InetAddress; -import java.net.Socket; -import java.net.UnknownHostException; -import java.util.Iterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Sends simple MailMessages via smtp. - */ -public class MailSender implements Runnable { - - private static final Logger logger = Logger.getLogger(MailSender.class.getName()); - - int traceLevel = 0; - - Socket sserver; - String server; - - BufferedReader in; - - OutputStreamWriter out; - - MailMessage message; - - MailListener listener = null; - - private static final int SMTP_PORT = 25; - - /** - * Create for a given mail server. - */ - public MailSender(String server) { - this.server = server; - } - - /** - * Set the listener to handle MessageEvents. - */ - public void setMailListener(MailListener listener){ - this.listener = listener; - } - - /** - * Send the message. - */ - public void run() { - send(message); - } - - /** - * Send the message in a background thread. - */ - public void sendInBackground(MailMessage message){ - this.message = message; - Thread thread = new Thread(this); - thread.start(); - } - - - /** - * Send the message in the current thread. - */ - public void send(MailMessage message){ - try { - Iterator i = message.getRecipientList(); - while (i.hasNext()) { - MailAddress recipientAddress = (MailAddress) i.next(); - sserver = new Socket(server, SMTP_PORT); - send(message, sserver, recipientAddress); - sserver.close(); - - if (listener != null){ - MailEvent event = new MailEvent(message, null); - listener.handleEvent(event); - } - } - } catch (Exception ex) { - if (listener != null){ - MailEvent event = new MailEvent(message, ex); - listener.handleEvent(event); - } else { - logger.log(Level.SEVERE, null, ex); - } - } - } - - private void send(MailMessage message, Socket sserver, MailAddress recipientAddress) throws IOException { - - // A bit convoluted, but doesn't depend on DNS in any way... - InetAddress localhost = sserver.getLocalAddress(); - String localaddress = localhost.getHostAddress(); - MailAddress sender = message.getSender(); - message.setCurrentRecipient(recipientAddress); - - // Mandatory header fields, Date and From - if (message.getHeader("Date") == null){ - message.addHeader("Date", new java.util.Date().toString()); - } - if (message.getHeader("From") == null){ - message.addHeader("From", sender.getAlias() + " <" + sender.getEmailAddress() + ">"); - } - - //if (message.getHeader("From") == null){ - message.addHeader("To", recipientAddress.getAlias() + " <" + recipientAddress.getEmailAddress() + ">"); - //} - - out = new OutputStreamWriter(sserver.getOutputStream()); - in = new BufferedReader(new InputStreamReader(sserver.getInputStream())); - String sintro = readln(); - if (!sintro.startsWith("220")) { // 220 - logger.fine("SmtpSender: intro==" + sintro); - return; - } - - writeln("EHLO " + localaddress); - if (!expect250()) { - return; - } - - writeln("MAIL FROM:<" + sender.getEmailAddress() + ">"); - if (!expect250()) { - return; - } - writeln("RCPT TO:<" + recipientAddress.getEmailAddress() + ">"); - if (!expect250()){ - return; - } - writeln("DATA"); - while (true) { // may be multiple 250 replies pending from server - String line = readln(); - if (line.startsWith("3")) - break; // ready to send - if (!line.startsWith("2")) { - logger.fine("SmtpSender.send reponse to DATA: " + line); - return; - } - } - Iterator hi = message.getHeaderFields(); - while (hi.hasNext()) { - String key = (String) hi.next(); - writeln(key + ": " + message.getHeader(key)); - } - writeln(""); // end of header; - Iterator e = message.getBodyLines(); - while (e.hasNext()) { - String bline = (String) e.next(); - if (bline.startsWith(".")) { - bline = "." + bline; - } - writeln(bline); - } - writeln("."); - expect250(); - writeln("QUIT"); - - } - - private boolean expect250() throws IOException { - String line = readln(); - if (!line.startsWith("2")) { - logger.info("SmtpSender.expect250: " + line); - return false; - } - return true; - } - - private void writeln(String s) throws IOException { - if (traceLevel > 2){ - logger.fine("From client: " + s); - } - out.write(s + "\r\n"); - out.flush(); - } - - private String readln() throws IOException { - String line = in.readLine(); - if (traceLevel > 1){ - logger.fine("From server: " + line); - } - return line; - } - - - /** - * Set the trace level. - */ - public void setTraceLevel(int traceLevel){ - this.traceLevel = traceLevel; - } - - /** - * Return the hostname of the local machine. - */ - public String getLocalHostName() { - try { - InetAddress ipaddress = InetAddress.getLocalHost(); - String localHost = ipaddress.getHostName(); - if (localHost == null) { - return "localhost"; - } else { - return localHost; - } - } catch (UnknownHostException e) { - return "localhost"; - } - } -} +package com.avaje.ebeaninternal.server.lib.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.Iterator; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Sends simple MailMessages via smtp. + */ +public class MailSender implements Runnable { + + private static final Logger logger = Logger.getLogger(MailSender.class.getName()); + + int traceLevel = 0; + + Socket sserver; + String server; + + BufferedReader in; + + OutputStreamWriter out; + + MailMessage message; + + MailListener listener = null; + + private static final int SMTP_PORT = 25; + + /** + * Create for a given mail server. + */ + public MailSender(String server) { + this.server = server; + } + + /** + * Set the listener to handle MessageEvents. + */ + public void setMailListener(MailListener listener){ + this.listener = listener; + } + + /** + * Send the message. + */ + public void run() { + send(message); + } + + /** + * Send the message in a background thread. + */ + public void sendInBackground(MailMessage message){ + this.message = message; + Thread thread = new Thread(this); + thread.start(); + } + + + /** + * Send the message in the current thread. + */ + public void send(MailMessage message){ + try { + Iterator i = message.getRecipientList(); + while (i.hasNext()) { + MailAddress recipientAddress = (MailAddress) i.next(); + sserver = new Socket(server, SMTP_PORT); + send(message, sserver, recipientAddress); + sserver.close(); + + if (listener != null){ + MailEvent event = new MailEvent(message, null); + listener.handleEvent(event); + } + } + } catch (Exception ex) { + if (listener != null){ + MailEvent event = new MailEvent(message, ex); + listener.handleEvent(event); + } else { + logger.log(Level.SEVERE, null, ex); + } + } + } + + private void send(MailMessage message, Socket sserver, MailAddress recipientAddress) throws IOException { + + // A bit convoluted, but doesn't depend on DNS in any way... + InetAddress localhost = sserver.getLocalAddress(); + String localaddress = localhost.getHostAddress(); + MailAddress sender = message.getSender(); + message.setCurrentRecipient(recipientAddress); + + // Mandatory header fields, Date and From + if (message.getHeader("Date") == null){ + message.addHeader("Date", new java.util.Date().toString()); + } + if (message.getHeader("From") == null){ + message.addHeader("From", sender.getAlias() + " <" + sender.getEmailAddress() + ">"); + } + + //if (message.getHeader("From") == null){ + message.addHeader("To", recipientAddress.getAlias() + " <" + recipientAddress.getEmailAddress() + ">"); + //} + + out = new OutputStreamWriter(sserver.getOutputStream()); + in = new BufferedReader(new InputStreamReader(sserver.getInputStream())); + String sintro = readln(); + if (!sintro.startsWith("220")) { // 220 + logger.fine("SmtpSender: intro==" + sintro); + return; + } + + writeln("EHLO " + localaddress); + if (!expect250()) { + return; + } + + writeln("MAIL FROM:<" + sender.getEmailAddress() + ">"); + if (!expect250()) { + return; + } + writeln("RCPT TO:<" + recipientAddress.getEmailAddress() + ">"); + if (!expect250()){ + return; + } + writeln("DATA"); + while (true) { // may be multiple 250 replies pending from server + String line = readln(); + if (line.startsWith("3")) + break; // ready to send + if (!line.startsWith("2")) { + logger.fine("SmtpSender.send reponse to DATA: " + line); + return; + } + } + Iterator hi = message.getHeaderFields(); + while (hi.hasNext()) { + String key = (String) hi.next(); + writeln(key + ": " + message.getHeader(key)); + } + writeln(""); // end of header; + Iterator e = message.getBodyLines(); + while (e.hasNext()) { + String bline = (String) e.next(); + if (bline.startsWith(".")) { + bline = "." + bline; + } + writeln(bline); + } + writeln("."); + expect250(); + writeln("QUIT"); + + } + + private boolean expect250() throws IOException { + String line = readln(); + if (!line.startsWith("2")) { + logger.info("SmtpSender.expect250: " + line); + return false; + } + return true; + } + + private void writeln(String s) throws IOException { + if (traceLevel > 2){ + logger.fine("From client: " + s); + } + out.write(s + "\r\n"); + out.flush(); + } + + private String readln() throws IOException { + String line = in.readLine(); + if (traceLevel > 1){ + logger.fine("From server: " + line); + } + return line; + } + + + /** + * Set the trace level. + */ + public void setTraceLevel(int traceLevel){ + this.traceLevel = traceLevel; + } + + /** + * Return the hostname of the local machine. + */ + public String getLocalHostName() { + try { + InetAddress ipaddress = InetAddress.getLocalHost(); + String localHost = ipaddress.getHostName(); + if (localHost == null) { + return "localhost"; + } else { + return localHost; + } + } catch (UnknownHostException e) { + return "localhost"; + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/MapFromString.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/MapFromString.java index 1dcf1eeb3..cec1ed413 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/MapFromString.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/MapFromString.java @@ -1,80 +1,63 @@ -/** - * 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.lib.util; - -import java.util.LinkedHashMap; - -/** - * Utility String class that supports String manipulation functions. - */ -public class MapFromString { - - LinkedHashMap map = new LinkedHashMap(); - - String mapToString; - - int stringLength; - int keyStart = 0; - int eqPos = 0; - int valEnd = 0; - - public static LinkedHashMap parse(String mapToString) { - MapFromString c = new MapFromString(mapToString); - return c.parse(); - } - - private MapFromString(String mapToString) { - if (mapToString.charAt(0) == '{'){ - mapToString = mapToString.substring(1); - } - if (mapToString.charAt(mapToString.length()-1) == '}'){ - mapToString = mapToString.substring(0, mapToString.length()-1); - } - - this.mapToString = mapToString; - this.stringLength = mapToString.length(); - } - - private LinkedHashMap parse() { - while(findNext()){ - } - return map; - } - - private boolean findNext() { - if (keyStart > stringLength){ - return false; - } - eqPos = mapToString.indexOf("=",keyStart); - if (eqPos == -1){ - throw new RuntimeException("No = after "+keyStart); - } - valEnd = mapToString.indexOf(", ",eqPos); - if (valEnd == -1){ - valEnd = mapToString.length(); - } - // check that the next valEnd occurs after the next eqPos - - String keyValue = mapToString.substring(keyStart,eqPos); - String valValue = mapToString.substring(eqPos+1,valEnd); - map.put(keyValue, valValue); - keyStart = valEnd + 2; - return true; - } - -} +package com.avaje.ebeaninternal.server.lib.util; + +import java.util.LinkedHashMap; + +/** + * Utility String class that supports String manipulation functions. + */ +public class MapFromString { + + LinkedHashMap map = new LinkedHashMap(); + + String mapToString; + + int stringLength; + int keyStart = 0; + int eqPos = 0; + int valEnd = 0; + + public static LinkedHashMap parse(String mapToString) { + MapFromString c = new MapFromString(mapToString); + return c.parse(); + } + + private MapFromString(String mapToString) { + if (mapToString.charAt(0) == '{'){ + mapToString = mapToString.substring(1); + } + if (mapToString.charAt(mapToString.length()-1) == '}'){ + mapToString = mapToString.substring(0, mapToString.length()-1); + } + + this.mapToString = mapToString; + this.stringLength = mapToString.length(); + } + + private LinkedHashMap parse() { + while(findNext()){ + } + return map; + } + + private boolean findNext() { + if (keyStart > stringLength){ + return false; + } + eqPos = mapToString.indexOf("=",keyStart); + if (eqPos == -1){ + throw new RuntimeException("No = after "+keyStart); + } + valEnd = mapToString.indexOf(", ",eqPos); + if (valEnd == -1){ + valEnd = mapToString.length(); + } + // check that the next valEnd occurs after the next eqPos + + String keyValue = mapToString.substring(keyStart,eqPos); + String valValue = mapToString.substring(eqPos+1,valEnd); + map.put(keyValue, valValue); + keyStart = valEnd + 2; + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/MimeTypeHelper.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/MimeTypeHelper.java index 1d4eb1bcb..2554616d4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/MimeTypeHelper.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/MimeTypeHelper.java @@ -1,57 +1,40 @@ -/** - * 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.lib.util; - - -import java.util.MissingResourceException; -import java.util.ResourceBundle; - -/** - * Helper methods to determine the mime type based on a file name. - */ -public class MimeTypeHelper { - - /** - * Return the mimeType for a given file path. - * This will extract the file extension, and then use that - * to look up an appropriate mime type (from the mimetypes.props file). - * - * To add a new mime type, add it to the mimetype.props file. - */ - public static String getMimeType(String filePath) { - - int lastPeriod = filePath.lastIndexOf("."); - if (lastPeriod > -1) { - filePath = filePath.substring(lastPeriod+1); - } - - try { - return resources.getString(filePath.toLowerCase()); - - } catch (MissingResourceException e) { - return null; - //String m = "Unable to locate mimetype for ["+filePath.toLowerCase()+"] in mimetypes.properties"; - //throw new NotFoundException(m); - } - - } - - private static ResourceBundle resources = ResourceBundle.getBundle("com.avaje.lib.util.mimetypes"); - - -}; +package com.avaje.ebeaninternal.server.lib.util; + + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * Helper methods to determine the mime type based on a file name. + */ +public class MimeTypeHelper { + + /** + * Return the mimeType for a given file path. + * This will extract the file extension, and then use that + * to look up an appropriate mime type (from the mimetypes.props file). + * + * To add a new mime type, add it to the mimetype.props file. + */ + public static String getMimeType(String filePath) { + + int lastPeriod = filePath.lastIndexOf("."); + if (lastPeriod > -1) { + filePath = filePath.substring(lastPeriod+1); + } + + try { + return resources.getString(filePath.toLowerCase()); + + } catch (MissingResourceException e) { + return null; + //String m = "Unable to locate mimetype for ["+filePath.toLowerCase()+"] in mimetypes.properties"; + //throw new NotFoundException(m); + } + + } + + private static ResourceBundle resources = ResourceBundle.getBundle("com.avaje.lib.util.mimetypes"); + + +}; diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/NotFoundException.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/NotFoundException.java index 86ab67623..02fe023e4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/NotFoundException.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/NotFoundException.java @@ -1,40 +1,23 @@ -/** - * 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.lib.util; - - -/** - * A general exception where data is not found. - */ -public class NotFoundException extends RuntimeException -{ - static final long serialVersionUID = 7061559938704539845L; - - public NotFoundException(Exception cause) { - super(cause); - } - - public NotFoundException(String s, Exception cause) { - super(s, cause); - } - - public NotFoundException(String s) { - super(s); - } - -} +package com.avaje.ebeaninternal.server.lib.util; + + +/** + * A general exception where data is not found. + */ +public class NotFoundException extends RuntimeException +{ + static final long serialVersionUID = 7061559938704539845L; + + public NotFoundException(Exception cause) { + super(cause); + } + + public NotFoundException(String s, Exception cause) { + super(s, cause); + } + + public NotFoundException(String s) { + super(s); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/StringHelper.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/StringHelper.java index ed956b31a..cd7e6763f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/StringHelper.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/StringHelper.java @@ -1,616 +1,599 @@ -/** - * 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.lib.util; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -/** - * Utility String class that supports String manipulation functions. - */ -public class StringHelper { - - private static final char SINGLE_QUOTE = '\''; - - private static final char DOUBLE_QUOTE = '"'; - - /** - * parses a String of the form name1='value1' name2='value2'. Note that you - * can use either single or double quotes for any particular name value pair - * and the end quote must match the begin quote. - */ - public static HashMap parseNameQuotedValue(String tag) throws RuntimeException { - - if (tag == null || tag.length() < 1) { - return null; - } - - // make sure that the quotes are matched... - // int remainer = countOccurances(tag, ""+quote) % 2; - // if (remainer == 1) { - // dp("remainder = "+remainer); - // throw new StringParsingException("Unmatched quote in "+tag); - // } - - // make sure that th last character is not an equals... - // (check now so I don't need to check this every time..) - if (tag.charAt(tag.length() - 1) == '=') { - throw new RuntimeException("missing quoted value at the end of " + tag); - } - - HashMap map = new HashMap(); - // recursively parse out the name value pairs... - return parseNameQuotedValue(map, tag, 0); - } - - /** - * recursively parse out name value pairs (where the value is quoted, with - * either single or double quotes). - */ - private static HashMap parseNameQuotedValue(HashMap map, - String tag, int pos) throws RuntimeException { - - int equalsPos = tag.indexOf("=", pos); - if (equalsPos > -1) { - // check for begin quote... - char firstQuote = tag.charAt(equalsPos + 1); - if (firstQuote != SINGLE_QUOTE && firstQuote != DOUBLE_QUOTE) { - throw new RuntimeException("missing begin quote at " + (equalsPos) + "[" - + tag.charAt(equalsPos + 1) + "] in [" + tag + "]"); - } - - // check for end quote... - int endQuotePos = tag.indexOf(firstQuote, equalsPos + 2); - if (endQuotePos == -1) { - throw new RuntimeException("missing end quote [" + firstQuote + "] after " + pos - + " in [" + tag + "]"); - } - - // we have a valid name and value... - // dp("pos="+pos+" equalsPos="+equalsPos+" - // endQuotePos="+endQuotePos); - String name = tag.substring(pos, equalsPos); - String value = tag.substring(equalsPos + 2, endQuotePos); - // dp("name="+name+"; value="+value+";"); - - // trim off any whitespace from the front of name... - name = trimFront(name, " "); - if ((name.indexOf(SINGLE_QUOTE) > -1) || (name.indexOf(DOUBLE_QUOTE) > -1)) { - throw new RuntimeException("attribute name contains a quote [" + name + "]"); - } - map.put(name, value); - - return parseNameQuotedValue(map, tag, endQuotePos + 1); - - } else { - // no more equals... stop parsing... - return map; - } - } - - /** - * Returns the number of times a particular String occurs in another String. - * e.g. count the number of single quotes. - */ - public static int countOccurances(String content, String occurs) { - return countOccurances(content, occurs, 0, 0); - } - - private static int countOccurances(String content, String occurs, int pos, int countSoFar) { - int equalsPos = content.indexOf(occurs, pos); - if (equalsPos > -1) { - countSoFar = countSoFar + 1; - pos = equalsPos + occurs.length(); - // dp("countSoFar="+countSoFar+" pos="+pos); - return countOccurances(content, occurs, pos, countSoFar); - } else { - return countSoFar; - } - } - - /** - * Parses out a list of Name Value pairs that are delimited together. Will - * always return a StringMap. If allNameValuePairs is null, or no name - * values can be parsed out an empty StringMap is returned. - * - * @param allNameValuePairs - * the entire string to be parsed. - * @param listDelimiter - * (typically ';') the delimited between the list - * @param nameValueSeparator - * (typically '=') the separator between the name and value - */ - public static Map delimitedToMap(String allNameValuePairs, - String listDelimiter, String nameValueSeparator) { - - HashMap params = new HashMap(); - if ((allNameValuePairs == null) || (allNameValuePairs.length() == 0)) { - return params; - } - // trim off any leading listDelimiter... - allNameValuePairs = trimFront(allNameValuePairs, listDelimiter); - return getKeyValue(params, 0, allNameValuePairs, listDelimiter, nameValueSeparator); - } - - /** - * Trims off recurring strings from the front of a string. - * - * @param source - * the source string - * @param trim - * the string to trim off the front - */ - public static String trimFront(String source, String trim) { - if (source == null) { - return null; - } - if (source.indexOf(trim) == 0) { - // dp("trim ..."); - return trimFront(source.substring(trim.length()), trim); - } else { - return source; - } - } - - /** - * Return true if the value is null or an empty string. - */ - public static boolean isNull(String value) { - if (value == null || value.trim().length() == 0) { - return true; - } - return false; - } - - /** - * Recursively pulls out the key value pairs from a raw string. - */ - private static HashMap getKeyValue(HashMap map, int pos, - String allNameValuePairs, String listDelimiter, String nameValueSeparator) { - - if (pos >= allNameValuePairs.length()) { - // dp("end as "+pos+" >= "+allNameValuePairs.length() ); - return map; - } - - int equalsPos = allNameValuePairs.indexOf(nameValueSeparator, pos); - int delimPos = allNameValuePairs.indexOf(listDelimiter, pos); - - if (delimPos == -1) { - delimPos = allNameValuePairs.length(); - } - if (equalsPos == -1) { - // dp("no more equals..."); - return map; - } - if (delimPos == (equalsPos + 1)) { - // dp("Ignoring as nothing between delim and equals... - // delim:"+delimPos+" eq:"+equalsPos); - return getKeyValue(map, delimPos + 1, allNameValuePairs, listDelimiter, - nameValueSeparator); - } - if (equalsPos > delimPos) { - // there is a key without a value? - String key = allNameValuePairs.substring(pos, delimPos); - key = key.trim(); - if (key.length() > 0) { - map.put(key, null); - } - return getKeyValue(map, delimPos + 1, allNameValuePairs, listDelimiter, - nameValueSeparator); - - } - String key = allNameValuePairs.substring(pos, equalsPos); - - if (delimPos > -1) { - String value = allNameValuePairs.substring(equalsPos + 1, delimPos); - // dp("cont "+key+","+value+" pos:"+pos+" - // len:"+allNameValuePairs.length()); - key = key.trim(); - - map.put(key, value); - pos = delimPos + 1; - - // recurse the rest of the values... - return getKeyValue(map, pos, allNameValuePairs, listDelimiter, nameValueSeparator); - } else { - // dp("ERROR: delimPos < 0 ???"); - return map; - } - } - - /** - * Convert a string that has delimited values (say comma delimited) in a - * String[]. You must explicitly choose whether or not to include empty - * values (say two commas that a right beside each other. - * - *

- * e.g. "alpha,beta,,theta"
- * With keepEmpties true, this results in a String[] of size 4 with the - * third one having a String of 0 length. With keepEmpties false, this - * results in a String[] of size 3. - *

- *

- *

- *

- * e.g. ",alpha,beta,,theta,"
- * With keepEmpties true, this results in a String[] of size 6 with the - * 1st,4th and 6th one having a String of 0 length. With keepEmpties false, - * this results in a String[] of size 3. - *

- */ - public static String[] delimitedToArray(String str, String delimiter, boolean keepEmpties) { - - ArrayList list = new ArrayList(); - int startPos = 0; - delimiter(str, delimiter, keepEmpties, startPos, list); - String[] result = new String[list.size()]; - return (String[]) list.toArray(result); - } - - private static void delimiter(String str, String delimiter, boolean keepEmpties, int startPos, - ArrayList list) { - - int endPos = str.indexOf(delimiter, startPos); - if (endPos == -1) { - if (startPos <= str.length()) { - String lastValue = str.substring(startPos, str.length()); - // dp("lastValue="+lastValue); - if (!keepEmpties && lastValue.length() == 0) { - // dp("not keeping..."); - } else { - list.add(lastValue); - } - } - // we have finished parsing the string... - return; - } else { - // get the delimited value... add it.. - String value = str.substring(startPos, endPos); - // dp(startPos+","+endPos+" value="+value); - if (!keepEmpties && value.length() == 0) { - // dp("not keeping..."); - } else { - list.add(value); - } - // recursively search as we are not at the end yet... - delimiter(str, delimiter, keepEmpties, endPos + 1, list); - } - } - - /** - * This returns the FIRST string in str that is bounded on the left by - * leftBound, and bounded on the right by rightBound. This will return null - * if the leftBound is not found within str. - * - *

- * If leftBound can't be found this returns null. - *

- *

- * This rightBound can't be found then this throws a - * StringIndexOutOfBoundsException. - *

- * - * @param str - * the base string that we will search for the bounded string. - * @param leftBound - * the left bound of the string. - * @param rightBound - * the right bound of the string. - */ - public static String getBoundedString(String str, String leftBound, String rightBound) - throws RuntimeException { - - if (str == null) { - throw new RuntimeException("string to parse is null?"); - } - int startPos = str.indexOf(leftBound); - if (startPos > -1) { - startPos = startPos + leftBound.length(); - int endPos = str.indexOf(rightBound, startPos); - // dp(str+" start:"+startPos+" end:"+endPos); - if (endPos == -1) { - throw new RuntimeException("Can't find rightBound: " + rightBound); - } - return str.substring(startPos, endPos); - } else { - // if no leftBound can be found.. return null... could be in a - // search n parse type loop? - // this keeps "no tag"==null different from "tag not formed - // properly"==StringParsingException - return null; - } - } - - /** - * Takes the String bounded by leftBound & rightBound, and replaces it with - * replaceString. Actually removes the left and right bound strings aswell. - */ - public static String setBoundedString(String str, String leftBound, String rightBound, - String replaceString) { - - int startPos = str.indexOf(leftBound); - if (startPos > -1) { - // startPos = startPos; - int endPos = str.indexOf(rightBound, startPos + leftBound.length()); - if (endPos > -1) { - String toReplace = str.substring(startPos, endPos + 1); - return replaceString(str, toReplace, replaceString); - } else { - return str; - } - } else { - return str; - } - } - - // public static String replaceString(String str, String oldSub, String - // newSub) { - // - // if (str == null) { - // return null; - // } - // StringBuilder newSB = new StringBuilder(str.length()+20); - // int iPos = 0; - // int iPrevPos = 0; - // - // while (true) { - // iPos = str.indexOf(oldSub, iPrevPos); - // if (iPos > -1) { - // // found - // newSB.append(str.substring(iPrevPos, iPos)); - // newSB.append(newSub); - // iPrevPos = iPos + oldSub.length(); - // } else { - // // not found - // newSB.append(str.substring(iPrevPos)); - // break; - // } - // } - // - // return newSB.toString(); - // } - - /** - * This method takes a String and will replace all occurrences of the match - * String with that of the replace String. - * - * @param source - * the source string - * @param match - * the string used to find a match - * @param replace - * the string used to replace match with - * @return the source string after the search and replace - */ - public static String replaceString(String source, String match, String replace) { - if (source == null){ - return null; - } - if (replace == null){ - return source; - } - if (match == null){ - throw new NullPointerException("match is null?"); - } - if (match.equals(replace)){ - return source; - } - return replaceString(source, match, replace, 30, 0, source.length()); - } - - /** - * Additionally specify the additionalSize to add to the buffer. This will - * make the buffer bigger so that it doesn't have to grow when replacement - * occurs. - */ - public static String replaceString(String source, String match, String replace, - int additionalSize, int startPos, int endPos) { - - if (source == null){ - return source; - } - - char match0 = match.charAt(0); - - int matchLength = match.length(); - - if (matchLength == 1 && replace.length() == 1) { - char replace0 = replace.charAt(0); - return source.replace(match0, replace0); - } - if (matchLength >= replace.length()) { - additionalSize = 0; - } - - - int sourceLength = source.length(); - int lastMatch = endPos - matchLength; - - StringBuilder sb = new StringBuilder(sourceLength + additionalSize); - - if (startPos > 0) { - sb.append(source.substring(0, startPos)); - } - - char sourceChar; - boolean isMatch; - int sourceMatchPos; - - for (int i = startPos; i < sourceLength; i++) { - sourceChar = source.charAt(i); - if (i > lastMatch || sourceChar != match0) { - sb.append(sourceChar); - - } else { - // check to see if this is a match - isMatch = true; - sourceMatchPos = i; - - // check each following character... - for (int j = 1; j < matchLength; j++) { - sourceMatchPos++; - if (source.charAt(sourceMatchPos) != match.charAt(j)) { - isMatch = false; - break; - } - } - if (isMatch) { - i = i + matchLength - 1; - sb.append(replace); - } else { - // was not a match - sb.append(sourceChar); - } - } - } - - return sb.toString(); - } - - /** - * A search and replace with multiple matching strings. - *

- * Useful when converting CRNL CR and NL all to a BR tag for example. - *

- * - *

-	 * String[] multi = { "\r\n", "\r", "\n" };
-	 * content = StringHelper.replaceStringMulti(content, multi, "<br/>");
-	 * 
- */ - public static String replaceStringMulti(String source, String[] match, String replace) { - return replaceStringMulti(source, match, replace, 30, 0, source.length()); - } - - /** - * Additionally specify an additional size estimate for the buffer plus - * start and end positions. - *

- * The start and end positions can limit the search and replace. Otherwise - * these default to startPos = 0 and endPos = source.length(). - *

- */ - public static String replaceStringMulti(String source, String[] match, String replace, - int additionalSize, int startPos, int endPos) { - - int shortestMatch = match[0].length(); - - char[] match0 = new char[match.length]; - for (int i = 0; i < match0.length; i++) { - match0[i] = match[i].charAt(0); - if (match[i].length() < shortestMatch) { - shortestMatch = match[i].length(); - } - } - - StringBuilder sb = new StringBuilder(source.length() + additionalSize); - - char sourceChar; - - int len = source.length(); - int lastMatch = endPos - shortestMatch; - - if (startPos > 0) { - sb.append(source.substring(0, startPos)); - } - - int matchCount = 0; - - for (int i = startPos; i < len; i++) { - sourceChar = source.charAt(i); - if (i > lastMatch) { - sb.append(sourceChar); - } else { - matchCount = 0; - for (int k = 0; k < match0.length; k++) { - if (matchCount == 0 && sourceChar == match0[k]) { - if (match[k].length() + i <= len) { - - ++matchCount; - int j = 1; - for (; j < match[k].length(); j++) { - if (source.charAt(i + j) != match[k].charAt(j)) { - --matchCount; - break; - } - } - if (matchCount > 0) { - i = i + j - 1; - sb.append(replace); - break; - } - } - } - } - if (matchCount == 0) { - sb.append(sourceChar); - } - } - } - - return sb.toString(); - } - - /** - * This method takes a String as an argument and removes all occurrences of - * the supplied Char. It returns the resulting String. - */ - public static String removeChar(String s, char chr) { - - StringBuilder sb = new StringBuilder(s.length()); - - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c != chr){ - sb.append(c); - } - } - - return sb.toString(); - } - - /** - * This method takes a String as an argument and removes all occurrences of - * the supplied Chars. It returns the resulting String. - */ - public static String removeChars(String s, char[] chr) { - - StringBuilder sb = new StringBuilder(s.length()); - - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (!charMatch(c, chr)){ - sb.append(c); - } - } - - return sb.toString(); - } - - private static boolean charMatch(int iChr, char[] chr) { - for (int i = 0; i < chr.length; i++) { - if (iChr == chr[i]) { - return true; - } - } - return false; - } - -} +package com.avaje.ebeaninternal.server.lib.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +/** + * Utility String class that supports String manipulation functions. + */ +public class StringHelper { + + private static final char SINGLE_QUOTE = '\''; + + private static final char DOUBLE_QUOTE = '"'; + + /** + * parses a String of the form name1='value1' name2='value2'. Note that you + * can use either single or double quotes for any particular name value pair + * and the end quote must match the begin quote. + */ + public static HashMap parseNameQuotedValue(String tag) throws RuntimeException { + + if (tag == null || tag.length() < 1) { + return null; + } + + // make sure that the quotes are matched... + // int remainer = countOccurances(tag, ""+quote) % 2; + // if (remainer == 1) { + // dp("remainder = "+remainer); + // throw new StringParsingException("Unmatched quote in "+tag); + // } + + // make sure that th last character is not an equals... + // (check now so I don't need to check this every time..) + if (tag.charAt(tag.length() - 1) == '=') { + throw new RuntimeException("missing quoted value at the end of " + tag); + } + + HashMap map = new HashMap(); + // recursively parse out the name value pairs... + return parseNameQuotedValue(map, tag, 0); + } + + /** + * recursively parse out name value pairs (where the value is quoted, with + * either single or double quotes). + */ + private static HashMap parseNameQuotedValue(HashMap map, + String tag, int pos) throws RuntimeException { + + int equalsPos = tag.indexOf("=", pos); + if (equalsPos > -1) { + // check for begin quote... + char firstQuote = tag.charAt(equalsPos + 1); + if (firstQuote != SINGLE_QUOTE && firstQuote != DOUBLE_QUOTE) { + throw new RuntimeException("missing begin quote at " + (equalsPos) + "[" + + tag.charAt(equalsPos + 1) + "] in [" + tag + "]"); + } + + // check for end quote... + int endQuotePos = tag.indexOf(firstQuote, equalsPos + 2); + if (endQuotePos == -1) { + throw new RuntimeException("missing end quote [" + firstQuote + "] after " + pos + + " in [" + tag + "]"); + } + + // we have a valid name and value... + // dp("pos="+pos+" equalsPos="+equalsPos+" + // endQuotePos="+endQuotePos); + String name = tag.substring(pos, equalsPos); + String value = tag.substring(equalsPos + 2, endQuotePos); + // dp("name="+name+"; value="+value+";"); + + // trim off any whitespace from the front of name... + name = trimFront(name, " "); + if ((name.indexOf(SINGLE_QUOTE) > -1) || (name.indexOf(DOUBLE_QUOTE) > -1)) { + throw new RuntimeException("attribute name contains a quote [" + name + "]"); + } + map.put(name, value); + + return parseNameQuotedValue(map, tag, endQuotePos + 1); + + } else { + // no more equals... stop parsing... + return map; + } + } + + /** + * Returns the number of times a particular String occurs in another String. + * e.g. count the number of single quotes. + */ + public static int countOccurances(String content, String occurs) { + return countOccurances(content, occurs, 0, 0); + } + + private static int countOccurances(String content, String occurs, int pos, int countSoFar) { + int equalsPos = content.indexOf(occurs, pos); + if (equalsPos > -1) { + countSoFar = countSoFar + 1; + pos = equalsPos + occurs.length(); + // dp("countSoFar="+countSoFar+" pos="+pos); + return countOccurances(content, occurs, pos, countSoFar); + } else { + return countSoFar; + } + } + + /** + * Parses out a list of Name Value pairs that are delimited together. Will + * always return a StringMap. If allNameValuePairs is null, or no name + * values can be parsed out an empty StringMap is returned. + * + * @param allNameValuePairs + * the entire string to be parsed. + * @param listDelimiter + * (typically ';') the delimited between the list + * @param nameValueSeparator + * (typically '=') the separator between the name and value + */ + public static Map delimitedToMap(String allNameValuePairs, + String listDelimiter, String nameValueSeparator) { + + HashMap params = new HashMap(); + if ((allNameValuePairs == null) || (allNameValuePairs.length() == 0)) { + return params; + } + // trim off any leading listDelimiter... + allNameValuePairs = trimFront(allNameValuePairs, listDelimiter); + return getKeyValue(params, 0, allNameValuePairs, listDelimiter, nameValueSeparator); + } + + /** + * Trims off recurring strings from the front of a string. + * + * @param source + * the source string + * @param trim + * the string to trim off the front + */ + public static String trimFront(String source, String trim) { + if (source == null) { + return null; + } + if (source.indexOf(trim) == 0) { + // dp("trim ..."); + return trimFront(source.substring(trim.length()), trim); + } else { + return source; + } + } + + /** + * Return true if the value is null or an empty string. + */ + public static boolean isNull(String value) { + if (value == null || value.trim().length() == 0) { + return true; + } + return false; + } + + /** + * Recursively pulls out the key value pairs from a raw string. + */ + private static HashMap getKeyValue(HashMap map, int pos, + String allNameValuePairs, String listDelimiter, String nameValueSeparator) { + + if (pos >= allNameValuePairs.length()) { + // dp("end as "+pos+" >= "+allNameValuePairs.length() ); + return map; + } + + int equalsPos = allNameValuePairs.indexOf(nameValueSeparator, pos); + int delimPos = allNameValuePairs.indexOf(listDelimiter, pos); + + if (delimPos == -1) { + delimPos = allNameValuePairs.length(); + } + if (equalsPos == -1) { + // dp("no more equals..."); + return map; + } + if (delimPos == (equalsPos + 1)) { + // dp("Ignoring as nothing between delim and equals... + // delim:"+delimPos+" eq:"+equalsPos); + return getKeyValue(map, delimPos + 1, allNameValuePairs, listDelimiter, + nameValueSeparator); + } + if (equalsPos > delimPos) { + // there is a key without a value? + String key = allNameValuePairs.substring(pos, delimPos); + key = key.trim(); + if (key.length() > 0) { + map.put(key, null); + } + return getKeyValue(map, delimPos + 1, allNameValuePairs, listDelimiter, + nameValueSeparator); + + } + String key = allNameValuePairs.substring(pos, equalsPos); + + if (delimPos > -1) { + String value = allNameValuePairs.substring(equalsPos + 1, delimPos); + // dp("cont "+key+","+value+" pos:"+pos+" + // len:"+allNameValuePairs.length()); + key = key.trim(); + + map.put(key, value); + pos = delimPos + 1; + + // recurse the rest of the values... + return getKeyValue(map, pos, allNameValuePairs, listDelimiter, nameValueSeparator); + } else { + // dp("ERROR: delimPos < 0 ???"); + return map; + } + } + + /** + * Convert a string that has delimited values (say comma delimited) in a + * String[]. You must explicitly choose whether or not to include empty + * values (say two commas that a right beside each other. + * + *

+ * e.g. "alpha,beta,,theta"
+ * With keepEmpties true, this results in a String[] of size 4 with the + * third one having a String of 0 length. With keepEmpties false, this + * results in a String[] of size 3. + *

+ *

+ *

+ *

+ * e.g. ",alpha,beta,,theta,"
+ * With keepEmpties true, this results in a String[] of size 6 with the + * 1st,4th and 6th one having a String of 0 length. With keepEmpties false, + * this results in a String[] of size 3. + *

+ */ + public static String[] delimitedToArray(String str, String delimiter, boolean keepEmpties) { + + ArrayList list = new ArrayList(); + int startPos = 0; + delimiter(str, delimiter, keepEmpties, startPos, list); + String[] result = new String[list.size()]; + return (String[]) list.toArray(result); + } + + private static void delimiter(String str, String delimiter, boolean keepEmpties, int startPos, + ArrayList list) { + + int endPos = str.indexOf(delimiter, startPos); + if (endPos == -1) { + if (startPos <= str.length()) { + String lastValue = str.substring(startPos, str.length()); + // dp("lastValue="+lastValue); + if (!keepEmpties && lastValue.length() == 0) { + // dp("not keeping..."); + } else { + list.add(lastValue); + } + } + // we have finished parsing the string... + return; + } else { + // get the delimited value... add it.. + String value = str.substring(startPos, endPos); + // dp(startPos+","+endPos+" value="+value); + if (!keepEmpties && value.length() == 0) { + // dp("not keeping..."); + } else { + list.add(value); + } + // recursively search as we are not at the end yet... + delimiter(str, delimiter, keepEmpties, endPos + 1, list); + } + } + + /** + * This returns the FIRST string in str that is bounded on the left by + * leftBound, and bounded on the right by rightBound. This will return null + * if the leftBound is not found within str. + * + *

+ * If leftBound can't be found this returns null. + *

+ *

+ * This rightBound can't be found then this throws a + * StringIndexOutOfBoundsException. + *

+ * + * @param str + * the base string that we will search for the bounded string. + * @param leftBound + * the left bound of the string. + * @param rightBound + * the right bound of the string. + */ + public static String getBoundedString(String str, String leftBound, String rightBound) + throws RuntimeException { + + if (str == null) { + throw new RuntimeException("string to parse is null?"); + } + int startPos = str.indexOf(leftBound); + if (startPos > -1) { + startPos = startPos + leftBound.length(); + int endPos = str.indexOf(rightBound, startPos); + // dp(str+" start:"+startPos+" end:"+endPos); + if (endPos == -1) { + throw new RuntimeException("Can't find rightBound: " + rightBound); + } + return str.substring(startPos, endPos); + } else { + // if no leftBound can be found.. return null... could be in a + // search n parse type loop? + // this keeps "no tag"==null different from "tag not formed + // properly"==StringParsingException + return null; + } + } + + /** + * Takes the String bounded by leftBound & rightBound, and replaces it with + * replaceString. Actually removes the left and right bound strings aswell. + */ + public static String setBoundedString(String str, String leftBound, String rightBound, + String replaceString) { + + int startPos = str.indexOf(leftBound); + if (startPos > -1) { + // startPos = startPos; + int endPos = str.indexOf(rightBound, startPos + leftBound.length()); + if (endPos > -1) { + String toReplace = str.substring(startPos, endPos + 1); + return replaceString(str, toReplace, replaceString); + } else { + return str; + } + } else { + return str; + } + } + + // public static String replaceString(String str, String oldSub, String + // newSub) { + // + // if (str == null) { + // return null; + // } + // StringBuilder newSB = new StringBuilder(str.length()+20); + // int iPos = 0; + // int iPrevPos = 0; + // + // while (true) { + // iPos = str.indexOf(oldSub, iPrevPos); + // if (iPos > -1) { + // // found + // newSB.append(str.substring(iPrevPos, iPos)); + // newSB.append(newSub); + // iPrevPos = iPos + oldSub.length(); + // } else { + // // not found + // newSB.append(str.substring(iPrevPos)); + // break; + // } + // } + // + // return newSB.toString(); + // } + + /** + * This method takes a String and will replace all occurrences of the match + * String with that of the replace String. + * + * @param source + * the source string + * @param match + * the string used to find a match + * @param replace + * the string used to replace match with + * @return the source string after the search and replace + */ + public static String replaceString(String source, String match, String replace) { + if (source == null){ + return null; + } + if (replace == null){ + return source; + } + if (match == null){ + throw new NullPointerException("match is null?"); + } + if (match.equals(replace)){ + return source; + } + return replaceString(source, match, replace, 30, 0, source.length()); + } + + /** + * Additionally specify the additionalSize to add to the buffer. This will + * make the buffer bigger so that it doesn't have to grow when replacement + * occurs. + */ + public static String replaceString(String source, String match, String replace, + int additionalSize, int startPos, int endPos) { + + if (source == null){ + return source; + } + + char match0 = match.charAt(0); + + int matchLength = match.length(); + + if (matchLength == 1 && replace.length() == 1) { + char replace0 = replace.charAt(0); + return source.replace(match0, replace0); + } + if (matchLength >= replace.length()) { + additionalSize = 0; + } + + + int sourceLength = source.length(); + int lastMatch = endPos - matchLength; + + StringBuilder sb = new StringBuilder(sourceLength + additionalSize); + + if (startPos > 0) { + sb.append(source.substring(0, startPos)); + } + + char sourceChar; + boolean isMatch; + int sourceMatchPos; + + for (int i = startPos; i < sourceLength; i++) { + sourceChar = source.charAt(i); + if (i > lastMatch || sourceChar != match0) { + sb.append(sourceChar); + + } else { + // check to see if this is a match + isMatch = true; + sourceMatchPos = i; + + // check each following character... + for (int j = 1; j < matchLength; j++) { + sourceMatchPos++; + if (source.charAt(sourceMatchPos) != match.charAt(j)) { + isMatch = false; + break; + } + } + if (isMatch) { + i = i + matchLength - 1; + sb.append(replace); + } else { + // was not a match + sb.append(sourceChar); + } + } + } + + return sb.toString(); + } + + /** + * A search and replace with multiple matching strings. + *

+ * Useful when converting CRNL CR and NL all to a BR tag for example. + *

+ * + *

+	 * String[] multi = { "\r\n", "\r", "\n" };
+	 * content = StringHelper.replaceStringMulti(content, multi, "<br/>");
+	 * 
+ */ + public static String replaceStringMulti(String source, String[] match, String replace) { + return replaceStringMulti(source, match, replace, 30, 0, source.length()); + } + + /** + * Additionally specify an additional size estimate for the buffer plus + * start and end positions. + *

+ * The start and end positions can limit the search and replace. Otherwise + * these default to startPos = 0 and endPos = source.length(). + *

+ */ + public static String replaceStringMulti(String source, String[] match, String replace, + int additionalSize, int startPos, int endPos) { + + int shortestMatch = match[0].length(); + + char[] match0 = new char[match.length]; + for (int i = 0; i < match0.length; i++) { + match0[i] = match[i].charAt(0); + if (match[i].length() < shortestMatch) { + shortestMatch = match[i].length(); + } + } + + StringBuilder sb = new StringBuilder(source.length() + additionalSize); + + char sourceChar; + + int len = source.length(); + int lastMatch = endPos - shortestMatch; + + if (startPos > 0) { + sb.append(source.substring(0, startPos)); + } + + int matchCount = 0; + + for (int i = startPos; i < len; i++) { + sourceChar = source.charAt(i); + if (i > lastMatch) { + sb.append(sourceChar); + } else { + matchCount = 0; + for (int k = 0; k < match0.length; k++) { + if (matchCount == 0 && sourceChar == match0[k]) { + if (match[k].length() + i <= len) { + + ++matchCount; + int j = 1; + for (; j < match[k].length(); j++) { + if (source.charAt(i + j) != match[k].charAt(j)) { + --matchCount; + break; + } + } + if (matchCount > 0) { + i = i + j - 1; + sb.append(replace); + break; + } + } + } + } + if (matchCount == 0) { + sb.append(sourceChar); + } + } + } + + return sb.toString(); + } + + /** + * This method takes a String as an argument and removes all occurrences of + * the supplied Char. It returns the resulting String. + */ + public static String removeChar(String s, char chr) { + + StringBuilder sb = new StringBuilder(s.length()); + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c != chr){ + sb.append(c); + } + } + + return sb.toString(); + } + + /** + * This method takes a String as an argument and removes all occurrences of + * the supplied Chars. It returns the resulting String. + */ + public static String removeChars(String s, char[] chr) { + + StringBuilder sb = new StringBuilder(s.length()); + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (!charMatch(c, chr)){ + sb.append(c); + } + } + + return sb.toString(); + } + + private static boolean charMatch(int iChr, char[] chr) { + for (int i = 0; i < chr.length; i++) { + if (iChr == chr[i]) { + return true; + } + } + return false; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/StringParsingException.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/StringParsingException.java index 076ec57bc..cdd091a54 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/StringParsingException.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/StringParsingException.java @@ -1,32 +1,15 @@ -/** - * 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.lib.util; - - -/** - * A general string parsing exception. - */ -public class StringParsingException extends RuntimeException { - - static final long serialVersionUID = -3070423471260426402L; - - public StringParsingException(String message) { - super(message); - } -}; - +package com.avaje.ebeaninternal.server.lib.util; + + +/** + * A general string parsing exception. + */ +public class StringParsingException extends RuntimeException { + + static final long serialVersionUID = -3070423471260426402L; + + public StringParsingException(String message) { + super(message); + } +}; + diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/ThrowablePrinter.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/ThrowablePrinter.java index 6ec2abffd..a21a4cb0e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/ThrowablePrinter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/ThrowablePrinter.java @@ -1,108 +1,91 @@ -/** - * 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.lib.util; - -/** - * Builds a string from a stack trace. - *

- * Generally used to flatten a stack trace into a single string - * removing \r\n and limiting the size of any given stack. - *

- */ -public class ThrowablePrinter { - - private static final String atString = " at "; - - private String newLineChar = "\\r\\n"; - - private int maxStackTraceLines = 3; - - /** - * Set the maximum number of lines in any one part of - * the stack trace. This is not the total maximum. - */ - public void setMaxStackTraceLines(int maxStackTraceLines) { - this.maxStackTraceLines = maxStackTraceLines; - } - - /** - * Set the new line character used to replace \r\n with. - * This is useful so that the stack is placed on a single line - * in a log file. - */ - public void setNewLineChar(String newLineChar) { - this.newLineChar = newLineChar; - } - - /** - * Convert the error into a string representation. - *

- * Replaces the \r\n and limits the stack lines. - *

- */ - public String print(Throwable e) { - StringBuffer sb = new StringBuffer(); - printThrowable(sb, e, false); - - String line = sb.toString(); - line = StringHelper.replaceString(line, "\r", "\\r"); - line = StringHelper.replaceString(line, "\n", "\\n"); - - return line; - } - - /** - * Recursively output the Throwable stack trace to the log. - * - * @param sb the buffer to write the stack trace to - * @param e the source throwable - * @param isCause flag to indicate if this is the top level throwable or a - * cause - */ - protected void printThrowable(StringBuffer sb, Throwable e, boolean isCause) { - if (e != null) { - if (isCause) { - sb.append("Caused by: "); - } - sb.append(e.getClass().getName()); - sb.append(":"); - sb.append(e.getMessage()).append(newLineChar); - - StackTraceElement[] ste = e.getStackTrace(); - int outputStackLines = ste.length; - int notShownCount = 0; - if (ste.length > maxStackTraceLines) { - outputStackLines = maxStackTraceLines; - notShownCount = ste.length - outputStackLines; - } - for (int i = 0; i < outputStackLines; i++) { - sb.append(atString); - sb.append(ste[i].toString()).append(newLineChar); - } - if (notShownCount > 0) { - sb.append(" ... "); - sb.append(notShownCount); - sb.append(" more").append(newLineChar); - } - Throwable cause = e.getCause(); - if (cause != null) { - printThrowable(sb, cause, true); - } - } - } -} +package com.avaje.ebeaninternal.server.lib.util; + +/** + * Builds a string from a stack trace. + *

+ * Generally used to flatten a stack trace into a single string + * removing \r\n and limiting the size of any given stack. + *

+ */ +public class ThrowablePrinter { + + private static final String atString = " at "; + + private String newLineChar = "\\r\\n"; + + private int maxStackTraceLines = 3; + + /** + * Set the maximum number of lines in any one part of + * the stack trace. This is not the total maximum. + */ + public void setMaxStackTraceLines(int maxStackTraceLines) { + this.maxStackTraceLines = maxStackTraceLines; + } + + /** + * Set the new line character used to replace \r\n with. + * This is useful so that the stack is placed on a single line + * in a log file. + */ + public void setNewLineChar(String newLineChar) { + this.newLineChar = newLineChar; + } + + /** + * Convert the error into a string representation. + *

+ * Replaces the \r\n and limits the stack lines. + *

+ */ + public String print(Throwable e) { + StringBuffer sb = new StringBuffer(); + printThrowable(sb, e, false); + + String line = sb.toString(); + line = StringHelper.replaceString(line, "\r", "\\r"); + line = StringHelper.replaceString(line, "\n", "\\n"); + + return line; + } + + /** + * Recursively output the Throwable stack trace to the log. + * + * @param sb the buffer to write the stack trace to + * @param e the source throwable + * @param isCause flag to indicate if this is the top level throwable or a + * cause + */ + protected void printThrowable(StringBuffer sb, Throwable e, boolean isCause) { + if (e != null) { + if (isCause) { + sb.append("Caused by: "); + } + sb.append(e.getClass().getName()); + sb.append(":"); + sb.append(e.getMessage()).append(newLineChar); + + StackTraceElement[] ste = e.getStackTrace(); + int outputStackLines = ste.length; + int notShownCount = 0; + if (ste.length > maxStackTraceLines) { + outputStackLines = maxStackTraceLines; + notShownCount = ste.length - outputStackLines; + } + for (int i = 0; i < outputStackLines; i++) { + sb.append(atString); + sb.append(ste[i].toString()).append(newLineChar); + } + if (notShownCount > 0) { + sb.append(" ... "); + sb.append(notShownCount); + sb.append(" more").append(newLineChar); + } + Throwable cause = e.getCause(); + if (cause != null) { + printThrowable(sb, cause, true); + } + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java index f873682ea..b20ce5076 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java @@ -1,274 +1,255 @@ -/** - * 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.loadcontext; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebean.bean.BeanLoader; -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.LoadContext; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties; - -/** - * Default implementation of LoadBeanContext. - * - */ -public class DLoadBeanContext implements LoadBeanContext, BeanLoader { - - private static final Logger logger = Logger.getLogger(DLoadBeanContext.class.getName()); - - protected final DLoadContext parent; - - protected final BeanDescriptor desc; - - protected final String path; - - protected final String fullPath; - - private final DLoadList weakList; - - private final OrmQueryProperties queryProps; - - private int batchSize; - - public DLoadBeanContext(DLoadContext parent, BeanDescriptor desc, String path, int batchSize, - OrmQueryProperties queryProps, DLoadList weakList) { - - this.parent = parent; - this.desc = desc; - this.path = path; - this.batchSize = batchSize; - this.queryProps = queryProps; - this.weakList = weakList; - - if (parent.getRelativePath() == null) { - this.fullPath = path; - } else { - this.fullPath = parent.getRelativePath() + "." + path; - } - } - - public void configureQuery(SpiQuery query, String lazyLoadProperty) { - - // propagate the readOnly state - if (parent.isReadOnly() != null) { - query.setReadOnly(parent.isReadOnly()); - } - query.setParentNode(getObjectGraphNode()); - query.setLazyLoadProperty(lazyLoadProperty); - - if (queryProps != null) { - queryProps.configureBeanQuery(query); - } - if (parent.isUseAutofetchManager()) { - query.setAutofetch(true); - } - } - - public String getFullPath() { - return fullPath; - } - - public PersistenceContext getPersistenceContext() { - return parent.getPersistenceContext(); - } - - public OrmQueryProperties getQueryProps() { - return queryProps; - } - - public ObjectGraphNode getObjectGraphNode() { - return parent.getObjectGraphNode(path); - } - - public String getPath() { - return path; - } - - public String getName() { - return parent.getEbeanServer().getName(); - } - - public int getBatchSize() { - return batchSize; - } - - public void setBatchSize(int batchSize) { - this.batchSize = batchSize; - } - - public BeanDescriptor getBeanDescriptor() { - return desc; - } - - public LoadContext getGraphContext() { - return parent; - } - - public void register(EntityBeanIntercept ebi){ - int pos = weakList.add(ebi); - ebi.setBeanLoader(pos, this, parent.getPersistenceContext()); - } - - /** - * Check if we can load the bean from L2 cache. If so avoid loading from the DB. - */ - private boolean loadBeanFromCache(EntityBeanIntercept ebi, int position) { - - if (!desc.loadFromCache(ebi)) { - return false; - } - // we loaded the bean from cache - weakList.removeEntry(position); - if (logger.isLoggable(Level.FINEST)) { - logger.log(Level.FINEST, "Loading path:" + fullPath + " - bean loaded from L2 cache, position[" + position + "]"); - } - return true; - } - - /** - * Load this bean and potentially a batch of similar beans. - */ - public void loadBean(EntityBeanIntercept ebi) { - - // A synchronized (this) is effectively held by EntityBeanIntercept.loadBean() - - if (desc.lazyLoadMany(ebi)) { - // lazy load property was a Many - return; - } - - int position = ebi.getBeanLoaderIndex(); - boolean hitCache = !parent.isExcludeBeanCache() && desc.isBeanCaching(); - - if (hitCache && loadBeanFromCache(ebi, position)) { - // successfully hit the L2 cache so don't invoke DB lazy loading - return; - } - - // Get a batch of beans to lazy load - List batch = null; - try { - batch = weakList.getLoadBatch(position, batchSize); - } catch (IllegalStateException e) { - logger.log(Level.SEVERE, "type["+desc.getFullName()+"] fullPath[" + fullPath + "] batchSize["+batchSize+"]", e); - } - - if (hitCache && batchSize > 1) { - // Check each of the beans in the batch to see if they are in the L2 cache. - // Add more as necessary to make up our batch that will be loaded. - batch = loadBeanCheckBatch(batch); - } - - if (logger.isLoggable(Level.FINER)) { - for (int i = 0; i < batch.size(); i++) { - - EntityBeanIntercept entityBeanIntercept = batch.get(i); - EntityBean owner = entityBeanIntercept.getOwner(); - Object id = desc.getId(owner); - - logger.finer("LoadBean type["+owner.getClass().getName()+"] fullPath["+fullPath+"] id["+id+"] batchIndex["+i+"] beanLoaderIndex["+entityBeanIntercept.getBeanLoaderIndex()+"]"); - } - } - - LoadBeanRequest req = new LoadBeanRequest(this, batch, null, batchSize, true, ebi.getLazyLoadProperty(), hitCache); - parent.getEbeanServer().loadBean(req); - - } - - /** - * Check each of the beans in the batch to see if they are in the cache. - * Get more beans out as necessary to get our desired batch size. - */ - private List loadBeanCheckBatch(List batch) { - - - List actualLoadBatch = new ArrayList(batchSize); - List batchToCheck = batch; - - int loadedFromCache = 0; - - while (true) { - // check each bean (not already checked) to see if it is in the cache - for (int i = 0; i < batchToCheck.size(); i++) { - if (!desc.loadFromCache(batchToCheck.get(i))) { - actualLoadBatch.add(batchToCheck.get(i)); - } else { - loadedFromCache++; - if (logger.isLoggable(Level.FINEST)) { - logger.log(Level.FINEST, "Loading path:" + fullPath + " - bean loaded from L2 cache(batch)"); - } - } - } - - if (batchToCheck.isEmpty()) { - // we have exhausted all the beans that need lazy loading - break; - } - int more = batchSize - actualLoadBatch.size(); - if (more <= 0 || loadedFromCache > 500) { - break; - } - // get some more to check as we loaded some from L2 cache - batchToCheck = weakList.getNextBatch(more); - } - return actualLoadBatch; - } - - public void loadSecondaryQuery(OrmQueryRequest parentRequest, int requestedBatchSize, boolean all) { - - synchronized (this) { - do { - List batch = weakList.getNextBatch(requestedBatchSize); - if (batch.size() == 0) { - // there are no beans to load - if (logger.isLoggable(Level.FINEST)) { - logger.log(Level.FINEST, "Loading path:" + fullPath + " - no more beans to load"); - } - return; - } - boolean loadCache = false; - LoadBeanRequest req = new LoadBeanRequest(this, batch, parentRequest.getTransaction(), requestedBatchSize, false, null, loadCache); - - if (logger.isLoggable(Level.FINEST)) { - logger.log(Level.FINEST, "Loading path:" + fullPath + " - secondary query batch load [" + batch.size() + "] beans"); - } - - parent.getEbeanServer().loadBean(req); - if (!all) { - break; - } - - } while (true); - } - } - -} +package com.avaje.ebeaninternal.server.loadcontext; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebean.bean.BeanLoader; +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.LoadContext; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties; + +/** + * Default implementation of LoadBeanContext. + * + */ +public class DLoadBeanContext implements LoadBeanContext, BeanLoader { + + private static final Logger logger = Logger.getLogger(DLoadBeanContext.class.getName()); + + protected final DLoadContext parent; + + protected final BeanDescriptor desc; + + protected final String path; + + protected final String fullPath; + + private final DLoadList weakList; + + private final OrmQueryProperties queryProps; + + private int batchSize; + + public DLoadBeanContext(DLoadContext parent, BeanDescriptor desc, String path, int batchSize, + OrmQueryProperties queryProps, DLoadList weakList) { + + this.parent = parent; + this.desc = desc; + this.path = path; + this.batchSize = batchSize; + this.queryProps = queryProps; + this.weakList = weakList; + + if (parent.getRelativePath() == null) { + this.fullPath = path; + } else { + this.fullPath = parent.getRelativePath() + "." + path; + } + } + + public void configureQuery(SpiQuery query, String lazyLoadProperty) { + + // propagate the readOnly state + if (parent.isReadOnly() != null) { + query.setReadOnly(parent.isReadOnly()); + } + query.setParentNode(getObjectGraphNode()); + query.setLazyLoadProperty(lazyLoadProperty); + + if (queryProps != null) { + queryProps.configureBeanQuery(query); + } + if (parent.isUseAutofetchManager()) { + query.setAutofetch(true); + } + } + + public String getFullPath() { + return fullPath; + } + + public PersistenceContext getPersistenceContext() { + return parent.getPersistenceContext(); + } + + public OrmQueryProperties getQueryProps() { + return queryProps; + } + + public ObjectGraphNode getObjectGraphNode() { + return parent.getObjectGraphNode(path); + } + + public String getPath() { + return path; + } + + public String getName() { + return parent.getEbeanServer().getName(); + } + + public int getBatchSize() { + return batchSize; + } + + public void setBatchSize(int batchSize) { + this.batchSize = batchSize; + } + + public BeanDescriptor getBeanDescriptor() { + return desc; + } + + public LoadContext getGraphContext() { + return parent; + } + + public void register(EntityBeanIntercept ebi){ + int pos = weakList.add(ebi); + ebi.setBeanLoader(pos, this, parent.getPersistenceContext()); + } + + /** + * Check if we can load the bean from L2 cache. If so avoid loading from the DB. + */ + private boolean loadBeanFromCache(EntityBeanIntercept ebi, int position) { + + if (!desc.loadFromCache(ebi)) { + return false; + } + // we loaded the bean from cache + weakList.removeEntry(position); + if (logger.isLoggable(Level.FINEST)) { + logger.log(Level.FINEST, "Loading path:" + fullPath + " - bean loaded from L2 cache, position[" + position + "]"); + } + return true; + } + + /** + * Load this bean and potentially a batch of similar beans. + */ + public void loadBean(EntityBeanIntercept ebi) { + + // A synchronized (this) is effectively held by EntityBeanIntercept.loadBean() + + if (desc.lazyLoadMany(ebi)) { + // lazy load property was a Many + return; + } + + int position = ebi.getBeanLoaderIndex(); + boolean hitCache = !parent.isExcludeBeanCache() && desc.isBeanCaching(); + + if (hitCache && loadBeanFromCache(ebi, position)) { + // successfully hit the L2 cache so don't invoke DB lazy loading + return; + } + + // Get a batch of beans to lazy load + List batch = null; + try { + batch = weakList.getLoadBatch(position, batchSize); + } catch (IllegalStateException e) { + logger.log(Level.SEVERE, "type["+desc.getFullName()+"] fullPath[" + fullPath + "] batchSize["+batchSize+"]", e); + } + + if (hitCache && batchSize > 1) { + // Check each of the beans in the batch to see if they are in the L2 cache. + // Add more as necessary to make up our batch that will be loaded. + batch = loadBeanCheckBatch(batch); + } + + if (logger.isLoggable(Level.FINER)) { + for (int i = 0; i < batch.size(); i++) { + + EntityBeanIntercept entityBeanIntercept = batch.get(i); + EntityBean owner = entityBeanIntercept.getOwner(); + Object id = desc.getId(owner); + + logger.finer("LoadBean type["+owner.getClass().getName()+"] fullPath["+fullPath+"] id["+id+"] batchIndex["+i+"] beanLoaderIndex["+entityBeanIntercept.getBeanLoaderIndex()+"]"); + } + } + + LoadBeanRequest req = new LoadBeanRequest(this, batch, null, batchSize, true, ebi.getLazyLoadProperty(), hitCache); + parent.getEbeanServer().loadBean(req); + + } + + /** + * Check each of the beans in the batch to see if they are in the cache. + * Get more beans out as necessary to get our desired batch size. + */ + private List loadBeanCheckBatch(List batch) { + + + List actualLoadBatch = new ArrayList(batchSize); + List batchToCheck = batch; + + int loadedFromCache = 0; + + while (true) { + // check each bean (not already checked) to see if it is in the cache + for (int i = 0; i < batchToCheck.size(); i++) { + if (!desc.loadFromCache(batchToCheck.get(i))) { + actualLoadBatch.add(batchToCheck.get(i)); + } else { + loadedFromCache++; + if (logger.isLoggable(Level.FINEST)) { + logger.log(Level.FINEST, "Loading path:" + fullPath + " - bean loaded from L2 cache(batch)"); + } + } + } + + if (batchToCheck.isEmpty()) { + // we have exhausted all the beans that need lazy loading + break; + } + int more = batchSize - actualLoadBatch.size(); + if (more <= 0 || loadedFromCache > 500) { + break; + } + // get some more to check as we loaded some from L2 cache + batchToCheck = weakList.getNextBatch(more); + } + return actualLoadBatch; + } + + public void loadSecondaryQuery(OrmQueryRequest parentRequest, int requestedBatchSize, boolean all) { + + synchronized (this) { + do { + List batch = weakList.getNextBatch(requestedBatchSize); + if (batch.size() == 0) { + // there are no beans to load + if (logger.isLoggable(Level.FINEST)) { + logger.log(Level.FINEST, "Loading path:" + fullPath + " - no more beans to load"); + } + return; + } + boolean loadCache = false; + LoadBeanRequest req = new LoadBeanRequest(this, batch, parentRequest.getTransaction(), requestedBatchSize, false, null, loadCache); + + if (logger.isLoggable(Level.FINEST)) { + logger.log(Level.FINEST, "Loading path:" + fullPath + " - secondary query batch load [" + batch.size() + "] beans"); + } + + parent.getEbeanServer().loadBean(req); + if (!all) { + break; + } + + } while (true); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java index 57758aa44..e26e5660e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java @@ -1,337 +1,318 @@ -/** - * 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.loadcontext; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.bean.ObjectGraphOrigin; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebeaninternal.api.LoadContext; -import com.avaje.ebeaninternal.api.LoadSecondaryQuery; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties; - -/** - * Default implementation of LoadContext. - * - * @author rbygrave - */ -public class DLoadContext implements LoadContext { - - private final SpiEbeanServer ebeanServer; - - private final BeanDescriptor rootDescriptor; - - private final Map beanMap = new HashMap(); - private final Map manyMap = new HashMap(); - - private final DLoadBeanContext rootBeanContext; - - private final Boolean readOnly; - private final boolean excludeBeanCache; - private final int defaultBatchSize; - - /** - * The path relative to the root of the object graph. - */ - private final String relativePath; - private final ObjectGraphOrigin origin; - private final boolean useAutofetchManager; - private final boolean hardRefs; - - private final Map nodePathMap = new HashMap(); - - private PersistenceContext persistenceContext; - private List secQuery; - - public DLoadContext(SpiEbeanServer ebeanServer, BeanDescriptor rootDescriptor, Boolean readOnly, SpiQuery query) { - this(ebeanServer, rootDescriptor, readOnly, - Boolean.FALSE.equals(query.isUseBeanCache()), - query.getParentNode(), - query.getAutoFetchManager() != null); - } - - public DLoadContext(SpiEbeanServer ebeanServer, BeanDescriptor rootDescriptor, Boolean readOnly, - boolean excludeBeanCache, ObjectGraphNode parentNode, boolean useAutofetchManager) { - - this.ebeanServer = ebeanServer; - this.hardRefs = GlobalProperties.getBoolean("ebean.hardrefs", false); - this.defaultBatchSize = ebeanServer.getLazyLoadBatchSize(); - this.rootDescriptor = rootDescriptor; - this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null, createBeanLoadList()); - this.readOnly = readOnly; - this.excludeBeanCache = excludeBeanCache; - this.useAutofetchManager = useAutofetchManager; - - if (parentNode != null){ - this.origin = parentNode.getOriginQueryPoint(); - this.relativePath = parentNode.getPath(); - } else { - this.origin = null; - this.relativePath = null; - } - } - - protected boolean isExcludeBeanCache() { - return excludeBeanCache; - } - - /** - * Return the minimum batch size when using QueryIterator with query joins. - */ - public int getSecondaryQueriesMinBatchSize(OrmQueryRequest parentRequest, int defaultQueryBatch) { - - if (secQuery == null){ - return -1; - } - - int maxBatch = 0; - for (int i = 0; i < secQuery.size(); i++) { - int batchSize = secQuery.get(i).getQueryFetchBatch(); - if (batchSize == 0){ - batchSize = defaultQueryBatch; - } - maxBatch = Math.max(maxBatch, batchSize); - } - return maxBatch; - } - - /** - * Execute all the secondary queries. - */ - public void executeSecondaryQueries(OrmQueryRequest parentRequest, int defaultQueryBatch) { - - if (secQuery != null){ - for (int i = 0; i < secQuery.size(); i++) { - OrmQueryProperties properties = secQuery.get(i); - - int batchSize = properties.getQueryFetchBatch(); - if (batchSize == 0){ - batchSize = defaultQueryBatch; - } - LoadSecondaryQuery load = getLoadSecondaryQuery(properties.getPath()); - load.loadSecondaryQuery(parentRequest, batchSize, properties.isQueryFetchAll()); - } - } - } - - /** - * Return the LoadBeanContext or LoadManyContext for the given path. - */ - private LoadSecondaryQuery getLoadSecondaryQuery(String path){ - LoadSecondaryQuery beanLoad = beanMap.get(path); - if (beanLoad == null){ - beanLoad = manyMap.get(path); - } - return beanLoad; - } - - /** - * Remove the +query and +lazy secondary queries and - * register them with their appropriate LoadBeanContext - * or LoadManyContext. - *

- * The parts of the secondary queries are removed and used - * by LoadBeanContext/LoadManyContext to build the appropriate - * queries. - *

- */ - public void registerSecondaryQueries(SpiQuery query) { - - secQuery = query.removeQueryJoins(); - if (secQuery != null){ - for (int i = 0; i < secQuery.size(); i++) { - OrmQueryProperties props = secQuery.get(i); - registerSecondaryQuery(props); - } - } - - List lazyQueries = query.removeLazyJoins(); - if (lazyQueries != null){ - for (int i = 0; i < lazyQueries.size(); i++) { - OrmQueryProperties lazyProps = lazyQueries.get(i); - registerSecondaryQuery(lazyProps); - } - } - } - - /** - * Setup the load context at this path with OrmQueryProperties which is - * used to build the appropriate query for +query or +lazy loading. - */ - private void registerSecondaryQuery(OrmQueryProperties props) { - - String propName = props.getPath(); - ElPropertyValue elGetValue = rootDescriptor.getElGetValue(propName); - - boolean many = elGetValue.getBeanProperty().containsMany(); - registerSecondaryNode(many, props); - } - - - public ObjectGraphNode getObjectGraphNode(String path) { - - ObjectGraphNode node = nodePathMap.get(path); - if (node == null){ - node = createObjectGraphNode(path); - nodePathMap.put(path, node); - } - - return node; - } - - private ObjectGraphNode createObjectGraphNode(String path) { - - if (relativePath != null){ - if (path == null){ - path = relativePath; - } else { - path = relativePath+"."+path; - } - } - return new ObjectGraphNode(origin, path); - } - - public boolean isUseAutofetchManager() { - return useAutofetchManager; - } - - public String getRelativePath() { - return relativePath; - } - - protected SpiEbeanServer getEbeanServer() { - return ebeanServer; - } - - /** - * Return the parent state which defines the sharedInstance and readOnly status - * which needs to be propagated to other beans and collections. - */ - protected Boolean isReadOnly() { - return readOnly; - } - - public PersistenceContext getPersistenceContext() { - return persistenceContext; - } - - public void setPersistenceContext(PersistenceContext persistenceContext) { - this.persistenceContext = persistenceContext; - } - - public void register(String path, EntityBeanIntercept ebi){ - getBeanContext(path).register(ebi); - } - - public void register(String path, BeanCollection bc){ - getManyContext(path).register(bc); - } - - private DLoadBeanContext getBeanContext(String path) { - if (path == null){ - return rootBeanContext; - } - DLoadBeanContext beanContext = beanMap.get(path); - if (beanContext == null){ - beanContext = createBeanContext(path, defaultBatchSize, null); - beanMap.put(path, beanContext); - } - return beanContext; - } - - private void registerSecondaryNode(boolean many, OrmQueryProperties props) { - - String path = props.getPath(); - int lazyJoinBatch = props.getLazyFetchBatch(); - int batchSize = lazyJoinBatch > 0 ? lazyJoinBatch : defaultBatchSize; - - if (many){ - DLoadManyContext manyContext = createManyContext(path, batchSize, props); - manyMap.put(path, manyContext); - } else { - DLoadBeanContext beanContext = createBeanContext(path, batchSize, props); - beanMap.put(path, beanContext); - } - } - - private DLoadManyContext getManyContext(String path) { - if (path == null){ - throw new RuntimeException("path is null?"); - } - DLoadManyContext ctx = manyMap.get(path); - if (ctx == null){ - ctx = createManyContext(path, defaultBatchSize, null); - manyMap.put(path, ctx); - } - return ctx; - } - - private DLoadManyContext createManyContext(String path, int batchSize, OrmQueryProperties queryProps) { - - BeanPropertyAssocMany p = (BeanPropertyAssocMany)getBeanProperty(rootDescriptor, path); - - return new DLoadManyContext(this, p, path, batchSize, queryProps, createBeanCollectionLoadList()); - } - - - private DLoadList createBeanLoadList() { - if (hardRefs){ - return new DLoadHardList(); - } else { - return new DLoadWeakList(); - } - } - - private DLoadList> createBeanCollectionLoadList() { - if (hardRefs){ - return new DLoadHardList>(); - } else { - return new DLoadWeakList>(); - } - } - - private DLoadBeanContext createBeanContext(String path, int batchSize, OrmQueryProperties queryProps) { - - BeanPropertyAssoc p = (BeanPropertyAssoc)getBeanProperty(rootDescriptor, path); - BeanDescriptor targetDescriptor = p.getTargetDescriptor(); - - return new DLoadBeanContext(this, targetDescriptor, path, batchSize, queryProps, createBeanLoadList()); - } - - private BeanProperty getBeanProperty(BeanDescriptor desc, String path){ - - return desc.getBeanPropertyFromPath(path); - } - -} +package com.avaje.ebeaninternal.server.loadcontext; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebean.bean.ObjectGraphOrigin; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebeaninternal.api.LoadContext; +import com.avaje.ebeaninternal.api.LoadSecondaryQuery; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties; + +/** + * Default implementation of LoadContext. + * + * @author rbygrave + */ +public class DLoadContext implements LoadContext { + + private final SpiEbeanServer ebeanServer; + + private final BeanDescriptor rootDescriptor; + + private final Map beanMap = new HashMap(); + private final Map manyMap = new HashMap(); + + private final DLoadBeanContext rootBeanContext; + + private final Boolean readOnly; + private final boolean excludeBeanCache; + private final int defaultBatchSize; + + /** + * The path relative to the root of the object graph. + */ + private final String relativePath; + private final ObjectGraphOrigin origin; + private final boolean useAutofetchManager; + private final boolean hardRefs; + + private final Map nodePathMap = new HashMap(); + + private PersistenceContext persistenceContext; + private List secQuery; + + public DLoadContext(SpiEbeanServer ebeanServer, BeanDescriptor rootDescriptor, Boolean readOnly, SpiQuery query) { + this(ebeanServer, rootDescriptor, readOnly, + Boolean.FALSE.equals(query.isUseBeanCache()), + query.getParentNode(), + query.getAutoFetchManager() != null); + } + + public DLoadContext(SpiEbeanServer ebeanServer, BeanDescriptor rootDescriptor, Boolean readOnly, + boolean excludeBeanCache, ObjectGraphNode parentNode, boolean useAutofetchManager) { + + this.ebeanServer = ebeanServer; + this.hardRefs = GlobalProperties.getBoolean("ebean.hardrefs", false); + this.defaultBatchSize = ebeanServer.getLazyLoadBatchSize(); + this.rootDescriptor = rootDescriptor; + this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null, createBeanLoadList()); + this.readOnly = readOnly; + this.excludeBeanCache = excludeBeanCache; + this.useAutofetchManager = useAutofetchManager; + + if (parentNode != null){ + this.origin = parentNode.getOriginQueryPoint(); + this.relativePath = parentNode.getPath(); + } else { + this.origin = null; + this.relativePath = null; + } + } + + protected boolean isExcludeBeanCache() { + return excludeBeanCache; + } + + /** + * Return the minimum batch size when using QueryIterator with query joins. + */ + public int getSecondaryQueriesMinBatchSize(OrmQueryRequest parentRequest, int defaultQueryBatch) { + + if (secQuery == null){ + return -1; + } + + int maxBatch = 0; + for (int i = 0; i < secQuery.size(); i++) { + int batchSize = secQuery.get(i).getQueryFetchBatch(); + if (batchSize == 0){ + batchSize = defaultQueryBatch; + } + maxBatch = Math.max(maxBatch, batchSize); + } + return maxBatch; + } + + /** + * Execute all the secondary queries. + */ + public void executeSecondaryQueries(OrmQueryRequest parentRequest, int defaultQueryBatch) { + + if (secQuery != null){ + for (int i = 0; i < secQuery.size(); i++) { + OrmQueryProperties properties = secQuery.get(i); + + int batchSize = properties.getQueryFetchBatch(); + if (batchSize == 0){ + batchSize = defaultQueryBatch; + } + LoadSecondaryQuery load = getLoadSecondaryQuery(properties.getPath()); + load.loadSecondaryQuery(parentRequest, batchSize, properties.isQueryFetchAll()); + } + } + } + + /** + * Return the LoadBeanContext or LoadManyContext for the given path. + */ + private LoadSecondaryQuery getLoadSecondaryQuery(String path){ + LoadSecondaryQuery beanLoad = beanMap.get(path); + if (beanLoad == null){ + beanLoad = manyMap.get(path); + } + return beanLoad; + } + + /** + * Remove the +query and +lazy secondary queries and + * register them with their appropriate LoadBeanContext + * or LoadManyContext. + *

+ * The parts of the secondary queries are removed and used + * by LoadBeanContext/LoadManyContext to build the appropriate + * queries. + *

+ */ + public void registerSecondaryQueries(SpiQuery query) { + + secQuery = query.removeQueryJoins(); + if (secQuery != null){ + for (int i = 0; i < secQuery.size(); i++) { + OrmQueryProperties props = secQuery.get(i); + registerSecondaryQuery(props); + } + } + + List lazyQueries = query.removeLazyJoins(); + if (lazyQueries != null){ + for (int i = 0; i < lazyQueries.size(); i++) { + OrmQueryProperties lazyProps = lazyQueries.get(i); + registerSecondaryQuery(lazyProps); + } + } + } + + /** + * Setup the load context at this path with OrmQueryProperties which is + * used to build the appropriate query for +query or +lazy loading. + */ + private void registerSecondaryQuery(OrmQueryProperties props) { + + String propName = props.getPath(); + ElPropertyValue elGetValue = rootDescriptor.getElGetValue(propName); + + boolean many = elGetValue.getBeanProperty().containsMany(); + registerSecondaryNode(many, props); + } + + + public ObjectGraphNode getObjectGraphNode(String path) { + + ObjectGraphNode node = nodePathMap.get(path); + if (node == null){ + node = createObjectGraphNode(path); + nodePathMap.put(path, node); + } + + return node; + } + + private ObjectGraphNode createObjectGraphNode(String path) { + + if (relativePath != null){ + if (path == null){ + path = relativePath; + } else { + path = relativePath+"."+path; + } + } + return new ObjectGraphNode(origin, path); + } + + public boolean isUseAutofetchManager() { + return useAutofetchManager; + } + + public String getRelativePath() { + return relativePath; + } + + protected SpiEbeanServer getEbeanServer() { + return ebeanServer; + } + + /** + * Return the parent state which defines the sharedInstance and readOnly status + * which needs to be propagated to other beans and collections. + */ + protected Boolean isReadOnly() { + return readOnly; + } + + public PersistenceContext getPersistenceContext() { + return persistenceContext; + } + + public void setPersistenceContext(PersistenceContext persistenceContext) { + this.persistenceContext = persistenceContext; + } + + public void register(String path, EntityBeanIntercept ebi){ + getBeanContext(path).register(ebi); + } + + public void register(String path, BeanCollection bc){ + getManyContext(path).register(bc); + } + + private DLoadBeanContext getBeanContext(String path) { + if (path == null){ + return rootBeanContext; + } + DLoadBeanContext beanContext = beanMap.get(path); + if (beanContext == null){ + beanContext = createBeanContext(path, defaultBatchSize, null); + beanMap.put(path, beanContext); + } + return beanContext; + } + + private void registerSecondaryNode(boolean many, OrmQueryProperties props) { + + String path = props.getPath(); + int lazyJoinBatch = props.getLazyFetchBatch(); + int batchSize = lazyJoinBatch > 0 ? lazyJoinBatch : defaultBatchSize; + + if (many){ + DLoadManyContext manyContext = createManyContext(path, batchSize, props); + manyMap.put(path, manyContext); + } else { + DLoadBeanContext beanContext = createBeanContext(path, batchSize, props); + beanMap.put(path, beanContext); + } + } + + private DLoadManyContext getManyContext(String path) { + if (path == null){ + throw new RuntimeException("path is null?"); + } + DLoadManyContext ctx = manyMap.get(path); + if (ctx == null){ + ctx = createManyContext(path, defaultBatchSize, null); + manyMap.put(path, ctx); + } + return ctx; + } + + private DLoadManyContext createManyContext(String path, int batchSize, OrmQueryProperties queryProps) { + + BeanPropertyAssocMany p = (BeanPropertyAssocMany)getBeanProperty(rootDescriptor, path); + + return new DLoadManyContext(this, p, path, batchSize, queryProps, createBeanCollectionLoadList()); + } + + + private DLoadList createBeanLoadList() { + if (hardRefs){ + return new DLoadHardList(); + } else { + return new DLoadWeakList(); + } + } + + private DLoadList> createBeanCollectionLoadList() { + if (hardRefs){ + return new DLoadHardList>(); + } else { + return new DLoadWeakList>(); + } + } + + private DLoadBeanContext createBeanContext(String path, int batchSize, OrmQueryProperties queryProps) { + + BeanPropertyAssoc p = (BeanPropertyAssoc)getBeanProperty(rootDescriptor, path); + BeanDescriptor targetDescriptor = p.getTargetDescriptor(); + + return new DLoadBeanContext(this, targetDescriptor, path, batchSize, queryProps, createBeanLoadList()); + } + + private BeanProperty getBeanProperty(BeanDescriptor desc, String path){ + + return desc.getBeanPropertyFromPath(path); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadHardList.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadHardList.java index f4ce75e64..d2db05e77 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadHardList.java +++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadHardList.java @@ -1,125 +1,106 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.loadcontext; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class DLoadHardList implements DLoadList { - - private static final Logger logger = Logger.getLogger(DLoadHardList.class.getName()); - - protected final ArrayList list = new ArrayList(); - - protected int removedFromTop; - - protected DLoadHardList() { - - } - - public int add(T e) { - synchronized (this) { - int i = list.size(); - list.add(e); - return i; - } - } - - public void removeEntry(int position) { - synchronized (this) { - T object = list.get(position); - if (object == null) { - logger.log(Level.WARNING, "removeEntry found no Object for position[" + position + "]"); - } else { - // just set the entry to null - list.set(position, null); - } - if (position == removedFromTop) { - removedFromTop++; - } - } - } - - public List getNextBatch(int batchSize) { - if (removedFromTop >= list.size()){ - return new ArrayList(0); - } - return getLoadBatch(removedFromTop, batchSize, true); - } - - public List getLoadBatch(int position, int batchSize) { - return getLoadBatch(position, batchSize, false); - } - - private List getLoadBatch(int position, int batchSize, boolean ignoreMissing) { - - synchronized (this) { - if (batchSize < 1) { - throw new RuntimeException("batchSize " + batchSize + " < 1 ??!!"); - } - - ArrayList batch = new ArrayList(); - - if (!addObjectToBatchAt(batch, position) && !ignoreMissing) { - String msg = "getLoadBatch position[" + position + "] didn't find a bean in the list?"; - throw new IllegalStateException(msg); - } - - for (int i = position; i < list.size(); i++) { - addObjectToBatchAt(batch, i); - if (batch.size() == batchSize) { - // found enough beans going forward - return batch; - } - } - - // search the front of the list to fill our batch - for (int i = removedFromTop; i < position; i++) { - addObjectToBatchAt(batch, i); - if (batch.size() == batchSize) { - // found enough beans going forward from start of list - return batch; - } - } - - return batch; - } - } - - private boolean addObjectToBatchAt(ArrayList batch, int i) { - - boolean found = false; - T object = list.get(i); - if (object != null) { - found = true; - batch.add(object); - // set it to null saying we have loaded this one - list.set(i, null); - } - - if (i == removedFromTop) { - removedFromTop++; - } - return found; - } - -} +package com.avaje.ebeaninternal.server.loadcontext; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DLoadHardList implements DLoadList { + + private static final Logger logger = Logger.getLogger(DLoadHardList.class.getName()); + + protected final ArrayList list = new ArrayList(); + + protected int removedFromTop; + + protected DLoadHardList() { + + } + + public int add(T e) { + synchronized (this) { + int i = list.size(); + list.add(e); + return i; + } + } + + public void removeEntry(int position) { + synchronized (this) { + T object = list.get(position); + if (object == null) { + logger.log(Level.WARNING, "removeEntry found no Object for position[" + position + "]"); + } else { + // just set the entry to null + list.set(position, null); + } + if (position == removedFromTop) { + removedFromTop++; + } + } + } + + public List getNextBatch(int batchSize) { + if (removedFromTop >= list.size()){ + return new ArrayList(0); + } + return getLoadBatch(removedFromTop, batchSize, true); + } + + public List getLoadBatch(int position, int batchSize) { + return getLoadBatch(position, batchSize, false); + } + + private List getLoadBatch(int position, int batchSize, boolean ignoreMissing) { + + synchronized (this) { + if (batchSize < 1) { + throw new RuntimeException("batchSize " + batchSize + " < 1 ??!!"); + } + + ArrayList batch = new ArrayList(); + + if (!addObjectToBatchAt(batch, position) && !ignoreMissing) { + String msg = "getLoadBatch position[" + position + "] didn't find a bean in the list?"; + throw new IllegalStateException(msg); + } + + for (int i = position; i < list.size(); i++) { + addObjectToBatchAt(batch, i); + if (batch.size() == batchSize) { + // found enough beans going forward + return batch; + } + } + + // search the front of the list to fill our batch + for (int i = removedFromTop; i < position; i++) { + addObjectToBatchAt(batch, i); + if (batch.size() == batchSize) { + // found enough beans going forward from start of list + return batch; + } + } + + return batch; + } + } + + private boolean addObjectToBatchAt(ArrayList batch, int i) { + + boolean found = false; + T object = list.get(i); + if (object != null) { + found = true; + batch.add(object); + // set it to null saying we have loaded this one + list.set(i, null); + } + + if (i == removedFromTop) { + removedFromTop++; + } + return found; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java index cf51b35c0..677e8c6ea 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java @@ -1,180 +1,161 @@ -/** - * 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.loadcontext; - -import java.util.List; - -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.BeanCollectionLoader; -import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.api.LoadManyContext; -import com.avaje.ebeaninternal.api.LoadManyRequest; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties; - -public class DLoadManyContext implements LoadManyContext, BeanCollectionLoader { - - protected final DLoadContext parent; - - protected final String fullPath; - - private final BeanDescriptor desc; - - private final BeanPropertyAssocMany property; - - private final String path; - - private final int batchSize; - - private final OrmQueryProperties queryProps; - - private final DLoadList> weakList; - - public DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany p, - String path, int batchSize, OrmQueryProperties queryProps, DLoadList> weakList) { - - this.parent = parent; - this.property = p; - this.desc = p.getBeanDescriptor(); - this.path = path; - this.batchSize = batchSize; - this.queryProps = queryProps; - this.weakList = weakList;//new DLoadWeakList>(); - - if (parent.getRelativePath() == null){ - this.fullPath = path; - } else { - this.fullPath = parent.getRelativePath()+"."+path; - } - - } - - public void configureQuery(SpiQuery query){ - - // propagate the readOnly state - if (parent.isReadOnly() != null){ - query.setReadOnly(parent.isReadOnly()); - } - query.setParentNode(getObjectGraphNode()); - - if (queryProps != null){ - queryProps.configureManyQuery(query); - } - - if (parent.isUseAutofetchManager()){ - query.setAutofetch(true); - } - } - - public ObjectGraphNode getObjectGraphNode() { - - // we return the parent node ... as we actually - // query on the parent selecting just it's id - - int pos = path.lastIndexOf('.'); - if (pos == -1){ - return parent.getObjectGraphNode(null); - } else { - String parentPath = path.substring(0, pos); - return parent.getObjectGraphNode(parentPath); - } - } - - public String getFullPath() { - return fullPath; - } - - public PersistenceContext getPersistenceContext() { - return parent.getPersistenceContext(); - } - - public int getBatchSize() { - return batchSize; - } - - public BeanPropertyAssocMany getBeanProperty() { - return property; - } - - public BeanDescriptor getBeanDescriptor() { - return desc; - } - - public String getPath() { - return path; - } - - public String getName() { - return parent.getEbeanServer().getName(); - } - - public void register(BeanCollection bc){ - int pos = weakList.add(bc); - bc.setLoader(pos, this); - } - - public void loadMany(BeanCollection bc, boolean onlyIds) { - - int position = bc.getLoaderIndex(); - - LoadManyRequest req; - synchronized (weakList) { - boolean hitCache = desc.isBeanCaching() && !onlyIds && !parent.isExcludeBeanCache(); - if (hitCache){ - Object ownerBean = bc.getOwnerBean(); - BeanDescriptor parentDesc = desc.getBeanDescriptor(ownerBean.getClass()); - Object parentId = parentDesc.getId(ownerBean); - if (parentDesc.cacheLoadMany(property, bc, parentId, parent.isReadOnly(), false)) { - // we loaded the bean from cache - weakList.removeEntry(position); - return; - } - } - - List> loadBatch = weakList.getLoadBatch(position, batchSize); - req = new LoadManyRequest(this, loadBatch, null, batchSize, true, onlyIds, hitCache); - } - parent.getEbeanServer().loadMany(req); - } - - public void loadSecondaryQuery(OrmQueryRequest parentRequest, int requestedBatchSize, boolean all){ - - do { - LoadManyRequest req; - synchronized (weakList) { - List> batch = weakList.getNextBatch(requestedBatchSize); - if (batch.size() == 0){ - return; - } - req = new LoadManyRequest(this, batch, parentRequest.getTransaction(), requestedBatchSize, false, false, false); - } - parent.getEbeanServer().loadMany(req); - if (!all){ - // queryFirst(batch) - break; - } - } while (true); - } - -} +package com.avaje.ebeaninternal.server.loadcontext; + +import java.util.List; + +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.BeanCollectionLoader; +import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebeaninternal.api.LoadManyContext; +import com.avaje.ebeaninternal.api.LoadManyRequest; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties; + +public class DLoadManyContext implements LoadManyContext, BeanCollectionLoader { + + protected final DLoadContext parent; + + protected final String fullPath; + + private final BeanDescriptor desc; + + private final BeanPropertyAssocMany property; + + private final String path; + + private final int batchSize; + + private final OrmQueryProperties queryProps; + + private final DLoadList> weakList; + + public DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany p, + String path, int batchSize, OrmQueryProperties queryProps, DLoadList> weakList) { + + this.parent = parent; + this.property = p; + this.desc = p.getBeanDescriptor(); + this.path = path; + this.batchSize = batchSize; + this.queryProps = queryProps; + this.weakList = weakList;//new DLoadWeakList>(); + + if (parent.getRelativePath() == null){ + this.fullPath = path; + } else { + this.fullPath = parent.getRelativePath()+"."+path; + } + + } + + public void configureQuery(SpiQuery query){ + + // propagate the readOnly state + if (parent.isReadOnly() != null){ + query.setReadOnly(parent.isReadOnly()); + } + query.setParentNode(getObjectGraphNode()); + + if (queryProps != null){ + queryProps.configureManyQuery(query); + } + + if (parent.isUseAutofetchManager()){ + query.setAutofetch(true); + } + } + + public ObjectGraphNode getObjectGraphNode() { + + // we return the parent node ... as we actually + // query on the parent selecting just it's id + + int pos = path.lastIndexOf('.'); + if (pos == -1){ + return parent.getObjectGraphNode(null); + } else { + String parentPath = path.substring(0, pos); + return parent.getObjectGraphNode(parentPath); + } + } + + public String getFullPath() { + return fullPath; + } + + public PersistenceContext getPersistenceContext() { + return parent.getPersistenceContext(); + } + + public int getBatchSize() { + return batchSize; + } + + public BeanPropertyAssocMany getBeanProperty() { + return property; + } + + public BeanDescriptor getBeanDescriptor() { + return desc; + } + + public String getPath() { + return path; + } + + public String getName() { + return parent.getEbeanServer().getName(); + } + + public void register(BeanCollection bc){ + int pos = weakList.add(bc); + bc.setLoader(pos, this); + } + + public void loadMany(BeanCollection bc, boolean onlyIds) { + + int position = bc.getLoaderIndex(); + + LoadManyRequest req; + synchronized (weakList) { + boolean hitCache = desc.isBeanCaching() && !onlyIds && !parent.isExcludeBeanCache(); + if (hitCache){ + Object ownerBean = bc.getOwnerBean(); + BeanDescriptor parentDesc = desc.getBeanDescriptor(ownerBean.getClass()); + Object parentId = parentDesc.getId(ownerBean); + if (parentDesc.cacheLoadMany(property, bc, parentId, parent.isReadOnly(), false)) { + // we loaded the bean from cache + weakList.removeEntry(position); + return; + } + } + + List> loadBatch = weakList.getLoadBatch(position, batchSize); + req = new LoadManyRequest(this, loadBatch, null, batchSize, true, onlyIds, hitCache); + } + parent.getEbeanServer().loadMany(req); + } + + public void loadSecondaryQuery(OrmQueryRequest parentRequest, int requestedBatchSize, boolean all){ + + do { + LoadManyRequest req; + synchronized (weakList) { + List> batch = weakList.getNextBatch(requestedBatchSize); + if (batch.size() == 0){ + return; + } + req = new LoadManyRequest(this, batch, parentRequest.getTransaction(), requestedBatchSize, false, false, false); + } + parent.getEbeanServer().loadMany(req); + if (!all){ + // queryFirst(batch) + break; + } + } while (true); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadWeakList.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadWeakList.java index ff4b9972d..9c88fdad9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadWeakList.java +++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadWeakList.java @@ -1,134 +1,115 @@ -/** - * 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.loadcontext; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class DLoadWeakList implements DLoadList { - - private static final Logger logger = Logger.getLogger(DLoadWeakList.class.getName()); - - protected final ArrayList> list = new ArrayList>(); - - protected int removedFromTop; - - protected DLoadWeakList() { - - } - - public int add(T e) { - synchronized (this) { - int i = list.size(); - list.add(new WeakReference(e)); - return i; - } - } - - public void removeEntry(int position) { - synchronized (this) { - WeakReference wref = list.get(position); - if (wref == null) { - logger.log(Level.WARNING, "removeEntry found no WeakReference for position[" + position + "]"); - } else { - // just set the entry to null - list.set(position, null); - T object = wref.get(); - if (object == null) { - logger.log(Level.WARNING, "removeEntry found no Object held by WeakReference for position[" + position + "]"); - } - } - if (position == removedFromTop) { - removedFromTop++; - } - } - } - - public List getNextBatch(int batchSize) { - if (removedFromTop >= list.size()){ - return new ArrayList(0); - } - return getLoadBatch(removedFromTop, batchSize, true); - } - - public List getLoadBatch(int position, int batchSize) { - return getLoadBatch(position, batchSize, false); - } - - private List getLoadBatch(int position, int batchSize, boolean ignoreMissing) { - - synchronized (this) { - if (batchSize < 1) { - throw new RuntimeException("batchSize " + batchSize + " < 1 ??!!"); - } - - ArrayList batch = new ArrayList(); - - if (!addObjectToBatchAt(batch, position) && !ignoreMissing) { - String msg = "getLoadBatch position[" + position + "] didn't find a bean in the list?"; - throw new IllegalStateException(msg); - } - - for (int i = position; i < list.size(); i++) { - addObjectToBatchAt(batch, i); - if (batch.size() == batchSize) { - // found enough beans going forward - return batch; - } - } - - // search the front of the list to fill our batch - for (int i = removedFromTop; i < position; i++) { - addObjectToBatchAt(batch, i); - if (batch.size() == batchSize) { - // found enough beans going forward from start of list - return batch; - } - } - - return batch; - } - } - - private boolean addObjectToBatchAt(ArrayList batch, int i) { - - boolean found = false; - WeakReference wref = list.get(i); - if (wref != null) { - T object = wref.get(); - if (object == null) { - logger.log(Level.WARNING, "Bean is null from weak reference"); - } else { - found = true; - batch.add(object); - } - // set it to null saying we have loaded this one - list.set(i, null); - } - if (i == removedFromTop) { - removedFromTop++; - } - return found; - } - -} +package com.avaje.ebeaninternal.server.loadcontext; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DLoadWeakList implements DLoadList { + + private static final Logger logger = Logger.getLogger(DLoadWeakList.class.getName()); + + protected final ArrayList> list = new ArrayList>(); + + protected int removedFromTop; + + protected DLoadWeakList() { + + } + + public int add(T e) { + synchronized (this) { + int i = list.size(); + list.add(new WeakReference(e)); + return i; + } + } + + public void removeEntry(int position) { + synchronized (this) { + WeakReference wref = list.get(position); + if (wref == null) { + logger.log(Level.WARNING, "removeEntry found no WeakReference for position[" + position + "]"); + } else { + // just set the entry to null + list.set(position, null); + T object = wref.get(); + if (object == null) { + logger.log(Level.WARNING, "removeEntry found no Object held by WeakReference for position[" + position + "]"); + } + } + if (position == removedFromTop) { + removedFromTop++; + } + } + } + + public List getNextBatch(int batchSize) { + if (removedFromTop >= list.size()){ + return new ArrayList(0); + } + return getLoadBatch(removedFromTop, batchSize, true); + } + + public List getLoadBatch(int position, int batchSize) { + return getLoadBatch(position, batchSize, false); + } + + private List getLoadBatch(int position, int batchSize, boolean ignoreMissing) { + + synchronized (this) { + if (batchSize < 1) { + throw new RuntimeException("batchSize " + batchSize + " < 1 ??!!"); + } + + ArrayList batch = new ArrayList(); + + if (!addObjectToBatchAt(batch, position) && !ignoreMissing) { + String msg = "getLoadBatch position[" + position + "] didn't find a bean in the list?"; + throw new IllegalStateException(msg); + } + + for (int i = position; i < list.size(); i++) { + addObjectToBatchAt(batch, i); + if (batch.size() == batchSize) { + // found enough beans going forward + return batch; + } + } + + // search the front of the list to fill our batch + for (int i = removedFromTop; i < position; i++) { + addObjectToBatchAt(batch, i); + if (batch.size() == batchSize) { + // found enough beans going forward from start of list + return batch; + } + } + + return batch; + } + } + + private boolean addObjectToBatchAt(ArrayList batch, int i) { + + boolean found = false; + WeakReference wref = list.get(i); + if (wref != null) { + T object = wref.get(); + if (object == null) { + logger.log(Level.WARNING, "Bean is null from weak reference"); + } else { + found = true; + batch.add(object); + } + // set it to null saying we have loaded this one + list.set(i, null); + } + if (i == removedFromTop) { + removedFromTop++; + } + return found; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/package-info.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/package-info.java index 90cdc170a..4e75032d8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/package-info.java +++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/package-info.java @@ -1,4 +1 @@ -/** - * Load context (supports batch lazy loading). - */ package com.avaje.ebeaninternal.server.loadcontext; \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java index 39047127d..1a9360935 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchControl.java @@ -1,269 +1,250 @@ -/** - * 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.persist; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.PersistRequest; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; - -/** - * Controls the batch ordering of persist requests. - *

- * Persist requests include bean inserts updates deletes and UpdateSql and - * CallableSql requests. - *

- *

- * This object queues up the requests into appropriate entries according to the - * 'depth' and the 'type' of the requests. The depth relates to how saves and - * deletes cascade following the associations of a bean. For saving Associated - * One cascades reduce the depth (-1) and associated many's increase the depth. - * The initial depth of a request is 0. - *

- */ -public final class BatchControl { - - private static final Logger logger = Logger.getLogger(BatchControl.class.getName()); - - /** - * Used to sort queue entries by depth. - */ - private static final BatchDepthComparator depthComparator = new BatchDepthComparator(); - - /** - * The associated transaction. - */ - private final SpiTransaction transaction; - - /** - * Controls batching of the PreparedStatements. This should be flushed after - * each 'depth'. - */ - private final BatchedPstmtHolder pstmtHolder = new BatchedPstmtHolder(); - - /** - * The size at which the batch queue will flush. This should be close to the - * number of statements that are batched into a single PreparedStatement. This - * size relates to the size of a list in a BatchQueueEntry and not the total - * number of request which could be more than that. - */ - private int batchSize; - - /** - * If true try to get generated keys from inserts. - */ - private boolean getGeneratedKeys; - - private boolean batchFlushOnMixed = true; - - private final BatchedBeanControl beanControl; - - /** - * Create for a given transaction, PersistExecute, default size and - * getGeneratedKeys. - */ - public BatchControl(SpiTransaction t, int batchSize, boolean getGenKeys) { - this.transaction = t; - this.batchSize = batchSize; - this.getGeneratedKeys = getGenKeys; - this.beanControl = new BatchedBeanControl(t, this); - transaction.setBatchControl(this); - } - - /** - * Set this flag to false to allow batching of a mix of Beans and UpdateSql - * (or CallableSql). Normally if you mix the two this will result in an - * automatic flush. - *

- * Note that UpdateSql and CallableSql will ALWAYS flush first. This is due to - * it already having been bound to a PreparedStatement where as the Beans go - * through a 2 step process when they are flushed (delayed binding). - *

- */ - public void setBatchFlushOnMixed(boolean flushBatchOnMixed) { - this.batchFlushOnMixed = flushBatchOnMixed; - } - - /** - * Return the batchSize. - */ - public int getBatchSize() { - return batchSize; - } - - /** - * Set the size of batch execution. - *

- * The user can set this via the Transaction. - *

- */ - public void setBatchSize(int batchSize) { - if (batchSize > 1) { - this.batchSize = batchSize; - } - } - - /** - * Set whether or not to use getGeneratedKeys for this batch execution. - *

- * The user can set this via the transaction - *

- */ - public void setGetGeneratedKeys(Boolean getGeneratedKeys) { - if (getGeneratedKeys != null) { - this.getGeneratedKeys = getGeneratedKeys; - } - } - - /** - * Execute a Orm Update, SqlUpdate or CallableSql. - *

- * These all go straight to jdbc and use addBatch(). Entity beans goto a queue - * and wait there so that the jdbc is executed in the correct order according - * to the depth. - *

- */ - public int executeStatementOrBatch(PersistRequest request, boolean batch) { - if (!batch || (batchFlushOnMixed && !beanControl.isEmpty())) { - // flush when mixing beans and updateSql - flush(); - } - if (!batch) { - // execute the request immediately without batching - return request.executeNow(); - } - - if (pstmtHolder.getMaxSize() >= batchSize) { - flush(); - } - // for OrmUpdate, SqlUpdate, CallableSql there is no queue... - // so straight to jdbc prepared statement and use addBatch(). - // aka executeNow() may use addBatch(). - request.executeNow(); - return -1; - } - - /** - * Entity Bean insert, update or delete. This will either execute the request - * immediately or queue it for batch processing later. The queue is flushed - * according to the depth (object graph depth). - */ - public int executeOrQueue(PersistRequestBean request, boolean batch) { - - if (!batch || (batchFlushOnMixed && !pstmtHolder.isEmpty())) { - // flush when mixing beans and updateSql - flush(); - } - if (!batch) { - return request.executeNow(); - } - - // get the list we will add this request to - ArrayList persistList = beanControl.getPersistList(request); - if (persistList == null) { - // special case where the same bean instance has been added - // to the batch more than once - if (logger.isLoggable(Level.FINE)) { - logger.fine("Bean instance already in this batch: " + request.getBean()); - } - return -1; - } - - if (persistList.size() >= batchSize) { - // flush everything that has been batched - flush(); - - // we need to get the persistList again after the - // flush as the flush clears out the bean holders - persistList = beanControl.getPersistList(request); - } - - persistList.add(request); - return -1; - } - - /** - * Return the actual batch of PreparedStatements. - */ - public BatchedPstmtHolder getPstmtHolder() { - return pstmtHolder; - } - - /** - * Return true if the queue is empty. - */ - public boolean isEmpty() { - return (beanControl.isEmpty() && pstmtHolder.isEmpty()); - } - - /** - * Flush any batched PreparedStatements. - */ - protected void flushPstmtHolder() { - pstmtHolder.flush(getGeneratedKeys); - } - - /** - * Execute all the requests contained in the list. - */ - protected void executeNow(ArrayList list) { - for (int i = 0; i < list.size(); i++) { - list.get(i).executeNow(); - } - } - - /** - * execute all the requests currently queued or batched. - */ - public void flush() throws PersistenceException { - - if (!pstmtHolder.isEmpty()) { - // Flush existing pstmts (updateSql or callableSql) - flushPstmtHolder(); - } - if (beanControl.isEmpty()) { - // Nothing in queue to flush - return; - } - - // convert entry map to array for sorting - BatchedBeanHolder[] bsArray = beanControl.getArray(); - - // sort the entries by depth - Arrays.sort(bsArray, depthComparator); - - if (transaction.isLogSummary()) { - transaction.logInternal("BatchControl flush " + Arrays.toString(bsArray)); - } - for (int i = 0; i < bsArray.length; i++) { - BatchedBeanHolder bs = bsArray[i]; - bs.executeNow(); - // flush all the batched Pstmts - flushPstmtHolder(); - } - } - -} +package com.avaje.ebeaninternal.server.persist; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.PersistRequest; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; + +/** + * Controls the batch ordering of persist requests. + *

+ * Persist requests include bean inserts updates deletes and UpdateSql and + * CallableSql requests. + *

+ *

+ * This object queues up the requests into appropriate entries according to the + * 'depth' and the 'type' of the requests. The depth relates to how saves and + * deletes cascade following the associations of a bean. For saving Associated + * One cascades reduce the depth (-1) and associated many's increase the depth. + * The initial depth of a request is 0. + *

+ */ +public final class BatchControl { + + private static final Logger logger = Logger.getLogger(BatchControl.class.getName()); + + /** + * Used to sort queue entries by depth. + */ + private static final BatchDepthComparator depthComparator = new BatchDepthComparator(); + + /** + * The associated transaction. + */ + private final SpiTransaction transaction; + + /** + * Controls batching of the PreparedStatements. This should be flushed after + * each 'depth'. + */ + private final BatchedPstmtHolder pstmtHolder = new BatchedPstmtHolder(); + + /** + * The size at which the batch queue will flush. This should be close to the + * number of statements that are batched into a single PreparedStatement. This + * size relates to the size of a list in a BatchQueueEntry and not the total + * number of request which could be more than that. + */ + private int batchSize; + + /** + * If true try to get generated keys from inserts. + */ + private boolean getGeneratedKeys; + + private boolean batchFlushOnMixed = true; + + private final BatchedBeanControl beanControl; + + /** + * Create for a given transaction, PersistExecute, default size and + * getGeneratedKeys. + */ + public BatchControl(SpiTransaction t, int batchSize, boolean getGenKeys) { + this.transaction = t; + this.batchSize = batchSize; + this.getGeneratedKeys = getGenKeys; + this.beanControl = new BatchedBeanControl(t, this); + transaction.setBatchControl(this); + } + + /** + * Set this flag to false to allow batching of a mix of Beans and UpdateSql + * (or CallableSql). Normally if you mix the two this will result in an + * automatic flush. + *

+ * Note that UpdateSql and CallableSql will ALWAYS flush first. This is due to + * it already having been bound to a PreparedStatement where as the Beans go + * through a 2 step process when they are flushed (delayed binding). + *

+ */ + public void setBatchFlushOnMixed(boolean flushBatchOnMixed) { + this.batchFlushOnMixed = flushBatchOnMixed; + } + + /** + * Return the batchSize. + */ + public int getBatchSize() { + return batchSize; + } + + /** + * Set the size of batch execution. + *

+ * The user can set this via the Transaction. + *

+ */ + public void setBatchSize(int batchSize) { + if (batchSize > 1) { + this.batchSize = batchSize; + } + } + + /** + * Set whether or not to use getGeneratedKeys for this batch execution. + *

+ * The user can set this via the transaction + *

+ */ + public void setGetGeneratedKeys(Boolean getGeneratedKeys) { + if (getGeneratedKeys != null) { + this.getGeneratedKeys = getGeneratedKeys; + } + } + + /** + * Execute a Orm Update, SqlUpdate or CallableSql. + *

+ * These all go straight to jdbc and use addBatch(). Entity beans goto a queue + * and wait there so that the jdbc is executed in the correct order according + * to the depth. + *

+ */ + public int executeStatementOrBatch(PersistRequest request, boolean batch) { + if (!batch || (batchFlushOnMixed && !beanControl.isEmpty())) { + // flush when mixing beans and updateSql + flush(); + } + if (!batch) { + // execute the request immediately without batching + return request.executeNow(); + } + + if (pstmtHolder.getMaxSize() >= batchSize) { + flush(); + } + // for OrmUpdate, SqlUpdate, CallableSql there is no queue... + // so straight to jdbc prepared statement and use addBatch(). + // aka executeNow() may use addBatch(). + request.executeNow(); + return -1; + } + + /** + * Entity Bean insert, update or delete. This will either execute the request + * immediately or queue it for batch processing later. The queue is flushed + * according to the depth (object graph depth). + */ + public int executeOrQueue(PersistRequestBean request, boolean batch) { + + if (!batch || (batchFlushOnMixed && !pstmtHolder.isEmpty())) { + // flush when mixing beans and updateSql + flush(); + } + if (!batch) { + return request.executeNow(); + } + + // get the list we will add this request to + ArrayList persistList = beanControl.getPersistList(request); + if (persistList == null) { + // special case where the same bean instance has been added + // to the batch more than once + if (logger.isLoggable(Level.FINE)) { + logger.fine("Bean instance already in this batch: " + request.getBean()); + } + return -1; + } + + if (persistList.size() >= batchSize) { + // flush everything that has been batched + flush(); + + // we need to get the persistList again after the + // flush as the flush clears out the bean holders + persistList = beanControl.getPersistList(request); + } + + persistList.add(request); + return -1; + } + + /** + * Return the actual batch of PreparedStatements. + */ + public BatchedPstmtHolder getPstmtHolder() { + return pstmtHolder; + } + + /** + * Return true if the queue is empty. + */ + public boolean isEmpty() { + return (beanControl.isEmpty() && pstmtHolder.isEmpty()); + } + + /** + * Flush any batched PreparedStatements. + */ + protected void flushPstmtHolder() { + pstmtHolder.flush(getGeneratedKeys); + } + + /** + * Execute all the requests contained in the list. + */ + protected void executeNow(ArrayList list) { + for (int i = 0; i < list.size(); i++) { + list.get(i).executeNow(); + } + } + + /** + * execute all the requests currently queued or batched. + */ + public void flush() throws PersistenceException { + + if (!pstmtHolder.isEmpty()) { + // Flush existing pstmts (updateSql or callableSql) + flushPstmtHolder(); + } + if (beanControl.isEmpty()) { + // Nothing in queue to flush + return; + } + + // convert entry map to array for sorting + BatchedBeanHolder[] bsArray = beanControl.getArray(); + + // sort the entries by depth + Arrays.sort(bsArray, depthComparator); + + if (transaction.isLogSummary()) { + transaction.logInternal("BatchControl flush " + Arrays.toString(bsArray)); + } + for (int i = 0; i < bsArray.length; i++) { + BatchedBeanHolder bs = bsArray[i]; + bs.executeNow(); + // flush all the batched Pstmts + flushPstmtHolder(); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchDepthComparator.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchDepthComparator.java index 851be3c3d..d5414b4e8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchDepthComparator.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchDepthComparator.java @@ -1,49 +1,30 @@ -/** - * 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.persist; - -import java.io.Serializable; -import java.util.Comparator; - -/** - * Used to sort BatchedBeanHolder by their depth. - *

- * Beans are queued and put into BatchedBeanHolder along with their depth. This - * delays the actually binding to PreparedStatements until the - * BatchedBeanHolder's are flushed. This is so that we can get the generated - * keys from inserts. These values are required to persist the 'detail' beans. - *

- */ -public class BatchDepthComparator implements Comparator, Serializable { - - private static final long serialVersionUID = 264611821665757991L; - - public int compare(BatchedBeanHolder b1, BatchedBeanHolder b2) { - - if (b1.getOrder() < b2.getOrder()) { - return -1; - } - if (b1.getOrder() == b2.getOrder()) { - return 0; - } - return 1; - } - -} +package com.avaje.ebeaninternal.server.persist; + +import java.io.Serializable; +import java.util.Comparator; + +/** + * Used to sort BatchedBeanHolder by their depth. + *

+ * Beans are queued and put into BatchedBeanHolder along with their depth. This + * delays the actually binding to PreparedStatements until the + * BatchedBeanHolder's are flushed. This is so that we can get the generated + * keys from inserts. These values are required to persist the 'detail' beans. + *

+ */ +public class BatchDepthComparator implements Comparator, Serializable { + + private static final long serialVersionUID = 264611821665757991L; + + public int compare(BatchedBeanHolder b1, BatchedBeanHolder b2) { + + if (b1.getOrder() < b2.getOrder()) { + return -1; + } + if (b1.getOrder() == b2.getOrder()) { + return 0; + } + return 1; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchPostExecute.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchPostExecute.java index f417ae469..bd1afdb26 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchPostExecute.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchPostExecute.java @@ -1,55 +1,36 @@ -/** - * 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.persist; - -import java.sql.SQLException; - -/** - * Handles the processing required after batch execution. - *

- * This includes concurrency checking, generated keys on inserts, transaction - * logging, transaction event table modifcation and for beans resetting their - * 'loaded' status. - *

- */ -public interface BatchPostExecute { - - - /** - * Check that the rowCount is correct for this execute. This is for - * performing concurrency checking in batch execution. - */ - public void checkRowCount(int rowCount) throws SQLException; - - /** - * For inserts with generated keys. Otherwise not used. - */ - public void setGeneratedKey(Object idValue); - - /** - * Execute the post execute processing. - *

- * This includes transaction logging, transaction event table modification - * and for beans resetting their 'loaded' status. - *

- */ - public void postExecute() throws SQLException; - -} +package com.avaje.ebeaninternal.server.persist; + +import java.sql.SQLException; + +/** + * Handles the processing required after batch execution. + *

+ * This includes concurrency checking, generated keys on inserts, transaction + * logging, transaction event table modifcation and for beans resetting their + * 'loaded' status. + *

+ */ +public interface BatchPostExecute { + + + /** + * Check that the rowCount is correct for this execute. This is for + * performing concurrency checking in batch execution. + */ + public void checkRowCount(int rowCount) throws SQLException; + + /** + * For inserts with generated keys. Otherwise not used. + */ + public void setGeneratedKey(Object idValue); + + /** + * Execute the post execute processing. + *

+ * This includes transaction logging, transaction event table modification + * and for beans resetting their 'loaded' status. + *

+ */ + public void postExecute() throws SQLException; + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedBeanControl.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedBeanControl.java index b734a3d36..a4caf8875 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedBeanControl.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedBeanControl.java @@ -1,98 +1,79 @@ -/** - * 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.persist; - -import java.util.ArrayList; -import java.util.HashMap; - -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.PersistRequest; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; - -/** - * Holds all the batched beans. - *

- * The beans are held here which delays the binding to a PreparedStatement. This - * 'delayed' binding is required as the beans need to be bound and executed in - * the correct order (according to the depth). - *

- */ -public class BatchedBeanControl { - - /** - * Map of the BatchedBeanHolder objects. They each have a depth and are later - * sorted by their depth to get the execution order. - */ - private final HashMap beanHoldMap = new HashMap(); - - private final SpiTransaction transaction; - - private final BatchControl batchControl; - - private int topOrder; - - public BatchedBeanControl(SpiTransaction t, BatchControl batchControl) { - this.transaction = t; - this.batchControl = batchControl; - } - - public ArrayList getPersistList(PersistRequestBean request) { - return getBeanHolder(request).getList(request); - } - - /** - * Return an entry for the given type description. The type description is - * typically the bean class name (or table name for MapBeans). - */ - private BatchedBeanHolder getBeanHolder(PersistRequestBean request) { - - BeanDescriptor beanDescriptor = request.getBeanDescriptor(); - BatchedBeanHolder batchBeanHolder = beanHoldMap.get(beanDescriptor.getFullName()); - if (batchBeanHolder == null) { - int relativeDepth = transaction.depth(0); - if (relativeDepth == 0){ - topOrder++; - } - int stmtOrder = topOrder*100 + relativeDepth; - - batchBeanHolder = new BatchedBeanHolder(batchControl, beanDescriptor, stmtOrder); - beanHoldMap.put(beanDescriptor.getFullName(), batchBeanHolder); - } - return batchBeanHolder; - } - - /** - * Return true if this holds no persist requests. - */ - public boolean isEmpty() { - return beanHoldMap.isEmpty(); - } - - /** - * Return the BatchedBeanHolder's ready for sorting and executing. - */ - public BatchedBeanHolder[] getArray() { - BatchedBeanHolder[] bsArray = new BatchedBeanHolder[beanHoldMap.size()]; - beanHoldMap.values().toArray(bsArray); - return bsArray; - } - -} +package com.avaje.ebeaninternal.server.persist; + +import java.util.ArrayList; +import java.util.HashMap; + +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.PersistRequest; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Holds all the batched beans. + *

+ * The beans are held here which delays the binding to a PreparedStatement. This + * 'delayed' binding is required as the beans need to be bound and executed in + * the correct order (according to the depth). + *

+ */ +public class BatchedBeanControl { + + /** + * Map of the BatchedBeanHolder objects. They each have a depth and are later + * sorted by their depth to get the execution order. + */ + private final HashMap beanHoldMap = new HashMap(); + + private final SpiTransaction transaction; + + private final BatchControl batchControl; + + private int topOrder; + + public BatchedBeanControl(SpiTransaction t, BatchControl batchControl) { + this.transaction = t; + this.batchControl = batchControl; + } + + public ArrayList getPersistList(PersistRequestBean request) { + return getBeanHolder(request).getList(request); + } + + /** + * Return an entry for the given type description. The type description is + * typically the bean class name (or table name for MapBeans). + */ + private BatchedBeanHolder getBeanHolder(PersistRequestBean request) { + + BeanDescriptor beanDescriptor = request.getBeanDescriptor(); + BatchedBeanHolder batchBeanHolder = beanHoldMap.get(beanDescriptor.getFullName()); + if (batchBeanHolder == null) { + int relativeDepth = transaction.depth(0); + if (relativeDepth == 0){ + topOrder++; + } + int stmtOrder = topOrder*100 + relativeDepth; + + batchBeanHolder = new BatchedBeanHolder(batchControl, beanDescriptor, stmtOrder); + beanHoldMap.put(beanDescriptor.getFullName(), batchBeanHolder); + } + return batchBeanHolder; + } + + /** + * Return true if this holds no persist requests. + */ + public boolean isEmpty() { + return beanHoldMap.isEmpty(); + } + + /** + * Return the BatchedBeanHolder's ready for sorting and executing. + */ + public BatchedBeanHolder[] getArray() { + BatchedBeanHolder[] bsArray = new BatchedBeanHolder[beanHoldMap.size()]; + beanHoldMap.values().toArray(bsArray); + return bsArray; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedBeanHolder.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedBeanHolder.java index 8e6cfde30..4868cdadb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedBeanHolder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedBeanHolder.java @@ -1,157 +1,138 @@ -/** - * 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.persist; - -import java.util.ArrayList; -import java.util.HashSet; - -import com.avaje.ebeaninternal.server.core.PersistRequest; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; - -/** - * Holds lists of persist requests for beans of a given typeDescription. - *

- * This is used to delay the actual binding of the bean to PreparedStatements. - * The reason is that don't have all the bind values yet in the case of inserts - * with getGeneratedKeys. - *

- *

- * Has a depth which is used to determine the order in which it should be - * executed. The lowest depth is executed first. - *

- */ -public class BatchedBeanHolder { - - /** - * The owning queue. - */ - private final BatchControl control; - - private final String shortDesc; - - /** - * The 'depth' which is used to determine the execution order. - */ - private final int order; - - /** - * The list of bean insert requests. - */ - private ArrayList inserts; - - /** - * The list of bean update requests. - */ - private ArrayList updates; - - /** - * The list of bean delete requests. - */ - private ArrayList deletes; - - private HashSet beanHashCodes = new HashSet(); - - /** - * Create a new entry with a given type and depth. - */ - public BatchedBeanHolder(BatchControl control, BeanDescriptor beanDescriptor, int order) { - this.control = control; - this.shortDesc = beanDescriptor.getName() + ":" + order; - this.order = order; - } - - /** - * Return the depth. - */ - public int getOrder() { - return order; - } - - /** - * Execute all the persist requests in this entry. - *

- * This will Batch all the similar requests into one or more BatchStatements - * and then execute them. - *

- */ - public void executeNow() { - // process the requests. Creates one or more PreparedStatements - // with binding addBatch() for each request. - - // Note updates and deletes can result in many PreparedStatements - // if their where clauses differ via use of IS NOT NULL. - if (inserts != null && !inserts.isEmpty()) { - control.executeNow(inserts); - inserts.clear(); - } - if (updates != null && !updates.isEmpty()) { - control.executeNow(updates); - updates.clear(); - } - if (deletes != null && !deletes.isEmpty()) { - control.executeNow(deletes); - deletes.clear(); - } - beanHashCodes.clear(); - } - - public String toString() { - return shortDesc; - } - - /** - * Return the list for the typeCode. - */ - public ArrayList getList(PersistRequestBean request) { - - Integer objHashCode = Integer.valueOf(System.identityHashCode(request.getBean())); - - if (!beanHashCodes.add(objHashCode)) { - // special case where the same bean instance has already been - // added to the batch (doesn't really occur with non-batching - // as the bean gets changed from dirty to loaded earlier) - return null; - } - - switch (request.getType()) { - case INSERT: - if (inserts == null) { - inserts = new ArrayList(); - } - return inserts; - - case UPDATE: - if (updates == null) { - updates = new ArrayList(); - } - return updates; - - case DELETE: - if (deletes == null) { - deletes = new ArrayList(); - } - return deletes; - - default: - throw new RuntimeException("Invalid type code " + request.getType()); - } - } -} +package com.avaje.ebeaninternal.server.persist; + +import java.util.ArrayList; +import java.util.HashSet; + +import com.avaje.ebeaninternal.server.core.PersistRequest; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Holds lists of persist requests for beans of a given typeDescription. + *

+ * This is used to delay the actual binding of the bean to PreparedStatements. + * The reason is that don't have all the bind values yet in the case of inserts + * with getGeneratedKeys. + *

+ *

+ * Has a depth which is used to determine the order in which it should be + * executed. The lowest depth is executed first. + *

+ */ +public class BatchedBeanHolder { + + /** + * The owning queue. + */ + private final BatchControl control; + + private final String shortDesc; + + /** + * The 'depth' which is used to determine the execution order. + */ + private final int order; + + /** + * The list of bean insert requests. + */ + private ArrayList inserts; + + /** + * The list of bean update requests. + */ + private ArrayList updates; + + /** + * The list of bean delete requests. + */ + private ArrayList deletes; + + private HashSet beanHashCodes = new HashSet(); + + /** + * Create a new entry with a given type and depth. + */ + public BatchedBeanHolder(BatchControl control, BeanDescriptor beanDescriptor, int order) { + this.control = control; + this.shortDesc = beanDescriptor.getName() + ":" + order; + this.order = order; + } + + /** + * Return the depth. + */ + public int getOrder() { + return order; + } + + /** + * Execute all the persist requests in this entry. + *

+ * This will Batch all the similar requests into one or more BatchStatements + * and then execute them. + *

+ */ + public void executeNow() { + // process the requests. Creates one or more PreparedStatements + // with binding addBatch() for each request. + + // Note updates and deletes can result in many PreparedStatements + // if their where clauses differ via use of IS NOT NULL. + if (inserts != null && !inserts.isEmpty()) { + control.executeNow(inserts); + inserts.clear(); + } + if (updates != null && !updates.isEmpty()) { + control.executeNow(updates); + updates.clear(); + } + if (deletes != null && !deletes.isEmpty()) { + control.executeNow(deletes); + deletes.clear(); + } + beanHashCodes.clear(); + } + + public String toString() { + return shortDesc; + } + + /** + * Return the list for the typeCode. + */ + public ArrayList getList(PersistRequestBean request) { + + Integer objHashCode = Integer.valueOf(System.identityHashCode(request.getBean())); + + if (!beanHashCodes.add(objHashCode)) { + // special case where the same bean instance has already been + // added to the batch (doesn't really occur with non-batching + // as the bean gets changed from dirty to loaded earlier) + return null; + } + + switch (request.getType()) { + case INSERT: + if (inserts == null) { + inserts = new ArrayList(); + } + return inserts; + + case UPDATE: + if (updates == null) { + updates = new ArrayList(); + } + return updates; + + case DELETE: + if (deletes == null) { + deletes = new ArrayList(); + } + return deletes; + + default: + throw new RuntimeException("Invalid type code " + request.getType()); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedPstmt.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedPstmt.java index 231025fdd..05d33ef1a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedPstmt.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedPstmt.java @@ -1,176 +1,157 @@ -/** - * 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.persist; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; - -import com.avaje.ebeaninternal.server.core.PstmtBatch; - -/** - * A batched statement that is held in BatchedPstmtHolder. It has a list of - * BatchPostExecute which it will process after the statement is executed. - *

- * This can hold CallableStatements as well. - *

- */ -public class BatchedPstmt { - - /** - * The underlying statement. - */ - private PreparedStatement pstmt; - - /** - * True if an insert that uses generated keys. - */ - private final boolean isGenKeys; - - /** - * The list of BatchPostExecute used to perform post processing. - */ - private final ArrayList list = new ArrayList(); - - private final String sql; - - private final PstmtBatch pstmtBatch; - - private final boolean occCheck; - - - /** - * Create with a given statement. - * @param isGenKeys true if an insert that uses generatedKeys - */ - public BatchedPstmt(PreparedStatement pstmt, boolean isGenKeys, String sql, PstmtBatch pstmtBatch, boolean occCheck) { - - this.pstmt = pstmt; - this.isGenKeys = isGenKeys; - this.sql = sql; - this.pstmtBatch = pstmtBatch; - this.occCheck = occCheck; - } - - /** - * Return the number of batched statements. - */ - public int size() { - return list.size(); - } - - /** - * Return the sql - */ - public String getSql() { - return sql; - } - - /** - * Return the statement. - */ - public PreparedStatement getStatement() { - return pstmt; - } - - /** - * Add the BatchPostExecute to the list for post execute processing. - */ - public void add(BatchPostExecute batchExecute){ - list.add(batchExecute); - } - - /** - * Execute the statement using executeBatch(). - * Run any post processing including getGeneratedKeys. - */ - public void executeBatch(boolean getGeneratedKeys) throws SQLException { - - executeAndCheckRowCounts(); - if (isGenKeys && getGeneratedKeys){ - getGeneratedKeys(); - } - postExecute(); - close(); - } - - /** - * Close the underlying statement. - */ - public void close() throws SQLException { - if (pstmt != null){ - pstmt.close(); - pstmt = null; - } - } - - private void postExecute() throws SQLException { - for (int i = 0; i < list.size(); i++) { - list.get(i).postExecute(); - } - } - - private void executeAndCheckRowCounts() throws SQLException { - - if (pstmtBatch != null){ - // oracle specific JDBC batch processing - int rc = pstmtBatch.executeBatch(pstmt, list.size(), sql, occCheck); - if (list.size() == 1){ - list.get(0).checkRowCount(rc); - } - // the optimistic concurrency row count check - // has already been done by pstmtBatch so just return - return; - - } - - // normal JDBC batch processing - int[] results = pstmt.executeBatch(); - - if (results.length != list.size()){ - String s = "results array error "+results.length+" "+list.size(); - throw new SQLException(s); - } - - // check for concurrency exceptions... - for (int i = 0; i < results.length; i++) { - list.get(i).checkRowCount(results[i]); - } - } - - private void getGeneratedKeys() throws SQLException { - - int index = 0; - ResultSet rset = pstmt.getGeneratedKeys(); - try { - while(rset.next()) { - Object idValue = rset.getObject(1); - list.get(index).setGeneratedKey(idValue); - index++; - } - } finally { - if (rset != null){ - rset.close(); - } - } - } - -} +package com.avaje.ebeaninternal.server.persist; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; + +import com.avaje.ebeaninternal.server.core.PstmtBatch; + +/** + * A batched statement that is held in BatchedPstmtHolder. It has a list of + * BatchPostExecute which it will process after the statement is executed. + *

+ * This can hold CallableStatements as well. + *

+ */ +public class BatchedPstmt { + + /** + * The underlying statement. + */ + private PreparedStatement pstmt; + + /** + * True if an insert that uses generated keys. + */ + private final boolean isGenKeys; + + /** + * The list of BatchPostExecute used to perform post processing. + */ + private final ArrayList list = new ArrayList(); + + private final String sql; + + private final PstmtBatch pstmtBatch; + + private final boolean occCheck; + + + /** + * Create with a given statement. + * @param isGenKeys true if an insert that uses generatedKeys + */ + public BatchedPstmt(PreparedStatement pstmt, boolean isGenKeys, String sql, PstmtBatch pstmtBatch, boolean occCheck) { + + this.pstmt = pstmt; + this.isGenKeys = isGenKeys; + this.sql = sql; + this.pstmtBatch = pstmtBatch; + this.occCheck = occCheck; + } + + /** + * Return the number of batched statements. + */ + public int size() { + return list.size(); + } + + /** + * Return the sql + */ + public String getSql() { + return sql; + } + + /** + * Return the statement. + */ + public PreparedStatement getStatement() { + return pstmt; + } + + /** + * Add the BatchPostExecute to the list for post execute processing. + */ + public void add(BatchPostExecute batchExecute){ + list.add(batchExecute); + } + + /** + * Execute the statement using executeBatch(). + * Run any post processing including getGeneratedKeys. + */ + public void executeBatch(boolean getGeneratedKeys) throws SQLException { + + executeAndCheckRowCounts(); + if (isGenKeys && getGeneratedKeys){ + getGeneratedKeys(); + } + postExecute(); + close(); + } + + /** + * Close the underlying statement. + */ + public void close() throws SQLException { + if (pstmt != null){ + pstmt.close(); + pstmt = null; + } + } + + private void postExecute() throws SQLException { + for (int i = 0; i < list.size(); i++) { + list.get(i).postExecute(); + } + } + + private void executeAndCheckRowCounts() throws SQLException { + + if (pstmtBatch != null){ + // oracle specific JDBC batch processing + int rc = pstmtBatch.executeBatch(pstmt, list.size(), sql, occCheck); + if (list.size() == 1){ + list.get(0).checkRowCount(rc); + } + // the optimistic concurrency row count check + // has already been done by pstmtBatch so just return + return; + + } + + // normal JDBC batch processing + int[] results = pstmt.executeBatch(); + + if (results.length != list.size()){ + String s = "results array error "+results.length+" "+list.size(); + throw new SQLException(s); + } + + // check for concurrency exceptions... + for (int i = 0; i < results.length; i++) { + list.get(i).checkRowCount(results[i]); + } + } + + private void getGeneratedKeys() throws SQLException { + + int index = 0; + ResultSet rset = pstmt.getGeneratedKeys(); + try { + while(rset.next()) { + Object idValue = rset.getObject(1); + list.get(index).setGeneratedKey(idValue); + index++; + } + } finally { + if (rset != null){ + rset.close(); + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedPstmtHolder.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedPstmtHolder.java index 6b2a6b6fb..cfa56f069 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedPstmtHolder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BatchedPstmtHolder.java @@ -1,165 +1,146 @@ -/** - * 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.persist; - -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; - -/** - * Used to hold BatchedPstmt objects for batch based execution. - *

- * The BatchControl 'front ends' the batching by queuing the persist requests - * and ordering them according to depth and type. This object should only batch - * statements of a single 'depth' at any given time. - *

- */ -public class BatchedPstmtHolder { - - private static final Logger logger = Logger.getLogger(BatchedPstmtHolder.class.getName()); - - /** - * A Map of the statements using a String key. This is used so that the same - * Statement,Prepared,Callable is reused. - */ - private LinkedHashMap stmtMap = new LinkedHashMap(); - - /** - * The Max size across all the BatchedPstmt. - */ - private int maxSize; - - public BatchedPstmtHolder() { - - } - - /** - * Return the PreparedStatement if it has already been used in this Batch. - * This will return null if no matching PreparedStatement is found. - */ - public PreparedStatement getStmt(String stmtKey, BatchPostExecute postExecute) { - BatchedPstmt bs = stmtMap.get(stmtKey); - if (bs == null) { - // the PreparedStatement has need been created - return null; - } - // add the post execute processing for this bean/row - bs.add(postExecute); - - // maintain a max batch size for any given batched stmt. - // Used to determine when to flush. - int bsSize = bs.size(); - if (bsSize > maxSize){ - maxSize = bsSize; - } - return bs.getStatement(); - } - - /** - * Add a new PreparedStatement wrapped in the BatchStatement object. - */ - public void addStmt(BatchedPstmt bs, BatchPostExecute postExecute) { - // add the batch post execute to the statement for POST processing - bs.add(postExecute); - - // cache so that getStmt() can find it for additional beans/rows - stmtMap.put(bs.getSql(), bs); - } - - /** - * Return true if the batch has no statements to execute. - */ - public boolean isEmpty() { - return stmtMap.isEmpty(); - } - - /** - * Execute all batched PreparedStatements. - * - * @param getGeneratedKeys - * if true try to get generated keys for inserts - */ - public void flush(boolean getGeneratedKeys) throws PersistenceException { - - SQLException firstError = null; - String errorSql = null; - - // flag set if something fails. Will not execute - // but still need to close PreparedStatements. - boolean isError = false; - - Iterator it = stmtMap.values().iterator(); - while (it.hasNext()) { - BatchedPstmt bs = it.next(); - try { - if (!isError) { - bs.executeBatch(getGeneratedKeys); - } - } catch (SQLException ex) { - SQLException next = ex.getNextException(); - while(next != null) { - logger.log(Level.SEVERE, "Next Exception during batch execution", next); - next = next.getNextException(); - } - - if (firstError == null) { - firstError = ex; - errorSql = bs.getSql(); - } else { - logger.log(Level.SEVERE, null, ex); - } - isError = true; - - } finally { - try { - bs.close(); - } catch (SQLException ex) { - // error closing PreparedStatement - logger.log(Level.SEVERE, null, ex); - } - } - } - - // clear the batch cache - stmtMap.clear(); - maxSize = 0; - - if (firstError != null) { - String msg = "Error when batch flush on sql: "+errorSql; - throw new PersistenceException(msg, firstError); - } - } - - /** - * Return the size of the biggest batched statement. - *

- * Used to determine when to flush the batch. - *

- */ - public int getMaxSize() { - return maxSize; - } - -} +package com.avaje.ebeaninternal.server.persist; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; + +/** + * Used to hold BatchedPstmt objects for batch based execution. + *

+ * The BatchControl 'front ends' the batching by queuing the persist requests + * and ordering them according to depth and type. This object should only batch + * statements of a single 'depth' at any given time. + *

+ */ +public class BatchedPstmtHolder { + + private static final Logger logger = Logger.getLogger(BatchedPstmtHolder.class.getName()); + + /** + * A Map of the statements using a String key. This is used so that the same + * Statement,Prepared,Callable is reused. + */ + private LinkedHashMap stmtMap = new LinkedHashMap(); + + /** + * The Max size across all the BatchedPstmt. + */ + private int maxSize; + + public BatchedPstmtHolder() { + + } + + /** + * Return the PreparedStatement if it has already been used in this Batch. + * This will return null if no matching PreparedStatement is found. + */ + public PreparedStatement getStmt(String stmtKey, BatchPostExecute postExecute) { + BatchedPstmt bs = stmtMap.get(stmtKey); + if (bs == null) { + // the PreparedStatement has need been created + return null; + } + // add the post execute processing for this bean/row + bs.add(postExecute); + + // maintain a max batch size for any given batched stmt. + // Used to determine when to flush. + int bsSize = bs.size(); + if (bsSize > maxSize){ + maxSize = bsSize; + } + return bs.getStatement(); + } + + /** + * Add a new PreparedStatement wrapped in the BatchStatement object. + */ + public void addStmt(BatchedPstmt bs, BatchPostExecute postExecute) { + // add the batch post execute to the statement for POST processing + bs.add(postExecute); + + // cache so that getStmt() can find it for additional beans/rows + stmtMap.put(bs.getSql(), bs); + } + + /** + * Return true if the batch has no statements to execute. + */ + public boolean isEmpty() { + return stmtMap.isEmpty(); + } + + /** + * Execute all batched PreparedStatements. + * + * @param getGeneratedKeys + * if true try to get generated keys for inserts + */ + public void flush(boolean getGeneratedKeys) throws PersistenceException { + + SQLException firstError = null; + String errorSql = null; + + // flag set if something fails. Will not execute + // but still need to close PreparedStatements. + boolean isError = false; + + Iterator it = stmtMap.values().iterator(); + while (it.hasNext()) { + BatchedPstmt bs = it.next(); + try { + if (!isError) { + bs.executeBatch(getGeneratedKeys); + } + } catch (SQLException ex) { + SQLException next = ex.getNextException(); + while(next != null) { + logger.log(Level.SEVERE, "Next Exception during batch execution", next); + next = next.getNextException(); + } + + if (firstError == null) { + firstError = ex; + errorSql = bs.getSql(); + } else { + logger.log(Level.SEVERE, null, ex); + } + isError = true; + + } finally { + try { + bs.close(); + } catch (SQLException ex) { + // error closing PreparedStatement + logger.log(Level.SEVERE, null, ex); + } + } + } + + // clear the batch cache + stmtMap.clear(); + maxSize = 0; + + if (firstError != null) { + String msg = "Error when batch flush on sql: "+errorSql; + throw new PersistenceException(msg, firstError); + } + } + + /** + * Return the size of the biggest batched statement. + *

+ * Used to determine when to flush the batch. + *

+ */ + public int getMaxSize() { + return maxSize; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BeanPersister.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BeanPersister.java index 70ca47857..3f12be0cc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/BeanPersister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BeanPersister.java @@ -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.persist; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; - -/** - * Defines bean insert update and delete implementation. - */ -public interface BeanPersister { - - /** - * execute the insert bean request. - */ - public void insert(PersistRequestBean request) throws PersistenceException; - - /** - * execute the update bean request. - */ - public void update(PersistRequestBean request) throws PersistenceException; - - /** - * execute the delete bean request. - */ - public void delete(PersistRequestBean request) throws PersistenceException; - -} +package com.avaje.ebeaninternal.server.persist; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.server.core.PersistRequestBean; + +/** + * Defines bean insert update and delete implementation. + */ +public interface BeanPersister { + + /** + * execute the insert bean request. + */ + public void insert(PersistRequestBean request) throws PersistenceException; + + /** + * execute the update bean request. + */ + public void update(PersistRequestBean request) throws PersistenceException; + + /** + * execute the delete bean request. + */ + public void delete(PersistRequestBean request) throws PersistenceException; + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BeanPersisterFactory.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BeanPersisterFactory.java index 3cfe8d371..2a7475057 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/BeanPersisterFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BeanPersisterFactory.java @@ -1,34 +1,15 @@ -/** - * 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.persist; - -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; - -/** - * Factory for creating BeanPersister implementations. - */ -public interface BeanPersisterFactory { - - /** - * Create the BeanPersister implemenation for a given type. - */ - public BeanPersister create(BeanDescriptor desc); - -} +package com.avaje.ebeaninternal.server.persist; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Factory for creating BeanPersister implementations. + */ +public interface BeanPersisterFactory { + + /** + * Create the BeanPersister implemenation for a given type. + */ + public BeanPersister create(BeanDescriptor desc); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/BindValues.java b/src/main/java/com/avaje/ebeaninternal/server/persist/BindValues.java index ea733a9a8..bfe3f11c1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/BindValues.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/BindValues.java @@ -1,135 +1,116 @@ -/** - * 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.persist; - -import java.util.ArrayList; - -/** - * Holds a list of bind values for binding to a PreparedStatement. - */ -public class BindValues { - - int commentCount; - - final ArrayList list = new ArrayList(); - - /** - * Create with a Binder. - */ - public BindValues(){ - } - - /** - * Return the number of bind values. - */ - public int size() { - return list.size() - commentCount; - } - - /** - * Add a bind value with its JDBC datatype. - * - * @param value the bind value - * @param dbType the type as per java.sql.Types - */ - public void add(Object value, int dbType, String name){ - list.add(new Value(value, dbType, name)); - } - - public void addComment(String comment){ - ++commentCount; - list.add(new Value(comment)); - } - - /** - * List of bind values. - */ - public ArrayList values() { - return list; - } - - /** - * A Value has additionally the JDBC data type. - */ - public static class Value { - - private final Object value; - - private final int dbType; - - private final String name; - - private final boolean isComment; - - /** - * Create a comment. This is so that comments can be put into - * the bind log. - */ - public Value(String comment) { - this.name = comment; - this.isComment = true; - value = null; - dbType = 0; - } - - - /** - * Create the value. - */ - public Value(Object value, int dbType, String name) { - this.isComment = false; - this.value = value; - this.dbType = dbType; - this.name = name; - } - - /** - * This is a comment for the bind log and NOT an actual bind value. - */ - public boolean isComment() { - return isComment; - } - - /** - * Return the type as per java.sql.Types. - */ - public int getDbType() { - return dbType; - } - - /** - * Return the value. - */ - public Object getValue() { - return value; - } - - /** - * Return the property name. - */ - public String getName() { - return name; - } - - public String toString(){ - return ""+value; - } - } -} +package com.avaje.ebeaninternal.server.persist; + +import java.util.ArrayList; + +/** + * Holds a list of bind values for binding to a PreparedStatement. + */ +public class BindValues { + + int commentCount; + + final ArrayList list = new ArrayList(); + + /** + * Create with a Binder. + */ + public BindValues(){ + } + + /** + * Return the number of bind values. + */ + public int size() { + return list.size() - commentCount; + } + + /** + * Add a bind value with its JDBC datatype. + * + * @param value the bind value + * @param dbType the type as per java.sql.Types + */ + public void add(Object value, int dbType, String name){ + list.add(new Value(value, dbType, name)); + } + + public void addComment(String comment){ + ++commentCount; + list.add(new Value(comment)); + } + + /** + * List of bind values. + */ + public ArrayList values() { + return list; + } + + /** + * A Value has additionally the JDBC data type. + */ + public static class Value { + + private final Object value; + + private final int dbType; + + private final String name; + + private final boolean isComment; + + /** + * Create a comment. This is so that comments can be put into + * the bind log. + */ + public Value(String comment) { + this.name = comment; + this.isComment = true; + value = null; + dbType = 0; + } + + + /** + * Create the value. + */ + public Value(Object value, int dbType, String name) { + this.isComment = false; + this.value = value; + this.dbType = dbType; + this.name = name; + } + + /** + * This is a comment for the bind log and NOT an actual bind value. + */ + public boolean isComment() { + return isComment; + } + + /** + * Return the type as per java.sql.Types. + */ + public int getDbType() { + return dbType; + } + + /** + * Return the value. + */ + public Object getValue() { + return value; + } + + /** + * Return the property name. + */ + public String getName() { + return name; + } + + public String toString(){ + return ""+value; + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/Binder.java b/src/main/java/com/avaje/ebeaninternal/server/persist/Binder.java index 91b2dc2aa..9893b6ab9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/Binder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/Binder.java @@ -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.persist; - -import java.math.BigDecimal; -import java.sql.CallableStatement; -import java.sql.SQLException; -import java.sql.Types; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.api.BindParams; -import com.avaje.ebeaninternal.server.core.Message; -import com.avaje.ebeaninternal.server.type.DataBind; -import com.avaje.ebeaninternal.server.type.ScalarType; -import com.avaje.ebeaninternal.server.type.TypeManager; - -/** - * Binds bean values to a PreparedStatement. - */ -public class Binder { - - private static final Logger logger = Logger.getLogger(Binder.class.getName()); - - //private final Calendar calendar; - - private final TypeManager typeManager; - - /** - * Set the PreparedStatement with which to bind variables to. - */ - public Binder(TypeManager typeManager) { - - this.typeManager = typeManager; - //this.calendar = new GregorianCalendar(); - } - - /** - * Bind the values to the Prepared Statement. - */ - public void bind(BindValues bindValues, DataBind dataBind, StringBuilder bindBuf) - throws SQLException { - - String logPrefix = ""; - - ArrayList list = bindValues.values(); - for (int i = 0; i < list.size(); i++) { - BindValues.Value bindValue = (BindValues.Value) list.get(i); - if (bindValue.isComment()) { - if (bindBuf != null) { - bindBuf.append(bindValue.getName()); - if (logPrefix.equals("")) { - logPrefix = ", "; - } - } - } else { - Object val = bindValue.getValue(); - int dt = bindValue.getDbType(); - bindObject(dataBind, val, dt); - - if (bindBuf != null) { - bindBuf.append(logPrefix); - if (logPrefix.equals("")) { - logPrefix = ", "; - } - bindBuf.append(bindValue.getName()); - bindBuf.append("="); - if (isLob(dt)) { - bindBuf.append("[LOB]"); - } else { - bindBuf.append(String.valueOf(val)); - } - } - } - } - } - - /** - * Bind the list of positionedParameters in BindParams. - */ - public String bind(BindParams bindParams, DataBind dataBind) - throws SQLException { - - StringBuilder bindLog = new StringBuilder(); - bind(bindParams, dataBind, bindLog); - return bindLog.toString(); - } - - /** - * Bind the list of positionedParameters in BindParams. - */ - public void bind(BindParams bindParams, DataBind dataBind, StringBuilder bindLog) - throws SQLException { - - bind(bindParams.positionedParameters(), dataBind, bindLog); - } - - /** - * Bind the list of parameters.. - */ - public void bind(List list, DataBind dataBind, StringBuilder bindLog) - throws SQLException { - - CallableStatement cstmt = null; - - if (dataBind.getPstmt() instanceof CallableStatement) { - cstmt = (CallableStatement) dataBind.getPstmt(); - } - - // the iterator is assumed to be in the correct order - Object value = null; - try { - for (int i = 0; i < list.size(); i++) { - - BindParams.Param param = list.get(i); - - if (param.isOutParam() && cstmt != null){ - cstmt.registerOutParameter(dataBind.nextPos(), param.getType()); - if (param.isInParam()) { - dataBind.decrementPos(); - } - } - if (param.isInParam()) { - value = param.getInValue(); - if (bindLog != null) { - if (param.isEncryptionKey()){ - bindLog.append("****"); - } else { - bindLog.append(value); - } - bindLog.append(", "); - } - if (value == null) { - // this doesn't work for query predicates - bindObject(dataBind, null, param.getType()); - } else { - bindObject(dataBind, value); - } - } - } - - } catch (SQLException ex) { - logger.warning(Message.msg("fetch.bind.error", "" + (dataBind.currentPos() - 1), value)); - throw ex; - } - } - - /** - * Bind an Object with unknown data type. - */ - public void bindObject(DataBind dataBind, Object value) throws SQLException { - - if (value == null) { - // null of unknown type - bindObject(dataBind, null, Types.OTHER); - - } else { - - ScalarType type = typeManager.getScalarType(value.getClass()); - if (type == null){ - // the type is not registered with the TypeManager. - String msg = "No ScalarType registered for "+value.getClass(); - throw new PersistenceException(msg); - - } else if (!type.isJdbcNative()) { - // convert to a JDBC native type - value = type.toJdbcType(value); - } - - int dbType = type.getJdbcType(); - bindObject(dataBind, value, dbType); - } - } - - /** - * bind a single value. - *

- * Note that java.math.BigInteger is supported by converting it to a Long. - *

- *

- * Note if we get a java.util.Date or java.util.Calendar then these have - * been anonymously passed in (UpdateSql etc). There is a global setting to - * convert then to a java.sql.Date or java.sql.Timestamp for binding. The - * default is that both are converted to java.sql.Timestamp. - *

- */ - public void bindObject(DataBind dataBind, Object data, int dbType) - throws SQLException { - - if (data == null){ - dataBind.setNull(dbType); - return; - } - - switch (dbType) { - case java.sql.Types.LONGVARCHAR: - bindLongVarChar(dataBind, data); - break; - - case java.sql.Types.LONGVARBINARY: - bindLongVarBinary(dataBind, data); - break; - - case java.sql.Types.CLOB: - bindClob(dataBind, data); - break; - - case java.sql.Types.BLOB: - bindBlob(dataBind, data); - break; - - default: - - bindSimpleData(dataBind, dbType, data); - } - } - - /** - * Binds the value to the statement according to the data type. - */ - private void bindSimpleData(DataBind b, int dataType, Object data) - throws SQLException { - - try { - switch (dataType) { - case java.sql.Types.BOOLEAN: - Boolean bo = (Boolean) data; - b.setBoolean(bo.booleanValue()); - break; - case java.sql.Types.BIT: - // Types.BIT should map to Java Boolean - Boolean bitBool = (Boolean) data; - b.setBoolean(bitBool.booleanValue()); - break; - - case java.sql.Types.VARCHAR: - b.setString((String) data); - break; - - case java.sql.Types.CHAR: - b.setString(data.toString()); - break; - - case java.sql.Types.TINYINT: - b.setByte(((Byte) data).byteValue()); - break; - - case java.sql.Types.SMALLINT: - b.setShort(((Short) data).shortValue()); - break; - - case java.sql.Types.INTEGER: - b.setInt(((Integer) data).intValue()); - break; - - case java.sql.Types.BIGINT: - b.setLong(((Long) data).longValue()); - break; - - case java.sql.Types.REAL: - b.setFloat(((Float) data).floatValue()); - break; - - case java.sql.Types.FLOAT: - // DB Float in theory maps to Java Double type - b.setDouble(((Double) data).doubleValue()); - break; - - case java.sql.Types.DOUBLE: - b.setDouble(((Double) data).doubleValue()); - break; - - case java.sql.Types.NUMERIC: - b.setBigDecimal((BigDecimal) data); - break; - - case java.sql.Types.DECIMAL: - b.setBigDecimal((BigDecimal) data); - break; - - case java.sql.Types.TIME: - //pstmt.setTime(index, (java.sql.Time) data, calendar); - b.setTime((java.sql.Time) data); - break; - - case java.sql.Types.DATE: - //pstmt.setDate(index, (java.sql.Date) data, calendar); - b.setDate((java.sql.Date) data); - break; - - case java.sql.Types.TIMESTAMP: - //pstmt.setTimestamp(index, (java.sql.Timestamp) data, calendar); - b.setTimestamp((java.sql.Timestamp) data); - break; - - case java.sql.Types.BINARY: - b.setBytes((byte[]) data); - break; - - case java.sql.Types.VARBINARY: - b.setBytes((byte[]) data); - break; - - case java.sql.Types.OTHER: - b.setObject(data); - break; - - case java.sql.Types.JAVA_OBJECT: - // Not too sure about this. - b.setObject(data); - break; - - default: - String msg = Message.msg("persist.bind.datatype", "" + dataType, "" + b.currentPos()); - throw new SQLException(msg); - } - - } catch (Exception e) { - String dataClass = "Data is null?"; - if (data != null) { - dataClass = data.getClass().getName(); - } - String m = "Error with property[" + b.currentPos() + "] dt[" + dataType + "]"; - m += "data[" + data + "][" + dataClass + "]"; - throw new PersistenceException(m, e); - } - } - - /** - * Bind String data to a LONGVARCHAR column. - */ - private void bindLongVarChar(DataBind b, Object data) - throws SQLException { - - String sd = (String) data; - b.setClob(sd); - } - - /** - * Bind byte[] data to a LONGVARBINARY column. - */ - private void bindLongVarBinary(DataBind b, Object data) - throws SQLException { - - byte[] bytes = (byte[]) data; - b.setBlob(bytes); - } - - /** - * Bind String data to a CLOB column. - */ - private void bindClob(DataBind b, Object data) throws SQLException { - - String sd = (String) data; - b.setClob(sd); - } - - /** - * Bind byte[] data to a BLOB column. - */ - private void bindBlob(DataBind b, Object data) throws SQLException { - - byte[] bytes = (byte[]) data; - b.setBlob(bytes); - } - - private boolean isLob(int dbType) { - switch (dbType) { - case Types.CLOB: - return true; - case Types.LONGVARCHAR: - return true; - case Types.BLOB: - return true; - case Types.LONGVARBINARY: - return true; - - default: - return false; - } - } -} +package com.avaje.ebeaninternal.server.persist; + +import java.math.BigDecimal; +import java.sql.CallableStatement; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.api.BindParams; +import com.avaje.ebeaninternal.server.core.Message; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.type.ScalarType; +import com.avaje.ebeaninternal.server.type.TypeManager; + +/** + * Binds bean values to a PreparedStatement. + */ +public class Binder { + + private static final Logger logger = Logger.getLogger(Binder.class.getName()); + + //private final Calendar calendar; + + private final TypeManager typeManager; + + /** + * Set the PreparedStatement with which to bind variables to. + */ + public Binder(TypeManager typeManager) { + + this.typeManager = typeManager; + //this.calendar = new GregorianCalendar(); + } + + /** + * Bind the values to the Prepared Statement. + */ + public void bind(BindValues bindValues, DataBind dataBind, StringBuilder bindBuf) + throws SQLException { + + String logPrefix = ""; + + ArrayList list = bindValues.values(); + for (int i = 0; i < list.size(); i++) { + BindValues.Value bindValue = (BindValues.Value) list.get(i); + if (bindValue.isComment()) { + if (bindBuf != null) { + bindBuf.append(bindValue.getName()); + if (logPrefix.equals("")) { + logPrefix = ", "; + } + } + } else { + Object val = bindValue.getValue(); + int dt = bindValue.getDbType(); + bindObject(dataBind, val, dt); + + if (bindBuf != null) { + bindBuf.append(logPrefix); + if (logPrefix.equals("")) { + logPrefix = ", "; + } + bindBuf.append(bindValue.getName()); + bindBuf.append("="); + if (isLob(dt)) { + bindBuf.append("[LOB]"); + } else { + bindBuf.append(String.valueOf(val)); + } + } + } + } + } + + /** + * Bind the list of positionedParameters in BindParams. + */ + public String bind(BindParams bindParams, DataBind dataBind) + throws SQLException { + + StringBuilder bindLog = new StringBuilder(); + bind(bindParams, dataBind, bindLog); + return bindLog.toString(); + } + + /** + * Bind the list of positionedParameters in BindParams. + */ + public void bind(BindParams bindParams, DataBind dataBind, StringBuilder bindLog) + throws SQLException { + + bind(bindParams.positionedParameters(), dataBind, bindLog); + } + + /** + * Bind the list of parameters.. + */ + public void bind(List list, DataBind dataBind, StringBuilder bindLog) + throws SQLException { + + CallableStatement cstmt = null; + + if (dataBind.getPstmt() instanceof CallableStatement) { + cstmt = (CallableStatement) dataBind.getPstmt(); + } + + // the iterator is assumed to be in the correct order + Object value = null; + try { + for (int i = 0; i < list.size(); i++) { + + BindParams.Param param = list.get(i); + + if (param.isOutParam() && cstmt != null){ + cstmt.registerOutParameter(dataBind.nextPos(), param.getType()); + if (param.isInParam()) { + dataBind.decrementPos(); + } + } + if (param.isInParam()) { + value = param.getInValue(); + if (bindLog != null) { + if (param.isEncryptionKey()){ + bindLog.append("****"); + } else { + bindLog.append(value); + } + bindLog.append(", "); + } + if (value == null) { + // this doesn't work for query predicates + bindObject(dataBind, null, param.getType()); + } else { + bindObject(dataBind, value); + } + } + } + + } catch (SQLException ex) { + logger.warning(Message.msg("fetch.bind.error", "" + (dataBind.currentPos() - 1), value)); + throw ex; + } + } + + /** + * Bind an Object with unknown data type. + */ + public void bindObject(DataBind dataBind, Object value) throws SQLException { + + if (value == null) { + // null of unknown type + bindObject(dataBind, null, Types.OTHER); + + } else { + + ScalarType type = typeManager.getScalarType(value.getClass()); + if (type == null){ + // the type is not registered with the TypeManager. + String msg = "No ScalarType registered for "+value.getClass(); + throw new PersistenceException(msg); + + } else if (!type.isJdbcNative()) { + // convert to a JDBC native type + value = type.toJdbcType(value); + } + + int dbType = type.getJdbcType(); + bindObject(dataBind, value, dbType); + } + } + + /** + * bind a single value. + *

+ * Note that java.math.BigInteger is supported by converting it to a Long. + *

+ *

+ * Note if we get a java.util.Date or java.util.Calendar then these have + * been anonymously passed in (UpdateSql etc). There is a global setting to + * convert then to a java.sql.Date or java.sql.Timestamp for binding. The + * default is that both are converted to java.sql.Timestamp. + *

+ */ + public void bindObject(DataBind dataBind, Object data, int dbType) + throws SQLException { + + if (data == null){ + dataBind.setNull(dbType); + return; + } + + switch (dbType) { + case java.sql.Types.LONGVARCHAR: + bindLongVarChar(dataBind, data); + break; + + case java.sql.Types.LONGVARBINARY: + bindLongVarBinary(dataBind, data); + break; + + case java.sql.Types.CLOB: + bindClob(dataBind, data); + break; + + case java.sql.Types.BLOB: + bindBlob(dataBind, data); + break; + + default: + + bindSimpleData(dataBind, dbType, data); + } + } + + /** + * Binds the value to the statement according to the data type. + */ + private void bindSimpleData(DataBind b, int dataType, Object data) + throws SQLException { + + try { + switch (dataType) { + case java.sql.Types.BOOLEAN: + Boolean bo = (Boolean) data; + b.setBoolean(bo.booleanValue()); + break; + case java.sql.Types.BIT: + // Types.BIT should map to Java Boolean + Boolean bitBool = (Boolean) data; + b.setBoolean(bitBool.booleanValue()); + break; + + case java.sql.Types.VARCHAR: + b.setString((String) data); + break; + + case java.sql.Types.CHAR: + b.setString(data.toString()); + break; + + case java.sql.Types.TINYINT: + b.setByte(((Byte) data).byteValue()); + break; + + case java.sql.Types.SMALLINT: + b.setShort(((Short) data).shortValue()); + break; + + case java.sql.Types.INTEGER: + b.setInt(((Integer) data).intValue()); + break; + + case java.sql.Types.BIGINT: + b.setLong(((Long) data).longValue()); + break; + + case java.sql.Types.REAL: + b.setFloat(((Float) data).floatValue()); + break; + + case java.sql.Types.FLOAT: + // DB Float in theory maps to Java Double type + b.setDouble(((Double) data).doubleValue()); + break; + + case java.sql.Types.DOUBLE: + b.setDouble(((Double) data).doubleValue()); + break; + + case java.sql.Types.NUMERIC: + b.setBigDecimal((BigDecimal) data); + break; + + case java.sql.Types.DECIMAL: + b.setBigDecimal((BigDecimal) data); + break; + + case java.sql.Types.TIME: + //pstmt.setTime(index, (java.sql.Time) data, calendar); + b.setTime((java.sql.Time) data); + break; + + case java.sql.Types.DATE: + //pstmt.setDate(index, (java.sql.Date) data, calendar); + b.setDate((java.sql.Date) data); + break; + + case java.sql.Types.TIMESTAMP: + //pstmt.setTimestamp(index, (java.sql.Timestamp) data, calendar); + b.setTimestamp((java.sql.Timestamp) data); + break; + + case java.sql.Types.BINARY: + b.setBytes((byte[]) data); + break; + + case java.sql.Types.VARBINARY: + b.setBytes((byte[]) data); + break; + + case java.sql.Types.OTHER: + b.setObject(data); + break; + + case java.sql.Types.JAVA_OBJECT: + // Not too sure about this. + b.setObject(data); + break; + + default: + String msg = Message.msg("persist.bind.datatype", "" + dataType, "" + b.currentPos()); + throw new SQLException(msg); + } + + } catch (Exception e) { + String dataClass = "Data is null?"; + if (data != null) { + dataClass = data.getClass().getName(); + } + String m = "Error with property[" + b.currentPos() + "] dt[" + dataType + "]"; + m += "data[" + data + "][" + dataClass + "]"; + throw new PersistenceException(m, e); + } + } + + /** + * Bind String data to a LONGVARCHAR column. + */ + private void bindLongVarChar(DataBind b, Object data) + throws SQLException { + + String sd = (String) data; + b.setClob(sd); + } + + /** + * Bind byte[] data to a LONGVARBINARY column. + */ + private void bindLongVarBinary(DataBind b, Object data) + throws SQLException { + + byte[] bytes = (byte[]) data; + b.setBlob(bytes); + } + + /** + * Bind String data to a CLOB column. + */ + private void bindClob(DataBind b, Object data) throws SQLException { + + String sd = (String) data; + b.setClob(sd); + } + + /** + * Bind byte[] data to a BLOB column. + */ + private void bindBlob(DataBind b, Object data) throws SQLException { + + byte[] bytes = (byte[]) data; + b.setBlob(bytes); + } + + private boolean isLob(int dbType) { + switch (dbType) { + case Types.CLOB: + return true; + case Types.LONGVARCHAR: + return true; + case Types.BLOB: + return true; + case Types.LONGVARBINARY: + return true; + + default: + return false; + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/Constant.java b/src/main/java/com/avaje/ebeaninternal/server/persist/Constant.java index f6a2fd21c..731ebc1f1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/Constant.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/Constant.java @@ -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.persist; - -/** - * Contants used in persist. - */ -public interface Constant { - - /** - * An INSERT clause. - */ - public static final int IN_INSERT = 1; - - /** - * An UPDATE SET clause. - */ - public static final int IN_UPDATE_SET = 2; - - /** - * An UPDATE WHERE clause. - */ - public static final int IN_UPDATE_WHERE = 3; - - /** - * A DELETE WHERE clause. - */ - public static final int IN_DELETE_WHERE = 4; -} +package com.avaje.ebeaninternal.server.persist; + +/** + * Contants used in persist. + */ +public interface Constant { + + /** + * An INSERT clause. + */ + public static final int IN_INSERT = 1; + + /** + * An UPDATE SET clause. + */ + public static final int IN_UPDATE_SET = 2; + + /** + * An UPDATE WHERE clause. + */ + public static final int IN_UPDATE_WHERE = 3; + + /** + * A DELETE WHERE clause. + */ + public static final int IN_DELETE_WHERE = 4; +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersistExecute.java b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersistExecute.java index 760228e77..115de6e91 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersistExecute.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersistExecute.java @@ -1,154 +1,135 @@ -/** - * 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.persist; - -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebean.event.BeanPersistController; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql; -import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate; -import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql; -import com.avaje.ebeaninternal.server.core.PstmtBatch; -import com.avaje.ebeaninternal.server.deploy.BeanManager; - -/** - * Default PersistExecute implementation using DML statements. - *

- * Supports the use of PreparedStatement batching. - *

- */ -public final class DefaultPersistExecute implements PersistExecute { - - private final ExeCallableSql exeCallableSql; - - private final ExeUpdateSql exeUpdateSql; - - private final ExeOrmUpdate exeOrmUpdate; - - /** - * The default batch size. - */ - private final int defaultBatchSize; - - /** - * Default for whether to call getGeneratedKeys after batch insert. - */ - private final boolean defaultBatchGenKeys; - - private final boolean validate; - - /** - * Construct this DmlPersistExecute. - */ - public DefaultPersistExecute(boolean validate, Binder binder, PstmtBatch pstmtBatch) { - - this.validate = validate; - this.exeOrmUpdate = new ExeOrmUpdate(binder, pstmtBatch); - this.exeUpdateSql = new ExeUpdateSql(binder, pstmtBatch); - this.exeCallableSql = new ExeCallableSql(binder, pstmtBatch); - - this.defaultBatchGenKeys = GlobalProperties.getBoolean("batch.getgeneratedkeys", true); - this.defaultBatchSize = GlobalProperties.getInt("batch.size", 20); - } - - public BatchControl createBatchControl(SpiTransaction t) { - - // create a BatchControl and set its defaults - return new BatchControl(t, defaultBatchSize, defaultBatchGenKeys); - } - - /** - * execute the bean insert request. - */ - public void executeInsertBean(PersistRequestBean request) { - - BeanManager mgr = request.getBeanManager(); - BeanPersister persister = mgr.getBeanPersister(); - - BeanPersistController controller = request.getBeanController(); - if (controller == null || controller.preInsert(request)) { - if (validate){ - request.validate(); - } - persister.insert(request); - // NOTE: the persister fires the postInsert so that this - // occurs before ebeanIntercept.setLoaded(true) - } - } - - /** - * execute the bean update request. - */ - public void executeUpdateBean(PersistRequestBean request) { - - BeanManager mgr = request.getBeanManager(); - BeanPersister persister = mgr.getBeanPersister(); - - BeanPersistController controller = request.getBeanController(); - if (controller == null || controller.preUpdate(request)) { - if (validate){ - request.validate(); - } - persister.update(request); - // NOTE: the persister fires the postUpdate so that this - // occurs before ebeanIntercept.setLoaded(true) - } - } - - - /** - * execute the bean delete request. - */ - public void executeDeleteBean(PersistRequestBean request) { - - BeanManager mgr = request.getBeanManager(); - BeanPersister persister = mgr.getBeanPersister(); - - BeanPersistController controller = request.getBeanController(); - if (controller == null || controller.preDelete(request)) { - - persister.delete(request); - // NOTE: the persister fires the postDelete - } - } - - /** - * Execute the updateSqlRequest - */ - public int executeOrmUpdate(PersistRequestOrmUpdate request) { - return exeOrmUpdate.execute(request); - } - - /** - * Execute the updateSqlRequest - */ - public int executeSqlUpdate(PersistRequestUpdateSql request) { - return exeUpdateSql.execute(request); - } - - /** - * Execute the CallableSqlRequest. - */ - public int executeSqlCallable(PersistRequestCallableSql request) { - return exeCallableSql.execute(request); - } - -} +package com.avaje.ebeaninternal.server.persist; + +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebean.event.BeanPersistController; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql; +import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate; +import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql; +import com.avaje.ebeaninternal.server.core.PstmtBatch; +import com.avaje.ebeaninternal.server.deploy.BeanManager; + +/** + * Default PersistExecute implementation using DML statements. + *

+ * Supports the use of PreparedStatement batching. + *

+ */ +public final class DefaultPersistExecute implements PersistExecute { + + private final ExeCallableSql exeCallableSql; + + private final ExeUpdateSql exeUpdateSql; + + private final ExeOrmUpdate exeOrmUpdate; + + /** + * The default batch size. + */ + private final int defaultBatchSize; + + /** + * Default for whether to call getGeneratedKeys after batch insert. + */ + private final boolean defaultBatchGenKeys; + + private final boolean validate; + + /** + * Construct this DmlPersistExecute. + */ + public DefaultPersistExecute(boolean validate, Binder binder, PstmtBatch pstmtBatch) { + + this.validate = validate; + this.exeOrmUpdate = new ExeOrmUpdate(binder, pstmtBatch); + this.exeUpdateSql = new ExeUpdateSql(binder, pstmtBatch); + this.exeCallableSql = new ExeCallableSql(binder, pstmtBatch); + + this.defaultBatchGenKeys = GlobalProperties.getBoolean("batch.getgeneratedkeys", true); + this.defaultBatchSize = GlobalProperties.getInt("batch.size", 20); + } + + public BatchControl createBatchControl(SpiTransaction t) { + + // create a BatchControl and set its defaults + return new BatchControl(t, defaultBatchSize, defaultBatchGenKeys); + } + + /** + * execute the bean insert request. + */ + public void executeInsertBean(PersistRequestBean request) { + + BeanManager mgr = request.getBeanManager(); + BeanPersister persister = mgr.getBeanPersister(); + + BeanPersistController controller = request.getBeanController(); + if (controller == null || controller.preInsert(request)) { + if (validate){ + request.validate(); + } + persister.insert(request); + // NOTE: the persister fires the postInsert so that this + // occurs before ebeanIntercept.setLoaded(true) + } + } + + /** + * execute the bean update request. + */ + public void executeUpdateBean(PersistRequestBean request) { + + BeanManager mgr = request.getBeanManager(); + BeanPersister persister = mgr.getBeanPersister(); + + BeanPersistController controller = request.getBeanController(); + if (controller == null || controller.preUpdate(request)) { + if (validate){ + request.validate(); + } + persister.update(request); + // NOTE: the persister fires the postUpdate so that this + // occurs before ebeanIntercept.setLoaded(true) + } + } + + + /** + * execute the bean delete request. + */ + public void executeDeleteBean(PersistRequestBean request) { + + BeanManager mgr = request.getBeanManager(); + BeanPersister persister = mgr.getBeanPersister(); + + BeanPersistController controller = request.getBeanController(); + if (controller == null || controller.preDelete(request)) { + + persister.delete(request); + // NOTE: the persister fires the postDelete + } + } + + /** + * Execute the updateSqlRequest + */ + public int executeOrmUpdate(PersistRequestOrmUpdate request) { + return exeOrmUpdate.execute(request); + } + + /** + * Execute the updateSqlRequest + */ + public int executeSqlUpdate(PersistRequestUpdateSql request) { + return exeUpdateSql.execute(request); + } + + /** + * Execute the CallableSqlRequest. + */ + public int executeSqlCallable(PersistRequestCallableSql request) { + return exeCallableSql.execute(request); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java index 73394d5db..a8c29748c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java @@ -1,1390 +1,1371 @@ -/** - * 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.persist; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.CallableSql; -import com.avaje.ebean.Query; -import com.avaje.ebean.SqlUpdate; -import com.avaje.ebean.Transaction; -import com.avaje.ebean.Update; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.config.ldap.LdapContextFactory; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.api.SpiUpdate; -import com.avaje.ebeaninternal.server.core.ConcurrencyMode; -import com.avaje.ebeaninternal.server.core.Message; -import com.avaje.ebeaninternal.server.core.PersistRequest; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql; -import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate; -import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql; -import com.avaje.ebeaninternal.server.core.Persister; -import com.avaje.ebeaninternal.server.core.PstmtBatch; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; -import com.avaje.ebeaninternal.server.deploy.BeanManager; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.deploy.IntersectionRow; -import com.avaje.ebeaninternal.server.deploy.ManyType; -import com.avaje.ebeaninternal.server.ldap.DefaultLdapPersister; -import com.avaje.ebeaninternal.server.ldap.LdapPersistBeanRequest; - -/** - * Persister implementation using DML. - *

- * This object uses DmlPersistExecute to perform the actual persist execution. - *

- *

- * This object: - *

    - *
  • Determines insert or update for saved beans
  • - *
  • Determines the concurrency mode
  • - *
  • Handles cascading of save and delete
  • - *
  • Handles the batching and queueing
  • - *

    - * - * @see com.avaje.ebeaninternal.server.persist.DefaultPersistExecute - */ -public final class DefaultPersister implements Persister { - - private static final Logger logger = Logger.getLogger(DefaultPersister.class.getName()); - - /** - * Actually does the persisting work. - */ - private final PersistExecute persistExecute; - - private final DefaultLdapPersister ldapPersister; - - private final SpiEbeanServer server; - - private final BeanDescriptorManager beanDescriptorManager; - - private final boolean defaultUpdateNullProperties; - private final boolean defaultDeleteMissingChildren; - - public DefaultPersister(SpiEbeanServer server, boolean validate, - Binder binder, BeanDescriptorManager descMgr, PstmtBatch pstmtBatch, LdapContextFactory contextFactory) { - - this.server = server; - this.beanDescriptorManager = descMgr; - - this.persistExecute = new DefaultPersistExecute(validate, binder, pstmtBatch); - this.ldapPersister = new DefaultLdapPersister(contextFactory); - - this.defaultUpdateNullProperties = server.isDefaultUpdateNullProperties(); - this.defaultDeleteMissingChildren = server.isDefaultDeleteMissingChildren(); - } - - /** - * Execute the CallableSql. - */ - public int executeCallable(CallableSql callSql, Transaction t) { - - PersistRequestCallableSql request = new PersistRequestCallableSql(server, callSql, (SpiTransaction) t, persistExecute); - try { - request.initTransIfRequired(); - int rc = request.executeOrQueue(); - request.commitTransIfRequired(); - return rc; - - } catch (RuntimeException e) { - request.rollbackTransIfRequired(); - throw e; - } - } - - /** - * Execute the orm update. - */ - public int executeOrmUpdate(Update update, Transaction t) { - - SpiUpdate ormUpdate = (SpiUpdate) update; - - BeanManager mgr = beanDescriptorManager.getBeanManager(ormUpdate.getBeanType()); - - if (mgr == null) { - String msg = "No BeanManager found for type [" + ormUpdate.getBeanType() + "]. Is it an entity?"; - throw new PersistenceException(msg); - } - - PersistRequestOrmUpdate request = new PersistRequestOrmUpdate(server, mgr, ormUpdate, (SpiTransaction) t, persistExecute); - try { - request.initTransIfRequired(); - int rc = request.executeOrQueue(); - request.commitTransIfRequired(); - return rc; - - } catch (RuntimeException e) { - request.rollbackTransIfRequired(); - throw e; - } - } - - /** - * Execute the updateSql. - */ - public int executeSqlUpdate(SqlUpdate updSql, Transaction t) { - - PersistRequestUpdateSql request = new PersistRequestUpdateSql(server, updSql, (SpiTransaction) t, persistExecute); - try { - request.initTransIfRequired(); - int rc = request.executeOrQueue(); - request.commitTransIfRequired(); - return rc; - - } catch (RuntimeException e) { - request.rollbackTransIfRequired(); - throw e; - } - } - - /** - * Recursively delete the bean. This calls back to the EbeanServer. - */ - private void deleteRecurse(Object detailBean, Transaction t) { - // NB: a new PersistRequest is made - server.delete(detailBean, t); - } - - /** - * Force an Update using the given bean. - */ - public void forceUpdate(Object bean, Set updateProps, Transaction t, boolean deleteMissingChildren, boolean updateNullProperties) { - - if (bean == null) { - throw new NullPointerException(Message.msg("bean.isnull")); - } - - if (updateProps == null) { - // checking to see if this is just a 'normal' update - if (bean instanceof EntityBean) { - EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept(); - if (ebi.isDirty() || ebi.isLoaded()) { - // a 'normal' update using 'dirty' properties from internal bean state. - // if not dirty we still update in case any cascading save occurs - PersistRequestBean req = createRequest(bean, t, null); - try { - req.initTransIfRequired(); - update(req); - req.commitTransIfRequired(); - // finished a 'normal' update - return; - - } catch (RuntimeException ex) { - req.rollbackTransIfRequired(); - throw ex; - } - } else if (ebi.isReference()) { - // just return as no point in cascading (no modified beans/lists) - return; - } - - // loadedProps set by Ebean JSON / XML Marshalling - updateProps = ebi.getLoadedProps(); - } - } - - BeanManager mgr = getBeanManager(bean); - if (mgr == null) { - throw new PersistenceException(errNotRegistered(bean.getClass())); - } - - forceUpdateStateless(bean, t, null, mgr, updateProps, deleteMissingChildren, updateNullProperties); - } - - /** - * Force a 'stateless' update determining which properties to update. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - private void forceUpdateStateless(Object bean, Transaction t, Object parentBean, BeanManager mgr, Set updateProps, - boolean deleteMissingChildren, boolean updateNullProperties) { - - BeanDescriptor descriptor = mgr.getBeanDescriptor(); - - // determine concurrency mode based on version property not null - ConcurrencyMode mode = descriptor.determineConcurrencyMode(bean); - - if (updateProps == null) { - // determine based on null treatment (all properties updated or just the non-null ones) - updateProps = updateNullProperties ? null : descriptor.determineLoadedProperties(bean); - - } else if (updateProps.isEmpty()) { - // in this case means we want to include all properties in the update - updateProps = null; - - } else if (ConcurrencyMode.VERSION.equals(mode)) { - // check that the version property is included - String verName = descriptor.firstVersionProperty().getName(); - if (!updateProps.contains(verName)) { - // defensively copy the updateProps and add the version property name - updateProps = new HashSet(updateProps); - updateProps.add(verName); - } - } - - PersistRequestBean req; - if (descriptor.isLdapEntityType()) { - req = new LdapPersistBeanRequest(server, bean, parentBean, mgr, ldapPersister, updateProps, mode); - - } else { - // special constructor for force 'stateless' Update mode ... - req = new PersistRequestBean(server, bean, parentBean, mgr, (SpiTransaction) t, persistExecute, updateProps, mode); - req.setStatelessUpdate(true, deleteMissingChildren, updateNullProperties); - } - - try { - req.initTransIfRequired(); - update(req); - req.commitTransIfRequired(); - - } catch (RuntimeException ex) { - req.rollbackTransIfRequired(); - throw ex; - } - } - - public void save(Object bean, Transaction t) { - saveRecurse(bean, t, null); - } - - /** - * Explicitly specify to insert this bean. - */ - public void forceInsert(Object bean, Transaction t) { - - PersistRequestBean req = createRequest(bean, t, null); - try { - req.initTransIfRequired(); - insert(req); - req.commitTransIfRequired(); - - } catch (RuntimeException ex) { - req.rollbackTransIfRequired(); - throw ex; - } - } - - private void saveRecurse(Object bean, Transaction t, Object parentBean) { - if (bean == null) { - throw new NullPointerException(Message.msg("bean.isnull")); - } - - if (bean instanceof EntityBean == false) { - saveVanillaRecurse(bean, t, parentBean); - return; - } - - PersistRequestBean req = createRequest(bean, t, parentBean); - try { - req.initTransIfRequired(); - saveEnhanced(req); - req.commitTransIfRequired(); - - } catch (RuntimeException ex) { - req.rollbackTransIfRequired(); - throw ex; - } - } - - /** - * Insert or update the bean depending on PersistControl and the bean state. - */ - private void saveEnhanced(PersistRequestBean request) { - - EntityBeanIntercept intercept = request.getEntityBeanIntercept(); - - if (intercept.isReference()) { - // its a reference... - if (request.isPersistCascade()) { - // save any associated List held beans - intercept.setLoaded(); - saveAssocMany(false, request); - intercept.setReference(); - } - - } else { - if (intercept.isLoaded()) { - // Need to call setLoaded(false) to simulate insert - update(request); - } else { - insert(request); - } - } - } - - /** - * Determine if this is an Insert or update for the 'vanilla' bean. - */ - private void saveVanillaRecurse(Object bean, Transaction t, Object parentBean) { - - BeanManager mgr = getBeanManager(bean); - if (mgr == null) { - throw new RuntimeException("No Mgr found for " + bean + " " + bean.getClass()); - } - // use the version property to determine insert or update - if (mgr.getBeanDescriptor().isVanillaInsert(bean)) { - saveVanillaInsert(bean, t, parentBean, mgr); - - } else { - // update non-null properties (no partial object knowledge with vanilla bean) - forceUpdateStateless(bean, t, parentBean, mgr, null, defaultDeleteMissingChildren, defaultUpdateNullProperties); - } - } - - /** - * Perform insert on non-enhanced bean (effectively same as enhanced bean). - */ - private void saveVanillaInsert(Object bean, Transaction t, Object parentBean, BeanManager mgr) { - - PersistRequestBean req = createRequest(bean, t, parentBean, mgr); - try { - req.initTransIfRequired(); - insert(req); - req.commitTransIfRequired(); - - } catch (RuntimeException ex) { - req.rollbackTransIfRequired(); - throw ex; - } - } - - /** - * Insert the bean. - */ - private void insert(PersistRequestBean request) { - - if (request.isRegisteredBean()){ - // skip as already inserted/updated in this request (recursive cascading) - return; - } - - try { - request.setType(PersistRequest.Type.INSERT); - - if (request.isPersistCascade()) { - // save associated One beans recursively first - saveAssocOne(request); - } - - // set the IDGenerated value if required - setIdGenValue(request); - request.executeOrQueue(); - - if (request.isPersistCascade()) { - // save any associated List held beans - saveAssocMany(true, request); - } - } finally { - request.unRegisterBean(); - } - } - - /** - * Update the bean. Return NOT_SAVED if the bean values have not changed. - */ - private void update(PersistRequestBean request) { - - if (request.isRegisteredBean()){ - // skip as already inserted/updated in this request (recursive cascading) - return; - } - - try { - // we have determined that it is an update - request.setType(PersistRequest.Type.UPDATE); - if (request.isPersistCascade()) { - // save associated One beans recursively first - saveAssocOne(request); - } - - if (request.isDirty()) { - request.executeOrQueue(); - - } else { - // skip validation on unchanged bean - if (logger.isLoggable(Level.FINE)) { - logger.fine(Message.msg("persist.update.skipped", request.getBean())); - } - } - - if (request.isPersistCascade()) { - // save all the beans in assocMany's after - saveAssocMany(false, request); - } - } finally { - request.unRegisterBean(); - } - } - - /** - * Delete the bean with the explicit transaction. - */ - public void delete(Object bean, Transaction t) { - - PersistRequestBean req = createRequest(bean, t, null); - if (req.isRegisteredForDeleteBean()) { - // skip deleting bean. Used where cascade is on - // both sides of a relationship - if (logger.isLoggable(Level.FINE)) { - logger.fine("skipping delete on alreadyRegistered " + bean); - } - return; - } - req.setType(PersistRequest.Type.DELETE); - try { - req.initTransIfRequired(); - delete(req); - req.commitTransIfRequired(); - - } catch (RuntimeException ex) { - req.rollbackTransIfRequired(); - throw ex; - } - } - - private void deleteList(List beanList, Transaction t) { - for (int i = 0; i < beanList.size(); i++) { - Object bean = beanList.get(i); - delete(bean, t); - } - } - - /** - * Delete by a List of Id's. - */ - public void deleteMany(Class beanType, Collection ids, Transaction transaction) { - - if (ids == null || ids.size() == 0) { - return; - } - - BeanDescriptor descriptor = beanDescriptorManager.getBeanDescriptor(beanType); - - ArrayList idList = new ArrayList(ids.size()); - for (Object id : ids) { - // convert to appropriate type if required - idList.add(descriptor.convertId(id)); - } - - delete(descriptor, null, idList, transaction); - } - - /** - * Delete by Id. - */ - public int delete(Class beanType, Object id, Transaction transaction) { - - BeanDescriptor descriptor = beanDescriptorManager.getBeanDescriptor(beanType); - - // convert to appropriate type if required - id = descriptor.convertId(id); - return delete(descriptor, id, null, transaction); - } - - /** - * Delete by Id or a List of Id's. - */ - private int delete(BeanDescriptor descriptor, Object id, List idList, Transaction transaction) { - - SpiTransaction t = (SpiTransaction) transaction; - if (t.isPersistCascade()) { - BeanPropertyAssocOne[] propImportDelete = descriptor.propertiesOneImportedDelete(); - if (propImportDelete.length > 0) { - // We actually need to execute a query to get the foreign key values - // as they are required for the delete cascade. Query back just the - // Id and the appropriate foreign key values - Query q = deleteRequiresQuery(descriptor, propImportDelete); - if (idList != null) { - q.where().idIn(idList); - if (t.isLogSummary()) { - t.logInternal("-- DeleteById of " + descriptor.getName() + " ids[" + idList + "] requires fetch of foreign key values"); - } - List beanList = server.findList(q, t); - deleteList(beanList, t); - return beanList.size(); - - } else { - q.where().idEq(id); - if (t.isLogSummary()) { - t.logInternal("-- DeleteById of " + descriptor.getName() + " id[" + id + "] requires fetch of foreign key values"); - } - Object bean = server.findUnique(q, t); - if (bean == null) { - return 0; - } else { - delete(bean, t); - return 1; - } - } - } - } - - if (t.isPersistCascade()) { - // OneToOne exported side with delete cascade - BeanPropertyAssocOne[] expOnes = descriptor.propertiesOneExportedDelete(); - for (int i = 0; i < expOnes.length; i++) { - BeanDescriptor targetDesc = expOnes[i].getTargetDescriptor(); - if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isUsingL2Cache()) { - SqlUpdate sqlDelete = expOnes[i].deleteByParentId(id, idList); - executeSqlUpdate(sqlDelete, t); - } else { - List childIds = expOnes[i].findIdsByParentId(id, idList, t); - delete(targetDesc, null, childIds, t); - } - } - - // OneToMany's with delete cascade - BeanPropertyAssocMany[] manys = descriptor.propertiesManyDelete(); - for (int i = 0; i < manys.length; i++) { - BeanDescriptor targetDesc = manys[i].getTargetDescriptor(); - if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isUsingL2Cache()) { - // we can just delete children with a single statement - SqlUpdate sqlDelete = manys[i].deleteByParentId(id, idList); - executeSqlUpdate(sqlDelete, t); - } else { - // we need to fetch the Id's to delete (recurse or notify L2 cache/lucene) - List childIds = manys[i].findIdsByParentId(id, idList, t, null); - delete(targetDesc, null, childIds, t); - } - } - } - - // ManyToMany's ... delete from intersection table - BeanPropertyAssocMany[] manys = descriptor.propertiesManyToMany(); - for (int i = 0; i < manys.length; i++) { - SqlUpdate sqlDelete = manys[i].deleteByParentId(id, idList); - if (t.isLogSummary()) { - t.logInternal("-- Deleting intersection table entries: " + manys[i].getFullBeanName()); - } - executeSqlUpdate(sqlDelete, t); - } - - // delete the bean(s) - SqlUpdate deleteById = descriptor.deleteById(id, idList); - if (t.isLogSummary()) { - t.logInternal("-- Deleting " + descriptor.getName() + " Ids" + idList); - } - - // use Id's to update L2 cache rather than Bulk table event - deleteById.setAutoTableMod(false); - if (idList != null) { - t.getEvent().addDeleteByIdList(descriptor, idList); - } else { - t.getEvent().addDeleteById(descriptor, id); - } - return executeSqlUpdate(deleteById, t); - } - - /** - * We need to create and execute a query to get the foreign key values as - * the delete cascades to them (foreign keys). - */ - private Query deleteRequiresQuery(BeanDescriptor desc, BeanPropertyAssocOne[] propImportDelete) { - - Query q = server.createQuery(desc.getBeanType()); - StringBuilder sb = new StringBuilder(30); - for (int i = 0; i < propImportDelete.length; i++) { - sb.append(propImportDelete[i].getName()).append(","); - } - q.setAutofetch(false); - q.select(sb.toString()); - return q; - } - - /** - * Delete the bean. - *

    - * Note that preDelete fires before the deletion of children. - *

    - */ - private void delete(PersistRequestBean request) { - - DeleteUnloadedForeignKeys unloadedForeignKeys = null; - -// boolean pauseIndexInvalidate = request.getBeanDescriptor().isLuceneIndexed(); -// if (pauseIndexInvalidate) { -// // Stop any deletions of child beans to Invalidate the Index as we -// // will use the delete of this top level object to maintain the index -// request.pauseIndexInvalidate(); -// } - - if (request.isPersistCascade()) { - // delete children first ... register the - // bean to handle bi-directional cascading - request.registerDeleteBean(); - deleteAssocMany(request); - request.unregisterDeleteBean(); - - unloadedForeignKeys = getDeleteUnloadedForeignKeys(request); - if (unloadedForeignKeys != null) { - // there are foreign keys that we don't have on this partially - // populated bean so we actually need to query them (to cascade delete) - unloadedForeignKeys.queryForeignKeys(); - } - } - - request.executeOrQueue(); - - if (request.isPersistCascade()) { - deleteAssocOne(request); - - if (unloadedForeignKeys != null) { - unloadedForeignKeys.deleteCascade(); - } - } -// if (pauseIndexInvalidate) { -// request.resumeIndexInvalidate(); -// } - } - - /** - * Save the associated child beans contained in a List. - *

    - * This will automatically copy over any join properties from the parent - * bean to the child beans. - *

    - */ - private void saveAssocMany(boolean insertedParent, PersistRequestBean request) { - - Object parentBean = request.getBean(); - BeanDescriptor desc = request.getBeanDescriptor(); - SpiTransaction t = request.getTransaction(); - - // exported ones with cascade save - BeanPropertyAssocOne[] expOnes = desc.propertiesOneExportedSave(); - for (int i = 0; i < expOnes.length; i++) { - BeanPropertyAssocOne prop = expOnes[i]; - - // check for partial beans - if (request.isLoadedProperty(prop)) { - Object detailBean = prop.getValue(parentBean); - if (detailBean != null) { - if (prop.isSaveRecurseSkippable(detailBean)) { - // skip saving this bean - } else { - t.depth(+1); - saveRecurse(detailBean, t, parentBean); - t.depth(-1); - } - } - } - } - - // many's with cascade save - BeanPropertyAssocMany[] manys = desc.propertiesManySave(); - for (int i = 0; i < manys.length; i++) { - saveMany(new SaveManyPropRequest(insertedParent, manys[i], parentBean, request)); - } - } - - /** - * Helper to wrap the details when saving a OneToMany or ManyToMany - * relationship. - */ - private static class SaveManyPropRequest { - private final boolean insertedParent; - private final BeanPropertyAssocMany many; - private final Object parentBean; - private final SpiTransaction t; - private final boolean cascade; - private final boolean statelessUpdate; - private final boolean deleteMissingChildren; - private final boolean updateNullProperties; - - private SaveManyPropRequest(boolean insertedParent, BeanPropertyAssocMany many, Object parentBean, PersistRequestBean request) { - this.insertedParent = insertedParent; - this.many = many; - this.cascade = many.getCascadeInfo().isSave(); - this.parentBean = parentBean; - this.t = request.getTransaction(); - this.statelessUpdate = request.isStatelessUpdate(); - this.deleteMissingChildren = request.isDeleteMissingChildren(); - this.updateNullProperties = request.isUpdateNullProperties(); - } - - private SaveManyPropRequest(BeanPropertyAssocMany many, Object parentBean, SpiTransaction t) { - this.insertedParent = false; - this.many = many; - this.parentBean = parentBean; - this.t = t; - this.cascade = true; - this.statelessUpdate = false; - this.deleteMissingChildren = false; - this.updateNullProperties = false; - } - - private Object getValueUnderlying() { - return many.getValueUnderlying(parentBean); - } - - private boolean isModifyListenMode() { - return ModifyListenMode.REMOVALS.equals(many.getModifyListenMode()); - } - - private boolean isStatelessUpdate() { - return statelessUpdate; - } - - private boolean isDeleteMissingChildren() { - return deleteMissingChildren; - } - - private boolean isUpdateNullProperties() { - return updateNullProperties; - } - - private boolean isInsertedParent() { - return insertedParent; - } - - private BeanPropertyAssocMany getMany() { - return many; - } - - private Object getParentBean() { - return parentBean; - } - - private SpiTransaction getTransaction() { - return t; - } - - private boolean isCascade() { - return cascade; - } - } - - private void saveMany(SaveManyPropRequest saveMany) { - - if (saveMany.getMany().isManyToMany()) { - // save the beans that are in the manyToMany - if (saveMany.isCascade()) { - // Need explicit Cascade to save the beans on other side - saveAssocManyDetails(saveMany, false, saveMany.isUpdateNullProperties()); - // for ManyToMany save the 'relationship' via inserts/deletes - // into/from the intersection table - saveAssocManyIntersection(saveMany, saveMany.isDeleteMissingChildren()); - } - - } else { - if (saveMany.isCascade()) { - saveAssocManyDetails(saveMany, saveMany.isDeleteMissingChildren(), saveMany.isUpdateNullProperties()); - } - if (saveMany.isModifyListenMode()) { - removeAssocManyPrivateOwned(saveMany); - } - } - } - - private void removeAssocManyPrivateOwned(SaveManyPropRequest saveMany) { - - Object details = saveMany.getValueUnderlying(); - - // check that the list is not null and if it is a BeanCollection - // check that is has been populated (don't trigger lazy loading) - if (details instanceof BeanCollection) { - - BeanCollection c = (BeanCollection) details; - Set modifyRemovals = c.getModifyRemovals(); - if (modifyRemovals != null && !modifyRemovals.isEmpty()) { - - SpiTransaction t = saveMany.getTransaction(); - // increase depth for batching order - t.depth(+1); - for (Object removedBean : modifyRemovals) { - deleteRecurse(removedBean, t); - } - t.depth(-1); - } - } - } - - /** - * Save the details from a OneToMany collection. - */ - private void saveAssocManyDetails(SaveManyPropRequest saveMany, boolean deleteMissingChildren, boolean updateNullProperties) { - - BeanPropertyAssocMany prop = saveMany.getMany(); - - Object details = saveMany.getValueUnderlying(); - - // check that the list is not null and if it is a BeanCollection - // check that is has been populated (don't trigger lazy loading) - Collection collection = getDetailsIterator(details); - - if (collection == null) { - // nothing to do here - return; - } - - if (saveMany.isInsertedParent()) { - // performance optimisation for large collections - prop.getTargetDescriptor().preAllocateIds(collection.size()); - } - - BeanDescriptor targetDescriptor = prop.getTargetDescriptor(); - ArrayList detailIds = null; - if (deleteMissingChildren) { - // collect the Id's (to exclude from deleteManyDetails) - detailIds = new ArrayList(); - } - - // increase depth for batching order - SpiTransaction t = saveMany.getTransaction(); - t.depth(+1); - - // if a map, then we get the key value and - // set it to the appropriate property on the - // detail bean before we save it - boolean isMap = ManyType.JAVA_MAP.equals(prop.getManyType()); - Object parentBean = saveMany.getParentBean(); - Object mapKeyValue = null; - - boolean saveSkippable = prop.isSaveRecurseSkippable(); - boolean skipSavingThisBean = false; - - for (Object detailBean : collection) { - if (isMap) { - // its a map so need the key and value - Map.Entry entry = (Map.Entry) detailBean; - mapKeyValue = entry.getKey(); - detailBean = entry.getValue(); - } - - if (prop.isManyToMany()) { - if (detailBean instanceof EntityBean) { - skipSavingThisBean = ((EntityBean) detailBean)._ebean_getIntercept().isReference(); - } - } else { - // set the 'parent/master' bean to the detailBean as long - // as we don't make it 'dirty' in doing so - if (detailBean instanceof EntityBean) { - EntityBeanIntercept ebi = ((EntityBean) detailBean)._ebean_getIntercept(); - if (ebi.isNewOrDirty()) { - // set the parent bean to detailBean - prop.setJoinValuesToChild(parentBean, detailBean, mapKeyValue); - } else if (ebi.isReference()) { - // we can skip this one - skipSavingThisBean = true; - - } else { - // unmodified so skip depending on prop.isSaveRecurseSkippable(); - skipSavingThisBean = saveSkippable; - } - } else { - // set the parent bean to detailBean - prop.setJoinValuesToChild(parentBean, detailBean, mapKeyValue); - } - } - - if (skipSavingThisBean) { - // unmodified bean that does not recurse its save - // so we can skip the save for this bean. - // Reset skipSavingThisBean for the next detailBean - skipSavingThisBean = false; - - } else if (!saveMany.isStatelessUpdate()) { - // normal save recurse - saveRecurse(detailBean, t, parentBean); - - } else { - if (targetDescriptor.isStatelessUpdate(detailBean)) { - // update based on the value of Version/Id properties - // cascade update in stateless mode - forceUpdate(detailBean, null, t, deleteMissingChildren, updateNullProperties); - } else { - // cascade insert - forceInsert(detailBean, t); - } - } - - if (detailIds != null) { - // remember the Id (other details not in the collection) will be removed - Object id = targetDescriptor.getId(detailBean); - if (!DmlUtil.isNullOrZero(id)) { - detailIds.add(id); - } - } - } - - if (detailIds != null) { - deleteManyDetails(t, prop.getBeanDescriptor(), parentBean, prop, detailIds); - } - - t.depth(-1); - - } - - public int deleteManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) { - - BeanDescriptor descriptor = beanDescriptorManager.getBeanDescriptor(ownerBean.getClass()); - BeanPropertyAssocMany prop = (BeanPropertyAssocMany) descriptor.getBeanProperty(propertyName); - return deleteAssocManyIntersection(ownerBean, prop, t); - } - - public void saveManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) { - - BeanDescriptor descriptor = beanDescriptorManager.getBeanDescriptor(ownerBean.getClass()); - BeanPropertyAssocMany prop = (BeanPropertyAssocMany) descriptor.getBeanProperty(propertyName); - - saveAssocManyIntersection(new SaveManyPropRequest(prop, ownerBean, (SpiTransaction) t), false); - } - - public void saveAssociation(Object parentBean, String propertyName, Transaction t) { - - BeanDescriptor descriptor = beanDescriptorManager.getBeanDescriptor(parentBean.getClass()); - SpiTransaction trans = (SpiTransaction) t; - - BeanProperty prop = descriptor.getBeanProperty(propertyName); - if (prop == null) { - String msg = "Could not find property [" + propertyName + "] on bean " + parentBean.getClass(); - throw new PersistenceException(msg); - } - - if (prop instanceof BeanPropertyAssocMany) { - BeanPropertyAssocMany manyProp = (BeanPropertyAssocMany) prop; - saveMany(new SaveManyPropRequest(manyProp, parentBean, (SpiTransaction) t)); - - } else if (prop instanceof BeanPropertyAssocOne) { - BeanPropertyAssocOne oneProp = (BeanPropertyAssocOne) prop; - Object assocBean = oneProp.getValue(parentBean); - - int depth = oneProp.isOneToOneExported() ? 1 : -1; - int revertDepth = -1 * depth; - - trans.depth(depth); - saveRecurse(assocBean, t, parentBean); - trans.depth(revertDepth); - - } else { - String msg = "Expecting [" + prop.getFullBeanName() + "] to be a OneToMany, OneToOne, ManyToOne or ManyToMany property?"; - throw new PersistenceException(msg); - } - - } - - /** - * Save the additions and removals from a ManyToMany collection as inserts - * and deletes from the intersection table. - *

    - * This is done via MapBeans. - *

    - */ - private void saveAssocManyIntersection(SaveManyPropRequest saveManyPropRequest, boolean deleteMissingChildren) { - - BeanPropertyAssocMany prop = saveManyPropRequest.getMany(); - Object value = prop.getValueUnderlying(saveManyPropRequest.getParentBean()); - if (value == null) { - return; - } - - SpiTransaction t = saveManyPropRequest.getTransaction(); - Collection additions = null; - Collection deletions = null; - - boolean vanillaCollection = (value instanceof BeanCollection == false); - - if (vanillaCollection || deleteMissingChildren) { - // delete all intersection rows and then treat all - // beans in the collection as additions - deleteAssocManyIntersection(saveManyPropRequest.getParentBean(), prop, t); - } - - if (saveManyPropRequest.isInsertedParent() || vanillaCollection || deleteMissingChildren) { - // treat everything in the list/set/map as an intersection addition - if (value instanceof Map) { - additions = ((Map) value).values(); - } else if (value instanceof Collection) { - additions = (Collection) value; - } else { - String msg = "Unhandled ManyToMany type " + value.getClass().getName() + " for " + prop.getFullBeanName(); - throw new PersistenceException(msg); - } - if (!vanillaCollection) { - ((BeanCollection) value).modifyReset(); - } - } else { - // BeanCollection so get the additions/deletions - BeanCollection manyValue = (BeanCollection) value; - additions = manyValue.getModifyAdditions(); - deletions = manyValue.getModifyRemovals(); - // reset so the changes are only processed once - manyValue.modifyReset(); - } - - t.depth(+1); - - if (additions != null && !additions.isEmpty()) { - for (Object otherBean : additions) { - // the object from the 'other' side of the ManyToMany - if (deletions != null && deletions.remove(otherBean)) { - String m = "Inserting and Deleting same object? " + otherBean; - if (t.isLogSummary()) { - t.logInternal(m); - } - logger.log(Level.WARNING, m); - - } else { - if (!prop.hasImportedId(otherBean)) { - String msg = "ManyToMany bean " + otherBean + " does not have an Id value."; - throw new PersistenceException(msg); - - } else { - // build a intersection row for 'insert' - IntersectionRow intRow = prop.buildManyToManyMapBean(saveManyPropRequest.getParentBean(), otherBean); - SqlUpdate sqlInsert = intRow.createInsert(server); - executeSqlUpdate(sqlInsert, t); - } - } - } - } - if (deletions != null && !deletions.isEmpty()) { - for (Object otherDelete : deletions) { - // the object from the 'other' side of the ManyToMany - // build a intersection row for 'delete' - IntersectionRow intRow = prop.buildManyToManyMapBean(saveManyPropRequest.getParentBean(), otherDelete); - SqlUpdate sqlDelete = intRow.createDelete(server); - executeSqlUpdate(sqlDelete, t); - } - } - - // decrease the depth back to what it was - t.depth(-1); - } - - private int deleteAssocManyIntersection(Object bean, BeanPropertyAssocMany many, Transaction t) { - - // delete all intersection rows for this bean - IntersectionRow intRow = many.buildManyToManyDeleteChildren(bean); - SqlUpdate sqlDelete = intRow.createDeleteChildren(server); - - return executeSqlUpdate(sqlDelete, t); - } - - /** - * Delete beans in any associated many. - *

    - * This is called prior to deleting the parent bean. - *

    - */ - private void deleteAssocMany(PersistRequestBean request) { - - SpiTransaction t = request.getTransaction(); - t.depth(-1); - - BeanDescriptor desc = request.getBeanDescriptor(); - Object parentBean = request.getBean(); - - BeanPropertyAssocOne[] expOnes = desc.propertiesOneExportedDelete(); - if (expOnes.length > 0) { - - DeleteUnloadedForeignKeys unloaded = null; - for (int i = 0; i < expOnes.length; i++) { - BeanPropertyAssocOne prop = expOnes[i]; - if (request.isLoadedProperty(prop)) { - Object detailBean = prop.getValue(parentBean); - if (detailBean != null) { - deleteRecurse(detailBean, t); - } - } else { - if (unloaded == null) { - unloaded = new DeleteUnloadedForeignKeys(server, request); - } - unloaded.add(prop); - } - } - if (unloaded != null) { - unloaded.queryForeignKeys(); - unloaded.deleteCascade(); - } - } - - // Many's with delete cascade - BeanPropertyAssocMany[] manys = desc.propertiesManyDelete(); - for (int i = 0; i < manys.length; i++) { - if (manys[i].isManyToMany()) { - // delete associated rows from intersection table - deleteAssocManyIntersection(parentBean, manys[i], t); - - } else { - - if (ModifyListenMode.REMOVALS.equals(manys[i].getModifyListenMode())) { - // PrivateOwned ... - Object details = manys[i].getValueUnderlying(parentBean); - if (details instanceof BeanCollection) { - Set modifyRemovals = ((BeanCollection) details).getModifyRemovals(); - if (modifyRemovals != null && !modifyRemovals.isEmpty()) { - - // delete the orphans that have been removed from the collection - for (Object detailBean : modifyRemovals) { - if (manys[i].hasId(detailBean)) { - deleteRecurse(detailBean, t); - } - } - } - } - } - - deleteManyDetails(t, desc, parentBean, manys[i], null); - } - } - - // restore the depth - t.depth(+1); - } - - /** - * Delete the 'many' detail beans for a given parent bean. - *

    - * For stateless updates this deletes details beans that are no longer in - * the many - the excludeDetailIds holds the detail beans that are in the - * collection (and should not be deleted). - *

    - */ - private void deleteManyDetails(SpiTransaction t, BeanDescriptor desc, Object parentBean, - BeanPropertyAssocMany many, ArrayList excludeDetailIds) { - - if (many.getCascadeInfo().isDelete()) { - // cascade delete the beans in the collection - BeanDescriptor targetDesc = many.getTargetDescriptor(); - if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isUsingL2Cache()) { - // Just delete all the children with one statement - IntersectionRow intRow = many.buildManyDeleteChildren(parentBean, excludeDetailIds); - SqlUpdate sqlDelete = intRow.createDelete(server); - executeSqlUpdate(sqlDelete, t); - - } else { - // Delete recurse using the Id values of the children - Object parentId = desc.getId(parentBean); - List idsByParentId = many.findIdsByParentId(parentId, null, t, excludeDetailIds); - if (!idsByParentId.isEmpty()) { - delete(targetDesc, null, idsByParentId, t); - } - } - } - } - - /** - * Save any associated one beans. - */ - private void saveAssocOne(PersistRequestBean request) { - - BeanDescriptor desc = request.getBeanDescriptor(); - - // imported ones with save cascade - BeanPropertyAssocOne[] ones = desc.propertiesOneImportedSave(); - - for (int i = 0; i < ones.length; i++) { - BeanPropertyAssocOne prop = ones[i]; - - // check for partial objects - if (request.isLoadedProperty(prop)) { - Object detailBean = prop.getValue(request.getBean()); - if (detailBean != null) { - if (isReference(detailBean)) { - // skip saving a reference - } else if (request.isParent(detailBean)) { - // skip saving the parent as already saved - } else if (prop.isSaveRecurseSkippable(detailBean)) { - // we can skip saving this bean - - } else { - SpiTransaction t = request.getTransaction(); - t.depth(-1); - saveRecurse(detailBean, t, null); - t.depth(+1); - } - } - } - } - } - - /** - * Return true if the bean is a reference. - */ - private boolean isReference(Object bean) { - return (bean instanceof EntityBean) && ((EntityBean) bean)._ebean_getIntercept().isReference(); - } - - /** - * Support for loading any Imported Associated One properties that are not - * loaded but required for Delete cascade. - */ - private DeleteUnloadedForeignKeys getDeleteUnloadedForeignKeys(PersistRequestBean request) { - - DeleteUnloadedForeignKeys fkeys = null; - - BeanPropertyAssocOne[] ones = request.getBeanDescriptor().propertiesOneImportedDelete(); - for (int i = 0; i < ones.length; i++) { - if (!request.isLoadedProperty(ones[i])) { - // we have cascade Delete on a partially populated bean and - // this property was not loaded (so we are going to have to fetch it) - if (fkeys == null) { - fkeys = new DeleteUnloadedForeignKeys(server, request); - } - fkeys.add(ones[i]); - } - } - - return fkeys; - } - - /** - * Delete any associated one beans. - */ - private void deleteAssocOne(PersistRequestBean request) { - - BeanDescriptor desc = request.getBeanDescriptor(); - BeanPropertyAssocOne[] ones = desc.propertiesOneImportedDelete(); - - for (int i = 0; i < ones.length; i++) { - BeanPropertyAssocOne prop = ones[i]; - if (!request.isLoadedProperty(prop)) { - // handled by DeleteUnloadedForeignKeys that was built - // via getDeleteUnloadedForeignKeys(); - } else { - Object detailBean = prop.getValue(request.getBean()); - if (detailBean != null && prop.hasId(detailBean)) { - deleteRecurse(detailBean, request.getTransaction()); - } - } - } - } - - /** - * Set Id Generated value for insert. - */ - private void setIdGenValue(PersistRequestBean request) { - - BeanDescriptor desc = request.getBeanDescriptor(); - if (!desc.isUseIdGenerator()) { - return; - } - - BeanProperty idProp = desc.getSingleIdProperty(); - if (idProp == null || idProp.isEmbedded()) { - // not supporting IdGeneration for concatenated or Embedded - return; - } - - Object bean = request.getBean(); - Object uid = idProp.getValue(bean); - - if (DmlUtil.isNullOrZero(uid)) { - - // generate the nextId and set it to the property - Object nextId = desc.nextId(request.getTransaction()); - - // cast the data type if required and set it - desc.convertSetId(nextId, bean); - } - } - - /** - * Return the details of the collection or map taking care to avoid - * unnecessary fetching of the data. - */ - private Collection getDetailsIterator(Object o) { - if (o == null) { - return null; - } - if (o instanceof BeanCollection) { - BeanCollection bc = (BeanCollection) o; - if (!bc.isPopulated()) { - return null; - } - return bc.getActualDetails(); - } - - if (o instanceof Map) { - // yes, we want the entrySet (to set the keys) - return ((Map) o).entrySet(); - - } else if (o instanceof Collection) { - return ((Collection) o); - } - String m = "expecting a Map or Collection but got [" + o.getClass().getName() + "]"; - throw new PersistenceException(m); - } - - /** - * Create the Persist Request Object that wraps all the objects used to - * perform an insert, update or delete. - */ - @SuppressWarnings("unchecked") - private PersistRequestBean createRequest(T bean, Transaction t, Object parentBean) { - BeanManager mgr = getBeanManager(bean); - if (mgr == null) { - throw new PersistenceException(errNotRegistered(bean.getClass())); - } - return (PersistRequestBean) createRequest(bean, t, parentBean, mgr); - } - - private String errNotRegistered(Class beanClass) { - String msg = "The type [" + beanClass + "] is not a registered entity?"; - msg += " If you don't explicitly list the entity classes to use Ebean will search for them in the classpath."; - msg += " If the entity is in a Jar check the ebean.search.jars property in ebean.properties file or check ServerConfig.addJar()."; - return msg; - } - - /** - * Create the Persist Request Object that wraps all the objects used to - * perform an insert, update or delete. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - private PersistRequestBean createRequest(Object bean, Transaction t, Object parentBean, BeanManager mgr) { - - if (mgr.isLdapEntityType()) { - return new LdapPersistBeanRequest(server, bean, parentBean, mgr, ldapPersister); - } - return new PersistRequestBean(server, bean, parentBean, mgr, (SpiTransaction) t, persistExecute); - } - - /** - * Return the BeanDescriptor for a bean that is being persisted. - *

    - * Note that this checks to see if the bean is a MapBean with a tableName. - * If so it will return the table based BeanDescriptor. - *

    - */ - @SuppressWarnings("unchecked") - private BeanManager getBeanManager(T bean) { - - return (BeanManager) beanDescriptorManager.getBeanManager(bean.getClass()); - } -} +package com.avaje.ebeaninternal.server.persist; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.CallableSql; +import com.avaje.ebean.Query; +import com.avaje.ebean.SqlUpdate; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.Update; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.BeanCollection.ModifyListenMode; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.config.ldap.LdapContextFactory; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.api.SpiUpdate; +import com.avaje.ebeaninternal.server.core.ConcurrencyMode; +import com.avaje.ebeaninternal.server.core.Message; +import com.avaje.ebeaninternal.server.core.PersistRequest; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql; +import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate; +import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql; +import com.avaje.ebeaninternal.server.core.Persister; +import com.avaje.ebeaninternal.server.core.PstmtBatch; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; +import com.avaje.ebeaninternal.server.deploy.BeanManager; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.IntersectionRow; +import com.avaje.ebeaninternal.server.deploy.ManyType; +import com.avaje.ebeaninternal.server.ldap.DefaultLdapPersister; +import com.avaje.ebeaninternal.server.ldap.LdapPersistBeanRequest; + +/** + * Persister implementation using DML. + *

    + * This object uses DmlPersistExecute to perform the actual persist execution. + *

    + *

    + * This object: + *

      + *
    • Determines insert or update for saved beans
    • + *
    • Determines the concurrency mode
    • + *
    • Handles cascading of save and delete
    • + *
    • Handles the batching and queueing
    • + *

      + * + * @see com.avaje.ebeaninternal.server.persist.DefaultPersistExecute + */ +public final class DefaultPersister implements Persister { + + private static final Logger logger = Logger.getLogger(DefaultPersister.class.getName()); + + /** + * Actually does the persisting work. + */ + private final PersistExecute persistExecute; + + private final DefaultLdapPersister ldapPersister; + + private final SpiEbeanServer server; + + private final BeanDescriptorManager beanDescriptorManager; + + private final boolean defaultUpdateNullProperties; + private final boolean defaultDeleteMissingChildren; + + public DefaultPersister(SpiEbeanServer server, boolean validate, + Binder binder, BeanDescriptorManager descMgr, PstmtBatch pstmtBatch, LdapContextFactory contextFactory) { + + this.server = server; + this.beanDescriptorManager = descMgr; + + this.persistExecute = new DefaultPersistExecute(validate, binder, pstmtBatch); + this.ldapPersister = new DefaultLdapPersister(contextFactory); + + this.defaultUpdateNullProperties = server.isDefaultUpdateNullProperties(); + this.defaultDeleteMissingChildren = server.isDefaultDeleteMissingChildren(); + } + + /** + * Execute the CallableSql. + */ + public int executeCallable(CallableSql callSql, Transaction t) { + + PersistRequestCallableSql request = new PersistRequestCallableSql(server, callSql, (SpiTransaction) t, persistExecute); + try { + request.initTransIfRequired(); + int rc = request.executeOrQueue(); + request.commitTransIfRequired(); + return rc; + + } catch (RuntimeException e) { + request.rollbackTransIfRequired(); + throw e; + } + } + + /** + * Execute the orm update. + */ + public int executeOrmUpdate(Update update, Transaction t) { + + SpiUpdate ormUpdate = (SpiUpdate) update; + + BeanManager mgr = beanDescriptorManager.getBeanManager(ormUpdate.getBeanType()); + + if (mgr == null) { + String msg = "No BeanManager found for type [" + ormUpdate.getBeanType() + "]. Is it an entity?"; + throw new PersistenceException(msg); + } + + PersistRequestOrmUpdate request = new PersistRequestOrmUpdate(server, mgr, ormUpdate, (SpiTransaction) t, persistExecute); + try { + request.initTransIfRequired(); + int rc = request.executeOrQueue(); + request.commitTransIfRequired(); + return rc; + + } catch (RuntimeException e) { + request.rollbackTransIfRequired(); + throw e; + } + } + + /** + * Execute the updateSql. + */ + public int executeSqlUpdate(SqlUpdate updSql, Transaction t) { + + PersistRequestUpdateSql request = new PersistRequestUpdateSql(server, updSql, (SpiTransaction) t, persistExecute); + try { + request.initTransIfRequired(); + int rc = request.executeOrQueue(); + request.commitTransIfRequired(); + return rc; + + } catch (RuntimeException e) { + request.rollbackTransIfRequired(); + throw e; + } + } + + /** + * Recursively delete the bean. This calls back to the EbeanServer. + */ + private void deleteRecurse(Object detailBean, Transaction t) { + // NB: a new PersistRequest is made + server.delete(detailBean, t); + } + + /** + * Force an Update using the given bean. + */ + public void forceUpdate(Object bean, Set updateProps, Transaction t, boolean deleteMissingChildren, boolean updateNullProperties) { + + if (bean == null) { + throw new NullPointerException(Message.msg("bean.isnull")); + } + + if (updateProps == null) { + // checking to see if this is just a 'normal' update + if (bean instanceof EntityBean) { + EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept(); + if (ebi.isDirty() || ebi.isLoaded()) { + // a 'normal' update using 'dirty' properties from internal bean state. + // if not dirty we still update in case any cascading save occurs + PersistRequestBean req = createRequest(bean, t, null); + try { + req.initTransIfRequired(); + update(req); + req.commitTransIfRequired(); + // finished a 'normal' update + return; + + } catch (RuntimeException ex) { + req.rollbackTransIfRequired(); + throw ex; + } + } else if (ebi.isReference()) { + // just return as no point in cascading (no modified beans/lists) + return; + } + + // loadedProps set by Ebean JSON / XML Marshalling + updateProps = ebi.getLoadedProps(); + } + } + + BeanManager mgr = getBeanManager(bean); + if (mgr == null) { + throw new PersistenceException(errNotRegistered(bean.getClass())); + } + + forceUpdateStateless(bean, t, null, mgr, updateProps, deleteMissingChildren, updateNullProperties); + } + + /** + * Force a 'stateless' update determining which properties to update. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void forceUpdateStateless(Object bean, Transaction t, Object parentBean, BeanManager mgr, Set updateProps, + boolean deleteMissingChildren, boolean updateNullProperties) { + + BeanDescriptor descriptor = mgr.getBeanDescriptor(); + + // determine concurrency mode based on version property not null + ConcurrencyMode mode = descriptor.determineConcurrencyMode(bean); + + if (updateProps == null) { + // determine based on null treatment (all properties updated or just the non-null ones) + updateProps = updateNullProperties ? null : descriptor.determineLoadedProperties(bean); + + } else if (updateProps.isEmpty()) { + // in this case means we want to include all properties in the update + updateProps = null; + + } else if (ConcurrencyMode.VERSION.equals(mode)) { + // check that the version property is included + String verName = descriptor.firstVersionProperty().getName(); + if (!updateProps.contains(verName)) { + // defensively copy the updateProps and add the version property name + updateProps = new HashSet(updateProps); + updateProps.add(verName); + } + } + + PersistRequestBean req; + if (descriptor.isLdapEntityType()) { + req = new LdapPersistBeanRequest(server, bean, parentBean, mgr, ldapPersister, updateProps, mode); + + } else { + // special constructor for force 'stateless' Update mode ... + req = new PersistRequestBean(server, bean, parentBean, mgr, (SpiTransaction) t, persistExecute, updateProps, mode); + req.setStatelessUpdate(true, deleteMissingChildren, updateNullProperties); + } + + try { + req.initTransIfRequired(); + update(req); + req.commitTransIfRequired(); + + } catch (RuntimeException ex) { + req.rollbackTransIfRequired(); + throw ex; + } + } + + public void save(Object bean, Transaction t) { + saveRecurse(bean, t, null); + } + + /** + * Explicitly specify to insert this bean. + */ + public void forceInsert(Object bean, Transaction t) { + + PersistRequestBean req = createRequest(bean, t, null); + try { + req.initTransIfRequired(); + insert(req); + req.commitTransIfRequired(); + + } catch (RuntimeException ex) { + req.rollbackTransIfRequired(); + throw ex; + } + } + + private void saveRecurse(Object bean, Transaction t, Object parentBean) { + if (bean == null) { + throw new NullPointerException(Message.msg("bean.isnull")); + } + + if (bean instanceof EntityBean == false) { + saveVanillaRecurse(bean, t, parentBean); + return; + } + + PersistRequestBean req = createRequest(bean, t, parentBean); + try { + req.initTransIfRequired(); + saveEnhanced(req); + req.commitTransIfRequired(); + + } catch (RuntimeException ex) { + req.rollbackTransIfRequired(); + throw ex; + } + } + + /** + * Insert or update the bean depending on PersistControl and the bean state. + */ + private void saveEnhanced(PersistRequestBean request) { + + EntityBeanIntercept intercept = request.getEntityBeanIntercept(); + + if (intercept.isReference()) { + // its a reference... + if (request.isPersistCascade()) { + // save any associated List held beans + intercept.setLoaded(); + saveAssocMany(false, request); + intercept.setReference(); + } + + } else { + if (intercept.isLoaded()) { + // Need to call setLoaded(false) to simulate insert + update(request); + } else { + insert(request); + } + } + } + + /** + * Determine if this is an Insert or update for the 'vanilla' bean. + */ + private void saveVanillaRecurse(Object bean, Transaction t, Object parentBean) { + + BeanManager mgr = getBeanManager(bean); + if (mgr == null) { + throw new RuntimeException("No Mgr found for " + bean + " " + bean.getClass()); + } + // use the version property to determine insert or update + if (mgr.getBeanDescriptor().isVanillaInsert(bean)) { + saveVanillaInsert(bean, t, parentBean, mgr); + + } else { + // update non-null properties (no partial object knowledge with vanilla bean) + forceUpdateStateless(bean, t, parentBean, mgr, null, defaultDeleteMissingChildren, defaultUpdateNullProperties); + } + } + + /** + * Perform insert on non-enhanced bean (effectively same as enhanced bean). + */ + private void saveVanillaInsert(Object bean, Transaction t, Object parentBean, BeanManager mgr) { + + PersistRequestBean req = createRequest(bean, t, parentBean, mgr); + try { + req.initTransIfRequired(); + insert(req); + req.commitTransIfRequired(); + + } catch (RuntimeException ex) { + req.rollbackTransIfRequired(); + throw ex; + } + } + + /** + * Insert the bean. + */ + private void insert(PersistRequestBean request) { + + if (request.isRegisteredBean()){ + // skip as already inserted/updated in this request (recursive cascading) + return; + } + + try { + request.setType(PersistRequest.Type.INSERT); + + if (request.isPersistCascade()) { + // save associated One beans recursively first + saveAssocOne(request); + } + + // set the IDGenerated value if required + setIdGenValue(request); + request.executeOrQueue(); + + if (request.isPersistCascade()) { + // save any associated List held beans + saveAssocMany(true, request); + } + } finally { + request.unRegisterBean(); + } + } + + /** + * Update the bean. Return NOT_SAVED if the bean values have not changed. + */ + private void update(PersistRequestBean request) { + + if (request.isRegisteredBean()){ + // skip as already inserted/updated in this request (recursive cascading) + return; + } + + try { + // we have determined that it is an update + request.setType(PersistRequest.Type.UPDATE); + if (request.isPersistCascade()) { + // save associated One beans recursively first + saveAssocOne(request); + } + + if (request.isDirty()) { + request.executeOrQueue(); + + } else { + // skip validation on unchanged bean + if (logger.isLoggable(Level.FINE)) { + logger.fine(Message.msg("persist.update.skipped", request.getBean())); + } + } + + if (request.isPersistCascade()) { + // save all the beans in assocMany's after + saveAssocMany(false, request); + } + } finally { + request.unRegisterBean(); + } + } + + /** + * Delete the bean with the explicit transaction. + */ + public void delete(Object bean, Transaction t) { + + PersistRequestBean req = createRequest(bean, t, null); + if (req.isRegisteredForDeleteBean()) { + // skip deleting bean. Used where cascade is on + // both sides of a relationship + if (logger.isLoggable(Level.FINE)) { + logger.fine("skipping delete on alreadyRegistered " + bean); + } + return; + } + req.setType(PersistRequest.Type.DELETE); + try { + req.initTransIfRequired(); + delete(req); + req.commitTransIfRequired(); + + } catch (RuntimeException ex) { + req.rollbackTransIfRequired(); + throw ex; + } + } + + private void deleteList(List beanList, Transaction t) { + for (int i = 0; i < beanList.size(); i++) { + Object bean = beanList.get(i); + delete(bean, t); + } + } + + /** + * Delete by a List of Id's. + */ + public void deleteMany(Class beanType, Collection ids, Transaction transaction) { + + if (ids == null || ids.size() == 0) { + return; + } + + BeanDescriptor descriptor = beanDescriptorManager.getBeanDescriptor(beanType); + + ArrayList idList = new ArrayList(ids.size()); + for (Object id : ids) { + // convert to appropriate type if required + idList.add(descriptor.convertId(id)); + } + + delete(descriptor, null, idList, transaction); + } + + /** + * Delete by Id. + */ + public int delete(Class beanType, Object id, Transaction transaction) { + + BeanDescriptor descriptor = beanDescriptorManager.getBeanDescriptor(beanType); + + // convert to appropriate type if required + id = descriptor.convertId(id); + return delete(descriptor, id, null, transaction); + } + + /** + * Delete by Id or a List of Id's. + */ + private int delete(BeanDescriptor descriptor, Object id, List idList, Transaction transaction) { + + SpiTransaction t = (SpiTransaction) transaction; + if (t.isPersistCascade()) { + BeanPropertyAssocOne[] propImportDelete = descriptor.propertiesOneImportedDelete(); + if (propImportDelete.length > 0) { + // We actually need to execute a query to get the foreign key values + // as they are required for the delete cascade. Query back just the + // Id and the appropriate foreign key values + Query q = deleteRequiresQuery(descriptor, propImportDelete); + if (idList != null) { + q.where().idIn(idList); + if (t.isLogSummary()) { + t.logInternal("-- DeleteById of " + descriptor.getName() + " ids[" + idList + "] requires fetch of foreign key values"); + } + List beanList = server.findList(q, t); + deleteList(beanList, t); + return beanList.size(); + + } else { + q.where().idEq(id); + if (t.isLogSummary()) { + t.logInternal("-- DeleteById of " + descriptor.getName() + " id[" + id + "] requires fetch of foreign key values"); + } + Object bean = server.findUnique(q, t); + if (bean == null) { + return 0; + } else { + delete(bean, t); + return 1; + } + } + } + } + + if (t.isPersistCascade()) { + // OneToOne exported side with delete cascade + BeanPropertyAssocOne[] expOnes = descriptor.propertiesOneExportedDelete(); + for (int i = 0; i < expOnes.length; i++) { + BeanDescriptor targetDesc = expOnes[i].getTargetDescriptor(); + if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isUsingL2Cache()) { + SqlUpdate sqlDelete = expOnes[i].deleteByParentId(id, idList); + executeSqlUpdate(sqlDelete, t); + } else { + List childIds = expOnes[i].findIdsByParentId(id, idList, t); + delete(targetDesc, null, childIds, t); + } + } + + // OneToMany's with delete cascade + BeanPropertyAssocMany[] manys = descriptor.propertiesManyDelete(); + for (int i = 0; i < manys.length; i++) { + BeanDescriptor targetDesc = manys[i].getTargetDescriptor(); + if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isUsingL2Cache()) { + // we can just delete children with a single statement + SqlUpdate sqlDelete = manys[i].deleteByParentId(id, idList); + executeSqlUpdate(sqlDelete, t); + } else { + // we need to fetch the Id's to delete (recurse or notify L2 cache/lucene) + List childIds = manys[i].findIdsByParentId(id, idList, t, null); + delete(targetDesc, null, childIds, t); + } + } + } + + // ManyToMany's ... delete from intersection table + BeanPropertyAssocMany[] manys = descriptor.propertiesManyToMany(); + for (int i = 0; i < manys.length; i++) { + SqlUpdate sqlDelete = manys[i].deleteByParentId(id, idList); + if (t.isLogSummary()) { + t.logInternal("-- Deleting intersection table entries: " + manys[i].getFullBeanName()); + } + executeSqlUpdate(sqlDelete, t); + } + + // delete the bean(s) + SqlUpdate deleteById = descriptor.deleteById(id, idList); + if (t.isLogSummary()) { + t.logInternal("-- Deleting " + descriptor.getName() + " Ids" + idList); + } + + // use Id's to update L2 cache rather than Bulk table event + deleteById.setAutoTableMod(false); + if (idList != null) { + t.getEvent().addDeleteByIdList(descriptor, idList); + } else { + t.getEvent().addDeleteById(descriptor, id); + } + return executeSqlUpdate(deleteById, t); + } + + /** + * We need to create and execute a query to get the foreign key values as + * the delete cascades to them (foreign keys). + */ + private Query deleteRequiresQuery(BeanDescriptor desc, BeanPropertyAssocOne[] propImportDelete) { + + Query q = server.createQuery(desc.getBeanType()); + StringBuilder sb = new StringBuilder(30); + for (int i = 0; i < propImportDelete.length; i++) { + sb.append(propImportDelete[i].getName()).append(","); + } + q.setAutofetch(false); + q.select(sb.toString()); + return q; + } + + /** + * Delete the bean. + *

      + * Note that preDelete fires before the deletion of children. + *

      + */ + private void delete(PersistRequestBean request) { + + DeleteUnloadedForeignKeys unloadedForeignKeys = null; + +// boolean pauseIndexInvalidate = request.getBeanDescriptor().isLuceneIndexed(); +// if (pauseIndexInvalidate) { +// // Stop any deletions of child beans to Invalidate the Index as we +// // will use the delete of this top level object to maintain the index +// request.pauseIndexInvalidate(); +// } + + if (request.isPersistCascade()) { + // delete children first ... register the + // bean to handle bi-directional cascading + request.registerDeleteBean(); + deleteAssocMany(request); + request.unregisterDeleteBean(); + + unloadedForeignKeys = getDeleteUnloadedForeignKeys(request); + if (unloadedForeignKeys != null) { + // there are foreign keys that we don't have on this partially + // populated bean so we actually need to query them (to cascade delete) + unloadedForeignKeys.queryForeignKeys(); + } + } + + request.executeOrQueue(); + + if (request.isPersistCascade()) { + deleteAssocOne(request); + + if (unloadedForeignKeys != null) { + unloadedForeignKeys.deleteCascade(); + } + } +// if (pauseIndexInvalidate) { +// request.resumeIndexInvalidate(); +// } + } + + /** + * Save the associated child beans contained in a List. + *

      + * This will automatically copy over any join properties from the parent + * bean to the child beans. + *

      + */ + private void saveAssocMany(boolean insertedParent, PersistRequestBean request) { + + Object parentBean = request.getBean(); + BeanDescriptor desc = request.getBeanDescriptor(); + SpiTransaction t = request.getTransaction(); + + // exported ones with cascade save + BeanPropertyAssocOne[] expOnes = desc.propertiesOneExportedSave(); + for (int i = 0; i < expOnes.length; i++) { + BeanPropertyAssocOne prop = expOnes[i]; + + // check for partial beans + if (request.isLoadedProperty(prop)) { + Object detailBean = prop.getValue(parentBean); + if (detailBean != null) { + if (prop.isSaveRecurseSkippable(detailBean)) { + // skip saving this bean + } else { + t.depth(+1); + saveRecurse(detailBean, t, parentBean); + t.depth(-1); + } + } + } + } + + // many's with cascade save + BeanPropertyAssocMany[] manys = desc.propertiesManySave(); + for (int i = 0; i < manys.length; i++) { + saveMany(new SaveManyPropRequest(insertedParent, manys[i], parentBean, request)); + } + } + + /** + * Helper to wrap the details when saving a OneToMany or ManyToMany + * relationship. + */ + private static class SaveManyPropRequest { + private final boolean insertedParent; + private final BeanPropertyAssocMany many; + private final Object parentBean; + private final SpiTransaction t; + private final boolean cascade; + private final boolean statelessUpdate; + private final boolean deleteMissingChildren; + private final boolean updateNullProperties; + + private SaveManyPropRequest(boolean insertedParent, BeanPropertyAssocMany many, Object parentBean, PersistRequestBean request) { + this.insertedParent = insertedParent; + this.many = many; + this.cascade = many.getCascadeInfo().isSave(); + this.parentBean = parentBean; + this.t = request.getTransaction(); + this.statelessUpdate = request.isStatelessUpdate(); + this.deleteMissingChildren = request.isDeleteMissingChildren(); + this.updateNullProperties = request.isUpdateNullProperties(); + } + + private SaveManyPropRequest(BeanPropertyAssocMany many, Object parentBean, SpiTransaction t) { + this.insertedParent = false; + this.many = many; + this.parentBean = parentBean; + this.t = t; + this.cascade = true; + this.statelessUpdate = false; + this.deleteMissingChildren = false; + this.updateNullProperties = false; + } + + private Object getValueUnderlying() { + return many.getValueUnderlying(parentBean); + } + + private boolean isModifyListenMode() { + return ModifyListenMode.REMOVALS.equals(many.getModifyListenMode()); + } + + private boolean isStatelessUpdate() { + return statelessUpdate; + } + + private boolean isDeleteMissingChildren() { + return deleteMissingChildren; + } + + private boolean isUpdateNullProperties() { + return updateNullProperties; + } + + private boolean isInsertedParent() { + return insertedParent; + } + + private BeanPropertyAssocMany getMany() { + return many; + } + + private Object getParentBean() { + return parentBean; + } + + private SpiTransaction getTransaction() { + return t; + } + + private boolean isCascade() { + return cascade; + } + } + + private void saveMany(SaveManyPropRequest saveMany) { + + if (saveMany.getMany().isManyToMany()) { + // save the beans that are in the manyToMany + if (saveMany.isCascade()) { + // Need explicit Cascade to save the beans on other side + saveAssocManyDetails(saveMany, false, saveMany.isUpdateNullProperties()); + // for ManyToMany save the 'relationship' via inserts/deletes + // into/from the intersection table + saveAssocManyIntersection(saveMany, saveMany.isDeleteMissingChildren()); + } + + } else { + if (saveMany.isCascade()) { + saveAssocManyDetails(saveMany, saveMany.isDeleteMissingChildren(), saveMany.isUpdateNullProperties()); + } + if (saveMany.isModifyListenMode()) { + removeAssocManyPrivateOwned(saveMany); + } + } + } + + private void removeAssocManyPrivateOwned(SaveManyPropRequest saveMany) { + + Object details = saveMany.getValueUnderlying(); + + // check that the list is not null and if it is a BeanCollection + // check that is has been populated (don't trigger lazy loading) + if (details instanceof BeanCollection) { + + BeanCollection c = (BeanCollection) details; + Set modifyRemovals = c.getModifyRemovals(); + if (modifyRemovals != null && !modifyRemovals.isEmpty()) { + + SpiTransaction t = saveMany.getTransaction(); + // increase depth for batching order + t.depth(+1); + for (Object removedBean : modifyRemovals) { + deleteRecurse(removedBean, t); + } + t.depth(-1); + } + } + } + + /** + * Save the details from a OneToMany collection. + */ + private void saveAssocManyDetails(SaveManyPropRequest saveMany, boolean deleteMissingChildren, boolean updateNullProperties) { + + BeanPropertyAssocMany prop = saveMany.getMany(); + + Object details = saveMany.getValueUnderlying(); + + // check that the list is not null and if it is a BeanCollection + // check that is has been populated (don't trigger lazy loading) + Collection collection = getDetailsIterator(details); + + if (collection == null) { + // nothing to do here + return; + } + + if (saveMany.isInsertedParent()) { + // performance optimisation for large collections + prop.getTargetDescriptor().preAllocateIds(collection.size()); + } + + BeanDescriptor targetDescriptor = prop.getTargetDescriptor(); + ArrayList detailIds = null; + if (deleteMissingChildren) { + // collect the Id's (to exclude from deleteManyDetails) + detailIds = new ArrayList(); + } + + // increase depth for batching order + SpiTransaction t = saveMany.getTransaction(); + t.depth(+1); + + // if a map, then we get the key value and + // set it to the appropriate property on the + // detail bean before we save it + boolean isMap = ManyType.JAVA_MAP.equals(prop.getManyType()); + Object parentBean = saveMany.getParentBean(); + Object mapKeyValue = null; + + boolean saveSkippable = prop.isSaveRecurseSkippable(); + boolean skipSavingThisBean = false; + + for (Object detailBean : collection) { + if (isMap) { + // its a map so need the key and value + Map.Entry entry = (Map.Entry) detailBean; + mapKeyValue = entry.getKey(); + detailBean = entry.getValue(); + } + + if (prop.isManyToMany()) { + if (detailBean instanceof EntityBean) { + skipSavingThisBean = ((EntityBean) detailBean)._ebean_getIntercept().isReference(); + } + } else { + // set the 'parent/master' bean to the detailBean as long + // as we don't make it 'dirty' in doing so + if (detailBean instanceof EntityBean) { + EntityBeanIntercept ebi = ((EntityBean) detailBean)._ebean_getIntercept(); + if (ebi.isNewOrDirty()) { + // set the parent bean to detailBean + prop.setJoinValuesToChild(parentBean, detailBean, mapKeyValue); + } else if (ebi.isReference()) { + // we can skip this one + skipSavingThisBean = true; + + } else { + // unmodified so skip depending on prop.isSaveRecurseSkippable(); + skipSavingThisBean = saveSkippable; + } + } else { + // set the parent bean to detailBean + prop.setJoinValuesToChild(parentBean, detailBean, mapKeyValue); + } + } + + if (skipSavingThisBean) { + // unmodified bean that does not recurse its save + // so we can skip the save for this bean. + // Reset skipSavingThisBean for the next detailBean + skipSavingThisBean = false; + + } else if (!saveMany.isStatelessUpdate()) { + // normal save recurse + saveRecurse(detailBean, t, parentBean); + + } else { + if (targetDescriptor.isStatelessUpdate(detailBean)) { + // update based on the value of Version/Id properties + // cascade update in stateless mode + forceUpdate(detailBean, null, t, deleteMissingChildren, updateNullProperties); + } else { + // cascade insert + forceInsert(detailBean, t); + } + } + + if (detailIds != null) { + // remember the Id (other details not in the collection) will be removed + Object id = targetDescriptor.getId(detailBean); + if (!DmlUtil.isNullOrZero(id)) { + detailIds.add(id); + } + } + } + + if (detailIds != null) { + deleteManyDetails(t, prop.getBeanDescriptor(), parentBean, prop, detailIds); + } + + t.depth(-1); + + } + + public int deleteManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) { + + BeanDescriptor descriptor = beanDescriptorManager.getBeanDescriptor(ownerBean.getClass()); + BeanPropertyAssocMany prop = (BeanPropertyAssocMany) descriptor.getBeanProperty(propertyName); + return deleteAssocManyIntersection(ownerBean, prop, t); + } + + public void saveManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) { + + BeanDescriptor descriptor = beanDescriptorManager.getBeanDescriptor(ownerBean.getClass()); + BeanPropertyAssocMany prop = (BeanPropertyAssocMany) descriptor.getBeanProperty(propertyName); + + saveAssocManyIntersection(new SaveManyPropRequest(prop, ownerBean, (SpiTransaction) t), false); + } + + public void saveAssociation(Object parentBean, String propertyName, Transaction t) { + + BeanDescriptor descriptor = beanDescriptorManager.getBeanDescriptor(parentBean.getClass()); + SpiTransaction trans = (SpiTransaction) t; + + BeanProperty prop = descriptor.getBeanProperty(propertyName); + if (prop == null) { + String msg = "Could not find property [" + propertyName + "] on bean " + parentBean.getClass(); + throw new PersistenceException(msg); + } + + if (prop instanceof BeanPropertyAssocMany) { + BeanPropertyAssocMany manyProp = (BeanPropertyAssocMany) prop; + saveMany(new SaveManyPropRequest(manyProp, parentBean, (SpiTransaction) t)); + + } else if (prop instanceof BeanPropertyAssocOne) { + BeanPropertyAssocOne oneProp = (BeanPropertyAssocOne) prop; + Object assocBean = oneProp.getValue(parentBean); + + int depth = oneProp.isOneToOneExported() ? 1 : -1; + int revertDepth = -1 * depth; + + trans.depth(depth); + saveRecurse(assocBean, t, parentBean); + trans.depth(revertDepth); + + } else { + String msg = "Expecting [" + prop.getFullBeanName() + "] to be a OneToMany, OneToOne, ManyToOne or ManyToMany property?"; + throw new PersistenceException(msg); + } + + } + + /** + * Save the additions and removals from a ManyToMany collection as inserts + * and deletes from the intersection table. + *

      + * This is done via MapBeans. + *

      + */ + private void saveAssocManyIntersection(SaveManyPropRequest saveManyPropRequest, boolean deleteMissingChildren) { + + BeanPropertyAssocMany prop = saveManyPropRequest.getMany(); + Object value = prop.getValueUnderlying(saveManyPropRequest.getParentBean()); + if (value == null) { + return; + } + + SpiTransaction t = saveManyPropRequest.getTransaction(); + Collection additions = null; + Collection deletions = null; + + boolean vanillaCollection = (value instanceof BeanCollection == false); + + if (vanillaCollection || deleteMissingChildren) { + // delete all intersection rows and then treat all + // beans in the collection as additions + deleteAssocManyIntersection(saveManyPropRequest.getParentBean(), prop, t); + } + + if (saveManyPropRequest.isInsertedParent() || vanillaCollection || deleteMissingChildren) { + // treat everything in the list/set/map as an intersection addition + if (value instanceof Map) { + additions = ((Map) value).values(); + } else if (value instanceof Collection) { + additions = (Collection) value; + } else { + String msg = "Unhandled ManyToMany type " + value.getClass().getName() + " for " + prop.getFullBeanName(); + throw new PersistenceException(msg); + } + if (!vanillaCollection) { + ((BeanCollection) value).modifyReset(); + } + } else { + // BeanCollection so get the additions/deletions + BeanCollection manyValue = (BeanCollection) value; + additions = manyValue.getModifyAdditions(); + deletions = manyValue.getModifyRemovals(); + // reset so the changes are only processed once + manyValue.modifyReset(); + } + + t.depth(+1); + + if (additions != null && !additions.isEmpty()) { + for (Object otherBean : additions) { + // the object from the 'other' side of the ManyToMany + if (deletions != null && deletions.remove(otherBean)) { + String m = "Inserting and Deleting same object? " + otherBean; + if (t.isLogSummary()) { + t.logInternal(m); + } + logger.log(Level.WARNING, m); + + } else { + if (!prop.hasImportedId(otherBean)) { + String msg = "ManyToMany bean " + otherBean + " does not have an Id value."; + throw new PersistenceException(msg); + + } else { + // build a intersection row for 'insert' + IntersectionRow intRow = prop.buildManyToManyMapBean(saveManyPropRequest.getParentBean(), otherBean); + SqlUpdate sqlInsert = intRow.createInsert(server); + executeSqlUpdate(sqlInsert, t); + } + } + } + } + if (deletions != null && !deletions.isEmpty()) { + for (Object otherDelete : deletions) { + // the object from the 'other' side of the ManyToMany + // build a intersection row for 'delete' + IntersectionRow intRow = prop.buildManyToManyMapBean(saveManyPropRequest.getParentBean(), otherDelete); + SqlUpdate sqlDelete = intRow.createDelete(server); + executeSqlUpdate(sqlDelete, t); + } + } + + // decrease the depth back to what it was + t.depth(-1); + } + + private int deleteAssocManyIntersection(Object bean, BeanPropertyAssocMany many, Transaction t) { + + // delete all intersection rows for this bean + IntersectionRow intRow = many.buildManyToManyDeleteChildren(bean); + SqlUpdate sqlDelete = intRow.createDeleteChildren(server); + + return executeSqlUpdate(sqlDelete, t); + } + + /** + * Delete beans in any associated many. + *

      + * This is called prior to deleting the parent bean. + *

      + */ + private void deleteAssocMany(PersistRequestBean request) { + + SpiTransaction t = request.getTransaction(); + t.depth(-1); + + BeanDescriptor desc = request.getBeanDescriptor(); + Object parentBean = request.getBean(); + + BeanPropertyAssocOne[] expOnes = desc.propertiesOneExportedDelete(); + if (expOnes.length > 0) { + + DeleteUnloadedForeignKeys unloaded = null; + for (int i = 0; i < expOnes.length; i++) { + BeanPropertyAssocOne prop = expOnes[i]; + if (request.isLoadedProperty(prop)) { + Object detailBean = prop.getValue(parentBean); + if (detailBean != null) { + deleteRecurse(detailBean, t); + } + } else { + if (unloaded == null) { + unloaded = new DeleteUnloadedForeignKeys(server, request); + } + unloaded.add(prop); + } + } + if (unloaded != null) { + unloaded.queryForeignKeys(); + unloaded.deleteCascade(); + } + } + + // Many's with delete cascade + BeanPropertyAssocMany[] manys = desc.propertiesManyDelete(); + for (int i = 0; i < manys.length; i++) { + if (manys[i].isManyToMany()) { + // delete associated rows from intersection table + deleteAssocManyIntersection(parentBean, manys[i], t); + + } else { + + if (ModifyListenMode.REMOVALS.equals(manys[i].getModifyListenMode())) { + // PrivateOwned ... + Object details = manys[i].getValueUnderlying(parentBean); + if (details instanceof BeanCollection) { + Set modifyRemovals = ((BeanCollection) details).getModifyRemovals(); + if (modifyRemovals != null && !modifyRemovals.isEmpty()) { + + // delete the orphans that have been removed from the collection + for (Object detailBean : modifyRemovals) { + if (manys[i].hasId(detailBean)) { + deleteRecurse(detailBean, t); + } + } + } + } + } + + deleteManyDetails(t, desc, parentBean, manys[i], null); + } + } + + // restore the depth + t.depth(+1); + } + + /** + * Delete the 'many' detail beans for a given parent bean. + *

      + * For stateless updates this deletes details beans that are no longer in + * the many - the excludeDetailIds holds the detail beans that are in the + * collection (and should not be deleted). + *

      + */ + private void deleteManyDetails(SpiTransaction t, BeanDescriptor desc, Object parentBean, + BeanPropertyAssocMany many, ArrayList excludeDetailIds) { + + if (many.getCascadeInfo().isDelete()) { + // cascade delete the beans in the collection + BeanDescriptor targetDesc = many.getTargetDescriptor(); + if (targetDesc.isDeleteRecurseSkippable() && !targetDesc.isUsingL2Cache()) { + // Just delete all the children with one statement + IntersectionRow intRow = many.buildManyDeleteChildren(parentBean, excludeDetailIds); + SqlUpdate sqlDelete = intRow.createDelete(server); + executeSqlUpdate(sqlDelete, t); + + } else { + // Delete recurse using the Id values of the children + Object parentId = desc.getId(parentBean); + List idsByParentId = many.findIdsByParentId(parentId, null, t, excludeDetailIds); + if (!idsByParentId.isEmpty()) { + delete(targetDesc, null, idsByParentId, t); + } + } + } + } + + /** + * Save any associated one beans. + */ + private void saveAssocOne(PersistRequestBean request) { + + BeanDescriptor desc = request.getBeanDescriptor(); + + // imported ones with save cascade + BeanPropertyAssocOne[] ones = desc.propertiesOneImportedSave(); + + for (int i = 0; i < ones.length; i++) { + BeanPropertyAssocOne prop = ones[i]; + + // check for partial objects + if (request.isLoadedProperty(prop)) { + Object detailBean = prop.getValue(request.getBean()); + if (detailBean != null) { + if (isReference(detailBean)) { + // skip saving a reference + } else if (request.isParent(detailBean)) { + // skip saving the parent as already saved + } else if (prop.isSaveRecurseSkippable(detailBean)) { + // we can skip saving this bean + + } else { + SpiTransaction t = request.getTransaction(); + t.depth(-1); + saveRecurse(detailBean, t, null); + t.depth(+1); + } + } + } + } + } + + /** + * Return true if the bean is a reference. + */ + private boolean isReference(Object bean) { + return (bean instanceof EntityBean) && ((EntityBean) bean)._ebean_getIntercept().isReference(); + } + + /** + * Support for loading any Imported Associated One properties that are not + * loaded but required for Delete cascade. + */ + private DeleteUnloadedForeignKeys getDeleteUnloadedForeignKeys(PersistRequestBean request) { + + DeleteUnloadedForeignKeys fkeys = null; + + BeanPropertyAssocOne[] ones = request.getBeanDescriptor().propertiesOneImportedDelete(); + for (int i = 0; i < ones.length; i++) { + if (!request.isLoadedProperty(ones[i])) { + // we have cascade Delete on a partially populated bean and + // this property was not loaded (so we are going to have to fetch it) + if (fkeys == null) { + fkeys = new DeleteUnloadedForeignKeys(server, request); + } + fkeys.add(ones[i]); + } + } + + return fkeys; + } + + /** + * Delete any associated one beans. + */ + private void deleteAssocOne(PersistRequestBean request) { + + BeanDescriptor desc = request.getBeanDescriptor(); + BeanPropertyAssocOne[] ones = desc.propertiesOneImportedDelete(); + + for (int i = 0; i < ones.length; i++) { + BeanPropertyAssocOne prop = ones[i]; + if (!request.isLoadedProperty(prop)) { + // handled by DeleteUnloadedForeignKeys that was built + // via getDeleteUnloadedForeignKeys(); + } else { + Object detailBean = prop.getValue(request.getBean()); + if (detailBean != null && prop.hasId(detailBean)) { + deleteRecurse(detailBean, request.getTransaction()); + } + } + } + } + + /** + * Set Id Generated value for insert. + */ + private void setIdGenValue(PersistRequestBean request) { + + BeanDescriptor desc = request.getBeanDescriptor(); + if (!desc.isUseIdGenerator()) { + return; + } + + BeanProperty idProp = desc.getSingleIdProperty(); + if (idProp == null || idProp.isEmbedded()) { + // not supporting IdGeneration for concatenated or Embedded + return; + } + + Object bean = request.getBean(); + Object uid = idProp.getValue(bean); + + if (DmlUtil.isNullOrZero(uid)) { + + // generate the nextId and set it to the property + Object nextId = desc.nextId(request.getTransaction()); + + // cast the data type if required and set it + desc.convertSetId(nextId, bean); + } + } + + /** + * Return the details of the collection or map taking care to avoid + * unnecessary fetching of the data. + */ + private Collection getDetailsIterator(Object o) { + if (o == null) { + return null; + } + if (o instanceof BeanCollection) { + BeanCollection bc = (BeanCollection) o; + if (!bc.isPopulated()) { + return null; + } + return bc.getActualDetails(); + } + + if (o instanceof Map) { + // yes, we want the entrySet (to set the keys) + return ((Map) o).entrySet(); + + } else if (o instanceof Collection) { + return ((Collection) o); + } + String m = "expecting a Map or Collection but got [" + o.getClass().getName() + "]"; + throw new PersistenceException(m); + } + + /** + * Create the Persist Request Object that wraps all the objects used to + * perform an insert, update or delete. + */ + @SuppressWarnings("unchecked") + private PersistRequestBean createRequest(T bean, Transaction t, Object parentBean) { + BeanManager mgr = getBeanManager(bean); + if (mgr == null) { + throw new PersistenceException(errNotRegistered(bean.getClass())); + } + return (PersistRequestBean) createRequest(bean, t, parentBean, mgr); + } + + private String errNotRegistered(Class beanClass) { + String msg = "The type [" + beanClass + "] is not a registered entity?"; + msg += " If you don't explicitly list the entity classes to use Ebean will search for them in the classpath."; + msg += " If the entity is in a Jar check the ebean.search.jars property in ebean.properties file or check ServerConfig.addJar()."; + return msg; + } + + /** + * Create the Persist Request Object that wraps all the objects used to + * perform an insert, update or delete. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + private PersistRequestBean createRequest(Object bean, Transaction t, Object parentBean, BeanManager mgr) { + + if (mgr.isLdapEntityType()) { + return new LdapPersistBeanRequest(server, bean, parentBean, mgr, ldapPersister); + } + return new PersistRequestBean(server, bean, parentBean, mgr, (SpiTransaction) t, persistExecute); + } + + /** + * Return the BeanDescriptor for a bean that is being persisted. + *

      + * Note that this checks to see if the bean is a MapBean with a tableName. + * If so it will return the table based BeanDescriptor. + *

      + */ + @SuppressWarnings("unchecked") + private BeanManager getBeanManager(T bean) { + + return (BeanManager) beanDescriptorManager.getBeanManager(bean.getClass()); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/DeleteUnloadedForeignKeys.java b/src/main/java/com/avaje/ebeaninternal/server/persist/DeleteUnloadedForeignKeys.java index 864a35eea..fd3285285 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/DeleteUnloadedForeignKeys.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/DeleteUnloadedForeignKeys.java @@ -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.persist; - -import java.util.ArrayList; -import java.util.List; - -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext; - -/** - * Used for deletion of a partially populated bean where some cascade delete - * properties where not loaded. - *

      - * This bean effectively holds the foreign properties that where not loaded, and - * helps fetch the foreign keys and delete the appropriate rows. - *

      - * - * @author rbygrave - */ -public class DeleteUnloadedForeignKeys { - - private final List> propList = new ArrayList>(4); - - private final SpiEbeanServer server; - - private final PersistRequestBean request; - - private Object beanWithForeignKeys; - - public DeleteUnloadedForeignKeys(SpiEbeanServer server, PersistRequestBean request) { - this.server = server; - this.request = request; - } - - public boolean isEmpty() { - return propList.isEmpty(); - } - - public void add(BeanPropertyAssocOne prop) { - propList.add(prop); - } - - /** - * Execute a query fetching the missing (unloaded) foreign keys. We need to - * fetch these key values before the parent bean is deleted. - */ - public void queryForeignKeys() { - - BeanDescriptor descriptor = request.getBeanDescriptor(); - SpiQuery q = (SpiQuery) server.createQuery(descriptor.getBeanType()); - - Object id = request.getBeanId(); - - StringBuilder sb = new StringBuilder(30); - for (int i = 0; i < propList.size(); i++) { - sb.append(propList.get(i).getName()).append(","); - } - - // run query in a separate persistence context - q.setPersistenceContext(new DefaultPersistenceContext()); - q.setAutofetch(false); - q.select(sb.toString()); - q.where().idEq(id); - - SpiTransaction t = request.getTransaction(); - if (t.isLogSummary()) { - t.logInternal("-- Ebean fetching foreign key values for delete of " + descriptor.getName() + " id:" + id); - } - beanWithForeignKeys = server.findUnique(q, t); - } - - /** - * Delete the rows relating to the foreign keys. These deletions occur after - * the parent bean has been deleted. - */ - public void deleteCascade() { - - for (int i = 0; i < propList.size(); i++) { - BeanPropertyAssocOne prop = propList.get(i); - Object detailBean = prop.getValue(beanWithForeignKeys); - - // if bean exists with a unique id then delete it - if (detailBean != null && prop.hasId(detailBean)) { - server.delete(detailBean, request.getTransaction()); - } - } - } -} +package com.avaje.ebeaninternal.server.persist; + +import java.util.ArrayList; +import java.util.List; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext; + +/** + * Used for deletion of a partially populated bean where some cascade delete + * properties where not loaded. + *

      + * This bean effectively holds the foreign properties that where not loaded, and + * helps fetch the foreign keys and delete the appropriate rows. + *

      + * + * @author rbygrave + */ +public class DeleteUnloadedForeignKeys { + + private final List> propList = new ArrayList>(4); + + private final SpiEbeanServer server; + + private final PersistRequestBean request; + + private Object beanWithForeignKeys; + + public DeleteUnloadedForeignKeys(SpiEbeanServer server, PersistRequestBean request) { + this.server = server; + this.request = request; + } + + public boolean isEmpty() { + return propList.isEmpty(); + } + + public void add(BeanPropertyAssocOne prop) { + propList.add(prop); + } + + /** + * Execute a query fetching the missing (unloaded) foreign keys. We need to + * fetch these key values before the parent bean is deleted. + */ + public void queryForeignKeys() { + + BeanDescriptor descriptor = request.getBeanDescriptor(); + SpiQuery q = (SpiQuery) server.createQuery(descriptor.getBeanType()); + + Object id = request.getBeanId(); + + StringBuilder sb = new StringBuilder(30); + for (int i = 0; i < propList.size(); i++) { + sb.append(propList.get(i).getName()).append(","); + } + + // run query in a separate persistence context + q.setPersistenceContext(new DefaultPersistenceContext()); + q.setAutofetch(false); + q.select(sb.toString()); + q.where().idEq(id); + + SpiTransaction t = request.getTransaction(); + if (t.isLogSummary()) { + t.logInternal("-- Ebean fetching foreign key values for delete of " + descriptor.getName() + " id:" + id); + } + beanWithForeignKeys = server.findUnique(q, t); + } + + /** + * Delete the rows relating to the foreign keys. These deletions occur after + * the parent bean has been deleted. + */ + public void deleteCascade() { + + for (int i = 0; i < propList.size(); i++) { + BeanPropertyAssocOne prop = propList.get(i); + Object detailBean = prop.getValue(beanWithForeignKeys); + + // if bean exists with a unique id then delete it + if (detailBean != null && prop.hasId(detailBean)) { + server.delete(detailBean, request.getTransaction()); + } + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeCallableSql.java b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeCallableSql.java index 8a84b8073..4c41a45fe 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeCallableSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeCallableSql.java @@ -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.persist; - -import java.sql.CallableStatement; -import java.sql.SQLException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.api.BindParams; -import com.avaje.ebeaninternal.api.SpiCallableSql; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql; -import com.avaje.ebeaninternal.server.core.PstmtBatch; -import com.avaje.ebeaninternal.server.type.DataBind; -import com.avaje.ebeaninternal.server.util.BindParamsParser; - -/** - * Handles the execution of CallableSql requests. - */ -public class ExeCallableSql { - - private static final Logger logger = Logger.getLogger(ExeCallableSql.class.getName()); - - private final Binder binder; - - private final PstmtFactory pstmtFactory; - - public ExeCallableSql(Binder binder, PstmtBatch pstmtBatch) { - this.binder = binder; - // no batch support for CallableStatement in Oracle anyway - this.pstmtFactory = new PstmtFactory(null); - } - - /** - * execute the CallableSql requests. - */ - public int execute(PersistRequestCallableSql request) { - - SpiTransaction t = request.getTransaction(); - - boolean batchThisRequest = t.isBatchThisRequest(); - - CallableStatement cstmt = null; - try { - - cstmt = bindStmt(request, batchThisRequest); - - if (batchThisRequest){ - cstmt.addBatch(); - // return -1 to indicate batch mode - return -1; - - } else { - // handles executeOverride() and also - // reading of registered OUT parameters - int rowCount = request.executeUpdate(); - request.postExecute(); - return rowCount; - - } - - } catch (SQLException ex) { - throw new PersistenceException(ex); - - } finally { - if (!batchThisRequest && cstmt != null) { - try { - cstmt.close(); - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - } - } - } - - - private CallableStatement bindStmt(PersistRequestCallableSql request, boolean batchThisRequest) throws SQLException { - - SpiCallableSql callableSql = request.getCallableSql(); - SpiTransaction t = request.getTransaction(); - - String sql = callableSql.getSql(); - - BindParams bindParams = callableSql.getBindParams(); - - // process named parameters if required - sql = BindParamsParser.parse(bindParams, sql); - - boolean logSql = request.isLogSql(); - - CallableStatement cstmt; - if (batchThisRequest){ - cstmt = pstmtFactory.getCstmt(t, logSql, sql, request); - - } else { - if (logSql){ - t.logInternal(sql); - } - cstmt = pstmtFactory.getCstmt(t, sql); - } - - if (callableSql.getTimeout() > 0){ - cstmt.setQueryTimeout(callableSql.getTimeout()); - } - - String bindLog = null; - if (!bindParams.isEmpty()){ - bindLog = binder.bind(bindParams, new DataBind(cstmt)); - } - - request.setBindLog(bindLog); - - // required to read OUT params later - request.setBound(bindParams, cstmt); - - return cstmt; - } -} +package com.avaje.ebeaninternal.server.persist; + +import java.sql.CallableStatement; +import java.sql.SQLException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.api.BindParams; +import com.avaje.ebeaninternal.api.SpiCallableSql; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql; +import com.avaje.ebeaninternal.server.core.PstmtBatch; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.util.BindParamsParser; + +/** + * Handles the execution of CallableSql requests. + */ +public class ExeCallableSql { + + private static final Logger logger = Logger.getLogger(ExeCallableSql.class.getName()); + + private final Binder binder; + + private final PstmtFactory pstmtFactory; + + public ExeCallableSql(Binder binder, PstmtBatch pstmtBatch) { + this.binder = binder; + // no batch support for CallableStatement in Oracle anyway + this.pstmtFactory = new PstmtFactory(null); + } + + /** + * execute the CallableSql requests. + */ + public int execute(PersistRequestCallableSql request) { + + SpiTransaction t = request.getTransaction(); + + boolean batchThisRequest = t.isBatchThisRequest(); + + CallableStatement cstmt = null; + try { + + cstmt = bindStmt(request, batchThisRequest); + + if (batchThisRequest){ + cstmt.addBatch(); + // return -1 to indicate batch mode + return -1; + + } else { + // handles executeOverride() and also + // reading of registered OUT parameters + int rowCount = request.executeUpdate(); + request.postExecute(); + return rowCount; + + } + + } catch (SQLException ex) { + throw new PersistenceException(ex); + + } finally { + if (!batchThisRequest && cstmt != null) { + try { + cstmt.close(); + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + } + } + } + + + private CallableStatement bindStmt(PersistRequestCallableSql request, boolean batchThisRequest) throws SQLException { + + SpiCallableSql callableSql = request.getCallableSql(); + SpiTransaction t = request.getTransaction(); + + String sql = callableSql.getSql(); + + BindParams bindParams = callableSql.getBindParams(); + + // process named parameters if required + sql = BindParamsParser.parse(bindParams, sql); + + boolean logSql = request.isLogSql(); + + CallableStatement cstmt; + if (batchThisRequest){ + cstmt = pstmtFactory.getCstmt(t, logSql, sql, request); + + } else { + if (logSql){ + t.logInternal(sql); + } + cstmt = pstmtFactory.getCstmt(t, sql); + } + + if (callableSql.getTimeout() > 0){ + cstmt.setQueryTimeout(callableSql.getTimeout()); + } + + String bindLog = null; + if (!bindParams.isEmpty()){ + bindLog = binder.bind(bindParams, new DataBind(cstmt)); + } + + request.setBindLog(bindLog); + + // required to read OUT params later + request.setBound(bindParams, cstmt); + + return cstmt; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeOrmUpdate.java b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeOrmUpdate.java index e1da81951..c530cb264 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeOrmUpdate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeOrmUpdate.java @@ -1,160 +1,141 @@ -/** - * 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.persist; - -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.api.BindParams; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.api.SpiUpdate; -import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate; -import com.avaje.ebeaninternal.server.core.PstmtBatch; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.type.DataBind; -import com.avaje.ebeaninternal.server.util.BindParamsParser; - -/** - * Executes the UpdateSql requests. - */ -public class ExeOrmUpdate { - - private static final Logger logger = Logger.getLogger(ExeOrmUpdate.class.getName()); - - private final Binder binder; - - private final PstmtFactory pstmtFactory; - - /** - * Create with a given binder. - */ - public ExeOrmUpdate(Binder binder, PstmtBatch pstmtBatch) { - this.pstmtFactory = new PstmtFactory(pstmtBatch); - this.binder = binder; - } - - /** - * Execute the orm update request. - */ - public int execute(PersistRequestOrmUpdate request) { - - SpiTransaction t = request.getTransaction(); - - boolean batchThisRequest = t.isBatchThisRequest(); - - PreparedStatement pstmt = null; - try { - - pstmt = bindStmt(request, batchThisRequest); - - if (batchThisRequest){ - PstmtBatch pstmtBatch = request.getPstmtBatch(); - if (pstmtBatch != null){ - pstmtBatch.addBatch(pstmt); - } else { - pstmt.addBatch(); - } - // return -1 to indicate batch mode - return -1; - - } else { - SpiUpdate ormUpdate = request.getOrmUpdate(); - if (ormUpdate.getTimeout() > 0){ - pstmt.setQueryTimeout(ormUpdate.getTimeout()); - } - - int rowCount = pstmt.executeUpdate(); - request.checkRowCount(rowCount); - request.postExecute(); - return rowCount; - - } - - } catch (SQLException ex) { - SpiUpdate ormUpdate = request.getOrmUpdate(); - String msg = "Error executing: "+ormUpdate.getGeneratedSql(); - throw new PersistenceException(msg, ex); - - } finally { - if (!batchThisRequest && pstmt != null) { - try { - pstmt.close(); - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - } - } - } - - /** - * Convert bean and property names to db table and columns. - */ - private String translate(PersistRequestOrmUpdate request, String sql) { - - BeanDescriptor descriptor = request.getBeanDescriptor(); - return descriptor.convertOrmUpdateToSql(sql); - } - - private PreparedStatement bindStmt(PersistRequestOrmUpdate request, boolean batchThisRequest) throws SQLException { - - SpiUpdate ormUpdate = request.getOrmUpdate(); - SpiTransaction t = request.getTransaction(); - - String sql = ormUpdate.getUpdateStatement(); - - // convert bean and property names to table and - // column names if required - sql = translate(request, sql); - - BindParams bindParams = ormUpdate.getBindParams(); - - // process named parameters if required - sql = BindParamsParser.parse(bindParams, sql); - - ormUpdate.setGeneratedSql(sql); - - boolean logSql = request.isLogSql(); - - PreparedStatement pstmt; - if (batchThisRequest){ - pstmt = pstmtFactory.getPstmt(t, logSql, sql, request); - - } else { - if (logSql){ - t.logInternal(sql); - } - pstmt = pstmtFactory.getPstmt(t, sql); - } - - String bindLog = null; - if (!bindParams.isEmpty()){ - bindLog = binder.bind(bindParams, new DataBind(pstmt)); - } - - request.setBindLog(bindLog); - - return pstmt; - } - -} +package com.avaje.ebeaninternal.server.persist; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.api.BindParams; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.api.SpiUpdate; +import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate; +import com.avaje.ebeaninternal.server.core.PstmtBatch; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.util.BindParamsParser; + +/** + * Executes the UpdateSql requests. + */ +public class ExeOrmUpdate { + + private static final Logger logger = Logger.getLogger(ExeOrmUpdate.class.getName()); + + private final Binder binder; + + private final PstmtFactory pstmtFactory; + + /** + * Create with a given binder. + */ + public ExeOrmUpdate(Binder binder, PstmtBatch pstmtBatch) { + this.pstmtFactory = new PstmtFactory(pstmtBatch); + this.binder = binder; + } + + /** + * Execute the orm update request. + */ + public int execute(PersistRequestOrmUpdate request) { + + SpiTransaction t = request.getTransaction(); + + boolean batchThisRequest = t.isBatchThisRequest(); + + PreparedStatement pstmt = null; + try { + + pstmt = bindStmt(request, batchThisRequest); + + if (batchThisRequest){ + PstmtBatch pstmtBatch = request.getPstmtBatch(); + if (pstmtBatch != null){ + pstmtBatch.addBatch(pstmt); + } else { + pstmt.addBatch(); + } + // return -1 to indicate batch mode + return -1; + + } else { + SpiUpdate ormUpdate = request.getOrmUpdate(); + if (ormUpdate.getTimeout() > 0){ + pstmt.setQueryTimeout(ormUpdate.getTimeout()); + } + + int rowCount = pstmt.executeUpdate(); + request.checkRowCount(rowCount); + request.postExecute(); + return rowCount; + + } + + } catch (SQLException ex) { + SpiUpdate ormUpdate = request.getOrmUpdate(); + String msg = "Error executing: "+ormUpdate.getGeneratedSql(); + throw new PersistenceException(msg, ex); + + } finally { + if (!batchThisRequest && pstmt != null) { + try { + pstmt.close(); + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + } + } + } + + /** + * Convert bean and property names to db table and columns. + */ + private String translate(PersistRequestOrmUpdate request, String sql) { + + BeanDescriptor descriptor = request.getBeanDescriptor(); + return descriptor.convertOrmUpdateToSql(sql); + } + + private PreparedStatement bindStmt(PersistRequestOrmUpdate request, boolean batchThisRequest) throws SQLException { + + SpiUpdate ormUpdate = request.getOrmUpdate(); + SpiTransaction t = request.getTransaction(); + + String sql = ormUpdate.getUpdateStatement(); + + // convert bean and property names to table and + // column names if required + sql = translate(request, sql); + + BindParams bindParams = ormUpdate.getBindParams(); + + // process named parameters if required + sql = BindParamsParser.parse(bindParams, sql); + + ormUpdate.setGeneratedSql(sql); + + boolean logSql = request.isLogSql(); + + PreparedStatement pstmt; + if (batchThisRequest){ + pstmt = pstmtFactory.getPstmt(t, logSql, sql, request); + + } else { + if (logSql){ + t.logInternal(sql); + } + pstmt = pstmtFactory.getPstmt(t, sql); + } + + String bindLog = null; + if (!bindParams.isEmpty()){ + bindLog = binder.bind(bindParams, new DataBind(pstmt)); + } + + request.setBindLog(bindLog); + + return pstmt; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeUpdateSql.java b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeUpdateSql.java index 25aea1350..ae9b1df64 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/ExeUpdateSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/ExeUpdateSql.java @@ -1,218 +1,199 @@ -/** - * 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.persist; - -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.api.BindParams; -import com.avaje.ebeaninternal.api.SpiSqlUpdate; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql; -import com.avaje.ebeaninternal.server.core.PstmtBatch; -import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql.SqlType; -import com.avaje.ebeaninternal.server.type.DataBind; -import com.avaje.ebeaninternal.server.util.BindParamsParser; - -/** - * Executes the UpdateSql requests. - */ -public class ExeUpdateSql { - - private static final Logger logger = Logger.getLogger(ExeUpdateSql.class.getName()); - - private final Binder binder; - - private final PstmtFactory pstmtFactory; - - private final PstmtBatch pstmtBatch; - - //TODO: get defaultBatchSize - private int defaultBatchSize = 20; - - /** - * Create with a given binder. - */ - public ExeUpdateSql(Binder binder, PstmtBatch pstmtBatch) { - this.binder = binder; - this.pstmtBatch = pstmtBatch; - this.pstmtFactory = new PstmtFactory(pstmtBatch); - } - - /** - * Execute the UpdateSql request. - */ - public int execute(PersistRequestUpdateSql request) { - - SpiTransaction t = request.getTransaction(); - - boolean batchThisRequest = t.isBatchThisRequest(); - - PreparedStatement pstmt = null; - try { - - pstmt = bindStmt(request, batchThisRequest); - - if (batchThisRequest){ - if (pstmtBatch != null){ - pstmtBatch.addBatch(pstmt); - } else { - pstmt.addBatch(); - } - // return -1 to indicate batch mode - return -1; - - } else { - int rowCount = pstmt.executeUpdate(); - request.checkRowCount(rowCount); - request.postExecute(); - return rowCount; - - } - - } catch (SQLException ex) { - throw new PersistenceException(ex); - - } finally { - if (!batchThisRequest && pstmt != null) { - try { - pstmt.close(); - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - } - } - } - - - private PreparedStatement bindStmt(PersistRequestUpdateSql request, boolean batchThisRequest) throws SQLException { - - SpiSqlUpdate updateSql = request.getUpdateSql(); - SpiTransaction t = request.getTransaction(); - - String sql = updateSql.getSql(); - - BindParams bindParams = updateSql.getBindParams(); - - // process named parameters if required - sql = BindParamsParser.parse(bindParams, sql); - - boolean logSql = request.isLogSql(); - - PreparedStatement pstmt; - if (batchThisRequest){ - pstmt = pstmtFactory.getPstmt(t, logSql, sql, request); - if (pstmtBatch != null){ - // oracle specific JDBC setting batch size ahead of time - int batchSize = t.getBatchSize(); - if (batchSize < 1){ - batchSize = defaultBatchSize; - } - pstmtBatch.setBatchSize(pstmt, batchSize); - } - - } else { - if (logSql){ - t.logInternal(sql); - } - pstmt = pstmtFactory.getPstmt(t, sql); - } - - if (updateSql.getTimeout() > 0){ - pstmt.setQueryTimeout(updateSql.getTimeout()); - } - - String bindLog = null; - if (!bindParams.isEmpty()){ - bindLog = binder.bind(bindParams, new DataBind(pstmt)); - } - - request.setBindLog(bindLog); - - // derive the statement type (for TransactionEvent) - parseUpdate(sql, request); - - return pstmt; - } - - - private void determineType(String word1, String word2, String word3, PersistRequestUpdateSql request) { - if (word1.equalsIgnoreCase("UPDATE")) { - request.setType(SqlType.SQL_UPDATE, word2, "UpdateSql"); - - } else if (word1.equalsIgnoreCase("DELETE")) { - request.setType(SqlType.SQL_DELETE, word3, "DeleteSql"); - - } else if (word1.equalsIgnoreCase("INSERT")) { - request.setType(SqlType.SQL_INSERT, word3, "InsertSql"); - - } else { - request.setType(SqlType.SQL_UNKNOWN, null, "UnknownSql"); - - } - } - - private void parseUpdate(String sql, PersistRequestUpdateSql request) { - - int start = ltrim(sql); - - int[] pos = new int[3]; - int spaceCount = 0; - - int len = sql.length(); - for (int i = start; i < len; i++) { - char c = sql.charAt(i); - if (Character.isWhitespace(c)) { - pos[spaceCount] = i; - spaceCount++; - if (spaceCount > 2){ - break; - } - } - } - - String firstWord = sql.substring(0, pos[0]); - String secWord = sql.substring(pos[0]+1, pos[1]); - String thirdWord; - if (pos[2] == 0){ - // there is nothing after the table name - thirdWord = sql.substring(pos[1]+1); - } else { - thirdWord = sql.substring(pos[1]+1, pos[2]); - } - - determineType(firstWord, secWord, thirdWord, request); - } - - private int ltrim(String s) { - int len = s.length(); - int i = 0; - for (i = 0; i < len; i++) { - if (!Character.isWhitespace(s.charAt(i))) { - return i; - } - } - return 0; - } -} +package com.avaje.ebeaninternal.server.persist; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.api.BindParams; +import com.avaje.ebeaninternal.api.SpiSqlUpdate; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql; +import com.avaje.ebeaninternal.server.core.PstmtBatch; +import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql.SqlType; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.util.BindParamsParser; + +/** + * Executes the UpdateSql requests. + */ +public class ExeUpdateSql { + + private static final Logger logger = Logger.getLogger(ExeUpdateSql.class.getName()); + + private final Binder binder; + + private final PstmtFactory pstmtFactory; + + private final PstmtBatch pstmtBatch; + + //TODO: get defaultBatchSize + private int defaultBatchSize = 20; + + /** + * Create with a given binder. + */ + public ExeUpdateSql(Binder binder, PstmtBatch pstmtBatch) { + this.binder = binder; + this.pstmtBatch = pstmtBatch; + this.pstmtFactory = new PstmtFactory(pstmtBatch); + } + + /** + * Execute the UpdateSql request. + */ + public int execute(PersistRequestUpdateSql request) { + + SpiTransaction t = request.getTransaction(); + + boolean batchThisRequest = t.isBatchThisRequest(); + + PreparedStatement pstmt = null; + try { + + pstmt = bindStmt(request, batchThisRequest); + + if (batchThisRequest){ + if (pstmtBatch != null){ + pstmtBatch.addBatch(pstmt); + } else { + pstmt.addBatch(); + } + // return -1 to indicate batch mode + return -1; + + } else { + int rowCount = pstmt.executeUpdate(); + request.checkRowCount(rowCount); + request.postExecute(); + return rowCount; + + } + + } catch (SQLException ex) { + throw new PersistenceException(ex); + + } finally { + if (!batchThisRequest && pstmt != null) { + try { + pstmt.close(); + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + } + } + } + + + private PreparedStatement bindStmt(PersistRequestUpdateSql request, boolean batchThisRequest) throws SQLException { + + SpiSqlUpdate updateSql = request.getUpdateSql(); + SpiTransaction t = request.getTransaction(); + + String sql = updateSql.getSql(); + + BindParams bindParams = updateSql.getBindParams(); + + // process named parameters if required + sql = BindParamsParser.parse(bindParams, sql); + + boolean logSql = request.isLogSql(); + + PreparedStatement pstmt; + if (batchThisRequest){ + pstmt = pstmtFactory.getPstmt(t, logSql, sql, request); + if (pstmtBatch != null){ + // oracle specific JDBC setting batch size ahead of time + int batchSize = t.getBatchSize(); + if (batchSize < 1){ + batchSize = defaultBatchSize; + } + pstmtBatch.setBatchSize(pstmt, batchSize); + } + + } else { + if (logSql){ + t.logInternal(sql); + } + pstmt = pstmtFactory.getPstmt(t, sql); + } + + if (updateSql.getTimeout() > 0){ + pstmt.setQueryTimeout(updateSql.getTimeout()); + } + + String bindLog = null; + if (!bindParams.isEmpty()){ + bindLog = binder.bind(bindParams, new DataBind(pstmt)); + } + + request.setBindLog(bindLog); + + // derive the statement type (for TransactionEvent) + parseUpdate(sql, request); + + return pstmt; + } + + + private void determineType(String word1, String word2, String word3, PersistRequestUpdateSql request) { + if (word1.equalsIgnoreCase("UPDATE")) { + request.setType(SqlType.SQL_UPDATE, word2, "UpdateSql"); + + } else if (word1.equalsIgnoreCase("DELETE")) { + request.setType(SqlType.SQL_DELETE, word3, "DeleteSql"); + + } else if (word1.equalsIgnoreCase("INSERT")) { + request.setType(SqlType.SQL_INSERT, word3, "InsertSql"); + + } else { + request.setType(SqlType.SQL_UNKNOWN, null, "UnknownSql"); + + } + } + + private void parseUpdate(String sql, PersistRequestUpdateSql request) { + + int start = ltrim(sql); + + int[] pos = new int[3]; + int spaceCount = 0; + + int len = sql.length(); + for (int i = start; i < len; i++) { + char c = sql.charAt(i); + if (Character.isWhitespace(c)) { + pos[spaceCount] = i; + spaceCount++; + if (spaceCount > 2){ + break; + } + } + } + + String firstWord = sql.substring(0, pos[0]); + String secWord = sql.substring(pos[0]+1, pos[1]); + String thirdWord; + if (pos[2] == 0){ + // there is nothing after the table name + thirdWord = sql.substring(pos[1]+1); + } else { + thirdWord = sql.substring(pos[1]+1, pos[2]); + } + + determineType(firstWord, secWord, thirdWord, request); + } + + private int ltrim(String s) { + int len = s.length(); + int i = 0; + for (i = 0; i < len; i++) { + if (!Character.isWhitespace(s.charAt(i))) { + return i; + } + } + return 0; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/PersistExecute.java b/src/main/java/com/avaje/ebeaninternal/server/persist/PersistExecute.java index 20b81b074..306680eee 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/PersistExecute.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/PersistExecute.java @@ -1,73 +1,54 @@ -/** - * 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.persist; - -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql; -import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate; -import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql; - -/** - * The actual execution of persist requests. - *

      - * A Persister 'front-ends' this object and handles the - * batching, cascading, concurrency mode detection etc. - *

      - * - */ -public interface PersistExecute { - - /** - * Create a BatchControl for the current transaction. - */ - public BatchControl createBatchControl(SpiTransaction t); - - /** - * Execute a Bean (or MapBean) insert. - */ - public void executeInsertBean(PersistRequestBean request); - - /** - * Execute a Bean (or MapBean) update. - */ - public void executeUpdateBean(PersistRequestBean request); - - /** - * Execute a Bean (or MapBean) delete. - */ - public void executeDeleteBean(PersistRequestBean request); - - /** - * Execute a Update. - */ - public int executeOrmUpdate(PersistRequestOrmUpdate request); - - /** - * Execute a CallableSql. - */ - public int executeSqlCallable(PersistRequestCallableSql request); - - /** - * Execute a UpdateSql. - */ - public int executeSqlUpdate(PersistRequestUpdateSql request); - -} +package com.avaje.ebeaninternal.server.persist; + +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.core.PersistRequestCallableSql; +import com.avaje.ebeaninternal.server.core.PersistRequestOrmUpdate; +import com.avaje.ebeaninternal.server.core.PersistRequestUpdateSql; + +/** + * The actual execution of persist requests. + *

      + * A Persister 'front-ends' this object and handles the + * batching, cascading, concurrency mode detection etc. + *

      + * + */ +public interface PersistExecute { + + /** + * Create a BatchControl for the current transaction. + */ + public BatchControl createBatchControl(SpiTransaction t); + + /** + * Execute a Bean (or MapBean) insert. + */ + public void executeInsertBean(PersistRequestBean request); + + /** + * Execute a Bean (or MapBean) update. + */ + public void executeUpdateBean(PersistRequestBean request); + + /** + * Execute a Bean (or MapBean) delete. + */ + public void executeDeleteBean(PersistRequestBean request); + + /** + * Execute a Update. + */ + public int executeOrmUpdate(PersistRequestOrmUpdate request); + + /** + * Execute a CallableSql. + */ + public int executeSqlCallable(PersistRequestCallableSql request); + + /** + * Execute a UpdateSql. + */ + public int executeSqlUpdate(PersistRequestUpdateSql request); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/PstmtFactory.java b/src/main/java/com/avaje/ebeaninternal/server/persist/PstmtFactory.java index 07ca4b2be..5a1cf50ac 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/PstmtFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/PstmtFactory.java @@ -1,115 +1,96 @@ -/** - * 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.persist; - -import java.sql.CallableStatement; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; - -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.PstmtBatch; - -/** - * Factory for creating Statements. - *

      - * This is only used by CallableSql and UpdateSql requests and does not support - * getGeneratedKeys. - *

      - */ -public class PstmtFactory { - - - private final PstmtBatch pstmtBatch; - - public PstmtFactory(PstmtBatch pstmtBatch) { - this.pstmtBatch = pstmtBatch; - } - - /** - * Get a callable statement without any batching. - */ - public CallableStatement getCstmt(SpiTransaction t, String sql) throws SQLException { - Connection conn = t.getInternalConnection(); - return conn.prepareCall(sql); - } - - /** - * Get a prepared statement without any batching. - */ - public PreparedStatement getPstmt(SpiTransaction t, String sql) throws SQLException { - Connection conn = t.getInternalConnection(); - return conn.prepareStatement(sql); - } - - /** - * Return a prepared statement taking into account batch requirements. - */ - public PreparedStatement getPstmt(SpiTransaction t, boolean logSql, String sql, BatchPostExecute batchExe) - throws SQLException { - - BatchedPstmtHolder batch = t.getBatchControl().getPstmtHolder(); - PreparedStatement stmt = batch.getStmt(sql, batchExe); - - if (stmt != null) { - return stmt; - } - - if (logSql){ - t.logInternal(sql); - } - - Connection conn = t.getInternalConnection(); - stmt = conn.prepareStatement(sql); - - if (pstmtBatch != null){ - pstmtBatch.setBatchSize(stmt, t.getBatchControl().getBatchSize()); - } - - BatchedPstmt bs = new BatchedPstmt(stmt, false, sql, pstmtBatch, false); - batch.addStmt(bs, batchExe); - return stmt; - } - - /** - * Return a callable statement taking into account batch requirements. - */ - public CallableStatement getCstmt(SpiTransaction t, boolean logSql, String sql, BatchPostExecute batchExe) - throws SQLException { - - BatchedPstmtHolder batch = t.getBatchControl().getPstmtHolder(); - CallableStatement stmt = (CallableStatement) batch.getStmt(sql, batchExe); - - if (stmt != null) { - return stmt; - } - - if (logSql){ - t.logInternal(sql); - } - - Connection conn = t.getInternalConnection(); - stmt = conn.prepareCall(sql); - - BatchedPstmt bs = new BatchedPstmt(stmt, false, sql, pstmtBatch, false); - batch.addStmt(bs, batchExe); - return stmt; - } -} +package com.avaje.ebeaninternal.server.persist; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.PstmtBatch; + +/** + * Factory for creating Statements. + *

      + * This is only used by CallableSql and UpdateSql requests and does not support + * getGeneratedKeys. + *

      + */ +public class PstmtFactory { + + + private final PstmtBatch pstmtBatch; + + public PstmtFactory(PstmtBatch pstmtBatch) { + this.pstmtBatch = pstmtBatch; + } + + /** + * Get a callable statement without any batching. + */ + public CallableStatement getCstmt(SpiTransaction t, String sql) throws SQLException { + Connection conn = t.getInternalConnection(); + return conn.prepareCall(sql); + } + + /** + * Get a prepared statement without any batching. + */ + public PreparedStatement getPstmt(SpiTransaction t, String sql) throws SQLException { + Connection conn = t.getInternalConnection(); + return conn.prepareStatement(sql); + } + + /** + * Return a prepared statement taking into account batch requirements. + */ + public PreparedStatement getPstmt(SpiTransaction t, boolean logSql, String sql, BatchPostExecute batchExe) + throws SQLException { + + BatchedPstmtHolder batch = t.getBatchControl().getPstmtHolder(); + PreparedStatement stmt = batch.getStmt(sql, batchExe); + + if (stmt != null) { + return stmt; + } + + if (logSql){ + t.logInternal(sql); + } + + Connection conn = t.getInternalConnection(); + stmt = conn.prepareStatement(sql); + + if (pstmtBatch != null){ + pstmtBatch.setBatchSize(stmt, t.getBatchControl().getBatchSize()); + } + + BatchedPstmt bs = new BatchedPstmt(stmt, false, sql, pstmtBatch, false); + batch.addStmt(bs, batchExe); + return stmt; + } + + /** + * Return a callable statement taking into account batch requirements. + */ + public CallableStatement getCstmt(SpiTransaction t, boolean logSql, String sql, BatchPostExecute batchExe) + throws SQLException { + + BatchedPstmtHolder batch = t.getBatchControl().getPstmtHolder(); + CallableStatement stmt = (CallableStatement) batch.getStmt(sql, batchExe); + + if (stmt != null) { + return stmt; + } + + if (logSql){ + t.logInternal(sql); + } + + Connection conn = t.getInternalConnection(); + stmt = conn.prepareCall(sql); + + BatchedPstmt bs = new BatchedPstmt(stmt, false, sql, pstmtBatch, false); + batch.addStmt(bs, batchExe); + return stmt; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteHandler.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteHandler.java index 29ffbf6bd..5d1a38c1b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteHandler.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteHandler.java @@ -1,99 +1,80 @@ -/** - * 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.persist.dml; - -import java.sql.PreparedStatement; -import java.sql.SQLException; - -import javax.persistence.OptimisticLockException; - -import com.avaje.ebeaninternal.api.DerivedRelationshipData; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.type.DataBind; - -/** - * Delete bean handler. - */ -public class DeleteHandler extends DmlHandler { - - private final DeleteMeta meta; - - public DeleteHandler(PersistRequestBean persist, DeleteMeta meta) { - super(persist, meta.isEmptyStringAsNull()); - this.meta = meta; - } - - /** - * Generate and bind the delete statement. - */ - public void bind() throws SQLException { - - sql = meta.getSql(persistRequest); - - SpiTransaction t = persistRequest.getTransaction(); - boolean isBatch = t.isBatchThisRequest(); - - PreparedStatement pstmt; - if (isBatch) { - pstmt = getPstmt(t, sql, persistRequest, false); - - } else { - logSql(sql); - pstmt = getPstmt(t, sql, false); - } - dataBind = new DataBind(pstmt); - - bindLogAppend("Binding Delete ["); - bindLogAppend(meta.getTableName()); - bindLogAppend("] where["); - - meta.bind(persistRequest, this); - - bindLogAppend("]"); - - // log the binding to transaction log if requested - logBinding(); - } - - /** - * Execute the delete non-batch. - */ - public void execute() throws SQLException, OptimisticLockException { - int rowCount = dataBind.executeUpdate(); - checkRowCount(rowCount); - } - - @Override - public boolean isIncluded(BeanProperty prop) { - return prop.isDbUpdatable() && super.isIncluded(prop); - } - - @Override - public boolean isIncludedWhere(BeanProperty prop) { - return prop.isDbUpdatable() && (loadedProps == null || loadedProps.contains(prop.getName())); - } - - public void registerDerivedRelationship(DerivedRelationshipData assocBean) { - throw new RuntimeException("Never called on delete"); - } - -} +package com.avaje.ebeaninternal.server.persist.dml; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import javax.persistence.OptimisticLockException; + +import com.avaje.ebeaninternal.api.DerivedRelationshipData; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.type.DataBind; + +/** + * Delete bean handler. + */ +public class DeleteHandler extends DmlHandler { + + private final DeleteMeta meta; + + public DeleteHandler(PersistRequestBean persist, DeleteMeta meta) { + super(persist, meta.isEmptyStringAsNull()); + this.meta = meta; + } + + /** + * Generate and bind the delete statement. + */ + public void bind() throws SQLException { + + sql = meta.getSql(persistRequest); + + SpiTransaction t = persistRequest.getTransaction(); + boolean isBatch = t.isBatchThisRequest(); + + PreparedStatement pstmt; + if (isBatch) { + pstmt = getPstmt(t, sql, persistRequest, false); + + } else { + logSql(sql); + pstmt = getPstmt(t, sql, false); + } + dataBind = new DataBind(pstmt); + + bindLogAppend("Binding Delete ["); + bindLogAppend(meta.getTableName()); + bindLogAppend("] where["); + + meta.bind(persistRequest, this); + + bindLogAppend("]"); + + // log the binding to transaction log if requested + logBinding(); + } + + /** + * Execute the delete non-batch. + */ + public void execute() throws SQLException, OptimisticLockException { + int rowCount = dataBind.executeUpdate(); + checkRowCount(rowCount); + } + + @Override + public boolean isIncluded(BeanProperty prop) { + return prop.isDbUpdatable() && super.isIncluded(prop); + } + + @Override + public boolean isIncludedWhere(BeanProperty prop) { + return prop.isDbUpdatable() && (loadedProps == null || loadedProps.contains(prop.getName())); + } + + public void registerDerivedRelationship(DerivedRelationshipData assocBean) { + throw new RuntimeException("Never called on delete"); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteMeta.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteMeta.java index a2c312aa9..43e4c347a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteMeta.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DeleteMeta.java @@ -1,165 +1,146 @@ -/** - * 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.persist.dml; - -import java.sql.SQLException; -import java.util.Set; - -import com.avaje.ebeaninternal.server.core.ConcurrencyMode; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable; -import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId; - -/** - * Meta data for delete handler. The meta data is for a particular bean type. It - * is considered immutable and is thread safe. - */ -public final class DeleteMeta { - - private final String sqlVersion; - - private final String sqlNone; - - private final BindableId id; - - private final Bindable version; - - private final Bindable all; - - private final String tableName; - - private final boolean emptyStringAsNull; - - public DeleteMeta(boolean emptyStringAsNull, BeanDescriptor desc, BindableId id, Bindable version, Bindable all) { - this.emptyStringAsNull = emptyStringAsNull; - this.tableName = desc.getBaseTable(); - this.id = id; - this.version = version; - this.all = all; - - sqlNone = genSql(ConcurrencyMode.NONE); - sqlVersion = genSql(ConcurrencyMode.VERSION); - } - - public boolean isEmptyStringAsNull() { - return emptyStringAsNull; - } - - /** - * Return the table name. - */ - public String getTableName() { - return tableName; - } - - /** - * Bind the request based on the concurrency mode. - */ - public void bind(PersistRequestBean persist, DmlHandler bind) throws SQLException { - - Object bean = persist.getBean(); - - id.dmlBind(bind, false, bean); - - switch (persist.getConcurrencyMode()) { - case VERSION: - version.dmlBind(bind, false, bean); - break; - - case ALL: - Object oldBean = persist.getOldValues(); - all.dmlBindWhere(bind, true, oldBean); - break; - - default: - break; - } - } - - /** - * get or generate the sql based on the concurrency mode. - */ - public String getSql(PersistRequestBean request) throws SQLException { - - if (id.isEmpty()) { - throw new IllegalStateException("Can not deleteById on " + request.getFullName() + " as no @Id property"); - } - - switch (request.determineConcurrencyMode()) { - case NONE: - return sqlNone; - - case VERSION: - return sqlVersion; - - case ALL: - return genDynamicWhere(request.getLoadedProperties(), request.getOldValues()); - - default: - throw new RuntimeException("Invalid mode " + request.determineConcurrencyMode()); - } - } - - private String genSql(ConcurrencyMode conMode) { - - // delete ... where bcol=? and bc1=? and bc2 is null and ... - - GenerateDmlRequest request = new GenerateDmlRequest(emptyStringAsNull); - - request.append("delete from ").append(tableName); - request.append(" where "); - - request.setWhereIdMode(); - id.dmlAppend(request, false); - - if (ConcurrencyMode.VERSION.equals(conMode)) { - if (version == null) { - return null; - } - version.dmlAppend(request, false); - - } else if (ConcurrencyMode.ALL.equals(conMode)) { - throw new RuntimeException("Never called for ConcurrencyMode.ALL"); - } - - return request.toString(); - } - - /** - * Generate the sql dynamically for where using IS NULL for binding null - * values. - */ - private String genDynamicWhere(Set includedProps, Object oldBean) throws SQLException { - - // always has a preceding id property(s) so the first - // option is always ' and ' and not blank. - - GenerateDmlRequest request = new GenerateDmlRequest(emptyStringAsNull, includedProps, oldBean); - - request.append(sqlNone); - - request.setWhereMode(); - all.dmlWhere(request, true, oldBean); - - return request.toString(); - } - -} +package com.avaje.ebeaninternal.server.persist.dml; + +import java.sql.SQLException; +import java.util.Set; + +import com.avaje.ebeaninternal.server.core.ConcurrencyMode; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable; +import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId; + +/** + * Meta data for delete handler. The meta data is for a particular bean type. It + * is considered immutable and is thread safe. + */ +public final class DeleteMeta { + + private final String sqlVersion; + + private final String sqlNone; + + private final BindableId id; + + private final Bindable version; + + private final Bindable all; + + private final String tableName; + + private final boolean emptyStringAsNull; + + public DeleteMeta(boolean emptyStringAsNull, BeanDescriptor desc, BindableId id, Bindable version, Bindable all) { + this.emptyStringAsNull = emptyStringAsNull; + this.tableName = desc.getBaseTable(); + this.id = id; + this.version = version; + this.all = all; + + sqlNone = genSql(ConcurrencyMode.NONE); + sqlVersion = genSql(ConcurrencyMode.VERSION); + } + + public boolean isEmptyStringAsNull() { + return emptyStringAsNull; + } + + /** + * Return the table name. + */ + public String getTableName() { + return tableName; + } + + /** + * Bind the request based on the concurrency mode. + */ + public void bind(PersistRequestBean persist, DmlHandler bind) throws SQLException { + + Object bean = persist.getBean(); + + id.dmlBind(bind, false, bean); + + switch (persist.getConcurrencyMode()) { + case VERSION: + version.dmlBind(bind, false, bean); + break; + + case ALL: + Object oldBean = persist.getOldValues(); + all.dmlBindWhere(bind, true, oldBean); + break; + + default: + break; + } + } + + /** + * get or generate the sql based on the concurrency mode. + */ + public String getSql(PersistRequestBean request) throws SQLException { + + if (id.isEmpty()) { + throw new IllegalStateException("Can not deleteById on " + request.getFullName() + " as no @Id property"); + } + + switch (request.determineConcurrencyMode()) { + case NONE: + return sqlNone; + + case VERSION: + return sqlVersion; + + case ALL: + return genDynamicWhere(request.getLoadedProperties(), request.getOldValues()); + + default: + throw new RuntimeException("Invalid mode " + request.determineConcurrencyMode()); + } + } + + private String genSql(ConcurrencyMode conMode) { + + // delete ... where bcol=? and bc1=? and bc2 is null and ... + + GenerateDmlRequest request = new GenerateDmlRequest(emptyStringAsNull); + + request.append("delete from ").append(tableName); + request.append(" where "); + + request.setWhereIdMode(); + id.dmlAppend(request, false); + + if (ConcurrencyMode.VERSION.equals(conMode)) { + if (version == null) { + return null; + } + version.dmlAppend(request, false); + + } else if (ConcurrencyMode.ALL.equals(conMode)) { + throw new RuntimeException("Never called for ConcurrencyMode.ALL"); + } + + return request.toString(); + } + + /** + * Generate the sql dynamically for where using IS NULL for binding null + * values. + */ + private String genDynamicWhere(Set includedProps, Object oldBean) throws SQLException { + + // always has a preceding id property(s) so the first + // option is always ' and ' and not blank. + + GenerateDmlRequest request = new GenerateDmlRequest(emptyStringAsNull, includedProps, oldBean); + + request.append(sqlNone); + + request.setWhereMode(); + all.dmlWhere(request, true, oldBean); + + return request.toString(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersister.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersister.java index 7152a87e0..f706fa6ce 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersister.java @@ -1,129 +1,110 @@ -/** - * 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.persist.dml; - -import java.sql.SQLException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.PersistRequest; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; -import com.avaje.ebeaninternal.server.persist.BeanPersister; - -/** - * Bean persister that uses the Handler and Meta objects. - *

      - * The design of this is based on the immutable Meta objects. They hold a - * information in the form of lists of Bindable objects. This effectively - * flattens the structure of the bean with embedded and associated objects into - * a flat list of Bindable objects. - *

      - */ -public final class DmlBeanPersister implements BeanPersister { - - private static final Logger logger = Logger.getLogger(DmlBeanPersister.class.getName()); - - private final UpdateMeta updateMeta; - - private final InsertMeta insertMeta; - - private final DeleteMeta deleteMeta; - - - public DmlBeanPersister(UpdateMeta updateMeta, InsertMeta insertMeta, DeleteMeta deleteMeta) { - - this.updateMeta = updateMeta; - this.insertMeta = insertMeta; - this.deleteMeta = deleteMeta; - } - - /** - * execute the bean delete request. - */ - public void delete(PersistRequestBean request) { - - DeleteHandler delete = new DeleteHandler(request, deleteMeta); - execute(request, delete); - } - - /** - * execute the bean insert request. - */ - public void insert(PersistRequestBean request) { - - InsertHandler insert = new InsertHandler(request, insertMeta); - execute(request, insert); - } - - /** - * execute the bean update request. - */ - public void update(PersistRequestBean request) { - - UpdateHandler update = new UpdateHandler(request, updateMeta); - execute(request, update); - } - - /** - * execute request taking batching into account. - */ - private void execute(PersistRequest request, PersistHandler handler) { - - SpiTransaction trans = request.getTransaction(); - boolean batchThisRequest = trans.isBatchThisRequest(); - - try { - - handler.bind(); - - if (batchThisRequest) { - handler.addBatch(); - - } else { - // immediate insert - handler.execute(); - } - - } catch (SQLException e) { - // log the error to the transaction log - String errMsg = StringHelper.replaceStringMulti(e.getMessage(), new String[]{"\r","\n"}, "\\n "); - String msg = "ERROR executing DML bindLog["+handler.getBindLog()+"] error["+errMsg+"]"; - if (request.getTransaction().isLogSummary()) { - request.getTransaction().logInternal(msg); - } - - throw new PersistenceException(msg, e); - - } finally { - if (!batchThisRequest && handler != null) { - try { - handler.close(); - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - } - } - } - -} +package com.avaje.ebeaninternal.server.persist.dml; + +import java.sql.SQLException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.PersistRequest; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import com.avaje.ebeaninternal.server.persist.BeanPersister; + +/** + * Bean persister that uses the Handler and Meta objects. + *

      + * The design of this is based on the immutable Meta objects. They hold a + * information in the form of lists of Bindable objects. This effectively + * flattens the structure of the bean with embedded and associated objects into + * a flat list of Bindable objects. + *

      + */ +public final class DmlBeanPersister implements BeanPersister { + + private static final Logger logger = Logger.getLogger(DmlBeanPersister.class.getName()); + + private final UpdateMeta updateMeta; + + private final InsertMeta insertMeta; + + private final DeleteMeta deleteMeta; + + + public DmlBeanPersister(UpdateMeta updateMeta, InsertMeta insertMeta, DeleteMeta deleteMeta) { + + this.updateMeta = updateMeta; + this.insertMeta = insertMeta; + this.deleteMeta = deleteMeta; + } + + /** + * execute the bean delete request. + */ + public void delete(PersistRequestBean request) { + + DeleteHandler delete = new DeleteHandler(request, deleteMeta); + execute(request, delete); + } + + /** + * execute the bean insert request. + */ + public void insert(PersistRequestBean request) { + + InsertHandler insert = new InsertHandler(request, insertMeta); + execute(request, insert); + } + + /** + * execute the bean update request. + */ + public void update(PersistRequestBean request) { + + UpdateHandler update = new UpdateHandler(request, updateMeta); + execute(request, update); + } + + /** + * execute request taking batching into account. + */ + private void execute(PersistRequest request, PersistHandler handler) { + + SpiTransaction trans = request.getTransaction(); + boolean batchThisRequest = trans.isBatchThisRequest(); + + try { + + handler.bind(); + + if (batchThisRequest) { + handler.addBatch(); + + } else { + // immediate insert + handler.execute(); + } + + } catch (SQLException e) { + // log the error to the transaction log + String errMsg = StringHelper.replaceStringMulti(e.getMessage(), new String[]{"\r","\n"}, "\\n "); + String msg = "ERROR executing DML bindLog["+handler.getBindLog()+"] error["+errMsg+"]"; + if (request.getTransaction().isLogSummary()) { + request.getTransaction().logInternal(msg); + } + + throw new PersistenceException(msg, e); + + } finally { + if (!batchThisRequest && handler != null) { + try { + handler.close(); + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersisterFactory.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersisterFactory.java index ff5627b83..0e36acc70 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersisterFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlBeanPersisterFactory.java @@ -1,52 +1,33 @@ -/** - * 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.persist.dml; - -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.persist.BeanPersister; -import com.avaje.ebeaninternal.server.persist.BeanPersisterFactory; - -/** - * Factory for creating a DmlBeanPersister for a bean type. - */ -public class DmlBeanPersisterFactory implements BeanPersisterFactory { - - private final MetaFactory metaFactory; - - public DmlBeanPersisterFactory(DatabasePlatform dbPlatform) { - this.metaFactory = new MetaFactory(dbPlatform); - } - - - /** - * Create a DmlBeanPersister for the given bean type. - */ - public BeanPersister create(BeanDescriptor desc) { - - UpdateMeta updMeta = metaFactory.createUpdate(desc); - DeleteMeta delMeta = metaFactory.createDelete(desc); - InsertMeta insMeta = metaFactory.createInsert(desc); - - return new DmlBeanPersister(updMeta, insMeta, delMeta); - - } - -} +package com.avaje.ebeaninternal.server.persist.dml; + +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.persist.BeanPersister; +import com.avaje.ebeaninternal.server.persist.BeanPersisterFactory; + +/** + * Factory for creating a DmlBeanPersister for a bean type. + */ +public class DmlBeanPersisterFactory implements BeanPersisterFactory { + + private final MetaFactory metaFactory; + + public DmlBeanPersisterFactory(DatabasePlatform dbPlatform) { + this.metaFactory = new MetaFactory(dbPlatform); + } + + + /** + * Create a DmlBeanPersister for the given bean type. + */ + public BeanPersister create(BeanDescriptor desc) { + + UpdateMeta updMeta = metaFactory.createUpdate(desc); + DeleteMeta delMeta = metaFactory.createDelete(desc); + InsertMeta insMeta = metaFactory.createInsert(desc); + + return new DmlBeanPersister(updMeta, insMeta, delMeta); + + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlHandler.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlHandler.java index 44249e872..3fe985203 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlHandler.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/DmlHandler.java @@ -1,418 +1,399 @@ -/** - * 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.persist.dml; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.OptimisticLockException; - -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.core.PstmtBatch; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.persist.BatchedPstmt; -import com.avaje.ebeaninternal.server.persist.BatchedPstmtHolder; -import com.avaje.ebeaninternal.server.persist.dmlbind.BindableRequest; -import com.avaje.ebeaninternal.server.type.DataBind; - - -/** - * Base class for Handler implementations. - */ -public abstract class DmlHandler implements PersistHandler, BindableRequest { - - protected static final Logger logger = Logger.getLogger(DmlHandler.class.getName()); - - /** - * The originating request. - */ - protected final PersistRequestBean persistRequest; - - protected final StringBuilder bindLog; - - protected final Set loadedProps; - - protected final SpiTransaction transaction; - - protected final boolean emptyStringToNull; - - protected final boolean logLevelSql; - - /** - * The PreparedStatement used for the dml. - */ - protected DataBind dataBind; - - protected String sql; - - protected ArrayList updateGenValues; - - private Set additionalProps; - -// private boolean checkDelta; -// -// private BeanDelta deltaBean; - - protected DmlHandler(PersistRequestBean persistRequest, boolean emptyStringToNull) { - this.persistRequest = persistRequest; - this.emptyStringToNull = emptyStringToNull; - this.loadedProps = persistRequest.getLoadedProperties(); - this.transaction = persistRequest.getTransaction(); - this.logLevelSql = transaction.isLogSql(); - if (logLevelSql) { - this.bindLog = new StringBuilder(); - } else { - this.bindLog = null; - } - } - -// protected void setCheckDelta(boolean checkDelta) { -// this.checkDelta = checkDelta; -// } - - public PersistRequestBean getPersistRequest() { - return persistRequest; - } - - /** - * Get the sql and bind the statement. - */ - public abstract void bind() throws SQLException; - - /** - * Execute now for non-batch execution. - */ - public abstract void execute() throws SQLException; - - /** - * Check the rowCount. - */ - protected void checkRowCount(int rowCount) throws SQLException, OptimisticLockException { - try { - persistRequest.checkRowCount(rowCount); - persistRequest.postExecute(); - } catch (OptimisticLockException e){ - // add the SQL and bind values to error message - String m = e.getMessage()+" sql["+sql+"] bind["+bindLog+"]"; - persistRequest.getTransaction().log("OptimisticLockException:"+m); - throw new OptimisticLockException(m, null, e.getEntity()); - } - } - - /** - * Add this for batch execution. - */ - public void addBatch() throws SQLException { - PstmtBatch pstmtBatch = persistRequest.getPstmtBatch(); - if (pstmtBatch != null){ - pstmtBatch.addBatch(dataBind.getPstmt()); - } else { - dataBind.getPstmt().addBatch(); - } - } - - /** - * Close the underlying statement. - */ - public void close() { - try { - if (dataBind != null){ - dataBind.close(); - } - } catch (SQLException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - /** - * Return the bind log. - */ - public String getBindLog() { - return bindLog == null ? "" : bindLog.toString(); - } - - /** - * Set the Id value that was bound. This value is used for logging summary - * level information. - */ - public void setIdValue(Object idValue) { - persistRequest.setBoundId(idValue); - } - - /** - * Log the bind information to the transaction log. - */ - protected void logBinding() { - if (logLevelSql) { - transaction.logInternal(bindLog.toString()); - } - } - - /** - * Log the sql to the transaction log. - */ - protected void logSql(String sql) { - if (logLevelSql) { - transaction.logInternal(sql); - } - } - - - public boolean isIncluded(BeanProperty prop) { - return (loadedProps == null || loadedProps.contains(prop.getName())); - } - - public boolean isIncludedWhere(BeanProperty prop) { - if (prop.isDbEncrypted()){ - // update without a version property ... - // for encrypted properties only include if it was - // also an updated/modified property - return isIncluded(prop); - } - return prop.isDbUpdatable() && (loadedProps == null || loadedProps.contains(prop.getName())); - } - - /** - * Bind a raw value. Used to bind the discriminator column. - */ - public Object bind(String propName, Object value, int sqlType) throws SQLException { - if (logLevelSql) { - bindLog.append(propName).append("="); - bindLog.append(value).append(", "); - } - dataBind.setObject(value, sqlType); - return value; - } - - public Object bindNoLog(Object value, int sqlType, String logPlaceHolder) throws SQLException { - if (logLevelSql) { - bindLog.append(logPlaceHolder).append(" "); - } - dataBind.setObject(value, sqlType); - return value; - } - - /** - * Bind the value to the preparedStatement. - */ - public Object bind(Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException { - return bindInternal(logLevelSql, value, prop, propName, bindNull); - } - - /** - * Bind the value to the preparedStatement without logging. - */ - public Object bindNoLog(Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException { - return bindInternal(false, value, prop, propName, bindNull); - } - - private Object bindInternal(boolean log, Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException { - - if (!bindNull){ - if (emptyStringToNull && (value instanceof String) && ((String)value).length() == 0){ - // support Oracle conversion of empty string to null - //value = prop.getDbNullValue(value); - value = null; - } - } - - if (!bindNull && value == null) { - // where will have IS NULL clause so don't actually bind - if (log) { - bindLog.append(propName).append("="); - bindLog.append("null, "); - } - } else { - if (log) { - bindLog.append(propName).append("="); - if (prop.isLob()){ - bindLog.append("[LOB]"); - } else { - String sv = String.valueOf(value); - if (sv.length() > 50){ - sv = sv.substring(0,47)+"..."; - } - bindLog.append(sv); - } - bindLog.append(", "); - } - // do the actual binding to PreparedStatement - prop.bind(dataBind, value); -// if (checkDelta) { -// if (!prop.isId() && prop.isDeltaRequired()){ -// if (deltaBean == null){ -// deltaBean = persistRequest.createDeltaBean(); -// transaction.getEvent().addBeanDelta(deltaBean); -// } -// deltaBean.add(prop, value); -// } -// } - } - return value; - } - - /** - * Add the comment to the bind information log. - */ - protected void bindLogAppend(String comment) { - if (logLevelSql) { - bindLog.append(comment); - } - } - - /** - * For generated properties set on insert register as additional - * loaded properties if required. - */ - public final void registerAdditionalProperty(String propertyName) { - if (loadedProps != null && !loadedProps.contains(propertyName)){ - if (additionalProps == null){ - additionalProps = new HashSet(); - } - additionalProps.add(propertyName); - } - } - - /** - * Set any additional (generated) properties to the set of loaded properties - * if required. - */ - protected void setAdditionalProperties() { - if (additionalProps != null){ - // additional generated properties set on insert - // added to the set of loaded properties - additionalProps.addAll(loadedProps); - persistRequest.setLoadedProps(additionalProps); - } - } - - /** - * Register a generated value on a update. This can not be set to the bean - * until after the where clause has been bound for concurrency checking. - *

      - * GeneratedProperty values are likely going to be used for optimistic - * concurrency checking. This includes 'counter' and 'update timestamp' - * generation. - *

      - */ - public void registerUpdateGenValue(BeanProperty prop, Object bean, Object value) { - if (updateGenValues == null) { - updateGenValues = new ArrayList(); - } - updateGenValues.add(new UpdateGenValue(prop, bean, value)); - registerAdditionalProperty(prop.getName()); - } - - - - /** - * Set any update generated values to the bean. Must be called after where - * clause has been bound. - */ - public void setUpdateGenValues() { - if (updateGenValues != null) { - for (int i = 0; i < updateGenValues.size(); i++) { - UpdateGenValue updGenVal = updateGenValues.get(i); - updGenVal.setValue(); - } - } - } - - - /** - * Check with useGeneratedKeys to get appropriate PreparedStatement. - */ - protected PreparedStatement getPstmt(SpiTransaction t, String sql, boolean genKeys) throws SQLException { - Connection conn = t.getInternalConnection(); - if (genKeys) { - // the Id generated is always the first column - // Required to stop Oracle10 giving us Oracle rowId?? - // Other jdbc drivers seem fine without this hint. - int[] columns = {1}; - return conn.prepareStatement(sql, columns); - - } else { - return conn.prepareStatement(sql); - } - } - - /** - * Return a prepared statement taking into account batch requirements. - */ - protected PreparedStatement getPstmt(SpiTransaction t, String sql, PersistRequestBean request, boolean genKeys) - throws SQLException { - - BatchedPstmtHolder batch = t.getBatchControl().getPstmtHolder(); - PreparedStatement stmt = batch.getStmt(sql, request); - - if (stmt != null) { - return stmt; - } - - if (logLevelSql){ - t.logInternal(sql); - } - - stmt = getPstmt(t, sql, genKeys); - - PstmtBatch pstmtBatch = request.getPstmtBatch(); - if (pstmtBatch != null){ - pstmtBatch.setBatchSize(stmt, t.getBatchControl().getBatchSize()); - } - - BatchedPstmt bs = new BatchedPstmt(stmt, genKeys, sql, request.getPstmtBatch(), true); - batch.addStmt(bs, request); - return stmt; - } - - /** - * Hold the values from GeneratedValue that need to be set to the bean - * property after the where clause has been built. - */ - private static final class UpdateGenValue { - - private final BeanProperty property; - - private final Object bean; - - private final Object value; - - private UpdateGenValue(BeanProperty property, Object bean, Object value) { - this.property = property; - this.bean = bean; - this.value = value; - } - - /** - * Set the value to the bean property. - */ - private void setValue() { - // support PropertyChangeSupport - property.setValueIntercept(bean, value); - } - } -} +package com.avaje.ebeaninternal.server.persist.dml; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.OptimisticLockException; + +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.core.PstmtBatch; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.persist.BatchedPstmt; +import com.avaje.ebeaninternal.server.persist.BatchedPstmtHolder; +import com.avaje.ebeaninternal.server.persist.dmlbind.BindableRequest; +import com.avaje.ebeaninternal.server.type.DataBind; + + +/** + * Base class for Handler implementations. + */ +public abstract class DmlHandler implements PersistHandler, BindableRequest { + + protected static final Logger logger = Logger.getLogger(DmlHandler.class.getName()); + + /** + * The originating request. + */ + protected final PersistRequestBean persistRequest; + + protected final StringBuilder bindLog; + + protected final Set loadedProps; + + protected final SpiTransaction transaction; + + protected final boolean emptyStringToNull; + + protected final boolean logLevelSql; + + /** + * The PreparedStatement used for the dml. + */ + protected DataBind dataBind; + + protected String sql; + + protected ArrayList updateGenValues; + + private Set additionalProps; + +// private boolean checkDelta; +// +// private BeanDelta deltaBean; + + protected DmlHandler(PersistRequestBean persistRequest, boolean emptyStringToNull) { + this.persistRequest = persistRequest; + this.emptyStringToNull = emptyStringToNull; + this.loadedProps = persistRequest.getLoadedProperties(); + this.transaction = persistRequest.getTransaction(); + this.logLevelSql = transaction.isLogSql(); + if (logLevelSql) { + this.bindLog = new StringBuilder(); + } else { + this.bindLog = null; + } + } + +// protected void setCheckDelta(boolean checkDelta) { +// this.checkDelta = checkDelta; +// } + + public PersistRequestBean getPersistRequest() { + return persistRequest; + } + + /** + * Get the sql and bind the statement. + */ + public abstract void bind() throws SQLException; + + /** + * Execute now for non-batch execution. + */ + public abstract void execute() throws SQLException; + + /** + * Check the rowCount. + */ + protected void checkRowCount(int rowCount) throws SQLException, OptimisticLockException { + try { + persistRequest.checkRowCount(rowCount); + persistRequest.postExecute(); + } catch (OptimisticLockException e){ + // add the SQL and bind values to error message + String m = e.getMessage()+" sql["+sql+"] bind["+bindLog+"]"; + persistRequest.getTransaction().log("OptimisticLockException:"+m); + throw new OptimisticLockException(m, null, e.getEntity()); + } + } + + /** + * Add this for batch execution. + */ + public void addBatch() throws SQLException { + PstmtBatch pstmtBatch = persistRequest.getPstmtBatch(); + if (pstmtBatch != null){ + pstmtBatch.addBatch(dataBind.getPstmt()); + } else { + dataBind.getPstmt().addBatch(); + } + } + + /** + * Close the underlying statement. + */ + public void close() { + try { + if (dataBind != null){ + dataBind.close(); + } + } catch (SQLException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + /** + * Return the bind log. + */ + public String getBindLog() { + return bindLog == null ? "" : bindLog.toString(); + } + + /** + * Set the Id value that was bound. This value is used for logging summary + * level information. + */ + public void setIdValue(Object idValue) { + persistRequest.setBoundId(idValue); + } + + /** + * Log the bind information to the transaction log. + */ + protected void logBinding() { + if (logLevelSql) { + transaction.logInternal(bindLog.toString()); + } + } + + /** + * Log the sql to the transaction log. + */ + protected void logSql(String sql) { + if (logLevelSql) { + transaction.logInternal(sql); + } + } + + + public boolean isIncluded(BeanProperty prop) { + return (loadedProps == null || loadedProps.contains(prop.getName())); + } + + public boolean isIncludedWhere(BeanProperty prop) { + if (prop.isDbEncrypted()){ + // update without a version property ... + // for encrypted properties only include if it was + // also an updated/modified property + return isIncluded(prop); + } + return prop.isDbUpdatable() && (loadedProps == null || loadedProps.contains(prop.getName())); + } + + /** + * Bind a raw value. Used to bind the discriminator column. + */ + public Object bind(String propName, Object value, int sqlType) throws SQLException { + if (logLevelSql) { + bindLog.append(propName).append("="); + bindLog.append(value).append(", "); + } + dataBind.setObject(value, sqlType); + return value; + } + + public Object bindNoLog(Object value, int sqlType, String logPlaceHolder) throws SQLException { + if (logLevelSql) { + bindLog.append(logPlaceHolder).append(" "); + } + dataBind.setObject(value, sqlType); + return value; + } + + /** + * Bind the value to the preparedStatement. + */ + public Object bind(Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException { + return bindInternal(logLevelSql, value, prop, propName, bindNull); + } + + /** + * Bind the value to the preparedStatement without logging. + */ + public Object bindNoLog(Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException { + return bindInternal(false, value, prop, propName, bindNull); + } + + private Object bindInternal(boolean log, Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException { + + if (!bindNull){ + if (emptyStringToNull && (value instanceof String) && ((String)value).length() == 0){ + // support Oracle conversion of empty string to null + //value = prop.getDbNullValue(value); + value = null; + } + } + + if (!bindNull && value == null) { + // where will have IS NULL clause so don't actually bind + if (log) { + bindLog.append(propName).append("="); + bindLog.append("null, "); + } + } else { + if (log) { + bindLog.append(propName).append("="); + if (prop.isLob()){ + bindLog.append("[LOB]"); + } else { + String sv = String.valueOf(value); + if (sv.length() > 50){ + sv = sv.substring(0,47)+"..."; + } + bindLog.append(sv); + } + bindLog.append(", "); + } + // do the actual binding to PreparedStatement + prop.bind(dataBind, value); +// if (checkDelta) { +// if (!prop.isId() && prop.isDeltaRequired()){ +// if (deltaBean == null){ +// deltaBean = persistRequest.createDeltaBean(); +// transaction.getEvent().addBeanDelta(deltaBean); +// } +// deltaBean.add(prop, value); +// } +// } + } + return value; + } + + /** + * Add the comment to the bind information log. + */ + protected void bindLogAppend(String comment) { + if (logLevelSql) { + bindLog.append(comment); + } + } + + /** + * For generated properties set on insert register as additional + * loaded properties if required. + */ + public final void registerAdditionalProperty(String propertyName) { + if (loadedProps != null && !loadedProps.contains(propertyName)){ + if (additionalProps == null){ + additionalProps = new HashSet(); + } + additionalProps.add(propertyName); + } + } + + /** + * Set any additional (generated) properties to the set of loaded properties + * if required. + */ + protected void setAdditionalProperties() { + if (additionalProps != null){ + // additional generated properties set on insert + // added to the set of loaded properties + additionalProps.addAll(loadedProps); + persistRequest.setLoadedProps(additionalProps); + } + } + + /** + * Register a generated value on a update. This can not be set to the bean + * until after the where clause has been bound for concurrency checking. + *

      + * GeneratedProperty values are likely going to be used for optimistic + * concurrency checking. This includes 'counter' and 'update timestamp' + * generation. + *

      + */ + public void registerUpdateGenValue(BeanProperty prop, Object bean, Object value) { + if (updateGenValues == null) { + updateGenValues = new ArrayList(); + } + updateGenValues.add(new UpdateGenValue(prop, bean, value)); + registerAdditionalProperty(prop.getName()); + } + + + + /** + * Set any update generated values to the bean. Must be called after where + * clause has been bound. + */ + public void setUpdateGenValues() { + if (updateGenValues != null) { + for (int i = 0; i < updateGenValues.size(); i++) { + UpdateGenValue updGenVal = updateGenValues.get(i); + updGenVal.setValue(); + } + } + } + + + /** + * Check with useGeneratedKeys to get appropriate PreparedStatement. + */ + protected PreparedStatement getPstmt(SpiTransaction t, String sql, boolean genKeys) throws SQLException { + Connection conn = t.getInternalConnection(); + if (genKeys) { + // the Id generated is always the first column + // Required to stop Oracle10 giving us Oracle rowId?? + // Other jdbc drivers seem fine without this hint. + int[] columns = {1}; + return conn.prepareStatement(sql, columns); + + } else { + return conn.prepareStatement(sql); + } + } + + /** + * Return a prepared statement taking into account batch requirements. + */ + protected PreparedStatement getPstmt(SpiTransaction t, String sql, PersistRequestBean request, boolean genKeys) + throws SQLException { + + BatchedPstmtHolder batch = t.getBatchControl().getPstmtHolder(); + PreparedStatement stmt = batch.getStmt(sql, request); + + if (stmt != null) { + return stmt; + } + + if (logLevelSql){ + t.logInternal(sql); + } + + stmt = getPstmt(t, sql, genKeys); + + PstmtBatch pstmtBatch = request.getPstmtBatch(); + if (pstmtBatch != null){ + pstmtBatch.setBatchSize(stmt, t.getBatchControl().getBatchSize()); + } + + BatchedPstmt bs = new BatchedPstmt(stmt, genKeys, sql, request.getPstmtBatch(), true); + batch.addStmt(bs, request); + return stmt; + } + + /** + * Hold the values from GeneratedValue that need to be set to the bean + * property after the where clause has been built. + */ + private static final class UpdateGenValue { + + private final BeanProperty property; + + private final Object bean; + + private final Object value; + + private UpdateGenValue(BeanProperty property, Object bean, Object value) { + this.property = property; + this.bean = bean; + this.value = value; + } + + /** + * Set the value to the bean property. + */ + private void setValue() { + // support PropertyChangeSupport + property.setValueIntercept(bean, value); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertHandler.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertHandler.java index e80fd9769..9841dddbc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertHandler.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertHandler.java @@ -1,258 +1,239 @@ -/** - * 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.persist.dml; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.HashSet; -import java.util.List; -import java.util.logging.Level; - -import javax.persistence.OptimisticLockException; -import javax.persistence.PersistenceException; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebeaninternal.api.DerivedRelationshipData; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.Message; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.persist.DmlUtil; -import com.avaje.ebeaninternal.server.type.DataBind; - -/** - * Insert bean handler. - */ -public class InsertHandler extends DmlHandler { - - /** - * The associated InsertMeta data. - */ - private final InsertMeta meta; - - /** - * Set to true when the key is concatenated. - */ - private final boolean concatinatedKey; - - /** - * Flag set when using getGeneratedKeys. - */ - private boolean useGeneratedKeys; - - /** - * A SQL Select used to fetch back the Id where generatedKeys is not - * supported. - */ - private String selectLastInsertedId; - - /** - * Create to handle the insert execution. - */ - public InsertHandler(PersistRequestBean persist, InsertMeta meta) { - super(persist, meta.isEmptyStringToNull()); - this.meta = meta; - this.concatinatedKey = meta.isConcatinatedKey(); - } - - @Override - public boolean isIncluded(BeanProperty prop) { - return prop.isDbInsertable() && (super.isIncluded(prop)); - } - - /** - * Generate and bind the insert statement. - */ - public void bind() throws SQLException { - - BeanDescriptor desc = persistRequest.getBeanDescriptor(); - Object bean = persistRequest.getBean(); - - Object idValue = desc.getId(bean); - - boolean withId = !DmlUtil.isNullOrZero(idValue); - - // check to see if we are going to use generated keys - if (!withId) { - if (concatinatedKey) { - // expecting a concatenated key that can - // be built from supplied AssocOne beans - withId = meta.deriveConcatenatedId(persistRequest); - - } else if (meta.supportsGetGeneratedKeys()) { - // Identity with getGeneratedKeys - useGeneratedKeys = true; - } else { - // use a query to get the last inserted id - selectLastInsertedId = meta.getSelectLastInsertedId(); - } - } - - SpiTransaction t = persistRequest.getTransaction(); - boolean isBatch = t.isBatchThisRequest(); - - // get the appropriate sql - sql = meta.getSql(withId); - - PreparedStatement pstmt; - if (isBatch) { - pstmt = getPstmt(t, sql, persistRequest, useGeneratedKeys); - - } else { - logSql(sql); - pstmt = getPstmt(t, sql, useGeneratedKeys); - } - dataBind = new DataBind(pstmt); - - bindLogAppend("Binding Insert ["); - bindLogAppend(desc.getBaseTable()); - bindLogAppend("] set["); - - // bind the bean property values - meta.bind(this, bean, withId); - - bindLogAppend("]"); - logBinding(); - } - - /** - * Check with useGeneratedKeys to get appropriate PreparedStatement. - */ - @Override - protected PreparedStatement getPstmt(SpiTransaction t, String sql, boolean useGeneratedKeys) throws SQLException { - Connection conn = t.getInternalConnection(); - if (useGeneratedKeys) { - return conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); - - } else { - return conn.prepareStatement(sql); - } - } - - /** - * Execute the insert in a normal non batch fashion. Additionally using - * getGeneratedKeys if required. - */ - public void execute() throws SQLException, OptimisticLockException { - int rc = dataBind.executeUpdate(); - if (useGeneratedKeys) { - // get the auto-increment value back and set into the bean - getGeneratedKeys(); - - } else if (selectLastInsertedId != null) { - // fetch back the Id using a query - fetchGeneratedKeyUsingSelect(); - } - - checkRowCount(rc); - setAdditionalProperties(); - executeDerivedRelationships(); - } - - protected void executeDerivedRelationships() { - List derivedRelationships = persistRequest.getDerivedRelationships(); - if (derivedRelationships != null) { - for (int i = 0; i < derivedRelationships.size(); i++) { - DerivedRelationshipData derivedRelationshipData = derivedRelationships.get(i); - - EbeanServer ebeanServer = persistRequest.getEbeanServer(); - HashSet updateProps = new HashSet(); - updateProps.add(derivedRelationshipData.getLogicalName()); - ebeanServer.update(derivedRelationshipData.getBean(), updateProps, transaction, false, true); - } - } - } - - /** - * For non batch insert with generated keys. - */ - private void getGeneratedKeys() throws SQLException { - - ResultSet rset = dataBind.getPstmt().getGeneratedKeys(); - try { - if (rset.next()) { - Object idValue = rset.getObject(1); - if (idValue != null) { - persistRequest.setGeneratedKey(idValue); - } - - } else { - throw new PersistenceException(Message.msg("persist.autoinc.norows")); - } - } finally { - try { - rset.close(); - } catch (SQLException ex) { - String msg = "Error closing rset for returning generatedKeys?"; - logger.log(Level.WARNING, msg, ex); - } - } - } - - /** - * For non batch insert with DBs that do not support getGeneratedKeys. Use a - * SQL select to fetch back the Id value. - */ - private void fetchGeneratedKeyUsingSelect() throws SQLException { - - Connection conn = transaction.getConnection(); - - PreparedStatement stmt = null; - ResultSet rset = null; - try { - stmt = conn.prepareStatement(selectLastInsertedId); - rset = stmt.executeQuery(); - if (rset.next()) { - Object idValue = rset.getObject(1); - if (idValue != null) { - persistRequest.setGeneratedKey(idValue); - } - } else { - throw new PersistenceException(Message.msg("persist.autoinc.norows")); - } - } finally { - try { - if (rset != null) { - rset.close(); - } - } catch (SQLException ex) { - String msg = "Error closing rset for fetchGeneratedKeyUsingSelect?"; - logger.log(Level.WARNING, msg, ex); - } - try { - if (stmt != null) { - stmt.close(); - } - } catch (SQLException ex) { - String msg = "Error closing stmt for fetchGeneratedKeyUsingSelect?"; - logger.log(Level.WARNING, msg, ex); - } - } - } - - public void registerDerivedRelationship(DerivedRelationshipData derivedRelationship) { - persistRequest.getTransaction().registerDerivedRelationship(derivedRelationship); - } - -} +package com.avaje.ebeaninternal.server.persist.dml; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashSet; +import java.util.List; +import java.util.logging.Level; + +import javax.persistence.OptimisticLockException; +import javax.persistence.PersistenceException; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebeaninternal.api.DerivedRelationshipData; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.Message; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.persist.DmlUtil; +import com.avaje.ebeaninternal.server.type.DataBind; + +/** + * Insert bean handler. + */ +public class InsertHandler extends DmlHandler { + + /** + * The associated InsertMeta data. + */ + private final InsertMeta meta; + + /** + * Set to true when the key is concatenated. + */ + private final boolean concatinatedKey; + + /** + * Flag set when using getGeneratedKeys. + */ + private boolean useGeneratedKeys; + + /** + * A SQL Select used to fetch back the Id where generatedKeys is not + * supported. + */ + private String selectLastInsertedId; + + /** + * Create to handle the insert execution. + */ + public InsertHandler(PersistRequestBean persist, InsertMeta meta) { + super(persist, meta.isEmptyStringToNull()); + this.meta = meta; + this.concatinatedKey = meta.isConcatinatedKey(); + } + + @Override + public boolean isIncluded(BeanProperty prop) { + return prop.isDbInsertable() && (super.isIncluded(prop)); + } + + /** + * Generate and bind the insert statement. + */ + public void bind() throws SQLException { + + BeanDescriptor desc = persistRequest.getBeanDescriptor(); + Object bean = persistRequest.getBean(); + + Object idValue = desc.getId(bean); + + boolean withId = !DmlUtil.isNullOrZero(idValue); + + // check to see if we are going to use generated keys + if (!withId) { + if (concatinatedKey) { + // expecting a concatenated key that can + // be built from supplied AssocOne beans + withId = meta.deriveConcatenatedId(persistRequest); + + } else if (meta.supportsGetGeneratedKeys()) { + // Identity with getGeneratedKeys + useGeneratedKeys = true; + } else { + // use a query to get the last inserted id + selectLastInsertedId = meta.getSelectLastInsertedId(); + } + } + + SpiTransaction t = persistRequest.getTransaction(); + boolean isBatch = t.isBatchThisRequest(); + + // get the appropriate sql + sql = meta.getSql(withId); + + PreparedStatement pstmt; + if (isBatch) { + pstmt = getPstmt(t, sql, persistRequest, useGeneratedKeys); + + } else { + logSql(sql); + pstmt = getPstmt(t, sql, useGeneratedKeys); + } + dataBind = new DataBind(pstmt); + + bindLogAppend("Binding Insert ["); + bindLogAppend(desc.getBaseTable()); + bindLogAppend("] set["); + + // bind the bean property values + meta.bind(this, bean, withId); + + bindLogAppend("]"); + logBinding(); + } + + /** + * Check with useGeneratedKeys to get appropriate PreparedStatement. + */ + @Override + protected PreparedStatement getPstmt(SpiTransaction t, String sql, boolean useGeneratedKeys) throws SQLException { + Connection conn = t.getInternalConnection(); + if (useGeneratedKeys) { + return conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + + } else { + return conn.prepareStatement(sql); + } + } + + /** + * Execute the insert in a normal non batch fashion. Additionally using + * getGeneratedKeys if required. + */ + public void execute() throws SQLException, OptimisticLockException { + int rc = dataBind.executeUpdate(); + if (useGeneratedKeys) { + // get the auto-increment value back and set into the bean + getGeneratedKeys(); + + } else if (selectLastInsertedId != null) { + // fetch back the Id using a query + fetchGeneratedKeyUsingSelect(); + } + + checkRowCount(rc); + setAdditionalProperties(); + executeDerivedRelationships(); + } + + protected void executeDerivedRelationships() { + List derivedRelationships = persistRequest.getDerivedRelationships(); + if (derivedRelationships != null) { + for (int i = 0; i < derivedRelationships.size(); i++) { + DerivedRelationshipData derivedRelationshipData = derivedRelationships.get(i); + + EbeanServer ebeanServer = persistRequest.getEbeanServer(); + HashSet updateProps = new HashSet(); + updateProps.add(derivedRelationshipData.getLogicalName()); + ebeanServer.update(derivedRelationshipData.getBean(), updateProps, transaction, false, true); + } + } + } + + /** + * For non batch insert with generated keys. + */ + private void getGeneratedKeys() throws SQLException { + + ResultSet rset = dataBind.getPstmt().getGeneratedKeys(); + try { + if (rset.next()) { + Object idValue = rset.getObject(1); + if (idValue != null) { + persistRequest.setGeneratedKey(idValue); + } + + } else { + throw new PersistenceException(Message.msg("persist.autoinc.norows")); + } + } finally { + try { + rset.close(); + } catch (SQLException ex) { + String msg = "Error closing rset for returning generatedKeys?"; + logger.log(Level.WARNING, msg, ex); + } + } + } + + /** + * For non batch insert with DBs that do not support getGeneratedKeys. Use a + * SQL select to fetch back the Id value. + */ + private void fetchGeneratedKeyUsingSelect() throws SQLException { + + Connection conn = transaction.getConnection(); + + PreparedStatement stmt = null; + ResultSet rset = null; + try { + stmt = conn.prepareStatement(selectLastInsertedId); + rset = stmt.executeQuery(); + if (rset.next()) { + Object idValue = rset.getObject(1); + if (idValue != null) { + persistRequest.setGeneratedKey(idValue); + } + } else { + throw new PersistenceException(Message.msg("persist.autoinc.norows")); + } + } finally { + try { + if (rset != null) { + rset.close(); + } + } catch (SQLException ex) { + String msg = "Error closing rset for fetchGeneratedKeyUsingSelect?"; + logger.log(Level.WARNING, msg, ex); + } + try { + if (stmt != null) { + stmt.close(); + } + } catch (SQLException ex) { + String msg = "Error closing stmt for fetchGeneratedKeyUsingSelect?"; + logger.log(Level.WARNING, msg, ex); + } + } + } + + public void registerDerivedRelationship(DerivedRelationshipData derivedRelationship) { + persistRequest.getTransaction().registerDerivedRelationship(derivedRelationship); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertMeta.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertMeta.java index 04e2939b4..348bc09c6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertMeta.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/InsertMeta.java @@ -1,208 +1,189 @@ -/** - * 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.persist.dml; - -import java.sql.SQLException; -import java.util.Set; - -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.InheritInfo; -import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable; -import com.avaje.ebeaninternal.server.persist.dmlbind.BindableDiscriminator; -import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId; - -/** - * Meta data for insert handler. The meta data is for a particular bean type. It - * is considered immutable and is thread safe. - */ -public final class InsertMeta { - - private final String sqlNullId; - - private final String sqlWithId; - - private final BindableId id; - - private final Bindable discriminator; - - private final Bindable all; - - private final boolean supportsGetGeneratedKeys; - - private final boolean concatinatedKey; - - private final String tableName; - - /** - * Used for DB that do not support getGeneratedKeys. - */ - private final String selectLastInsertedId; - - private final Bindable shadowFKey; - - private final String[] identityDbColumns; - - private final boolean emptyStringToNull; - - public InsertMeta(DatabasePlatform dbPlatform, BeanDescriptor desc, Bindable shadowFKey, BindableId id, Bindable all) { - - this.emptyStringToNull = dbPlatform.isTreatEmptyStringsAsNull(); - this.tableName = desc.getBaseTable(); - this.discriminator = getDiscriminator(desc); - this.id = id; - this.all = all; - this.shadowFKey = shadowFKey; - - this.sqlWithId = genSql(false, null); - - // only available for single Id property - if (id.isConcatenated()) { - // concatenated key - this.concatinatedKey = true; - this.identityDbColumns = null; - this.sqlNullId = null; - this.supportsGetGeneratedKeys = false; - this.selectLastInsertedId = null; - - } else { - // insert sql for db identity or sequence insert - this.concatinatedKey = false; - this.identityDbColumns = new String[]{id.getIdentityColumn()}; - this.sqlNullId = genSql(true, null); - this.supportsGetGeneratedKeys = dbPlatform.getDbIdentity().isSupportsGetGeneratedKeys(); - this.selectLastInsertedId = desc.getSelectLastInsertedId(); - } - } - - private static Bindable getDiscriminator(BeanDescriptor desc){ - InheritInfo inheritInfo = desc.getInheritInfo(); - if (inheritInfo != null){ - return new BindableDiscriminator(inheritInfo); - } else { - return null; - } - } - - /** - * Return true if empty strings should be treated as null. - */ - public boolean isEmptyStringToNull() { - return emptyStringToNull; - } - - /** - * Return true if this is a concatenated key. - */ - public boolean isConcatinatedKey() { - return concatinatedKey; - } - - public String[] getIdentityDbColumns() { - return identityDbColumns; - } - - /** - * Returns sql that is used to fetch back the last inserted id. This will - * return null if it should not be used. - *

      - * This is only for DB's that do not support getGeneratedKeys. For MS - * SQLServer 2000 this could return "SELECT (at)(at)IDENTITY as id". - *

      - */ - public String getSelectLastInsertedId() { - return selectLastInsertedId; - } - - /** - * Return true if getGeneratedKeys is supported by the underlying jdbc - * driver and database. - */ - public boolean supportsGetGeneratedKeys() { - return supportsGetGeneratedKeys; - } - - /** - * Return true if the Id can be derived from other property values. - */ - public boolean deriveConcatenatedId(PersistRequestBean persist) { - return id.deriveConcatenatedId(persist); - } - - /** - * Bind the request based on whether the id value(s) are null. - */ - public void bind(DmlHandler request, Object bean, boolean withId) throws SQLException { - - if (withId) { - id.dmlBind(request, false, bean); - } - if (shadowFKey != null){ - shadowFKey.dmlBind(request, false, bean); - } - if (discriminator != null){ - discriminator.dmlBind(request, false, bean); - } - all.dmlBind(request, false, bean); - } - - /** - * get the sql based whether the id value(s) are null. - */ - public String getSql(boolean withId) { - - if (withId) { - return sqlWithId; - } else { - return sqlNullId; - } - } - - private String genSql(boolean nullId, Set loadedProps) { - - GenerateDmlRequest request = new GenerateDmlRequest(emptyStringToNull, loadedProps, null); - request.setInsertSetMode(); - - request.append("insert into ").append(tableName); - request.append(" ("); - - if (!nullId) { - id.dmlInsert(request, false); - } - - if (shadowFKey != null){ - shadowFKey.dmlInsert(request, false); - } - - if (discriminator != null){ - discriminator.dmlInsert(request, false); - } - - all.dmlInsert(request, false); - - request.append(") values ("); - request.append(request.getInsertBindBuffer()); - request.append(")"); - - return request.toString(); - } - -} +package com.avaje.ebeaninternal.server.persist.dml; + +import java.sql.SQLException; +import java.util.Set; + +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable; +import com.avaje.ebeaninternal.server.persist.dmlbind.BindableDiscriminator; +import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId; + +/** + * Meta data for insert handler. The meta data is for a particular bean type. It + * is considered immutable and is thread safe. + */ +public final class InsertMeta { + + private final String sqlNullId; + + private final String sqlWithId; + + private final BindableId id; + + private final Bindable discriminator; + + private final Bindable all; + + private final boolean supportsGetGeneratedKeys; + + private final boolean concatinatedKey; + + private final String tableName; + + /** + * Used for DB that do not support getGeneratedKeys. + */ + private final String selectLastInsertedId; + + private final Bindable shadowFKey; + + private final String[] identityDbColumns; + + private final boolean emptyStringToNull; + + public InsertMeta(DatabasePlatform dbPlatform, BeanDescriptor desc, Bindable shadowFKey, BindableId id, Bindable all) { + + this.emptyStringToNull = dbPlatform.isTreatEmptyStringsAsNull(); + this.tableName = desc.getBaseTable(); + this.discriminator = getDiscriminator(desc); + this.id = id; + this.all = all; + this.shadowFKey = shadowFKey; + + this.sqlWithId = genSql(false, null); + + // only available for single Id property + if (id.isConcatenated()) { + // concatenated key + this.concatinatedKey = true; + this.identityDbColumns = null; + this.sqlNullId = null; + this.supportsGetGeneratedKeys = false; + this.selectLastInsertedId = null; + + } else { + // insert sql for db identity or sequence insert + this.concatinatedKey = false; + this.identityDbColumns = new String[]{id.getIdentityColumn()}; + this.sqlNullId = genSql(true, null); + this.supportsGetGeneratedKeys = dbPlatform.getDbIdentity().isSupportsGetGeneratedKeys(); + this.selectLastInsertedId = desc.getSelectLastInsertedId(); + } + } + + private static Bindable getDiscriminator(BeanDescriptor desc){ + InheritInfo inheritInfo = desc.getInheritInfo(); + if (inheritInfo != null){ + return new BindableDiscriminator(inheritInfo); + } else { + return null; + } + } + + /** + * Return true if empty strings should be treated as null. + */ + public boolean isEmptyStringToNull() { + return emptyStringToNull; + } + + /** + * Return true if this is a concatenated key. + */ + public boolean isConcatinatedKey() { + return concatinatedKey; + } + + public String[] getIdentityDbColumns() { + return identityDbColumns; + } + + /** + * Returns sql that is used to fetch back the last inserted id. This will + * return null if it should not be used. + *

      + * This is only for DB's that do not support getGeneratedKeys. For MS + * SQLServer 2000 this could return "SELECT (at)(at)IDENTITY as id". + *

      + */ + public String getSelectLastInsertedId() { + return selectLastInsertedId; + } + + /** + * Return true if getGeneratedKeys is supported by the underlying jdbc + * driver and database. + */ + public boolean supportsGetGeneratedKeys() { + return supportsGetGeneratedKeys; + } + + /** + * Return true if the Id can be derived from other property values. + */ + public boolean deriveConcatenatedId(PersistRequestBean persist) { + return id.deriveConcatenatedId(persist); + } + + /** + * Bind the request based on whether the id value(s) are null. + */ + public void bind(DmlHandler request, Object bean, boolean withId) throws SQLException { + + if (withId) { + id.dmlBind(request, false, bean); + } + if (shadowFKey != null){ + shadowFKey.dmlBind(request, false, bean); + } + if (discriminator != null){ + discriminator.dmlBind(request, false, bean); + } + all.dmlBind(request, false, bean); + } + + /** + * get the sql based whether the id value(s) are null. + */ + public String getSql(boolean withId) { + + if (withId) { + return sqlWithId; + } else { + return sqlNullId; + } + } + + private String genSql(boolean nullId, Set loadedProps) { + + GenerateDmlRequest request = new GenerateDmlRequest(emptyStringToNull, loadedProps, null); + request.setInsertSetMode(); + + request.append("insert into ").append(tableName); + request.append(" ("); + + if (!nullId) { + id.dmlInsert(request, false); + } + + if (shadowFKey != null){ + shadowFKey.dmlInsert(request, false); + } + + if (discriminator != null){ + discriminator.dmlInsert(request, false); + } + + all.dmlInsert(request, false); + + request.append(") values ("); + request.append(request.getInsertBindBuffer()); + request.append(")"); + + return request.toString(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/MetaFactory.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/MetaFactory.java index 2c7cd0a06..d57559d4d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/MetaFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/MetaFactory.java @@ -1,146 +1,127 @@ -/** - * Copyright (C) 2006 Robin Bygrave - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.persist.dml; - -import java.util.ArrayList; -import java.util.List; - -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebean.config.dbplatform.DbEncrypt; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable; -import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId; -import com.avaje.ebeaninternal.server.persist.dmlbind.BindableList; -import com.avaje.ebeaninternal.server.persist.dmlbind.BindableUnidirectional; -import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryAssocOnes; -import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryBaseProperties; -import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryEmbedded; -import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryId; -import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryVersion; - -/** - * Factory for creating InsertMeta UpdateMeta and DeleteMeta. - */ -public class MetaFactory { - - private final FactoryBaseProperties baseFact; - private final FactoryEmbedded embeddedFact; - private final FactoryVersion versionFact = new FactoryVersion(); - private final FactoryAssocOnes assocOneFact = new FactoryAssocOnes(); - - private final FactoryId idFact = new FactoryId(); - - /** - * Include Lobs in the base statement. Generally true. Oracle9 used to require - * a separate statement for Clobs and Blobs. - */ - private static final boolean includeLobs = true; - - private final DatabasePlatform dbPlatform; - - private final boolean emptyStringAsNull; - - public MetaFactory(DatabasePlatform dbPlatform) { - this.dbPlatform = dbPlatform; - this.emptyStringAsNull = dbPlatform.isTreatEmptyStringsAsNull(); - - // to bind encryption data before or after the encryption key - DbEncrypt dbEncrypt = dbPlatform.getDbEncrypt(); - boolean bindEncryptDataFirst = dbEncrypt == null ? true : dbEncrypt.isBindEncryptDataFirst(); - - this.baseFact = new FactoryBaseProperties(bindEncryptDataFirst); - this.embeddedFact = new FactoryEmbedded(bindEncryptDataFirst); - } - - /** - * Create the UpdateMeta for the given bean type. - */ - public UpdateMeta createUpdate(BeanDescriptor desc) { - - List setList = new ArrayList(); - - baseFact.create(setList, desc, DmlMode.UPDATE, includeLobs); - embeddedFact.create(setList, desc, DmlMode.UPDATE, includeLobs); - assocOneFact.create(setList, desc, DmlMode.UPDATE); - - BindableId id = idFact.createId(desc); - - Bindable ver = versionFact.create(desc); - - List allList = new ArrayList(); - - baseFact.create(allList, desc, DmlMode.WHERE, false); - embeddedFact.create(allList, desc, DmlMode.WHERE, false); - assocOneFact.create(allList, desc, DmlMode.WHERE); - - Bindable setBindable = new BindableList(setList); - Bindable allBindable = new BindableList(allList); - - return new UpdateMeta(emptyStringAsNull, desc, setBindable, id, ver, allBindable); - } - - /** - * Create the DeleteMeta for the given bean type. - */ - public DeleteMeta createDelete(BeanDescriptor desc) { - - BindableId id = idFact.createId(desc); - - Bindable ver = versionFact.create(desc); - - List allList = new ArrayList(); - - baseFact.create(allList, desc, DmlMode.WHERE, false); - embeddedFact.create(allList, desc, DmlMode.WHERE, false); - assocOneFact.create(allList, desc, DmlMode.WHERE); - - Bindable allBindable = new BindableList(allList); - - return new DeleteMeta(emptyStringAsNull, desc, id, ver, allBindable); - } - - /** - * Create the InsertMeta for the given bean type. - */ - public InsertMeta createInsert(BeanDescriptor desc) { - - BindableId id = idFact.createId(desc); - - List allList = new ArrayList(); - - baseFact.create(allList, desc, DmlMode.INSERT, includeLobs); - embeddedFact.create(allList, desc, DmlMode.INSERT, includeLobs); - assocOneFact.create(allList, desc, DmlMode.INSERT); - - Bindable allBindable = new BindableList(allList); - - BeanPropertyAssocOne unidirectional = desc.getUnidirectional(); - - Bindable shadowFkey; - if (unidirectional == null) { - shadowFkey = null; - } else { - shadowFkey = new BindableUnidirectional(desc, unidirectional); - } - - return new InsertMeta(dbPlatform, desc, shadowFkey, id, allBindable); - } -} +package com.avaje.ebeaninternal.server.persist.dml; + +import java.util.ArrayList; +import java.util.List; + +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.config.dbplatform.DbEncrypt; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable; +import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId; +import com.avaje.ebeaninternal.server.persist.dmlbind.BindableList; +import com.avaje.ebeaninternal.server.persist.dmlbind.BindableUnidirectional; +import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryAssocOnes; +import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryBaseProperties; +import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryEmbedded; +import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryId; +import com.avaje.ebeaninternal.server.persist.dmlbind.FactoryVersion; + +/** + * Factory for creating InsertMeta UpdateMeta and DeleteMeta. + */ +public class MetaFactory { + + private final FactoryBaseProperties baseFact; + private final FactoryEmbedded embeddedFact; + private final FactoryVersion versionFact = new FactoryVersion(); + private final FactoryAssocOnes assocOneFact = new FactoryAssocOnes(); + + private final FactoryId idFact = new FactoryId(); + + /** + * Include Lobs in the base statement. Generally true. Oracle9 used to require + * a separate statement for Clobs and Blobs. + */ + private static final boolean includeLobs = true; + + private final DatabasePlatform dbPlatform; + + private final boolean emptyStringAsNull; + + public MetaFactory(DatabasePlatform dbPlatform) { + this.dbPlatform = dbPlatform; + this.emptyStringAsNull = dbPlatform.isTreatEmptyStringsAsNull(); + + // to bind encryption data before or after the encryption key + DbEncrypt dbEncrypt = dbPlatform.getDbEncrypt(); + boolean bindEncryptDataFirst = dbEncrypt == null ? true : dbEncrypt.isBindEncryptDataFirst(); + + this.baseFact = new FactoryBaseProperties(bindEncryptDataFirst); + this.embeddedFact = new FactoryEmbedded(bindEncryptDataFirst); + } + + /** + * Create the UpdateMeta for the given bean type. + */ + public UpdateMeta createUpdate(BeanDescriptor desc) { + + List setList = new ArrayList(); + + baseFact.create(setList, desc, DmlMode.UPDATE, includeLobs); + embeddedFact.create(setList, desc, DmlMode.UPDATE, includeLobs); + assocOneFact.create(setList, desc, DmlMode.UPDATE); + + BindableId id = idFact.createId(desc); + + Bindable ver = versionFact.create(desc); + + List allList = new ArrayList(); + + baseFact.create(allList, desc, DmlMode.WHERE, false); + embeddedFact.create(allList, desc, DmlMode.WHERE, false); + assocOneFact.create(allList, desc, DmlMode.WHERE); + + Bindable setBindable = new BindableList(setList); + Bindable allBindable = new BindableList(allList); + + return new UpdateMeta(emptyStringAsNull, desc, setBindable, id, ver, allBindable); + } + + /** + * Create the DeleteMeta for the given bean type. + */ + public DeleteMeta createDelete(BeanDescriptor desc) { + + BindableId id = idFact.createId(desc); + + Bindable ver = versionFact.create(desc); + + List allList = new ArrayList(); + + baseFact.create(allList, desc, DmlMode.WHERE, false); + embeddedFact.create(allList, desc, DmlMode.WHERE, false); + assocOneFact.create(allList, desc, DmlMode.WHERE); + + Bindable allBindable = new BindableList(allList); + + return new DeleteMeta(emptyStringAsNull, desc, id, ver, allBindable); + } + + /** + * Create the InsertMeta for the given bean type. + */ + public InsertMeta createInsert(BeanDescriptor desc) { + + BindableId id = idFact.createId(desc); + + List allList = new ArrayList(); + + baseFact.create(allList, desc, DmlMode.INSERT, includeLobs); + embeddedFact.create(allList, desc, DmlMode.INSERT, includeLobs); + assocOneFact.create(allList, desc, DmlMode.INSERT); + + Bindable allBindable = new BindableList(allList); + + BeanPropertyAssocOne unidirectional = desc.getUnidirectional(); + + Bindable shadowFkey; + if (unidirectional == null) { + shadowFkey = null; + } else { + shadowFkey = new BindableUnidirectional(desc, unidirectional); + } + + return new InsertMeta(dbPlatform, desc, shadowFkey, id, allBindable); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/PersistHandler.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/PersistHandler.java index ccd1de207..f431f4b71 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/PersistHandler.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/PersistHandler.java @@ -1,53 +1,34 @@ -/** - * 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.persist.dml; - -import java.sql.SQLException; - -/** - * Implementation API for insert update and delete handlers. - */ -public interface PersistHandler { - - /** - * Return the bind log. - */ - public String getBindLog(); - - /** - * Get the sql and bind the statement. - */ - public void bind() throws SQLException; - - /** - * Add this for batch execution. - */ - public void addBatch() throws SQLException; - - /** - * Execute now for non-batch execution. - */ - public void execute() throws SQLException; - - /** - * Close resources including underlying preparedStatement. - */ - public void close() throws SQLException; -} +package com.avaje.ebeaninternal.server.persist.dml; + +import java.sql.SQLException; + +/** + * Implementation API for insert update and delete handlers. + */ +public interface PersistHandler { + + /** + * Return the bind log. + */ + public String getBindLog(); + + /** + * Get the sql and bind the statement. + */ + public void bind() throws SQLException; + + /** + * Add this for batch execution. + */ + public void addBatch() throws SQLException; + + /** + * Execute now for non-batch execution. + */ + public void execute() throws SQLException; + + /** + * Close resources including underlying preparedStatement. + */ + public void close() throws SQLException; +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateHandler.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateHandler.java index fcffedf19..6b42f6200 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateHandler.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateHandler.java @@ -1,122 +1,103 @@ -/** - * 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.persist.dml; - -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.Set; - -import javax.persistence.OptimisticLockException; - -import com.avaje.ebeaninternal.api.DerivedRelationshipData; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.api.SpiUpdatePlan; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.type.DataBind; - -/** - * Update bean handler. - */ -public class UpdateHandler extends DmlHandler { - - - private final UpdateMeta meta; - - private Set updatedProperties; - - private boolean emptySetClause; - - public UpdateHandler(PersistRequestBean persist, UpdateMeta meta) { - super(persist, meta.isEmptyStringAsNull()); - this.meta = meta; - } - - /** - * Generate and bind the update statement. - */ - public void bind() throws SQLException { - - SpiUpdatePlan updatePlan = meta.getUpdatePlan(persistRequest); - - if (updatePlan.isEmptySetClause()) { - emptySetClause = true; - return; - } - - updatedProperties = updatePlan.getProperties(); - - sql = updatePlan.getSql(); - - SpiTransaction t = persistRequest.getTransaction(); - boolean isBatch = t.isBatchThisRequest(); - - PreparedStatement pstmt; - if (isBatch) { - pstmt = getPstmt(t, sql, persistRequest, false); - - } else { - logSql(sql); - pstmt = getPstmt(t, sql, false); - } - dataBind = new DataBind(pstmt); - - bindLogAppend("Binding Update ["); - bindLogAppend(meta.getTableName()); - bindLogAppend("] "); - - meta.bind(persistRequest, this, updatePlan); - - setUpdateGenValues(); - - bindLogAppend("]"); - logBinding(); - } - - @Override - public void addBatch() throws SQLException { - if (!emptySetClause){ - super.addBatch(); - } - } - - /** - * Execute the update in non-batch. - */ - @Override - public void execute() throws SQLException, OptimisticLockException { - if (!emptySetClause){ - int rowCount = dataBind.executeUpdate(); - checkRowCount(rowCount); - setAdditionalProperties(); - } - } - - @Override - public boolean isIncluded(BeanProperty prop) { - - return prop.isDbUpdatable() && (updatedProperties == null || updatedProperties.contains(prop.getName())); - } - - public void registerDerivedRelationship(DerivedRelationshipData derivedRelationship) { - persistRequest.getTransaction().registerDerivedRelationship(derivedRelationship); - } - -} +package com.avaje.ebeaninternal.server.persist.dml; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Set; + +import javax.persistence.OptimisticLockException; + +import com.avaje.ebeaninternal.api.DerivedRelationshipData; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.api.SpiUpdatePlan; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.type.DataBind; + +/** + * Update bean handler. + */ +public class UpdateHandler extends DmlHandler { + + + private final UpdateMeta meta; + + private Set updatedProperties; + + private boolean emptySetClause; + + public UpdateHandler(PersistRequestBean persist, UpdateMeta meta) { + super(persist, meta.isEmptyStringAsNull()); + this.meta = meta; + } + + /** + * Generate and bind the update statement. + */ + public void bind() throws SQLException { + + SpiUpdatePlan updatePlan = meta.getUpdatePlan(persistRequest); + + if (updatePlan.isEmptySetClause()) { + emptySetClause = true; + return; + } + + updatedProperties = updatePlan.getProperties(); + + sql = updatePlan.getSql(); + + SpiTransaction t = persistRequest.getTransaction(); + boolean isBatch = t.isBatchThisRequest(); + + PreparedStatement pstmt; + if (isBatch) { + pstmt = getPstmt(t, sql, persistRequest, false); + + } else { + logSql(sql); + pstmt = getPstmt(t, sql, false); + } + dataBind = new DataBind(pstmt); + + bindLogAppend("Binding Update ["); + bindLogAppend(meta.getTableName()); + bindLogAppend("] "); + + meta.bind(persistRequest, this, updatePlan); + + setUpdateGenValues(); + + bindLogAppend("]"); + logBinding(); + } + + @Override + public void addBatch() throws SQLException { + if (!emptySetClause){ + super.addBatch(); + } + } + + /** + * Execute the update in non-batch. + */ + @Override + public void execute() throws SQLException, OptimisticLockException { + if (!emptySetClause){ + int rowCount = dataBind.executeUpdate(); + checkRowCount(rowCount); + setAdditionalProperties(); + } + } + + @Override + public boolean isIncluded(BeanProperty prop) { + + return prop.isDbUpdatable() && (updatedProperties == null || updatedProperties.contains(prop.getName())); + } + + public void registerDerivedRelationship(DerivedRelationshipData derivedRelationship) { + persistRequest.getTransaction().registerDerivedRelationship(derivedRelationship); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java index 3d614e17d..df1e1179e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdateMeta.java @@ -1,259 +1,240 @@ -/** - * 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.persist.dml; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.api.SpiUpdatePlan; -import com.avaje.ebeaninternal.server.core.ConcurrencyMode; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable; -import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId; -import com.avaje.ebeaninternal.server.persist.dmlbind.BindableList; - -/** - * Meta data for update handler. The meta data is for a particular bean type. It - * is considered immutable and is thread safe. - */ -public final class UpdateMeta { - - private final String sqlVersion; - - private final String sqlNone; - - private final Bindable set; - private final BindableId id; - private final Bindable version; - private final Bindable all; - - private final String tableName; - - private final UpdatePlan modeNoneUpdatePlan; - private final UpdatePlan modeVersionUpdatePlan; - - private final boolean emptyStringAsNull; - - public UpdateMeta(boolean emptyStringAsNull, BeanDescriptor desc, Bindable set, BindableId id, Bindable version, Bindable all) { - this.emptyStringAsNull = emptyStringAsNull; - this.tableName = desc.getBaseTable(); - this.set = set; - this.id = id; - this.version = version; - this.all = all; - - this.sqlNone = genSql(ConcurrencyMode.NONE, null, null); - this.sqlVersion = genSql(ConcurrencyMode.VERSION, null, null); - - this.modeNoneUpdatePlan = new UpdatePlan(ConcurrencyMode.NONE, sqlNone, set); - this.modeVersionUpdatePlan = new UpdatePlan(ConcurrencyMode.VERSION, sqlVersion, set); - - } - - /** - * Return true if empty strings should be treated as null. - */ - public boolean isEmptyStringAsNull() { - return emptyStringAsNull; - } - - /** - * Return the base table name. - */ - public String getTableName() { - return tableName; - } - - /** - * Bind the request based on the concurrency mode. - */ - public void bind(PersistRequestBean persist, DmlHandler bind, SpiUpdatePlan updatePlan) throws SQLException { - - Object bean = persist.getBean(); - - bind.bindLogAppend(" set["); - // bind.setCheckDelta(true); - updatePlan.bindSet(bind, bean); - // bind.setCheckDelta(false); - - bind.bindLogAppend("] where["); - id.dmlBind(bind, false, bean); - - switch (persist.getConcurrencyMode()) { - case VERSION: - version.dmlBind(bind, false, bean); - break; - case ALL: - Object oldBean = persist.getOldValues(); - all.dmlBindWhere(bind, true, oldBean); - break; - - default: - break; - } - } - - /** - * get or generate the sql based on the concurrency mode. - */ - public SpiUpdatePlan getUpdatePlan(PersistRequestBean request) { - - ConcurrencyMode mode = request.determineConcurrencyMode(); - if (request.isDynamicUpdateSql()) { - return getDynamicUpdatePlan(mode, request); - } - - // 'full bean' update... - switch (mode) { - case NONE: - return modeNoneUpdatePlan; - - case VERSION: - return modeVersionUpdatePlan; - - case ALL: - Object oldValues = request.getOldValues(); - if (oldValues == null) { - throw new PersistenceException("OldValues are null?"); - } - String sql = genDynamicWhere(request.getUpdatedProperties(), request.getLoadedProperties(), oldValues); - return new UpdatePlan(ConcurrencyMode.ALL, sql, set); - - default: - throw new RuntimeException("Invalid mode " + mode); - } - } - - private SpiUpdatePlan getDynamicUpdatePlan(ConcurrencyMode mode, PersistRequestBean persistRequest) { - - Set updatedProps = persistRequest.getUpdatedProperties(); - - if (ConcurrencyMode.ALL.equals(mode)) { - // due to is null in where clause we won't bother trying to - // cache plans for ConcurrencyMode.ALL - String sql = genSql(mode, persistRequest, null); - if (sql == null) { - // changed properties must have been updatable=false - return UpdatePlan.EMPTY_SET_CLAUSE; - } else { - return new UpdatePlan(null, mode, sql, set, updatedProps); - } - } - - // we can use a cached UpdatePlan for the changed properties - int hash = mode.hashCode(); - hash = hash * 31 + (updatedProps == null ? 0 : updatedProps.hashCode()); - Integer key = Integer.valueOf(hash); - - BeanDescriptor beanDescriptor = persistRequest.getBeanDescriptor(); - SpiUpdatePlan updatePlan = beanDescriptor.getUpdatePlan(key); - if (updatePlan != null) { - return updatePlan; - } - - // build a new UpdatePlan and cache it - - // build a bindableList that only contains the changed properties - List list = new ArrayList(); - set.addChanged(persistRequest, list); - BindableList bindableList = new BindableList(list); - - // build the SQL for this update statement - String sql = genSql(mode, persistRequest, bindableList); - - updatePlan = new UpdatePlan(key, mode, sql, bindableList, null); - - // add the UpdatePlan to the cache - beanDescriptor.putUpdatePlan(key, updatePlan); - - return updatePlan; - } - - private String genSql(ConcurrencyMode conMode, PersistRequestBean persistRequest, BindableList bindableList) { - - // update set col0=?, col1=?, col2=? where bcol=? and bc1=? and bc2=? - - GenerateDmlRequest request; - if (persistRequest == null) { - // For generation of None and Version DML/SQL - request = new GenerateDmlRequest(emptyStringAsNull); - } else { - request = persistRequest.createGenerateDmlRequest(emptyStringAsNull); - } - - request.append("update ").append(tableName).append(" set "); - - request.setUpdateSetMode(); - if (bindableList != null) { - bindableList.dmlAppend(request, false); - } else { - set.dmlAppend(request, true); - } - - if (request.getBindColumnCount() == 0) { - // update properties must have been updatable=false - // with the result that nothing is in the set clause - return null; - } - - request.append(" where "); - - request.setWhereIdMode(); - id.dmlAppend(request, false); - - if (ConcurrencyMode.VERSION.equals(conMode)) { - if (version == null) { - return null; - } - version.dmlAppend(request, false); - - } else if (ConcurrencyMode.ALL.equals(conMode)) { - - all.dmlWhere(request, true, request.getOldValues()); - } - - return request.toString(); - } - - /** - * Generate the sql dynamically for where using IS NULL for binding null - * values. - */ - private String genDynamicWhere(Set loadedProps, Set whereProps, Object oldBean) { - - // always has a preceding id property(s) so the first - // option is always ' and ' and not blank. - - GenerateDmlRequest request = new GenerateDmlRequest(emptyStringAsNull, loadedProps, whereProps, oldBean); - - request.append(sqlNone); - - request.setWhereMode(); - all.dmlWhere(request, true, oldBean); - - return request.toString(); - } - -} +package com.avaje.ebeaninternal.server.persist.dml; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.api.SpiUpdatePlan; +import com.avaje.ebeaninternal.server.core.ConcurrencyMode; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable; +import com.avaje.ebeaninternal.server.persist.dmlbind.BindableId; +import com.avaje.ebeaninternal.server.persist.dmlbind.BindableList; + +/** + * Meta data for update handler. The meta data is for a particular bean type. It + * is considered immutable and is thread safe. + */ +public final class UpdateMeta { + + private final String sqlVersion; + + private final String sqlNone; + + private final Bindable set; + private final BindableId id; + private final Bindable version; + private final Bindable all; + + private final String tableName; + + private final UpdatePlan modeNoneUpdatePlan; + private final UpdatePlan modeVersionUpdatePlan; + + private final boolean emptyStringAsNull; + + public UpdateMeta(boolean emptyStringAsNull, BeanDescriptor desc, Bindable set, BindableId id, Bindable version, Bindable all) { + this.emptyStringAsNull = emptyStringAsNull; + this.tableName = desc.getBaseTable(); + this.set = set; + this.id = id; + this.version = version; + this.all = all; + + this.sqlNone = genSql(ConcurrencyMode.NONE, null, null); + this.sqlVersion = genSql(ConcurrencyMode.VERSION, null, null); + + this.modeNoneUpdatePlan = new UpdatePlan(ConcurrencyMode.NONE, sqlNone, set); + this.modeVersionUpdatePlan = new UpdatePlan(ConcurrencyMode.VERSION, sqlVersion, set); + + } + + /** + * Return true if empty strings should be treated as null. + */ + public boolean isEmptyStringAsNull() { + return emptyStringAsNull; + } + + /** + * Return the base table name. + */ + public String getTableName() { + return tableName; + } + + /** + * Bind the request based on the concurrency mode. + */ + public void bind(PersistRequestBean persist, DmlHandler bind, SpiUpdatePlan updatePlan) throws SQLException { + + Object bean = persist.getBean(); + + bind.bindLogAppend(" set["); + // bind.setCheckDelta(true); + updatePlan.bindSet(bind, bean); + // bind.setCheckDelta(false); + + bind.bindLogAppend("] where["); + id.dmlBind(bind, false, bean); + + switch (persist.getConcurrencyMode()) { + case VERSION: + version.dmlBind(bind, false, bean); + break; + case ALL: + Object oldBean = persist.getOldValues(); + all.dmlBindWhere(bind, true, oldBean); + break; + + default: + break; + } + } + + /** + * get or generate the sql based on the concurrency mode. + */ + public SpiUpdatePlan getUpdatePlan(PersistRequestBean request) { + + ConcurrencyMode mode = request.determineConcurrencyMode(); + if (request.isDynamicUpdateSql()) { + return getDynamicUpdatePlan(mode, request); + } + + // 'full bean' update... + switch (mode) { + case NONE: + return modeNoneUpdatePlan; + + case VERSION: + return modeVersionUpdatePlan; + + case ALL: + Object oldValues = request.getOldValues(); + if (oldValues == null) { + throw new PersistenceException("OldValues are null?"); + } + String sql = genDynamicWhere(request.getUpdatedProperties(), request.getLoadedProperties(), oldValues); + return new UpdatePlan(ConcurrencyMode.ALL, sql, set); + + default: + throw new RuntimeException("Invalid mode " + mode); + } + } + + private SpiUpdatePlan getDynamicUpdatePlan(ConcurrencyMode mode, PersistRequestBean persistRequest) { + + Set updatedProps = persistRequest.getUpdatedProperties(); + + if (ConcurrencyMode.ALL.equals(mode)) { + // due to is null in where clause we won't bother trying to + // cache plans for ConcurrencyMode.ALL + String sql = genSql(mode, persistRequest, null); + if (sql == null) { + // changed properties must have been updatable=false + return UpdatePlan.EMPTY_SET_CLAUSE; + } else { + return new UpdatePlan(null, mode, sql, set, updatedProps); + } + } + + // we can use a cached UpdatePlan for the changed properties + int hash = mode.hashCode(); + hash = hash * 31 + (updatedProps == null ? 0 : updatedProps.hashCode()); + Integer key = Integer.valueOf(hash); + + BeanDescriptor beanDescriptor = persistRequest.getBeanDescriptor(); + SpiUpdatePlan updatePlan = beanDescriptor.getUpdatePlan(key); + if (updatePlan != null) { + return updatePlan; + } + + // build a new UpdatePlan and cache it + + // build a bindableList that only contains the changed properties + List list = new ArrayList(); + set.addChanged(persistRequest, list); + BindableList bindableList = new BindableList(list); + + // build the SQL for this update statement + String sql = genSql(mode, persistRequest, bindableList); + + updatePlan = new UpdatePlan(key, mode, sql, bindableList, null); + + // add the UpdatePlan to the cache + beanDescriptor.putUpdatePlan(key, updatePlan); + + return updatePlan; + } + + private String genSql(ConcurrencyMode conMode, PersistRequestBean persistRequest, BindableList bindableList) { + + // update set col0=?, col1=?, col2=? where bcol=? and bc1=? and bc2=? + + GenerateDmlRequest request; + if (persistRequest == null) { + // For generation of None and Version DML/SQL + request = new GenerateDmlRequest(emptyStringAsNull); + } else { + request = persistRequest.createGenerateDmlRequest(emptyStringAsNull); + } + + request.append("update ").append(tableName).append(" set "); + + request.setUpdateSetMode(); + if (bindableList != null) { + bindableList.dmlAppend(request, false); + } else { + set.dmlAppend(request, true); + } + + if (request.getBindColumnCount() == 0) { + // update properties must have been updatable=false + // with the result that nothing is in the set clause + return null; + } + + request.append(" where "); + + request.setWhereIdMode(); + id.dmlAppend(request, false); + + if (ConcurrencyMode.VERSION.equals(conMode)) { + if (version == null) { + return null; + } + version.dmlAppend(request, false); + + } else if (ConcurrencyMode.ALL.equals(conMode)) { + + all.dmlWhere(request, true, request.getOldValues()); + } + + return request.toString(); + } + + /** + * Generate the sql dynamically for where using IS NULL for binding null + * values. + */ + private String genDynamicWhere(Set loadedProps, Set whereProps, Object oldBean) { + + // always has a preceding id property(s) so the first + // option is always ' and ' and not blank. + + GenerateDmlRequest request = new GenerateDmlRequest(emptyStringAsNull, loadedProps, whereProps, oldBean); + + request.append(sqlNone); + + request.setWhereMode(); + all.dmlWhere(request, true, oldBean); + + return request.toString(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdatePlan.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdatePlan.java index b3410cfc0..ed5e71686 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdatePlan.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dml/UpdatePlan.java @@ -1,172 +1,153 @@ -/** - * 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.persist.dml; - -import java.sql.SQLException; -import java.util.Set; - -import com.avaje.ebeaninternal.api.SpiUpdatePlan; -import com.avaje.ebeaninternal.server.core.ConcurrencyMode; -import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable; - -/** - * Cachable plan for executing bean updates for a given set of changed - * properties. - * - * @author rbygrave - */ -public class UpdatePlan implements SpiUpdatePlan { - - /** - * Special plan used when there is nothing in the set clause and the update - * should in fact be skipped. Occurs when the updated properties have - * updatable=false in their deployment. - */ - public static final UpdatePlan EMPTY_SET_CLAUSE = new UpdatePlan(); - - private final Integer key; - - private final ConcurrencyMode mode; - - private final String sql; - - private final Bindable set; - - private final Set properties; - - private final boolean checkIncludes; - - private final long timeCreated; - - private final boolean emptySetClause; - - private Long timeLastUsed; - - /** - * Create a non cachable UpdatePlan. - */ - public UpdatePlan(ConcurrencyMode mode, String sql, Bindable set) { - - this(null, mode, sql, set, null); - } - - /** - * Create a cachable UpdatePlan with a given key. - */ - public UpdatePlan(Integer key, ConcurrencyMode mode, String sql, - Bindable set, Set properties) { - - this.emptySetClause = false; - this.key = key; - this.mode = mode; - this.sql = sql; - this.set = set; - this.properties = properties; - this.checkIncludes = properties != null; - this.timeCreated = System.currentTimeMillis(); - } - - /** - * Special constructor for emptySetClause=true instance. - */ - private UpdatePlan(){ - this.emptySetClause = true; - this.key = Integer.valueOf(0); - this.mode = ConcurrencyMode.NONE; - this.sql = null; - this.set = null; - this.properties = null; - this.checkIncludes = false; - this.timeCreated = 0; - } - - - public boolean isEmptySetClause() { - return emptySetClause; - } - - /** - * Run the prepared statement binding for the 'update set' properties. - */ - public void bindSet(DmlHandler bind, Object bean) throws SQLException { - - set.dmlBind(bind, checkIncludes, bean); - - // not strictly 'thread safe' but object assignment is atomic - Long touched = Long.valueOf(System.currentTimeMillis()); - this.timeLastUsed = touched; - } - - /** - * Return the time this plan was created. - */ - public long getTimeCreated() { - return timeCreated; - } - - /** - * Return the time this plan was last used. - */ - public Long getTimeLastUsed() { - - // not thread safe but atomic - return timeLastUsed; - } - - /** - * Return the hash key. - */ - public Integer getKey() { - return key; - } - - /** - * Return the concurrency mode for this plan. - */ - public ConcurrencyMode getMode() { - return mode; - } - - /** - * Return the DML statement. - */ - public String getSql() { - return sql; - } - - /** - * Return the Bindable properties for the update set. - */ - public Bindable getSet() { - return set; - } - - /** - * Return the set of changed properties. - *

      - * This can return null when all properties in the set are being bound in - * the update statement. - *

      - */ - public Set getProperties() { - return properties; - } - -} +package com.avaje.ebeaninternal.server.persist.dml; + +import java.sql.SQLException; +import java.util.Set; + +import com.avaje.ebeaninternal.api.SpiUpdatePlan; +import com.avaje.ebeaninternal.server.core.ConcurrencyMode; +import com.avaje.ebeaninternal.server.persist.dmlbind.Bindable; + +/** + * Cachable plan for executing bean updates for a given set of changed + * properties. + * + * @author rbygrave + */ +public class UpdatePlan implements SpiUpdatePlan { + + /** + * Special plan used when there is nothing in the set clause and the update + * should in fact be skipped. Occurs when the updated properties have + * updatable=false in their deployment. + */ + public static final UpdatePlan EMPTY_SET_CLAUSE = new UpdatePlan(); + + private final Integer key; + + private final ConcurrencyMode mode; + + private final String sql; + + private final Bindable set; + + private final Set properties; + + private final boolean checkIncludes; + + private final long timeCreated; + + private final boolean emptySetClause; + + private Long timeLastUsed; + + /** + * Create a non cachable UpdatePlan. + */ + public UpdatePlan(ConcurrencyMode mode, String sql, Bindable set) { + + this(null, mode, sql, set, null); + } + + /** + * Create a cachable UpdatePlan with a given key. + */ + public UpdatePlan(Integer key, ConcurrencyMode mode, String sql, + Bindable set, Set properties) { + + this.emptySetClause = false; + this.key = key; + this.mode = mode; + this.sql = sql; + this.set = set; + this.properties = properties; + this.checkIncludes = properties != null; + this.timeCreated = System.currentTimeMillis(); + } + + /** + * Special constructor for emptySetClause=true instance. + */ + private UpdatePlan(){ + this.emptySetClause = true; + this.key = Integer.valueOf(0); + this.mode = ConcurrencyMode.NONE; + this.sql = null; + this.set = null; + this.properties = null; + this.checkIncludes = false; + this.timeCreated = 0; + } + + + public boolean isEmptySetClause() { + return emptySetClause; + } + + /** + * Run the prepared statement binding for the 'update set' properties. + */ + public void bindSet(DmlHandler bind, Object bean) throws SQLException { + + set.dmlBind(bind, checkIncludes, bean); + + // not strictly 'thread safe' but object assignment is atomic + Long touched = Long.valueOf(System.currentTimeMillis()); + this.timeLastUsed = touched; + } + + /** + * Return the time this plan was created. + */ + public long getTimeCreated() { + return timeCreated; + } + + /** + * Return the time this plan was last used. + */ + public Long getTimeLastUsed() { + + // not thread safe but atomic + return timeLastUsed; + } + + /** + * Return the hash key. + */ + public Integer getKey() { + return key; + } + + /** + * Return the concurrency mode for this plan. + */ + public ConcurrencyMode getMode() { + return mode; + } + + /** + * Return the DML statement. + */ + public String getSql() { + return sql; + } + + /** + * Return the Bindable properties for the update set. + */ + public Bindable getSet() { + return set; + } + + /** + * Return the set of changed properties. + *

      + * This can return null when all properties in the set are being bound in + * the update statement. + *

      + */ + public Set getProperties() { + return properties; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/Bindable.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/Bindable.java index 21ebf8dec..46d4953a0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/Bindable.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/Bindable.java @@ -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.persist.dmlbind; - -import java.sql.SQLException; -import java.util.List; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; - -/** - * Item held by Meta objects used to generate and bind bean insert update and - * delete statements. - *

      - * An implementation is expected to be immutable and thread safe. - *

      - *

      - * The design is to take a bean structure with embedded and associated objects - * etc and flatten that into lists of Bindable objects. These are put into - * InsertMeta UpdateMeta and DeleteMeta objects to support the generation of DML - * and binding of statements in a fast and painless manor. - *

      - */ -public interface Bindable { - - /** - * For Updates including only changed properties add the Bindable to the - * list if it should be included in the 'update set'. - */ - public void addChanged(PersistRequestBean request, List list); - - /** - * append sql to the buffer with prefix and suffix options. - */ - public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes); - - /** - * append sql to the buffer with prefix and suffix options. - */ - public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes); - - /** - * For WHERE clauses append sql to the buffer with prefix and suffix - * options. These need to take into account binding of null values. - */ - public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean); - - /** - * Bind given the request and bean. The bean could be the oldValues bean - * when binding a update or delete where clause with ALL concurrency mode. - */ - public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) - throws SQLException; - - public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) - throws SQLException; - -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.sql.SQLException; +import java.util.List; + +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; + +/** + * Item held by Meta objects used to generate and bind bean insert update and + * delete statements. + *

      + * An implementation is expected to be immutable and thread safe. + *

      + *

      + * The design is to take a bean structure with embedded and associated objects + * etc and flatten that into lists of Bindable objects. These are put into + * InsertMeta UpdateMeta and DeleteMeta objects to support the generation of DML + * and binding of statements in a fast and painless manor. + *

      + */ +public interface Bindable { + + /** + * For Updates including only changed properties add the Bindable to the + * list if it should be included in the 'update set'. + */ + public void addChanged(PersistRequestBean request, List list); + + /** + * append sql to the buffer with prefix and suffix options. + */ + public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes); + + /** + * append sql to the buffer with prefix and suffix options. + */ + public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes); + + /** + * For WHERE clauses append sql to the buffer with prefix and suffix + * options. These need to take into account binding of null values. + */ + public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean); + + /** + * Bind given the request and bean. The bean could be the oldValues bean + * when binding a update or delete where clause with ALL concurrency mode. + */ + public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) + throws SQLException; + + public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) + throws SQLException; + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableAssocOne.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableAssocOne.java index 383029776..84a8f4b56 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableAssocOne.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableAssocOne.java @@ -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.persist.dmlbind; - -import java.sql.SQLException; -import java.util.List; - -import com.avaje.ebeaninternal.api.DerivedRelationshipData; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.deploy.id.ImportedId; -import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; - -/** - * Bindable for an ManyToOne or OneToOne associated bean. - */ -public class BindableAssocOne implements Bindable { - - private final BeanPropertyAssocOne assocOne; - - private final ImportedId importedId; - - public BindableAssocOne(BeanPropertyAssocOne assocOne) { - this.assocOne = assocOne; - this.importedId = assocOne.getImportedId(); - } - - public String toString() { - return "BindableAssocOne " + assocOne; - } - - public void addChanged(PersistRequestBean request, List list) { - if (request.hasChanged(assocOne)) { - list.add(this); - } - } - - public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { - dmlAppend(request, checkIncludes); - } - - public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { - if (checkIncludes && !request.isIncluded(assocOne)) { - return; - } - importedId.dmlAppend(request); - } - - /** - * Used for dynamic where clause generation. - */ - public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { - if (checkIncludes && !request.isIncludedWhere(assocOne)) { - return; - } - Object assocBean = assocOne.getValue(bean); - importedId.dmlWhere(request, assocBean); - } - - public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - if (checkIncludes && !request.isIncluded(assocOne)) { - return; - } - dmlBind(request, bean, true); - } - - public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - if (checkIncludes && !request.isIncludedWhere(assocOne)) { - return; - } - dmlBind(request, bean, false); - } - - private void dmlBind(BindableRequest request, Object bean, boolean bindNull) - throws SQLException { - - Object assocBean = assocOne.getValue(bean); - Object boundValue = importedId.bind(request, assocBean, bindNull); - if (bindNull && boundValue == null && assocBean != null){ - // this is the scenario for a derived foreign key - // which will require an additional update - // register for post insert of assocBean - // update of bean set ... importedId.getLogicalName(); - // value of assocBean.getId - DerivedRelationshipData d = new DerivedRelationshipData(assocBean, assocOne.getName(), bean); - request.registerDerivedRelationship(d); - } - } - -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.sql.SQLException; +import java.util.List; + +import com.avaje.ebeaninternal.api.DerivedRelationshipData; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.id.ImportedId; +import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; + +/** + * Bindable for an ManyToOne or OneToOne associated bean. + */ +public class BindableAssocOne implements Bindable { + + private final BeanPropertyAssocOne assocOne; + + private final ImportedId importedId; + + public BindableAssocOne(BeanPropertyAssocOne assocOne) { + this.assocOne = assocOne; + this.importedId = assocOne.getImportedId(); + } + + public String toString() { + return "BindableAssocOne " + assocOne; + } + + public void addChanged(PersistRequestBean request, List list) { + if (request.hasChanged(assocOne)) { + list.add(this); + } + } + + public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { + dmlAppend(request, checkIncludes); + } + + public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { + if (checkIncludes && !request.isIncluded(assocOne)) { + return; + } + importedId.dmlAppend(request); + } + + /** + * Used for dynamic where clause generation. + */ + public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { + if (checkIncludes && !request.isIncludedWhere(assocOne)) { + return; + } + Object assocBean = assocOne.getValue(bean); + importedId.dmlWhere(request, assocBean); + } + + public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + if (checkIncludes && !request.isIncluded(assocOne)) { + return; + } + dmlBind(request, bean, true); + } + + public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + if (checkIncludes && !request.isIncludedWhere(assocOne)) { + return; + } + dmlBind(request, bean, false); + } + + private void dmlBind(BindableRequest request, Object bean, boolean bindNull) + throws SQLException { + + Object assocBean = assocOne.getValue(bean); + Object boundValue = importedId.bind(request, assocBean, bindNull); + if (bindNull && boundValue == null && assocBean != null){ + // this is the scenario for a derived foreign key + // which will require an additional update + // register for post insert of assocBean + // update of bean set ... importedId.getLogicalName(); + // value of assocBean.getId + DerivedRelationshipData d = new DerivedRelationshipData(assocBean, assocOne.getName(), bean); + request.registerDerivedRelationship(d); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableCompound.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableCompound.java index 5735adfd3..49330db7d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableCompound.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableCompound.java @@ -1,106 +1,87 @@ -/** - * 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.persist.dmlbind; - -import java.sql.SQLException; -import java.util.Arrays; -import java.util.List; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; -import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; - -/** - * Bindable for a Immutable Compound value object. - */ -public class BindableCompound implements Bindable { - - private final Bindable[] items; - - private final BeanPropertyCompound compound; - - public BindableCompound(BeanPropertyCompound embProp, List list) { - this.compound = embProp; - this.items = list.toArray(new Bindable[list.size()]); - } - - public String toString() { - return "BindableCompound " + compound + " items:" + Arrays.toString(items); - } - - public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { - dmlAppend(request, checkIncludes); - } - - public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { - if (checkIncludes && !request.isIncluded(compound)) { - return; - } - - for (int i = 0; i < items.length; i++) { - items[i].dmlAppend(request, false); - } - } - - /** - * Used for dynamic where clause generation. - */ - public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object origBean) { - if (checkIncludes && !request.isIncludedWhere(compound)) { - return; - } - - Object valueObject = compound.getValue(origBean); - - for (int i = 0; i < items.length; i++) { - items[i].dmlWhere(request, false, valueObject); - } - } - - public void addChanged(PersistRequestBean request, List list) { - if (request.hasChanged(compound)) { - list.add(this); - } - } - - public void dmlBind(BindableRequest bindRequest, boolean checkIncludes, Object bean) throws SQLException { - if (checkIncludes && !bindRequest.isIncluded(compound)) { - return; - } - - Object valueObject = compound.getValue(bean); - - for (int i = 0; i < items.length; i++) { - items[i].dmlBind(bindRequest, false, valueObject); - } - } - - public void dmlBindWhere(BindableRequest bindRequest, boolean checkIncludes, Object bean) throws SQLException { - if (checkIncludes && !bindRequest.isIncludedWhere(compound)) { - return; - } - - Object valueObject = compound.getValue(bean); - - for (int i = 0; i < items.length; i++) { - items[i].dmlBindWhere(bindRequest, false, valueObject); - } - } -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.sql.SQLException; +import java.util.Arrays; +import java.util.List; + +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; + +/** + * Bindable for a Immutable Compound value object. + */ +public class BindableCompound implements Bindable { + + private final Bindable[] items; + + private final BeanPropertyCompound compound; + + public BindableCompound(BeanPropertyCompound embProp, List list) { + this.compound = embProp; + this.items = list.toArray(new Bindable[list.size()]); + } + + public String toString() { + return "BindableCompound " + compound + " items:" + Arrays.toString(items); + } + + public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { + dmlAppend(request, checkIncludes); + } + + public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { + if (checkIncludes && !request.isIncluded(compound)) { + return; + } + + for (int i = 0; i < items.length; i++) { + items[i].dmlAppend(request, false); + } + } + + /** + * Used for dynamic where clause generation. + */ + public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object origBean) { + if (checkIncludes && !request.isIncludedWhere(compound)) { + return; + } + + Object valueObject = compound.getValue(origBean); + + for (int i = 0; i < items.length; i++) { + items[i].dmlWhere(request, false, valueObject); + } + } + + public void addChanged(PersistRequestBean request, List list) { + if (request.hasChanged(compound)) { + list.add(this); + } + } + + public void dmlBind(BindableRequest bindRequest, boolean checkIncludes, Object bean) throws SQLException { + if (checkIncludes && !bindRequest.isIncluded(compound)) { + return; + } + + Object valueObject = compound.getValue(bean); + + for (int i = 0; i < items.length; i++) { + items[i].dmlBind(bindRequest, false, valueObject); + } + } + + public void dmlBindWhere(BindableRequest bindRequest, boolean checkIncludes, Object bean) throws SQLException { + if (checkIncludes && !bindRequest.isIncludedWhere(compound)) { + return; + } + + Object valueObject = compound.getValue(bean); + + for (int i = 0; i < items.length; i++) { + items[i].dmlBindWhere(bindRequest, false, valueObject); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableDiscriminator.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableDiscriminator.java index 1c70a136c..2f1336b13 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableDiscriminator.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableDiscriminator.java @@ -1,79 +1,60 @@ -/** - * 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.persist.dmlbind; - -import java.sql.SQLException; -import java.util.List; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.InheritInfo; -import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; - -/** - * Bindable for inserting a discriminator value. - */ -public class BindableDiscriminator implements Bindable { - - private final String columnName; - private final Object discValue; - private final int sqlType; - - public BindableDiscriminator(InheritInfo inheritInfo) { - this.columnName = inheritInfo.getDiscriminatorColumn(); - this.discValue = inheritInfo.getDiscriminatorValue(); - this.sqlType = inheritInfo.getDiscriminatorType(); - } - - public String toString() { - return columnName + " = " + discValue; - } - - public void addChanged(PersistRequestBean request, List list) { - throw new PersistenceException("Never called (only for inserts)"); - } - - public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { - dmlAppend(request, checkIncludes); - } - - /** - * Never used in where clause. - */ - public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { - // never used in where - } - - public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { - request.appendColumn(columnName); - } - - public void dmlBind(BindableRequest bindRequest, boolean checkIncludes, Object bean) throws SQLException { - - bindRequest.bind(columnName, discValue, sqlType); - } - - public void dmlBindWhere(BindableRequest bindRequest, boolean checkIncludes, Object bean) throws SQLException { - - bindRequest.bind(columnName, discValue, sqlType); - } - -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.sql.SQLException; +import java.util.List; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; + +/** + * Bindable for inserting a discriminator value. + */ +public class BindableDiscriminator implements Bindable { + + private final String columnName; + private final Object discValue; + private final int sqlType; + + public BindableDiscriminator(InheritInfo inheritInfo) { + this.columnName = inheritInfo.getDiscriminatorColumn(); + this.discValue = inheritInfo.getDiscriminatorValue(); + this.sqlType = inheritInfo.getDiscriminatorType(); + } + + public String toString() { + return columnName + " = " + discValue; + } + + public void addChanged(PersistRequestBean request, List list) { + throw new PersistenceException("Never called (only for inserts)"); + } + + public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { + dmlAppend(request, checkIncludes); + } + + /** + * Never used in where clause. + */ + public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { + // never used in where + } + + public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { + request.appendColumn(columnName); + } + + public void dmlBind(BindableRequest bindRequest, boolean checkIncludes, Object bean) throws SQLException { + + bindRequest.bind(columnName, discValue, sqlType); + } + + public void dmlBindWhere(BindableRequest bindRequest, boolean checkIncludes, Object bean) throws SQLException { + + bindRequest.bind(columnName, discValue, sqlType); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEmbedded.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEmbedded.java index dab9950f4..aceb49f35 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEmbedded.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEmbedded.java @@ -1,139 +1,120 @@ -/** - * 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.persist.dmlbind; - -import java.sql.SQLException; -import java.util.Arrays; -import java.util.List; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; - -/** - * Bindable for a Embedded bean. - */ -public class BindableEmbedded implements Bindable { - - private final Bindable[] items; - - private final BeanPropertyAssocOne embProp; - - public BindableEmbedded(BeanPropertyAssocOne embProp, List list) { - this.embProp = embProp; - this.items = list.toArray(new Bindable[list.size()]); - } - - public String toString() { - return "BindableEmbedded " + embProp + " items:" + Arrays.toString(items); - } - - public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { - dmlAppend(request, checkIncludes); - } - - public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { - if (checkIncludes && !request.isIncluded(embProp)) { - return; - } - - for (int i = 0; i < items.length; i++) { - items[i].dmlAppend(request, false); - } - } - - /** - * Used for dynamic where clause generation. - */ - public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object origBean) { - if (checkIncludes && !request.isIncludedWhere(embProp)) { - return; - } - Object embBean = embProp.getValue(origBean); - Object oldValues = getOldValue(embBean); - - for (int i = 0; i < items.length; i++) { - items[i].dmlWhere(request, false, oldValues); - } - } - - public void addChanged(PersistRequestBean request, List list) { - if (request.hasChanged(embProp)) { - list.add(this); - } - } - - public void dmlBind(BindableRequest bindRequest, boolean checkIncludes, Object bean) - throws SQLException { - - if (checkIncludes && !bindRequest.isIncluded(embProp)) { - return; - } - - // get the embedded bean - Object embBean = embProp.getValue(bean); - - for (int i = 0; i < items.length; i++) { - items[i].dmlBind(bindRequest, false, embBean); - } - } - - public void dmlBindWhere(BindableRequest bindRequest, boolean checkIncludes, Object bean) - throws SQLException { - - if (checkIncludes && !bindRequest.isIncludedWhere(embProp)) { - return; - } - - // get the embedded bean - Object embBean = embProp.getValue(bean); - Object oldEmbBean = getOldValue(embBean); - - for (int i = 0; i < items.length; i++) { - items[i].dmlBindWhere(bindRequest, false, oldEmbBean); - } - } - - /** - * Get the old bean which will have the original values. - *

      - * These are bound to the WHERE clause for updates. - *

      - */ - private Object getOldValue(Object embBean) { - - Object oldValues = null; - - if (embBean instanceof EntityBean) { - // get the old embedded bean (with the original values) - oldValues = ((EntityBean) embBean)._ebean_getIntercept().getOldValues(); - } - - if (oldValues == null) { - // this embedded bean was not modified - // (or not an EntityBean) - oldValues = embBean; - } - - return oldValues; - } - -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.sql.SQLException; +import java.util.Arrays; +import java.util.List; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; + +/** + * Bindable for a Embedded bean. + */ +public class BindableEmbedded implements Bindable { + + private final Bindable[] items; + + private final BeanPropertyAssocOne embProp; + + public BindableEmbedded(BeanPropertyAssocOne embProp, List list) { + this.embProp = embProp; + this.items = list.toArray(new Bindable[list.size()]); + } + + public String toString() { + return "BindableEmbedded " + embProp + " items:" + Arrays.toString(items); + } + + public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { + dmlAppend(request, checkIncludes); + } + + public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { + if (checkIncludes && !request.isIncluded(embProp)) { + return; + } + + for (int i = 0; i < items.length; i++) { + items[i].dmlAppend(request, false); + } + } + + /** + * Used for dynamic where clause generation. + */ + public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object origBean) { + if (checkIncludes && !request.isIncludedWhere(embProp)) { + return; + } + Object embBean = embProp.getValue(origBean); + Object oldValues = getOldValue(embBean); + + for (int i = 0; i < items.length; i++) { + items[i].dmlWhere(request, false, oldValues); + } + } + + public void addChanged(PersistRequestBean request, List list) { + if (request.hasChanged(embProp)) { + list.add(this); + } + } + + public void dmlBind(BindableRequest bindRequest, boolean checkIncludes, Object bean) + throws SQLException { + + if (checkIncludes && !bindRequest.isIncluded(embProp)) { + return; + } + + // get the embedded bean + Object embBean = embProp.getValue(bean); + + for (int i = 0; i < items.length; i++) { + items[i].dmlBind(bindRequest, false, embBean); + } + } + + public void dmlBindWhere(BindableRequest bindRequest, boolean checkIncludes, Object bean) + throws SQLException { + + if (checkIncludes && !bindRequest.isIncludedWhere(embProp)) { + return; + } + + // get the embedded bean + Object embBean = embProp.getValue(bean); + Object oldEmbBean = getOldValue(embBean); + + for (int i = 0; i < items.length; i++) { + items[i].dmlBindWhere(bindRequest, false, oldEmbBean); + } + } + + /** + * Get the old bean which will have the original values. + *

      + * These are bound to the WHERE clause for updates. + *

      + */ + private Object getOldValue(Object embBean) { + + Object oldValues = null; + + if (embBean instanceof EntityBean) { + // get the old embedded bean (with the original values) + oldValues = ((EntityBean) embBean)._ebean_getIntercept().getOldValues(); + } + + if (oldValues == null) { + // this embedded bean was not modified + // (or not an EntityBean) + oldValues = embBean; + } + + return oldValues; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEncryptedProperty.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEncryptedProperty.java index af00059b7..5f6b02bfd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEncryptedProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableEncryptedProperty.java @@ -1,144 +1,125 @@ -/** - * 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.persist.dmlbind; - -import java.sql.SQLException; -import java.sql.Types; -import java.util.List; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; - -/** - * Bindable for a DB encrypted BeanProperty. - */ -public class BindableEncryptedProperty implements Bindable { - - private final BeanProperty prop; - - private final boolean bindEncryptDataFirst; - - public BindableEncryptedProperty(BeanProperty prop, boolean bindEncryptDataFirst) { - this.prop = prop; - this.bindEncryptDataFirst = bindEncryptDataFirst; - } - - public String toString() { - return prop.toString(); - } - - public void addChanged(PersistRequestBean request, List list) { - if (request.hasChanged(prop)) { - list.add(this); - } - } - - public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { - - if (checkIncludes && !request.isIncluded(prop)) { - return; - } - // columnName - // AES_ENCRYPT(?,?) - request.appendColumn(prop.getDbColumn(), prop.getDbBind()); - } - - public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { - - if (checkIncludes && !request.isIncluded(prop)) { - return; - } - // columnName = AES_ENCRYPT(?,?) - request.appendColumn(prop.getDbColumn(), "=", prop.getDbBind()); - } - - /** - * Used for dynamic where clause generation. - */ - public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { - // only include encrypted property in where when it is included - // in the update as well (so not using isIncludedWhere) - if (checkIncludes && !request.isIncluded(prop)) { - return; - } - - if (bean == null || request.isDbNull(prop.getValue(bean))) { - request.appendColumnIsNull(prop.getDbColumn()); - - } else { - // ? = AES_DECRYPT(columnName,?) - request.appendColumn("? = ", prop.getDecryptSql()); - } - } - - /** - * Bind a value in a Insert SET clause. - */ - public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) - throws SQLException { - - if (checkIncludes && !request.isIncluded(prop)) { - return; - } - Object value = null; - if (bean != null) { - value = prop.getValue(bean); - } - - // get Encrypt key - String encryptKeyValue = prop.getEncryptKey().getStringValue(); - - if (!bindEncryptDataFirst){ - // H2 encrypt function ... different parameter order - request.bindNoLog(encryptKeyValue, Types.VARCHAR, prop.getName() + "=****"); - } - request.bindNoLog(value, prop, prop.getName(), true); - - if (bindEncryptDataFirst){ - // MySql, Postgres, Oracle - request.bindNoLog(encryptKeyValue, Types.VARCHAR, prop.getName() + "=****"); - } - - } - - - /** - * Bind a value in a Insert SET clause. - */ - public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) - throws SQLException { - - if (checkIncludes && !request.isIncluded(prop)) { - return; - } - Object value = null; - if (bean != null) { - value = prop.getValue(bean); - } - - // get Encrypt key - String encryptKeyValue = prop.getEncryptKey().getStringValue(); - - request.bind(value, prop, prop.getName(), false); - request.bindNoLog(encryptKeyValue, Types.VARCHAR, prop.getName() + "=****"); - - } -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.sql.SQLException; +import java.sql.Types; +import java.util.List; + +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; + +/** + * Bindable for a DB encrypted BeanProperty. + */ +public class BindableEncryptedProperty implements Bindable { + + private final BeanProperty prop; + + private final boolean bindEncryptDataFirst; + + public BindableEncryptedProperty(BeanProperty prop, boolean bindEncryptDataFirst) { + this.prop = prop; + this.bindEncryptDataFirst = bindEncryptDataFirst; + } + + public String toString() { + return prop.toString(); + } + + public void addChanged(PersistRequestBean request, List list) { + if (request.hasChanged(prop)) { + list.add(this); + } + } + + public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { + + if (checkIncludes && !request.isIncluded(prop)) { + return; + } + // columnName + // AES_ENCRYPT(?,?) + request.appendColumn(prop.getDbColumn(), prop.getDbBind()); + } + + public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { + + if (checkIncludes && !request.isIncluded(prop)) { + return; + } + // columnName = AES_ENCRYPT(?,?) + request.appendColumn(prop.getDbColumn(), "=", prop.getDbBind()); + } + + /** + * Used for dynamic where clause generation. + */ + public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { + // only include encrypted property in where when it is included + // in the update as well (so not using isIncludedWhere) + if (checkIncludes && !request.isIncluded(prop)) { + return; + } + + if (bean == null || request.isDbNull(prop.getValue(bean))) { + request.appendColumnIsNull(prop.getDbColumn()); + + } else { + // ? = AES_DECRYPT(columnName,?) + request.appendColumn("? = ", prop.getDecryptSql()); + } + } + + /** + * Bind a value in a Insert SET clause. + */ + public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) + throws SQLException { + + if (checkIncludes && !request.isIncluded(prop)) { + return; + } + Object value = null; + if (bean != null) { + value = prop.getValue(bean); + } + + // get Encrypt key + String encryptKeyValue = prop.getEncryptKey().getStringValue(); + + if (!bindEncryptDataFirst){ + // H2 encrypt function ... different parameter order + request.bindNoLog(encryptKeyValue, Types.VARCHAR, prop.getName() + "=****"); + } + request.bindNoLog(value, prop, prop.getName(), true); + + if (bindEncryptDataFirst){ + // MySql, Postgres, Oracle + request.bindNoLog(encryptKeyValue, Types.VARCHAR, prop.getName() + "=****"); + } + + } + + + /** + * Bind a value in a Insert SET clause. + */ + public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) + throws SQLException { + + if (checkIncludes && !request.isIncluded(prop)) { + return; + } + Object value = null; + if (bean != null) { + value = prop.getValue(bean); + } + + // get Encrypt key + String encryptKeyValue = prop.getEncryptKey().getStringValue(); + + request.bind(value, prop, prop.getName(), false); + request.bindNoLog(encryptKeyValue, Types.VARCHAR, prop.getName() + "=****"); + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableId.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableId.java index 86ad3d5ee..d5f5243b9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableId.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableId.java @@ -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.persist.dmlbind; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; - -/** - * Adds support for id creation for concatenated ids on intersection tables. - *

      - * Specifically if the concatenated id object is null on insert this can be - * built from the matching ManyToOne associated beans. For example RoleUserId - * embeddedId object could be built from the associated Role and User beans. - *

      - *

      - * This is only attempted if the id is null when it gets to the insert. - *

      - */ -public interface BindableId extends Bindable { - - /** - * Return true if there is no Id properties at all. - */ - public boolean isEmpty(); - - /** - * Return true if this is a concatenated key. - */ - public boolean isConcatenated(); - - /** - * Return the DB Column to use with genGeneratedKeys. - */ - public String getIdentityColumn(); - - /** - * Create the concatenated id for inserts with PFK relationships. - *

      - * Really only where there are ManyToOne assoc beans that make up the - * primary key and the values can be got from those. - *

      - */ - public boolean deriveConcatenatedId(PersistRequestBean persist); - -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import com.avaje.ebeaninternal.server.core.PersistRequestBean; + +/** + * Adds support for id creation for concatenated ids on intersection tables. + *

      + * Specifically if the concatenated id object is null on insert this can be + * built from the matching ManyToOne associated beans. For example RoleUserId + * embeddedId object could be built from the associated Role and User beans. + *

      + *

      + * This is only attempted if the id is null when it gets to the insert. + *

      + */ +public interface BindableId extends Bindable { + + /** + * Return true if there is no Id properties at all. + */ + public boolean isEmpty(); + + /** + * Return true if this is a concatenated key. + */ + public boolean isConcatenated(); + + /** + * Return the DB Column to use with genGeneratedKeys. + */ + public String getIdentityColumn(); + + /** + * Create the concatenated id for inserts with PFK relationships. + *

      + * Really only where there are ManyToOne assoc beans that make up the + * primary key and the values can be got from those. + *

      + */ + public boolean deriveConcatenatedId(PersistRequestBean persist); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdEmbedded.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdEmbedded.java index 05b47a6eb..660c01601 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdEmbedded.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdEmbedded.java @@ -1,148 +1,129 @@ -/** - * 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.persist.dmlbind; - -import java.sql.SQLException; -import java.util.Arrays; -import java.util.List; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -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.server.persist.dml.GenerateDmlRequest; - -/** - * Bindable for a EmbeddedId. - */ -public final class BindableIdEmbedded implements BindableId { - - private final BeanPropertyAssocOne embId; - - private final BeanProperty[] props; - - private final MatchedImportedProperty[] matches; - - public BindableIdEmbedded(BeanPropertyAssocOne embId, BeanDescriptor desc) { - this.embId = embId; - this.props = embId.getTargetDescriptor().propertiesBaseScalar(); - matches = MatchedImportedProperty.build(props, desc); - } - - public boolean isEmpty() { - return false; - } - - public boolean isConcatenated() { - return true; - } - - public String getIdentityColumn() { - // return null for concatenated keys - return null; - } - - @Override - public String toString() { - return embId + " props:" + Arrays.toString(props); - } - - /** - * Does nothing for BindableId. - */ - public void addChanged(PersistRequestBean request, List list) { - // do nothing (id not changing) - } - - public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - dmlBind(request, checkIncludes, bean, true); - } - - public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - dmlBind(request, checkIncludes, bean, false); - } - - private void dmlBind(BindableRequest bindRequest, boolean checkIncludes, Object bean, boolean bindNull) throws SQLException { - - if (checkIncludes && !bindRequest.isIncluded(embId)) { - return; - } - - Object idValue = embId.getValue(bean); - - for (int i = 0; i < props.length; i++) { - - Object value = props[i].getValue(idValue); - bindRequest.bind(value, props[i], props[i].getDbColumn(), bindNull); - } - - bindRequest.setIdValue(idValue); - } - - /** - * Id values are never null in where clause. - */ - public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { - if (checkIncludes && !request.isIncluded(embId)) { - return; - } - dmlAppend(request, false); - } - - public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { - dmlAppend(request, checkIncludes); - } - - public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { - if (checkIncludes && !request.isIncluded(embId)) { - return; - } - for (int i = 0; i < props.length; i++) { - request.appendColumn(props[i].getDbColumn()); - } - } - - public boolean deriveConcatenatedId(PersistRequestBean persist) { - - if (matches == null) { - String m = "Matches for the concatinated key columns where not found?" - + " I expect that the concatinated key was null, and this bean does" - + " not have ManyToOne assoc beans matching the primary key columns?"; - throw new PersistenceException(m); - } - - Object bean = persist.getBean(); - - // create the new id - Object newId = embId.createEmbeddedId(); - - // populate it from the assoc one id values... - for (int i = 0; i < matches.length; i++) { - matches[i].populate(bean, newId); - } - - // support PropertyChangeSupport - embId.setValueIntercept(bean, newId); - return true; - } - -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.sql.SQLException; +import java.util.Arrays; +import java.util.List; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +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.server.persist.dml.GenerateDmlRequest; + +/** + * Bindable for a EmbeddedId. + */ +public final class BindableIdEmbedded implements BindableId { + + private final BeanPropertyAssocOne embId; + + private final BeanProperty[] props; + + private final MatchedImportedProperty[] matches; + + public BindableIdEmbedded(BeanPropertyAssocOne embId, BeanDescriptor desc) { + this.embId = embId; + this.props = embId.getTargetDescriptor().propertiesBaseScalar(); + matches = MatchedImportedProperty.build(props, desc); + } + + public boolean isEmpty() { + return false; + } + + public boolean isConcatenated() { + return true; + } + + public String getIdentityColumn() { + // return null for concatenated keys + return null; + } + + @Override + public String toString() { + return embId + " props:" + Arrays.toString(props); + } + + /** + * Does nothing for BindableId. + */ + public void addChanged(PersistRequestBean request, List list) { + // do nothing (id not changing) + } + + public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + dmlBind(request, checkIncludes, bean, true); + } + + public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + dmlBind(request, checkIncludes, bean, false); + } + + private void dmlBind(BindableRequest bindRequest, boolean checkIncludes, Object bean, boolean bindNull) throws SQLException { + + if (checkIncludes && !bindRequest.isIncluded(embId)) { + return; + } + + Object idValue = embId.getValue(bean); + + for (int i = 0; i < props.length; i++) { + + Object value = props[i].getValue(idValue); + bindRequest.bind(value, props[i], props[i].getDbColumn(), bindNull); + } + + bindRequest.setIdValue(idValue); + } + + /** + * Id values are never null in where clause. + */ + public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { + if (checkIncludes && !request.isIncluded(embId)) { + return; + } + dmlAppend(request, false); + } + + public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { + dmlAppend(request, checkIncludes); + } + + public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { + if (checkIncludes && !request.isIncluded(embId)) { + return; + } + for (int i = 0; i < props.length; i++) { + request.appendColumn(props[i].getDbColumn()); + } + } + + public boolean deriveConcatenatedId(PersistRequestBean persist) { + + if (matches == null) { + String m = "Matches for the concatinated key columns where not found?" + + " I expect that the concatinated key was null, and this bean does" + + " not have ManyToOne assoc beans matching the primary key columns?"; + throw new PersistenceException(m); + } + + Object bean = persist.getBean(); + + // create the new id + Object newId = embId.createEmbeddedId(); + + // populate it from the assoc one id values... + for (int i = 0; i < matches.length; i++) { + matches[i].populate(bean, newId); + } + + // support PropertyChangeSupport + embId.setValueIntercept(bean, newId); + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdMap.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdMap.java index c6e4dce1b..e00d74964 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdMap.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdMap.java @@ -1,133 +1,114 @@ -/** - * 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.persist.dmlbind; - -import java.sql.SQLException; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; - -/** - * Bindable for a concatenated id that is not embedded. - */ -public final class BindableIdMap implements BindableId { - - private final BeanProperty[] uids; - - private final MatchedImportedProperty[] matches; - - public BindableIdMap(BeanProperty[] uids, BeanDescriptor desc) { - this.uids = uids; - matches = MatchedImportedProperty.build(uids, desc); - } - - public boolean isEmpty() { - return false; - } - - public boolean isConcatenated() { - return true; - } - - public String getIdentityColumn() { - // return null for concatenated keys - return null; - } - - @Override - public String toString() { - return Arrays.toString(uids); - } - - /** - * Does nothing for BindableId. - */ - public void addChanged(PersistRequestBean request, List list) { - // do nothing (id not changing) - } - - /** - * Id values are never null in where clause. - */ - public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { - // id values are never null in where clause - dmlAppend(request, false); - } - - public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { - dmlAppend(request, checkIncludes); - } - - public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { - for (int i = 0; i < uids.length; i++) { - request.appendColumn(uids[i].getDbColumn()); - } - } - - public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - dmlBind(request, checkIncludes, bean, true); - } - - public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - dmlBind(request, checkIncludes, bean, false); - } - - private void dmlBind(BindableRequest bindRequest, boolean checkIncludes, Object bean, boolean bindNull) throws SQLException { - - LinkedHashMap mapId = new LinkedHashMap(); - for (int i = 0; i < uids.length; i++) { - Object value = uids[i].getValue(bean); - - bindRequest.bind(value, uids[i], uids[i].getName(), bindNull); - - // putting logicalType into map rather than - // the dbType (which may have been converted). - mapId.put(uids[i].getName(), value); - } - bindRequest.setIdValue(mapId); - } - - public boolean deriveConcatenatedId(PersistRequestBean persist) { - - if (matches == null) { - String m = "Matches for the concatinated key columns where not found?" - + " I expect that the concatinated key was null, and this bean does" - + " not have ManyToOne assoc beans matching the primary key columns?"; - throw new PersistenceException(m); - } - - Object bean = persist.getBean(); - - // populate it from the assoc one id values... - for (int i = 0; i < matches.length; i++) { - matches[i].populate(bean, bean); - } - - return true; - } - -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.sql.SQLException; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; + +/** + * Bindable for a concatenated id that is not embedded. + */ +public final class BindableIdMap implements BindableId { + + private final BeanProperty[] uids; + + private final MatchedImportedProperty[] matches; + + public BindableIdMap(BeanProperty[] uids, BeanDescriptor desc) { + this.uids = uids; + matches = MatchedImportedProperty.build(uids, desc); + } + + public boolean isEmpty() { + return false; + } + + public boolean isConcatenated() { + return true; + } + + public String getIdentityColumn() { + // return null for concatenated keys + return null; + } + + @Override + public String toString() { + return Arrays.toString(uids); + } + + /** + * Does nothing for BindableId. + */ + public void addChanged(PersistRequestBean request, List list) { + // do nothing (id not changing) + } + + /** + * Id values are never null in where clause. + */ + public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { + // id values are never null in where clause + dmlAppend(request, false); + } + + public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { + dmlAppend(request, checkIncludes); + } + + public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { + for (int i = 0; i < uids.length; i++) { + request.appendColumn(uids[i].getDbColumn()); + } + } + + public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + dmlBind(request, checkIncludes, bean, true); + } + + public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + dmlBind(request, checkIncludes, bean, false); + } + + private void dmlBind(BindableRequest bindRequest, boolean checkIncludes, Object bean, boolean bindNull) throws SQLException { + + LinkedHashMap mapId = new LinkedHashMap(); + for (int i = 0; i < uids.length; i++) { + Object value = uids[i].getValue(bean); + + bindRequest.bind(value, uids[i], uids[i].getName(), bindNull); + + // putting logicalType into map rather than + // the dbType (which may have been converted). + mapId.put(uids[i].getName(), value); + } + bindRequest.setIdValue(mapId); + } + + public boolean deriveConcatenatedId(PersistRequestBean persist) { + + if (matches == null) { + String m = "Matches for the concatinated key columns where not found?" + + " I expect that the concatinated key was null, and this bean does" + + " not have ManyToOne assoc beans matching the primary key columns?"; + throw new PersistenceException(m); + } + + Object bean = persist.getBean(); + + // populate it from the assoc one id values... + for (int i = 0; i < matches.length; i++) { + matches[i].populate(bean, bean); + } + + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdScalar.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdScalar.java index 5c7d2e5de..3980ee96b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdScalar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableIdScalar.java @@ -1,108 +1,89 @@ -/** - * 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.persist.dmlbind; - -import java.sql.SQLException; -import java.util.List; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; - -/** - * Bindable for a single scalar id property. - */ -public final class BindableIdScalar implements BindableId { - - private final BeanProperty uidProp; - - public BindableIdScalar(BeanProperty uidProp) { - this.uidProp = uidProp; - } - - public boolean isEmpty() { - return false; - } - - public boolean isConcatenated() { - return false; - } - - public String getIdentityColumn() { - return uidProp.getDbColumn(); - } - - @Override - public String toString() { - return uidProp.toString(); - } - - /** - * Does nothing for BindableId. - */ - public void addChanged(PersistRequestBean request, List list) { - // do nothing (id not changing) - } - - /** - * Should not be called as this is really only for concatenated keys. - */ - public boolean deriveConcatenatedId(PersistRequestBean persist) { - throw new PersistenceException("Should not be called? only for concatinated keys"); - } - - /** - * Id values are never null in where clause. - */ - public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { - // id values are never null in where clause - request.appendColumn(uidProp.getDbColumn()); - } - - public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { - dmlAppend(request, checkIncludes); - } - - public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { - - request.appendColumn(uidProp.getDbColumn()); - } - - public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - dmlBind(request, checkIncludes, bean, true); - } - - public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - dmlBind(request, checkIncludes, bean, false); - } - - private void dmlBind(BindableRequest bindRequest, boolean checkIncludes, Object bean, boolean bindNull) throws SQLException { - - Object value = uidProp.getValue(bean); - - bindRequest.bind(value, uidProp, uidProp.getName(), bindNull); - - // used for summary logging - bindRequest.setIdValue(value); - } - -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.sql.SQLException; +import java.util.List; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; + +/** + * Bindable for a single scalar id property. + */ +public final class BindableIdScalar implements BindableId { + + private final BeanProperty uidProp; + + public BindableIdScalar(BeanProperty uidProp) { + this.uidProp = uidProp; + } + + public boolean isEmpty() { + return false; + } + + public boolean isConcatenated() { + return false; + } + + public String getIdentityColumn() { + return uidProp.getDbColumn(); + } + + @Override + public String toString() { + return uidProp.toString(); + } + + /** + * Does nothing for BindableId. + */ + public void addChanged(PersistRequestBean request, List list) { + // do nothing (id not changing) + } + + /** + * Should not be called as this is really only for concatenated keys. + */ + public boolean deriveConcatenatedId(PersistRequestBean persist) { + throw new PersistenceException("Should not be called? only for concatinated keys"); + } + + /** + * Id values are never null in where clause. + */ + public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { + // id values are never null in where clause + request.appendColumn(uidProp.getDbColumn()); + } + + public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { + dmlAppend(request, checkIncludes); + } + + public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { + + request.appendColumn(uidProp.getDbColumn()); + } + + public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + dmlBind(request, checkIncludes, bean, true); + } + + public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + dmlBind(request, checkIncludes, bean, false); + } + + private void dmlBind(BindableRequest bindRequest, boolean checkIncludes, Object bean, boolean bindNull) throws SQLException { + + Object value = uidProp.getValue(bean); + + bindRequest.bind(value, uidProp, uidProp.getName(), bindNull); + + // used for summary logging + bindRequest.setIdValue(value); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableList.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableList.java index 8857585cf..f12c9ee89 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableList.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableList.java @@ -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.persist.dmlbind; - -import java.sql.SQLException; -import java.util.List; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; - -/** - * List of Bindable items. - */ -public class BindableList implements Bindable { - - private final Bindable[] items; - - public BindableList(List list) { - items = list.toArray(new Bindable[list.size()]); - } - - public void addChanged(PersistRequestBean request, List list) { - for (int i = 0; i < items.length; i++) { - items[i].addChanged(request, list); - } - } - - public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { - for (int i = 0; i < items.length; i++) { - items[i].dmlInsert(request, checkIncludes); - } - } - - public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { - - for (int i = 0; i < items.length; i++) { - items[i].dmlAppend(request, checkIncludes); - } - } - - public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { - - for (int i = 0; i < items.length; i++) { - items[i].dmlWhere(request, checkIncludes, bean); - } - } - - public void dmlBind(BindableRequest bindRequest, boolean checkIncludes, Object bean) - throws SQLException { - - for (int i = 0; i < items.length; i++) { - items[i].dmlBind(bindRequest, checkIncludes, bean); - } - } - - public void dmlBindWhere(BindableRequest bindRequest, boolean checkIncludes, Object bean) - throws SQLException { - - for (int i = 0; i < items.length; i++) { - items[i].dmlBindWhere(bindRequest, checkIncludes, bean); - } - } -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.sql.SQLException; +import java.util.List; + +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; + +/** + * List of Bindable items. + */ +public class BindableList implements Bindable { + + private final Bindable[] items; + + public BindableList(List list) { + items = list.toArray(new Bindable[list.size()]); + } + + public void addChanged(PersistRequestBean request, List list) { + for (int i = 0; i < items.length; i++) { + items[i].addChanged(request, list); + } + } + + public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { + for (int i = 0; i < items.length; i++) { + items[i].dmlInsert(request, checkIncludes); + } + } + + public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { + + for (int i = 0; i < items.length; i++) { + items[i].dmlAppend(request, checkIncludes); + } + } + + public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { + + for (int i = 0; i < items.length; i++) { + items[i].dmlWhere(request, checkIncludes, bean); + } + } + + public void dmlBind(BindableRequest bindRequest, boolean checkIncludes, Object bean) + throws SQLException { + + for (int i = 0; i < items.length; i++) { + items[i].dmlBind(bindRequest, checkIncludes, bean); + } + } + + public void dmlBindWhere(BindableRequest bindRequest, boolean checkIncludes, Object bean) + throws SQLException { + + for (int i = 0; i < items.length; i++) { + items[i].dmlBindWhere(bindRequest, checkIncludes, bean); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableProperty.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableProperty.java index 7f4d3e070..5750d001a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableProperty.java @@ -1,102 +1,83 @@ -/** - * 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.persist.dmlbind; - -import java.sql.SQLException; -import java.util.List; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; - -/** - * Bindable for a single BeanProperty. - */ -public class BindableProperty implements Bindable { - - protected final BeanProperty prop; - - public BindableProperty(BeanProperty prop) { - this.prop = prop; - } - - public String toString() { - return prop.toString(); - } - - public void addChanged(PersistRequestBean request, List list) { - if (request.hasChanged(prop)) { - list.add(this); - } - } - - public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { - dmlAppend(request, checkIncludes); - } - - public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { - if (checkIncludes && !request.isIncluded(prop)) { - return; - } - request.appendColumn(prop.getDbColumn()); - } - - /** - * Used for dynamic where clause generation. - */ - public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { - if (checkIncludes && !request.isIncludedWhere(prop)) { - return; - } - - if (bean == null || request.isDbNull(prop.getValue(bean))) { - request.appendColumnIsNull(prop.getDbColumn()); - - } else { - request.appendColumn(prop.getDbColumn()); - } - } - - public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - - if (checkIncludes && !request.isIncluded(prop)) { - return; - } - dmlBind(request, bean, true); - } - - public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - if (checkIncludes && !request.isIncludedWhere(prop)) { - return; - } - dmlBind(request, bean, false); - } - - private void dmlBind(BindableRequest request, Object bean, boolean bindNull) - throws SQLException { - - Object value = null; - if (bean != null) { - value = prop.getValue(bean); - } - // value = prop.getDefaultValue(); - request.bind(value, prop, prop.getName(), bindNull); - } -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.sql.SQLException; +import java.util.List; + +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; + +/** + * Bindable for a single BeanProperty. + */ +public class BindableProperty implements Bindable { + + protected final BeanProperty prop; + + public BindableProperty(BeanProperty prop) { + this.prop = prop; + } + + public String toString() { + return prop.toString(); + } + + public void addChanged(PersistRequestBean request, List list) { + if (request.hasChanged(prop)) { + list.add(this); + } + } + + public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { + dmlAppend(request, checkIncludes); + } + + public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { + if (checkIncludes && !request.isIncluded(prop)) { + return; + } + request.appendColumn(prop.getDbColumn()); + } + + /** + * Used for dynamic where clause generation. + */ + public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { + if (checkIncludes && !request.isIncludedWhere(prop)) { + return; + } + + if (bean == null || request.isDbNull(prop.getValue(bean))) { + request.appendColumnIsNull(prop.getDbColumn()); + + } else { + request.appendColumn(prop.getDbColumn()); + } + } + + public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + + if (checkIncludes && !request.isIncluded(prop)) { + return; + } + dmlBind(request, bean, true); + } + + public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + if (checkIncludes && !request.isIncludedWhere(prop)) { + return; + } + dmlBind(request, bean, false); + } + + private void dmlBind(BindableRequest request, Object bean, boolean bindNull) + throws SQLException { + + Object value = null; + if (bean != null) { + value = prop.getValue(bean); + } + // value = prop.getDefaultValue(); + request.bind(value, prop, prop.getName(), bindNull); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyInsertGenerated.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyInsertGenerated.java index d4dfb3961..cf5151f4d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyInsertGenerated.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyInsertGenerated.java @@ -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.persist.dmlbind; - -import java.sql.SQLException; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; -import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; - -/** - * Bindable for insert on a property with a GeneratedProperty. - *

      - * This is typically a 'insert timestamp', 'update timestamp' or 'counter'. - *

      - */ -public class BindablePropertyInsertGenerated extends BindableProperty { - - private final GeneratedProperty gen; - - public BindablePropertyInsertGenerated(BeanProperty prop, GeneratedProperty gen) { - super(prop); - this.gen = gen; - } - - public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - dmlBind(request, checkIncludes, bean, true); - } - - public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - dmlBind(request, checkIncludes, bean, false); - } - - /** - * Bind a value in a Insert SET clause. - */ - private void dmlBind(BindableRequest request, boolean checkIncludes, Object bean, boolean bindNull) throws SQLException { - - Object value = gen.getInsertValue(prop, bean); - - // generated value should be the correct type - if (bean != null){ - // support PropertyChangeSupport - prop.setValueIntercept(bean, value); - request.registerAdditionalProperty(prop.getName()); - } - //value = prop.getDefaultValue(); - request.bind(value, prop, prop.getName(), bindNull); - } - - /** - * Always bind on Insert SET. - */ - @Override - public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes){ - request.appendColumn(prop.getDbColumn()); - } - -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.sql.SQLException; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; +import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; + +/** + * Bindable for insert on a property with a GeneratedProperty. + *

      + * This is typically a 'insert timestamp', 'update timestamp' or 'counter'. + *

      + */ +public class BindablePropertyInsertGenerated extends BindableProperty { + + private final GeneratedProperty gen; + + public BindablePropertyInsertGenerated(BeanProperty prop, GeneratedProperty gen) { + super(prop); + this.gen = gen; + } + + public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + dmlBind(request, checkIncludes, bean, true); + } + + public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + dmlBind(request, checkIncludes, bean, false); + } + + /** + * Bind a value in a Insert SET clause. + */ + private void dmlBind(BindableRequest request, boolean checkIncludes, Object bean, boolean bindNull) throws SQLException { + + Object value = gen.getInsertValue(prop, bean); + + // generated value should be the correct type + if (bean != null){ + // support PropertyChangeSupport + prop.setValueIntercept(bean, value); + request.registerAdditionalProperty(prop.getName()); + } + //value = prop.getDefaultValue(); + request.bind(value, prop, prop.getName(), bindNull); + } + + /** + * Always bind on Insert SET. + */ + @Override + public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes){ + request.appendColumn(prop.getDbColumn()); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyUpdateGenerated.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyUpdateGenerated.java index ec9c09397..639ffef3f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyUpdateGenerated.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindablePropertyUpdateGenerated.java @@ -1,95 +1,76 @@ -/** - * 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.persist.dmlbind; - -import java.sql.SQLException; -import java.util.List; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; -import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; - -/** - * Bindable for update on a property with a GeneratedProperty. - *

      - * This is typically a 'update timestamp' or 'counter'. - *

      - */ -public class BindablePropertyUpdateGenerated extends BindableProperty { - - private final GeneratedProperty gen; - - public BindablePropertyUpdateGenerated(BeanProperty prop, GeneratedProperty gen) { - super(prop); - this.gen = gen; - } - - /** - * Always add BindablePropertyUpdateGenerated properties. - */ - public void addChanged(PersistRequestBean request, List list) { - - list.add(this); - } - - public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - if (checkIncludes && !request.isIncluded(prop)){ - return; - } - dmlBind(request, bean, true); - } - - public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - if (checkIncludes && !request.isIncludedWhere(prop)){ - return; - } - dmlBind(request, bean, false); - } - - private void dmlBind(BindableRequest request, Object bean, boolean bindNull) throws SQLException { - - Object value = gen.getUpdateValue(prop, bean); - - // generated value should be the correct type - request.bind(value, prop, prop.getName(), bindNull); - - // only register the update value if it was included - // in the bean in the first place - if (request.isIncluded(prop)) { - // need to set the generated value to the bean later - // after the where clause has been generated - request.registerUpdateGenValue(prop, bean, value); - } - } - - /** - * Always bind on Insert SET. - */ - @Override - public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes){ - if (checkIncludes && !request.isIncluded(prop)){ - return; - } - request.appendColumn(prop.getDbColumn()); - } - - -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.sql.SQLException; +import java.util.List; + +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; +import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; + +/** + * Bindable for update on a property with a GeneratedProperty. + *

      + * This is typically a 'update timestamp' or 'counter'. + *

      + */ +public class BindablePropertyUpdateGenerated extends BindableProperty { + + private final GeneratedProperty gen; + + public BindablePropertyUpdateGenerated(BeanProperty prop, GeneratedProperty gen) { + super(prop); + this.gen = gen; + } + + /** + * Always add BindablePropertyUpdateGenerated properties. + */ + public void addChanged(PersistRequestBean request, List list) { + + list.add(this); + } + + public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + if (checkIncludes && !request.isIncluded(prop)){ + return; + } + dmlBind(request, bean, true); + } + + public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + if (checkIncludes && !request.isIncludedWhere(prop)){ + return; + } + dmlBind(request, bean, false); + } + + private void dmlBind(BindableRequest request, Object bean, boolean bindNull) throws SQLException { + + Object value = gen.getUpdateValue(prop, bean); + + // generated value should be the correct type + request.bind(value, prop, prop.getName(), bindNull); + + // only register the update value if it was included + // in the bean in the first place + if (request.isIncluded(prop)) { + // need to set the generated value to the bean later + // after the where clause has been generated + request.registerUpdateGenValue(prop, bean, value); + } + } + + /** + * Always bind on Insert SET. + */ + @Override + public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes){ + if (checkIncludes && !request.isIncluded(prop)){ + return; + } + request.appendColumn(prop.getDbColumn()); + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableRequest.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableRequest.java index afbe3d5f9..c1613b06f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableRequest.java @@ -1,98 +1,79 @@ -/** - * 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.persist.dmlbind; - -import java.sql.SQLException; - -import com.avaje.ebeaninternal.api.DerivedRelationshipData; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -/** - * Request object passed to bindables. - */ -public interface BindableRequest { - - /** - * Set the id for use with summary level logging. - */ - public void setIdValue(Object idValue); - - /** - * Bind the value to a PreparedStatement. - *

      - * Takes into account logicalType to dbType conversion if required. - *

      - *

      - * Returns the value that was bound (and was potentially converted from - * logicalType to dbType. - *

      - * - * @param value - * the value of a property - * @param bindNull - * if true bind null values, if false use IS NULL. - */ - public Object bind(Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException; - - /** - * Bind a raw value. Used to bind the discriminator column. - */ - public Object bind(String propName, Object value, int sqlType) throws SQLException; - - /** - * Bind a raw value with a placeHolder to put into the transaction log. - */ - public Object bindNoLog(Object value, int sqlType, String logPlaceHolder) throws SQLException; - - /** - * Bind the value to the preparedStatement without logging. - */ - public Object bindNoLog(Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException; - - /** - * Return true if the property is included in this request. - */ - public boolean isIncluded(BeanProperty prop); - - /** - * Return true if the property is included in the WHERE clause for this - * request. - */ - public boolean isIncludedWhere(BeanProperty prop); - - /** - * Register the value from a update GeneratedValue. This can only be set to - * the bean property after the where clause has bean built. - */ - public void registerUpdateGenValue(BeanProperty prop, Object bean, Object value); - - /** - * Register a property into loadedProperties if required. - */ - public void registerAdditionalProperty(String propertyName); - - /** - * Return the original PersistRequest. - */ - public PersistRequestBean getPersistRequest(); - - public void registerDerivedRelationship(DerivedRelationshipData assocBean); -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.sql.SQLException; + +import com.avaje.ebeaninternal.api.DerivedRelationshipData; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +/** + * Request object passed to bindables. + */ +public interface BindableRequest { + + /** + * Set the id for use with summary level logging. + */ + public void setIdValue(Object idValue); + + /** + * Bind the value to a PreparedStatement. + *

      + * Takes into account logicalType to dbType conversion if required. + *

      + *

      + * Returns the value that was bound (and was potentially converted from + * logicalType to dbType. + *

      + * + * @param value + * the value of a property + * @param bindNull + * if true bind null values, if false use IS NULL. + */ + public Object bind(Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException; + + /** + * Bind a raw value. Used to bind the discriminator column. + */ + public Object bind(String propName, Object value, int sqlType) throws SQLException; + + /** + * Bind a raw value with a placeHolder to put into the transaction log. + */ + public Object bindNoLog(Object value, int sqlType, String logPlaceHolder) throws SQLException; + + /** + * Bind the value to the preparedStatement without logging. + */ + public Object bindNoLog(Object value, BeanProperty prop, String propName, boolean bindNull) throws SQLException; + + /** + * Return true if the property is included in this request. + */ + public boolean isIncluded(BeanProperty prop); + + /** + * Return true if the property is included in the WHERE clause for this + * request. + */ + public boolean isIncludedWhere(BeanProperty prop); + + /** + * Register the value from a update GeneratedValue. This can only be set to + * the bean property after the where clause has bean built. + */ + public void registerUpdateGenValue(BeanProperty prop, Object bean, Object value); + + /** + * Register a property into loadedProperties if required. + */ + public void registerAdditionalProperty(String propertyName); + + /** + * Return the original PersistRequest. + */ + public PersistRequestBean getPersistRequest(); + + public void registerDerivedRelationship(DerivedRelationshipData assocBean); +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableUnidirectional.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableUnidirectional.java index 8239f66eb..1a9545d3f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableUnidirectional.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/BindableUnidirectional.java @@ -1,102 +1,83 @@ -/** - * 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.persist.dmlbind; - -import java.sql.SQLException; -import java.util.List; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.deploy.id.ImportedId; -import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; - -/** - * Bindable for a unidirectional relationship. - *

      - * This inserts the foreign key value that is retrieved from the id of the - * parentBean. - *

      - */ -public class BindableUnidirectional implements Bindable { - - private final BeanPropertyAssocOne unidirectional; - - private final ImportedId importedId; - - private final BeanDescriptor desc; - - public BindableUnidirectional(BeanDescriptor desc, BeanPropertyAssocOne unidirectional) { - this.desc = desc; - this.unidirectional = unidirectional; - this.importedId = unidirectional.getImportedId(); - - } - - public String toString() { - return "BindableShadowFKey " + unidirectional; - } - - public void addChanged(PersistRequestBean request, List list) { - throw new PersistenceException("Never called (for insert only)"); - } - - public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { - dmlAppend(request, checkIncludes); - } - - public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { - // always included (in insert) - importedId.dmlAppend(request); - } - - public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { - throw new RuntimeException("Never called"); - } - - public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - dmlBind(request, checkIncludes, bean, true); - } - - public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { - dmlBind(request, checkIncludes, bean, false); - } - - private void dmlBind(BindableRequest request, boolean checkIncludes, Object bean, boolean bindNull) - throws SQLException { - - PersistRequestBean persistRequest = request.getPersistRequest(); - Object parentBean = persistRequest.getParentBean(); - - if (parentBean == null) { - Class localType = desc.getBeanType(); - Class targetType = unidirectional.getTargetType(); - ; - String msg = "Error inserting bean [" + localType + "] with unidirectional relationship. "; - msg += "For inserts you must use cascade save on the master bean [" + targetType + "]."; - throw new PersistenceException(msg); - } - - importedId.bind(request, parentBean, bindNull); - } - -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.sql.SQLException; +import java.util.List; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.id.ImportedId; +import com.avaje.ebeaninternal.server.persist.dml.GenerateDmlRequest; + +/** + * Bindable for a unidirectional relationship. + *

      + * This inserts the foreign key value that is retrieved from the id of the + * parentBean. + *

      + */ +public class BindableUnidirectional implements Bindable { + + private final BeanPropertyAssocOne unidirectional; + + private final ImportedId importedId; + + private final BeanDescriptor desc; + + public BindableUnidirectional(BeanDescriptor desc, BeanPropertyAssocOne unidirectional) { + this.desc = desc; + this.unidirectional = unidirectional; + this.importedId = unidirectional.getImportedId(); + + } + + public String toString() { + return "BindableShadowFKey " + unidirectional; + } + + public void addChanged(PersistRequestBean request, List list) { + throw new PersistenceException("Never called (for insert only)"); + } + + public void dmlInsert(GenerateDmlRequest request, boolean checkIncludes) { + dmlAppend(request, checkIncludes); + } + + public void dmlAppend(GenerateDmlRequest request, boolean checkIncludes) { + // always included (in insert) + importedId.dmlAppend(request); + } + + public void dmlWhere(GenerateDmlRequest request, boolean checkIncludes, Object bean) { + throw new RuntimeException("Never called"); + } + + public void dmlBind(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + dmlBind(request, checkIncludes, bean, true); + } + + public void dmlBindWhere(BindableRequest request, boolean checkIncludes, Object bean) throws SQLException { + dmlBind(request, checkIncludes, bean, false); + } + + private void dmlBind(BindableRequest request, boolean checkIncludes, Object bean, boolean bindNull) + throws SQLException { + + PersistRequestBean persistRequest = request.getPersistRequest(); + Object parentBean = persistRequest.getParentBean(); + + if (parentBean == null) { + Class localType = desc.getBeanType(); + Class targetType = unidirectional.getTargetType(); + ; + String msg = "Error inserting bean [" + localType + "] with unidirectional relationship. "; + msg += "For inserts you must use cascade save on the master bean [" + targetType + "]."; + throw new PersistenceException(msg); + } + + importedId.bind(request, parentBean, bindNull); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryAssocOnes.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryAssocOnes.java index 62b96ec0a..cb39e1743 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryAssocOnes.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryAssocOnes.java @@ -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.persist.dmlbind; - -import java.util.List; - -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.persist.dml.DmlMode; - -/** - * A factory that builds Bindable for BeanPropertyAssocOne properties. - */ -public class FactoryAssocOnes { - - public FactoryAssocOnes() { - } - - /** - * Add foreign key columns from associated one beans. - */ - public List create(List list, BeanDescriptor desc, DmlMode mode) { - - BeanPropertyAssocOne[] ones = desc.propertiesOneImported(); - - for (int i = 0; i < ones.length; i++) { - if (ones[i].isImportedPrimaryKey()){ - // excluded as already part of the primary key - - } else { - switch (mode) { - case WHERE: - break; - case INSERT: - if (!ones[i].isInsertable()) { - continue; - } - break; - case UPDATE: - if (!ones[i].isUpdateable()) { - continue; - } - break; - } - list.add(new BindableAssocOne(ones[i])); - } - } - - return list; - } -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.util.List; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.persist.dml.DmlMode; + +/** + * A factory that builds Bindable for BeanPropertyAssocOne properties. + */ +public class FactoryAssocOnes { + + public FactoryAssocOnes() { + } + + /** + * Add foreign key columns from associated one beans. + */ + public List create(List list, BeanDescriptor desc, DmlMode mode) { + + BeanPropertyAssocOne[] ones = desc.propertiesOneImported(); + + for (int i = 0; i < ones.length; i++) { + if (ones[i].isImportedPrimaryKey()){ + // excluded as already part of the primary key + + } else { + switch (mode) { + case WHERE: + break; + case INSERT: + if (!ones[i].isInsertable()) { + continue; + } + break; + case UPDATE: + if (!ones[i].isUpdateable()) { + continue; + } + break; + } + list.add(new BindableAssocOne(ones[i])); + } + } + + return list; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryBaseProperties.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryBaseProperties.java index 6ec30f922..d24a30957 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryBaseProperties.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryBaseProperties.java @@ -1,79 +1,60 @@ -/** - * 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.persist.dmlbind; - -import java.util.ArrayList; -import java.util.List; - -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; -import com.avaje.ebeaninternal.server.persist.dml.DmlMode; - -/** - * Add base properties to the BindableList for a bean type. - *

      - * This excludes unique embedded and associated properties. - *

      - */ -public class FactoryBaseProperties { - - private final FactoryProperty factoryProperty; - - - public FactoryBaseProperties(boolean bindEncryptDataFirst) { - factoryProperty = new FactoryProperty(bindEncryptDataFirst); - } - - /** - * Add Bindable for the base properties to the list. - */ - public void create(List list, BeanDescriptor desc, DmlMode mode, boolean withLobs) { - - add(desc.propertiesBaseScalar(), list, desc, mode, withLobs); - - BeanPropertyCompound[] compoundProps = desc.propertiesBaseCompound(); - for (int i = 0; i < compoundProps.length; i++) { - BeanProperty[] props = compoundProps[i].getScalarProperties(); - - ArrayList newList = new ArrayList(props.length); - add(props, newList, desc, mode, withLobs); - - BindableCompound compoundBindable = new BindableCompound(compoundProps[i], newList); - - list.add(compoundBindable); - } - } - - private void add(BeanProperty[] props, List list, BeanDescriptor desc, DmlMode mode, boolean withLobs) { - - for (int i = 0; i < props.length; i++) { - - Bindable item = factoryProperty.create(props[i], mode, withLobs); - if (item != null) { - list.add(item); - } else { - // null where readOnly (Secondary tables) or Lob exclusion - } - } - - } - -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.util.ArrayList; +import java.util.List; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; +import com.avaje.ebeaninternal.server.persist.dml.DmlMode; + +/** + * Add base properties to the BindableList for a bean type. + *

      + * This excludes unique embedded and associated properties. + *

      + */ +public class FactoryBaseProperties { + + private final FactoryProperty factoryProperty; + + + public FactoryBaseProperties(boolean bindEncryptDataFirst) { + factoryProperty = new FactoryProperty(bindEncryptDataFirst); + } + + /** + * Add Bindable for the base properties to the list. + */ + public void create(List list, BeanDescriptor desc, DmlMode mode, boolean withLobs) { + + add(desc.propertiesBaseScalar(), list, desc, mode, withLobs); + + BeanPropertyCompound[] compoundProps = desc.propertiesBaseCompound(); + for (int i = 0; i < compoundProps.length; i++) { + BeanProperty[] props = compoundProps[i].getScalarProperties(); + + ArrayList newList = new ArrayList(props.length); + add(props, newList, desc, mode, withLobs); + + BindableCompound compoundBindable = new BindableCompound(compoundProps[i], newList); + + list.add(compoundBindable); + } + } + + private void add(BeanProperty[] props, List list, BeanDescriptor desc, DmlMode mode, boolean withLobs) { + + for (int i = 0; i < props.length; i++) { + + Bindable item = factoryProperty.create(props[i], mode, withLobs); + if (item != null) { + list.add(item); + } else { + // null where readOnly (Secondary tables) or Lob exclusion + } + } + + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryEmbedded.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryEmbedded.java index 042b09f2b..28bff2f6f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryEmbedded.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryEmbedded.java @@ -1,65 +1,46 @@ -/** - * 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.persist.dmlbind; - -import java.util.ArrayList; -import java.util.List; - -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.server.persist.dml.DmlMode; - -/** - * A factory that builds Bindable for embedded bean properties. - */ -public class FactoryEmbedded { - - private final FactoryProperty factoryProperty; - - public FactoryEmbedded(boolean bindEncryptDataFirst) { - factoryProperty = new FactoryProperty(bindEncryptDataFirst); - } - - /** - * Add bindable for the embedded properties to the list. - */ - public void create(List list, BeanDescriptor desc, DmlMode mode, boolean withLobs) { - - BeanPropertyAssocOne[] embedded = desc.propertiesEmbedded(); - - for (int j = 0; j < embedded.length; j++) { - - List bindList = new ArrayList(); - - BeanProperty[] props = embedded[j].getProperties(); - for (int i = 0; i < props.length; i++) { - Bindable item = factoryProperty.create(props[i], mode, withLobs); - if (item != null){ - bindList.add(item); - } - } - - list.add(new BindableEmbedded(embedded[j], bindList)); - } - } - - -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.util.ArrayList; +import java.util.List; + +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.server.persist.dml.DmlMode; + +/** + * A factory that builds Bindable for embedded bean properties. + */ +public class FactoryEmbedded { + + private final FactoryProperty factoryProperty; + + public FactoryEmbedded(boolean bindEncryptDataFirst) { + factoryProperty = new FactoryProperty(bindEncryptDataFirst); + } + + /** + * Add bindable for the embedded properties to the list. + */ + public void create(List list, BeanDescriptor desc, DmlMode mode, boolean withLobs) { + + BeanPropertyAssocOne[] embedded = desc.propertiesEmbedded(); + + for (int j = 0; j < embedded.length; j++) { + + List bindList = new ArrayList(); + + BeanProperty[] props = embedded[j].getProperties(); + for (int i = 0; i < props.length; i++) { + Bindable item = factoryProperty.create(props[i], mode, withLobs); + if (item != null){ + bindList.add(item); + } + } + + list.add(new BindableEmbedded(embedded[j], bindList)); + } + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryId.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryId.java index 65d99b86c..8b2b20707 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryId.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryId.java @@ -1,55 +1,36 @@ -/** - * 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.persist.dmlbind; - -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; - -/** - * Create a Bindable for the ids of a bean type. - */ -public class FactoryId { - - public FactoryId() { - } - - /** - * Add uniqueId properties. - */ - public BindableId createId(BeanDescriptor desc) { - - BeanProperty[] uids = desc.propertiesId(); - if (uids.length == 0) { - return new BindableIdEmpty(); - - } else if (uids.length == 1) { - if (!uids[0].isEmbedded()) { - return new BindableIdScalar(uids[0]); - - } else { - BeanPropertyAssocOne embId = (BeanPropertyAssocOne) uids[0]; - return new BindableIdEmbedded(embId, desc); - } - } else { - return new BindableIdMap(uids, desc); - } - } -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; + +/** + * Create a Bindable for the ids of a bean type. + */ +public class FactoryId { + + public FactoryId() { + } + + /** + * Add uniqueId properties. + */ + public BindableId createId(BeanDescriptor desc) { + + BeanProperty[] uids = desc.propertiesId(); + if (uids.length == 0) { + return new BindableIdEmpty(); + + } else if (uids.length == 1) { + if (!uids[0].isEmbedded()) { + return new BindableIdScalar(uids[0]); + + } else { + BeanPropertyAssocOne embId = (BeanPropertyAssocOne) uids[0]; + return new BindableIdEmbedded(embId, desc); + } + } else { + return new BindableIdMap(uids, desc); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryProperty.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryProperty.java index b0bbe7cf9..82b4ee7b3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryProperty.java @@ -1,84 +1,65 @@ -/** - * 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.persist.dmlbind; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; -import com.avaje.ebeaninternal.server.persist.dml.DmlMode; - -/** - * Creates the appropriate Bindable for a BeanProperty. - *

      - * Lob properties can be excluded and it creates BindablePropertyInsertGenerated - * and BindablePropertyUpdateGenerated as required. - *

      - */ -public class FactoryProperty { - - private final boolean bindEncryptDataFirst; - - public FactoryProperty(boolean bindEncryptDataFirst) { - this.bindEncryptDataFirst = bindEncryptDataFirst; - } - - /** - * Create a Bindable for the property given the mode and withLobs flag. - */ - public Bindable create(BeanProperty prop, DmlMode mode, boolean withLobs) { - - if (DmlMode.INSERT.equals(mode) && !prop.isDbInsertable()){ - return null; - } - if (DmlMode.UPDATE.equals(mode) && !prop.isDbUpdatable()){ - return null; - } - - if (prop.isLob()) { - if (DmlMode.WHERE.equals(mode) || !withLobs) { - // Lob exclusion - return null; - } else { - return prop.isDbEncrypted() ? new BindableEncryptedProperty(prop, bindEncryptDataFirst) : new BindableProperty(prop); - } - } - - GeneratedProperty gen = prop.getGeneratedProperty(); - if (gen != null) { - if (DmlMode.INSERT.equals(mode)) { - if (gen.includeInInsert()) { - return new BindablePropertyInsertGenerated(prop, gen); - } else { - return null; - } - - } - if (DmlMode.UPDATE.equals(mode)) { - if (gen.includeInUpdate()) { - return new BindablePropertyUpdateGenerated(prop, gen); - } else { - // An 'Insert Timestamp' is never updated - return null; - } - } - } - - return prop.isDbEncrypted() ? new BindableEncryptedProperty(prop, bindEncryptDataFirst) : new BindableProperty(prop); - } -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty; +import com.avaje.ebeaninternal.server.persist.dml.DmlMode; + +/** + * Creates the appropriate Bindable for a BeanProperty. + *

      + * Lob properties can be excluded and it creates BindablePropertyInsertGenerated + * and BindablePropertyUpdateGenerated as required. + *

      + */ +public class FactoryProperty { + + private final boolean bindEncryptDataFirst; + + public FactoryProperty(boolean bindEncryptDataFirst) { + this.bindEncryptDataFirst = bindEncryptDataFirst; + } + + /** + * Create a Bindable for the property given the mode and withLobs flag. + */ + public Bindable create(BeanProperty prop, DmlMode mode, boolean withLobs) { + + if (DmlMode.INSERT.equals(mode) && !prop.isDbInsertable()){ + return null; + } + if (DmlMode.UPDATE.equals(mode) && !prop.isDbUpdatable()){ + return null; + } + + if (prop.isLob()) { + if (DmlMode.WHERE.equals(mode) || !withLobs) { + // Lob exclusion + return null; + } else { + return prop.isDbEncrypted() ? new BindableEncryptedProperty(prop, bindEncryptDataFirst) : new BindableProperty(prop); + } + } + + GeneratedProperty gen = prop.getGeneratedProperty(); + if (gen != null) { + if (DmlMode.INSERT.equals(mode)) { + if (gen.includeInInsert()) { + return new BindablePropertyInsertGenerated(prop, gen); + } else { + return null; + } + + } + if (DmlMode.UPDATE.equals(mode)) { + if (gen.includeInUpdate()) { + return new BindablePropertyUpdateGenerated(prop, gen); + } else { + // An 'Insert Timestamp' is never updated + return null; + } + } + } + + return prop.isDbEncrypted() ? new BindableEncryptedProperty(prop, bindEncryptDataFirst) : new BindableProperty(prop); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryVersion.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryVersion.java index 0329df693..5e7656469 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryVersion.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/FactoryVersion.java @@ -1,79 +1,60 @@ -/** - * 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.persist.dmlbind; - -import java.util.ArrayList; -import java.util.List; - -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; - -/** - * Creates a Bindable to support version concurrency where clauses. - */ -public class FactoryVersion { - - - public FactoryVersion() { - } - - /** - * Create a Bindable for the version property(s) for a bean type. - */ - public Bindable create(BeanDescriptor desc) { - - List verList = new ArrayList(); - - BeanProperty[] vers = desc.propertiesVersion(); - for (int i = 0; i < vers.length; i++) { - verList.add(new BindableProperty(vers[i])); - } - - // version columns on embedded beans? - BeanPropertyAssocOne[] embedded = desc.propertiesEmbedded(); - for (int j = 0; j < embedded.length; j++) { - - if (embedded[j].isEmbeddedVersion()) { - - List bindList = new ArrayList(); - - BeanProperty[] embProps = embedded[j].getProperties(); - - for (int i = 0; i < embProps.length; i++) { - if (embProps[i].isVersion()){ - bindList.add(new BindableProperty(embProps[i])); - } - } - - verList.add(new BindableEmbedded(embedded[j], bindList)); - } - } - - if (verList.size() == 0){ - return null; - } - if (verList.size() == 1){ - return verList.get(0); - } - - return new BindableList(verList); - } -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import java.util.ArrayList; +import java.util.List; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; + +/** + * Creates a Bindable to support version concurrency where clauses. + */ +public class FactoryVersion { + + + public FactoryVersion() { + } + + /** + * Create a Bindable for the version property(s) for a bean type. + */ + public Bindable create(BeanDescriptor desc) { + + List verList = new ArrayList(); + + BeanProperty[] vers = desc.propertiesVersion(); + for (int i = 0; i < vers.length; i++) { + verList.add(new BindableProperty(vers[i])); + } + + // version columns on embedded beans? + BeanPropertyAssocOne[] embedded = desc.propertiesEmbedded(); + for (int j = 0; j < embedded.length; j++) { + + if (embedded[j].isEmbeddedVersion()) { + + List bindList = new ArrayList(); + + BeanProperty[] embProps = embedded[j].getProperties(); + + for (int i = 0; i < embProps.length; i++) { + if (embProps[i].isVersion()){ + bindList.add(new BindableProperty(embProps[i])); + } + } + + verList.add(new BindableEmbedded(embedded[j], bindList)); + } + } + + if (verList.size() == 0){ + return null; + } + if (verList.size() == 1){ + return verList.get(0); + } + + return new BindableList(verList); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/MatchedImportedProperty.java b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/MatchedImportedProperty.java index 20fa62a2c..2dd910aff 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/MatchedImportedProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/dmlbind/MatchedImportedProperty.java @@ -1,104 +1,85 @@ -/** - * 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.persist.dmlbind; - -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; - -/** - * Matches local embedded id properties to 'matching' properties from a - * ManyToOne associated bean that is a 'imported primary key'. - *

      - * This object is designed to help BindableIdEmbedded and BindableIdMap to - * create a concatenated id from the id values from ManyToOne associated beans. - * This can be done when those ManyToOne associated beans make up the primary - * key. This typically means the BindableIdEmbedded is for a intersection table - * of a Many to Many relationship. - *

      - */ -class MatchedImportedProperty { - - private final BeanPropertyAssocOne assocOne; - - private final BeanProperty foreignProp; - - private final BeanProperty localProp; - - protected MatchedImportedProperty(BeanPropertyAssocOne assocOne, BeanProperty foreignProp, - BeanProperty localProp) { - this.assocOne = assocOne; - this.foreignProp = foreignProp; - this.localProp = localProp; - } - - protected void populate(Object sourceBean, Object destBean) { - Object assocBean = assocOne.getValue(sourceBean); - if (assocBean == null) { - String msg = "The assoc bean for " + assocOne + " is null?"; - throw new NullPointerException(msg); - } - - Object value = foreignProp.getValue(assocBean); - localProp.setValue(destBean, value); - } - - /** - * Create the array of matchedImportedProperty based on the properties and descriptor. - */ - protected static MatchedImportedProperty[] build(BeanProperty[] props, BeanDescriptor desc) { - - MatchedImportedProperty[] matches = new MatchedImportedProperty[props.length]; - - for (int i = 0; i < props.length; i++) { - // find matching assoc one property for dbColumn - matches[i] = MatchedImportedProperty.findMatch(props[i], desc); - if (matches[i] == null) { - // ok, the assoc ones are not on the bean? - return null; - } - } - return matches; - } - - private static MatchedImportedProperty findMatch(BeanProperty prop, BeanDescriptor desc) { - - // find matching against the local database column - String dbColumn = prop.getDbColumn(); - - BeanPropertyAssocOne[] assocOnes = desc.propertiesOne(); - for (int i = 0; i < assocOnes.length; i++) { - if (assocOnes[i].isImportedPrimaryKey()) { - - // search using the ImportedId from the assoc one - BeanProperty foreignMatch = assocOnes[i].getImportedId().findMatchImport(dbColumn); - - if (foreignMatch != null) { - return new MatchedImportedProperty(assocOnes[i], foreignMatch, prop); - } - } - } - - // there was no matching assoc one property. - // example UserRole bean missing assoc one to User? - return null; - } - -} +package com.avaje.ebeaninternal.server.persist.dmlbind; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; + +/** + * Matches local embedded id properties to 'matching' properties from a + * ManyToOne associated bean that is a 'imported primary key'. + *

      + * This object is designed to help BindableIdEmbedded and BindableIdMap to + * create a concatenated id from the id values from ManyToOne associated beans. + * This can be done when those ManyToOne associated beans make up the primary + * key. This typically means the BindableIdEmbedded is for a intersection table + * of a Many to Many relationship. + *

      + */ +class MatchedImportedProperty { + + private final BeanPropertyAssocOne assocOne; + + private final BeanProperty foreignProp; + + private final BeanProperty localProp; + + protected MatchedImportedProperty(BeanPropertyAssocOne assocOne, BeanProperty foreignProp, + BeanProperty localProp) { + this.assocOne = assocOne; + this.foreignProp = foreignProp; + this.localProp = localProp; + } + + protected void populate(Object sourceBean, Object destBean) { + Object assocBean = assocOne.getValue(sourceBean); + if (assocBean == null) { + String msg = "The assoc bean for " + assocOne + " is null?"; + throw new NullPointerException(msg); + } + + Object value = foreignProp.getValue(assocBean); + localProp.setValue(destBean, value); + } + + /** + * Create the array of matchedImportedProperty based on the properties and descriptor. + */ + protected static MatchedImportedProperty[] build(BeanProperty[] props, BeanDescriptor desc) { + + MatchedImportedProperty[] matches = new MatchedImportedProperty[props.length]; + + for (int i = 0; i < props.length; i++) { + // find matching assoc one property for dbColumn + matches[i] = MatchedImportedProperty.findMatch(props[i], desc); + if (matches[i] == null) { + // ok, the assoc ones are not on the bean? + return null; + } + } + return matches; + } + + private static MatchedImportedProperty findMatch(BeanProperty prop, BeanDescriptor desc) { + + // find matching against the local database column + String dbColumn = prop.getDbColumn(); + + BeanPropertyAssocOne[] assocOnes = desc.propertiesOne(); + for (int i = 0; i < assocOnes.length; i++) { + if (assocOnes[i].isImportedPrimaryKey()) { + + // search using the ImportedId from the assoc one + BeanProperty foreignMatch = assocOnes[i].getImportedId().findMatchImport(dbColumn); + + if (foreignMatch != null) { + return new MatchedImportedProperty(assocOnes[i], foreignMatch, prop); + } + } + } + + // there was no matching assoc one property. + // example UserRole bean missing assoc one to User? + return null; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/BackgroundFetch.java b/src/main/java/com/avaje/ebeaninternal/server/query/BackgroundFetch.java index e57d554b9..78b2d5596 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/BackgroundFetch.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/BackgroundFetch.java @@ -1,87 +1,68 @@ -/** - * 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.query; - -import java.util.concurrent.Callable; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebeaninternal.api.SpiTransaction; - -/** - * Continue the fetch using a Background thread. The client knows when this has - * finished by checking to see if beanList.finishedFetch() is true. - */ -public class BackgroundFetch implements Callable { - - private static final Logger logger = Logger.getLogger(BackgroundFetch.class.getName()); - - private final CQuery cquery; - - private final SpiTransaction transaction; - - /** - * Create the BackgroundFetch. - */ - public BackgroundFetch(CQuery cquery) { - this.cquery = cquery; - this.transaction = cquery.getTransaction(); - } - - /** - * Continue the fetch. - */ - public Integer call() { - try { - - BeanCollection bc = cquery.continueFetchingInBackground(); - - return bc.size(); - - } catch (Exception e) { - logger.log(Level.SEVERE, null, e); - return Integer.valueOf(0); - - } finally { - try { - cquery.close(); - } catch (Exception e) { - logger.log(Level.SEVERE, null, e); - } - try { - // we must have our own transaction for background fetching - // and this performs the rollback... returning the - // connection back into the connection pool. - transaction.rollback(); - } catch (Exception e) { - logger.log(Level.SEVERE, null, e); - } - } - - } - - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append("BackgroundFetch ").append(cquery); - return sb.toString(); - } - -} +package com.avaje.ebeaninternal.server.query; + +import java.util.concurrent.Callable; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebeaninternal.api.SpiTransaction; + +/** + * Continue the fetch using a Background thread. The client knows when this has + * finished by checking to see if beanList.finishedFetch() is true. + */ +public class BackgroundFetch implements Callable { + + private static final Logger logger = Logger.getLogger(BackgroundFetch.class.getName()); + + private final CQuery cquery; + + private final SpiTransaction transaction; + + /** + * Create the BackgroundFetch. + */ + public BackgroundFetch(CQuery cquery) { + this.cquery = cquery; + this.transaction = cquery.getTransaction(); + } + + /** + * Continue the fetch. + */ + public Integer call() { + try { + + BeanCollection bc = cquery.continueFetchingInBackground(); + + return bc.size(); + + } catch (Exception e) { + logger.log(Level.SEVERE, null, e); + return Integer.valueOf(0); + + } finally { + try { + cquery.close(); + } catch (Exception e) { + logger.log(Level.SEVERE, null, e); + } + try { + // we must have our own transaction for background fetching + // and this performs the rollback... returning the + // connection back into the connection pool. + transaction.rollback(); + } catch (Exception e) { + logger.log(Level.SEVERE, null, e); + } + } + + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("BackgroundFetch ").append(cquery); + return sb.toString(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/BackgroundIdFetch.java b/src/main/java/com/avaje/ebeaninternal/server/query/BackgroundIdFetch.java index 6b715bc52..254cbb349 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/BackgroundIdFetch.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/BackgroundIdFetch.java @@ -1,128 +1,109 @@ -/** - * 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.query; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.concurrent.Callable; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebeaninternal.api.BeanIdList; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.DbReadContext; - -/** - * Continue the fetch using a Background thread. The client knows when this has - * finished by checking to see if beanList.finishedFetch() is true. - */ -public class BackgroundIdFetch implements Callable { - - private static final Logger logger = Logger.getLogger(BackgroundIdFetch.class.getName()); - - private final ResultSet rset; - - private final PreparedStatement pstmt; - - private final SpiTransaction transaction; - - private final DbReadContext ctx; - - private final BeanDescriptor beanDescriptor; - - private final BeanIdList idList; - /** - * Create the BackgroundFetch. - */ - public BackgroundIdFetch(SpiTransaction transaction, - ResultSet rset, PreparedStatement pstmt, - DbReadContext ctx, BeanDescriptor beanDescriptor, - BeanIdList idList) { - - this.ctx = ctx; - this.transaction = transaction; - this.rset = rset; - this.pstmt = pstmt; - this.beanDescriptor = beanDescriptor; - this.idList = idList; - } - - /** - * Continue the fetch. - */ - public Integer call() { - try { - int startSize = idList.getIdList().size(); - int rowsRead = 0; - while (rset.next()){ - Object idValue = beanDescriptor.getIdBinder().read(ctx); - idList.add(idValue); - ctx.getDataReader().resetColumnPosition(); - rowsRead++; - } - - if (logger.isLoggable(Level.INFO)){ - logger.info("BG FetchIds read:"+rowsRead+" total:"+(startSize+rowsRead)); - } - - return rowsRead; - - } catch (Exception e) { - logger.log(Level.SEVERE, null, e); - return 0; - - } finally { - try { - close(); - } catch (Exception e) { - logger.log(Level.SEVERE, null, e); - } - try { - // we must have our own transaction for background fetching - // and this performs the rollback... returning the - // connection back into the connection pool. - transaction.rollback(); - } catch (Exception e) { - logger.log(Level.SEVERE, null, e); - } - } - - } - - private void close() { - try { - if (rset != null) { - rset.close(); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - try { - if (pstmt != null) { - pstmt.close(); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - } - -} +package com.avaje.ebeaninternal.server.query; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.Callable; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebeaninternal.api.BeanIdList; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.DbReadContext; + +/** + * Continue the fetch using a Background thread. The client knows when this has + * finished by checking to see if beanList.finishedFetch() is true. + */ +public class BackgroundIdFetch implements Callable { + + private static final Logger logger = Logger.getLogger(BackgroundIdFetch.class.getName()); + + private final ResultSet rset; + + private final PreparedStatement pstmt; + + private final SpiTransaction transaction; + + private final DbReadContext ctx; + + private final BeanDescriptor beanDescriptor; + + private final BeanIdList idList; + /** + * Create the BackgroundFetch. + */ + public BackgroundIdFetch(SpiTransaction transaction, + ResultSet rset, PreparedStatement pstmt, + DbReadContext ctx, BeanDescriptor beanDescriptor, + BeanIdList idList) { + + this.ctx = ctx; + this.transaction = transaction; + this.rset = rset; + this.pstmt = pstmt; + this.beanDescriptor = beanDescriptor; + this.idList = idList; + } + + /** + * Continue the fetch. + */ + public Integer call() { + try { + int startSize = idList.getIdList().size(); + int rowsRead = 0; + while (rset.next()){ + Object idValue = beanDescriptor.getIdBinder().read(ctx); + idList.add(idValue); + ctx.getDataReader().resetColumnPosition(); + rowsRead++; + } + + if (logger.isLoggable(Level.INFO)){ + logger.info("BG FetchIds read:"+rowsRead+" total:"+(startSize+rowsRead)); + } + + return rowsRead; + + } catch (Exception e) { + logger.log(Level.SEVERE, null, e); + return 0; + + } finally { + try { + close(); + } catch (Exception e) { + logger.log(Level.SEVERE, null, e); + } + try { + // we must have our own transaction for background fetching + // and this performs the rollback... returning the + // connection back into the connection pool. + transaction.rollback(); + } catch (Exception e) { + logger.log(Level.SEVERE, null, e); + } + } + + } + + private void close() { + try { + if (rset != null) { + rset.close(); + } + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + try { + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/BaseFuture.java b/src/main/java/com/avaje/ebeaninternal/server/query/BaseFuture.java index 31adeba9e..e2067989e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/BaseFuture.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/BaseFuture.java @@ -1,66 +1,47 @@ -/** - * 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.query; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.FutureTask; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * A base object for query Future objects. - * - * @author rbygrave - * - * @param the entity bean type - */ -public abstract class BaseFuture implements Future { - - private final FutureTask futureTask; - - public BaseFuture(FutureTask futureTask) { - this.futureTask = futureTask; - } - - public boolean cancel(boolean mayInterruptIfRunning) { - return futureTask.cancel(mayInterruptIfRunning); - } - - public T get() throws InterruptedException, ExecutionException { - return futureTask.get(); - } - - public T get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - - return futureTask.get(timeout, unit); - } - - public boolean isCancelled() { - return futureTask.isCancelled(); - } - - public boolean isDone() { - return futureTask.isDone(); - } - - -} +package com.avaje.ebeaninternal.server.query; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * A base object for query Future objects. + * + * @author rbygrave + * + * @param the entity bean type + */ +public abstract class BaseFuture implements Future { + + private final FutureTask futureTask; + + public BaseFuture(FutureTask futureTask) { + this.futureTask = futureTask; + } + + public boolean cancel(boolean mayInterruptIfRunning) { + return futureTask.cancel(mayInterruptIfRunning); + } + + public T get() throws InterruptedException, ExecutionException { + return futureTask.get(); + } + + public T get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + + return futureTask.get(timeout, unit); + } + + public boolean isCancelled() { + return futureTask.isCancelled(); + } + + public boolean isDone() { + return futureTask.isDone(); + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/BeanCollectionWrapper.java b/src/main/java/com/avaje/ebeaninternal/server/query/BeanCollectionWrapper.java index 25e88ab4e..05b032cea 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/BeanCollectionWrapper.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/BeanCollectionWrapper.java @@ -1,220 +1,201 @@ -/** - * 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.query; - -import java.util.Collection; -import java.util.Map; - -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; -import com.avaje.ebeaninternal.server.core.RelationalQueryRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.util.BeanCollectionFactory; -import com.avaje.ebeaninternal.server.util.BeanCollectionParams; - -/** - * Wraps a BeanCollection with helper methods to add beans. - *

      - * Helps adding the bean to the underlying set list or map. - *

      - */ -public final class BeanCollectionWrapper { - - /** - * Flag set if this builds a Map rather than a Collection. - */ - private final boolean isMap; - - /** - * The type. - */ - private final SpiQuery.Type queryType; - - /** - * A property name used as key for a Map. - */ - private final String mapKey; - - /** - * The actual BeanCollection. - */ - private final BeanCollection beanCollection; - - /** - * Collection type of BeanCollection. - */ - private final Collection collection; - - /** - * Map type of BeanCollection. - */ - private final Map map; - - /** - * The associated BeanDescriptor. - */ - private final BeanDescriptor desc; - - /** - * The number of rows added. - */ - private int rowCount; - - public BeanCollectionWrapper(RelationalQueryRequest request) { - - this.desc = null; - this.queryType = request.getQueryType(); - this.mapKey = request.getQuery().getMapKey(); - this.isMap = SpiQuery.Type.MAP.equals(queryType); - - this.beanCollection = createBeanCollection(queryType); - this.collection = getCollection(isMap); - this.map = getMap(isMap); - } - - /** - * Create based on a Find. - */ - public BeanCollectionWrapper(OrmQueryRequest request) { - - this.desc = request.getBeanDescriptor(); - this.queryType = request.getQueryType(); - this.mapKey = request.getQuery().getMapKey(); - this.isMap = SpiQuery.Type.MAP.equals(queryType); - - this.beanCollection = createBeanCollection(queryType); - this.collection = getCollection(isMap); - this.map = getMap(isMap); - } - - /** - * Create based on a ManyType and mapKey. Note the mapKey is only used if - * the manyType is a Map. - *

      - * modifyListening is set to true if this is a collection used to hold - * ManyToMany associated objects. - *

      - */ - public BeanCollectionWrapper(BeanPropertyAssocMany manyProp) { - - this.queryType = manyProp.getManyType().getQueryType(); - this.mapKey = manyProp.getMapKey(); - this.desc = manyProp.getTargetDescriptor(); - this.isMap = SpiQuery.Type.MAP.equals(queryType); - - this.beanCollection = createBeanCollection(queryType); - this.collection = getCollection(isMap); - this.map = getMap(isMap); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private Map getMap(boolean isMap) { - return isMap ? (Map)beanCollection : null; - } - - @SuppressWarnings("unchecked") - private Collection getCollection(boolean isMap) { - return isMap ? null : (Collection)beanCollection ; - } - - /** - * Return the underlying BeanCollection. - */ - public BeanCollection getBeanCollection() { - return beanCollection; - } - - /** - * Create a BeanCollection of the correct type. - */ - private BeanCollection createBeanCollection(SpiQuery.Type manyType) { - BeanCollectionParams p = new BeanCollectionParams(manyType); - return BeanCollectionFactory.create(p); - } - - /** - * Return true if this wraps a Map rather than a set or list. - */ - public boolean isMap() { - return isMap; - } - - /** - * Return the number of rows added to this wrapper. - */ - public int size() { - return rowCount; - } - - /** - * Add the bean to the collection held in this wrapper. - */ - public void add(Object bean) { - add(bean, beanCollection); - } - - /** - * Add the bean to the collection passed. - * - * @param bean - * the bean to add - * @param collection - * the collection or map to add the bean to - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public void add(Object bean, Object collection) { - if (bean == null) { - return; - } - rowCount++; - if (isMap) { - Object keyValue = null; - if (mapKey != null) { - // use the value for the property - keyValue = desc.getValue(bean, mapKey); - } else { - // use the uniqueId for this - keyValue = desc.getId(bean); - } - - Map mapColl = (Map) collection; - mapColl.put(keyValue, bean); - } else { - ((Collection) collection).add(bean); - } - } - - /** - * Specifically add to a Collection. - */ - public void addToCollection(Object bean) { - collection.add(bean); - } - - /** - * Specifically add to this as a Map with a known key. - */ - public void addToMap(Object bean, Object key) { - map.put(key, bean); - } - -} +package com.avaje.ebeaninternal.server.query; + +import java.util.Collection; +import java.util.Map; + +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +import com.avaje.ebeaninternal.server.core.RelationalQueryRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.util.BeanCollectionFactory; +import com.avaje.ebeaninternal.server.util.BeanCollectionParams; + +/** + * Wraps a BeanCollection with helper methods to add beans. + *

      + * Helps adding the bean to the underlying set list or map. + *

      + */ +public final class BeanCollectionWrapper { + + /** + * Flag set if this builds a Map rather than a Collection. + */ + private final boolean isMap; + + /** + * The type. + */ + private final SpiQuery.Type queryType; + + /** + * A property name used as key for a Map. + */ + private final String mapKey; + + /** + * The actual BeanCollection. + */ + private final BeanCollection beanCollection; + + /** + * Collection type of BeanCollection. + */ + private final Collection collection; + + /** + * Map type of BeanCollection. + */ + private final Map map; + + /** + * The associated BeanDescriptor. + */ + private final BeanDescriptor desc; + + /** + * The number of rows added. + */ + private int rowCount; + + public BeanCollectionWrapper(RelationalQueryRequest request) { + + this.desc = null; + this.queryType = request.getQueryType(); + this.mapKey = request.getQuery().getMapKey(); + this.isMap = SpiQuery.Type.MAP.equals(queryType); + + this.beanCollection = createBeanCollection(queryType); + this.collection = getCollection(isMap); + this.map = getMap(isMap); + } + + /** + * Create based on a Find. + */ + public BeanCollectionWrapper(OrmQueryRequest request) { + + this.desc = request.getBeanDescriptor(); + this.queryType = request.getQueryType(); + this.mapKey = request.getQuery().getMapKey(); + this.isMap = SpiQuery.Type.MAP.equals(queryType); + + this.beanCollection = createBeanCollection(queryType); + this.collection = getCollection(isMap); + this.map = getMap(isMap); + } + + /** + * Create based on a ManyType and mapKey. Note the mapKey is only used if + * the manyType is a Map. + *

      + * modifyListening is set to true if this is a collection used to hold + * ManyToMany associated objects. + *

      + */ + public BeanCollectionWrapper(BeanPropertyAssocMany manyProp) { + + this.queryType = manyProp.getManyType().getQueryType(); + this.mapKey = manyProp.getMapKey(); + this.desc = manyProp.getTargetDescriptor(); + this.isMap = SpiQuery.Type.MAP.equals(queryType); + + this.beanCollection = createBeanCollection(queryType); + this.collection = getCollection(isMap); + this.map = getMap(isMap); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private Map getMap(boolean isMap) { + return isMap ? (Map)beanCollection : null; + } + + @SuppressWarnings("unchecked") + private Collection getCollection(boolean isMap) { + return isMap ? null : (Collection)beanCollection ; + } + + /** + * Return the underlying BeanCollection. + */ + public BeanCollection getBeanCollection() { + return beanCollection; + } + + /** + * Create a BeanCollection of the correct type. + */ + private BeanCollection createBeanCollection(SpiQuery.Type manyType) { + BeanCollectionParams p = new BeanCollectionParams(manyType); + return BeanCollectionFactory.create(p); + } + + /** + * Return true if this wraps a Map rather than a set or list. + */ + public boolean isMap() { + return isMap; + } + + /** + * Return the number of rows added to this wrapper. + */ + public int size() { + return rowCount; + } + + /** + * Add the bean to the collection held in this wrapper. + */ + public void add(Object bean) { + add(bean, beanCollection); + } + + /** + * Add the bean to the collection passed. + * + * @param bean + * the bean to add + * @param collection + * the collection or map to add the bean to + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void add(Object bean, Object collection) { + if (bean == null) { + return; + } + rowCount++; + if (isMap) { + Object keyValue = null; + if (mapKey != null) { + // use the value for the property + keyValue = desc.getValue(bean, mapKey); + } else { + // use the uniqueId for this + keyValue = desc.getId(bean); + } + + Map mapColl = (Map) collection; + mapColl.put(keyValue, bean); + } else { + ((Collection) collection).add(bean); + } + } + + /** + * Specifically add to a Collection. + */ + public void addToCollection(Object bean) { + collection.add(bean); + } + + /** + * Specifically add to this as a Map with a known key. + */ + public void addToMap(Object bean, Object key) { + map.put(key, bean); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java index 038848f28..33efb6ccb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java @@ -1,879 +1,860 @@ -/** - * 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.query; - -import java.lang.ref.WeakReference; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.QueryIterator; -import com.avaje.ebean.QueryListener; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.BeanCollectionAdd; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.bean.NodeUsageCollector; -import com.avaje.ebean.bean.NodeUsageListener; -import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.api.LoadContext; -import com.avaje.ebeaninternal.api.SpiExpressionList; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.api.SpiQuery.Mode; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; -import com.avaje.ebeaninternal.server.core.Message; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; -import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest; -import com.avaje.ebeaninternal.server.deploy.BeanCollectionHelp; -import com.avaje.ebeaninternal.server.deploy.BeanCollectionHelpFactory; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.DbReadContext; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties; -import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext; -import com.avaje.ebeaninternal.server.type.DataBind; -import com.avaje.ebeaninternal.server.type.DataReader; - -/** - * An object that represents a SqlSelect statement. - *

      - * The SqlSelect is based on a tree (Object Graph). The tree is traversed to see - * what parts are included in the tree according to the value of - * find.getInclude(); - *

      - *

      - * The tree structure is flattened into a SqlSelectChain. The SqlSelectChain is - * the key object used in reading the flat resultSet back into Objects. - *

      - */ -public class CQuery implements DbReadContext, CancelableQuery { - - private static final Logger logger = Logger.getLogger(CQuery.class.getName()); - - private static final int GLOBAL_ROW_LIMIT = 1000000; - - /** - * The resultSet rows read. - */ - private int rowCount; - - /** - * The number of master EntityBeans loaded. - */ - private int loadedBeanCount; - - /** - * Flag set when no more rows are in the resultSet. - */ - private boolean noMoreRows; - /** - * Id of loaded 'master' bean. - */ - private Object loadedBeanId; - /** - * Flag set when 'master' bean changed. - */ - boolean loadedBeanChanged; - /** - * The 'master' bean just loaded. - */ - private Object loadedBean; - - /** - * Holds the previous loaded bean. - */ - private Object prevLoadedBean; - - /** - * The detail bean just loaded. - */ - private Object loadedManyBean; - - /** - * The previous 'detail' collection remembered so that for manyToMany we can - * turn on the modify listening. - */ - private Object prevDetailCollection; - - /** - * The current 'detail' collection being populated. - */ - private Object currentDetailCollection; - - /** - * The 'master' collection being populated. - */ - private final BeanCollection collection; - /** - * The help for the 'master' collection. - */ - private final BeanCollectionHelp help; - - /** - * The overall find request wrapper object. - */ - private final OrmQueryRequest request; - - private final BeanDescriptor desc; - - private final SpiQuery query; - - private final QueryListener queryListener; - - private Map currentPathMap; - - private String currentPrefix; - - /** - * Flag set true when reading 'master' and 'detail' beans. - */ - private final boolean manyIncluded; - - /** - * Where clause predicates. - */ - private final CQueryPredicates predicates; - - /** - * Object handling the SELECT generation and reading. - */ - private final SqlTree sqlTree; - - private final boolean rawSql; - - /** - * The final sql that is generated. - */ - private final String sql; - - /** - * Where clause to show in logs when using an existing query plan. - */ - private final String logWhereSql; - - /** - * Set to true if the row number column is included in the sql. - */ - private final boolean rowNumberIncluded; - - /** - * Tree that knows how to build the master and detail beans from the - * resultSet. - */ - private final SqlTreeNode rootNode; - - /** - * For master detail query. - */ - private final BeanPropertyAssocMany manyProperty; - - /** - * The many property Expression language object. - */ - private final ElPropertyValue manyPropertyEl; - - private final int backgroundFetchAfter; - - private final int maxRowsLimit; - - /** - * Flag set when backgroundFetchAfter limit is hit. - */ - private boolean hasHitBackgroundFetchAfter; - - private final PersistenceContext persistenceContext; - - private DataReader dataReader; - - /** - * The statement used to create the resultSet. - */ - private PreparedStatement pstmt; - - private boolean cancelled; - - private String bindLog; - - private final CQueryPlan queryPlan; - - private long startNano; - - private final Mode queryMode; - - private final boolean autoFetchProfiling; - - private final ObjectGraphNode autoFetchParentNode; - - private final AutoFetchManager autoFetchManager; - private final WeakReference autoFetchManagerRef; - - private int executionTimeMicros; - - private final Boolean readOnly; - - private final SpiExpressionList filterMany; - - /** - * Create the Sql select based on the request. - */ - @SuppressWarnings("unchecked") - public CQuery(OrmQueryRequest request, CQueryPredicates predicates, CQueryPlan queryPlan) { - this.request = request; - this.queryPlan = queryPlan; - this.query = request.getQuery(); - this.queryMode = query.getMode(); - - this.readOnly = request.isReadOnly(); - - this.autoFetchManager = query.getAutoFetchManager(); - this.autoFetchProfiling = autoFetchManager != null; - this.autoFetchParentNode = autoFetchProfiling ? query.getParentNode() : null; - this.autoFetchManagerRef = autoFetchProfiling ? new WeakReference(autoFetchManager) : null; - - // set the generated sql back to the query - // so its available to the user... - query.setGeneratedSql(queryPlan.getSql()); - - this.sqlTree = queryPlan.getSqlTree(); - this.rootNode = sqlTree.getRootNode(); - - this.manyProperty = sqlTree.getManyProperty(); - this.manyPropertyEl = sqlTree.getManyPropertyEl(); - this.manyIncluded = sqlTree.isManyIncluded(); - if (manyIncluded) { - // get filter to put on the collection for reuse with refresh - String manyPropertyName = sqlTree.getManyPropertyName(); - OrmQueryProperties chunk = query.getDetail().getChunk(manyPropertyName, false); - this.filterMany = chunk.getFilterMany(); - } else { - this.filterMany = null; - } - - this.sql = queryPlan.getSql(); - this.rawSql = queryPlan.isRawSql(); - this.rowNumberIncluded = queryPlan.isRowNumberIncluded(); - this.logWhereSql = queryPlan.getLogWhereSql(); - this.desc = request.getBeanDescriptor(); - this.predicates = predicates; - - this.queryListener = query.getListener(); - if (queryListener == null) { - // normal, use the one from the transaction - this.persistenceContext = request.getPersistenceContext(); - } else { - // 'Row Level Transaction Context'... - // local transaction context that will be reset - // after each 'master' bean is sent to the listener - this.persistenceContext = new DefaultPersistenceContext(); - } - - this.maxRowsLimit = query.getMaxRows() > 0 ? query.getMaxRows() : GLOBAL_ROW_LIMIT; - this.backgroundFetchAfter = query.getBackgroundFetchAfter() > 0 ? query.getBackgroundFetchAfter() : Integer.MAX_VALUE; - - this.help = createHelp(request); - this.collection = (BeanCollection)(help != null ? help.createEmpty(false) : null); - } - - private BeanCollectionHelp createHelp(OrmQueryRequest request) { - if (request.isFindById()) { - return null; - } else { - SpiQuery.Type manyType = request.getQuery().getType(); - if (manyType == null){ - // subQuery compiled for InQueryExpression - return null; - } - return BeanCollectionHelpFactory.create(request); - } - } - - public Boolean isReadOnly() { - return readOnly; - } - - public void propagateState(Object e) { - if (Boolean.TRUE.equals(readOnly)){ - if (e instanceof EntityBean){ - ((EntityBean)e)._ebean_getIntercept().setReadOnly(true); - } - } - } - - public DataReader getDataReader() { - return dataReader; - } - - public Mode getQueryMode() { - return queryMode; - } - - /** - * Return true if we want to return vanilla (not enhanced) objects. - */ - public boolean isVanillaMode() { - return request.isVanillaMode(); - } - - public CQueryPredicates getPredicates() { - return predicates; - } - - public LoadContext getGraphContext() { - return request.getGraphContext(); - } - - public SpiOrmQueryRequest getQueryRequest() { - return request; - } - - public void cancel() { - synchronized (this) { - this.cancelled = true; - if (pstmt != null){ - try { - pstmt.cancel(); - } catch (SQLException e){ - String msg = "Error cancelling query"; - throw new PersistenceException(msg, e); - } - } - } - } - - public boolean prepareBindExecuteQuery() throws SQLException { - - synchronized (this) { - if (cancelled || query.isCancelled()){ - // cancelled before we started - cancelled = true; - return false; - } - - startNano = System.nanoTime(); - - // prepare - SpiTransaction t = request.getTransaction(); - Connection conn = t.getInternalConnection(); - pstmt = conn.prepareStatement(sql); - - if (query.getTimeout() > 0){ - pstmt.setQueryTimeout(query.getTimeout()); - } - if (query.getBufferFetchSizeHint() > 0){ - pstmt.setFetchSize(query.getBufferFetchSizeHint()); - } - - DataBind dataBind = new DataBind(pstmt); - - // bind keys for encrypted properties - queryPlan.bindEncryptedProperties(dataBind); - - bindLog = predicates.bind(dataBind); - - // executeQuery - ResultSet rset = pstmt.executeQuery(); - dataReader = queryPlan.createDataReader(rset); - - return true; - } - } - - /** - * Close the resources. - *

      - * The jdbc resultSet and statement need to be closed. Its important that - * this method is called. - *

      - */ - public void close() { - try { - if (dataReader != null) { - dataReader.close(); - dataReader = null; - } - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - try { - if (pstmt != null) { - pstmt.close(); - pstmt = null; - } - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - } - -// /** -// * Return the reference options used to define cache use. -// */ -// public ReferenceOptions getReferenceOptionsFor(BeanPropertyAssocOne beanProp) { -// -// String beanPropName = beanProp.getName(); -// if (currentPrefix != null){ -// beanPropName = currentPrefix+"."+beanPropName; -// } -// //ReferenceOptions opt = referenceOptionsMap.get(beanPropName); -// if (opt == null){ -// OrmQueryProperties chunk = queryDetail.getChunk(beanPropName, false); -// if (chunk != null) { -// // get the options from the query -// opt = chunk.getReferenceOptions(); -// } -// if (opt == null){ -// // get the default options defined for the target bean type -// opt = beanProp.getTargetDescriptor().getReferenceOptions(); -// } -// referenceOptionsMap.put(beanPropName, opt); -// } -// -// return opt; -// } - - /** - * Return the persistence context. - */ - public PersistenceContext getPersistenceContext(){ - return persistenceContext; - } - - public void setLoadedBean(Object bean, Object id) { - if (id != null && id.equals(loadedBeanId)) { - // master/detail loading with master bean - // unchanged. NB Using id to avoid any issue - // with equals not being implemented - - } else { - if (manyIncluded) { - if (rowCount > 1) { - loadedBeanChanged = true; - } - this.prevLoadedBean = loadedBean; - this.loadedBeanId = id; - } - this.loadedBean = bean; - } - } - - public void setLoadedManyBean(Object manyValue) { - this.loadedManyBean = manyValue; - } - - /** - * Return the last read bean. - */ - @SuppressWarnings("unchecked") - public T getLoadedBean() { - if (manyIncluded) { - if (prevDetailCollection instanceof BeanCollection) { - ((BeanCollection)prevDetailCollection).setModifyListening(manyProperty.getModifyListenMode()); - - } else if (currentDetailCollection instanceof BeanCollection) { - ((BeanCollection)currentDetailCollection).setModifyListening(manyProperty.getModifyListenMode()); - } - } - - if (prevLoadedBean != null) { - return (T)prevLoadedBean; - } else { - return (T)loadedBean; - } - } - - private boolean hasMoreRows() throws SQLException { - synchronized (this) { - if (cancelled){ - return false; - } - return dataReader.next(); - } - } - - /** - * Read a row from the result set returning a bean. - *

      - * If the query includes a many then the first object in the returned array - * is the one/master and the second the many/detail. - *

      - */ - private boolean readRow() throws SQLException { - - synchronized (this) { - if (cancelled){ - return false; - } - - if (!dataReader.next()){ - return false; - } - - rowCount++; - dataReader.resetColumnPosition(); - - if (rowNumberIncluded) { - // row_number() column used for limit features - dataReader.incrementPos(1); - } - - rootNode.load(this, null); - - return true; - } - } - - public int getQueryExecutionTimeMicros(){ - return executionTimeMicros; - } - - public boolean readBean() throws SQLException { - - boolean result = readBeanInternal(true); - - updateExecutionStatistics(); - - return result; - } - - private boolean readBeanInternal(boolean inForeground) throws SQLException { - - if (loadedBeanCount >= maxRowsLimit) { - collection.setHasMoreRows(hasMoreRows()); - return false; - } - - if (inForeground && loadedBeanCount >= backgroundFetchAfter) { - hasHitBackgroundFetchAfter = true; - collection.setFinishedFetch(false); - return false; - } - - if (!manyIncluded) { - // simple query... no details... - return readRow(); - } - - if (noMoreRows) { - return false; - } - - if (rowCount == 0) { - if (!readRow()) { - // no rows at all... - return false; - } else { - createNewDetailCollection(); - } - } - - if (readIntoCurrentDetailCollection()) { - createNewDetailCollection(); - // return prevLoadedBean - return true; - - } else { - // return loadedBean - prevDetailCollection = null; - prevLoadedBean = null; - noMoreRows = true; - return true; - } - } - - private boolean readIntoCurrentDetailCollection() throws SQLException { - while (readRow()) { - if (loadedBeanChanged) { - loadedBeanChanged = false; - return true; - } else { - addToCurrentDetailCollection(); - } - } - return false; - } - - private BeanCollectionAdd currentDetailAdd; - - private void createNewDetailCollection() { - prevDetailCollection = currentDetailCollection; - if (queryMode.equals(Mode.LAZYLOAD_MANY)){ - // just populate the current collection - currentDetailCollection = manyPropertyEl.elGetValue(loadedBean); - } else { - // create a new collection to populate and assign to the bean - currentDetailCollection = manyProperty.createEmpty(request.isVanillaMode()); - manyPropertyEl.elSetValue(loadedBean, currentDetailCollection, false, false); - } - - if (filterMany != null && !request.isVanillaMode()){ - // remember the for use with a refresh - ((BeanCollection)currentDetailCollection).setFilterMany(filterMany); - } - - // the manyKey is always null for this case, just using default mapKey on the property - currentDetailAdd = manyProperty.getBeanCollectionAdd(currentDetailCollection, null); - addToCurrentDetailCollection(); - } - - private void addToCurrentDetailCollection() { - if (loadedManyBean != null) { - currentDetailAdd.addBean(loadedManyBean); - } - } - - public BeanCollection continueFetchingInBackground() throws SQLException { - readTheRows(false); - collection.setFinishedFetch(true); - return collection; - } - - public BeanCollection readCollection() throws SQLException { - - readTheRows(true); - - updateExecutionStatistics(); - - return collection; - } - - protected void updateExecutionStatistics() { - try { - long exeNano = System.nanoTime() - startNano; - executionTimeMicros = (int)exeNano/1000; - - if (autoFetchProfiling){ - autoFetchManager.collectQueryInfo(autoFetchParentNode, loadedBeanCount, executionTimeMicros); - } - queryPlan.executionTime(loadedBeanCount, executionTimeMicros); - - } catch (Exception e){ - logger.log(Level.SEVERE, null, e); - } - } - - public QueryIterator readIterate(int bufferSize, OrmQueryRequest request) { - - if (bufferSize > 0){ - return new CQueryIteratorWithBuffer(this, request, bufferSize); - - } else { - return new CQueryIteratorSimple(this, request); - } - } - - private void readTheRows(boolean inForeground) throws SQLException { - while (hasNextBean(inForeground)) { - if (queryListener != null) { - queryListener.process(getLoadedBean()); - - } else { - // add to the list/set/map - help.add(collection, getLoadedBean()); - } - } - } - - - protected boolean hasNextBean(boolean inForeground) throws SQLException { - - if (!readBeanInternal(inForeground)) { - return false; - - } else { - loadedBeanCount++; - return true; - } - } - - public String getLoadedRowDetail() { - if (!manyIncluded) { - return String.valueOf(rowCount); - } else { - return loadedBeanCount + ":" + rowCount; - } - } - - public void register(String path, EntityBeanIntercept ebi){ - - path = getPath(path); - request.getGraphContext().register(path, ebi); - } - - public void register(String path, BeanCollection bc){ - - path = getPath(path); - request.getGraphContext().register(path, bc); - } - - - public boolean useBackgroundToContinueFetch() { - return hasHitBackgroundFetchAfter; - } - - /** - * Return the query name. - */ - public String getName() { - return query.getName(); - } - - /** - * Return true if this is a raw sql query as opposed to Ebean generated sql. - */ - public boolean isRawSql() { - return rawSql; - } - - /** - * Return the where predicate for display in the transaction log. - */ - public String getLogWhereSql() { - return logWhereSql; - } - - /** - * Return the property that is associated with the many. There can only be - * one per SqlSelect. This can be null. - */ - public BeanPropertyAssocMany getManyProperty() { - return manyProperty; - } - - /** - * Get the summary of the sql. - */ - public String getSummary() { - return sqlTree.getSummary(); - } - - /** - * Return the SqlSelectChain. This is the flattened structure that - * represents this query. - */ - public SqlTree getSqlTree() { - return sqlTree; - } - - public String getBindLog() { - return bindLog; - } - - public SpiTransaction getTransaction() { - return request.getTransaction(); - } - - public String getBeanType() { - return desc.getFullName(); - } - - /** - * Return the short bean name. - */ - public String getBeanName() { - return desc.getName(); - } - - /** - * Return the generated sql. - */ - public String getGeneratedSql() { - return sql; - } - - /** - * Create a PersistenceException including interesting information like the bindLog and sql used. - */ - public PersistenceException createPersistenceException(SQLException e) { - - return createPersistenceException(e, getTransaction(), bindLog, sql); - } - - /** - * Create a PersistenceException including interesting information like the bindLog and sql used. - */ - public static PersistenceException createPersistenceException(SQLException e, SpiTransaction t, String bindLog, String sql) { - - if (t.isLogSummary()) { - // log the error to the transaction log - String errMsg = StringHelper.replaceStringMulti(e.getMessage(), new String[] { "\r", "\n" }, "\\n "); - String msg = "ERROR executing query: bindLog[" + bindLog + "] error[" + errMsg + "]"; - t.logInternal(msg); - } - - // ensure 'rollback' is logged if queryOnly transaction - t.getConnection(); - - // build a decent error message for the exception - String m = Message.msg("fetch.sqlerror", e.getMessage(), bindLog, sql); - return new PersistenceException(m, e); - } - - /** - * Should we create profileNodes for beans created in this query. - *

      - * This is true for all queries except lazy load bean queries. - *

      - */ - public boolean isAutoFetchProfiling() { - // need query.isProfiling() because we just take the data - // from the lazy loaded or refreshed beans and put it into the already - // existing beans which are already collecting usage information - return autoFetchProfiling && query.isUsageProfiling(); - } - - private String getPath(String propertyName) { - - if (currentPrefix == null){ - return propertyName; - } else if (propertyName == null) { - return currentPrefix; - } - - String path = currentPathMap.get(propertyName); - if (path != null){ - return path; - } else { - return currentPrefix+"."+propertyName; - } - } - - - public void profileBean(EntityBeanIntercept ebi, String prefix) { - - ObjectGraphNode node = request.getGraphContext().getObjectGraphNode(prefix); - - ebi.setNodeUsageCollector(new NodeUsageCollector(node, autoFetchManagerRef)); - } - - public void setCurrentPrefix(String currentPrefix, Map currentPathMap) { - this.currentPrefix = currentPrefix; - this.currentPathMap = currentPathMap; - } - -} +package com.avaje.ebeaninternal.server.query; + +import java.lang.ref.WeakReference; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.QueryIterator; +import com.avaje.ebean.QueryListener; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.BeanCollectionAdd; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.bean.NodeUsageCollector; +import com.avaje.ebean.bean.NodeUsageListener; +import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebeaninternal.api.LoadContext; +import com.avaje.ebeaninternal.api.SpiExpressionList; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.api.SpiQuery.Mode; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; +import com.avaje.ebeaninternal.server.core.Message; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest; +import com.avaje.ebeaninternal.server.deploy.BeanCollectionHelp; +import com.avaje.ebeaninternal.server.deploy.BeanCollectionHelpFactory; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.DbReadContext; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties; +import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.type.DataReader; + +/** + * An object that represents a SqlSelect statement. + *

      + * The SqlSelect is based on a tree (Object Graph). The tree is traversed to see + * what parts are included in the tree according to the value of + * find.getInclude(); + *

      + *

      + * The tree structure is flattened into a SqlSelectChain. The SqlSelectChain is + * the key object used in reading the flat resultSet back into Objects. + *

      + */ +public class CQuery implements DbReadContext, CancelableQuery { + + private static final Logger logger = Logger.getLogger(CQuery.class.getName()); + + private static final int GLOBAL_ROW_LIMIT = 1000000; + + /** + * The resultSet rows read. + */ + private int rowCount; + + /** + * The number of master EntityBeans loaded. + */ + private int loadedBeanCount; + + /** + * Flag set when no more rows are in the resultSet. + */ + private boolean noMoreRows; + /** + * Id of loaded 'master' bean. + */ + private Object loadedBeanId; + /** + * Flag set when 'master' bean changed. + */ + boolean loadedBeanChanged; + /** + * The 'master' bean just loaded. + */ + private Object loadedBean; + + /** + * Holds the previous loaded bean. + */ + private Object prevLoadedBean; + + /** + * The detail bean just loaded. + */ + private Object loadedManyBean; + + /** + * The previous 'detail' collection remembered so that for manyToMany we can + * turn on the modify listening. + */ + private Object prevDetailCollection; + + /** + * The current 'detail' collection being populated. + */ + private Object currentDetailCollection; + + /** + * The 'master' collection being populated. + */ + private final BeanCollection collection; + /** + * The help for the 'master' collection. + */ + private final BeanCollectionHelp help; + + /** + * The overall find request wrapper object. + */ + private final OrmQueryRequest request; + + private final BeanDescriptor desc; + + private final SpiQuery query; + + private final QueryListener queryListener; + + private Map currentPathMap; + + private String currentPrefix; + + /** + * Flag set true when reading 'master' and 'detail' beans. + */ + private final boolean manyIncluded; + + /** + * Where clause predicates. + */ + private final CQueryPredicates predicates; + + /** + * Object handling the SELECT generation and reading. + */ + private final SqlTree sqlTree; + + private final boolean rawSql; + + /** + * The final sql that is generated. + */ + private final String sql; + + /** + * Where clause to show in logs when using an existing query plan. + */ + private final String logWhereSql; + + /** + * Set to true if the row number column is included in the sql. + */ + private final boolean rowNumberIncluded; + + /** + * Tree that knows how to build the master and detail beans from the + * resultSet. + */ + private final SqlTreeNode rootNode; + + /** + * For master detail query. + */ + private final BeanPropertyAssocMany manyProperty; + + /** + * The many property Expression language object. + */ + private final ElPropertyValue manyPropertyEl; + + private final int backgroundFetchAfter; + + private final int maxRowsLimit; + + /** + * Flag set when backgroundFetchAfter limit is hit. + */ + private boolean hasHitBackgroundFetchAfter; + + private final PersistenceContext persistenceContext; + + private DataReader dataReader; + + /** + * The statement used to create the resultSet. + */ + private PreparedStatement pstmt; + + private boolean cancelled; + + private String bindLog; + + private final CQueryPlan queryPlan; + + private long startNano; + + private final Mode queryMode; + + private final boolean autoFetchProfiling; + + private final ObjectGraphNode autoFetchParentNode; + + private final AutoFetchManager autoFetchManager; + private final WeakReference autoFetchManagerRef; + + private int executionTimeMicros; + + private final Boolean readOnly; + + private final SpiExpressionList filterMany; + + /** + * Create the Sql select based on the request. + */ + @SuppressWarnings("unchecked") + public CQuery(OrmQueryRequest request, CQueryPredicates predicates, CQueryPlan queryPlan) { + this.request = request; + this.queryPlan = queryPlan; + this.query = request.getQuery(); + this.queryMode = query.getMode(); + + this.readOnly = request.isReadOnly(); + + this.autoFetchManager = query.getAutoFetchManager(); + this.autoFetchProfiling = autoFetchManager != null; + this.autoFetchParentNode = autoFetchProfiling ? query.getParentNode() : null; + this.autoFetchManagerRef = autoFetchProfiling ? new WeakReference(autoFetchManager) : null; + + // set the generated sql back to the query + // so its available to the user... + query.setGeneratedSql(queryPlan.getSql()); + + this.sqlTree = queryPlan.getSqlTree(); + this.rootNode = sqlTree.getRootNode(); + + this.manyProperty = sqlTree.getManyProperty(); + this.manyPropertyEl = sqlTree.getManyPropertyEl(); + this.manyIncluded = sqlTree.isManyIncluded(); + if (manyIncluded) { + // get filter to put on the collection for reuse with refresh + String manyPropertyName = sqlTree.getManyPropertyName(); + OrmQueryProperties chunk = query.getDetail().getChunk(manyPropertyName, false); + this.filterMany = chunk.getFilterMany(); + } else { + this.filterMany = null; + } + + this.sql = queryPlan.getSql(); + this.rawSql = queryPlan.isRawSql(); + this.rowNumberIncluded = queryPlan.isRowNumberIncluded(); + this.logWhereSql = queryPlan.getLogWhereSql(); + this.desc = request.getBeanDescriptor(); + this.predicates = predicates; + + this.queryListener = query.getListener(); + if (queryListener == null) { + // normal, use the one from the transaction + this.persistenceContext = request.getPersistenceContext(); + } else { + // 'Row Level Transaction Context'... + // local transaction context that will be reset + // after each 'master' bean is sent to the listener + this.persistenceContext = new DefaultPersistenceContext(); + } + + this.maxRowsLimit = query.getMaxRows() > 0 ? query.getMaxRows() : GLOBAL_ROW_LIMIT; + this.backgroundFetchAfter = query.getBackgroundFetchAfter() > 0 ? query.getBackgroundFetchAfter() : Integer.MAX_VALUE; + + this.help = createHelp(request); + this.collection = (BeanCollection)(help != null ? help.createEmpty(false) : null); + } + + private BeanCollectionHelp createHelp(OrmQueryRequest request) { + if (request.isFindById()) { + return null; + } else { + SpiQuery.Type manyType = request.getQuery().getType(); + if (manyType == null){ + // subQuery compiled for InQueryExpression + return null; + } + return BeanCollectionHelpFactory.create(request); + } + } + + public Boolean isReadOnly() { + return readOnly; + } + + public void propagateState(Object e) { + if (Boolean.TRUE.equals(readOnly)){ + if (e instanceof EntityBean){ + ((EntityBean)e)._ebean_getIntercept().setReadOnly(true); + } + } + } + + public DataReader getDataReader() { + return dataReader; + } + + public Mode getQueryMode() { + return queryMode; + } + + /** + * Return true if we want to return vanilla (not enhanced) objects. + */ + public boolean isVanillaMode() { + return request.isVanillaMode(); + } + + public CQueryPredicates getPredicates() { + return predicates; + } + + public LoadContext getGraphContext() { + return request.getGraphContext(); + } + + public SpiOrmQueryRequest getQueryRequest() { + return request; + } + + public void cancel() { + synchronized (this) { + this.cancelled = true; + if (pstmt != null){ + try { + pstmt.cancel(); + } catch (SQLException e){ + String msg = "Error cancelling query"; + throw new PersistenceException(msg, e); + } + } + } + } + + public boolean prepareBindExecuteQuery() throws SQLException { + + synchronized (this) { + if (cancelled || query.isCancelled()){ + // cancelled before we started + cancelled = true; + return false; + } + + startNano = System.nanoTime(); + + // prepare + SpiTransaction t = request.getTransaction(); + Connection conn = t.getInternalConnection(); + pstmt = conn.prepareStatement(sql); + + if (query.getTimeout() > 0){ + pstmt.setQueryTimeout(query.getTimeout()); + } + if (query.getBufferFetchSizeHint() > 0){ + pstmt.setFetchSize(query.getBufferFetchSizeHint()); + } + + DataBind dataBind = new DataBind(pstmt); + + // bind keys for encrypted properties + queryPlan.bindEncryptedProperties(dataBind); + + bindLog = predicates.bind(dataBind); + + // executeQuery + ResultSet rset = pstmt.executeQuery(); + dataReader = queryPlan.createDataReader(rset); + + return true; + } + } + + /** + * Close the resources. + *

      + * The jdbc resultSet and statement need to be closed. Its important that + * this method is called. + *

      + */ + public void close() { + try { + if (dataReader != null) { + dataReader.close(); + dataReader = null; + } + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + try { + if (pstmt != null) { + pstmt.close(); + pstmt = null; + } + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + } + +// /** +// * Return the reference options used to define cache use. +// */ +// public ReferenceOptions getReferenceOptionsFor(BeanPropertyAssocOne beanProp) { +// +// String beanPropName = beanProp.getName(); +// if (currentPrefix != null){ +// beanPropName = currentPrefix+"."+beanPropName; +// } +// //ReferenceOptions opt = referenceOptionsMap.get(beanPropName); +// if (opt == null){ +// OrmQueryProperties chunk = queryDetail.getChunk(beanPropName, false); +// if (chunk != null) { +// // get the options from the query +// opt = chunk.getReferenceOptions(); +// } +// if (opt == null){ +// // get the default options defined for the target bean type +// opt = beanProp.getTargetDescriptor().getReferenceOptions(); +// } +// referenceOptionsMap.put(beanPropName, opt); +// } +// +// return opt; +// } + + /** + * Return the persistence context. + */ + public PersistenceContext getPersistenceContext(){ + return persistenceContext; + } + + public void setLoadedBean(Object bean, Object id) { + if (id != null && id.equals(loadedBeanId)) { + // master/detail loading with master bean + // unchanged. NB Using id to avoid any issue + // with equals not being implemented + + } else { + if (manyIncluded) { + if (rowCount > 1) { + loadedBeanChanged = true; + } + this.prevLoadedBean = loadedBean; + this.loadedBeanId = id; + } + this.loadedBean = bean; + } + } + + public void setLoadedManyBean(Object manyValue) { + this.loadedManyBean = manyValue; + } + + /** + * Return the last read bean. + */ + @SuppressWarnings("unchecked") + public T getLoadedBean() { + if (manyIncluded) { + if (prevDetailCollection instanceof BeanCollection) { + ((BeanCollection)prevDetailCollection).setModifyListening(manyProperty.getModifyListenMode()); + + } else if (currentDetailCollection instanceof BeanCollection) { + ((BeanCollection)currentDetailCollection).setModifyListening(manyProperty.getModifyListenMode()); + } + } + + if (prevLoadedBean != null) { + return (T)prevLoadedBean; + } else { + return (T)loadedBean; + } + } + + private boolean hasMoreRows() throws SQLException { + synchronized (this) { + if (cancelled){ + return false; + } + return dataReader.next(); + } + } + + /** + * Read a row from the result set returning a bean. + *

      + * If the query includes a many then the first object in the returned array + * is the one/master and the second the many/detail. + *

      + */ + private boolean readRow() throws SQLException { + + synchronized (this) { + if (cancelled){ + return false; + } + + if (!dataReader.next()){ + return false; + } + + rowCount++; + dataReader.resetColumnPosition(); + + if (rowNumberIncluded) { + // row_number() column used for limit features + dataReader.incrementPos(1); + } + + rootNode.load(this, null); + + return true; + } + } + + public int getQueryExecutionTimeMicros(){ + return executionTimeMicros; + } + + public boolean readBean() throws SQLException { + + boolean result = readBeanInternal(true); + + updateExecutionStatistics(); + + return result; + } + + private boolean readBeanInternal(boolean inForeground) throws SQLException { + + if (loadedBeanCount >= maxRowsLimit) { + collection.setHasMoreRows(hasMoreRows()); + return false; + } + + if (inForeground && loadedBeanCount >= backgroundFetchAfter) { + hasHitBackgroundFetchAfter = true; + collection.setFinishedFetch(false); + return false; + } + + if (!manyIncluded) { + // simple query... no details... + return readRow(); + } + + if (noMoreRows) { + return false; + } + + if (rowCount == 0) { + if (!readRow()) { + // no rows at all... + return false; + } else { + createNewDetailCollection(); + } + } + + if (readIntoCurrentDetailCollection()) { + createNewDetailCollection(); + // return prevLoadedBean + return true; + + } else { + // return loadedBean + prevDetailCollection = null; + prevLoadedBean = null; + noMoreRows = true; + return true; + } + } + + private boolean readIntoCurrentDetailCollection() throws SQLException { + while (readRow()) { + if (loadedBeanChanged) { + loadedBeanChanged = false; + return true; + } else { + addToCurrentDetailCollection(); + } + } + return false; + } + + private BeanCollectionAdd currentDetailAdd; + + private void createNewDetailCollection() { + prevDetailCollection = currentDetailCollection; + if (queryMode.equals(Mode.LAZYLOAD_MANY)){ + // just populate the current collection + currentDetailCollection = manyPropertyEl.elGetValue(loadedBean); + } else { + // create a new collection to populate and assign to the bean + currentDetailCollection = manyProperty.createEmpty(request.isVanillaMode()); + manyPropertyEl.elSetValue(loadedBean, currentDetailCollection, false, false); + } + + if (filterMany != null && !request.isVanillaMode()){ + // remember the for use with a refresh + ((BeanCollection)currentDetailCollection).setFilterMany(filterMany); + } + + // the manyKey is always null for this case, just using default mapKey on the property + currentDetailAdd = manyProperty.getBeanCollectionAdd(currentDetailCollection, null); + addToCurrentDetailCollection(); + } + + private void addToCurrentDetailCollection() { + if (loadedManyBean != null) { + currentDetailAdd.addBean(loadedManyBean); + } + } + + public BeanCollection continueFetchingInBackground() throws SQLException { + readTheRows(false); + collection.setFinishedFetch(true); + return collection; + } + + public BeanCollection readCollection() throws SQLException { + + readTheRows(true); + + updateExecutionStatistics(); + + return collection; + } + + protected void updateExecutionStatistics() { + try { + long exeNano = System.nanoTime() - startNano; + executionTimeMicros = (int)exeNano/1000; + + if (autoFetchProfiling){ + autoFetchManager.collectQueryInfo(autoFetchParentNode, loadedBeanCount, executionTimeMicros); + } + queryPlan.executionTime(loadedBeanCount, executionTimeMicros); + + } catch (Exception e){ + logger.log(Level.SEVERE, null, e); + } + } + + public QueryIterator readIterate(int bufferSize, OrmQueryRequest request) { + + if (bufferSize > 0){ + return new CQueryIteratorWithBuffer(this, request, bufferSize); + + } else { + return new CQueryIteratorSimple(this, request); + } + } + + private void readTheRows(boolean inForeground) throws SQLException { + while (hasNextBean(inForeground)) { + if (queryListener != null) { + queryListener.process(getLoadedBean()); + + } else { + // add to the list/set/map + help.add(collection, getLoadedBean()); + } + } + } + + + protected boolean hasNextBean(boolean inForeground) throws SQLException { + + if (!readBeanInternal(inForeground)) { + return false; + + } else { + loadedBeanCount++; + return true; + } + } + + public String getLoadedRowDetail() { + if (!manyIncluded) { + return String.valueOf(rowCount); + } else { + return loadedBeanCount + ":" + rowCount; + } + } + + public void register(String path, EntityBeanIntercept ebi){ + + path = getPath(path); + request.getGraphContext().register(path, ebi); + } + + public void register(String path, BeanCollection bc){ + + path = getPath(path); + request.getGraphContext().register(path, bc); + } + + + public boolean useBackgroundToContinueFetch() { + return hasHitBackgroundFetchAfter; + } + + /** + * Return the query name. + */ + public String getName() { + return query.getName(); + } + + /** + * Return true if this is a raw sql query as opposed to Ebean generated sql. + */ + public boolean isRawSql() { + return rawSql; + } + + /** + * Return the where predicate for display in the transaction log. + */ + public String getLogWhereSql() { + return logWhereSql; + } + + /** + * Return the property that is associated with the many. There can only be + * one per SqlSelect. This can be null. + */ + public BeanPropertyAssocMany getManyProperty() { + return manyProperty; + } + + /** + * Get the summary of the sql. + */ + public String getSummary() { + return sqlTree.getSummary(); + } + + /** + * Return the SqlSelectChain. This is the flattened structure that + * represents this query. + */ + public SqlTree getSqlTree() { + return sqlTree; + } + + public String getBindLog() { + return bindLog; + } + + public SpiTransaction getTransaction() { + return request.getTransaction(); + } + + public String getBeanType() { + return desc.getFullName(); + } + + /** + * Return the short bean name. + */ + public String getBeanName() { + return desc.getName(); + } + + /** + * Return the generated sql. + */ + public String getGeneratedSql() { + return sql; + } + + /** + * Create a PersistenceException including interesting information like the bindLog and sql used. + */ + public PersistenceException createPersistenceException(SQLException e) { + + return createPersistenceException(e, getTransaction(), bindLog, sql); + } + + /** + * Create a PersistenceException including interesting information like the bindLog and sql used. + */ + public static PersistenceException createPersistenceException(SQLException e, SpiTransaction t, String bindLog, String sql) { + + if (t.isLogSummary()) { + // log the error to the transaction log + String errMsg = StringHelper.replaceStringMulti(e.getMessage(), new String[] { "\r", "\n" }, "\\n "); + String msg = "ERROR executing query: bindLog[" + bindLog + "] error[" + errMsg + "]"; + t.logInternal(msg); + } + + // ensure 'rollback' is logged if queryOnly transaction + t.getConnection(); + + // build a decent error message for the exception + String m = Message.msg("fetch.sqlerror", e.getMessage(), bindLog, sql); + return new PersistenceException(m, e); + } + + /** + * Should we create profileNodes for beans created in this query. + *

      + * This is true for all queries except lazy load bean queries. + *

      + */ + public boolean isAutoFetchProfiling() { + // need query.isProfiling() because we just take the data + // from the lazy loaded or refreshed beans and put it into the already + // existing beans which are already collecting usage information + return autoFetchProfiling && query.isUsageProfiling(); + } + + private String getPath(String propertyName) { + + if (currentPrefix == null){ + return propertyName; + } else if (propertyName == null) { + return currentPrefix; + } + + String path = currentPathMap.get(propertyName); + if (path != null){ + return path; + } else { + return currentPrefix+"."+propertyName; + } + } + + + public void profileBean(EntityBeanIntercept ebi, String prefix) { + + ObjectGraphNode node = request.getGraphContext().getObjectGraphNode(prefix); + + ebi.setNodeUsageCollector(new NodeUsageCollector(node, autoFetchManagerRef)); + } + + public void setCurrentPrefix(String currentPrefix, Map currentPathMap) { + this.currentPrefix = currentPrefix; + this.currentPathMap = currentPathMap; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java index 54c13d2cb..265e40f70 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilder.java @@ -1,417 +1,398 @@ -/** - * 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.query; - -import java.util.Iterator; -import java.util.Set; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.BackgroundExecutor; -import com.avaje.ebean.RawSql; -import com.avaje.ebean.RawSql.ColumnMapping; -import com.avaje.ebean.RawSql.ColumnMapping.Column; -import com.avaje.ebean.RawSqlBuilder; -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebean.config.dbplatform.SqlLimitRequest; -import com.avaje.ebean.config.dbplatform.SqlLimitResponse; -import com.avaje.ebean.config.dbplatform.SqlLimiter; -import com.avaje.ebean.text.PathProperties; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; -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.BeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.persist.Binder; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest; - -/** - * Generates the SQL SELECT statements taking into account the physical - * deployment properties. - */ -public class CQueryBuilder implements Constants { - - private final String tableAliasPlaceHolder; - private final String columnAliasPrefix; - - private final SqlLimiter sqlLimiter; - - private final RawSqlSelectClauseBuilder sqlSelectBuilder; - private final CQueryBuilderRawSql rawSqlHandler; - - private final Binder binder; - - private final BackgroundExecutor backgroundExecutor; - - private final boolean selectCountWithAlias; - - private DatabasePlatform dbPlatform; - - /** - * Create the SqlGenSelect. - */ - public CQueryBuilder(BackgroundExecutor backgroundExecutor, DatabasePlatform dbPlatform, Binder binder) { - - this.backgroundExecutor = backgroundExecutor; - this.binder = binder; - this.tableAliasPlaceHolder = GlobalProperties.get("ebean.tableAliasPlaceHolder", "${ta}"); - this.columnAliasPrefix = GlobalProperties.get("ebean.columnAliasPrefix", "c"); - this.sqlSelectBuilder = new RawSqlSelectClauseBuilder(dbPlatform, binder); - - this.sqlLimiter = dbPlatform.getSqlLimiter(); - this.rawSqlHandler = new CQueryBuilderRawSql(sqlLimiter, dbPlatform); - - this.selectCountWithAlias = dbPlatform.isSelectCountWithAlias(); - - this.dbPlatform = dbPlatform; - } - - /** - * split the order by claus on the field delimiter and prefix each field with - * the relation name - */ - public static String prefixOrderByFields(String name, String orderBy) { - StringBuilder sb = new StringBuilder(); - for (String token : orderBy.split(",")) { - if (sb.length() > 0) { - sb.append(", "); - } - - sb.append(name); - sb.append("."); - sb.append(token.trim()); - } - - return sb.toString(); - } - - /** - * Build the row count query. - */ - public CQueryFetchIds buildFetchIdsQuery(OrmQueryRequest request) { - - SpiQuery query = request.getQuery(); - - query.setSelectId(); - - CQueryPredicates predicates = new CQueryPredicates(binder, request); - CQueryPlan queryPlan = request.getQueryPlan(); - if (queryPlan != null) { - // skip building the SqlTree and Sql string - predicates.prepare(false); - String sql = queryPlan.getSql(); - return new CQueryFetchIds(request, predicates, sql, backgroundExecutor); - - } - - // use RawSql or generated Sql - predicates.prepare(true); - - SqlTree sqlTree = createSqlTree(request, predicates); - SqlLimitResponse s = buildSql(null, request, predicates, sqlTree); - String sql = s.getSql(); - - // cache the query plan - queryPlan = new CQueryPlan(sql, sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql()); - - request.putQueryPlan(queryPlan); - return new CQueryFetchIds(request, predicates, sql, backgroundExecutor); - } - - /** - * Build the row count query. - */ - public CQueryRowCount buildRowCountQuery(OrmQueryRequest request) { - - SpiQuery query = request.getQuery(); - - // always set the order by to null for row count query - query.setOrder(null); - - boolean hasMany = !query.getManyWhereJoins().isEmpty(); - - query.setSelectId(); - - String sqlSelect = "select count(*)"; - if (hasMany) { - // need to count distinct id's ... - query.setDistinct(true); - sqlSelect = null; - } - - CQueryPredicates predicates = new CQueryPredicates(binder, request); - CQueryPlan queryPlan = request.getQueryPlan(); - if (queryPlan != null) { - // skip building the SqlTree and Sql string - predicates.prepare(false); - String sql = queryPlan.getSql(); - return new CQueryRowCount(request, predicates, sql); - } - - predicates.prepare(true); - - SqlTree sqlTree = createSqlTree(request, predicates); - SqlLimitResponse s = buildSql(sqlSelect, request, predicates, sqlTree); - String sql = s.getSql(); - if (hasMany) { - sql = "select count(*) from ( " + sql + ")"; - if (selectCountWithAlias) { - sql += " as c"; - } - } - - // cache the query plan - queryPlan = new CQueryPlan(sql, sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql()); - request.putQueryPlan(queryPlan); - - return new CQueryRowCount(request, predicates, sql); - } - - /** - * Return the SQL Select statement as a String. Converts logical property - * names to physical deployment column names. - */ - public CQuery buildQuery(OrmQueryRequest request) { - - if (request.isSqlSelect()) { - return sqlSelectBuilder.build(request); - } - - CQueryPredicates predicates = new CQueryPredicates(binder, request); - - CQueryPlan queryPlan = request.getQueryPlan(); - if (queryPlan != null) { - // Reuse the query plan so skip generating SqlTree and SQL. - // We do prepare and bind the new parameters - predicates.prepare(false); - return new CQuery(request, predicates, queryPlan); - } - - // RawSql or Generated Sql query - - // Prepare the where, having and order by clauses. - // This also parses them from logical property names to - // database columns and determines 'includes'. - - // We need to check these 'includes' for extra joins - // that are not included via select - predicates.prepare(true); - - // Build the tree structure that represents the query. - SqlTree sqlTree = createSqlTree(request, predicates); - SqlLimitResponse res = buildSql(null, request, predicates, sqlTree); - - boolean rawSql = request.isRawSql(); - if (rawSql) { - queryPlan = new CQueryPlanRawSql(request, res, sqlTree, predicates.getLogWhereSql()); - - } else { - queryPlan = new CQueryPlan(request, res, sqlTree, rawSql, predicates.getLogWhereSql(), null); - } - - // cache the query plan because we can reuse it and also - // gather query performance statistics based on it. - request.putQueryPlan(queryPlan); - - return new CQuery(request, predicates, queryPlan); - } - - /** - * Build the SqlTree. - *

      - * The SqlTree is immutable after construction and so is safe to use by - * concurrent threads. - *

      - *

      - * The predicates is used to add additional joins that come from the where or - * order by clauses that are not already included for the select clause. - *

      - */ - private SqlTree createSqlTree(OrmQueryRequest request, CQueryPredicates predicates) { - - if (request.isRawSql()) { - return createRawSqlSqlTree(request, predicates); - } - - return new SqlTreeBuilder(tableAliasPlaceHolder, columnAliasPrefix, request, predicates).build(); - } - - private SqlTree createRawSqlSqlTree(OrmQueryRequest request, CQueryPredicates predicates) { - - BeanDescriptor descriptor = request.getBeanDescriptor(); - ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping(); - - PathProperties pathProps = new PathProperties(); - - // convert list of columns into (tree like) PathProperties - Iterator it = columnMapping.getColumns(); - while (it.hasNext()) { - RawSql.ColumnMapping.Column column = it.next(); - String propertyName = column.getPropertyName(); - if (!RawSqlBuilder.IGNORE_COLUMN.equals(propertyName)) { - - ElPropertyValue el = descriptor.getElGetValue(propertyName); - if (el == null) { - String msg = "Property [" + propertyName + "] not found on " + descriptor.getFullName(); - throw new PersistenceException(msg); - } - BeanProperty beanProperty = el.getBeanProperty(); - if (beanProperty.isId()) { - // For @Id properties we chop off the last part of the path - propertyName = SplitName.parent(propertyName); - } else if (beanProperty instanceof BeanPropertyAssocOne) { - String msg = "Column [" + column.getDbColumn() + "] mapped to complex Property[" + propertyName + "]"; - msg += ". It should be mapped to a simple property (proably the Id property). "; - throw new PersistenceException(msg); - } - if (propertyName != null) { - String[] pathProp = SplitName.split(propertyName); - pathProps.addToPath(pathProp[0], pathProp[1]); - } - } - } - - OrmQueryDetail detail = new OrmQueryDetail(); - - // transfer PathProperties into OrmQueryDetail - Iterator pathIt = pathProps.getPaths().iterator(); - while (pathIt.hasNext()) { - String path = pathIt.next(); - Set props = pathProps.get(path); - detail.getChunk(path, true).setDefaultProperties(null, props); - } - - // build SqlTree based on OrmQueryDetail of the RawSql - return new SqlTreeBuilder(request, predicates, detail).build(); - } - - private SqlLimitResponse buildSql(String selectClause, OrmQueryRequest request, CQueryPredicates predicates, SqlTree select) { - - SpiQuery query = request.getQuery(); - - RawSql rawSql = query.getRawSql(); - if (rawSql != null) { - return rawSqlHandler.buildSql(request, predicates, rawSql.getSql()); - } - - BeanPropertyAssocMany manyProp = select.getManyProperty(); - - boolean useSqlLimiter = false; - - StringBuilder sb = new StringBuilder(500); - - if (selectClause != null) { - sb.append(selectClause); - - } else { - - useSqlLimiter = (query.hasMaxRowsOrFirstRow() && manyProp == null); - - if (!useSqlLimiter) { - sb.append("select "); - if (query.isDistinct()) { - sb.append("distinct "); - } - } - - sb.append(select.getSelectSql()); - } - - sb.append(" ").append(NEW_LINE); - sb.append("from "); - - // build the from clause potentially with joins - // required only for the predicates - sb.append(select.getFromSql()); - - String inheritanceWhere = select.getInheritanceWhereSql(); - - boolean hasWhere = false; - if (inheritanceWhere.length() > 0) { - sb.append(" ").append(NEW_LINE).append("where"); - sb.append(inheritanceWhere); - hasWhere = true; - } - - if (request.isFindById() || query.getId() != null) { - if (hasWhere) { - sb.append(" and "); - } else { - sb.append(NEW_LINE).append("where "); - } - - BeanDescriptor desc = request.getBeanDescriptor(); - String idSql = desc.getIdBinderIdSql(); - if (idSql.isEmpty()) { - throw new IllegalStateException("Executing FindById query on entity bean " + desc.getName() - + " that doesn't have an @Id property??"); - } - sb.append(idSql).append(" "); - hasWhere = true; - } - - String dbWhere = predicates.getDbWhere(); - if (!isEmpty(dbWhere)) { - if (!hasWhere) { - hasWhere = true; - sb.append(" ").append(NEW_LINE).append("where "); - } else { - sb.append("and "); - } - sb.append(dbWhere); - } - - String dbFilterMany = predicates.getDbFilterMany(); - if (!isEmpty(dbFilterMany)) { - if (!hasWhere) { - sb.append(" ").append(NEW_LINE).append("where "); - } else { - sb.append("and "); - } - sb.append(dbFilterMany); - } - - String dbOrderBy = predicates.getDbOrderBy(); - if (dbOrderBy != null) { - sb.append(" ").append(NEW_LINE); - sb.append("order by ").append(dbOrderBy); - } - - if (useSqlLimiter) { - // use LIMIT/OFFSET, ROW_NUMBER() or rownum type SQL query limitation - SqlLimitRequest r = new OrmQueryLimitRequest(sb.toString(), dbOrderBy, query, dbPlatform); - return sqlLimiter.limit(r); - - } else { - - return new SqlLimitResponse(dbPlatform.completeSql(sb.toString(), query), false); - } - - } - - private boolean isEmpty(String s) { - return s == null || s.length() == 0; - } - -} +package com.avaje.ebeaninternal.server.query; + +import java.util.Iterator; +import java.util.Set; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.BackgroundExecutor; +import com.avaje.ebean.RawSql; +import com.avaje.ebean.RawSql.ColumnMapping; +import com.avaje.ebean.RawSql.ColumnMapping.Column; +import com.avaje.ebean.RawSqlBuilder; +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.config.dbplatform.SqlLimitRequest; +import com.avaje.ebean.config.dbplatform.SqlLimitResponse; +import com.avaje.ebean.config.dbplatform.SqlLimiter; +import com.avaje.ebean.text.PathProperties; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +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.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.persist.Binder; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest; + +/** + * Generates the SQL SELECT statements taking into account the physical + * deployment properties. + */ +public class CQueryBuilder implements Constants { + + private final String tableAliasPlaceHolder; + private final String columnAliasPrefix; + + private final SqlLimiter sqlLimiter; + + private final RawSqlSelectClauseBuilder sqlSelectBuilder; + private final CQueryBuilderRawSql rawSqlHandler; + + private final Binder binder; + + private final BackgroundExecutor backgroundExecutor; + + private final boolean selectCountWithAlias; + + private DatabasePlatform dbPlatform; + + /** + * Create the SqlGenSelect. + */ + public CQueryBuilder(BackgroundExecutor backgroundExecutor, DatabasePlatform dbPlatform, Binder binder) { + + this.backgroundExecutor = backgroundExecutor; + this.binder = binder; + this.tableAliasPlaceHolder = GlobalProperties.get("ebean.tableAliasPlaceHolder", "${ta}"); + this.columnAliasPrefix = GlobalProperties.get("ebean.columnAliasPrefix", "c"); + this.sqlSelectBuilder = new RawSqlSelectClauseBuilder(dbPlatform, binder); + + this.sqlLimiter = dbPlatform.getSqlLimiter(); + this.rawSqlHandler = new CQueryBuilderRawSql(sqlLimiter, dbPlatform); + + this.selectCountWithAlias = dbPlatform.isSelectCountWithAlias(); + + this.dbPlatform = dbPlatform; + } + + /** + * split the order by claus on the field delimiter and prefix each field with + * the relation name + */ + public static String prefixOrderByFields(String name, String orderBy) { + StringBuilder sb = new StringBuilder(); + for (String token : orderBy.split(",")) { + if (sb.length() > 0) { + sb.append(", "); + } + + sb.append(name); + sb.append("."); + sb.append(token.trim()); + } + + return sb.toString(); + } + + /** + * Build the row count query. + */ + public CQueryFetchIds buildFetchIdsQuery(OrmQueryRequest request) { + + SpiQuery query = request.getQuery(); + + query.setSelectId(); + + CQueryPredicates predicates = new CQueryPredicates(binder, request); + CQueryPlan queryPlan = request.getQueryPlan(); + if (queryPlan != null) { + // skip building the SqlTree and Sql string + predicates.prepare(false); + String sql = queryPlan.getSql(); + return new CQueryFetchIds(request, predicates, sql, backgroundExecutor); + + } + + // use RawSql or generated Sql + predicates.prepare(true); + + SqlTree sqlTree = createSqlTree(request, predicates); + SqlLimitResponse s = buildSql(null, request, predicates, sqlTree); + String sql = s.getSql(); + + // cache the query plan + queryPlan = new CQueryPlan(sql, sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql()); + + request.putQueryPlan(queryPlan); + return new CQueryFetchIds(request, predicates, sql, backgroundExecutor); + } + + /** + * Build the row count query. + */ + public CQueryRowCount buildRowCountQuery(OrmQueryRequest request) { + + SpiQuery query = request.getQuery(); + + // always set the order by to null for row count query + query.setOrder(null); + + boolean hasMany = !query.getManyWhereJoins().isEmpty(); + + query.setSelectId(); + + String sqlSelect = "select count(*)"; + if (hasMany) { + // need to count distinct id's ... + query.setDistinct(true); + sqlSelect = null; + } + + CQueryPredicates predicates = new CQueryPredicates(binder, request); + CQueryPlan queryPlan = request.getQueryPlan(); + if (queryPlan != null) { + // skip building the SqlTree and Sql string + predicates.prepare(false); + String sql = queryPlan.getSql(); + return new CQueryRowCount(request, predicates, sql); + } + + predicates.prepare(true); + + SqlTree sqlTree = createSqlTree(request, predicates); + SqlLimitResponse s = buildSql(sqlSelect, request, predicates, sqlTree); + String sql = s.getSql(); + if (hasMany) { + sql = "select count(*) from ( " + sql + ")"; + if (selectCountWithAlias) { + sql += " as c"; + } + } + + // cache the query plan + queryPlan = new CQueryPlan(sql, sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql()); + request.putQueryPlan(queryPlan); + + return new CQueryRowCount(request, predicates, sql); + } + + /** + * Return the SQL Select statement as a String. Converts logical property + * names to physical deployment column names. + */ + public CQuery buildQuery(OrmQueryRequest request) { + + if (request.isSqlSelect()) { + return sqlSelectBuilder.build(request); + } + + CQueryPredicates predicates = new CQueryPredicates(binder, request); + + CQueryPlan queryPlan = request.getQueryPlan(); + if (queryPlan != null) { + // Reuse the query plan so skip generating SqlTree and SQL. + // We do prepare and bind the new parameters + predicates.prepare(false); + return new CQuery(request, predicates, queryPlan); + } + + // RawSql or Generated Sql query + + // Prepare the where, having and order by clauses. + // This also parses them from logical property names to + // database columns and determines 'includes'. + + // We need to check these 'includes' for extra joins + // that are not included via select + predicates.prepare(true); + + // Build the tree structure that represents the query. + SqlTree sqlTree = createSqlTree(request, predicates); + SqlLimitResponse res = buildSql(null, request, predicates, sqlTree); + + boolean rawSql = request.isRawSql(); + if (rawSql) { + queryPlan = new CQueryPlanRawSql(request, res, sqlTree, predicates.getLogWhereSql()); + + } else { + queryPlan = new CQueryPlan(request, res, sqlTree, rawSql, predicates.getLogWhereSql(), null); + } + + // cache the query plan because we can reuse it and also + // gather query performance statistics based on it. + request.putQueryPlan(queryPlan); + + return new CQuery(request, predicates, queryPlan); + } + + /** + * Build the SqlTree. + *

      + * The SqlTree is immutable after construction and so is safe to use by + * concurrent threads. + *

      + *

      + * The predicates is used to add additional joins that come from the where or + * order by clauses that are not already included for the select clause. + *

      + */ + private SqlTree createSqlTree(OrmQueryRequest request, CQueryPredicates predicates) { + + if (request.isRawSql()) { + return createRawSqlSqlTree(request, predicates); + } + + return new SqlTreeBuilder(tableAliasPlaceHolder, columnAliasPrefix, request, predicates).build(); + } + + private SqlTree createRawSqlSqlTree(OrmQueryRequest request, CQueryPredicates predicates) { + + BeanDescriptor descriptor = request.getBeanDescriptor(); + ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping(); + + PathProperties pathProps = new PathProperties(); + + // convert list of columns into (tree like) PathProperties + Iterator it = columnMapping.getColumns(); + while (it.hasNext()) { + RawSql.ColumnMapping.Column column = it.next(); + String propertyName = column.getPropertyName(); + if (!RawSqlBuilder.IGNORE_COLUMN.equals(propertyName)) { + + ElPropertyValue el = descriptor.getElGetValue(propertyName); + if (el == null) { + String msg = "Property [" + propertyName + "] not found on " + descriptor.getFullName(); + throw new PersistenceException(msg); + } + BeanProperty beanProperty = el.getBeanProperty(); + if (beanProperty.isId()) { + // For @Id properties we chop off the last part of the path + propertyName = SplitName.parent(propertyName); + } else if (beanProperty instanceof BeanPropertyAssocOne) { + String msg = "Column [" + column.getDbColumn() + "] mapped to complex Property[" + propertyName + "]"; + msg += ". It should be mapped to a simple property (proably the Id property). "; + throw new PersistenceException(msg); + } + if (propertyName != null) { + String[] pathProp = SplitName.split(propertyName); + pathProps.addToPath(pathProp[0], pathProp[1]); + } + } + } + + OrmQueryDetail detail = new OrmQueryDetail(); + + // transfer PathProperties into OrmQueryDetail + Iterator pathIt = pathProps.getPaths().iterator(); + while (pathIt.hasNext()) { + String path = pathIt.next(); + Set props = pathProps.get(path); + detail.getChunk(path, true).setDefaultProperties(null, props); + } + + // build SqlTree based on OrmQueryDetail of the RawSql + return new SqlTreeBuilder(request, predicates, detail).build(); + } + + private SqlLimitResponse buildSql(String selectClause, OrmQueryRequest request, CQueryPredicates predicates, SqlTree select) { + + SpiQuery query = request.getQuery(); + + RawSql rawSql = query.getRawSql(); + if (rawSql != null) { + return rawSqlHandler.buildSql(request, predicates, rawSql.getSql()); + } + + BeanPropertyAssocMany manyProp = select.getManyProperty(); + + boolean useSqlLimiter = false; + + StringBuilder sb = new StringBuilder(500); + + if (selectClause != null) { + sb.append(selectClause); + + } else { + + useSqlLimiter = (query.hasMaxRowsOrFirstRow() && manyProp == null); + + if (!useSqlLimiter) { + sb.append("select "); + if (query.isDistinct()) { + sb.append("distinct "); + } + } + + sb.append(select.getSelectSql()); + } + + sb.append(" ").append(NEW_LINE); + sb.append("from "); + + // build the from clause potentially with joins + // required only for the predicates + sb.append(select.getFromSql()); + + String inheritanceWhere = select.getInheritanceWhereSql(); + + boolean hasWhere = false; + if (inheritanceWhere.length() > 0) { + sb.append(" ").append(NEW_LINE).append("where"); + sb.append(inheritanceWhere); + hasWhere = true; + } + + if (request.isFindById() || query.getId() != null) { + if (hasWhere) { + sb.append(" and "); + } else { + sb.append(NEW_LINE).append("where "); + } + + BeanDescriptor desc = request.getBeanDescriptor(); + String idSql = desc.getIdBinderIdSql(); + if (idSql.isEmpty()) { + throw new IllegalStateException("Executing FindById query on entity bean " + desc.getName() + + " that doesn't have an @Id property??"); + } + sb.append(idSql).append(" "); + hasWhere = true; + } + + String dbWhere = predicates.getDbWhere(); + if (!isEmpty(dbWhere)) { + if (!hasWhere) { + hasWhere = true; + sb.append(" ").append(NEW_LINE).append("where "); + } else { + sb.append("and "); + } + sb.append(dbWhere); + } + + String dbFilterMany = predicates.getDbFilterMany(); + if (!isEmpty(dbFilterMany)) { + if (!hasWhere) { + sb.append(" ").append(NEW_LINE).append("where "); + } else { + sb.append("and "); + } + sb.append(dbFilterMany); + } + + String dbOrderBy = predicates.getDbOrderBy(); + if (dbOrderBy != null) { + sb.append(" ").append(NEW_LINE); + sb.append("order by ").append(dbOrderBy); + } + + if (useSqlLimiter) { + // use LIMIT/OFFSET, ROW_NUMBER() or rownum type SQL query limitation + SqlLimitRequest r = new OrmQueryLimitRequest(sb.toString(), dbOrderBy, query, dbPlatform); + return sqlLimiter.limit(r); + + } else { + + return new SqlLimitResponse(dbPlatform.completeSql(sb.toString(), query), false); + } + + } + + private boolean isEmpty(String s) { + return s == null || s.length() == 0; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilderRawSql.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilderRawSql.java index 303b2633f..be2ebe6b6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilderRawSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilderRawSql.java @@ -1,167 +1,148 @@ -/** - * 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.query; - -import com.avaje.ebean.RawSql; -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebean.config.dbplatform.SqlLimitResponse; -import com.avaje.ebean.config.dbplatform.SqlLimiter; -import com.avaje.ebeaninternal.api.BindParams; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest; -import com.avaje.ebeaninternal.server.util.BindParamsParser; - -public class CQueryBuilderRawSql implements Constants { - - private final SqlLimiter sqlLimiter; - private final DatabasePlatform dbPlatform; - - CQueryBuilderRawSql(SqlLimiter sqlLimiter, DatabasePlatform dbPlatform) { - this.sqlLimiter = sqlLimiter; - this.dbPlatform = dbPlatform; - } - - /** - * Build the full SQL Select statement for the request. - */ - public SqlLimitResponse buildSql(OrmQueryRequest request, CQueryPredicates predicates, RawSql.Sql rsql) { - - if (!rsql.isParsed()){ - String sql = rsql.getUnparsedSql(); - BindParams bindParams = request.getQuery().getBindParams(); - if (bindParams != null && bindParams.requiresNamedParamsPrepare()){ - // convert named parameters into positioned parameters - sql = BindParamsParser.parse(bindParams, sql); - } - - return new SqlLimitResponse(sql, false); - } - - String orderBy = getOrderBy(predicates, rsql); - - // build the actual sql String - String sql = buildMainQuery(orderBy, request, predicates, rsql); - - SpiQuery query = request.getQuery(); - if (query.hasMaxRowsOrFirstRow() && sqlLimiter != null) { - // wrap with a limit offset or ROW_NUMBER() etc - return sqlLimiter.limit(new OrmQueryLimitRequest(sql, orderBy, query, dbPlatform)); - - } else { - // add back select keyword (it was removed to support sqlQueryLimiter) - String prefix = "select "+ (rsql.isDistinct() ? "distinct " : ""); - sql = prefix + sql; - return new SqlLimitResponse(sql, false); - } - } - - private String buildMainQuery(String orderBy, OrmQueryRequest request, CQueryPredicates predicates, RawSql.Sql sql) { - - StringBuilder sb = new StringBuilder(); - sb.append(sql.getPreFrom()); - sb.append(" "); - sb.append(NEW_LINE); - - String s = sql.getPreWhere(); - BindParams bindParams = request.getQuery().getBindParams(); - if (bindParams != null && bindParams.requiresNamedParamsPrepare()){ - // convert named parameters into positioned parameters - // Named Parameters only allowed prior to dynamic where - // clause (so not allowed in having etc - use unparsed) - s = BindParamsParser.parse(bindParams, s); - } - sb.append(s); - sb.append(" "); - - String dynamicWhere = null; - if (request.getQuery().getId() != null) { - // need to convert this as well. This avoids the - // assumption that id has its proper dbColumn assigned - // which may change if using multiple raw sql statements - // against the same bean. - BeanDescriptor descriptor = request.getBeanDescriptor(); - //FIXME: I think this is broken... needs to be logical - // and then parsed for RawSqlSelect... - dynamicWhere = descriptor.getIdBinderIdSql(); - } - - String dbWhere = predicates.getDbWhere(); - if (!isEmpty(dbWhere)) { - if (dynamicWhere == null) { - dynamicWhere = dbWhere; - } else { - dynamicWhere += " and " + dbWhere; - } - } - - if (!isEmpty(dynamicWhere)) { - sb.append(NEW_LINE); - if (sql.isAndWhereExpr()) { - sb.append("and "); - } else { - sb.append("where "); - } - sb.append(dynamicWhere); - sb.append(" "); - } - - String preHaving = sql.getPreHaving(); - if (!isEmpty(preHaving)) { - sb.append(NEW_LINE); - sb.append(preHaving); - sb.append(" "); - } - - String dbHaving = predicates.getDbHaving(); - if (!isEmpty(dbHaving)) { - sb.append(" "); - sb.append(NEW_LINE); - if (sql.isAndHavingExpr()) { - sb.append("and "); - } else { - sb.append("having "); - } - sb.append(dbHaving); - sb.append(" "); - } - - if (!isEmpty(orderBy)) { - sb.append(NEW_LINE); - sb.append(" order by ").append(orderBy); - } - - return sb.toString().trim(); - } - - private boolean isEmpty(String s) { - return s == null || s.length() == 0; - } - - private String getOrderBy(CQueryPredicates predicates, RawSql.Sql sql) { - String orderBy = predicates.getDbOrderBy(); - if (orderBy != null) { - return orderBy; - } else { - return sql.getOrderBy(); - } - } -} +package com.avaje.ebeaninternal.server.query; + +import com.avaje.ebean.RawSql; +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.config.dbplatform.SqlLimitResponse; +import com.avaje.ebean.config.dbplatform.SqlLimiter; +import com.avaje.ebeaninternal.api.BindParams; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest; +import com.avaje.ebeaninternal.server.util.BindParamsParser; + +public class CQueryBuilderRawSql implements Constants { + + private final SqlLimiter sqlLimiter; + private final DatabasePlatform dbPlatform; + + CQueryBuilderRawSql(SqlLimiter sqlLimiter, DatabasePlatform dbPlatform) { + this.sqlLimiter = sqlLimiter; + this.dbPlatform = dbPlatform; + } + + /** + * Build the full SQL Select statement for the request. + */ + public SqlLimitResponse buildSql(OrmQueryRequest request, CQueryPredicates predicates, RawSql.Sql rsql) { + + if (!rsql.isParsed()){ + String sql = rsql.getUnparsedSql(); + BindParams bindParams = request.getQuery().getBindParams(); + if (bindParams != null && bindParams.requiresNamedParamsPrepare()){ + // convert named parameters into positioned parameters + sql = BindParamsParser.parse(bindParams, sql); + } + + return new SqlLimitResponse(sql, false); + } + + String orderBy = getOrderBy(predicates, rsql); + + // build the actual sql String + String sql = buildMainQuery(orderBy, request, predicates, rsql); + + SpiQuery query = request.getQuery(); + if (query.hasMaxRowsOrFirstRow() && sqlLimiter != null) { + // wrap with a limit offset or ROW_NUMBER() etc + return sqlLimiter.limit(new OrmQueryLimitRequest(sql, orderBy, query, dbPlatform)); + + } else { + // add back select keyword (it was removed to support sqlQueryLimiter) + String prefix = "select "+ (rsql.isDistinct() ? "distinct " : ""); + sql = prefix + sql; + return new SqlLimitResponse(sql, false); + } + } + + private String buildMainQuery(String orderBy, OrmQueryRequest request, CQueryPredicates predicates, RawSql.Sql sql) { + + StringBuilder sb = new StringBuilder(); + sb.append(sql.getPreFrom()); + sb.append(" "); + sb.append(NEW_LINE); + + String s = sql.getPreWhere(); + BindParams bindParams = request.getQuery().getBindParams(); + if (bindParams != null && bindParams.requiresNamedParamsPrepare()){ + // convert named parameters into positioned parameters + // Named Parameters only allowed prior to dynamic where + // clause (so not allowed in having etc - use unparsed) + s = BindParamsParser.parse(bindParams, s); + } + sb.append(s); + sb.append(" "); + + String dynamicWhere = null; + if (request.getQuery().getId() != null) { + // need to convert this as well. This avoids the + // assumption that id has its proper dbColumn assigned + // which may change if using multiple raw sql statements + // against the same bean. + BeanDescriptor descriptor = request.getBeanDescriptor(); + //FIXME: I think this is broken... needs to be logical + // and then parsed for RawSqlSelect... + dynamicWhere = descriptor.getIdBinderIdSql(); + } + + String dbWhere = predicates.getDbWhere(); + if (!isEmpty(dbWhere)) { + if (dynamicWhere == null) { + dynamicWhere = dbWhere; + } else { + dynamicWhere += " and " + dbWhere; + } + } + + if (!isEmpty(dynamicWhere)) { + sb.append(NEW_LINE); + if (sql.isAndWhereExpr()) { + sb.append("and "); + } else { + sb.append("where "); + } + sb.append(dynamicWhere); + sb.append(" "); + } + + String preHaving = sql.getPreHaving(); + if (!isEmpty(preHaving)) { + sb.append(NEW_LINE); + sb.append(preHaving); + sb.append(" "); + } + + String dbHaving = predicates.getDbHaving(); + if (!isEmpty(dbHaving)) { + sb.append(" "); + sb.append(NEW_LINE); + if (sql.isAndHavingExpr()) { + sb.append("and "); + } else { + sb.append("having "); + } + sb.append(dbHaving); + sb.append(" "); + } + + if (!isEmpty(orderBy)) { + sb.append(NEW_LINE); + sb.append(" order by ").append(orderBy); + } + + return sb.toString().trim(); + } + + private boolean isEmpty(String s) { + return s == null || s.length() == 0; + } + + private String getOrderBy(CQueryPredicates predicates, RawSql.Sql sql) { + String orderBy = predicates.getDbOrderBy(); + if (orderBy != null) { + return orderBy; + } else { + return sql.getOrderBy(); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java index 55016c4c1..75a9c159e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryEngine.java @@ -1,417 +1,398 @@ -/** - * 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.query; - -import java.sql.SQLException; -import java.util.concurrent.FutureTask; -import java.util.logging.Logger; - -import com.avaje.ebean.BackgroundExecutor; -import com.avaje.ebean.QueryIterator; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.BeanCollectionTouched; -import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebeaninternal.api.BeanIdList; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; -import com.avaje.ebeaninternal.server.jmx.MAdminLogging; -import com.avaje.ebeaninternal.server.persist.Binder; - -/** - * Handles the Object Relational fetching. - */ -public class CQueryEngine { - - private static final Logger logger = Logger.getLogger(CQueryEngine.class.getName()); - - private final CQueryBuilder queryBuilder; - - private final MAdminLogging logControl; - - private final BackgroundExecutor backgroundExecutor; - - private final int defaultSecondaryQueryBatchSize = 100; - - public CQueryEngine(DatabasePlatform dbPlatform, MAdminLogging logControl, Binder binder, BackgroundExecutor backgroundExecutor) { - - this.logControl = logControl; - this.backgroundExecutor = backgroundExecutor; - this.queryBuilder = new CQueryBuilder(backgroundExecutor, dbPlatform, binder); - } - - public CQuery buildQuery(OrmQueryRequest request) { - return queryBuilder.buildQuery(request); - } - - /** - * Build and execute the find Id's query. - */ - public BeanIdList findIds(OrmQueryRequest request) { - - CQueryFetchIds rcQuery = queryBuilder.buildFetchIdsQuery(request); - try { - - String sql = rcQuery.getGeneratedSql(); - sql = sql.replace(Constants.NEW_LINE, ' '); - - if (logControl.isDebugGeneratedSql()) { - System.out.println(sql); - } - request.logSql(sql); - - BeanIdList list = rcQuery.findIds(); - - if (request.isLogSummary()) { - request.getTransaction().logInternal(rcQuery.getSummary()); - } - - if (!list.isFetchingInBackground() && request.getQuery().isFutureFetch()) { - // end the transaction for futureFindIds (it had it's own one) - logger.fine("Future findIds completed!"); - request.getTransaction().end(); - } - - return list; - - } catch (SQLException e) { - throw CQuery.createPersistenceException(e, request.getTransaction(), rcQuery.getBindLog(), rcQuery.getGeneratedSql()); - } - } - - /** - * Build and execute the row count query. - */ - public int findRowCount(OrmQueryRequest request) { - - CQueryRowCount rcQuery = queryBuilder.buildRowCountQuery(request); - try { - - String sql = rcQuery.getGeneratedSql(); - sql = sql.replace(Constants.NEW_LINE, ' '); - - if (logControl.isDebugGeneratedSql()) { - System.out.println(sql); - } - request.logSql(sql); - - int rowCount = rcQuery.findRowCount(); - - if (request.isLogSummary()) { - request.getTransaction().logInternal(rcQuery.getSummary()); - } - - if (request.getQuery().isFutureFetch()) { - logger.fine("Future findRowCount completed!"); - request.getTransaction().end(); - } - - return rowCount; - - } catch (SQLException e) { - throw CQuery.createPersistenceException(e, request.getTransaction(), rcQuery.getBindLog(), rcQuery.getGeneratedSql()); - } - } - - /** - * Read many beans using an iterator (except you need to close() the iterator - * when you have finished). - */ - public QueryIterator findIterate(OrmQueryRequest request) { - - CQuery cquery = queryBuilder.buildQuery(request); - request.setCancelableQuery(cquery); - - try { - - if (logControl.isDebugGeneratedSql()) { - logSqlToConsole(cquery); - } - - if (request.isLogSql()) { - logSql(cquery); - } - - if (!cquery.prepareBindExecuteQuery()) { - // query has been cancelled already - logger.finest("Future fetch already cancelled"); - return null; - } - - int iterateBufferSize = request.getSecondaryQueriesMinBatchSize(defaultSecondaryQueryBatchSize); - - QueryIterator readIterate = cquery.readIterate(iterateBufferSize, request); - - if (request.isLogSummary()) { - logFindManySummary(cquery); - } - - return readIterate; - - } catch (SQLException e) { - throw cquery.createPersistenceException(e); - } - } - - /** - * Find a list/map/set of beans. - */ - public BeanCollection findMany(OrmQueryRequest request) { - - // flag indicating whether we need to close the resources... - boolean useBackgroundToContinueFetch = false; - - CQuery cquery = queryBuilder.buildQuery(request); - request.setCancelableQuery(cquery); - - try { - - if (logControl.isDebugGeneratedSql()) { - logSqlToConsole(cquery); - } - if (request.isLogSql()) { - logSql(cquery); - } - - if (!cquery.prepareBindExecuteQuery()) { - // query has been cancelled already - logger.finest("Future fetch already cancelled"); - return null; - } - - BeanCollection beanCollection = cquery.readCollection(); - - BeanCollectionTouched collectionTouched = request.getQuery().getBeanCollectionTouched(); - if (collectionTouched != null) { - // register a listener that wants to be notified when the - // bean collection is first used - beanCollection.setBeanCollectionTouched(collectionTouched); - } - - if (cquery.useBackgroundToContinueFetch()) { - // stop the request from putting connection back into pool - // before background fetching is finished. - request.setBackgroundFetching(); - useBackgroundToContinueFetch = true; - BackgroundFetch fetch = new BackgroundFetch(cquery); - - FutureTask future = new FutureTask(fetch); - beanCollection.setBackgroundFetch(future); - backgroundExecutor.execute(future); - } - - if (request.isLogSummary()) { - logFindManySummary(cquery); - } - - request.executeSecondaryQueries(defaultSecondaryQueryBatchSize); - - return beanCollection; - - } catch (SQLException e) { - throw cquery.createPersistenceException(e);// request, e, - // cquery.getBindLog(), - // cquery.getGeneratedSql()); - - } finally { - if (useBackgroundToContinueFetch) { - // left closing resources to BackgroundFetch... - } else { - if (cquery != null) { - cquery.close(); - } - if (request.getQuery().isFutureFetch()) { - // end the transaction for futureFindIds - // as it had it's own transaction - logger.fine("Future fetch completed!"); - request.getTransaction().end(); - } - } - } - } - - /** - * Find and return a single bean using its unique id. - */ - public T find(OrmQueryRequest request) { - - T bean = null; - - CQuery cquery = queryBuilder.buildQuery(request); - - try { - if (logControl.isDebugGeneratedSql()) { - logSqlToConsole(cquery); - } - if (request.isLogSql()) { - logSql(cquery); - } - - cquery.prepareBindExecuteQuery(); - - if (cquery.readBean()) { - bean = cquery.getLoadedBean(); - } - - if (request.isLogSummary()) { - logFindBeanSummary(cquery); - } - - request.executeSecondaryQueries(defaultSecondaryQueryBatchSize); - - return bean; - - } catch (SQLException e) { - throw cquery.createPersistenceException(e); - - } finally { - cquery.close(); - } - } - - /** - * Log the generated SQL to the console. - */ - private void logSqlToConsole(CQuery cquery) { - - SpiQuery query = cquery.getQueryRequest().getQuery(); - String loadMode = query.getLoadMode(); - String loadDesc = query.getLoadDescription(); - - String sql = cquery.getGeneratedSql(); - String summary = cquery.getSummary(); - - StringBuilder sb = new StringBuilder(1000); - sb.append(""); - sb.append(Constants.NEW_LINE); - sb.append(sql); - sb.append(Constants.NEW_LINE).append(""); - - System.out.println(sb.toString()); - } - - /** - * Log the generated SQL to the transaction log. - */ - private void logSql(CQuery query) { - - String sql = query.getGeneratedSql(); - sql = sql.replace(Constants.NEW_LINE, ' '); - query.getTransaction().logInternal(sql); - } - - /** - * Log the FindById summary to the transaction log. - */ - private void logFindBeanSummary(CQuery q) { - - SpiQuery query = q.getQueryRequest().getQuery(); - String loadMode = query.getLoadMode(); - String loadDesc = query.getLoadDescription(); - String lazyLoadProp = query.getLazyLoadProperty(); - ObjectGraphNode node = query.getParentNode(); - String originKey; - if (node == null || node.getOriginQueryPoint() == null) { - originKey = null; - } else { - originKey = node.getOriginQueryPoint().getKey(); - } - - StringBuilder msg = new StringBuilder(200); - msg.append("FindBean "); - if (loadMode != null) { - msg.append("mode[").append(loadMode).append("] "); - } - msg.append("type[").append(q.getBeanName()).append("] "); - if (query.isAutofetchTuned()) { - msg.append("tuned[true] "); - } - if (originKey != null) { - msg.append("origin[").append(originKey).append("] "); - } - if (lazyLoadProp != null) { - msg.append("lazyLoadProp[").append(lazyLoadProp).append("] "); - } - if (loadDesc != null) { - msg.append("load[").append(loadDesc).append("] "); - } - msg.append("exeMicros[").append(q.getQueryExecutionTimeMicros()); - msg.append("] rows[").append(q.getLoadedRowDetail()); - msg.append("] bind[").append(q.getBindLog()).append("]"); - - q.getTransaction().logInternal(msg.toString()); - } - - /** - * Log the FindMany to the transaction log. - */ - private void logFindManySummary(CQuery q) { - - SpiQuery query = q.getQueryRequest().getQuery(); - String loadMode = query.getLoadMode(); - String loadDesc = query.getLoadDescription(); - String lazyLoadProp = query.getLazyLoadProperty(); - ObjectGraphNode node = query.getParentNode(); - - String originKey; - if (node == null || node.getOriginQueryPoint() == null) { - originKey = null; - } else { - originKey = node.getOriginQueryPoint().getKey(); - } - - StringBuilder msg = new StringBuilder(200); - msg.append("FindMany "); - if (loadMode != null) { - msg.append("mode[").append(loadMode).append("] "); - } - msg.append("type[").append(q.getBeanName()).append("] "); - if (query.isAutofetchTuned()) { - msg.append("tuned[true] "); - } - if (originKey != null) { - msg.append("origin[").append(originKey).append("] "); - } - if (lazyLoadProp != null) { - msg.append("lazyLoadProp[").append(lazyLoadProp).append("] "); - } - if (loadDesc != null) { - msg.append("load[").append(loadDesc).append("] "); - } - msg.append("exeMicros[").append(q.getQueryExecutionTimeMicros()); - msg.append("] rows[").append(q.getLoadedRowDetail()); - msg.append("] name[").append(q.getName()); - msg.append("] predicates[").append(q.getLogWhereSql()); - msg.append("] bind[").append(q.getBindLog()).append("]"); - - q.getTransaction().logInternal(msg.toString()); - } -} +package com.avaje.ebeaninternal.server.query; + +import java.sql.SQLException; +import java.util.concurrent.FutureTask; +import java.util.logging.Logger; + +import com.avaje.ebean.BackgroundExecutor; +import com.avaje.ebean.QueryIterator; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.BeanCollectionTouched; +import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebeaninternal.api.BeanIdList; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +import com.avaje.ebeaninternal.server.jmx.MAdminLogging; +import com.avaje.ebeaninternal.server.persist.Binder; + +/** + * Handles the Object Relational fetching. + */ +public class CQueryEngine { + + private static final Logger logger = Logger.getLogger(CQueryEngine.class.getName()); + + private final CQueryBuilder queryBuilder; + + private final MAdminLogging logControl; + + private final BackgroundExecutor backgroundExecutor; + + private final int defaultSecondaryQueryBatchSize = 100; + + public CQueryEngine(DatabasePlatform dbPlatform, MAdminLogging logControl, Binder binder, BackgroundExecutor backgroundExecutor) { + + this.logControl = logControl; + this.backgroundExecutor = backgroundExecutor; + this.queryBuilder = new CQueryBuilder(backgroundExecutor, dbPlatform, binder); + } + + public CQuery buildQuery(OrmQueryRequest request) { + return queryBuilder.buildQuery(request); + } + + /** + * Build and execute the find Id's query. + */ + public BeanIdList findIds(OrmQueryRequest request) { + + CQueryFetchIds rcQuery = queryBuilder.buildFetchIdsQuery(request); + try { + + String sql = rcQuery.getGeneratedSql(); + sql = sql.replace(Constants.NEW_LINE, ' '); + + if (logControl.isDebugGeneratedSql()) { + System.out.println(sql); + } + request.logSql(sql); + + BeanIdList list = rcQuery.findIds(); + + if (request.isLogSummary()) { + request.getTransaction().logInternal(rcQuery.getSummary()); + } + + if (!list.isFetchingInBackground() && request.getQuery().isFutureFetch()) { + // end the transaction for futureFindIds (it had it's own one) + logger.fine("Future findIds completed!"); + request.getTransaction().end(); + } + + return list; + + } catch (SQLException e) { + throw CQuery.createPersistenceException(e, request.getTransaction(), rcQuery.getBindLog(), rcQuery.getGeneratedSql()); + } + } + + /** + * Build and execute the row count query. + */ + public int findRowCount(OrmQueryRequest request) { + + CQueryRowCount rcQuery = queryBuilder.buildRowCountQuery(request); + try { + + String sql = rcQuery.getGeneratedSql(); + sql = sql.replace(Constants.NEW_LINE, ' '); + + if (logControl.isDebugGeneratedSql()) { + System.out.println(sql); + } + request.logSql(sql); + + int rowCount = rcQuery.findRowCount(); + + if (request.isLogSummary()) { + request.getTransaction().logInternal(rcQuery.getSummary()); + } + + if (request.getQuery().isFutureFetch()) { + logger.fine("Future findRowCount completed!"); + request.getTransaction().end(); + } + + return rowCount; + + } catch (SQLException e) { + throw CQuery.createPersistenceException(e, request.getTransaction(), rcQuery.getBindLog(), rcQuery.getGeneratedSql()); + } + } + + /** + * Read many beans using an iterator (except you need to close() the iterator + * when you have finished). + */ + public QueryIterator findIterate(OrmQueryRequest request) { + + CQuery cquery = queryBuilder.buildQuery(request); + request.setCancelableQuery(cquery); + + try { + + if (logControl.isDebugGeneratedSql()) { + logSqlToConsole(cquery); + } + + if (request.isLogSql()) { + logSql(cquery); + } + + if (!cquery.prepareBindExecuteQuery()) { + // query has been cancelled already + logger.finest("Future fetch already cancelled"); + return null; + } + + int iterateBufferSize = request.getSecondaryQueriesMinBatchSize(defaultSecondaryQueryBatchSize); + + QueryIterator readIterate = cquery.readIterate(iterateBufferSize, request); + + if (request.isLogSummary()) { + logFindManySummary(cquery); + } + + return readIterate; + + } catch (SQLException e) { + throw cquery.createPersistenceException(e); + } + } + + /** + * Find a list/map/set of beans. + */ + public BeanCollection findMany(OrmQueryRequest request) { + + // flag indicating whether we need to close the resources... + boolean useBackgroundToContinueFetch = false; + + CQuery cquery = queryBuilder.buildQuery(request); + request.setCancelableQuery(cquery); + + try { + + if (logControl.isDebugGeneratedSql()) { + logSqlToConsole(cquery); + } + if (request.isLogSql()) { + logSql(cquery); + } + + if (!cquery.prepareBindExecuteQuery()) { + // query has been cancelled already + logger.finest("Future fetch already cancelled"); + return null; + } + + BeanCollection beanCollection = cquery.readCollection(); + + BeanCollectionTouched collectionTouched = request.getQuery().getBeanCollectionTouched(); + if (collectionTouched != null) { + // register a listener that wants to be notified when the + // bean collection is first used + beanCollection.setBeanCollectionTouched(collectionTouched); + } + + if (cquery.useBackgroundToContinueFetch()) { + // stop the request from putting connection back into pool + // before background fetching is finished. + request.setBackgroundFetching(); + useBackgroundToContinueFetch = true; + BackgroundFetch fetch = new BackgroundFetch(cquery); + + FutureTask future = new FutureTask(fetch); + beanCollection.setBackgroundFetch(future); + backgroundExecutor.execute(future); + } + + if (request.isLogSummary()) { + logFindManySummary(cquery); + } + + request.executeSecondaryQueries(defaultSecondaryQueryBatchSize); + + return beanCollection; + + } catch (SQLException e) { + throw cquery.createPersistenceException(e);// request, e, + // cquery.getBindLog(), + // cquery.getGeneratedSql()); + + } finally { + if (useBackgroundToContinueFetch) { + // left closing resources to BackgroundFetch... + } else { + if (cquery != null) { + cquery.close(); + } + if (request.getQuery().isFutureFetch()) { + // end the transaction for futureFindIds + // as it had it's own transaction + logger.fine("Future fetch completed!"); + request.getTransaction().end(); + } + } + } + } + + /** + * Find and return a single bean using its unique id. + */ + public T find(OrmQueryRequest request) { + + T bean = null; + + CQuery cquery = queryBuilder.buildQuery(request); + + try { + if (logControl.isDebugGeneratedSql()) { + logSqlToConsole(cquery); + } + if (request.isLogSql()) { + logSql(cquery); + } + + cquery.prepareBindExecuteQuery(); + + if (cquery.readBean()) { + bean = cquery.getLoadedBean(); + } + + if (request.isLogSummary()) { + logFindBeanSummary(cquery); + } + + request.executeSecondaryQueries(defaultSecondaryQueryBatchSize); + + return bean; + + } catch (SQLException e) { + throw cquery.createPersistenceException(e); + + } finally { + cquery.close(); + } + } + + /** + * Log the generated SQL to the console. + */ + private void logSqlToConsole(CQuery cquery) { + + SpiQuery query = cquery.getQueryRequest().getQuery(); + String loadMode = query.getLoadMode(); + String loadDesc = query.getLoadDescription(); + + String sql = cquery.getGeneratedSql(); + String summary = cquery.getSummary(); + + StringBuilder sb = new StringBuilder(1000); + sb.append(""); + sb.append(Constants.NEW_LINE); + sb.append(sql); + sb.append(Constants.NEW_LINE).append(""); + + System.out.println(sb.toString()); + } + + /** + * Log the generated SQL to the transaction log. + */ + private void logSql(CQuery query) { + + String sql = query.getGeneratedSql(); + sql = sql.replace(Constants.NEW_LINE, ' '); + query.getTransaction().logInternal(sql); + } + + /** + * Log the FindById summary to the transaction log. + */ + private void logFindBeanSummary(CQuery q) { + + SpiQuery query = q.getQueryRequest().getQuery(); + String loadMode = query.getLoadMode(); + String loadDesc = query.getLoadDescription(); + String lazyLoadProp = query.getLazyLoadProperty(); + ObjectGraphNode node = query.getParentNode(); + String originKey; + if (node == null || node.getOriginQueryPoint() == null) { + originKey = null; + } else { + originKey = node.getOriginQueryPoint().getKey(); + } + + StringBuilder msg = new StringBuilder(200); + msg.append("FindBean "); + if (loadMode != null) { + msg.append("mode[").append(loadMode).append("] "); + } + msg.append("type[").append(q.getBeanName()).append("] "); + if (query.isAutofetchTuned()) { + msg.append("tuned[true] "); + } + if (originKey != null) { + msg.append("origin[").append(originKey).append("] "); + } + if (lazyLoadProp != null) { + msg.append("lazyLoadProp[").append(lazyLoadProp).append("] "); + } + if (loadDesc != null) { + msg.append("load[").append(loadDesc).append("] "); + } + msg.append("exeMicros[").append(q.getQueryExecutionTimeMicros()); + msg.append("] rows[").append(q.getLoadedRowDetail()); + msg.append("] bind[").append(q.getBindLog()).append("]"); + + q.getTransaction().logInternal(msg.toString()); + } + + /** + * Log the FindMany to the transaction log. + */ + private void logFindManySummary(CQuery q) { + + SpiQuery query = q.getQueryRequest().getQuery(); + String loadMode = query.getLoadMode(); + String loadDesc = query.getLoadDescription(); + String lazyLoadProp = query.getLazyLoadProperty(); + ObjectGraphNode node = query.getParentNode(); + + String originKey; + if (node == null || node.getOriginQueryPoint() == null) { + originKey = null; + } else { + originKey = node.getOriginQueryPoint().getKey(); + } + + StringBuilder msg = new StringBuilder(200); + msg.append("FindMany "); + if (loadMode != null) { + msg.append("mode[").append(loadMode).append("] "); + } + msg.append("type[").append(q.getBeanName()).append("] "); + if (query.isAutofetchTuned()) { + msg.append("tuned[true] "); + } + if (originKey != null) { + msg.append("origin[").append(originKey).append("] "); + } + if (lazyLoadProp != null) { + msg.append("lazyLoadProp[").append(lazyLoadProp).append("] "); + } + if (loadDesc != null) { + msg.append("load[").append(loadDesc).append("] "); + } + msg.append("exeMicros[").append(q.getQueryExecutionTimeMicros()); + msg.append("] rows[").append(q.getLoadedRowDetail()); + msg.append("] name[").append(q.getName()); + msg.append("] predicates[").append(q.getLogWhereSql()); + msg.append("] bind[").append(q.getBindLog()).append("]"); + + q.getTransaction().logInternal(msg.toString()); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java index fd4278d1d..7b06d792f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryFetchIds.java @@ -1,335 +1,316 @@ -/** - * 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.query; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.FutureTask; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebean.BackgroundExecutor; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.api.BeanIdList; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.api.SpiQuery.Mode; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; -import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.DbReadContext; -import com.avaje.ebeaninternal.server.type.DataBind; -import com.avaje.ebeaninternal.server.type.DataReader; -import com.avaje.ebeaninternal.server.type.RsetDataReader; - -/** - * Executes the select row count query. - */ -public class CQueryFetchIds { - - private static final Logger logger = Logger.getLogger(CQueryFetchIds.class.getName()); - - /** - * The overall find request wrapper object. - */ - private final OrmQueryRequest request; - - private final BeanDescriptor desc; - - private final SpiQuery query; - - private final BackgroundExecutor backgroundExecutor; - - /** - * Where clause predicates. - */ - private final CQueryPredicates predicates; - - /** - * The final sql that is generated. - */ - private final String sql; - - private RsetDataReader dataReader; - - /** - * The statement used to create the resultSet. - */ - private PreparedStatement pstmt; - - private String bindLog; - - private long startNano; - - private int executionTimeMicros; - - private int rowCount; - - private final int maxRows; - private final int bgFetchAfter; - - /** - * Create the Sql select based on the request. - */ - public CQueryFetchIds(OrmQueryRequest request, CQueryPredicates predicates, - String sql, BackgroundExecutor backgroundExecutor) { - - this.backgroundExecutor = backgroundExecutor; - this.request = request; - this.query = request.getQuery(); - this.sql = sql; - this.maxRows = query.getMaxRows(); - this.bgFetchAfter = query.getBackgroundFetchAfter(); - - query.setGeneratedSql(sql); - - this.desc = request.getBeanDescriptor(); - this.predicates = predicates; - - } - - /** - * Return a summary description of this query. - */ - public String getSummary() { - StringBuilder sb = new StringBuilder(); - sb.append("FindIds exeMicros[").append(executionTimeMicros) - .append("] rows[").append(rowCount) - .append("] type[").append(desc.getName()) - .append("] predicates[").append(predicates.getLogWhereSql()) - .append("] bind[").append(bindLog).append("]"); - - return sb.toString(); - } - - /** - * Return the bind log. - */ - public String getBindLog() { - return bindLog; - } - - /** - * Return the generated sql. - */ - public String getGeneratedSql() { - return sql; - } - - public SpiOrmQueryRequest getQueryRequest() { - return request; - } - - /** - * Execute the query returning the row count. - */ - public BeanIdList findIds() throws SQLException { - - boolean useBackgroundToContinueFetch = false; - - startNano = System.nanoTime(); - - try { - // get the list that we are going to put the id's into. - // This was already set so that it is available to be - // read by other threads (it is a synchronised list) - List idList = query.getIdList(); - if (idList == null){ - // running in foreground thread (not FutureIds query) - idList = Collections.synchronizedList(new ArrayList()); - query.setIdList(idList); - } - - BeanIdList result = new BeanIdList(idList); - - SpiTransaction t = request.getTransaction(); - Connection conn = t.getInternalConnection(); - pstmt = conn.prepareStatement(sql); - - if (query.getBufferFetchSizeHint() > 0){ - pstmt.setFetchSize(query.getBufferFetchSizeHint()); - } - - if (query.getTimeout() > 0){ - pstmt.setQueryTimeout(query.getTimeout()); - } - - bindLog = predicates.bind(new DataBind(pstmt)); - - ResultSet rset = pstmt.executeQuery(); - dataReader = new RsetDataReader(rset); - - boolean hitMaxRows = false; - boolean hasMoreRows = false; - rowCount = 0; - - DbReadContext ctx = new DbContext(); - - while (rset.next()){ - Object idValue = desc.getIdBinder().read(ctx); - idList.add(idValue); - // reset back to 0 - dataReader.resetColumnPosition(); - rowCount++; - - if (maxRows > 0 && rowCount == maxRows) { - hitMaxRows = true; - hasMoreRows = rset.next(); - break; - - } else if (bgFetchAfter > 0 && rowCount >= bgFetchAfter) { - useBackgroundToContinueFetch = true; - break; - } - } - - if (hitMaxRows){ - result.setHasMore(hasMoreRows); - } - - if (useBackgroundToContinueFetch){ - // tell the request not to end the transaction - // as we leave that up to the BackgroundIdFetch - request.setBackgroundFetching(); - - // submit background future task - BackgroundIdFetch bgFetch = new BackgroundIdFetch(t, rset, pstmt, ctx, desc, result); - FutureTask future = new FutureTask(bgFetch); - backgroundExecutor.execute(future); - - // set on result so we can use the futureTask to wait - result.setBackgroundFetch(future); - } - - long exeNano = System.nanoTime() - startNano; - executionTimeMicros = (int)exeNano/1000; - - return result; - - } finally { - if (useBackgroundToContinueFetch) { - // left closing resources to BackgroundFetch... - } else { - close(); - } - } - } - - /** - * Close the resources. - *

      - * The jdbc resultSet and statement need to be closed. Its important that - * this method is called. - *

      - */ - private void close() { - try { - if (dataReader != null) { - dataReader.close(); - dataReader = null; - } - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - try { - if (pstmt != null) { - pstmt.close(); - pstmt = null; - } - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - } - - - class DbContext implements DbReadContext { - - public void propagateState(Object e) { - throw new RuntimeException("Not Called"); - } - - public Mode getQueryMode() { - return Mode.NORMAL; - } - - public DataReader getDataReader() { - return dataReader; - } - - public boolean isVanillaMode() { - return false; - } - - public Boolean isReadOnly() { - return Boolean.FALSE; - } - - public boolean isRawSql() { - return false; - } - - public void register(String path, EntityBeanIntercept ebi){ - } - - public void register(String path, BeanCollection bc){ - } - - public BeanPropertyAssocMany getManyProperty() { - // always null - return null; - } - - public PersistenceContext getPersistenceContext() { - // always null - return null; - } - - public boolean isAutoFetchProfiling() { - return false; - } - - public void profileBean(EntityBeanIntercept ebi, String prefix) { - // no-op - } - - public void setCurrentPrefix(String currentPrefix,Map pathMap) { - // no-op - } - - public void setLoadedBean(Object loadedBean, Object id) { - // no-op - } - - public void setLoadedManyBean(Object loadedBean) { - // no-op - } - - } - -} +package com.avaje.ebeaninternal.server.query; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.FutureTask; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebean.BackgroundExecutor; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebeaninternal.api.BeanIdList; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.api.SpiQuery.Mode; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.DbReadContext; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.type.DataReader; +import com.avaje.ebeaninternal.server.type.RsetDataReader; + +/** + * Executes the select row count query. + */ +public class CQueryFetchIds { + + private static final Logger logger = Logger.getLogger(CQueryFetchIds.class.getName()); + + /** + * The overall find request wrapper object. + */ + private final OrmQueryRequest request; + + private final BeanDescriptor desc; + + private final SpiQuery query; + + private final BackgroundExecutor backgroundExecutor; + + /** + * Where clause predicates. + */ + private final CQueryPredicates predicates; + + /** + * The final sql that is generated. + */ + private final String sql; + + private RsetDataReader dataReader; + + /** + * The statement used to create the resultSet. + */ + private PreparedStatement pstmt; + + private String bindLog; + + private long startNano; + + private int executionTimeMicros; + + private int rowCount; + + private final int maxRows; + private final int bgFetchAfter; + + /** + * Create the Sql select based on the request. + */ + public CQueryFetchIds(OrmQueryRequest request, CQueryPredicates predicates, + String sql, BackgroundExecutor backgroundExecutor) { + + this.backgroundExecutor = backgroundExecutor; + this.request = request; + this.query = request.getQuery(); + this.sql = sql; + this.maxRows = query.getMaxRows(); + this.bgFetchAfter = query.getBackgroundFetchAfter(); + + query.setGeneratedSql(sql); + + this.desc = request.getBeanDescriptor(); + this.predicates = predicates; + + } + + /** + * Return a summary description of this query. + */ + public String getSummary() { + StringBuilder sb = new StringBuilder(); + sb.append("FindIds exeMicros[").append(executionTimeMicros) + .append("] rows[").append(rowCount) + .append("] type[").append(desc.getName()) + .append("] predicates[").append(predicates.getLogWhereSql()) + .append("] bind[").append(bindLog).append("]"); + + return sb.toString(); + } + + /** + * Return the bind log. + */ + public String getBindLog() { + return bindLog; + } + + /** + * Return the generated sql. + */ + public String getGeneratedSql() { + return sql; + } + + public SpiOrmQueryRequest getQueryRequest() { + return request; + } + + /** + * Execute the query returning the row count. + */ + public BeanIdList findIds() throws SQLException { + + boolean useBackgroundToContinueFetch = false; + + startNano = System.nanoTime(); + + try { + // get the list that we are going to put the id's into. + // This was already set so that it is available to be + // read by other threads (it is a synchronised list) + List idList = query.getIdList(); + if (idList == null){ + // running in foreground thread (not FutureIds query) + idList = Collections.synchronizedList(new ArrayList()); + query.setIdList(idList); + } + + BeanIdList result = new BeanIdList(idList); + + SpiTransaction t = request.getTransaction(); + Connection conn = t.getInternalConnection(); + pstmt = conn.prepareStatement(sql); + + if (query.getBufferFetchSizeHint() > 0){ + pstmt.setFetchSize(query.getBufferFetchSizeHint()); + } + + if (query.getTimeout() > 0){ + pstmt.setQueryTimeout(query.getTimeout()); + } + + bindLog = predicates.bind(new DataBind(pstmt)); + + ResultSet rset = pstmt.executeQuery(); + dataReader = new RsetDataReader(rset); + + boolean hitMaxRows = false; + boolean hasMoreRows = false; + rowCount = 0; + + DbReadContext ctx = new DbContext(); + + while (rset.next()){ + Object idValue = desc.getIdBinder().read(ctx); + idList.add(idValue); + // reset back to 0 + dataReader.resetColumnPosition(); + rowCount++; + + if (maxRows > 0 && rowCount == maxRows) { + hitMaxRows = true; + hasMoreRows = rset.next(); + break; + + } else if (bgFetchAfter > 0 && rowCount >= bgFetchAfter) { + useBackgroundToContinueFetch = true; + break; + } + } + + if (hitMaxRows){ + result.setHasMore(hasMoreRows); + } + + if (useBackgroundToContinueFetch){ + // tell the request not to end the transaction + // as we leave that up to the BackgroundIdFetch + request.setBackgroundFetching(); + + // submit background future task + BackgroundIdFetch bgFetch = new BackgroundIdFetch(t, rset, pstmt, ctx, desc, result); + FutureTask future = new FutureTask(bgFetch); + backgroundExecutor.execute(future); + + // set on result so we can use the futureTask to wait + result.setBackgroundFetch(future); + } + + long exeNano = System.nanoTime() - startNano; + executionTimeMicros = (int)exeNano/1000; + + return result; + + } finally { + if (useBackgroundToContinueFetch) { + // left closing resources to BackgroundFetch... + } else { + close(); + } + } + } + + /** + * Close the resources. + *

      + * The jdbc resultSet and statement need to be closed. Its important that + * this method is called. + *

      + */ + private void close() { + try { + if (dataReader != null) { + dataReader.close(); + dataReader = null; + } + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + try { + if (pstmt != null) { + pstmt.close(); + pstmt = null; + } + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + } + + + class DbContext implements DbReadContext { + + public void propagateState(Object e) { + throw new RuntimeException("Not Called"); + } + + public Mode getQueryMode() { + return Mode.NORMAL; + } + + public DataReader getDataReader() { + return dataReader; + } + + public boolean isVanillaMode() { + return false; + } + + public Boolean isReadOnly() { + return Boolean.FALSE; + } + + public boolean isRawSql() { + return false; + } + + public void register(String path, EntityBeanIntercept ebi){ + } + + public void register(String path, BeanCollection bc){ + } + + public BeanPropertyAssocMany getManyProperty() { + // always null + return null; + } + + public PersistenceContext getPersistenceContext() { + // always null + return null; + } + + public boolean isAutoFetchProfiling() { + return false; + } + + public void profileBean(EntityBeanIntercept ebi, String prefix) { + // no-op + } + + public void setCurrentPrefix(String currentPrefix,Map pathMap) { + // no-op + } + + public void setLoadedBean(Object loadedBean, Object id) { + // no-op + } + + public void setLoadedManyBean(Object loadedBean) { + // no-op + } + + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorSimple.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorSimple.java index f34d8a6b2..ab2f3e2eb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorSimple.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorSimple.java @@ -1,68 +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.query; - -import java.sql.SQLException; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.QueryIterator; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; - -/** - * QueryIterator that does not require a buffer for secondary queries. - * - * @author rbygrave - */ -class CQueryIteratorSimple implements QueryIterator { - - private final CQuery cquery; - private final OrmQueryRequest request; - - CQueryIteratorSimple(CQuery cquery, OrmQueryRequest request){ - this.cquery = cquery; - this.request = request; - } - - public boolean hasNext() { - try { - return cquery.hasNextBean(true); - } catch (SQLException e){ - throw cquery.createPersistenceException(e); - } - } - - public T next() { - return cquery.getLoadedBean(); - } - - public void close() { - cquery.updateExecutionStatistics(); - cquery.close(); - request.endTransIfRequired(); - } - - public void remove() { - throw new PersistenceException("Remove not allowed"); - } +package com.avaje.ebeaninternal.server.query; + +import java.sql.SQLException; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.QueryIterator; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; + +/** + * QueryIterator that does not require a buffer for secondary queries. + * + * @author rbygrave + */ +class CQueryIteratorSimple implements QueryIterator { + + private final CQuery cquery; + private final OrmQueryRequest request; + + CQueryIteratorSimple(CQuery cquery, OrmQueryRequest request){ + this.cquery = cquery; + this.request = request; + } + + public boolean hasNext() { + try { + return cquery.hasNextBean(true); + } catch (SQLException e){ + throw cquery.createPersistenceException(e); + } + } + + public T next() { + return cquery.getLoadedBean(); + } + + public void close() { + cquery.updateExecutionStatistics(); + cquery.close(); + request.endTransIfRequired(); + } + + public void remove() { + throw new PersistenceException("Remove not allowed"); + } } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java index 4c68e368a..2df0fcd14 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryIteratorWithBuffer.java @@ -1,89 +1,67 @@ -/** - * 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.query; - -import java.sql.SQLException; -import java.util.ArrayList; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.QueryIterator; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; - -/** - * A QueryIterator that uses a buffer to execute secondary queries periodically. - * - * @author rbygrave - */ -class CQueryIteratorWithBuffer implements QueryIterator { - - private final CQuery cquery; - private final int bufferSize; - private final OrmQueryRequest request; - private final ArrayList buffer; - - private boolean moreToLoad = true; - - CQueryIteratorWithBuffer(CQuery cquery, OrmQueryRequest request, int bufferSize) { - this.cquery = cquery; - this.request = request; - this.bufferSize = bufferSize; - this.buffer = new ArrayList(bufferSize); - } - - public boolean hasNext() { - try { - if (buffer.isEmpty() && moreToLoad) { - // load buffer - int i = -1; - while (moreToLoad && ++i < bufferSize) { - if (cquery.hasNextBean(true)) { - buffer.add(cquery.getLoadedBean()); - } else { - moreToLoad = false; - } - } - // execute secondary queries - request.executeSecondaryQueries(bufferSize); - } - return !buffer.isEmpty(); - - } catch (SQLException e) { - throw cquery.createPersistenceException(e); - } - } - - public T next() { - return buffer.remove(0); - } - - public void close() { - cquery.updateExecutionStatistics(); - cquery.close(); - request.endTransIfRequired(); - } - - public void remove() { - throw new PersistenceException("Remove not allowed"); - } +package com.avaje.ebeaninternal.server.query; + +import java.sql.SQLException; +import java.util.ArrayList; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.QueryIterator; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; + +/** + * A QueryIterator that uses a buffer to execute secondary queries periodically. + * + * @author rbygrave + */ +class CQueryIteratorWithBuffer implements QueryIterator { + + private final CQuery cquery; + private final int bufferSize; + private final OrmQueryRequest request; + private final ArrayList buffer; + + private boolean moreToLoad = true; + + CQueryIteratorWithBuffer(CQuery cquery, OrmQueryRequest request, int bufferSize) { + this.cquery = cquery; + this.request = request; + this.bufferSize = bufferSize; + this.buffer = new ArrayList(bufferSize); + } + + public boolean hasNext() { + try { + if (buffer.isEmpty() && moreToLoad) { + // load buffer + int i = -1; + while (moreToLoad && ++i < bufferSize) { + if (cquery.hasNextBean(true)) { + buffer.add(cquery.getLoadedBean()); + } else { + moreToLoad = false; + } + } + // execute secondary queries + request.executeSecondaryQueries(bufferSize); + } + return !buffer.isEmpty(); + + } catch (SQLException e) { + throw cquery.createPersistenceException(e); + } + } + + public T next() { + return buffer.remove(0); + } + + public void close() { + cquery.updateExecutionStatistics(); + cquery.close(); + request.endTransIfRequired(); + } + + public void remove() { + throw new PersistenceException("Remove not allowed"); + } } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryOrderBy.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryOrderBy.java index 44881c4aa..aa985670c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryOrderBy.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryOrderBy.java @@ -1,96 +1,77 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.query; - -import java.util.List; - -import com.avaje.ebean.OrderBy; -import com.avaje.ebean.OrderBy.Property; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc; -import com.avaje.ebeaninternal.server.deploy.id.IdBinder; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; - -/** - * Creates the order by expression clause. - */ -public class CQueryOrderBy { - - private final BeanDescriptor desc; - - private final SpiQuery query; - - /** - * Create the logical order by clause. - */ - public static String parse(BeanDescriptor desc, SpiQuery query) { - return new CQueryOrderBy(desc, query).parseInternal(); - } - - private CQueryOrderBy(BeanDescriptor desc, SpiQuery query) { - this.desc = desc; - this.query = query; - } - - private String parseInternal() { - - OrderBy orderBy = query.getOrderBy(); - if (orderBy == null){ - return null; - } - - StringBuilder sb = new StringBuilder(); - - List properties = orderBy.getProperties(); - if (properties.isEmpty()){ - // order by clause removed by filterMany() - return null; - } - for (int i = 0; i < properties.size(); i++) { - if (i > 0){ - sb.append(", "); - } - Property p = properties.get(i); - String expression = parseProperty(p); - sb.append(expression); - } - return sb.toString(); - } - - private String parseProperty(Property p) { - - String propName = p.getProperty(); - ElPropertyValue el = desc.getElGetValue(propName); - if (el == null){ - return p.toStringFormat(); - } - - BeanProperty beanProperty = el.getBeanProperty(); - if (beanProperty instanceof BeanPropertyAssoc){ - BeanPropertyAssoc ap = (BeanPropertyAssoc)beanProperty; - IdBinder idBinder = ap.getTargetDescriptor().getIdBinder(); - return idBinder.getOrderBy(el.getElName(), p.isAscending()); - } - - return p.toStringFormat(); - } -} +package com.avaje.ebeaninternal.server.query; + +import java.util.List; + +import com.avaje.ebean.OrderBy; +import com.avaje.ebean.OrderBy.Property; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc; +import com.avaje.ebeaninternal.server.deploy.id.IdBinder; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; + +/** + * Creates the order by expression clause. + */ +public class CQueryOrderBy { + + private final BeanDescriptor desc; + + private final SpiQuery query; + + /** + * Create the logical order by clause. + */ + public static String parse(BeanDescriptor desc, SpiQuery query) { + return new CQueryOrderBy(desc, query).parseInternal(); + } + + private CQueryOrderBy(BeanDescriptor desc, SpiQuery query) { + this.desc = desc; + this.query = query; + } + + private String parseInternal() { + + OrderBy orderBy = query.getOrderBy(); + if (orderBy == null){ + return null; + } + + StringBuilder sb = new StringBuilder(); + + List properties = orderBy.getProperties(); + if (properties.isEmpty()){ + // order by clause removed by filterMany() + return null; + } + for (int i = 0; i < properties.size(); i++) { + if (i > 0){ + sb.append(", "); + } + Property p = properties.get(i); + String expression = parseProperty(p); + sb.append(expression); + } + return sb.toString(); + } + + private String parseProperty(Property p) { + + String propName = p.getProperty(); + ElPropertyValue el = desc.getElGetValue(propName); + if (el == null){ + return p.toStringFormat(); + } + + BeanProperty beanProperty = el.getBeanProperty(); + if (beanProperty instanceof BeanPropertyAssoc){ + BeanPropertyAssoc ap = (BeanPropertyAssoc)beanProperty; + IdBinder idBinder = ap.getTargetDescriptor().getIdBinder(); + return idBinder.getOrderBy(el.getElName(), p.isAscending()); + } + + return p.toStringFormat(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlanRawSql.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlanRawSql.java index a19e23d3a..696d0b02f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlanRawSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryPlanRawSql.java @@ -1,63 +1,44 @@ -/** - * 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.query; - -import java.sql.ResultSet; -import java.util.List; - -import com.avaje.ebean.RawSql.ColumnMapping; -import com.avaje.ebean.config.dbplatform.SqlLimitResponse; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; -import com.avaje.ebeaninternal.server.type.DataReader; -import com.avaje.ebeaninternal.server.type.RsetDataReaderIndexed; - -public class CQueryPlanRawSql extends CQueryPlan { - - private final int[] rsetIndexPositions; - - public CQueryPlanRawSql(OrmQueryRequest request, SqlLimitResponse sqlRes, SqlTree sqlTree, String logWhereSql) { - - super(request, sqlRes, sqlTree, true, logWhereSql, null); - - this.rsetIndexPositions = createIndexPositions(request, sqlTree); - } - - public DataReader createDataReader(ResultSet rset){ - - return new RsetDataReaderIndexed(rset, rsetIndexPositions, isRowNumberIncluded()); - } - - - private int[] createIndexPositions(OrmQueryRequest request, SqlTree sqlTree) { - - List chain = sqlTree.buildSelectExpressionChain(); - ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping(); - - int[] indexPositions = new int[chain.size()]; - - for (int i = 0; i < chain.size(); i++) { - String expr = chain.get(i); - int indexPos = 1 + columnMapping.getIndexPosition(expr); - indexPositions[i] = indexPos; - } - - return indexPositions; - } -} +package com.avaje.ebeaninternal.server.query; + +import java.sql.ResultSet; +import java.util.List; + +import com.avaje.ebean.RawSql.ColumnMapping; +import com.avaje.ebean.config.dbplatform.SqlLimitResponse; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +import com.avaje.ebeaninternal.server.type.DataReader; +import com.avaje.ebeaninternal.server.type.RsetDataReaderIndexed; + +public class CQueryPlanRawSql extends CQueryPlan { + + private final int[] rsetIndexPositions; + + public CQueryPlanRawSql(OrmQueryRequest request, SqlLimitResponse sqlRes, SqlTree sqlTree, String logWhereSql) { + + super(request, sqlRes, sqlTree, true, logWhereSql, null); + + this.rsetIndexPositions = createIndexPositions(request, sqlTree); + } + + public DataReader createDataReader(ResultSet rset){ + + return new RsetDataReaderIndexed(rset, rsetIndexPositions, isRowNumberIncluded()); + } + + + private int[] createIndexPositions(OrmQueryRequest request, SqlTree sqlTree) { + + List chain = sqlTree.buildSelectExpressionChain(); + ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping(); + + int[] indexPositions = new int[chain.size()]; + + for (int i = 0; i < chain.size(); i++) { + String expr = chain.get(i); + int indexPos = 1 + columnMapping.getIndexPosition(expr); + indexPositions[i] = indexPos; + } + + return indexPositions; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryRowCount.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryRowCount.java index 25e438039..bbbf1e1fa 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryRowCount.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryRowCount.java @@ -1,192 +1,173 @@ -/** - * 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.query; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; -import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.type.DataBind; - -/** - * Executes the select row count query. - */ -public class CQueryRowCount { - - private static final Logger logger = Logger.getLogger(CQueryRowCount.class.getName()); - - /** - * The overall find request wrapper object. - */ - private final OrmQueryRequest request; - - private final BeanDescriptor desc; - - private final SpiQuery query; - - /** - * Where clause predicates. - */ - private final CQueryPredicates predicates; - - /** - * The final sql that is generated. - */ - private final String sql; - - /** - * The resultSet that is read and converted to objects. - */ - private ResultSet rset; - - /** - * The statement used to create the resultSet. - */ - private PreparedStatement pstmt; - - private String bindLog; - - private long startNano; - - private int executionTimeMicros; - - private int rowCount; - - /** - * Create the Sql select based on the request. - */ - public CQueryRowCount(OrmQueryRequest request, CQueryPredicates predicates, String sql) { - this.request = request; - this.query = request.getQuery(); - this.sql = sql; - - query.setGeneratedSql(sql); - - this.desc = request.getBeanDescriptor(); - this.predicates = predicates; - - } - - /** - * Return a summary description of this query. - */ - public String getSummary() { - StringBuilder sb = new StringBuilder(); - sb.append("FindRowCount exeMicros[").append(executionTimeMicros) - .append("] rows[").append(rowCount) - .append("] type[").append(desc.getFullName()) - .append("] predicates[").append(predicates.getLogWhereSql()) - .append("] bind[").append(bindLog).append("]"); - - return sb.toString(); - } - - /** - * Return the bind log. - */ - public String getBindLog() { - return bindLog; - } - - /** - * Return the generated sql. - */ - public String getGeneratedSql() { - return sql; - } - - public SpiOrmQueryRequest getQueryRequest() { - return request; - } - - /** - * Execute the query returning the row count. - */ - public int findRowCount() throws SQLException { - - startNano = System.nanoTime(); - try { - - SpiTransaction t = request.getTransaction(); - Connection conn = t.getInternalConnection(); - pstmt = conn.prepareStatement(sql); - - if (query.getTimeout() > 0){ - pstmt.setQueryTimeout(query.getTimeout()); - } - - bindLog = predicates.bind(new DataBind(pstmt)); - - rset = pstmt.executeQuery(); - - if (!rset.next()){ - throw new PersistenceException("Expecting 1 row but got none?"); - } - - rowCount = rset.getInt(1); - - long exeNano = System.nanoTime() - startNano; - executionTimeMicros = (int)exeNano/1000; - - return rowCount; - - } finally { - close(); - } - } - - /** - * Close the resources. - *

      - * The jdbc resultSet and statement need to be closed. Its important that - * this method is called. - *

      - */ - private void close() { - try { - if (rset != null) { - rset.close(); - rset = null; - } - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - try { - if (pstmt != null) { - pstmt.close(); - pstmt = null; - } - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - } - - -} +package com.avaje.ebeaninternal.server.query; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.type.DataBind; + +/** + * Executes the select row count query. + */ +public class CQueryRowCount { + + private static final Logger logger = Logger.getLogger(CQueryRowCount.class.getName()); + + /** + * The overall find request wrapper object. + */ + private final OrmQueryRequest request; + + private final BeanDescriptor desc; + + private final SpiQuery query; + + /** + * Where clause predicates. + */ + private final CQueryPredicates predicates; + + /** + * The final sql that is generated. + */ + private final String sql; + + /** + * The resultSet that is read and converted to objects. + */ + private ResultSet rset; + + /** + * The statement used to create the resultSet. + */ + private PreparedStatement pstmt; + + private String bindLog; + + private long startNano; + + private int executionTimeMicros; + + private int rowCount; + + /** + * Create the Sql select based on the request. + */ + public CQueryRowCount(OrmQueryRequest request, CQueryPredicates predicates, String sql) { + this.request = request; + this.query = request.getQuery(); + this.sql = sql; + + query.setGeneratedSql(sql); + + this.desc = request.getBeanDescriptor(); + this.predicates = predicates; + + } + + /** + * Return a summary description of this query. + */ + public String getSummary() { + StringBuilder sb = new StringBuilder(); + sb.append("FindRowCount exeMicros[").append(executionTimeMicros) + .append("] rows[").append(rowCount) + .append("] type[").append(desc.getFullName()) + .append("] predicates[").append(predicates.getLogWhereSql()) + .append("] bind[").append(bindLog).append("]"); + + return sb.toString(); + } + + /** + * Return the bind log. + */ + public String getBindLog() { + return bindLog; + } + + /** + * Return the generated sql. + */ + public String getGeneratedSql() { + return sql; + } + + public SpiOrmQueryRequest getQueryRequest() { + return request; + } + + /** + * Execute the query returning the row count. + */ + public int findRowCount() throws SQLException { + + startNano = System.nanoTime(); + try { + + SpiTransaction t = request.getTransaction(); + Connection conn = t.getInternalConnection(); + pstmt = conn.prepareStatement(sql); + + if (query.getTimeout() > 0){ + pstmt.setQueryTimeout(query.getTimeout()); + } + + bindLog = predicates.bind(new DataBind(pstmt)); + + rset = pstmt.executeQuery(); + + if (!rset.next()){ + throw new PersistenceException("Expecting 1 row but got none?"); + } + + rowCount = rset.getInt(1); + + long exeNano = System.nanoTime() - startNano; + executionTimeMicros = (int)exeNano/1000; + + return rowCount; + + } finally { + close(); + } + } + + /** + * Close the resources. + *

      + * The jdbc resultSet and statement need to be closed. Its important that + * this method is called. + *

      + */ + private void close() { + try { + if (rset != null) { + rset.close(); + rset = null; + } + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + try { + if (pstmt != null) { + pstmt.close(); + pstmt = null; + } + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CallableQuery.java b/src/main/java/com/avaje/ebeaninternal/server/query/CallableQuery.java index 8a7bb5c02..ca56a81f4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CallableQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CallableQuery.java @@ -1,47 +1,28 @@ -/** - * 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.query; - -import com.avaje.ebean.Query; -import com.avaje.ebean.Transaction; -import com.avaje.ebeaninternal.api.SpiEbeanServer; - -/** - * Base object for making query execution into Callable's. - * - * @author rbygrave - * - * @param the entity bean type - */ -public abstract class CallableQuery { - - protected final Query query; - - protected final SpiEbeanServer server; - - protected final Transaction t; - - public CallableQuery(SpiEbeanServer server, Query query, Transaction t) { - this.server = server; - this.query = query; - this.t = t; - } - -} +package com.avaje.ebeaninternal.server.query; + +import com.avaje.ebean.Query; +import com.avaje.ebean.Transaction; +import com.avaje.ebeaninternal.api.SpiEbeanServer; + +/** + * Base object for making query execution into Callable's. + * + * @author rbygrave + * + * @param the entity bean type + */ +public abstract class CallableQuery { + + protected final Query query; + + protected final SpiEbeanServer server; + + protected final Transaction t; + + public CallableQuery(SpiEbeanServer server, Query query, Transaction t) { + this.server = server; + this.query = query; + this.t = t; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CallableQueryIds.java b/src/main/java/com/avaje/ebeaninternal/server/query/CallableQueryIds.java index 49aa8d753..18b404ea9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CallableQueryIds.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CallableQueryIds.java @@ -1,51 +1,32 @@ -/** - * 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.query; - -import java.util.List; -import java.util.concurrent.Callable; - -import com.avaje.ebean.Query; -import com.avaje.ebean.Transaction; -import com.avaje.ebeaninternal.api.SpiEbeanServer; - -/** - * Represent the fetch Id's query as a Callable. - * - * @param the entity bean type - */ -public class CallableQueryIds extends CallableQuery implements Callable> { - - - public CallableQueryIds(SpiEbeanServer server, Query query, Transaction t) { - super(server, query, t); - } - - /** - * Execute the find Id's query returning the list of Id's. - */ - public List call() throws Exception { - // we have already made a copy of the query - // this way the same query instance is available to the - // QueryFutureIds (as so has access to the List before it is done) - return server.findIdsWithCopy(query, t); - } - -} +package com.avaje.ebeaninternal.server.query; + +import java.util.List; +import java.util.concurrent.Callable; + +import com.avaje.ebean.Query; +import com.avaje.ebean.Transaction; +import com.avaje.ebeaninternal.api.SpiEbeanServer; + +/** + * Represent the fetch Id's query as a Callable. + * + * @param the entity bean type + */ +public class CallableQueryIds extends CallableQuery implements Callable> { + + + public CallableQueryIds(SpiEbeanServer server, Query query, Transaction t) { + super(server, query, t); + } + + /** + * Execute the find Id's query returning the list of Id's. + */ + public List call() throws Exception { + // we have already made a copy of the query + // this way the same query instance is available to the + // QueryFutureIds (as so has access to the List before it is done) + return server.findIdsWithCopy(query, t); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CallableQueryList.java b/src/main/java/com/avaje/ebeaninternal/server/query/CallableQueryList.java index b046d0e27..558234884 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CallableQueryList.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CallableQueryList.java @@ -1,50 +1,31 @@ -/** - * 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.query; - -import java.util.List; -import java.util.concurrent.Callable; - -import com.avaje.ebean.Query; -import com.avaje.ebean.Transaction; -import com.avaje.ebeaninternal.api.SpiEbeanServer; - -/** - * Represent the findList query as a Callable. - * - * @param the entity bean type - */ -public class CallableQueryList extends CallableQuery implements Callable> { - - - public CallableQueryList(SpiEbeanServer server, Query query, Transaction t) { - super(server, query, t); - } - - /** - * Execute the query returning the resulting List. - */ - public List call() throws Exception { - return server.findList(query, t); - } - - - -} +package com.avaje.ebeaninternal.server.query; + +import java.util.List; +import java.util.concurrent.Callable; + +import com.avaje.ebean.Query; +import com.avaje.ebean.Transaction; +import com.avaje.ebeaninternal.api.SpiEbeanServer; + +/** + * Represent the findList query as a Callable. + * + * @param the entity bean type + */ +public class CallableQueryList extends CallableQuery implements Callable> { + + + public CallableQueryList(SpiEbeanServer server, Query query, Transaction t) { + super(server, query, t); + } + + /** + * Execute the query returning the resulting List. + */ + public List call() throws Exception { + return server.findList(query, t); + } + + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CallableQueryRowCount.java b/src/main/java/com/avaje/ebeaninternal/server/query/CallableQueryRowCount.java index f660ad62e..55d3a0b17 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CallableQueryRowCount.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CallableQueryRowCount.java @@ -1,49 +1,30 @@ -/** - * 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.query; - -import java.util.concurrent.Callable; - -import com.avaje.ebean.Query; -import com.avaje.ebean.Transaction; -import com.avaje.ebeaninternal.api.SpiEbeanServer; - -/** - * Represent the findRowCount query as a Callable. - * - * @param the entity bean type - */ -public class CallableQueryRowCount extends CallableQuery implements Callable { - - - public CallableQueryRowCount(SpiEbeanServer server, Query query, Transaction t) { - super(server, query, t); - } - - /** - * Execute the query returning the row count. - */ - public Integer call() throws Exception { - return server.findRowCountWithCopy(query, t); - } - - - -} +package com.avaje.ebeaninternal.server.query; + +import java.util.concurrent.Callable; + +import com.avaje.ebean.Query; +import com.avaje.ebean.Transaction; +import com.avaje.ebeaninternal.api.SpiEbeanServer; + +/** + * Represent the findRowCount query as a Callable. + * + * @param the entity bean type + */ +public class CallableQueryRowCount extends CallableQuery implements Callable { + + + public CallableQueryRowCount(SpiEbeanServer server, Query query, Transaction t) { + super(server, query, t); + } + + /** + * Execute the query returning the row count. + */ + public Integer call() throws Exception { + return server.findRowCountWithCopy(query, t); + } + + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CallableSqlQueryList.java b/src/main/java/com/avaje/ebeaninternal/server/query/CallableSqlQueryList.java index c05c4db0e..70a3f4864 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CallableSqlQueryList.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CallableSqlQueryList.java @@ -1,56 +1,37 @@ -/** - * 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.query; - -import java.util.List; -import java.util.concurrent.Callable; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.SqlQuery; -import com.avaje.ebean.SqlRow; -import com.avaje.ebean.Transaction; - -/** - * Represent the SQL query findList as a Callable. - */ -public class CallableSqlQueryList implements Callable> { - - private final SqlQuery query; - - private final EbeanServer server; - - private final Transaction t; - - public CallableSqlQueryList(EbeanServer server, SqlQuery query, Transaction t) { - this.server = server; - this.query = query; - this.t = t; - } - - /** - * Execute the query returning the resulting list. - */ - public List call() throws Exception { - return server.findList(query, t); - } - - - -} +package com.avaje.ebeaninternal.server.query; + +import java.util.List; +import java.util.concurrent.Callable; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.SqlQuery; +import com.avaje.ebean.SqlRow; +import com.avaje.ebean.Transaction; + +/** + * Represent the SQL query findList as a Callable. + */ +public class CallableSqlQueryList implements Callable> { + + private final SqlQuery query; + + private final EbeanServer server; + + private final Transaction t; + + public CallableSqlQueryList(EbeanServer server, SqlQuery query, Transaction t) { + this.server = server; + this.query = query; + this.t = t; + } + + /** + * Execute the query returning the resulting list. + */ + public List call() throws Exception { + return server.findList(query, t); + } + + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CancelableQuery.java b/src/main/java/com/avaje/ebeaninternal/server/query/CancelableQuery.java index 0ae7c1c1f..38054132e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CancelableQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CancelableQuery.java @@ -1,38 +1,19 @@ -/** - * 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.query; - -/** - * Defines a cancelable query. - *

      - * Typically holds a representation of the PreparedStatement to perform the - * actual cancel. - *

      - */ -public interface CancelableQuery { - - /** - * Cancel the query. - *

      - * For JDBC this translates to calling cancel on the PreparedStatement. - *

      - */ - public void cancel(); -} +package com.avaje.ebeaninternal.server.query; + +/** + * Defines a cancelable query. + *

      + * Typically holds a representation of the PreparedStatement to perform the + * actual cancel. + *

      + */ +public interface CancelableQuery { + + /** + * Cancel the query. + *

      + * For JDBC this translates to calling cancel on the PreparedStatement. + *

      + */ + public void cancel(); +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/Constants.java b/src/main/java/com/avaje/ebeaninternal/server/query/Constants.java index 46684e0e3..10e51db4d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/Constants.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/Constants.java @@ -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.query; - -/** - * Constants used in find processing. - */ -public interface Constants { - - /** - * the new line character used. - *

      - * Note that this is removed for logging sql to the transaction log. - *

      - */ - public static final char NEW_LINE = '\n'; - - /** - * The carriage return character. - */ - public static final char CARRIAGE_RETURN = '\r'; - - /** - * literal used for SQL LIMIT in MySql and Postgres. - */ - public static final String LIMIT = "limit"; - - /** - * Literal used for SQL LIMIT OFFSET clause in MySql and Postgres. - */ - public static final String OFFSET = "offset"; - - /** - * ROW_NUMBER() OVER (ORDER BY - */ - public static final String ROW_NUMBER_OVER = "row_number() over (order by "; - - /** - * ) as rn, - */ - public static final String ROW_NUMBER_AS = ") as rn, "; -} +package com.avaje.ebeaninternal.server.query; + +/** + * Constants used in find processing. + */ +public interface Constants { + + /** + * the new line character used. + *

      + * Note that this is removed for logging sql to the transaction log. + *

      + */ + public static final char NEW_LINE = '\n'; + + /** + * The carriage return character. + */ + public static final char CARRIAGE_RETURN = '\r'; + + /** + * literal used for SQL LIMIT in MySql and Postgres. + */ + public static final String LIMIT = "limit"; + + /** + * Literal used for SQL LIMIT OFFSET clause in MySql and Postgres. + */ + public static final String OFFSET = "offset"; + + /** + * ROW_NUMBER() OVER (ORDER BY + */ + public static final String ROW_NUMBER_OVER = "row_number() over (order by "; + + /** + * ) as rn, + */ + public static final String ROW_NUMBER_AS = ") as rn, "; +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java index 26aad5778..fa2d92708 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java @@ -1,148 +1,129 @@ -/** - * 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.query; - -import java.util.Collection; - -import com.avaje.ebean.QueryIterator; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.event.BeanFinder; -import com.avaje.ebeaninternal.api.BeanIdList; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.OrmQueryEngine; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; - -/** - * Main Finder implementation. - */ -public class DefaultOrmQueryEngine implements OrmQueryEngine { - - /** - * Find using predicates - */ - private final CQueryEngine queryEngine; - - /** - * Create the Finder. - */ - public DefaultOrmQueryEngine(BeanDescriptorManager descMgr, CQueryEngine queryEngine) { - - this.queryEngine = queryEngine; - } - - public int findRowCount(OrmQueryRequest request){ - - return queryEngine.findRowCount(request); - } - - public BeanIdList findIds(OrmQueryRequest request){ - - return queryEngine.findIds(request); - } - - - public QueryIterator findIterate(OrmQueryRequest request) { - - // LIMITATION: You can not use QueryIterator to load bean cache - - SpiTransaction t = request.getTransaction(); - - // before we perform a query, we need to flush any - // previous persist requests that are queued/batched. - // The query may read data affected by those requests. - t.flushBatch(); - - return queryEngine.findIterate(request); - } - - public BeanCollection findMany(OrmQueryRequest request) { - - SpiQuery query = request.getQuery(); - - BeanCollection result = null; - - SpiTransaction t = request.getTransaction(); - - // before we perform a query, we need to flush any - // previous persist requests that are queued/batched. - // The query may read data affected by those requests. - t.flushBatch(); - - BeanFinder finder = request.getBeanFinder(); - if (finder != null) { - // this bean type has its own specific finder - result = finder.findMany(request); - } else { - result = queryEngine.findMany(request); - } - - if (query.isLoadBeanCache()){ - // load the individual beans into the bean cache - BeanDescriptor descriptor = request.getBeanDescriptor(); - Collection c = result.getActualDetails(); - for (T bean : c) { - descriptor.cachePutBeanData(bean); - } - } - - if (!result.isEmpty() && query.isUseQueryCache()){ - // load the query result into the query cache - request.putToQueryCache(result); - } - - return result; - } - - - /** - * Find a single bean using its unique id. - */ - public T findId(OrmQueryRequest request) { - - T result = null; - - SpiTransaction t = request.getTransaction(); - - if (t.isBatchFlushOnQuery()){ - // before we perform a query, we need to flush any - // previous persist requests that are queued/batched. - // The query may read data affected by those requests. - t.flushBatch(); - } - - BeanFinder finder = request.getBeanFinder(); - if (finder != null) { - result = finder.find(request); - } else { - result = queryEngine.find(request); - } - - if (result != null && request.isUseBeanCache()){ - request.getBeanDescriptor().cachePutBeanData(result); - } - - return result; - } - - -} +package com.avaje.ebeaninternal.server.query; + +import java.util.Collection; + +import com.avaje.ebean.QueryIterator; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.event.BeanFinder; +import com.avaje.ebeaninternal.api.BeanIdList; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.OrmQueryEngine; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; + +/** + * Main Finder implementation. + */ +public class DefaultOrmQueryEngine implements OrmQueryEngine { + + /** + * Find using predicates + */ + private final CQueryEngine queryEngine; + + /** + * Create the Finder. + */ + public DefaultOrmQueryEngine(BeanDescriptorManager descMgr, CQueryEngine queryEngine) { + + this.queryEngine = queryEngine; + } + + public int findRowCount(OrmQueryRequest request){ + + return queryEngine.findRowCount(request); + } + + public BeanIdList findIds(OrmQueryRequest request){ + + return queryEngine.findIds(request); + } + + + public QueryIterator findIterate(OrmQueryRequest request) { + + // LIMITATION: You can not use QueryIterator to load bean cache + + SpiTransaction t = request.getTransaction(); + + // before we perform a query, we need to flush any + // previous persist requests that are queued/batched. + // The query may read data affected by those requests. + t.flushBatch(); + + return queryEngine.findIterate(request); + } + + public BeanCollection findMany(OrmQueryRequest request) { + + SpiQuery query = request.getQuery(); + + BeanCollection result = null; + + SpiTransaction t = request.getTransaction(); + + // before we perform a query, we need to flush any + // previous persist requests that are queued/batched. + // The query may read data affected by those requests. + t.flushBatch(); + + BeanFinder finder = request.getBeanFinder(); + if (finder != null) { + // this bean type has its own specific finder + result = finder.findMany(request); + } else { + result = queryEngine.findMany(request); + } + + if (query.isLoadBeanCache()){ + // load the individual beans into the bean cache + BeanDescriptor descriptor = request.getBeanDescriptor(); + Collection c = result.getActualDetails(); + for (T bean : c) { + descriptor.cachePutBeanData(bean); + } + } + + if (!result.isEmpty() && query.isUseQueryCache()){ + // load the query result into the query cache + request.putToQueryCache(result); + } + + return result; + } + + + /** + * Find a single bean using its unique id. + */ + public T findId(OrmQueryRequest request) { + + T result = null; + + SpiTransaction t = request.getTransaction(); + + if (t.isBatchFlushOnQuery()){ + // before we perform a query, we need to flush any + // previous persist requests that are queued/batched. + // The query may read data affected by those requests. + t.flushBatch(); + } + + BeanFinder finder = request.getBeanFinder(); + if (finder != null) { + result = finder.find(request); + } else { + result = queryEngine.find(request); + } + + if (result != null && request.isUseBeanCache()){ + request.getBeanDescriptor().cachePutBeanData(result); + } + + return result; + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultRelationalQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultRelationalQueryEngine.java index fa024f2a5..36b382399 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultRelationalQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultRelationalQueryEngine.java @@ -1,278 +1,259 @@ -/** - * 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.query; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.SqlQueryListener; -import com.avaje.ebean.SqlRow; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebeaninternal.api.BindParams; -import com.avaje.ebeaninternal.api.SpiSqlQuery; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.core.Message; -import com.avaje.ebeaninternal.server.core.RelationalQueryEngine; -import com.avaje.ebeaninternal.server.core.RelationalQueryRequest; -import com.avaje.ebeaninternal.server.jmx.MAdminLogging; -import com.avaje.ebeaninternal.server.persist.Binder; -import com.avaje.ebeaninternal.server.type.DataBind; -import com.avaje.ebeaninternal.server.util.BindParamsParser; - -/** - * Perform native sql fetches. - */ -public class DefaultRelationalQueryEngine implements RelationalQueryEngine { - - private static final Logger logger = Logger.getLogger(DefaultRelationalQueryEngine.class.getName()); - - private final int defaultMaxRows; - - private final Binder binder; - - private final String dbTrueValue; - - public DefaultRelationalQueryEngine(MAdminLogging logControl, Binder binder, String dbTrueValue) { - this.binder = binder; - this.defaultMaxRows = GlobalProperties.getInt("nativesql.defaultmaxrows",100000); - this.dbTrueValue = dbTrueValue == null ? "true" : dbTrueValue; - } - - public Object findMany(RelationalQueryRequest request) { - - SpiSqlQuery query = request.getQuery(); - - long startTime = System.currentTimeMillis(); - - SpiTransaction t = request.getTransaction(); - Connection conn = t.getInternalConnection(); - ResultSet rset = null; - PreparedStatement pstmt = null; - - // flag indicating whether we need to close the resources... - boolean useBackgroundToContinueFetch = false; - - String sql = query.getQuery(); - - BindParams bindParams = query.getBindParams(); - - if (!bindParams.isEmpty()) { - // convert any named parameters if required - sql = BindParamsParser.parse(bindParams, sql); - } - - try { - - String bindLog = ""; - String[] propNames = null; - - synchronized (query) { - if (query.isCancelled()){ - logger.finest("Query already cancelled"); - return null; - } - - // synchronise for query.cancel() support - pstmt = conn.prepareStatement(sql); - - if (query.getTimeout() > 0){ - pstmt.setQueryTimeout(query.getTimeout()); - } - if (query.getBufferFetchSizeHint() > 0){ - pstmt.setFetchSize(query.getBufferFetchSizeHint()); - } - - if (!bindParams.isEmpty()) { - bindLog = binder.bind(bindParams, new DataBind(pstmt)); - } - - if (request.isLogSql()) { - String sOut = sql.replace(Constants.NEW_LINE, ' '); - sOut = sOut.replace(Constants.CARRIAGE_RETURN, ' '); - t.logInternal(sOut); - } - - rset = pstmt.executeQuery(); - - propNames = getPropertyNames(rset); - } - - // calculate the initialCapacity of the Map to reduce - // rehashing for queries with 12+ columns - float initCap = (propNames.length) / 0.7f; - int estimateCapacity = (int) initCap + 1; - - // determine the maxRows limit - int maxRows = defaultMaxRows; - if (query.getMaxRows() >= 1) { - maxRows = query.getMaxRows(); - } - - boolean hasHitMaxRows = false; - - int loadRowCount = 0; - - SqlQueryListener listener = query.getListener(); - - BeanCollectionWrapper wrapper = new BeanCollectionWrapper(request); - boolean isMap = wrapper.isMap(); - String mapKey = query.getMapKey(); - - SqlRow bean = null; - - while (rset.next()) { - synchronized (query) { - // synchronise for query.cancel() support - if (!query.isCancelled()){ - bean = readRow(request, rset, propNames, estimateCapacity); - } - } - if (bean != null){ - // bean can be null if query cancelled - if (listener != null) { - listener.process(bean); - - } else { - if (isMap) { - Object keyValue = bean.get(mapKey); - wrapper.addToMap(bean, keyValue); - } else { - wrapper.addToCollection(bean); - } - } - - loadRowCount++; - - if (loadRowCount == maxRows) { - // break, as we have hit the max rows to fetch... - hasHitMaxRows = true; - break; - } - } - } - - BeanCollection beanColl = wrapper.getBeanCollection(); - - if (hasHitMaxRows) { - if (rset.next()) { - // there are more rows available after the maxRows limit - beanColl.setHasMoreRows(true); - } - } - - if (!useBackgroundToContinueFetch) { - beanColl.setFinishedFetch(true); - } - - if (request.isLogSummary()) { - - long exeTime = System.currentTimeMillis() - startTime; - - String msg = "SqlQuery rows[" + loadRowCount + "] time[" + exeTime + "] bind[" - + bindLog + "] finished[" + beanColl.isFinishedFetch() + "]"; - - t.logInternal(msg); - } - - if (query.isCancelled()){ - logger.fine("Query was cancelled during execution rows:"+loadRowCount); - } - - return beanColl; - - } catch (Exception e) { - String m = Message.msg("fetch.error", e.getMessage(), sql); - throw new PersistenceException(m, e); - - } finally { - if (!useBackgroundToContinueFetch) { - try { - if (rset != null) { - rset.close(); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - try { - if (pstmt != null) { - pstmt.close(); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - } - } - } - - /** - * Build the list of property names. - */ - protected String[] getPropertyNames(ResultSet rset) throws SQLException { - - ArrayList propNames = new ArrayList(); - - ResultSetMetaData rsmd = rset.getMetaData(); - - int columnsPlusOne = rsmd.getColumnCount()+1; - - - for (int i = 1; i < columnsPlusOne; i++) { - String columnName = rsmd.getColumnLabel(i); - // will convert columnName to lower case - propNames.add(columnName); - } - - return (String[]) propNames.toArray(new String[propNames.size()]); - } - - /** - * Read the row from the ResultSet and return as a MapBean. - */ - protected SqlRow readRow(RelationalQueryRequest request, ResultSet rset, - String[] propNames, int initialCapacity) throws SQLException { - - // by default a map will rehash on the 12th entry - // it will be pretty common to have 12 or more entries so - // to reduce rehashing I am trying to estimate a good - // initial capacity for the MapBean to use. - SqlRow bean = new DefaultSqlRow(initialCapacity, 0.75f, dbTrueValue); - - int index = 0; - - for (int i = 0; i < propNames.length; i++) { - index++; - Object value = rset.getObject(index); - bean.set(propNames[i], value); - } - - return bean; - - } - -} +package com.avaje.ebeaninternal.server.query; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.SqlQueryListener; +import com.avaje.ebean.SqlRow; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebeaninternal.api.BindParams; +import com.avaje.ebeaninternal.api.SpiSqlQuery; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.core.Message; +import com.avaje.ebeaninternal.server.core.RelationalQueryEngine; +import com.avaje.ebeaninternal.server.core.RelationalQueryRequest; +import com.avaje.ebeaninternal.server.jmx.MAdminLogging; +import com.avaje.ebeaninternal.server.persist.Binder; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.util.BindParamsParser; + +/** + * Perform native sql fetches. + */ +public class DefaultRelationalQueryEngine implements RelationalQueryEngine { + + private static final Logger logger = Logger.getLogger(DefaultRelationalQueryEngine.class.getName()); + + private final int defaultMaxRows; + + private final Binder binder; + + private final String dbTrueValue; + + public DefaultRelationalQueryEngine(MAdminLogging logControl, Binder binder, String dbTrueValue) { + this.binder = binder; + this.defaultMaxRows = GlobalProperties.getInt("nativesql.defaultmaxrows",100000); + this.dbTrueValue = dbTrueValue == null ? "true" : dbTrueValue; + } + + public Object findMany(RelationalQueryRequest request) { + + SpiSqlQuery query = request.getQuery(); + + long startTime = System.currentTimeMillis(); + + SpiTransaction t = request.getTransaction(); + Connection conn = t.getInternalConnection(); + ResultSet rset = null; + PreparedStatement pstmt = null; + + // flag indicating whether we need to close the resources... + boolean useBackgroundToContinueFetch = false; + + String sql = query.getQuery(); + + BindParams bindParams = query.getBindParams(); + + if (!bindParams.isEmpty()) { + // convert any named parameters if required + sql = BindParamsParser.parse(bindParams, sql); + } + + try { + + String bindLog = ""; + String[] propNames = null; + + synchronized (query) { + if (query.isCancelled()){ + logger.finest("Query already cancelled"); + return null; + } + + // synchronise for query.cancel() support + pstmt = conn.prepareStatement(sql); + + if (query.getTimeout() > 0){ + pstmt.setQueryTimeout(query.getTimeout()); + } + if (query.getBufferFetchSizeHint() > 0){ + pstmt.setFetchSize(query.getBufferFetchSizeHint()); + } + + if (!bindParams.isEmpty()) { + bindLog = binder.bind(bindParams, new DataBind(pstmt)); + } + + if (request.isLogSql()) { + String sOut = sql.replace(Constants.NEW_LINE, ' '); + sOut = sOut.replace(Constants.CARRIAGE_RETURN, ' '); + t.logInternal(sOut); + } + + rset = pstmt.executeQuery(); + + propNames = getPropertyNames(rset); + } + + // calculate the initialCapacity of the Map to reduce + // rehashing for queries with 12+ columns + float initCap = (propNames.length) / 0.7f; + int estimateCapacity = (int) initCap + 1; + + // determine the maxRows limit + int maxRows = defaultMaxRows; + if (query.getMaxRows() >= 1) { + maxRows = query.getMaxRows(); + } + + boolean hasHitMaxRows = false; + + int loadRowCount = 0; + + SqlQueryListener listener = query.getListener(); + + BeanCollectionWrapper wrapper = new BeanCollectionWrapper(request); + boolean isMap = wrapper.isMap(); + String mapKey = query.getMapKey(); + + SqlRow bean = null; + + while (rset.next()) { + synchronized (query) { + // synchronise for query.cancel() support + if (!query.isCancelled()){ + bean = readRow(request, rset, propNames, estimateCapacity); + } + } + if (bean != null){ + // bean can be null if query cancelled + if (listener != null) { + listener.process(bean); + + } else { + if (isMap) { + Object keyValue = bean.get(mapKey); + wrapper.addToMap(bean, keyValue); + } else { + wrapper.addToCollection(bean); + } + } + + loadRowCount++; + + if (loadRowCount == maxRows) { + // break, as we have hit the max rows to fetch... + hasHitMaxRows = true; + break; + } + } + } + + BeanCollection beanColl = wrapper.getBeanCollection(); + + if (hasHitMaxRows) { + if (rset.next()) { + // there are more rows available after the maxRows limit + beanColl.setHasMoreRows(true); + } + } + + if (!useBackgroundToContinueFetch) { + beanColl.setFinishedFetch(true); + } + + if (request.isLogSummary()) { + + long exeTime = System.currentTimeMillis() - startTime; + + String msg = "SqlQuery rows[" + loadRowCount + "] time[" + exeTime + "] bind[" + + bindLog + "] finished[" + beanColl.isFinishedFetch() + "]"; + + t.logInternal(msg); + } + + if (query.isCancelled()){ + logger.fine("Query was cancelled during execution rows:"+loadRowCount); + } + + return beanColl; + + } catch (Exception e) { + String m = Message.msg("fetch.error", e.getMessage(), sql); + throw new PersistenceException(m, e); + + } finally { + if (!useBackgroundToContinueFetch) { + try { + if (rset != null) { + rset.close(); + } + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + try { + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + logger.log(Level.SEVERE, null, e); + } + } + } + } + + /** + * Build the list of property names. + */ + protected String[] getPropertyNames(ResultSet rset) throws SQLException { + + ArrayList propNames = new ArrayList(); + + ResultSetMetaData rsmd = rset.getMetaData(); + + int columnsPlusOne = rsmd.getColumnCount()+1; + + + for (int i = 1; i < columnsPlusOne; i++) { + String columnName = rsmd.getColumnLabel(i); + // will convert columnName to lower case + propNames.add(columnName); + } + + return (String[]) propNames.toArray(new String[propNames.size()]); + } + + /** + * Read the row from the ResultSet and return as a MapBean. + */ + protected SqlRow readRow(RelationalQueryRequest request, ResultSet rset, + String[] propNames, int initialCapacity) throws SQLException { + + // by default a map will rehash on the 12th entry + // it will be pretty common to have 12 or more entries so + // to reduce rehashing I am trying to estimate a good + // initial capacity for the MapBean to use. + SqlRow bean = new DefaultSqlRow(initialCapacity, 0.75f, dbTrueValue); + + int index = 0; + + for (int i = 0; i < propNames.length; i++) { + index++; + Object value = rset.getObject(index); + bean.set(propNames[i], value); + } + + return bean; + + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultSqlRow.java b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultSqlRow.java index f71d4edb1..a240cb6d3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultSqlRow.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultSqlRow.java @@ -1,226 +1,207 @@ -/** - * 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.query; - -import java.math.BigDecimal; -import java.sql.Date; -import java.sql.Timestamp; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; -import java.util.UUID; - -import com.avaje.ebean.SqlQuery; -import com.avaje.ebean.SqlRow; -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * Used to return raw SQL query results. - *

      - * Refer to {@link SqlQuery} for examples. - *

      - *

      - * There are convenience methods such as getInteger(), getBigDecimal() etc. The - * reason for these methods is that the values put into this map often come - * straight from the JDBC resultSet. Depending on the JDBC driver it may put a - * different type into a given property. For example an Integer, BigDecimal, - * Double could all be put into a property depending on the JDBC driver used. - * These convenience methods automatically convert the value as required - * returning the type you expect. - *

      - */ -public class DefaultSqlRow implements SqlRow { - - static final long serialVersionUID = -3120927797041336242L; - - private final String dbTrueValue; - - /** - * The underlying map of property data. - */ - Map map; - - /** - * Create with a specific Map implementation. - *

      - * The default Map implementation is LinkedHashMap. - *

      - */ - public DefaultSqlRow(Map map, String dbTrueValue) { - this.map = map; - this.dbTrueValue = dbTrueValue; - } - - /** - * Create a new MapBean based on a LinkedHashMap with default - * initialCapacity (of 16). - */ - public DefaultSqlRow(String dbTrueValue) { - this.map = new LinkedHashMap(); - this.dbTrueValue = dbTrueValue; - } - - /** - * Create with an initialCapacity and loadFactor. - *

      - * The defaults of these are 16 and 0.75. - *

      - *

      - * Note that the Map will rehash the contents when the number of keys in - * this map reaches its threshold (initialCapacity * loadFactor). - *

      - */ - public DefaultSqlRow(int initialCapacity, float loadFactor, String dbTrueValue) { - this.map = new LinkedHashMap(initialCapacity, loadFactor); - this.dbTrueValue = dbTrueValue; - } - - public Iterator keys() { - return map.keySet().iterator(); - } - - public Object remove(Object name) { - name = ((String) name).toLowerCase(); - return map.remove(name); - } - - public Object get(Object name) { - name = ((String) name).toLowerCase(); - return map.get(name); - } - - public Object put(String name, Object value) { - return setInternal(name, value); - } - - public Object set(String name, Object value) { - return setInternal(name, value); - } - - private Object setInternal(String name, Object newValue) { - // MapBean properties are always lowercase - name = name.toLowerCase(); - - // valueList = null; - return map.put(name, newValue); - } - - public UUID getUUID(String name) { - Object val = get(name); - return BasicTypeConverter.toUUID(val); - } - - public Boolean getBoolean(String name) { - Object val = get(name); - return BasicTypeConverter.toBoolean(val, dbTrueValue); - } - - public Integer getInteger(String name) { - Object val = get(name); - return BasicTypeConverter.toInteger(val); - } - - public BigDecimal getBigDecimal(String name) { - Object val = get(name); - return BasicTypeConverter.toBigDecimal(val); - } - - public Long getLong(String name) { - Object val = get(name); - return BasicTypeConverter.toLong(val); - } - - public Double getDouble(String name) { - Object val = get(name); - return BasicTypeConverter.toDouble(val); - } - - public Float getFloat(String name) { - Object val = get(name); - return BasicTypeConverter.toFloat(val); - } - - public String getString(String name) { - Object val = get(name); - return BasicTypeConverter.toString(val); - } - - public java.util.Date getUtilDate(String name) { - Object val = get(name); - return BasicTypeConverter.toUtilDate(val); - } - - public Date getDate(String name) { - Object val = get(name); - return BasicTypeConverter.toDate(val); - } - - public Timestamp getTimestamp(String name) { - Object val = get(name); - return BasicTypeConverter.toTimestamp(val); - } - - public String toString() { - return map.toString(); - } - - // ------------------------------------ - // Normal map methods... - - public void clear() { - map.clear(); - } - - public boolean containsKey(Object key) { - key = ((String) key).toLowerCase(); - return map.containsKey(key); - } - - public boolean containsValue(Object value) { - return map.containsValue(value); - } - - public Set> entrySet() { - return map.entrySet(); - } - - public boolean isEmpty() { - return map.isEmpty(); - } - - public Set keySet() { - return map.keySet(); - } - - public void putAll(Map t) { - map.putAll(t); - } - - public int size() { - return map.size(); - } - - public Collection values() { - return map.values(); - } - -} +package com.avaje.ebeaninternal.server.query; + +import java.math.BigDecimal; +import java.sql.Date; +import java.sql.Timestamp; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import com.avaje.ebean.SqlQuery; +import com.avaje.ebean.SqlRow; +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * Used to return raw SQL query results. + *

      + * Refer to {@link SqlQuery} for examples. + *

      + *

      + * There are convenience methods such as getInteger(), getBigDecimal() etc. The + * reason for these methods is that the values put into this map often come + * straight from the JDBC resultSet. Depending on the JDBC driver it may put a + * different type into a given property. For example an Integer, BigDecimal, + * Double could all be put into a property depending on the JDBC driver used. + * These convenience methods automatically convert the value as required + * returning the type you expect. + *

      + */ +public class DefaultSqlRow implements SqlRow { + + static final long serialVersionUID = -3120927797041336242L; + + private final String dbTrueValue; + + /** + * The underlying map of property data. + */ + Map map; + + /** + * Create with a specific Map implementation. + *

      + * The default Map implementation is LinkedHashMap. + *

      + */ + public DefaultSqlRow(Map map, String dbTrueValue) { + this.map = map; + this.dbTrueValue = dbTrueValue; + } + + /** + * Create a new MapBean based on a LinkedHashMap with default + * initialCapacity (of 16). + */ + public DefaultSqlRow(String dbTrueValue) { + this.map = new LinkedHashMap(); + this.dbTrueValue = dbTrueValue; + } + + /** + * Create with an initialCapacity and loadFactor. + *

      + * The defaults of these are 16 and 0.75. + *

      + *

      + * Note that the Map will rehash the contents when the number of keys in + * this map reaches its threshold (initialCapacity * loadFactor). + *

      + */ + public DefaultSqlRow(int initialCapacity, float loadFactor, String dbTrueValue) { + this.map = new LinkedHashMap(initialCapacity, loadFactor); + this.dbTrueValue = dbTrueValue; + } + + public Iterator keys() { + return map.keySet().iterator(); + } + + public Object remove(Object name) { + name = ((String) name).toLowerCase(); + return map.remove(name); + } + + public Object get(Object name) { + name = ((String) name).toLowerCase(); + return map.get(name); + } + + public Object put(String name, Object value) { + return setInternal(name, value); + } + + public Object set(String name, Object value) { + return setInternal(name, value); + } + + private Object setInternal(String name, Object newValue) { + // MapBean properties are always lowercase + name = name.toLowerCase(); + + // valueList = null; + return map.put(name, newValue); + } + + public UUID getUUID(String name) { + Object val = get(name); + return BasicTypeConverter.toUUID(val); + } + + public Boolean getBoolean(String name) { + Object val = get(name); + return BasicTypeConverter.toBoolean(val, dbTrueValue); + } + + public Integer getInteger(String name) { + Object val = get(name); + return BasicTypeConverter.toInteger(val); + } + + public BigDecimal getBigDecimal(String name) { + Object val = get(name); + return BasicTypeConverter.toBigDecimal(val); + } + + public Long getLong(String name) { + Object val = get(name); + return BasicTypeConverter.toLong(val); + } + + public Double getDouble(String name) { + Object val = get(name); + return BasicTypeConverter.toDouble(val); + } + + public Float getFloat(String name) { + Object val = get(name); + return BasicTypeConverter.toFloat(val); + } + + public String getString(String name) { + Object val = get(name); + return BasicTypeConverter.toString(val); + } + + public java.util.Date getUtilDate(String name) { + Object val = get(name); + return BasicTypeConverter.toUtilDate(val); + } + + public Date getDate(String name) { + Object val = get(name); + return BasicTypeConverter.toDate(val); + } + + public Timestamp getTimestamp(String name) { + Object val = get(name); + return BasicTypeConverter.toTimestamp(val); + } + + public String toString() { + return map.toString(); + } + + // ------------------------------------ + // Normal map methods... + + public void clear() { + map.clear(); + } + + public boolean containsKey(Object key) { + key = ((String) key).toLowerCase(); + return map.containsKey(key); + } + + public boolean containsValue(Object value) { + return map.containsValue(value); + } + + public Set> entrySet() { + return map.entrySet(); + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + public Set keySet() { + return map.keySet(); + } + + public void putAll(Map t) { + map.putAll(t); + } + + public int size() { + return map.size(); + } + + public Collection values() { + return map.values(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetList.java b/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetList.java index 265db00c5..a61f9ffff 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetList.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetList.java @@ -1,244 +1,225 @@ -/** - * 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.query; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; - -import com.avaje.ebean.Page; - -public class LimitOffsetList implements List { - - private final LimitOffsetPagingQuery owner; - - private List localCopy; - - public LimitOffsetList(LimitOffsetPagingQuery owner) { - this.owner = owner; - } - - private void ensureLocalCopy() { - if (localCopy == null){ - localCopy = new ArrayList(); - - int pgIndex = 0; - while(true){ - Page page = owner.getPage(pgIndex++); - List list = page.getList(); - localCopy.addAll(list); - if (!page.hasNext()){ - break; - } - } - } - } - - private boolean hasNext(int position){ - return owner.hasNext(position); - } - - public void clear() { - localCopy = new ArrayList(); - } - - public T get(int index) { - if (localCopy != null){ - return localCopy.get(index); - } else { - return owner.get(index); - } - } - - public boolean isEmpty() { - if (localCopy != null){ - return localCopy.isEmpty(); - } else { - return owner.getTotalRowCount() == 0; - } - } - - public int size() { - if (localCopy != null){ - return localCopy.size(); - } else { - return owner.getTotalRowCount(); - } - } - - public Iterator iterator() { - if (localCopy != null){ - return localCopy.iterator(); - } else { - return new ListItr(this, 0); - } - } - - public ListIterator listIterator() { - if (localCopy != null){ - return localCopy.listIterator(); - } else { - return new ListItr(this, 0); - } - } - - public ListIterator listIterator(int index) { - if (localCopy != null){ - return localCopy.listIterator(index); - } else { - return new ListItr(this, index); - } - } - - public List subList(int fromIndex, int toIndex) { - if (localCopy != null){ - return localCopy.subList(fromIndex, toIndex); - } else { - //FIXME: subList not implemented ... - throw new RuntimeException("Not implemented at this point"); - } - } - - public int lastIndexOf(Object o) { - ensureLocalCopy(); - return localCopy.lastIndexOf(o); - } - - public void add(int index, T element) { - ensureLocalCopy(); - localCopy.add(index, element); - } - - public boolean add(T o) { - ensureLocalCopy(); - return localCopy.add(o); - } - - public boolean addAll(Collection c) { - ensureLocalCopy(); - return localCopy.addAll(c); - } - - public boolean addAll(int index, Collection c) { - ensureLocalCopy(); - return localCopy.addAll(index, c); - } - - public boolean contains(Object o) { - ensureLocalCopy(); - return localCopy.contains(o); - } - - public boolean containsAll(Collection c) { - ensureLocalCopy(); - return localCopy.containsAll(c); - } - - public int indexOf(Object o) { - ensureLocalCopy(); - return localCopy.indexOf(o); - } - - public T remove(int index) { - ensureLocalCopy(); - return localCopy.remove(index); - } - - public boolean remove(Object o) { - ensureLocalCopy(); - return localCopy.remove(o); - } - - public boolean removeAll(Collection c) { - ensureLocalCopy(); - return localCopy.removeAll(c); - } - - public boolean retainAll(Collection c) { - ensureLocalCopy(); - return localCopy.retainAll(c); - } - - public T set(int index, T element) { - ensureLocalCopy(); - return localCopy.set(index, element); - } - - - public Object[] toArray() { - ensureLocalCopy(); - return localCopy.toArray(); - } - - public K[] toArray(K[] a) { - ensureLocalCopy(); - return localCopy.toArray(a); - } - - private class ListItr implements ListIterator { - - private LimitOffsetList ownerList; - private int position; - - ListItr(LimitOffsetList ownerList, int position) { - this.ownerList = ownerList; - this.position = position; - } - - public void add(T o) { - ownerList.add(position++, o); - } - - public boolean hasNext() { - return ownerList.hasNext(position); - } - - public boolean hasPrevious() { - return position > 0; - } - - public T next() { - return ownerList.get(position++); - } - - public int nextIndex() { - return position; - } - - public T previous() { - return get(--position); - } - - public int previousIndex() { - return position - 1; - } - - public void remove() { - throw new RuntimeException("Not supported yet"); - } - - public void set(T o) { - throw new RuntimeException("Not supported yet"); - } - } - -} +package com.avaje.ebeaninternal.server.query; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import com.avaje.ebean.Page; + +public class LimitOffsetList implements List { + + private final LimitOffsetPagingQuery owner; + + private List localCopy; + + public LimitOffsetList(LimitOffsetPagingQuery owner) { + this.owner = owner; + } + + private void ensureLocalCopy() { + if (localCopy == null){ + localCopy = new ArrayList(); + + int pgIndex = 0; + while(true){ + Page page = owner.getPage(pgIndex++); + List list = page.getList(); + localCopy.addAll(list); + if (!page.hasNext()){ + break; + } + } + } + } + + private boolean hasNext(int position){ + return owner.hasNext(position); + } + + public void clear() { + localCopy = new ArrayList(); + } + + public T get(int index) { + if (localCopy != null){ + return localCopy.get(index); + } else { + return owner.get(index); + } + } + + public boolean isEmpty() { + if (localCopy != null){ + return localCopy.isEmpty(); + } else { + return owner.getTotalRowCount() == 0; + } + } + + public int size() { + if (localCopy != null){ + return localCopy.size(); + } else { + return owner.getTotalRowCount(); + } + } + + public Iterator iterator() { + if (localCopy != null){ + return localCopy.iterator(); + } else { + return new ListItr(this, 0); + } + } + + public ListIterator listIterator() { + if (localCopy != null){ + return localCopy.listIterator(); + } else { + return new ListItr(this, 0); + } + } + + public ListIterator listIterator(int index) { + if (localCopy != null){ + return localCopy.listIterator(index); + } else { + return new ListItr(this, index); + } + } + + public List subList(int fromIndex, int toIndex) { + if (localCopy != null){ + return localCopy.subList(fromIndex, toIndex); + } else { + //FIXME: subList not implemented ... + throw new RuntimeException("Not implemented at this point"); + } + } + + public int lastIndexOf(Object o) { + ensureLocalCopy(); + return localCopy.lastIndexOf(o); + } + + public void add(int index, T element) { + ensureLocalCopy(); + localCopy.add(index, element); + } + + public boolean add(T o) { + ensureLocalCopy(); + return localCopy.add(o); + } + + public boolean addAll(Collection c) { + ensureLocalCopy(); + return localCopy.addAll(c); + } + + public boolean addAll(int index, Collection c) { + ensureLocalCopy(); + return localCopy.addAll(index, c); + } + + public boolean contains(Object o) { + ensureLocalCopy(); + return localCopy.contains(o); + } + + public boolean containsAll(Collection c) { + ensureLocalCopy(); + return localCopy.containsAll(c); + } + + public int indexOf(Object o) { + ensureLocalCopy(); + return localCopy.indexOf(o); + } + + public T remove(int index) { + ensureLocalCopy(); + return localCopy.remove(index); + } + + public boolean remove(Object o) { + ensureLocalCopy(); + return localCopy.remove(o); + } + + public boolean removeAll(Collection c) { + ensureLocalCopy(); + return localCopy.removeAll(c); + } + + public boolean retainAll(Collection c) { + ensureLocalCopy(); + return localCopy.retainAll(c); + } + + public T set(int index, T element) { + ensureLocalCopy(); + return localCopy.set(index, element); + } + + + public Object[] toArray() { + ensureLocalCopy(); + return localCopy.toArray(); + } + + public K[] toArray(K[] a) { + ensureLocalCopy(); + return localCopy.toArray(a); + } + + private class ListItr implements ListIterator { + + private LimitOffsetList ownerList; + private int position; + + ListItr(LimitOffsetList ownerList, int position) { + this.ownerList = ownerList; + this.position = position; + } + + public void add(T o) { + ownerList.add(position++, o); + } + + public boolean hasNext() { + return ownerList.hasNext(position); + } + + public boolean hasPrevious() { + return position > 0; + } + + public T next() { + return ownerList.get(position++); + } + + public int nextIndex() { + return position; + } + + public T previous() { + return get(--position); + } + + public int previousIndex() { + return position - 1; + } + + public void remove() { + throw new RuntimeException("Not supported yet"); + } + + public void set(T o) { + throw new RuntimeException("Not supported yet"); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPage.java b/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPage.java index 8ed61ddef..26f889e48 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPage.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPage.java @@ -1,127 +1,108 @@ -/** - * 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.query; - -import java.util.List; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.FutureList; -import com.avaje.ebean.Page; -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.BeanCollectionTouched; -import com.avaje.ebeaninternal.api.SpiQuery; - -/** - * Page implementation based on limit offset types of queries. - * - * @author rbygrave - * - * @param - * the entity bean type - */ -public class LimitOffsetPage implements Page, BeanCollectionTouched { - - private final int pageIndex; - - private final LimitOffsetPagingQuery owner; - - private FutureList futureList; - - public LimitOffsetPage(int pageIndex, LimitOffsetPagingQuery owner) { - this.pageIndex = pageIndex; - this.owner = owner; - } - - public FutureList getFutureList() { - - if (futureList == null) { - SpiQuery originalQuery = owner.getSpiQuery(); - SpiQuery copy = originalQuery.copy(); - copy.setPersistenceContext(originalQuery.getPersistenceContext()); - - int pageSize = owner.getPageSize(); - copy.setFirstRow(pageIndex * pageSize); - copy.setMaxRows(pageSize); - copy.setBeanCollectionTouched(this); - futureList = owner.getServer().findFutureList(copy, null); - } - - return futureList; - } - - /** - * Perform fetch ahead when the list is first accessed. - */ - public void notifyTouched(BeanCollection c) { - if (c.hasMoreRows()) { - owner.fetchAheadIfRequired(pageIndex); - } - } - - public List getList() { - try { - return getFutureList().get(); - } catch (Exception e) { - throw new PersistenceException(e); - } - } - - @SuppressWarnings("unchecked") - public boolean hasNext() { - return ((BeanCollection) getList()).hasMoreRows(); - } - - public boolean hasPrev() { - return pageIndex > 0; - } - - public Page next() { - return owner.getPage(pageIndex + 1); - } - - public Page prev() { - return owner.getPage(pageIndex - 1); - } - - public int getPageIndex() { - return pageIndex; - } - - public int getTotalPageCount() { - return owner.getTotalPageCount(); - } - - public int getTotalRowCount() { - return owner.getTotalRowCount(); - } - - public String getDisplayXtoYofZ(String to, String of) { - - int first = pageIndex * owner.getPageSize() + 1; - int last = first + getList().size() - 1; - int total = getTotalRowCount(); - - return first+to+last+of+total; - } - - - -} +package com.avaje.ebeaninternal.server.query; + +import java.util.List; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.FutureList; +import com.avaje.ebean.Page; +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.BeanCollectionTouched; +import com.avaje.ebeaninternal.api.SpiQuery; + +/** + * Page implementation based on limit offset types of queries. + * + * @author rbygrave + * + * @param + * the entity bean type + */ +public class LimitOffsetPage implements Page, BeanCollectionTouched { + + private final int pageIndex; + + private final LimitOffsetPagingQuery owner; + + private FutureList futureList; + + public LimitOffsetPage(int pageIndex, LimitOffsetPagingQuery owner) { + this.pageIndex = pageIndex; + this.owner = owner; + } + + public FutureList getFutureList() { + + if (futureList == null) { + SpiQuery originalQuery = owner.getSpiQuery(); + SpiQuery copy = originalQuery.copy(); + copy.setPersistenceContext(originalQuery.getPersistenceContext()); + + int pageSize = owner.getPageSize(); + copy.setFirstRow(pageIndex * pageSize); + copy.setMaxRows(pageSize); + copy.setBeanCollectionTouched(this); + futureList = owner.getServer().findFutureList(copy, null); + } + + return futureList; + } + + /** + * Perform fetch ahead when the list is first accessed. + */ + public void notifyTouched(BeanCollection c) { + if (c.hasMoreRows()) { + owner.fetchAheadIfRequired(pageIndex); + } + } + + public List getList() { + try { + return getFutureList().get(); + } catch (Exception e) { + throw new PersistenceException(e); + } + } + + @SuppressWarnings("unchecked") + public boolean hasNext() { + return ((BeanCollection) getList()).hasMoreRows(); + } + + public boolean hasPrev() { + return pageIndex > 0; + } + + public Page next() { + return owner.getPage(pageIndex + 1); + } + + public Page prev() { + return owner.getPage(pageIndex - 1); + } + + public int getPageIndex() { + return pageIndex; + } + + public int getTotalPageCount() { + return owner.getTotalPageCount(); + } + + public int getTotalRowCount() { + return owner.getTotalRowCount(); + } + + public String getDisplayXtoYofZ(String to, String of) { + + int first = pageIndex * owner.getPageSize() + 1; + int last = first + getList().size() - 1; + int total = getTotalRowCount(); + + return first+to+last+of+total; + } + + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPagingQuery.java b/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPagingQuery.java index 18ca363ed..915c037cc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPagingQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPagingQuery.java @@ -1,156 +1,137 @@ -/** - * 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.query; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Future; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.Page; -import com.avaje.ebean.PagingList; -import com.avaje.ebeaninternal.api.Monitor; -import com.avaje.ebeaninternal.api.SpiQuery; - -public class LimitOffsetPagingQuery implements PagingList { - - private transient EbeanServer server; - - private final SpiQuery query; - - private final List> pages = new ArrayList>(); - - private final Monitor monitor = new Monitor(); - - private final int pageSize; - - private boolean fetchAhead = true; - - private Future futureRowCount; - - public LimitOffsetPagingQuery(EbeanServer server, SpiQuery query, int pageSize) { - this.query = query; - this.pageSize = pageSize; - this.server = server; - } - - public EbeanServer getServer() { - return server; - } - - public void setServer(EbeanServer server) { - this.server = server; - } - - public SpiQuery getSpiQuery() { - return query; - } - - public PagingList setFetchAhead(boolean fetchAhead) { - this.fetchAhead = fetchAhead; - return this; - } - - public List getAsList() { - return new LimitOffsetList(this); - } - - public Future getFutureRowCount() { - synchronized (monitor) { - if (futureRowCount == null){ - futureRowCount = server.findFutureRowCount(query, null); - } - return futureRowCount; - } - } - - private LimitOffsetPage internalGetPage(int i){ - synchronized (monitor) { - int ps = pages.size(); - if (ps <= i){ - for (int j = ps; j <= i; j++) { - LimitOffsetPage p = new LimitOffsetPage(j, this); - pages.add(p); - } - } - return pages.get(i); - } - } - - protected void fetchAheadIfRequired(int pageIndex){ - synchronized (monitor) { - // Already checked in LimitOffsetPage that there is another page - if (fetchAhead){ - // fetchAhead is turned on so get the next page and trigger query - LimitOffsetPage nextPage = internalGetPage(pageIndex + 1); - nextPage.getFutureList(); - } - } - } - - public void refresh() { - synchronized (monitor) { - futureRowCount = null; - pages.clear(); - } - } - - public Page getPage(int i) { - return internalGetPage(i); - } - - protected boolean hasNext(int position){ - return position < getTotalRowCount(); - } - - protected T get(int rowIndex){ - int pg = rowIndex / pageSize; - int offset = rowIndex % pageSize; - - Page page = getPage(pg); - return page.getList().get(offset); - } - - public int getTotalPageCount() { - - int rowCount = getTotalRowCount(); - if (rowCount == 0){ - return 0; - } else { - return ((rowCount-1) / pageSize) + 1; - } - } - - public int getPageSize() { - return pageSize; - } - - public int getTotalRowCount() { - try { - return getFutureRowCount().get(); - } catch (Exception e) { - throw new PersistenceException(e); - } - } - - -} +package com.avaje.ebeaninternal.server.query; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Future; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.Page; +import com.avaje.ebean.PagingList; +import com.avaje.ebeaninternal.api.Monitor; +import com.avaje.ebeaninternal.api.SpiQuery; + +public class LimitOffsetPagingQuery implements PagingList { + + private transient EbeanServer server; + + private final SpiQuery query; + + private final List> pages = new ArrayList>(); + + private final Monitor monitor = new Monitor(); + + private final int pageSize; + + private boolean fetchAhead = true; + + private Future futureRowCount; + + public LimitOffsetPagingQuery(EbeanServer server, SpiQuery query, int pageSize) { + this.query = query; + this.pageSize = pageSize; + this.server = server; + } + + public EbeanServer getServer() { + return server; + } + + public void setServer(EbeanServer server) { + this.server = server; + } + + public SpiQuery getSpiQuery() { + return query; + } + + public PagingList setFetchAhead(boolean fetchAhead) { + this.fetchAhead = fetchAhead; + return this; + } + + public List getAsList() { + return new LimitOffsetList(this); + } + + public Future getFutureRowCount() { + synchronized (monitor) { + if (futureRowCount == null){ + futureRowCount = server.findFutureRowCount(query, null); + } + return futureRowCount; + } + } + + private LimitOffsetPage internalGetPage(int i){ + synchronized (monitor) { + int ps = pages.size(); + if (ps <= i){ + for (int j = ps; j <= i; j++) { + LimitOffsetPage p = new LimitOffsetPage(j, this); + pages.add(p); + } + } + return pages.get(i); + } + } + + protected void fetchAheadIfRequired(int pageIndex){ + synchronized (monitor) { + // Already checked in LimitOffsetPage that there is another page + if (fetchAhead){ + // fetchAhead is turned on so get the next page and trigger query + LimitOffsetPage nextPage = internalGetPage(pageIndex + 1); + nextPage.getFutureList(); + } + } + } + + public void refresh() { + synchronized (monitor) { + futureRowCount = null; + pages.clear(); + } + } + + public Page getPage(int i) { + return internalGetPage(i); + } + + protected boolean hasNext(int position){ + return position < getTotalRowCount(); + } + + protected T get(int rowIndex){ + int pg = rowIndex / pageSize; + int offset = rowIndex % pageSize; + + Page page = getPage(pg); + return page.getList().get(offset); + } + + public int getTotalPageCount() { + + int rowCount = getTotalRowCount(); + if (rowCount == 0){ + return 0; + } else { + return ((rowCount-1) / pageSize) + 1; + } + } + + public int getPageSize() { + return pageSize; + } + + public int getTotalRowCount() { + try { + return getFutureRowCount().get(); + } catch (Exception e) { + throw new PersistenceException(e); + } + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureIds.java b/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureIds.java index 0be32338d..cf9877154 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureIds.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureIds.java @@ -1,54 +1,35 @@ -/** - * 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.query; - -import java.util.List; -import java.util.concurrent.FutureTask; - -import com.avaje.ebean.FutureIds; -import com.avaje.ebean.Query; -import com.avaje.ebeaninternal.api.SpiQuery; - -/** - * Default implementation of FutureIds. - */ -public class QueryFutureIds extends BaseFuture> implements FutureIds { - - private final SpiQuery query; - - public QueryFutureIds(SpiQuery query, FutureTask> futureTask) { - super(futureTask); - this.query = query; - } - - public Query getQuery() { - return query; - } - - public List getPartialIds() { - return query.getIdList(); - } - - public boolean cancel(boolean mayInterruptIfRunning) { - query.cancel(); - return super.cancel(mayInterruptIfRunning); - } - -} +package com.avaje.ebeaninternal.server.query; + +import java.util.List; +import java.util.concurrent.FutureTask; + +import com.avaje.ebean.FutureIds; +import com.avaje.ebean.Query; +import com.avaje.ebeaninternal.api.SpiQuery; + +/** + * Default implementation of FutureIds. + */ +public class QueryFutureIds extends BaseFuture> implements FutureIds { + + private final SpiQuery query; + + public QueryFutureIds(SpiQuery query, FutureTask> futureTask) { + super(futureTask); + this.query = query; + } + + public Query getQuery() { + return query; + } + + public List getPartialIds() { + return query.getIdList(); + } + + public boolean cancel(boolean mayInterruptIfRunning) { + query.cancel(); + return super.cancel(mayInterruptIfRunning); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureList.java b/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureList.java index 834086e23..ce7e93c0a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureList.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureList.java @@ -1,51 +1,32 @@ -/** - * 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.query; - -import java.util.List; -import java.util.concurrent.FutureTask; - -import com.avaje.ebean.FutureList; -import com.avaje.ebean.Query; - -/** - * Default implementation for FutureList. - */ -public class QueryFutureList extends BaseFuture> implements FutureList { - - private final Query query; - - - public QueryFutureList(Query query, FutureTask> futureTask) { - super(futureTask); - this.query = query; - } - - public Query getQuery() { - return query; - } - - public boolean cancel(boolean mayInterruptIfRunning) { - query.cancel(); - return super.cancel(mayInterruptIfRunning); - } - - -} +package com.avaje.ebeaninternal.server.query; + +import java.util.List; +import java.util.concurrent.FutureTask; + +import com.avaje.ebean.FutureList; +import com.avaje.ebean.Query; + +/** + * Default implementation for FutureList. + */ +public class QueryFutureList extends BaseFuture> implements FutureList { + + private final Query query; + + + public QueryFutureList(Query query, FutureTask> futureTask) { + super(futureTask); + this.query = query; + } + + public Query getQuery() { + return query; + } + + public boolean cancel(boolean mayInterruptIfRunning) { + query.cancel(); + return super.cancel(mayInterruptIfRunning); + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureRowCount.java b/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureRowCount.java index e3d5b67e2..fee28f74e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureRowCount.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/QueryFutureRowCount.java @@ -1,49 +1,30 @@ -/** - * 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.query; - -import java.util.concurrent.FutureTask; - -import com.avaje.ebean.FutureRowCount; -import com.avaje.ebean.Query; - -/** - * Future implementation for the row count query. - */ -public class QueryFutureRowCount extends BaseFuture implements FutureRowCount { - - private final Query query; - - public QueryFutureRowCount(Query query, FutureTask futureTask) { - super(futureTask); - this.query = query; - } - - public Query getQuery() { - return query; - } - - public boolean cancel(boolean mayInterruptIfRunning) { - query.cancel(); - return super.cancel(mayInterruptIfRunning); - } - - -} +package com.avaje.ebeaninternal.server.query; + +import java.util.concurrent.FutureTask; + +import com.avaje.ebean.FutureRowCount; +import com.avaje.ebean.Query; + +/** + * Future implementation for the row count query. + */ +public class QueryFutureRowCount extends BaseFuture implements FutureRowCount { + + private final Query query; + + public QueryFutureRowCount(Query query, FutureTask futureTask) { + super(futureTask); + this.query = query; + } + + public Query getQuery() { + return query; + } + + public boolean cancel(boolean mayInterruptIfRunning) { + query.cancel(); + return super.cancel(mayInterruptIfRunning); + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/RawSqlSelectClauseBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/query/RawSqlSelectClauseBuilder.java index 46c58268a..d786f14fb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/RawSqlSelectClauseBuilder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/RawSqlSelectClauseBuilder.java @@ -1,118 +1,99 @@ -/** - * 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.query; - -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebean.config.dbplatform.SqlLimitResponse; -import com.avaje.ebean.config.dbplatform.SqlLimiter; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.DRawSqlSelect; -import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; -import com.avaje.ebeaninternal.server.deploy.DeployParser; -import com.avaje.ebeaninternal.server.persist.Binder; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest; - -import javax.persistence.PersistenceException; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Factory for SqlSelectClause based on raw sql. - *

      - * Its job is to execute the sql, read the meta data to determine the columns to - * bean property mapping. - *

      - */ -public class RawSqlSelectClauseBuilder { - - private static final Logger logger = Logger.getLogger(RawSqlSelectClauseBuilder.class.getName()); - - private final Binder binder; - - private final SqlLimiter dbQueryLimiter; - private final DatabasePlatform dbPlatform; - - public RawSqlSelectClauseBuilder(DatabasePlatform dbPlatform, Binder binder) { - - this.binder = binder; - this.dbQueryLimiter = dbPlatform.getSqlLimiter(); - this.dbPlatform = dbPlatform; - } - - /** - * Build based on the includes and using the BeanJoinTree. - */ - public CQuery build(OrmQueryRequest request) throws PersistenceException { - - SpiQuery query = request.getQuery(); - BeanDescriptor desc = request.getBeanDescriptor(); - - DeployNamedQuery namedQuery = desc.getNamedQuery(query.getName()); - DRawSqlSelect sqlSelect = namedQuery.getSqlSelect(); - - // create a parser for this specific SqlSelect... has to be really - // as each SqlSelect could have different table alias etc - DeployParser parser = sqlSelect.createDeployPropertyParser(); - - CQueryPredicates predicates = new CQueryPredicates(binder, request); - // prepare and convert logical property names to dbColumns etc - predicates.prepareRawSql(parser); - - SqlTreeAlias alias = new SqlTreeAlias(sqlSelect.getTableAlias()); - predicates.parseTableAlias(alias); - - String sql = null; - try { - - boolean includeRowNumColumn = false; - String orderBy = sqlSelect.getOrderBy(predicates); - - // build the actual sql String - sql = sqlSelect.buildSql(orderBy, predicates, request); - if (query.hasMaxRowsOrFirstRow() && dbQueryLimiter != null) { - // wrap with a limit offset or ROW_NUMBER() etc - SqlLimitResponse limitSql = dbQueryLimiter.limit(new OrmQueryLimitRequest(sql, orderBy, query, dbPlatform)); - includeRowNumColumn = limitSql.isIncludesRowNumberColumn(); - - sql = limitSql.getSql(); - } else { - // add back select keyword - // ... was removed to support dbQueryLimiter - sql = "select " + sql; - } - - SqlTree sqlTree = sqlSelect.getSqlTree(); - - CQueryPlan queryPlan = new CQueryPlan(sql, sqlTree, true, includeRowNumColumn, ""); - CQuery compiledQuery = new CQuery(request, predicates, queryPlan); - - return compiledQuery; - - } catch (Exception e) { - - String msg = "Error with " + desc.getFullName() + " query:\r" + sql; - logger.log(Level.SEVERE, msg); - throw new PersistenceException(e); - } - } - -} +package com.avaje.ebeaninternal.server.query; + +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.config.dbplatform.SqlLimitResponse; +import com.avaje.ebean.config.dbplatform.SqlLimiter; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.DRawSqlSelect; +import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; +import com.avaje.ebeaninternal.server.deploy.DeployParser; +import com.avaje.ebeaninternal.server.persist.Binder; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest; + +import javax.persistence.PersistenceException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Factory for SqlSelectClause based on raw sql. + *

      + * Its job is to execute the sql, read the meta data to determine the columns to + * bean property mapping. + *

      + */ +public class RawSqlSelectClauseBuilder { + + private static final Logger logger = Logger.getLogger(RawSqlSelectClauseBuilder.class.getName()); + + private final Binder binder; + + private final SqlLimiter dbQueryLimiter; + private final DatabasePlatform dbPlatform; + + public RawSqlSelectClauseBuilder(DatabasePlatform dbPlatform, Binder binder) { + + this.binder = binder; + this.dbQueryLimiter = dbPlatform.getSqlLimiter(); + this.dbPlatform = dbPlatform; + } + + /** + * Build based on the includes and using the BeanJoinTree. + */ + public CQuery build(OrmQueryRequest request) throws PersistenceException { + + SpiQuery query = request.getQuery(); + BeanDescriptor desc = request.getBeanDescriptor(); + + DeployNamedQuery namedQuery = desc.getNamedQuery(query.getName()); + DRawSqlSelect sqlSelect = namedQuery.getSqlSelect(); + + // create a parser for this specific SqlSelect... has to be really + // as each SqlSelect could have different table alias etc + DeployParser parser = sqlSelect.createDeployPropertyParser(); + + CQueryPredicates predicates = new CQueryPredicates(binder, request); + // prepare and convert logical property names to dbColumns etc + predicates.prepareRawSql(parser); + + SqlTreeAlias alias = new SqlTreeAlias(sqlSelect.getTableAlias()); + predicates.parseTableAlias(alias); + + String sql = null; + try { + + boolean includeRowNumColumn = false; + String orderBy = sqlSelect.getOrderBy(predicates); + + // build the actual sql String + sql = sqlSelect.buildSql(orderBy, predicates, request); + if (query.hasMaxRowsOrFirstRow() && dbQueryLimiter != null) { + // wrap with a limit offset or ROW_NUMBER() etc + SqlLimitResponse limitSql = dbQueryLimiter.limit(new OrmQueryLimitRequest(sql, orderBy, query, dbPlatform)); + includeRowNumColumn = limitSql.isIncludesRowNumberColumn(); + + sql = limitSql.getSql(); + } else { + // add back select keyword + // ... was removed to support dbQueryLimiter + sql = "select " + sql; + } + + SqlTree sqlTree = sqlSelect.getSqlTree(); + + CQueryPlan queryPlan = new CQueryPlan(sql, sqlTree, true, includeRowNumColumn, ""); + CQuery compiledQuery = new CQuery(request, predicates, queryPlan); + + return compiledQuery; + + } catch (Exception e) { + + String msg = "Error with " + desc.getFullName() + " query:\r" + sql; + logger.log(Level.SEVERE, msg); + throw new PersistenceException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlBeanLoad.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlBeanLoad.java index ecf7f972f..4539995fc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlBeanLoad.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlBeanLoad.java @@ -1,142 +1,123 @@ -/** - * 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.query; - -import java.sql.SQLException; -import java.util.Set; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebeaninternal.api.SpiQuery.Mode; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.DbReadContext; - -/** - * Controls the loading of property data into a bean. - *

      - * Takes into account the differences of lazy loading and - * partial objects. - *

      - */ -public class SqlBeanLoad { - - private final DbReadContext ctx; - private final Object bean; - private final Class type; - private final Object originalOldValues; - private final boolean isLazyLoad; - - // set of properties to exclude from the refresh because it is - // not a refresh but rather a lazyLoading event. - private final Set excludes; - private final boolean setOriginalOldValues; - - private final boolean rawSql; - - public SqlBeanLoad(DbReadContext ctx, Class type, Object bean, Mode queryMode) { - - this.ctx = ctx; - this.rawSql = ctx.isRawSql(); - this.type = type; - this.isLazyLoad = queryMode.equals(Mode.LAZYLOAD_BEAN); - this.bean = bean; - - if (bean instanceof EntityBean) { - EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept(); - - this.excludes = isLazyLoad ? ebi.getLoadedProps() : null; - 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(); - } else { - originalOldValues = null; - } - this.setOriginalOldValues = originalOldValues != null; - } else { - this.excludes = null; - this.originalOldValues = null; - this.setOriginalOldValues = false; - } - } - - /** - * Return true if this is a lazy loading. - */ - public boolean isLazyLoad() { - return isLazyLoad; - } - - /** - * Increment the resultSet index 1. - */ - public void loadIgnore(int increment) { - ctx.getDataReader().incrementPos(increment); - } - - public Object load(BeanProperty prop) throws SQLException { - - if (!rawSql && prop.isTransient()){ - return null; - } - - if ((bean == null) - || (excludes != null && excludes.contains(prop.getName())) - || (type != null && !prop.isAssignableFrom(type))){ - - // ignore this property - // ... null: bean already in persistence context - // ... excludes: partial bean that is lazy loading - // ... type: inheritance and not assignable to this instance - - prop.loadIgnore(ctx); - return null; - } - - try { - Object dbVal = prop.read(ctx); - if (isLazyLoad){ - prop.setValue(bean, dbVal); - } else { - prop.setValueIntercept(bean, dbVal); - } - if (setOriginalOldValues){ - // maintain original oldValues for partially loaded bean - prop.setValue(originalOldValues, dbVal); - } - return dbVal; - - } catch (Exception e) { - String msg = "Error loading on " + prop.getFullBeanName(); - throw new PersistenceException(msg, e); - } - } - - public void loadAssocMany(BeanPropertyAssocMany prop) { - - // do nothing, as a lazy loading BeanCollection 'reference' - // is created and registered with the loading context - // in SqlTreeNodeBean.createListProxies() - } -} +package com.avaje.ebeaninternal.server.query; + +import java.sql.SQLException; +import java.util.Set; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebeaninternal.api.SpiQuery.Mode; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.DbReadContext; + +/** + * Controls the loading of property data into a bean. + *

      + * Takes into account the differences of lazy loading and + * partial objects. + *

      + */ +public class SqlBeanLoad { + + private final DbReadContext ctx; + private final Object bean; + private final Class type; + private final Object originalOldValues; + private final boolean isLazyLoad; + + // set of properties to exclude from the refresh because it is + // not a refresh but rather a lazyLoading event. + private final Set excludes; + private final boolean setOriginalOldValues; + + private final boolean rawSql; + + public SqlBeanLoad(DbReadContext ctx, Class type, Object bean, Mode queryMode) { + + this.ctx = ctx; + this.rawSql = ctx.isRawSql(); + this.type = type; + this.isLazyLoad = queryMode.equals(Mode.LAZYLOAD_BEAN); + this.bean = bean; + + if (bean instanceof EntityBean) { + EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept(); + + this.excludes = isLazyLoad ? ebi.getLoadedProps() : null; + 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(); + } else { + originalOldValues = null; + } + this.setOriginalOldValues = originalOldValues != null; + } else { + this.excludes = null; + this.originalOldValues = null; + this.setOriginalOldValues = false; + } + } + + /** + * Return true if this is a lazy loading. + */ + public boolean isLazyLoad() { + return isLazyLoad; + } + + /** + * Increment the resultSet index 1. + */ + public void loadIgnore(int increment) { + ctx.getDataReader().incrementPos(increment); + } + + public Object load(BeanProperty prop) throws SQLException { + + if (!rawSql && prop.isTransient()){ + return null; + } + + if ((bean == null) + || (excludes != null && excludes.contains(prop.getName())) + || (type != null && !prop.isAssignableFrom(type))){ + + // ignore this property + // ... null: bean already in persistence context + // ... excludes: partial bean that is lazy loading + // ... type: inheritance and not assignable to this instance + + prop.loadIgnore(ctx); + return null; + } + + try { + Object dbVal = prop.read(ctx); + if (isLazyLoad){ + prop.setValue(bean, dbVal); + } else { + prop.setValueIntercept(bean, dbVal); + } + if (setOriginalOldValues){ + // maintain original oldValues for partially loaded bean + prop.setValue(originalOldValues, dbVal); + } + return dbVal; + + } catch (Exception e) { + String msg = "Error loading on " + prop.getFullBeanName(); + throw new PersistenceException(msg, e); + } + } + + public void loadAssocMany(BeanPropertyAssocMany prop) { + + // do nothing, as a lazy loading BeanCollection 'reference' + // is created and registered with the loading context + // in SqlTreeNodeBean.createListProxies() + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java index e9921e2b9..6eeabdd53 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTree.java @@ -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.query; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; - -/** - * Represents the SELECT clause part of the SQL query. - */ -public class SqlTree { - - private SqlTreeNode rootNode; - - - /** - * Property if resultSet contains master and detail rows. - */ - private BeanPropertyAssocMany manyProperty; - private String manyPropertyName; - private ElPropertyValue manyPropEl; - - private Set includes; - - /** - * Summary of the select being generated. - */ - private String summary; - - private String selectSql; - - private String fromSql; - - /** - * Encrypted Properties require additional binding. - */ - private BeanProperty[] encryptedProps; - - /** - * Where clause for inheritance. - */ - private String inheritanceWhereSql; - - - /** - * Create the SqlSelectClause. - */ - public SqlTree() { - } - - public List buildSelectExpressionChain() { - ArrayList list = new ArrayList(); - rootNode.buildSelectExpressionChain(list); - return list; - } - - /** - * Return the includes. Associated beans lists etc. - */ - public Set getIncludes() { - return includes; - } - - /** - * Set the association includes (Ones and Many's). - */ - public void setIncludes(Set includes) { - this.includes = includes; - } - - /** - * Set the manyProperty used for this query. - */ - public void setManyProperty(BeanPropertyAssocMany manyProperty, String manyPropertyName, ElPropertyValue manyPropEl) { - this.manyProperty = manyProperty; - this.manyPropertyName = manyPropertyName; - this.manyPropEl = manyPropEl; - } - - /** - * Return the String for the actual SQL. - */ - public String getSelectSql() { - return selectSql; - } - - /** - * Set the select sql clause. - */ - public void setSelectSql(String selectSql) { - this.selectSql = selectSql; - } - - - public String getFromSql() { - return fromSql; - } - - public void setFromSql(String fromSql) { - this.fromSql = fromSql; - } - - /** - * Return the where clause for inheritance. - */ - public String getInheritanceWhereSql() { - return inheritanceWhereSql; - } - - /** - * Set where clause(s) for inheritance. - */ - public void setInheritanceWhereSql(String whereSql) { - this.inheritanceWhereSql = whereSql; - } - - /** - * Set the summary description of the query. - */ - public void setSummary(String summary) { - this.summary = summary; - } - - /** - * Return a summary of the select clause. - */ - public String getSummary() { - return summary; - } - - public SqlTreeNode getRootNode() { - return rootNode; - } - - public void setRootNode(SqlTreeNode rootNode) { - this.rootNode = rootNode; - } - - /** - * Return the property that is associated with the many. There can only be - * one per SqlSelect. This can be null. - */ - public BeanPropertyAssocMany getManyProperty() { - return manyProperty; - } - - public String getManyPropertyName() { - return manyPropertyName; - } - - public ElPropertyValue getManyPropertyEl() { - return manyPropEl; - } - - /** - * Return true if this query includes a Many association. - */ - public boolean isManyIncluded() { - return (manyProperty != null); - } - - public BeanProperty[] getEncryptedProps() { - return encryptedProps; - } - - public void setEncryptedProps(BeanProperty[] encryptedProps) { - this.encryptedProps = encryptedProps; - } -} +package com.avaje.ebeaninternal.server.query; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; + +/** + * Represents the SELECT clause part of the SQL query. + */ +public class SqlTree { + + private SqlTreeNode rootNode; + + + /** + * Property if resultSet contains master and detail rows. + */ + private BeanPropertyAssocMany manyProperty; + private String manyPropertyName; + private ElPropertyValue manyPropEl; + + private Set includes; + + /** + * Summary of the select being generated. + */ + private String summary; + + private String selectSql; + + private String fromSql; + + /** + * Encrypted Properties require additional binding. + */ + private BeanProperty[] encryptedProps; + + /** + * Where clause for inheritance. + */ + private String inheritanceWhereSql; + + + /** + * Create the SqlSelectClause. + */ + public SqlTree() { + } + + public List buildSelectExpressionChain() { + ArrayList list = new ArrayList(); + rootNode.buildSelectExpressionChain(list); + return list; + } + + /** + * Return the includes. Associated beans lists etc. + */ + public Set getIncludes() { + return includes; + } + + /** + * Set the association includes (Ones and Many's). + */ + public void setIncludes(Set includes) { + this.includes = includes; + } + + /** + * Set the manyProperty used for this query. + */ + public void setManyProperty(BeanPropertyAssocMany manyProperty, String manyPropertyName, ElPropertyValue manyPropEl) { + this.manyProperty = manyProperty; + this.manyPropertyName = manyPropertyName; + this.manyPropEl = manyPropEl; + } + + /** + * Return the String for the actual SQL. + */ + public String getSelectSql() { + return selectSql; + } + + /** + * Set the select sql clause. + */ + public void setSelectSql(String selectSql) { + this.selectSql = selectSql; + } + + + public String getFromSql() { + return fromSql; + } + + public void setFromSql(String fromSql) { + this.fromSql = fromSql; + } + + /** + * Return the where clause for inheritance. + */ + public String getInheritanceWhereSql() { + return inheritanceWhereSql; + } + + /** + * Set where clause(s) for inheritance. + */ + public void setInheritanceWhereSql(String whereSql) { + this.inheritanceWhereSql = whereSql; + } + + /** + * Set the summary description of the query. + */ + public void setSummary(String summary) { + this.summary = summary; + } + + /** + * Return a summary of the select clause. + */ + public String getSummary() { + return summary; + } + + public SqlTreeNode getRootNode() { + return rootNode; + } + + public void setRootNode(SqlTreeNode rootNode) { + this.rootNode = rootNode; + } + + /** + * Return the property that is associated with the many. There can only be + * one per SqlSelect. This can be null. + */ + public BeanPropertyAssocMany getManyProperty() { + return manyProperty; + } + + public String getManyPropertyName() { + return manyPropertyName; + } + + public ElPropertyValue getManyPropertyEl() { + return manyPropEl; + } + + /** + * Return true if this query includes a Many association. + */ + public boolean isManyIncluded() { + return (manyProperty != null); + } + + public BeanProperty[] getEncryptedProps() { + return encryptedProps; + } + + public void setEncryptedProps(BeanProperty[] encryptedProps) { + this.encryptedProps = encryptedProps; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java index a56f87443..c63317fed 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeBuilder.java @@ -1,683 +1,664 @@ -/** - * 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.query; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebeaninternal.api.ManyWhereJoins; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.api.SpiQuery.Type; -import com.avaje.ebeaninternal.server.core.OrmQueryRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.deploy.InheritInfo; -import com.avaje.ebeaninternal.server.deploy.TableJoin; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties; - -/** - * Factory for SqlTree. - */ -public class SqlTreeBuilder { - - private static final Logger logger = Logger.getLogger(SqlTreeBuilder.class.getName()); - - private final SpiQuery query; - - private final BeanDescriptor desc; - - private final OrmQueryDetail queryDetail; - - private final StringBuilder summary = new StringBuilder(); - - private final CQueryPredicates predicates; - - private final boolean subQuery; - - /** - * Property if resultSet contains master and detail rows. - */ - private BeanPropertyAssocMany manyProperty; - - private String manyPropertyName; - - private final SqlTreeAlias alias; - - private final DefaultDbSqlContext ctx; - - private final HashSet selectIncludes = new HashSet(); - - private final ManyWhereJoins manyWhereJoins; - - private final TableJoin includeJoin; - - private final boolean rawSql; - - /** - * Construct for RawSql query. - */ - public SqlTreeBuilder(OrmQueryRequest request, CQueryPredicates predicates, OrmQueryDetail queryDetail) { - - this.rawSql = true; - this.desc = request.getBeanDescriptor(); - this.query = null; - this.subQuery = false; - this.queryDetail = queryDetail; - this.predicates = predicates; - - this.includeJoin = null; - this.manyWhereJoins = null; - this.alias = null; - this.ctx = null; - } - - /** - * The predicates are used to determine if 'extra' joins are required to - * support the where and/or order by clause. If so these extra joins are - * added to the root node. - */ - public SqlTreeBuilder(String tableAliasPlaceHolder, String columnAliasPrefix, OrmQueryRequest request, CQueryPredicates predicates) { - - this.rawSql = false; - this.desc = request.getBeanDescriptor(); - this.query = request.getQuery(); - - this.subQuery = Type.SUBQUERY.equals(query.getType()); - this.includeJoin = query.getIncludeTableJoin(); - this.manyWhereJoins = query.getManyWhereJoins(); - this.queryDetail = query.getDetail(); - - this.predicates = predicates; - this.alias = new SqlTreeAlias(request.getBeanDescriptor().getBaseTableAlias()); - this.ctx = new DefaultDbSqlContext(alias, tableAliasPlaceHolder, columnAliasPrefix, !subQuery); - } - - /** - * Build based on the includes and using the BeanJoinTree. - */ - public SqlTree build() { - - SqlTree sqlTree = new SqlTree(); - - summary.append(desc.getName()); - - // build the appropriate chain of SelectAdapter's - buildRoot(desc, sqlTree); - - // build the actual String - SqlTreeNode rootNode = sqlTree.getRootNode(); - - if (!rawSql){ - sqlTree.setSelectSql(buildSelectClause(rootNode)); - sqlTree.setFromSql(buildFromClause(rootNode)); - sqlTree.setInheritanceWhereSql(buildWhereClause(rootNode)); - sqlTree.setEncryptedProps(ctx.getEncryptedProps()); - } - sqlTree.setIncludes(queryDetail.getIncludes()); - sqlTree.setSummary(summary.toString()); - - if (manyPropertyName != null){ - ElPropertyValue manyPropEl = desc.getElGetValue(manyPropertyName); - sqlTree.setManyProperty(manyProperty, manyPropertyName, manyPropEl); - } - - return sqlTree; - } - - private String buildSelectClause(SqlTreeNode rootNode) { - - if (rawSql){ - return "Not Used"; - } - rootNode.appendSelect(ctx, subQuery); - - String selectSql = ctx.getContent(); - - // trim off the first comma - if (selectSql.length() >= SqlTreeNode.COMMA.length()) { - selectSql = selectSql.substring(SqlTreeNode.COMMA.length()); - } - - return selectSql; - } - - private String buildWhereClause(SqlTreeNode rootNode) { - - if (rawSql){ - return "Not Used"; - } - rootNode.appendWhere(ctx); - return ctx.getContent(); - } - - private String buildFromClause(SqlTreeNode rootNode) { - - if (rawSql){ - return "Not Used"; - } - rootNode.appendFrom(ctx, false); - return ctx.getContent(); - } - - private void buildRoot(BeanDescriptor desc, SqlTree sqlTree) { - - SqlTreeNode selectRoot = buildSelectChain(null, null, desc, null); - sqlTree.setRootNode(selectRoot); - - if (!rawSql){ - alias.addJoin(queryDetail.getIncludes(), desc); - alias.addJoin(predicates.getPredicateIncludes(), desc); - alias.addManyWhereJoins(manyWhereJoins.getJoins()); - - // build set of table alias - alias.buildAlias(); - - predicates.parseTableAlias(alias); - } - } - - /** - * Recursively build the query tree depending on what leaves in the tree - * should be included. - */ - private SqlTreeNode buildSelectChain(String prefix, BeanPropertyAssoc prop, BeanDescriptor desc, List joinList) { - - List myJoinList = new ArrayList(); - - BeanPropertyAssocOne[] ones = desc.propertiesOne(); - for (int i = 0; i < ones.length; i++) { - String propPrefix = SplitName.add(prefix, ones[i].getName()); - if (isIncludeBean(propPrefix, ones[i])) { - selectIncludes.add(propPrefix); - buildSelectChain(propPrefix, ones[i], ones[i].getTargetDescriptor(), myJoinList); - } - } - - BeanPropertyAssocMany[] manys = desc.propertiesMany(); - for (int i = 0; i < manys.length; i++) { - String propPrefix = SplitName.add(prefix, manys[i].getName()); - if (isIncludeMany(prefix, propPrefix, manys[i])) { - selectIncludes.add(propPrefix); - buildSelectChain(propPrefix, manys[i], manys[i].getTargetDescriptor(), myJoinList); - } - } - - if (prefix == null && !rawSql) { - addManyWhereJoins(myJoinList); - } - - SqlTreeNode selectNode = buildNode(prefix, prop, desc, myJoinList); - if (joinList != null) { - joinList.add(selectNode); - } - return selectNode; - } - - /** - * Add joins used to support where clause predicates on 'many' properties. - *

      - * These joins are effectively independent of any fetch joins on 'many' properties. - *

      - */ - private void addManyWhereJoins(List myJoinList) { - - Set includes = manyWhereJoins.getJoins(); - for (String joinProp : includes) { - - BeanPropertyAssoc beanProperty = (BeanPropertyAssoc) desc.getBeanPropertyFromPath(joinProp); - SqlTreeNodeManyWhereJoin nodeJoin = new SqlTreeNodeManyWhereJoin(joinProp, beanProperty); - myJoinList.add(nodeJoin); - } - } - - private SqlTreeNode buildNode(String prefix, BeanPropertyAssoc prop, BeanDescriptor desc, List myList) { - - OrmQueryProperties queryProps = queryDetail.getChunk(prefix, false); - - SqlTreeProperties props = getBaseSelect(desc, queryProps); - - if (prefix == null) { - buildExtraJoins(desc, myList); - return new SqlTreeNodeRoot(desc, props, myList, !subQuery, includeJoin); - - } else if (prop instanceof BeanPropertyAssocMany) { - return new SqlTreeNodeManyRoot(prefix, (BeanPropertyAssocMany) prop, props, myList); - - } else { - return new SqlTreeNodeBean(prefix, prop, props, myList, true); - } - } - - /** - * Build extra joins to support properties used in where clause but not - * already in select clause. - */ - private void buildExtraJoins(BeanDescriptor desc, List myList) { - - if (rawSql){ - return; - } - - Set predicateIncludes = predicates.getPredicateIncludes(); - - if (predicateIncludes == null) { - return; - } - - // Note includes - basically means joins. - // The selectIncludes is the set of joins that are required to support - // the 'select' part of the query. We may need to add other joins to - // support the predicates or order by clauses. - - // remove ManyWhereJoins from the predicateIncludes - predicateIncludes.removeAll(manyWhereJoins.getJoins()); - - // look for predicateIncludes that are not in selectIncludes and add - // them as extra joins to the query - IncludesDistiller extraJoinDistill = new IncludesDistiller(desc, selectIncludes, predicateIncludes); - - Collection extraJoins = extraJoinDistill.getExtraJoinRootNodes(); - if (extraJoins.isEmpty()) { - return; - - } else { - // add extra joins required to support predicates - // and/or order by clause - Iterator it = extraJoins.iterator(); - while (it.hasNext()) { - SqlTreeNodeExtraJoin extraJoin = it.next(); - myList.add(extraJoin); - - if (extraJoin.isManyJoin()) { - // as we are now going to join to the many then we need - // to add the distinct to the sql query to stop duplicate - // rows... - query.setDistinct(true); - } - } - } - } - - /** - * A subQuery has slightly different rules in that it just generates SQL - * (into the where clause) and its properties are not required to read the - * resultSet etc. - *

      - * This means it can included individual properties of an embedded bean. - *

      - */ - private void addPropertyToSubQuery(SqlTreeProperties selectProps, BeanDescriptor desc, OrmQueryProperties queryProps, String propName) { - - BeanProperty p = desc.findBeanProperty(propName); - if (p == null) { - logger.log(Level.SEVERE, "property [" + propName + "]not found on " + desc + " for query - excluding it."); - - } - else if (p instanceof BeanPropertyAssoc && p.isEmbedded()) { - // if the property is embedded we need to lookup the real column name - int pos = propName.indexOf("."); - if (pos > -1) { - String name = propName.substring(pos + 1); - p = ((BeanPropertyAssoc) p).getTargetDescriptor().findBeanProperty(name); - } - } - - selectProps.add(p); - } - - private void addProperty(SqlTreeProperties selectProps, BeanDescriptor desc, OrmQueryProperties queryProps, String propName) { - - if (subQuery) { - addPropertyToSubQuery(selectProps, desc, queryProps, propName); - return; - } - - int basePos = propName.indexOf('.'); - if (basePos > -1) { - // property on an embedded bean. Embedded beans do not yet - // support being partially populated so we include the - // 'base' property and make sure we only do that once - String baseName = propName.substring(0, basePos); - - // make sure we only included the base/embedded bean once - if (!selectProps.containsProperty(baseName)) { - BeanProperty p = desc.findBeanProperty(baseName); - if (p == null) { - String m = "property [" + propName + "] not found on " + desc + " for query - excluding it."; - logger.log(Level.SEVERE, m); - - } else if (p.isEmbedded()) { - // add the embedded bean (and effectively - // all its properties) - selectProps.add(p); - // also make sure it is added to included properties - // to avoid unnecessary lazy loading - selectProps.getIncludedProperties().add(baseName); - - } else { - String m = "property [" + p.getFullBeanName() - + "] expected to be an embedded bean for query - excluding it."; - logger.log(Level.SEVERE, m); - } - } - - } else { - // find the property including searching the - // sub class hierarchy if required - BeanProperty p = desc.findBeanProperty(propName); - if (p == null) { - logger.log(Level.SEVERE, "property [" + propName + "] not found on " + desc - + " for query - excluding it."); - - } else if (p.isId()) { - // do not bother to include id for normal queries as the - // id is always added (except for subQueries) - - } else if (p instanceof BeanPropertyAssoc) { - // need to check if this property should be - // excluded. This occurs when this property is - // included as a bean join. With a bean join - // the property should be excluded as the bean - // join has its own node in the SqlTree. - if (!queryProps.isIncludedBeanJoin(p.getName())) { - // include the property... which basically - // means include the foreign key column(s) - selectProps.add(p); - } - } else { - selectProps.add(p); - } - } - } - - private SqlTreeProperties getBaseSelectPartial(BeanDescriptor desc, OrmQueryProperties queryProps) { - - SqlTreeProperties selectProps = new SqlTreeProperties(); - selectProps.setReadOnly(queryProps.isReadOnly()); - selectProps.setIncludedProperties(queryProps.getAllIncludedProperties()); - - // add properties in the order in which they appear - // in the query. Gives predictable sql/properties for - // use with SqlSelect type queries. - - // Also note that this can include transient properties. - // This makes sense for transient properties used to - // hold sum() count() type values (with SqlSelect) - Iterator it = queryProps.getSelectProperties(); - while (it.hasNext()) { - String propName = it.next(); - if (propName.length() > 0) { - addProperty(selectProps, desc, queryProps, propName); - } - } - - return selectProps; - } - - private SqlTreeProperties getBaseSelect(BeanDescriptor desc, OrmQueryProperties queryProps) { - - boolean partial = queryProps != null && !queryProps.allProperties(); - if (partial) { - return getBaseSelectPartial(desc, queryProps); - } - - SqlTreeProperties selectProps = new SqlTreeProperties(); - - // normal simple properties of the bean - selectProps.add(desc.propertiesBaseScalar()); - selectProps.add(desc.propertiesBaseCompound()); - selectProps.add(desc.propertiesEmbedded()); - - BeanPropertyAssocOne[] propertiesOne = desc.propertiesOne(); - for (int i = 0; i < propertiesOne.length; i++) { - if (queryProps != null && queryProps.isIncludedBeanJoin(propertiesOne[i].getName())) { - // if it is a joined bean... then don't add the property - // as it will have its own entire Node in the SqlTree - } else { - selectProps.add(propertiesOne[i]); - } - } - - selectProps.setTableJoins(desc.tableJoins()); - - InheritInfo inheritInfo = desc.getInheritInfo(); - if (inheritInfo != null) { - // add sub type properties - inheritInfo.addChildrenProperties(selectProps); - - } - return selectProps; - } - - /** - * Return true if this many node should be included in the query. - */ - private boolean isIncludeMany(String prefix, String propName, BeanPropertyAssocMany manyProp) { - - if (queryDetail.isJoinsEmpty()) { - return false; - } - - if (queryDetail.includes(propName)) { - - if (manyProperty != null) { - // only one many associated allowed to be included in fetch - if (logger.isLoggable(Level.FINE)) { - String msg = "Not joining [" + propName + "] as already joined to a Many[" + manyProperty + "]."; - logger.fine(msg); - } - return false; - } - - manyProperty = manyProp; - manyPropertyName = propName; - summary.append(" +many:").append(propName); - return true; - } - return false; - } - - /** - * Test to see if we are including this node into the query. - *

      - * Return true if this node is FULLY included resulting in table join. If - * the node is not included but its parent has been included then a "bean - * proxy" is added and false is returned. - *

      - */ - private boolean isIncludeBean(String prefix, BeanPropertyAssocOne prop) { - - if (queryDetail.includes(prefix)) { - // explicitly included - summary.append(", ").append(prefix); - String[] splitNames = SplitName.split(prefix); - queryDetail.includeBeanJoin(splitNames[0], splitNames[1]); - return true; - } - - return false; - } - - /** - * Takes the select includes and the predicates includes and determines the - * extra joins required to support the predicates (that are not already - * supported by the select includes). - *

      - * This returns ONLY the leaves. The joins for the leaves - *

      - */ - private static class IncludesDistiller { - - private final Set selectIncludes; - private final Set predicateIncludes; - - /** - * Contains the 'root' extra joins. We only return the roots back. - */ - private final Map joinRegister = new HashMap(); - - /** - * Register of all the extra join nodes. - */ - private final Map rootRegister = new HashMap(); - - private final BeanDescriptor desc; - - private IncludesDistiller(BeanDescriptor desc, Set selectIncludes, Set predicateIncludes) { - this.desc = desc; - this.selectIncludes = selectIncludes; - this.predicateIncludes = predicateIncludes; - } - - /** - * Build the collection of extra joins returning just the roots. - *

      - * each root returned here could contain a little tree of joins. This - * follows the more natural pattern and allows for forcing outer joins - * from a join to a 'many' down through the rest of its tree. - *

      - */ - private Collection getExtraJoinRootNodes() { - - String[] extras = findExtras(); - if (extras.length == 0) { - return rootRegister.values(); - } - - // sort so we process only getting the leaves - // excluding nodes between root and the leaf - Arrays.sort(extras); - - // reverse order so get the leaves first... - for (int i = 0; i < extras.length; i++) { - createExtraJoin(extras[i]); - } - - return rootRegister.values(); - } - - private void createExtraJoin(String includeProp) { - - SqlTreeNodeExtraJoin extraJoin = createJoinLeaf(includeProp); - if (extraJoin != null) { - // add the extra join... - - // find root of this extra join... linking back to the - // parents (creating the tree) as it goes. - SqlTreeNodeExtraJoin root = findExtraJoinRoot(includeProp, extraJoin); - - // register the root because these are the only ones we - // return back. - rootRegister.put(root.getName(), root); - } - } - - /** - * Create a SqlTreeNodeExtraJoin, register and return it. - */ - private SqlTreeNodeExtraJoin createJoinLeaf(String propertyName) { - - ElPropertyValue elGetValue = desc.getElGetValue(propertyName); - - if (elGetValue == null) { - // this can occur for master detail queries - // with concatenated keys (so not an error now) - return null; - } - BeanProperty beanProperty = elGetValue.getBeanProperty(); - if (beanProperty instanceof BeanPropertyAssoc) { - BeanPropertyAssoc assocProp = (BeanPropertyAssoc) beanProperty; - if (assocProp.isEmbedded()) { - // no extra join required for embedded beans - return null; - } - SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, assocProp); - joinRegister.put(propertyName, extraJoin); - return extraJoin; - } - return null; - } - - /** - * Find the root the this extra join tree. - *

      - * This may need to create a parent join implicitly if a predicate join - * 'skips' a level. e.g. where details.user.id = 1 (maybe join to - * details is not specified and is implicitly created. - *

      - */ - private SqlTreeNodeExtraJoin findExtraJoinRoot(String includeProp, SqlTreeNodeExtraJoin childJoin) { - - int dotPos = includeProp.lastIndexOf('.'); - if (dotPos == -1) { - // no parent possible(parent is root) - return childJoin; - - } else { - // look in register ... - String parentPropertyName = includeProp.substring(0, dotPos); - if (selectIncludes.contains(parentPropertyName)) { - // parent already handled by select - return childJoin; - } - - SqlTreeNodeExtraJoin parentJoin = joinRegister.get(parentPropertyName); - if (parentJoin == null) { - // we need to create this the parent implicitly... - parentJoin = createJoinLeaf(parentPropertyName); - } - - parentJoin.addChild(childJoin); - return findExtraJoinRoot(parentPropertyName, parentJoin); - } - } - - /** - * Find the extra joins required by predicates and not already taken - * care of by the select. - */ - private String[] findExtras() { - - List extras = new ArrayList(); - - for (String predProp : predicateIncludes) { - if (!selectIncludes.contains(predProp)) { - extras.add(predProp); - } - } - return extras.toArray(new String[extras.size()]); - } - - } -} +package com.avaje.ebeaninternal.server.query; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebeaninternal.api.ManyWhereJoins; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.api.SpiQuery.Type; +import com.avaje.ebeaninternal.server.core.OrmQueryRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.deploy.TableJoin; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties; + +/** + * Factory for SqlTree. + */ +public class SqlTreeBuilder { + + private static final Logger logger = Logger.getLogger(SqlTreeBuilder.class.getName()); + + private final SpiQuery query; + + private final BeanDescriptor desc; + + private final OrmQueryDetail queryDetail; + + private final StringBuilder summary = new StringBuilder(); + + private final CQueryPredicates predicates; + + private final boolean subQuery; + + /** + * Property if resultSet contains master and detail rows. + */ + private BeanPropertyAssocMany manyProperty; + + private String manyPropertyName; + + private final SqlTreeAlias alias; + + private final DefaultDbSqlContext ctx; + + private final HashSet selectIncludes = new HashSet(); + + private final ManyWhereJoins manyWhereJoins; + + private final TableJoin includeJoin; + + private final boolean rawSql; + + /** + * Construct for RawSql query. + */ + public SqlTreeBuilder(OrmQueryRequest request, CQueryPredicates predicates, OrmQueryDetail queryDetail) { + + this.rawSql = true; + this.desc = request.getBeanDescriptor(); + this.query = null; + this.subQuery = false; + this.queryDetail = queryDetail; + this.predicates = predicates; + + this.includeJoin = null; + this.manyWhereJoins = null; + this.alias = null; + this.ctx = null; + } + + /** + * The predicates are used to determine if 'extra' joins are required to + * support the where and/or order by clause. If so these extra joins are + * added to the root node. + */ + public SqlTreeBuilder(String tableAliasPlaceHolder, String columnAliasPrefix, OrmQueryRequest request, CQueryPredicates predicates) { + + this.rawSql = false; + this.desc = request.getBeanDescriptor(); + this.query = request.getQuery(); + + this.subQuery = Type.SUBQUERY.equals(query.getType()); + this.includeJoin = query.getIncludeTableJoin(); + this.manyWhereJoins = query.getManyWhereJoins(); + this.queryDetail = query.getDetail(); + + this.predicates = predicates; + this.alias = new SqlTreeAlias(request.getBeanDescriptor().getBaseTableAlias()); + this.ctx = new DefaultDbSqlContext(alias, tableAliasPlaceHolder, columnAliasPrefix, !subQuery); + } + + /** + * Build based on the includes and using the BeanJoinTree. + */ + public SqlTree build() { + + SqlTree sqlTree = new SqlTree(); + + summary.append(desc.getName()); + + // build the appropriate chain of SelectAdapter's + buildRoot(desc, sqlTree); + + // build the actual String + SqlTreeNode rootNode = sqlTree.getRootNode(); + + if (!rawSql){ + sqlTree.setSelectSql(buildSelectClause(rootNode)); + sqlTree.setFromSql(buildFromClause(rootNode)); + sqlTree.setInheritanceWhereSql(buildWhereClause(rootNode)); + sqlTree.setEncryptedProps(ctx.getEncryptedProps()); + } + sqlTree.setIncludes(queryDetail.getIncludes()); + sqlTree.setSummary(summary.toString()); + + if (manyPropertyName != null){ + ElPropertyValue manyPropEl = desc.getElGetValue(manyPropertyName); + sqlTree.setManyProperty(manyProperty, manyPropertyName, manyPropEl); + } + + return sqlTree; + } + + private String buildSelectClause(SqlTreeNode rootNode) { + + if (rawSql){ + return "Not Used"; + } + rootNode.appendSelect(ctx, subQuery); + + String selectSql = ctx.getContent(); + + // trim off the first comma + if (selectSql.length() >= SqlTreeNode.COMMA.length()) { + selectSql = selectSql.substring(SqlTreeNode.COMMA.length()); + } + + return selectSql; + } + + private String buildWhereClause(SqlTreeNode rootNode) { + + if (rawSql){ + return "Not Used"; + } + rootNode.appendWhere(ctx); + return ctx.getContent(); + } + + private String buildFromClause(SqlTreeNode rootNode) { + + if (rawSql){ + return "Not Used"; + } + rootNode.appendFrom(ctx, false); + return ctx.getContent(); + } + + private void buildRoot(BeanDescriptor desc, SqlTree sqlTree) { + + SqlTreeNode selectRoot = buildSelectChain(null, null, desc, null); + sqlTree.setRootNode(selectRoot); + + if (!rawSql){ + alias.addJoin(queryDetail.getIncludes(), desc); + alias.addJoin(predicates.getPredicateIncludes(), desc); + alias.addManyWhereJoins(manyWhereJoins.getJoins()); + + // build set of table alias + alias.buildAlias(); + + predicates.parseTableAlias(alias); + } + } + + /** + * Recursively build the query tree depending on what leaves in the tree + * should be included. + */ + private SqlTreeNode buildSelectChain(String prefix, BeanPropertyAssoc prop, BeanDescriptor desc, List joinList) { + + List myJoinList = new ArrayList(); + + BeanPropertyAssocOne[] ones = desc.propertiesOne(); + for (int i = 0; i < ones.length; i++) { + String propPrefix = SplitName.add(prefix, ones[i].getName()); + if (isIncludeBean(propPrefix, ones[i])) { + selectIncludes.add(propPrefix); + buildSelectChain(propPrefix, ones[i], ones[i].getTargetDescriptor(), myJoinList); + } + } + + BeanPropertyAssocMany[] manys = desc.propertiesMany(); + for (int i = 0; i < manys.length; i++) { + String propPrefix = SplitName.add(prefix, manys[i].getName()); + if (isIncludeMany(prefix, propPrefix, manys[i])) { + selectIncludes.add(propPrefix); + buildSelectChain(propPrefix, manys[i], manys[i].getTargetDescriptor(), myJoinList); + } + } + + if (prefix == null && !rawSql) { + addManyWhereJoins(myJoinList); + } + + SqlTreeNode selectNode = buildNode(prefix, prop, desc, myJoinList); + if (joinList != null) { + joinList.add(selectNode); + } + return selectNode; + } + + /** + * Add joins used to support where clause predicates on 'many' properties. + *

      + * These joins are effectively independent of any fetch joins on 'many' properties. + *

      + */ + private void addManyWhereJoins(List myJoinList) { + + Set includes = manyWhereJoins.getJoins(); + for (String joinProp : includes) { + + BeanPropertyAssoc beanProperty = (BeanPropertyAssoc) desc.getBeanPropertyFromPath(joinProp); + SqlTreeNodeManyWhereJoin nodeJoin = new SqlTreeNodeManyWhereJoin(joinProp, beanProperty); + myJoinList.add(nodeJoin); + } + } + + private SqlTreeNode buildNode(String prefix, BeanPropertyAssoc prop, BeanDescriptor desc, List myList) { + + OrmQueryProperties queryProps = queryDetail.getChunk(prefix, false); + + SqlTreeProperties props = getBaseSelect(desc, queryProps); + + if (prefix == null) { + buildExtraJoins(desc, myList); + return new SqlTreeNodeRoot(desc, props, myList, !subQuery, includeJoin); + + } else if (prop instanceof BeanPropertyAssocMany) { + return new SqlTreeNodeManyRoot(prefix, (BeanPropertyAssocMany) prop, props, myList); + + } else { + return new SqlTreeNodeBean(prefix, prop, props, myList, true); + } + } + + /** + * Build extra joins to support properties used in where clause but not + * already in select clause. + */ + private void buildExtraJoins(BeanDescriptor desc, List myList) { + + if (rawSql){ + return; + } + + Set predicateIncludes = predicates.getPredicateIncludes(); + + if (predicateIncludes == null) { + return; + } + + // Note includes - basically means joins. + // The selectIncludes is the set of joins that are required to support + // the 'select' part of the query. We may need to add other joins to + // support the predicates or order by clauses. + + // remove ManyWhereJoins from the predicateIncludes + predicateIncludes.removeAll(manyWhereJoins.getJoins()); + + // look for predicateIncludes that are not in selectIncludes and add + // them as extra joins to the query + IncludesDistiller extraJoinDistill = new IncludesDistiller(desc, selectIncludes, predicateIncludes); + + Collection extraJoins = extraJoinDistill.getExtraJoinRootNodes(); + if (extraJoins.isEmpty()) { + return; + + } else { + // add extra joins required to support predicates + // and/or order by clause + Iterator it = extraJoins.iterator(); + while (it.hasNext()) { + SqlTreeNodeExtraJoin extraJoin = it.next(); + myList.add(extraJoin); + + if (extraJoin.isManyJoin()) { + // as we are now going to join to the many then we need + // to add the distinct to the sql query to stop duplicate + // rows... + query.setDistinct(true); + } + } + } + } + + /** + * A subQuery has slightly different rules in that it just generates SQL + * (into the where clause) and its properties are not required to read the + * resultSet etc. + *

      + * This means it can included individual properties of an embedded bean. + *

      + */ + private void addPropertyToSubQuery(SqlTreeProperties selectProps, BeanDescriptor desc, OrmQueryProperties queryProps, String propName) { + + BeanProperty p = desc.findBeanProperty(propName); + if (p == null) { + logger.log(Level.SEVERE, "property [" + propName + "]not found on " + desc + " for query - excluding it."); + + } + else if (p instanceof BeanPropertyAssoc && p.isEmbedded()) { + // if the property is embedded we need to lookup the real column name + int pos = propName.indexOf("."); + if (pos > -1) { + String name = propName.substring(pos + 1); + p = ((BeanPropertyAssoc) p).getTargetDescriptor().findBeanProperty(name); + } + } + + selectProps.add(p); + } + + private void addProperty(SqlTreeProperties selectProps, BeanDescriptor desc, OrmQueryProperties queryProps, String propName) { + + if (subQuery) { + addPropertyToSubQuery(selectProps, desc, queryProps, propName); + return; + } + + int basePos = propName.indexOf('.'); + if (basePos > -1) { + // property on an embedded bean. Embedded beans do not yet + // support being partially populated so we include the + // 'base' property and make sure we only do that once + String baseName = propName.substring(0, basePos); + + // make sure we only included the base/embedded bean once + if (!selectProps.containsProperty(baseName)) { + BeanProperty p = desc.findBeanProperty(baseName); + if (p == null) { + String m = "property [" + propName + "] not found on " + desc + " for query - excluding it."; + logger.log(Level.SEVERE, m); + + } else if (p.isEmbedded()) { + // add the embedded bean (and effectively + // all its properties) + selectProps.add(p); + // also make sure it is added to included properties + // to avoid unnecessary lazy loading + selectProps.getIncludedProperties().add(baseName); + + } else { + String m = "property [" + p.getFullBeanName() + + "] expected to be an embedded bean for query - excluding it."; + logger.log(Level.SEVERE, m); + } + } + + } else { + // find the property including searching the + // sub class hierarchy if required + BeanProperty p = desc.findBeanProperty(propName); + if (p == null) { + logger.log(Level.SEVERE, "property [" + propName + "] not found on " + desc + + " for query - excluding it."); + + } else if (p.isId()) { + // do not bother to include id for normal queries as the + // id is always added (except for subQueries) + + } else if (p instanceof BeanPropertyAssoc) { + // need to check if this property should be + // excluded. This occurs when this property is + // included as a bean join. With a bean join + // the property should be excluded as the bean + // join has its own node in the SqlTree. + if (!queryProps.isIncludedBeanJoin(p.getName())) { + // include the property... which basically + // means include the foreign key column(s) + selectProps.add(p); + } + } else { + selectProps.add(p); + } + } + } + + private SqlTreeProperties getBaseSelectPartial(BeanDescriptor desc, OrmQueryProperties queryProps) { + + SqlTreeProperties selectProps = new SqlTreeProperties(); + selectProps.setReadOnly(queryProps.isReadOnly()); + selectProps.setIncludedProperties(queryProps.getAllIncludedProperties()); + + // add properties in the order in which they appear + // in the query. Gives predictable sql/properties for + // use with SqlSelect type queries. + + // Also note that this can include transient properties. + // This makes sense for transient properties used to + // hold sum() count() type values (with SqlSelect) + Iterator it = queryProps.getSelectProperties(); + while (it.hasNext()) { + String propName = it.next(); + if (propName.length() > 0) { + addProperty(selectProps, desc, queryProps, propName); + } + } + + return selectProps; + } + + private SqlTreeProperties getBaseSelect(BeanDescriptor desc, OrmQueryProperties queryProps) { + + boolean partial = queryProps != null && !queryProps.allProperties(); + if (partial) { + return getBaseSelectPartial(desc, queryProps); + } + + SqlTreeProperties selectProps = new SqlTreeProperties(); + + // normal simple properties of the bean + selectProps.add(desc.propertiesBaseScalar()); + selectProps.add(desc.propertiesBaseCompound()); + selectProps.add(desc.propertiesEmbedded()); + + BeanPropertyAssocOne[] propertiesOne = desc.propertiesOne(); + for (int i = 0; i < propertiesOne.length; i++) { + if (queryProps != null && queryProps.isIncludedBeanJoin(propertiesOne[i].getName())) { + // if it is a joined bean... then don't add the property + // as it will have its own entire Node in the SqlTree + } else { + selectProps.add(propertiesOne[i]); + } + } + + selectProps.setTableJoins(desc.tableJoins()); + + InheritInfo inheritInfo = desc.getInheritInfo(); + if (inheritInfo != null) { + // add sub type properties + inheritInfo.addChildrenProperties(selectProps); + + } + return selectProps; + } + + /** + * Return true if this many node should be included in the query. + */ + private boolean isIncludeMany(String prefix, String propName, BeanPropertyAssocMany manyProp) { + + if (queryDetail.isJoinsEmpty()) { + return false; + } + + if (queryDetail.includes(propName)) { + + if (manyProperty != null) { + // only one many associated allowed to be included in fetch + if (logger.isLoggable(Level.FINE)) { + String msg = "Not joining [" + propName + "] as already joined to a Many[" + manyProperty + "]."; + logger.fine(msg); + } + return false; + } + + manyProperty = manyProp; + manyPropertyName = propName; + summary.append(" +many:").append(propName); + return true; + } + return false; + } + + /** + * Test to see if we are including this node into the query. + *

      + * Return true if this node is FULLY included resulting in table join. If + * the node is not included but its parent has been included then a "bean + * proxy" is added and false is returned. + *

      + */ + private boolean isIncludeBean(String prefix, BeanPropertyAssocOne prop) { + + if (queryDetail.includes(prefix)) { + // explicitly included + summary.append(", ").append(prefix); + String[] splitNames = SplitName.split(prefix); + queryDetail.includeBeanJoin(splitNames[0], splitNames[1]); + return true; + } + + return false; + } + + /** + * Takes the select includes and the predicates includes and determines the + * extra joins required to support the predicates (that are not already + * supported by the select includes). + *

      + * This returns ONLY the leaves. The joins for the leaves + *

      + */ + private static class IncludesDistiller { + + private final Set selectIncludes; + private final Set predicateIncludes; + + /** + * Contains the 'root' extra joins. We only return the roots back. + */ + private final Map joinRegister = new HashMap(); + + /** + * Register of all the extra join nodes. + */ + private final Map rootRegister = new HashMap(); + + private final BeanDescriptor desc; + + private IncludesDistiller(BeanDescriptor desc, Set selectIncludes, Set predicateIncludes) { + this.desc = desc; + this.selectIncludes = selectIncludes; + this.predicateIncludes = predicateIncludes; + } + + /** + * Build the collection of extra joins returning just the roots. + *

      + * each root returned here could contain a little tree of joins. This + * follows the more natural pattern and allows for forcing outer joins + * from a join to a 'many' down through the rest of its tree. + *

      + */ + private Collection getExtraJoinRootNodes() { + + String[] extras = findExtras(); + if (extras.length == 0) { + return rootRegister.values(); + } + + // sort so we process only getting the leaves + // excluding nodes between root and the leaf + Arrays.sort(extras); + + // reverse order so get the leaves first... + for (int i = 0; i < extras.length; i++) { + createExtraJoin(extras[i]); + } + + return rootRegister.values(); + } + + private void createExtraJoin(String includeProp) { + + SqlTreeNodeExtraJoin extraJoin = createJoinLeaf(includeProp); + if (extraJoin != null) { + // add the extra join... + + // find root of this extra join... linking back to the + // parents (creating the tree) as it goes. + SqlTreeNodeExtraJoin root = findExtraJoinRoot(includeProp, extraJoin); + + // register the root because these are the only ones we + // return back. + rootRegister.put(root.getName(), root); + } + } + + /** + * Create a SqlTreeNodeExtraJoin, register and return it. + */ + private SqlTreeNodeExtraJoin createJoinLeaf(String propertyName) { + + ElPropertyValue elGetValue = desc.getElGetValue(propertyName); + + if (elGetValue == null) { + // this can occur for master detail queries + // with concatenated keys (so not an error now) + return null; + } + BeanProperty beanProperty = elGetValue.getBeanProperty(); + if (beanProperty instanceof BeanPropertyAssoc) { + BeanPropertyAssoc assocProp = (BeanPropertyAssoc) beanProperty; + if (assocProp.isEmbedded()) { + // no extra join required for embedded beans + return null; + } + SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, assocProp); + joinRegister.put(propertyName, extraJoin); + return extraJoin; + } + return null; + } + + /** + * Find the root the this extra join tree. + *

      + * This may need to create a parent join implicitly if a predicate join + * 'skips' a level. e.g. where details.user.id = 1 (maybe join to + * details is not specified and is implicitly created. + *

      + */ + private SqlTreeNodeExtraJoin findExtraJoinRoot(String includeProp, SqlTreeNodeExtraJoin childJoin) { + + int dotPos = includeProp.lastIndexOf('.'); + if (dotPos == -1) { + // no parent possible(parent is root) + return childJoin; + + } else { + // look in register ... + String parentPropertyName = includeProp.substring(0, dotPos); + if (selectIncludes.contains(parentPropertyName)) { + // parent already handled by select + return childJoin; + } + + SqlTreeNodeExtraJoin parentJoin = joinRegister.get(parentPropertyName); + if (parentJoin == null) { + // we need to create this the parent implicitly... + parentJoin = createJoinLeaf(parentPropertyName); + } + + parentJoin.addChild(childJoin); + return findExtraJoinRoot(parentPropertyName, parentJoin); + } + } + + /** + * Find the extra joins required by predicates and not already taken + * care of by the select. + */ + private String[] findExtras() { + + List extras = new ArrayList(); + + for (String predProp : predicateIncludes) { + if (!selectIncludes.contains(predProp)) { + extras.add(predProp); + } + } + return extras.toArray(new String[extras.size()]); + } + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java index 3cd20392f..715a938ac 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeBean.java @@ -1,541 +1,522 @@ -/** - * 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.query; - -import com.avaje.ebean.bean.BeanCollection; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.api.SpiQuery.Mode; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.DbReadContext; -import com.avaje.ebeaninternal.server.deploy.DbSqlContext; -import com.avaje.ebeaninternal.server.deploy.InheritInfo; -import com.avaje.ebeaninternal.server.deploy.TableJoin; -import com.avaje.ebeaninternal.server.deploy.id.IdBinder; -import com.avaje.ebeaninternal.server.lib.util.StringHelper; - -import java.sql.SQLException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Normal bean included in the query. - */ -public class SqlTreeNodeBean implements SqlTreeNode { - - private static final SqlTreeNode[] NO_CHILDREN = new SqlTreeNode[0]; - - final BeanDescriptor desc; - - final IdBinder idBinder; - - /** - * The children which will be other SelectBean or SelectProxyBean. - */ - final SqlTreeNode[] children; - - final boolean readOnlyLeaf; - - /** - * Set to true if this is a partial object fetch. - */ - final boolean partialObject; - - /** - * The set of properties explicitly included in the query. - * We actually add the manyProp names to this as they are - * references/proxies we add via createListProxies(). - */ - final Set partialProps; - - /** - * The hash of the partialProps (calculate once). - */ - final int partialHash; - - final BeanProperty[] properties; - - /** - * Extra where clause added by Where annotation on associated many. - */ - final String extraWhere; - - final BeanPropertyAssoc nodeBeanProp; - - final TableJoin[] tableJoins; - - /** - * False if report bean and has no id property. - */ - final boolean readId; - - final boolean disableLazyLoad; - - final InheritInfo inheritInfo; - - final String prefix; - - final Set includedProps; - - final Map pathMap; - - public SqlTreeNodeBean(String prefix, BeanPropertyAssoc beanProp, - SqlTreeProperties props, List myChildren, boolean withId) { - - this(prefix, beanProp, beanProp.getTargetDescriptor(),props, myChildren, withId); - } - - /** - * Create with the appropriate node. - */ - public SqlTreeNodeBean(String prefix, BeanPropertyAssoc beanProp, BeanDescriptor desc, - SqlTreeProperties props, List myChildren, boolean withId) { - - this.prefix = prefix; - this.nodeBeanProp = beanProp; - this.desc = desc; - this.inheritInfo = desc.getInheritInfo(); - this.extraWhere = (beanProp == null) ? null : beanProp.getExtraWhere(); - - this.idBinder = desc.getIdBinder(); - - // the bean has an Id property and we want to use it - this.readId = withId && (desc.propertiesId().length > 0); - this.disableLazyLoad = !readId || desc.isSqlSelectBased(); - - this.tableJoins = props.getTableJoins(); - - this.partialObject = props.isPartialObject(); - this.partialProps = props.getIncludedProperties(); - this.partialHash = partialObject ? partialProps.hashCode() : 0; - - this.readOnlyLeaf = props.isReadOnly(); - - this.properties = props.getProps(); - - if (partialObject){ - // merge the explicit partialProps with the implicitly added - // list proxies (that are added by createListProxies()) to get - // the full set of 'loaded' properties for this bean. - includedProps = LoadedPropertiesCache.get(partialHash, partialProps, desc); - } else { - includedProps = null; - } - - if (myChildren == null) { - children = NO_CHILDREN; - } else { - children = myChildren.toArray(new SqlTreeNode[myChildren.size()]); - } - - pathMap = createPathMap(prefix, desc); - } - - private Map createPathMap(String prefix, BeanDescriptor desc) { - - BeanPropertyAssocMany[] manys = desc.propertiesMany(); - - HashMap m = new HashMap(); - for (int i = 0; i < manys.length; i++) { - String name = manys[i].getName(); - m.put(name, getPath(prefix, name)); - } - - return m; - } - - private String getPath(String prefix, String propertyName){ - if (prefix == null){ - return propertyName; - } else { - return prefix+"."+propertyName; - } - } - - protected void postLoad(DbReadContext cquery, Object loadedBean, Object id) { - } - - public void buildSelectExpressionChain(List selectChain){ - if (readId){ - idBinder.buildSelectExpressionChain(prefix, selectChain); - } - for (int i = 0, x = properties.length; i < x; i++) { - properties[i].buildSelectExpressionChain(prefix, selectChain); - } - // recursively continue reading... - for (int i = 0; i < children.length; i++) { - // read each child... and let them set their - // values back to this localBean - children[i].buildSelectExpressionChain(selectChain); - } - } - - /** - * read the properties from the resultSet. - */ - public void load(DbReadContext ctx, Object parentBean) throws SQLException { - - // bean already existing in the persistence context - Object contextBean = null; - - Class localType; - BeanDescriptor localDesc; - IdBinder localIdBinder; - Object localBean; - - if (inheritInfo != null){ - InheritInfo localInfo = inheritInfo.readType(ctx); - if (localInfo == null){ - // the bean must be null - localIdBinder = idBinder; - localBean = null; - localType = null; - localDesc = desc; - } else { - localBean = localInfo.createBean(ctx.isVanillaMode()); - localType = localInfo.getType(); - localIdBinder = localInfo.getIdBinder(); - localDesc = localInfo.getBeanDescriptor(); - } - - } else { - localType = null; - localDesc = desc; - localBean = desc.createBean(ctx.isVanillaMode()); - localIdBinder = idBinder; - } - - Mode queryMode = ctx.getQueryMode(); - - PersistenceContext persistenceContext = ctx.getPersistenceContext(); - - Object id = null; - if (!readId){ - // report type bean... or perhaps excluding the id for SqlSelect? - - } else { - id = localIdBinder.readSet(ctx, localBean); - if (id == null){ - // bean must be null... - localBean = null; - } else { - // check the PersistenceContext to see if the bean already exists - contextBean = persistenceContext.putIfAbsent(id, localBean); - if (contextBean == null){ - // bean just added to the persistenceContext - contextBean = localBean; - } else { - // bean already exists in persistenceContext - if (queryMode.isLoadContextBean()){ - // refresh it anyway (lazy loading for example) - localBean = contextBean; - if (localBean instanceof EntityBean){ - // temporarily turn off interception during load - ((EntityBean)localBean)._ebean_getIntercept().setIntercepting(false); - } - } else { - // ignore the DB data... - localBean = null; - } - } - } - } - - ctx.setCurrentPrefix(prefix, pathMap); - - ctx.propagateState(localBean); - - SqlBeanLoad sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode); - - if (inheritInfo == null){ - // normal behaviour with no inheritance - for (int i = 0, x = properties.length; i < x; i++) { - properties[i].load(sqlBeanLoad); - } - - } else { - // take account of inheritance and due to subclassing approach - // need to get a 'local' version of the property - for (int i = 0, x = properties.length; i < x; i++) { - // get a local version of the BeanProperty - BeanProperty p = localDesc.getBeanProperty(properties[i].getName()); - if (p != null){ - p.load(sqlBeanLoad); - } else { - properties[i].loadIgnore(ctx); - } - } - } - - for (int i = 0, x = tableJoins.length; i < x; i++) { - tableJoins[i].load(sqlBeanLoad); - } - - boolean lazyLoadMany = false; - if (localBean == null && queryMode.equals(Mode.LAZYLOAD_MANY)){ - // batch lazy load many into existing contextBean - localBean = contextBean; - lazyLoadMany = true; - } - - // recursively continue reading... - for (int i = 0; i < children.length; i++) { - // read each child... and let them set their - // values back to this localBean - children[i].load(ctx, localBean); - } - - if (lazyLoadMany){ - // special case where we load children - - } else if (localBean != null) { - - ctx.setCurrentPrefix(prefix, pathMap); - if (!ctx.isVanillaMode()){ - // only create lazy loading collection proxies - // when not in vanilla mode - createListProxies(localDesc, ctx, localBean); - } - - localDesc.postLoad(localBean, includedProps); - - if (localBean instanceof EntityBean) { - EntityBeanIntercept ebi = ((EntityBean)localBean)._ebean_getIntercept(); - ebi.setPersistenceContext(persistenceContext); - ebi.setLoadedProps(includedProps); - if (Mode.LAZYLOAD_BEAN.equals(queryMode)) { - // Lazy Load does not reset the dirty state - ebi.setLoadedLazy(); - } else { - // normal bean loading - ebi.setLoaded(); - } - - if (partialObject) { - ctx.register(null, ebi); - } - - if (disableLazyLoad) { - // bean does not have an Id or is SqlSelect based - ebi.setDisableLazyLoad(true); - } - if (ctx.isAutoFetchProfiling()) { - // collect autofetch profiling for this bean... - ctx.profileBean(ebi, prefix); - } - } - - } - if (parentBean != null && contextBean != null) { - // set this back to the parentBean - nodeBeanProp.setValue(parentBean, contextBean); - } - - if (!readId){ - // a bean with no Id (never found in context) - postLoad(ctx, localBean, id); - - } else { - // return the contextBean which is either the localBean - // read from the resultSet and put into the context OR - // the 'matching' bean that already existed in the context - postLoad(ctx, contextBean, id); - } - } - - /** - * Create lazy loading proxies for the Many's except for the one that is - * included in the actual query. - */ - private void createListProxies(BeanDescriptor localDesc, DbReadContext ctx, Object localBean) { - - BeanPropertyAssocMany fetchedMany = ctx.getManyProperty(); - - // load the List/Set/Map proxy objects (deferred fetching of lists) - BeanPropertyAssocMany[] manys = localDesc.propertiesMany(); - for (int i = 0; i < manys.length; i++) { - - if (fetchedMany != null && fetchedMany.equals(manys[i])) { - // this many property is included in the query... - // it is being loaded with real row data (result[1]) - } else { - // create a proxy for the many (deferred fetching) - BeanCollection ref = manys[i].createReferenceIfNull(localBean); - if (ref != null){ - ctx.register(manys[i].getName(), ref); - } - } - } - } - - /** - * Append the property columns to the buffer. - */ - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - - ctx.pushJoin(prefix); - ctx.pushTableAlias(prefix); - - if (nodeBeanProp != null) { - ctx.append(NEW_LINE).append(" "); - } - - if (!subQuery && inheritInfo != null){ - ctx.appendColumn(inheritInfo.getDiscriminatorColumn()); - } - - if (readId) { - appendSelect(ctx, false, idBinder.getProperties()); - } - appendSelect(ctx, subQuery, properties); - appendSelectTableJoins(ctx); - - for (int i = 0; i < children.length; i++) { - // read each child... and let them set their - // values back to this localBean - children[i].appendSelect(ctx, subQuery); - } - - ctx.popTableAlias(); - ctx.popJoin(); - } - - private void appendSelectTableJoins(DbSqlContext ctx) { - - String baseAlias = ctx.getTableAlias(prefix); - - for (int i = 0; i < tableJoins.length; i++) { - TableJoin join = tableJoins[i]; - - String alias = baseAlias+i; - - ctx.pushSecondaryTableAlias(alias); - join.appendSelect(ctx, false); - ctx.popTableAlias(); - } - } - - /** - * Append the properties to the buffer. - */ - private void appendSelect(DbSqlContext ctx, boolean subQuery, BeanProperty[] props) { - - for (int i = 0; i < props.length; i++) { - props[i].appendSelect(ctx, subQuery); - } - } - - - public void appendWhere(DbSqlContext ctx) { - - if (inheritInfo != null) { - if (inheritInfo.isRoot()) { - // at root of hierarchy so don't bother - // adding a where clause because we want - // all the types... - } else { - // restrict to this type and - // sub types of this type. - if (ctx.length() > 0){ - ctx.append(" and"); - } - ctx.append(" ").append(ctx.getTableAlias(prefix)).append(".");//tableAlias - ctx.append(inheritInfo.getWhere()).append(" "); - } - } - if (extraWhere != null){ - if (ctx.length() > 0){ - ctx.append(" and"); - } - String ta = ctx.getTableAlias(prefix); - String ew = StringHelper.replaceString(extraWhere, "${ta}", ta); - ctx.append(" ").append(ew).append(" "); - } - - for (int i = 0; i < children.length; i++) { - // recursively add to the where clause any - // fixed predicates (extraWhere etc) - children[i].appendWhere(ctx); - } - } - - /** - * Append to the FROM clause for this node. - */ - public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) { - - ctx.pushJoin(prefix); - ctx.pushTableAlias(prefix); - - forceOuterJoin = appendFromBaseTable(ctx, forceOuterJoin); - - for (int i = 0; i < properties.length; i++) { - // usually nothing... except for 1-1 Exported - properties[i].appendFrom(ctx, forceOuterJoin); - } - - for (int i = 0; i < children.length; i++) { - children[i].appendFrom(ctx, forceOuterJoin); - } - - ctx.popTableAlias(); - ctx.popJoin(); - } - - /** - * Join to base table for this node. This includes a join to - * the intersection table if this is a ManyToMany node. - */ - public boolean appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) { - - if (nodeBeanProp instanceof BeanPropertyAssocMany){ - BeanPropertyAssocMany manyProp = (BeanPropertyAssocMany)nodeBeanProp; - if (manyProp.isManyToMany()){ - - String alias = ctx.getTableAlias(prefix); - String[] split = SplitName.split(prefix); - String parentAlias = ctx.getTableAlias(split[0]); - String alias2 = alias+"z_"; - - TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin(); - manyToManyJoin.addJoin(forceOuterJoin, parentAlias, alias2, ctx); - - return nodeBeanProp.addJoin(forceOuterJoin, alias2, alias, ctx); - } - - } - - return nodeBeanProp.addJoin(forceOuterJoin, prefix, ctx); - } - - - /** - * Summary description. - */ - public String toString() { - return "SqlTreeNodeBean: " + desc; - } -} +package com.avaje.ebeaninternal.server.query; + +import com.avaje.ebean.bean.BeanCollection; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebeaninternal.api.SpiQuery.Mode; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.DbReadContext; +import com.avaje.ebeaninternal.server.deploy.DbSqlContext; +import com.avaje.ebeaninternal.server.deploy.InheritInfo; +import com.avaje.ebeaninternal.server.deploy.TableJoin; +import com.avaje.ebeaninternal.server.deploy.id.IdBinder; +import com.avaje.ebeaninternal.server.lib.util.StringHelper; + +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Normal bean included in the query. + */ +public class SqlTreeNodeBean implements SqlTreeNode { + + private static final SqlTreeNode[] NO_CHILDREN = new SqlTreeNode[0]; + + final BeanDescriptor desc; + + final IdBinder idBinder; + + /** + * The children which will be other SelectBean or SelectProxyBean. + */ + final SqlTreeNode[] children; + + final boolean readOnlyLeaf; + + /** + * Set to true if this is a partial object fetch. + */ + final boolean partialObject; + + /** + * The set of properties explicitly included in the query. + * We actually add the manyProp names to this as they are + * references/proxies we add via createListProxies(). + */ + final Set partialProps; + + /** + * The hash of the partialProps (calculate once). + */ + final int partialHash; + + final BeanProperty[] properties; + + /** + * Extra where clause added by Where annotation on associated many. + */ + final String extraWhere; + + final BeanPropertyAssoc nodeBeanProp; + + final TableJoin[] tableJoins; + + /** + * False if report bean and has no id property. + */ + final boolean readId; + + final boolean disableLazyLoad; + + final InheritInfo inheritInfo; + + final String prefix; + + final Set includedProps; + + final Map pathMap; + + public SqlTreeNodeBean(String prefix, BeanPropertyAssoc beanProp, + SqlTreeProperties props, List myChildren, boolean withId) { + + this(prefix, beanProp, beanProp.getTargetDescriptor(),props, myChildren, withId); + } + + /** + * Create with the appropriate node. + */ + public SqlTreeNodeBean(String prefix, BeanPropertyAssoc beanProp, BeanDescriptor desc, + SqlTreeProperties props, List myChildren, boolean withId) { + + this.prefix = prefix; + this.nodeBeanProp = beanProp; + this.desc = desc; + this.inheritInfo = desc.getInheritInfo(); + this.extraWhere = (beanProp == null) ? null : beanProp.getExtraWhere(); + + this.idBinder = desc.getIdBinder(); + + // the bean has an Id property and we want to use it + this.readId = withId && (desc.propertiesId().length > 0); + this.disableLazyLoad = !readId || desc.isSqlSelectBased(); + + this.tableJoins = props.getTableJoins(); + + this.partialObject = props.isPartialObject(); + this.partialProps = props.getIncludedProperties(); + this.partialHash = partialObject ? partialProps.hashCode() : 0; + + this.readOnlyLeaf = props.isReadOnly(); + + this.properties = props.getProps(); + + if (partialObject){ + // merge the explicit partialProps with the implicitly added + // list proxies (that are added by createListProxies()) to get + // the full set of 'loaded' properties for this bean. + includedProps = LoadedPropertiesCache.get(partialHash, partialProps, desc); + } else { + includedProps = null; + } + + if (myChildren == null) { + children = NO_CHILDREN; + } else { + children = myChildren.toArray(new SqlTreeNode[myChildren.size()]); + } + + pathMap = createPathMap(prefix, desc); + } + + private Map createPathMap(String prefix, BeanDescriptor desc) { + + BeanPropertyAssocMany[] manys = desc.propertiesMany(); + + HashMap m = new HashMap(); + for (int i = 0; i < manys.length; i++) { + String name = manys[i].getName(); + m.put(name, getPath(prefix, name)); + } + + return m; + } + + private String getPath(String prefix, String propertyName){ + if (prefix == null){ + return propertyName; + } else { + return prefix+"."+propertyName; + } + } + + protected void postLoad(DbReadContext cquery, Object loadedBean, Object id) { + } + + public void buildSelectExpressionChain(List selectChain){ + if (readId){ + idBinder.buildSelectExpressionChain(prefix, selectChain); + } + for (int i = 0, x = properties.length; i < x; i++) { + properties[i].buildSelectExpressionChain(prefix, selectChain); + } + // recursively continue reading... + for (int i = 0; i < children.length; i++) { + // read each child... and let them set their + // values back to this localBean + children[i].buildSelectExpressionChain(selectChain); + } + } + + /** + * read the properties from the resultSet. + */ + public void load(DbReadContext ctx, Object parentBean) throws SQLException { + + // bean already existing in the persistence context + Object contextBean = null; + + Class localType; + BeanDescriptor localDesc; + IdBinder localIdBinder; + Object localBean; + + if (inheritInfo != null){ + InheritInfo localInfo = inheritInfo.readType(ctx); + if (localInfo == null){ + // the bean must be null + localIdBinder = idBinder; + localBean = null; + localType = null; + localDesc = desc; + } else { + localBean = localInfo.createBean(ctx.isVanillaMode()); + localType = localInfo.getType(); + localIdBinder = localInfo.getIdBinder(); + localDesc = localInfo.getBeanDescriptor(); + } + + } else { + localType = null; + localDesc = desc; + localBean = desc.createBean(ctx.isVanillaMode()); + localIdBinder = idBinder; + } + + Mode queryMode = ctx.getQueryMode(); + + PersistenceContext persistenceContext = ctx.getPersistenceContext(); + + Object id = null; + if (!readId){ + // report type bean... or perhaps excluding the id for SqlSelect? + + } else { + id = localIdBinder.readSet(ctx, localBean); + if (id == null){ + // bean must be null... + localBean = null; + } else { + // check the PersistenceContext to see if the bean already exists + contextBean = persistenceContext.putIfAbsent(id, localBean); + if (contextBean == null){ + // bean just added to the persistenceContext + contextBean = localBean; + } else { + // bean already exists in persistenceContext + if (queryMode.isLoadContextBean()){ + // refresh it anyway (lazy loading for example) + localBean = contextBean; + if (localBean instanceof EntityBean){ + // temporarily turn off interception during load + ((EntityBean)localBean)._ebean_getIntercept().setIntercepting(false); + } + } else { + // ignore the DB data... + localBean = null; + } + } + } + } + + ctx.setCurrentPrefix(prefix, pathMap); + + ctx.propagateState(localBean); + + SqlBeanLoad sqlBeanLoad = new SqlBeanLoad(ctx, localType, localBean, queryMode); + + if (inheritInfo == null){ + // normal behaviour with no inheritance + for (int i = 0, x = properties.length; i < x; i++) { + properties[i].load(sqlBeanLoad); + } + + } else { + // take account of inheritance and due to subclassing approach + // need to get a 'local' version of the property + for (int i = 0, x = properties.length; i < x; i++) { + // get a local version of the BeanProperty + BeanProperty p = localDesc.getBeanProperty(properties[i].getName()); + if (p != null){ + p.load(sqlBeanLoad); + } else { + properties[i].loadIgnore(ctx); + } + } + } + + for (int i = 0, x = tableJoins.length; i < x; i++) { + tableJoins[i].load(sqlBeanLoad); + } + + boolean lazyLoadMany = false; + if (localBean == null && queryMode.equals(Mode.LAZYLOAD_MANY)){ + // batch lazy load many into existing contextBean + localBean = contextBean; + lazyLoadMany = true; + } + + // recursively continue reading... + for (int i = 0; i < children.length; i++) { + // read each child... and let them set their + // values back to this localBean + children[i].load(ctx, localBean); + } + + if (lazyLoadMany){ + // special case where we load children + + } else if (localBean != null) { + + ctx.setCurrentPrefix(prefix, pathMap); + if (!ctx.isVanillaMode()){ + // only create lazy loading collection proxies + // when not in vanilla mode + createListProxies(localDesc, ctx, localBean); + } + + localDesc.postLoad(localBean, includedProps); + + if (localBean instanceof EntityBean) { + EntityBeanIntercept ebi = ((EntityBean)localBean)._ebean_getIntercept(); + ebi.setPersistenceContext(persistenceContext); + ebi.setLoadedProps(includedProps); + if (Mode.LAZYLOAD_BEAN.equals(queryMode)) { + // Lazy Load does not reset the dirty state + ebi.setLoadedLazy(); + } else { + // normal bean loading + ebi.setLoaded(); + } + + if (partialObject) { + ctx.register(null, ebi); + } + + if (disableLazyLoad) { + // bean does not have an Id or is SqlSelect based + ebi.setDisableLazyLoad(true); + } + if (ctx.isAutoFetchProfiling()) { + // collect autofetch profiling for this bean... + ctx.profileBean(ebi, prefix); + } + } + + } + if (parentBean != null && contextBean != null) { + // set this back to the parentBean + nodeBeanProp.setValue(parentBean, contextBean); + } + + if (!readId){ + // a bean with no Id (never found in context) + postLoad(ctx, localBean, id); + + } else { + // return the contextBean which is either the localBean + // read from the resultSet and put into the context OR + // the 'matching' bean that already existed in the context + postLoad(ctx, contextBean, id); + } + } + + /** + * Create lazy loading proxies for the Many's except for the one that is + * included in the actual query. + */ + private void createListProxies(BeanDescriptor localDesc, DbReadContext ctx, Object localBean) { + + BeanPropertyAssocMany fetchedMany = ctx.getManyProperty(); + + // load the List/Set/Map proxy objects (deferred fetching of lists) + BeanPropertyAssocMany[] manys = localDesc.propertiesMany(); + for (int i = 0; i < manys.length; i++) { + + if (fetchedMany != null && fetchedMany.equals(manys[i])) { + // this many property is included in the query... + // it is being loaded with real row data (result[1]) + } else { + // create a proxy for the many (deferred fetching) + BeanCollection ref = manys[i].createReferenceIfNull(localBean); + if (ref != null){ + ctx.register(manys[i].getName(), ref); + } + } + } + } + + /** + * Append the property columns to the buffer. + */ + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + + ctx.pushJoin(prefix); + ctx.pushTableAlias(prefix); + + if (nodeBeanProp != null) { + ctx.append(NEW_LINE).append(" "); + } + + if (!subQuery && inheritInfo != null){ + ctx.appendColumn(inheritInfo.getDiscriminatorColumn()); + } + + if (readId) { + appendSelect(ctx, false, idBinder.getProperties()); + } + appendSelect(ctx, subQuery, properties); + appendSelectTableJoins(ctx); + + for (int i = 0; i < children.length; i++) { + // read each child... and let them set their + // values back to this localBean + children[i].appendSelect(ctx, subQuery); + } + + ctx.popTableAlias(); + ctx.popJoin(); + } + + private void appendSelectTableJoins(DbSqlContext ctx) { + + String baseAlias = ctx.getTableAlias(prefix); + + for (int i = 0; i < tableJoins.length; i++) { + TableJoin join = tableJoins[i]; + + String alias = baseAlias+i; + + ctx.pushSecondaryTableAlias(alias); + join.appendSelect(ctx, false); + ctx.popTableAlias(); + } + } + + /** + * Append the properties to the buffer. + */ + private void appendSelect(DbSqlContext ctx, boolean subQuery, BeanProperty[] props) { + + for (int i = 0; i < props.length; i++) { + props[i].appendSelect(ctx, subQuery); + } + } + + + public void appendWhere(DbSqlContext ctx) { + + if (inheritInfo != null) { + if (inheritInfo.isRoot()) { + // at root of hierarchy so don't bother + // adding a where clause because we want + // all the types... + } else { + // restrict to this type and + // sub types of this type. + if (ctx.length() > 0){ + ctx.append(" and"); + } + ctx.append(" ").append(ctx.getTableAlias(prefix)).append(".");//tableAlias + ctx.append(inheritInfo.getWhere()).append(" "); + } + } + if (extraWhere != null){ + if (ctx.length() > 0){ + ctx.append(" and"); + } + String ta = ctx.getTableAlias(prefix); + String ew = StringHelper.replaceString(extraWhere, "${ta}", ta); + ctx.append(" ").append(ew).append(" "); + } + + for (int i = 0; i < children.length; i++) { + // recursively add to the where clause any + // fixed predicates (extraWhere etc) + children[i].appendWhere(ctx); + } + } + + /** + * Append to the FROM clause for this node. + */ + public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) { + + ctx.pushJoin(prefix); + ctx.pushTableAlias(prefix); + + forceOuterJoin = appendFromBaseTable(ctx, forceOuterJoin); + + for (int i = 0; i < properties.length; i++) { + // usually nothing... except for 1-1 Exported + properties[i].appendFrom(ctx, forceOuterJoin); + } + + for (int i = 0; i < children.length; i++) { + children[i].appendFrom(ctx, forceOuterJoin); + } + + ctx.popTableAlias(); + ctx.popJoin(); + } + + /** + * Join to base table for this node. This includes a join to + * the intersection table if this is a ManyToMany node. + */ + public boolean appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) { + + if (nodeBeanProp instanceof BeanPropertyAssocMany){ + BeanPropertyAssocMany manyProp = (BeanPropertyAssocMany)nodeBeanProp; + if (manyProp.isManyToMany()){ + + String alias = ctx.getTableAlias(prefix); + String[] split = SplitName.split(prefix); + String parentAlias = ctx.getTableAlias(split[0]); + String alias2 = alias+"z_"; + + TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin(); + manyToManyJoin.addJoin(forceOuterJoin, parentAlias, alias2, ctx); + + return nodeBeanProp.addJoin(forceOuterJoin, alias2, alias, ctx); + } + + } + + return nodeBeanProp.addJoin(forceOuterJoin, prefix, ctx); + } + + + /** + * Summary description. + */ + public String toString() { + return "SqlTreeNodeBean: " + desc; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java index 5806b491f..ef6c7ae05 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeNodeManyWhereJoin.java @@ -1,112 +1,93 @@ -/** - * 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.query; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.deploy.DbReadContext; -import com.avaje.ebeaninternal.server.deploy.DbSqlContext; -import com.avaje.ebeaninternal.server.deploy.TableJoin; - -/** - * Join to Many (or child of a many) to support where clause predicates on many properties. - * - * @author rbygrave - */ -public class SqlTreeNodeManyWhereJoin implements SqlTreeNode { - - private final String parentPrefix; - private final String prefix; - private final BeanPropertyAssoc nodeBeanProp; - private final SqlTreeNode[] children; - - public SqlTreeNodeManyWhereJoin(String prefix, BeanPropertyAssoc prop) { - - this.nodeBeanProp = prop; - this.prefix = prefix; - - String[] split = SplitName.split(prefix); - this.parentPrefix = split[0]; - - List childrenList = new ArrayList(0); - this.children = childrenList.toArray(new SqlTreeNode[childrenList.size()]); - } - - /** - * Append to the FROM clause for this node. - */ - public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) { - - appendFromBaseTable(ctx, forceOuterJoin); - - for (int i = 0; i < children.length; i++) { - children[i].appendFrom(ctx, forceOuterJoin); - } - } - - /** - * Join to base table for this node. This includes a join to the - * intersection table if this is a ManyToMany node. - */ - public void appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) { - - String alias = ctx.getTableAliasManyWhere(prefix); - String parentAlias = ctx.getTableAliasManyWhere(parentPrefix); - - if (nodeBeanProp instanceof BeanPropertyAssocOne){ - nodeBeanProp.addInnerJoin(parentAlias, alias, ctx); - - } else { - BeanPropertyAssocMany manyProp = (BeanPropertyAssocMany)nodeBeanProp; - if (!manyProp.isManyToMany()) { - manyProp.addInnerJoin(parentAlias, alias, ctx); - - } else { - String alias2 = alias + "z_"; - - TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin(); - manyToManyJoin.addInnerJoin(parentAlias, alias2, ctx); - manyProp.addInnerJoin(alias2, alias, ctx); - } - } - } - - public void buildSelectExpressionChain(List selectChain) { - // nothing to add - } - - public void appendSelect(DbSqlContext ctx, boolean subQuery) { - // nothing to do here - } - - public void appendWhere(DbSqlContext ctx) { - // nothing to do here - } - - public void load(DbReadContext ctx, Object parentBean) throws SQLException { - // nothing to do here - } - -} +package com.avaje.ebeaninternal.server.query; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.deploy.DbReadContext; +import com.avaje.ebeaninternal.server.deploy.DbSqlContext; +import com.avaje.ebeaninternal.server.deploy.TableJoin; + +/** + * Join to Many (or child of a many) to support where clause predicates on many properties. + * + * @author rbygrave + */ +public class SqlTreeNodeManyWhereJoin implements SqlTreeNode { + + private final String parentPrefix; + private final String prefix; + private final BeanPropertyAssoc nodeBeanProp; + private final SqlTreeNode[] children; + + public SqlTreeNodeManyWhereJoin(String prefix, BeanPropertyAssoc prop) { + + this.nodeBeanProp = prop; + this.prefix = prefix; + + String[] split = SplitName.split(prefix); + this.parentPrefix = split[0]; + + List childrenList = new ArrayList(0); + this.children = childrenList.toArray(new SqlTreeNode[childrenList.size()]); + } + + /** + * Append to the FROM clause for this node. + */ + public void appendFrom(DbSqlContext ctx, boolean forceOuterJoin) { + + appendFromBaseTable(ctx, forceOuterJoin); + + for (int i = 0; i < children.length; i++) { + children[i].appendFrom(ctx, forceOuterJoin); + } + } + + /** + * Join to base table for this node. This includes a join to the + * intersection table if this is a ManyToMany node. + */ + public void appendFromBaseTable(DbSqlContext ctx, boolean forceOuterJoin) { + + String alias = ctx.getTableAliasManyWhere(prefix); + String parentAlias = ctx.getTableAliasManyWhere(parentPrefix); + + if (nodeBeanProp instanceof BeanPropertyAssocOne){ + nodeBeanProp.addInnerJoin(parentAlias, alias, ctx); + + } else { + BeanPropertyAssocMany manyProp = (BeanPropertyAssocMany)nodeBeanProp; + if (!manyProp.isManyToMany()) { + manyProp.addInnerJoin(parentAlias, alias, ctx); + + } else { + String alias2 = alias + "z_"; + + TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin(); + manyToManyJoin.addInnerJoin(parentAlias, alias2, ctx); + manyProp.addInnerJoin(alias2, alias, ctx); + } + } + } + + public void buildSelectExpressionChain(List selectChain) { + // nothing to add + } + + public void appendSelect(DbSqlContext ctx, boolean subQuery) { + // nothing to do here + } + + public void appendWhere(DbSqlContext ctx) { + // nothing to do here + } + + public void load(DbReadContext ctx, Object parentBean) throws SQLException { + // nothing to do here + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeProperties.java b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeProperties.java index 0cfcbb0b5..e622e2fef 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeProperties.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/SqlTreeProperties.java @@ -1,107 +1,104 @@ -/** - * - */ -package com.avaje.ebeaninternal.server.query; - -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.deploy.TableJoin; - -/** - * The select properties for a node in the SqlTree. - */ -public class SqlTreeProperties { - - /** - * The included Properties that will be used by EntityBeanIntercept - * to determine lazy loading on partial objects. - */ - Set includedProps; - - /** - * True if this node of the tree should have read only entity beans. - */ - boolean readOnly; - - /** - * set to false if the id field is not included. - */ - boolean includeId = true; - - TableJoin[] tableJoins = new TableJoin[0]; - - /** - * The bean properties in order. - */ - List propsList = new ArrayList(); - - /** - * Maintain a list of property names to detect embedded bean additions. - */ - LinkedHashSet propNames = new LinkedHashSet(); - - public SqlTreeProperties() { - - } - - public boolean containsProperty(String propName){ - return propNames.contains(propName); - } - - public void add(BeanProperty[] props) { - for (BeanProperty beanProperty : props) { - propsList.add(beanProperty); - } - } - - public void add(BeanProperty prop) { - propsList.add(prop); - propNames.add(prop.getName()); - - } - - public BeanProperty[] getProps() { - return propsList.toArray(new BeanProperty[propsList.size()]); - } - - public boolean isIncludeId() { - return includeId; - } - - public void setIncludeId(boolean includeId) { - this.includeId = includeId; - } - - public boolean isPartialObject() { - return includedProps != null; - } - - public Set getIncludedProperties() { - return includedProps; - } - - public void setIncludedProperties(Set includedProps) { - this.includedProps = includedProps; - } - - public boolean isReadOnly() { - return readOnly; - } - - public void setReadOnly(boolean readOnly) { - this.readOnly = readOnly; - } - - public TableJoin[] getTableJoins() { - return tableJoins; - } - - public void setTableJoins(TableJoin[] tableJoins) { - this.tableJoins = tableJoins; - } - +package com.avaje.ebeaninternal.server.query; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.deploy.TableJoin; + +/** + * The select properties for a node in the SqlTree. + */ +public class SqlTreeProperties { + + /** + * The included Properties that will be used by EntityBeanIntercept + * to determine lazy loading on partial objects. + */ + Set includedProps; + + /** + * True if this node of the tree should have read only entity beans. + */ + boolean readOnly; + + /** + * set to false if the id field is not included. + */ + boolean includeId = true; + + TableJoin[] tableJoins = new TableJoin[0]; + + /** + * The bean properties in order. + */ + List propsList = new ArrayList(); + + /** + * Maintain a list of property names to detect embedded bean additions. + */ + LinkedHashSet propNames = new LinkedHashSet(); + + public SqlTreeProperties() { + + } + + public boolean containsProperty(String propName){ + return propNames.contains(propName); + } + + public void add(BeanProperty[] props) { + for (BeanProperty beanProperty : props) { + propsList.add(beanProperty); + } + } + + public void add(BeanProperty prop) { + propsList.add(prop); + propNames.add(prop.getName()); + + } + + public BeanProperty[] getProps() { + return propsList.toArray(new BeanProperty[propsList.size()]); + } + + public boolean isIncludeId() { + return includeId; + } + + public void setIncludeId(boolean includeId) { + this.includeId = includeId; + } + + public boolean isPartialObject() { + return includedProps != null; + } + + public Set getIncludedProperties() { + return includedProps; + } + + public void setIncludedProperties(Set includedProps) { + this.includedProps = includedProps; + } + + public boolean isReadOnly() { + return readOnly; + } + + public void setReadOnly(boolean readOnly) { + this.readOnly = readOnly; + } + + public TableJoin[] getTableJoins() { + return tableJoins; + } + + public void setTableJoins(TableJoin[] tableJoins) { + this.tableJoins = tableJoins; + } + } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/package-info.java b/src/main/java/com/avaje/ebeaninternal/server/query/package-info.java index 13328791e..c78134345 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/package-info.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/package-info.java @@ -1,4 +1 @@ -/** - * Controls execution of the Orm and Raw Sql queries. - */ package com.avaje.ebeaninternal.server.query; \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmUpdate.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmUpdate.java index 445aa8782..e562149a9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmUpdate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmUpdate.java @@ -1,235 +1,216 @@ -/** - * 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.querydefn; - -import java.io.Serializable; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebeaninternal.api.BindParams; -import com.avaje.ebeaninternal.api.SpiUpdate; -import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; - -/** - * Default implementation of OrmUpdate. - */ -public final class DefaultOrmUpdate implements SpiUpdate, Serializable { - - private static final long serialVersionUID = -8791423602246515438L; - - - private transient final EbeanServer server; - - private final Class beanType; - - /** - * The name of the update. - */ - private final String name; - - /** - * The parameters used to bind to the sql. - */ - private final BindParams bindParams = new BindParams(); - - /** - * The sql update or delete statement. - */ - private final String updateStatement; - - /** - * Automatically detect the table being modified by this sql. This will - * register this information so that eBean invalidates cached objects if - * required. - */ - private boolean notifyCache = true; - - private int timeout; - - private String generatedSql; - - private final String baseTable; - - private final OrmUpdateType type; - - /** - * Create with a specific server. This means you can use the - * UpdateSql.execute() method. - */ - public DefaultOrmUpdate(Class beanType, EbeanServer server, String baseTable, String updateStatement) { - this.beanType = beanType; - this.server = server; - this.baseTable = baseTable; - this.name = ""; - this.updateStatement = updateStatement; - this.type = deriveType(updateStatement); - - } - - public DefaultOrmUpdate(Class beanType, EbeanServer server, String baseTable, DeployNamedUpdate namedUpdate) { - - this.beanType = beanType; - this.server = server; - this.baseTable = baseTable; - this.name = namedUpdate.getName(); - this.notifyCache = namedUpdate.isNotifyCache(); - - // named updates are always converted to sql as part - // of the initialisation - this.updateStatement = namedUpdate.getSqlUpdateStatement(); - this.type = deriveType(updateStatement); - } - - public DefaultOrmUpdate setTimeout(int secs){ - this.timeout = secs; - return this; - } - - public Class getBeanType() { - return beanType; - } - - /** - * Return the timeout in seconds. - */ - public int getTimeout() { - return timeout; - } - - private SpiUpdate.OrmUpdateType deriveType(String updateStatement) { - - updateStatement = updateStatement.trim(); - int spacepos = updateStatement.indexOf(' '); - if (spacepos == -1){ - return SpiUpdate.OrmUpdateType.UNKNOWN; - - } else { - String firstWord = updateStatement.substring(0, spacepos); - if (firstWord.equalsIgnoreCase("update")){ - return SpiUpdate.OrmUpdateType.UPDATE; - - } else if (firstWord.equalsIgnoreCase("insert")) { - return SpiUpdate.OrmUpdateType.INSERT; - - } else if (firstWord.equalsIgnoreCase("delete")) { - return SpiUpdate.OrmUpdateType.DELETE; - } else { - return SpiUpdate.OrmUpdateType.UNKNOWN; - } - } - } - - public int execute() { - return server.execute(this); - } - - /** - * Set this to false if you don't want eBean to automatically deduce the - * table modification information and process it. - *

      - * Set this to false if you don't want any cache invalidation or text index - * management to occur. You may do this when say you update only one column - * and you know that it is not important for cached objects or text indexes. - *

      - */ - public DefaultOrmUpdate setNotifyCache(boolean notifyCache) { - this.notifyCache = notifyCache; - return this; - } - - /** - * Return true if the cache should be notified so that invalidates - * appropriate objects. - */ - public boolean isNotifyCache() { - return notifyCache; - } - - public String getName() { - return name; - } - - public String getUpdateStatement() { - return updateStatement; - } - - public DefaultOrmUpdate set(int position, Object value) { - bindParams.setParameter(position, value); - return this; - } - - public DefaultOrmUpdate setParameter(int position, Object value) { - bindParams.setParameter(position, value); - return this; - } - - public DefaultOrmUpdate setNull(int position, int jdbcType) { - bindParams.setNullParameter(position, jdbcType); - return this; - } - - public DefaultOrmUpdate setNullParameter(int position, int jdbcType) { - bindParams.setNullParameter(position, jdbcType); - return this; - } - - public DefaultOrmUpdate set(String name, Object value) { - bindParams.setParameter(name, value); - return this; - } - - public DefaultOrmUpdate setParameter(String name, Object param) { - bindParams.setParameter(name, param); - return this; - } - - public DefaultOrmUpdate setNull(String name, int jdbcType) { - bindParams.setNullParameter(name, jdbcType); - return this; - } - - public DefaultOrmUpdate setNullParameter(String name, int jdbcType) { - bindParams.setNullParameter(name, jdbcType); - return this; - } - - /** - * Return the bind parameters. - */ - public BindParams getBindParams() { - return bindParams; - } - - public String getGeneratedSql() { - return generatedSql; - } - - public void setGeneratedSql(String generatedSql) { - this.generatedSql = generatedSql; - } - - public String getBaseTable() { - return baseTable; - } - - public OrmUpdateType getOrmUpdateType() { - return type; - } - -} +package com.avaje.ebeaninternal.server.querydefn; + +import java.io.Serializable; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebeaninternal.api.BindParams; +import com.avaje.ebeaninternal.api.SpiUpdate; +import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; + +/** + * Default implementation of OrmUpdate. + */ +public final class DefaultOrmUpdate implements SpiUpdate, Serializable { + + private static final long serialVersionUID = -8791423602246515438L; + + + private transient final EbeanServer server; + + private final Class beanType; + + /** + * The name of the update. + */ + private final String name; + + /** + * The parameters used to bind to the sql. + */ + private final BindParams bindParams = new BindParams(); + + /** + * The sql update or delete statement. + */ + private final String updateStatement; + + /** + * Automatically detect the table being modified by this sql. This will + * register this information so that eBean invalidates cached objects if + * required. + */ + private boolean notifyCache = true; + + private int timeout; + + private String generatedSql; + + private final String baseTable; + + private final OrmUpdateType type; + + /** + * Create with a specific server. This means you can use the + * UpdateSql.execute() method. + */ + public DefaultOrmUpdate(Class beanType, EbeanServer server, String baseTable, String updateStatement) { + this.beanType = beanType; + this.server = server; + this.baseTable = baseTable; + this.name = ""; + this.updateStatement = updateStatement; + this.type = deriveType(updateStatement); + + } + + public DefaultOrmUpdate(Class beanType, EbeanServer server, String baseTable, DeployNamedUpdate namedUpdate) { + + this.beanType = beanType; + this.server = server; + this.baseTable = baseTable; + this.name = namedUpdate.getName(); + this.notifyCache = namedUpdate.isNotifyCache(); + + // named updates are always converted to sql as part + // of the initialisation + this.updateStatement = namedUpdate.getSqlUpdateStatement(); + this.type = deriveType(updateStatement); + } + + public DefaultOrmUpdate setTimeout(int secs){ + this.timeout = secs; + return this; + } + + public Class getBeanType() { + return beanType; + } + + /** + * Return the timeout in seconds. + */ + public int getTimeout() { + return timeout; + } + + private SpiUpdate.OrmUpdateType deriveType(String updateStatement) { + + updateStatement = updateStatement.trim(); + int spacepos = updateStatement.indexOf(' '); + if (spacepos == -1){ + return SpiUpdate.OrmUpdateType.UNKNOWN; + + } else { + String firstWord = updateStatement.substring(0, spacepos); + if (firstWord.equalsIgnoreCase("update")){ + return SpiUpdate.OrmUpdateType.UPDATE; + + } else if (firstWord.equalsIgnoreCase("insert")) { + return SpiUpdate.OrmUpdateType.INSERT; + + } else if (firstWord.equalsIgnoreCase("delete")) { + return SpiUpdate.OrmUpdateType.DELETE; + } else { + return SpiUpdate.OrmUpdateType.UNKNOWN; + } + } + } + + public int execute() { + return server.execute(this); + } + + /** + * Set this to false if you don't want eBean to automatically deduce the + * table modification information and process it. + *

      + * Set this to false if you don't want any cache invalidation or text index + * management to occur. You may do this when say you update only one column + * and you know that it is not important for cached objects or text indexes. + *

      + */ + public DefaultOrmUpdate setNotifyCache(boolean notifyCache) { + this.notifyCache = notifyCache; + return this; + } + + /** + * Return true if the cache should be notified so that invalidates + * appropriate objects. + */ + public boolean isNotifyCache() { + return notifyCache; + } + + public String getName() { + return name; + } + + public String getUpdateStatement() { + return updateStatement; + } + + public DefaultOrmUpdate set(int position, Object value) { + bindParams.setParameter(position, value); + return this; + } + + public DefaultOrmUpdate setParameter(int position, Object value) { + bindParams.setParameter(position, value); + return this; + } + + public DefaultOrmUpdate setNull(int position, int jdbcType) { + bindParams.setNullParameter(position, jdbcType); + return this; + } + + public DefaultOrmUpdate setNullParameter(int position, int jdbcType) { + bindParams.setNullParameter(position, jdbcType); + return this; + } + + public DefaultOrmUpdate set(String name, Object value) { + bindParams.setParameter(name, value); + return this; + } + + public DefaultOrmUpdate setParameter(String name, Object param) { + bindParams.setParameter(name, param); + return this; + } + + public DefaultOrmUpdate setNull(String name, int jdbcType) { + bindParams.setNullParameter(name, jdbcType); + return this; + } + + public DefaultOrmUpdate setNullParameter(String name, int jdbcType) { + bindParams.setNullParameter(name, jdbcType); + return this; + } + + /** + * Return the bind parameters. + */ + public BindParams getBindParams() { + return bindParams; + } + + public String getGeneratedSql() { + return generatedSql; + } + + public void setGeneratedSql(String generatedSql) { + this.generatedSql = generatedSql; + } + + public String getBaseTable() { + return baseTable; + } + + public OrmUpdateType getOrmUpdateType() { + return type; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/package-info.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/package-info.java index 9bdbc5a64..6394f99e4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/package-info.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/package-info.java @@ -1,4 +1 @@ -/** - * Objects to define the ORM query itself. - */ package com.avaje.ebeaninternal.server.querydefn; \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflect.java b/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflect.java index 52fd95a65..90085e956 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflect.java +++ b/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflect.java @@ -1,52 +1,33 @@ -/** - * 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.reflect; - -/** - * Provides getter setter and construction methods for beans. - *

      - * This enables the implementation to use standard reflection or - * code generation. - *

      - */ -public interface BeanReflect { - - /** - * Create an EntityBean for this type. - */ - public Object createEntityBean(); - - /** - * Create a plain vanilla bean for this type. - */ - public Object createVanillaBean(); - - public boolean isVanillaOnly(); - - /** - * Return the getter for a given bean property. - */ - public BeanReflectGetter getGetter(String name); - - /** - * Return the setter for a given bean property. - */ - public BeanReflectSetter getSetter(String name); -} +package com.avaje.ebeaninternal.server.reflect; + +/** + * Provides getter setter and construction methods for beans. + *

      + * This enables the implementation to use standard reflection or + * code generation. + *

      + */ +public interface BeanReflect { + + /** + * Create an EntityBean for this type. + */ + public Object createEntityBean(); + + /** + * Create a plain vanilla bean for this type. + */ + public Object createVanillaBean(); + + public boolean isVanillaOnly(); + + /** + * Return the getter for a given bean property. + */ + public BeanReflectGetter getGetter(String name); + + /** + * Return the setter for a given bean property. + */ + public BeanReflectSetter getSetter(String name); +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflectFactory.java b/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflectFactory.java index 747fdff31..76085cdff 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflectFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflectFactory.java @@ -1,33 +1,14 @@ -/** - * 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.reflect; - - -/** - * Creates BeanReflect object used to provide getter setter and construction - * for the beans. - */ -public interface BeanReflectFactory { - - /** - * Create the BeanReflect for the given plain bean and its EntityBean equivalent. - */ - public BeanReflect create(Class vanillaType, Class entityBeanType); -} +package com.avaje.ebeaninternal.server.reflect; + + +/** + * Creates BeanReflect object used to provide getter setter and construction + * for the beans. + */ +public interface BeanReflectFactory { + + /** + * Create the BeanReflect for the given plain bean and its EntityBean equivalent. + */ + public BeanReflect create(Class vanillaType, Class entityBeanType); +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflectGetter.java b/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflectGetter.java index 314593078..3ccddabdb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflectGetter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflectGetter.java @@ -1,34 +1,15 @@ -/** - * 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.reflect; - -/** - * The getter implementation for a given bean property. - */ -public interface BeanReflectGetter { - - /** - * Return the value of a given bean property. - */ - public Object get(Object bean); - - public Object getIntercept(Object bean); - -} +package com.avaje.ebeaninternal.server.reflect; + +/** + * The getter implementation for a given bean property. + */ +public interface BeanReflectGetter { + + /** + * Return the value of a given bean property. + */ + public Object get(Object bean); + + public Object getIntercept(Object bean); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflectSetter.java b/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflectSetter.java index f9cdca051..63bb38743 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflectSetter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/reflect/BeanReflectSetter.java @@ -1,40 +1,21 @@ -/** - * 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.reflect; - -/** - * The setter for a given bean property. - */ -public interface BeanReflectSetter { - - /** - * Set the property value of a bean. - */ - public void set(Object bean, Object value); - - /** - * Set the property value of a bean with interception checks. - *

      - * This could invoke lazy loading and or oldValues creation. - *

      - */ - public void setIntercept(Object bean, Object value); - -} +package com.avaje.ebeaninternal.server.reflect; + +/** + * The setter for a given bean property. + */ +public interface BeanReflectSetter { + + /** + * Set the property value of a bean. + */ + public void set(Object bean, Object value); + + /** + * Set the property value of a bean with interception checks. + *

      + * This could invoke lazy loading and or oldValues creation. + *

      + */ + public void setIntercept(Object bean, Object value); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManager.java b/src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManager.java index e738a7dcb..6de21d307 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManager.java @@ -1,48 +1,29 @@ -/** - * 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.resource; - -import java.io.File; - -import com.avaje.ebeaninternal.server.lib.resource.ResourceSource; - -/** - * The ResourceManager implementation. - */ -public class ResourceManager { - - final ResourceSource resourceSource; - - final File autofetchDir; - - public ResourceManager(ResourceSource resourceSource, File autofetchDir) { - this.resourceSource = resourceSource; - this.autofetchDir = autofetchDir; - } - - public ResourceSource getResourceSource() { - return resourceSource; - } - - public File getAutofetchDirectory() { - return autofetchDir; - } - -} +package com.avaje.ebeaninternal.server.resource; + +import java.io.File; + +import com.avaje.ebeaninternal.server.lib.resource.ResourceSource; + +/** + * The ResourceManager implementation. + */ +public class ResourceManager { + + final ResourceSource resourceSource; + + final File autofetchDir; + + public ResourceManager(ResourceSource resourceSource, File autofetchDir) { + this.resourceSource = resourceSource; + this.autofetchDir = autofetchDir; + } + + public ResourceSource getResourceSource() { + return resourceSource; + } + + public File getAutofetchDirectory() { + return autofetchDir; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManagerFactory.java b/src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManagerFactory.java index d3fe2c691..c71ac9af8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManagerFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManagerFactory.java @@ -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.resource; - -import java.io.File; -import java.util.logging.Logger; - -import javax.servlet.ServletContext; - -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebeaninternal.server.lib.resource.DirectoryFinder; -import com.avaje.ebeaninternal.server.lib.resource.FileResourceSource; -import com.avaje.ebeaninternal.server.lib.resource.ResourceSource; -import com.avaje.ebeaninternal.server.lib.resource.UrlResourceSource; -import com.avaje.ebeaninternal.server.lib.util.NotFoundException; - -/** - * Creates a ResourceManager for a server depending on the avaje.properties. - *

      - * This can use URL based resource loading for web applications or file based - * otherwise. - *

      - */ -public class ResourceManagerFactory { - - private static final Logger logger = Logger.getLogger(ResourceManagerFactory.class.getName()); - - /** - * Construct with the properties for a server. - */ - public ResourceManagerFactory() { - } - - /** - * Create the resource manager given the properties for this server. - */ - public static ResourceManager createResourceManager(ServerConfig serverConfig) { - - ResourceSource resourceSource = createResourceSource(serverConfig); - File autofetchDir = getAutofetchDir(serverConfig, resourceSource); - - return new ResourceManager(resourceSource, autofetchDir); - } - - - /** - * Return the directory that autofetch file goes into. - */ - protected static File getAutofetchDir(ServerConfig serverConfig, ResourceSource resourceSource) { - - String dir = null; - if (serverConfig.getAutofetchConfig() != null) { - dir = serverConfig.getAutofetchConfig().getLogDirectoryWithEval(); - } - if (dir != null) { - return new File(dir); - } - - String realPath = resourceSource.getRealPath(); - if (realPath != null) { - return new File(realPath); - - } else { - throw new RuntimeException("No autofetch directory set?"); - } - } - - /** - * Return the resource loader for external sql files. - *

      - * This can be url based (for webapps) or otherwise file based. - *

      - */ - protected static ResourceSource createResourceSource(ServerConfig serverConfig) { - - // default for web application, override this for file system - String defaultDir = serverConfig.getResourceDirectory(); - - - // the default... check if a webapp first... - ServletContext sc = GlobalProperties.getServletContext(); - if (sc != null) { - // servlet container so use ServletContext.getResource() - if (defaultDir == null) { - defaultDir = "WEB-INF/ebean"; - } - return new UrlResourceSource(sc, defaultDir); - - } - // use File System directory - return createFileSource(defaultDir); - } - - private static ResourceSource createFileSource(String fileDir) { - - if (fileDir != null) { - // explicitly stated so - File dir = new File(fileDir); - if (dir.exists()) { - logger.info("ResourceManager initialised: type[file] [" + fileDir + "]"); - return new FileResourceSource(fileDir); - } else { - String msg = "ResourceManager could not find directory [" + fileDir + "]"; - throw new NotFoundException(msg); - } - } - - // try to guess the directory starting from the current working - // directory, and searching to a maximum depth of 3 subdirectories - File guessDir = DirectoryFinder.find(null, "WEB-INF", 3); - if (guessDir != null) { - // Typically this means we found the WEB-INF directory below the - // current working directory - logger.info("ResourceManager initialised: type[file] [" + guessDir.getPath() + "]"); - return new FileResourceSource(guessDir.getPath()); - } - - // default to the current working directory - File workingDir = new File("."); - return new FileResourceSource(workingDir); - } - -} +package com.avaje.ebeaninternal.server.resource; + +import java.io.File; +import java.util.logging.Logger; + +import javax.servlet.ServletContext; + +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebeaninternal.server.lib.resource.DirectoryFinder; +import com.avaje.ebeaninternal.server.lib.resource.FileResourceSource; +import com.avaje.ebeaninternal.server.lib.resource.ResourceSource; +import com.avaje.ebeaninternal.server.lib.resource.UrlResourceSource; +import com.avaje.ebeaninternal.server.lib.util.NotFoundException; + +/** + * Creates a ResourceManager for a server depending on the avaje.properties. + *

      + * This can use URL based resource loading for web applications or file based + * otherwise. + *

      + */ +public class ResourceManagerFactory { + + private static final Logger logger = Logger.getLogger(ResourceManagerFactory.class.getName()); + + /** + * Construct with the properties for a server. + */ + public ResourceManagerFactory() { + } + + /** + * Create the resource manager given the properties for this server. + */ + public static ResourceManager createResourceManager(ServerConfig serverConfig) { + + ResourceSource resourceSource = createResourceSource(serverConfig); + File autofetchDir = getAutofetchDir(serverConfig, resourceSource); + + return new ResourceManager(resourceSource, autofetchDir); + } + + + /** + * Return the directory that autofetch file goes into. + */ + protected static File getAutofetchDir(ServerConfig serverConfig, ResourceSource resourceSource) { + + String dir = null; + if (serverConfig.getAutofetchConfig() != null) { + dir = serverConfig.getAutofetchConfig().getLogDirectoryWithEval(); + } + if (dir != null) { + return new File(dir); + } + + String realPath = resourceSource.getRealPath(); + if (realPath != null) { + return new File(realPath); + + } else { + throw new RuntimeException("No autofetch directory set?"); + } + } + + /** + * Return the resource loader for external sql files. + *

      + * This can be url based (for webapps) or otherwise file based. + *

      + */ + protected static ResourceSource createResourceSource(ServerConfig serverConfig) { + + // default for web application, override this for file system + String defaultDir = serverConfig.getResourceDirectory(); + + + // the default... check if a webapp first... + ServletContext sc = GlobalProperties.getServletContext(); + if (sc != null) { + // servlet container so use ServletContext.getResource() + if (defaultDir == null) { + defaultDir = "WEB-INF/ebean"; + } + return new UrlResourceSource(sc, defaultDir); + + } + // use File System directory + return createFileSource(defaultDir); + } + + private static ResourceSource createFileSource(String fileDir) { + + if (fileDir != null) { + // explicitly stated so + File dir = new File(fileDir); + if (dir.exists()) { + logger.info("ResourceManager initialised: type[file] [" + fileDir + "]"); + return new FileResourceSource(fileDir); + } else { + String msg = "ResourceManager could not find directory [" + fileDir + "]"; + throw new NotFoundException(msg); + } + } + + // try to guess the directory starting from the current working + // directory, and searching to a maximum depth of 3 subdirectories + File guessDir = DirectoryFinder.find(null, "WEB-INF", 3); + if (guessDir != null) { + // Typically this means we found the WEB-INF directory below the + // current working directory + logger.info("ResourceManager initialised: type[file] [" + guessDir.getPath() + "]"); + return new FileResourceSource(guessDir.getPath()); + } + + // default to the current working directory + File workingDir = new File("."); + return new FileResourceSource(workingDir); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/subclass/GenSuffix.java b/src/main/java/com/avaje/ebeaninternal/server/subclass/GenSuffix.java index a4902e84b..12655ac87 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/subclass/GenSuffix.java +++ b/src/main/java/com/avaje/ebeaninternal/server/subclass/GenSuffix.java @@ -1,35 +1,16 @@ -/** - * 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.subclass; - -/** - * The suffix used build a generated EntityBean class. - *

      - * Note that the server name can be appended after - *

      - */ -public interface GenSuffix { - - /** - * The suffix added to the super class name. - */ - public static final String SUFFIX = "$$EntityBean"; - -} +package com.avaje.ebeaninternal.server.subclass; + +/** + * The suffix used build a generated EntityBean class. + *

      + * Note that the server name can be appended after + *

      + */ +public interface GenSuffix { + + /** + * The suffix added to the super class name. + */ + public static final String SUFFIX = "$$EntityBean"; + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/subclass/MethodWriteReplace.java b/src/main/java/com/avaje/ebeaninternal/server/subclass/MethodWriteReplace.java index 764d33ec5..0eb9227fb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/subclass/MethodWriteReplace.java +++ b/src/main/java/com/avaje/ebeaninternal/server/subclass/MethodWriteReplace.java @@ -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.subclass; - -import com.avaje.ebean.enhance.agent.ClassMeta; -import com.avaje.ebean.enhance.agent.EnhanceConstants; -import com.avaje.ebean.enhance.asm.ClassVisitor; -import com.avaje.ebean.enhance.asm.Label; -import com.avaje.ebean.enhance.asm.MethodVisitor; -import com.avaje.ebean.enhance.asm.Opcodes; - -/** - * Add a writeReplace method to support optional serialization to vanilla beans. - * - *
      
      - * private Object writeReplace() throws ObjectStreamException {
      - * 	return ebeanIntercept.writeReplaceIntercept();
      - * }
      - * 
      - */ -public class MethodWriteReplace implements Opcodes, EnhanceConstants { - - /** - * Add a writeReplace() method. - */ - public static void add(ClassVisitor cv, ClassMeta classMeta) { - - MethodVisitor mv = cv.visitMethod(ACC_PRIVATE, "writeReplace", "()Ljava/lang/Object;", - null, new String[] { "java/io/ObjectStreamException" }); - - mv.visitCode(); - Label l0 = new Label(); - mv.visitLabel(l0); - mv.visitLineNumber(1, l0); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, classMeta.getClassName(), INTERCEPT_FIELD, L_INTERCEPT); - mv.visitMethodInsn(INVOKEVIRTUAL, C_INTERCEPT, "writeReplaceIntercept","()Ljava/lang/Object;"); - - mv.visitInsn(ARETURN); - Label l1 = new Label(); - mv.visitLabel(l1); - mv.visitLocalVariable("this", "L"+classMeta.getClassName()+";", null, l0, l1, 0); - mv.visitMaxs(0, 0); - mv.visitEnd(); - - } -} +package com.avaje.ebeaninternal.server.subclass; + +import com.avaje.ebean.enhance.agent.ClassMeta; +import com.avaje.ebean.enhance.agent.EnhanceConstants; +import com.avaje.ebean.enhance.asm.ClassVisitor; +import com.avaje.ebean.enhance.asm.Label; +import com.avaje.ebean.enhance.asm.MethodVisitor; +import com.avaje.ebean.enhance.asm.Opcodes; + +/** + * Add a writeReplace method to support optional serialization to vanilla beans. + * + *
      
      + * private Object writeReplace() throws ObjectStreamException {
      + * 	return ebeanIntercept.writeReplaceIntercept();
      + * }
      + * 
      + */ +public class MethodWriteReplace implements Opcodes, EnhanceConstants { + + /** + * Add a writeReplace() method. + */ + public static void add(ClassVisitor cv, ClassMeta classMeta) { + + MethodVisitor mv = cv.visitMethod(ACC_PRIVATE, "writeReplace", "()Ljava/lang/Object;", + null, new String[] { "java/io/ObjectStreamException" }); + + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(1, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, classMeta.getClassName(), INTERCEPT_FIELD, L_INTERCEPT); + mv.visitMethodInsn(INVOKEVIRTUAL, C_INTERCEPT, "writeReplaceIntercept","()Ljava/lang/Object;"); + + mv.visitInsn(ARETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "L"+classMeta.getClassName()+";", null, l0, l1, 0); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassFactory.java b/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassFactory.java index 56527240c..6ebe10d5f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassFactory.java @@ -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.subclass; - -import java.io.IOException; -import java.io.InputStream; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebean.enhance.agent.ClassPathClassBytesReader; -import com.avaje.ebean.enhance.agent.EnhanceConstants; -import com.avaje.ebean.enhance.agent.EnhanceContext; -import com.avaje.ebean.enhance.asm.ClassReader; -import com.avaje.ebean.enhance.asm.ClassWriter; - -/** - * Creates Classes that implement EntityBean for a given normal bean Class. - *

      - * This dynamically creates a subclass of a normal bean class. The subclass has - * method interception to handle the lazy loading of references and old values - * creation. - *

      - */ -public class SubClassFactory extends ClassLoader implements EnhanceConstants, GenSuffix { - - private static final Logger logger = Logger.getLogger(SubClassFactory.class.getName()); - - private static final int CLASS_WRITER_FLAGS = ClassWriter.COMPUTE_FRAMES + ClassWriter.COMPUTE_MAXS; - - private final EnhanceContext enhanceContext; - - private final ClassLoader parentClassLoader; - - - /** - * Create with a given ClassLoader. - */ - public SubClassFactory(ClassLoader parent, int logLevel) { - super(parent); - parentClassLoader = parent; - - ClassPathClassBytesReader reader = new ClassPathClassBytesReader(null); - enhanceContext = new EnhanceContext(reader, true, "debug="+logLevel); - } - - /** - * Create a subclass for the given bean class that implements EntityBean interface. - *

      - * The transientGetters is a list of getter methods that are considered - * no persistent. That is, when they are called the bean should NOT - * trigger creation of an 'old values' copy of the beans values. - *

      - */ - public Class create(Class normalClass, String serverName) throws IOException { - - String subClassSuffix = EnhanceConstants.SUFFIX; - if (serverName != null){ - subClassSuffix += "$"+serverName; - } - - // Note: these have periods rather than slashes - String clsName = normalClass.getName(); - String subClsName = clsName+subClassSuffix; - - try { - byte[] newClsBytes = subclassBytes(clsName, subClassSuffix); - - Class newCls = defineClass(subClsName, newClsBytes, 0, newClsBytes.length); - return newCls; - - } catch (IOException ex){ - String m = "Error creating subclass for ["+clsName+"]"; - logger.log(Level.SEVERE, m, ex); - throw ex; - - } catch (Throwable ex){ - String m = "Error creating subclass for ["+clsName+"]"; - logger.log(Level.SEVERE, m, ex); - throw new RuntimeException(ex); - } - } - - - /** - * Return byte code for the subclass. - *

      - * Note that if transientInfo is null, then no interception of getters or setters - * takes place. - *

      - */ - private byte[] subclassBytes(String className, String subClassSuffix) - throws IOException { - - String resName = className.replace('.', '/')+".class"; - - InputStream is = getResourceAsStream(resName); - - ClassReader cr = new ClassReader(is); - ClassWriter cw = new ClassWriter(CLASS_WRITER_FLAGS); - - SubClassClassAdpater ca = new SubClassClassAdpater(subClassSuffix, cw, parentClassLoader, enhanceContext); - if (ca.isLog(1)) { - ca.log(" enhancing " + className+subClassSuffix); - } - - cr.accept(ca, 0); - - return cw.toByteArray(); - } -} +package com.avaje.ebeaninternal.server.subclass; + +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebean.enhance.agent.ClassPathClassBytesReader; +import com.avaje.ebean.enhance.agent.EnhanceConstants; +import com.avaje.ebean.enhance.agent.EnhanceContext; +import com.avaje.ebean.enhance.asm.ClassReader; +import com.avaje.ebean.enhance.asm.ClassWriter; + +/** + * Creates Classes that implement EntityBean for a given normal bean Class. + *

      + * This dynamically creates a subclass of a normal bean class. The subclass has + * method interception to handle the lazy loading of references and old values + * creation. + *

      + */ +public class SubClassFactory extends ClassLoader implements EnhanceConstants, GenSuffix { + + private static final Logger logger = Logger.getLogger(SubClassFactory.class.getName()); + + private static final int CLASS_WRITER_FLAGS = ClassWriter.COMPUTE_FRAMES + ClassWriter.COMPUTE_MAXS; + + private final EnhanceContext enhanceContext; + + private final ClassLoader parentClassLoader; + + + /** + * Create with a given ClassLoader. + */ + public SubClassFactory(ClassLoader parent, int logLevel) { + super(parent); + parentClassLoader = parent; + + ClassPathClassBytesReader reader = new ClassPathClassBytesReader(null); + enhanceContext = new EnhanceContext(reader, true, "debug="+logLevel); + } + + /** + * Create a subclass for the given bean class that implements EntityBean interface. + *

      + * The transientGetters is a list of getter methods that are considered + * no persistent. That is, when they are called the bean should NOT + * trigger creation of an 'old values' copy of the beans values. + *

      + */ + public Class create(Class normalClass, String serverName) throws IOException { + + String subClassSuffix = EnhanceConstants.SUFFIX; + if (serverName != null){ + subClassSuffix += "$"+serverName; + } + + // Note: these have periods rather than slashes + String clsName = normalClass.getName(); + String subClsName = clsName+subClassSuffix; + + try { + byte[] newClsBytes = subclassBytes(clsName, subClassSuffix); + + Class newCls = defineClass(subClsName, newClsBytes, 0, newClsBytes.length); + return newCls; + + } catch (IOException ex){ + String m = "Error creating subclass for ["+clsName+"]"; + logger.log(Level.SEVERE, m, ex); + throw ex; + + } catch (Throwable ex){ + String m = "Error creating subclass for ["+clsName+"]"; + logger.log(Level.SEVERE, m, ex); + throw new RuntimeException(ex); + } + } + + + /** + * Return byte code for the subclass. + *

      + * Note that if transientInfo is null, then no interception of getters or setters + * takes place. + *

      + */ + private byte[] subclassBytes(String className, String subClassSuffix) + throws IOException { + + String resName = className.replace('.', '/')+".class"; + + InputStream is = getResourceAsStream(resName); + + ClassReader cr = new ClassReader(is); + ClassWriter cw = new ClassWriter(CLASS_WRITER_FLAGS); + + SubClassClassAdpater ca = new SubClassClassAdpater(subClassSuffix, cw, parentClassLoader, enhanceContext); + if (ca.isLog(1)) { + ca.log(" enhancing " + className+subClassSuffix); + } + + cr.accept(ca, 0); + + return cw.toByteArray(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassGenerator.java b/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassGenerator.java index 56a9249ed..941860f23 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassGenerator.java +++ b/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassGenerator.java @@ -1,272 +1,253 @@ -/** - * 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.subclass; - - -/** - * Used to generate a subclass based on a bean. - *

      - * It does not have the fields or private methods of the read class. It replaces - * the method code with calls to super instead. It may need to add hashCode() - * and equals() methods to make sure a reference is loaded prior to either of - * these methods being called. It uses writeReplace() to modify the - * serialisation. - *

      - */ -public class SubClassGenerator {// extends ClassAdapter implements Opcodes, GenConstants { - -// private static final Logger logger = LogFactory.get(SubClassGenerator.class); -// -// boolean isInterceptFieldAdded = false; -// -// boolean isAddClonable = true; -// -// boolean superHasEquals = false; -// -// ClassInfo info; -// -// MethodInfo methodInfo; -// -// boolean hasSuperClass; -// -// /** -// * Create with the ClassInfo. -// */ -// public SubClassGenerator(ClassVisitor cv, ClassInfo info) { -// super(cv); -// this.info = info; -// this.methodInfo = info.getMethodInfo(); -// } -// -// /** -// * Create the class definition replacing the className and super class. -// */ -// public void visit(int version, int access, String name, String signature, String superName, -// String[] interfaces) { -// -// // Note: These have slashes rather than periods!! -// String className = name+info.getSuffix(); -// String superClassName; -// if ("java/lang/Object".endsWith(superName)){ -// superClassName = name; -// } else { -// hasSuperClass = true; -// superClassName = name;//superName+info.getSuffix(); -// } -// -// info.setClassName(className); -// info.setSuperClassName(superClassName); -// -// -// // Note: interfaces can be an empty array but not null -// int n = 1 + interfaces.length; -// String[] c = new String[n]; -// System.arraycopy(interfaces, 0, c, 0, interfaces.length); -// -// // Add the EntityBean interface -// c[c.length - 1] = ENTITYBEAN; -// -// super.visit(version, access, className, signature, superClassName, c); -// } -// -// /** -// * The ebeanIntercept field is added once but thats all. Note the other -// * fields are defined in the superclass. -// */ -// public FieldVisitor visitField(int access, String name, String desc, String signature, -// Object value) { -// -// if (!isInterceptFieldAdded) { -// -// FieldVisitor f0 = cv.visitField(ACC_PRIVATE + ACC_VOLATILE, IDENTITY_FIELD_NAME, "Ljava/lang/Object;", null, null); -// f0.visitEnd(); -// -// FieldVisitor f1 = cv.visitField(0, INTERCEPT_FIELD_NAME, L_INTERCEPT, null, null); -// f1.visitEnd(); -// -// isInterceptFieldAdded = true; -// return null; -// } -// -// return null; -// } -// -// /** -// * Replace the method code with calls to super. Add the intercept code as -// * required. -// */ -// public MethodVisitor visitMethod(int access, String name, String desc, String signature, -// String[] exceptions) { -// -// boolean isPrivate = ((access & Opcodes.ACC_PRIVATE) != 0); -// boolean isStatic = ((access & Opcodes.ACC_STATIC) != 0); -// if (isPrivate || isStatic) { -// // no intercept on static or private methods -// return null; -// } -// // the key to look up in methodInfo -// String methodKey = name + ":" + desc; -// -// if (hasSuperClass){ -// if (logger.isLoggable(Level.FINER)){ -// String msg = "existing methods "+info.getClassName()+" "+methodKey; -// logger.finer(msg); -// } -// } -// -// VisitMethodParams params = new VisitMethodParams(cv, access, name, desc, signature, exceptions); -// -// if (methodInfo.isSet(methodKey)) { -// // for persistent properties excluding assoc Many's & id -// // ie. Old values not created for id or assoc many. -// return new ProxySetterMethod(params, info, methodInfo); -// } -// -// if (methodInfo.isGet(methodKey)) { -// // for persistent properties excluding id properties. -// // ie. reference loading not fired for id properties. -// return new ProxyGetterMethod(params, info); -// } -// -// if ("".equals(name)) { -// return new ProxyConstructor(params, info); -// } -// -// if ("hashCode:()I".equals(methodKey)) { -// return new ProxyMethod(params, info); -// } -// -// if ("clone:()Ljava/lang/Object;".equals(methodKey)) { -// // SuperClass has a clone() method -// isAddClonable = false; -// return new MethodClone(params, info); -// } -// -// if ("toString:()Ljava/lang/String;".equals(methodKey)) { -// // No intercept on toString() as used by debuggers etc -// return null; -// } -// if ("hashCode:()I".equals(methodKey)) { -// return null; -// } -// if ("equals:(Ljava/lang/Object;)Z".equals(methodKey)) { -// superHasEquals = true; -// return null; -// } -// -// return null; -// } -// -// /** -// * Add methods to get and set the entityBeanIntercept. Also add the -// * writeReplace method to control serialisation. -// */ -// public void visitEnd() { -// -// if (isAddClonable){ -// // super has not overwritten the clone() method. -// // we will add the clone() method in case a super of the super has clone() -// String[] exceptions = new String[] { "java/lang/CloneNotSupportedException" }; -// VisitMethodParams params = new VisitMethodParams(cv, ACC_PUBLIC, "clone", "()Ljava/lang/Object;", null, exceptions); -// MethodClone methodClone = new MethodClone(params, info); -// methodClone.visitCode(); -// } -// -// MethodInfo methodInfo = info.getMethodInfo(); -// if (methodInfo.isEmbedded()){ -// // don't override equals etc when it is an embedded bean -// // Either EmbeddedId or a Embeddable -// -// } else if (methodInfo.overrideEquals(superHasEquals)) { -// // we want to generate a equals() hashCode() and ebeanGetIndentity() -// // methods so that the generated subclass has built in equals() support. -// -// if (methodInfo.getIdGetter() == null) { -// if (methodInfo.isSqlSelectBased()){ -// // This could be common for reporting type beans based on -// // sql-select that use group by type queries. -// } else { -// String m = "Can not generate equals for ["+info.getClassName(); -// m += "]. Concatinated id?"; -// logger.warning(m); -// } -// } else { -// -// if (generateEbeanGetIdentityMethod()){ -// // add equals() -// MethodEquals.add(cv, info); -// -// // add hashCode() -// MethodHashCode.add(cv, info); -// } -// } -// } -// -// // add additional getters from super class inheritance -// List additionalGetters = methodInfo.getAdditionalGetters(); -// for (MethodDesc methodDesc : additionalGetters) { -// VisitMethodParams params = new VisitMethodParams(cv, ACC_PUBLIC, methodDesc); -// ProxyGetterMethod getter = new ProxyGetterMethod(params, info); -// getter.visitCode(); -// } -// -// // add additional setters from super class inheritance -// List additionalSetters = methodInfo.getAdditionalSetters(); -// for (MethodDesc methodDesc : additionalSetters) { -// VisitMethodParams params = new VisitMethodParams(cv, ACC_PUBLIC, methodDesc); -// ProxySetterMethod setter = new ProxySetterMethod(params, info, methodInfo); -// setter.visitCode(); -// } -// -// // add set get methods for ebeanIntecept -// MethodGetSetIntercept.add(cv, info); -// -// // add a writeReplace method to control serialisation -// MethodWriteReplace.add(cv, info); -// -// super.visitEnd(); -// } -// -// private boolean generateEbeanGetIdentityMethod() { -// String idGetterDesc = methodInfo.getIdGetterDesc(); -// if (idGetterDesc.equals("()I")) { -// // int version of ebeanGetIndentity() -// MethodEbeanGetIdentityInt.add(cv, info); -// return true; -// -// } else if (idGetterDesc.equals("()J")) { -// // long version of ebeanGetIndentity() -// MethodEbeanGetIdentityLong.add(cv, info); -// return true; -// -// } else if (idGetterDesc.length() > 5) { -// // Object version of ebeanGetIndentity() -// MethodEbeanGetIdentity.add(cv, info); -// return true; -// -// } else { -// String m = "Can not generate equals for ["+info.getClassName(); -// m += "] due to type of id property: "+idGetterDesc; -// logger.warning(m); -// return false; -// } -// } - -} +package com.avaje.ebeaninternal.server.subclass; + + +/** + * Used to generate a subclass based on a bean. + *

      + * It does not have the fields or private methods of the read class. It replaces + * the method code with calls to super instead. It may need to add hashCode() + * and equals() methods to make sure a reference is loaded prior to either of + * these methods being called. It uses writeReplace() to modify the + * serialisation. + *

      + */ +public class SubClassGenerator {// extends ClassAdapter implements Opcodes, GenConstants { + +// private static final Logger logger = LogFactory.get(SubClassGenerator.class); +// +// boolean isInterceptFieldAdded = false; +// +// boolean isAddClonable = true; +// +// boolean superHasEquals = false; +// +// ClassInfo info; +// +// MethodInfo methodInfo; +// +// boolean hasSuperClass; +// +// /** +// * Create with the ClassInfo. +// */ +// public SubClassGenerator(ClassVisitor cv, ClassInfo info) { +// super(cv); +// this.info = info; +// this.methodInfo = info.getMethodInfo(); +// } +// +// /** +// * Create the class definition replacing the className and super class. +// */ +// public void visit(int version, int access, String name, String signature, String superName, +// String[] interfaces) { +// +// // Note: These have slashes rather than periods!! +// String className = name+info.getSuffix(); +// String superClassName; +// if ("java/lang/Object".endsWith(superName)){ +// superClassName = name; +// } else { +// hasSuperClass = true; +// superClassName = name;//superName+info.getSuffix(); +// } +// +// info.setClassName(className); +// info.setSuperClassName(superClassName); +// +// +// // Note: interfaces can be an empty array but not null +// int n = 1 + interfaces.length; +// String[] c = new String[n]; +// System.arraycopy(interfaces, 0, c, 0, interfaces.length); +// +// // Add the EntityBean interface +// c[c.length - 1] = ENTITYBEAN; +// +// super.visit(version, access, className, signature, superClassName, c); +// } +// +// /** +// * The ebeanIntercept field is added once but thats all. Note the other +// * fields are defined in the superclass. +// */ +// public FieldVisitor visitField(int access, String name, String desc, String signature, +// Object value) { +// +// if (!isInterceptFieldAdded) { +// +// FieldVisitor f0 = cv.visitField(ACC_PRIVATE + ACC_VOLATILE, IDENTITY_FIELD_NAME, "Ljava/lang/Object;", null, null); +// f0.visitEnd(); +// +// FieldVisitor f1 = cv.visitField(0, INTERCEPT_FIELD_NAME, L_INTERCEPT, null, null); +// f1.visitEnd(); +// +// isInterceptFieldAdded = true; +// return null; +// } +// +// return null; +// } +// +// /** +// * Replace the method code with calls to super. Add the intercept code as +// * required. +// */ +// public MethodVisitor visitMethod(int access, String name, String desc, String signature, +// String[] exceptions) { +// +// boolean isPrivate = ((access & Opcodes.ACC_PRIVATE) != 0); +// boolean isStatic = ((access & Opcodes.ACC_STATIC) != 0); +// if (isPrivate || isStatic) { +// // no intercept on static or private methods +// return null; +// } +// // the key to look up in methodInfo +// String methodKey = name + ":" + desc; +// +// if (hasSuperClass){ +// if (logger.isLoggable(Level.FINER)){ +// String msg = "existing methods "+info.getClassName()+" "+methodKey; +// logger.finer(msg); +// } +// } +// +// VisitMethodParams params = new VisitMethodParams(cv, access, name, desc, signature, exceptions); +// +// if (methodInfo.isSet(methodKey)) { +// // for persistent properties excluding assoc Many's & id +// // ie. Old values not created for id or assoc many. +// return new ProxySetterMethod(params, info, methodInfo); +// } +// +// if (methodInfo.isGet(methodKey)) { +// // for persistent properties excluding id properties. +// // ie. reference loading not fired for id properties. +// return new ProxyGetterMethod(params, info); +// } +// +// if ("".equals(name)) { +// return new ProxyConstructor(params, info); +// } +// +// if ("hashCode:()I".equals(methodKey)) { +// return new ProxyMethod(params, info); +// } +// +// if ("clone:()Ljava/lang/Object;".equals(methodKey)) { +// // SuperClass has a clone() method +// isAddClonable = false; +// return new MethodClone(params, info); +// } +// +// if ("toString:()Ljava/lang/String;".equals(methodKey)) { +// // No intercept on toString() as used by debuggers etc +// return null; +// } +// if ("hashCode:()I".equals(methodKey)) { +// return null; +// } +// if ("equals:(Ljava/lang/Object;)Z".equals(methodKey)) { +// superHasEquals = true; +// return null; +// } +// +// return null; +// } +// +// /** +// * Add methods to get and set the entityBeanIntercept. Also add the +// * writeReplace method to control serialisation. +// */ +// public void visitEnd() { +// +// if (isAddClonable){ +// // super has not overwritten the clone() method. +// // we will add the clone() method in case a super of the super has clone() +// String[] exceptions = new String[] { "java/lang/CloneNotSupportedException" }; +// VisitMethodParams params = new VisitMethodParams(cv, ACC_PUBLIC, "clone", "()Ljava/lang/Object;", null, exceptions); +// MethodClone methodClone = new MethodClone(params, info); +// methodClone.visitCode(); +// } +// +// MethodInfo methodInfo = info.getMethodInfo(); +// if (methodInfo.isEmbedded()){ +// // don't override equals etc when it is an embedded bean +// // Either EmbeddedId or a Embeddable +// +// } else if (methodInfo.overrideEquals(superHasEquals)) { +// // we want to generate a equals() hashCode() and ebeanGetIndentity() +// // methods so that the generated subclass has built in equals() support. +// +// if (methodInfo.getIdGetter() == null) { +// if (methodInfo.isSqlSelectBased()){ +// // This could be common for reporting type beans based on +// // sql-select that use group by type queries. +// } else { +// String m = "Can not generate equals for ["+info.getClassName(); +// m += "]. Concatinated id?"; +// logger.warning(m); +// } +// } else { +// +// if (generateEbeanGetIdentityMethod()){ +// // add equals() +// MethodEquals.add(cv, info); +// +// // add hashCode() +// MethodHashCode.add(cv, info); +// } +// } +// } +// +// // add additional getters from super class inheritance +// List additionalGetters = methodInfo.getAdditionalGetters(); +// for (MethodDesc methodDesc : additionalGetters) { +// VisitMethodParams params = new VisitMethodParams(cv, ACC_PUBLIC, methodDesc); +// ProxyGetterMethod getter = new ProxyGetterMethod(params, info); +// getter.visitCode(); +// } +// +// // add additional setters from super class inheritance +// List additionalSetters = methodInfo.getAdditionalSetters(); +// for (MethodDesc methodDesc : additionalSetters) { +// VisitMethodParams params = new VisitMethodParams(cv, ACC_PUBLIC, methodDesc); +// ProxySetterMethod setter = new ProxySetterMethod(params, info, methodInfo); +// setter.visitCode(); +// } +// +// // add set get methods for ebeanIntecept +// MethodGetSetIntercept.add(cv, info); +// +// // add a writeReplace method to control serialisation +// MethodWriteReplace.add(cv, info); +// +// super.visitEnd(); +// } +// +// private boolean generateEbeanGetIdentityMethod() { +// String idGetterDesc = methodInfo.getIdGetterDesc(); +// if (idGetterDesc.equals("()I")) { +// // int version of ebeanGetIndentity() +// MethodEbeanGetIdentityInt.add(cv, info); +// return true; +// +// } else if (idGetterDesc.equals("()J")) { +// // long version of ebeanGetIndentity() +// MethodEbeanGetIdentityLong.add(cv, info); +// return true; +// +// } else if (idGetterDesc.length() > 5) { +// // Object version of ebeanGetIndentity() +// MethodEbeanGetIdentity.add(cv, info); +// return true; +// +// } else { +// String m = "Can not generate equals for ["+info.getClassName(); +// m += "] due to type of id property: "+idGetterDesc; +// logger.warning(m); +// return false; +// } +// } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassManager.java b/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassManager.java index 35794357a..fbcdede1e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassManager.java @@ -1,121 +1,102 @@ -/** - * 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.subclass; - -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebean.enhance.agent.EnhanceConstants; -import com.avaje.ebeaninternal.api.ClassUtil; - -/** - * Creates and caches the dynamically generated subclasses. - *

      - * That is, the 'EntityBean' classes are dynamically generated subclasses of the - * 'vanilla' classes. - *

      - */ -public class SubClassManager implements EnhanceConstants { - - private static final Logger logger = Logger.getLogger(SubClassManager.class.getName()); - - private final ConcurrentHashMap> clzMap; - - private final SubClassFactory subclassFactory; - - private final String serverName; - - /** - * The log level for debugging subclass generation/enhancement. - */ - private final int logLevel; - - /** - * Construct with the ClassLoader used to load Ebean.class. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public SubClassManager(ServerConfig serverConfig) { - - String s = serverConfig.getProperty("subClassManager.preferContextClassloader", "true"); - final boolean preferContext = "true".equalsIgnoreCase(s); - - this.serverName = serverConfig.getName(); - this.logLevel = serverConfig.getEnhanceLogLevel(); - this.clzMap = new ConcurrentHashMap>(); - - try { - subclassFactory = (SubClassFactory) AccessController - .doPrivileged(new PrivilegedExceptionAction() { - public Object run() { - ClassLoader cl = ClassUtil.getClassLoader(this.getClass(), preferContext); - logger.info("SubClassFactory parent ClassLoader ["+cl.getClass().getName()+"]"); - return new SubClassFactory(cl, logLevel); - } - }); - } catch (PrivilegedActionException e) { - throw new PersistenceException(e); - } - } - - /** - * Resolve the Class for the class name. - *

      - * The methodInfo is used to determine the method interception on the - * generated class. - *

      - *

      - * If the class has already been generated then it is returned out of a - * cache. - *

      - */ - public Class resolve(String name) { - - synchronized (this) { - String superName = SubClassUtil.getSuperClassName(name); - Class clz = clzMap.get(superName); - if (clz == null) { - clz = createClass(superName); - clzMap.put(superName, clz); - } - return clz; - } - } - - private Class createClass(String name) { - - try { - - Class superClass = Class.forName(name, true, subclassFactory.getParent()); - - return subclassFactory.create(superClass, serverName); - - } catch (Exception ex) { - String m = "Error creating subclass for [" + name + "]"; - throw new PersistenceException(m, ex); - } - } - -} +package com.avaje.ebeaninternal.server.subclass; + +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.enhance.agent.EnhanceConstants; +import com.avaje.ebeaninternal.api.ClassUtil; + +/** + * Creates and caches the dynamically generated subclasses. + *

      + * That is, the 'EntityBean' classes are dynamically generated subclasses of the + * 'vanilla' classes. + *

      + */ +public class SubClassManager implements EnhanceConstants { + + private static final Logger logger = Logger.getLogger(SubClassManager.class.getName()); + + private final ConcurrentHashMap> clzMap; + + private final SubClassFactory subclassFactory; + + private final String serverName; + + /** + * The log level for debugging subclass generation/enhancement. + */ + private final int logLevel; + + /** + * Construct with the ClassLoader used to load Ebean.class. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public SubClassManager(ServerConfig serverConfig) { + + String s = serverConfig.getProperty("subClassManager.preferContextClassloader", "true"); + final boolean preferContext = "true".equalsIgnoreCase(s); + + this.serverName = serverConfig.getName(); + this.logLevel = serverConfig.getEnhanceLogLevel(); + this.clzMap = new ConcurrentHashMap>(); + + try { + subclassFactory = (SubClassFactory) AccessController + .doPrivileged(new PrivilegedExceptionAction() { + public Object run() { + ClassLoader cl = ClassUtil.getClassLoader(this.getClass(), preferContext); + logger.info("SubClassFactory parent ClassLoader ["+cl.getClass().getName()+"]"); + return new SubClassFactory(cl, logLevel); + } + }); + } catch (PrivilegedActionException e) { + throw new PersistenceException(e); + } + } + + /** + * Resolve the Class for the class name. + *

      + * The methodInfo is used to determine the method interception on the + * generated class. + *

      + *

      + * If the class has already been generated then it is returned out of a + * cache. + *

      + */ + public Class resolve(String name) { + + synchronized (this) { + String superName = SubClassUtil.getSuperClassName(name); + Class clz = clzMap.get(superName); + if (clz == null) { + clz = createClass(superName); + clzMap.put(superName, clz); + } + return clz; + } + } + + private Class createClass(String name) { + + try { + + Class superClass = Class.forName(name, true, subclassFactory.getParent()); + + return subclassFactory.create(superClass, serverName); + + } catch (Exception ex) { + String m = "Error creating subclass for [" + name + "]"; + throw new PersistenceException(m, ex); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassUtil.java b/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassUtil.java index 7112a6926..d8ca991f0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassUtil.java +++ b/src/main/java/com/avaje/ebeaninternal/server/subclass/SubClassUtil.java @@ -1,47 +1,28 @@ -/** - * 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.subclass; - - -/** - * Helper methods for generated sub classes. - */ -public class SubClassUtil implements GenSuffix { - - /** - * Return true if this is a generated class. - */ - public static boolean isSubClass(String className) { - - return (className.lastIndexOf(SUFFIX) != -1); - } - - /** - * Return the super class name given the generated className. - */ - public static String getSuperClassName(String className){ - int dPos = className.lastIndexOf(SUFFIX); - if (dPos > -1){ - return className.substring(0, dPos); - } - return className; - } - -} +package com.avaje.ebeaninternal.server.subclass; + + +/** + * Helper methods for generated sub classes. + */ +public class SubClassUtil implements GenSuffix { + + /** + * Return true if this is a generated class. + */ + public static boolean isSubClass(String className) { + + return (className.lastIndexOf(SUFFIX) != -1); + } + + /** + * Return the super class name given the generated className. + */ + public static String getSuperClassName(String className){ + int dPos = className.lastIndexOf(SUFFIX); + if (dPos > -1){ + return className.substring(0, dPos); + } + return className; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/csv/TCsvReader.java b/src/main/java/com/avaje/ebeaninternal/server/text/csv/TCsvReader.java index fb04ba9b3..a2aae50de 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/csv/TCsvReader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/csv/TCsvReader.java @@ -1,388 +1,369 @@ -/** - * 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.text.csv; - -import java.io.Reader; -import java.sql.Types; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Locale; - -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.text.StringParser; -import com.avaje.ebean.text.TextException; -import com.avaje.ebean.text.TimeStringParser; -import com.avaje.ebean.text.csv.CsvCallback; -import com.avaje.ebean.text.csv.CsvReader; -import com.avaje.ebean.text.csv.DefaultCsvCallback; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; - -/** - * - * @author rbygrave - */ -public class TCsvReader implements CsvReader { - - // private static final Logger logger = - // Logger.getLogger(TCsvReader.class.getName()); - - private static final TimeStringParser TIME_PARSER = new TimeStringParser(); - - private final EbeanServer server; - - private final BeanDescriptor descriptor; - - private final List columnList = new ArrayList(); - - private final CsvColumn ignoreColumn = new CsvColumn(); - - private boolean treatEmptyStringAsNull = true; - - private boolean hasHeader; - - private int logInfoFrequency = 1000; - - private String defaultTimeFormat = "HH:mm:ss"; - private String defaultDateFormat = "yyyy-MM-dd"; - private String defaultTimestampFormat = "yyyy-MM-dd hh:mm:ss.fffffffff"; - private Locale defaultLocale = Locale.getDefault(); - - /** - * The batch size used for JDBC statement batching. - */ - protected int persistBatchSize = 30; - - private boolean addPropertiesFromHeader; - - // private String addHeaderDateTimeFormat; - // private Locale addHeaderLocale; - - public TCsvReader(EbeanServer server, BeanDescriptor descriptor) { - this.server = server; - this.descriptor = descriptor; - } - - public void setDefaultLocale(Locale defaultLocale) { - this.defaultLocale = defaultLocale; - } - - public void setDefaultTimeFormat(String defaultTimeFormat) { - this.defaultTimeFormat = defaultTimeFormat; - } - - public void setDefaultDateFormat(String defaultDateFormat) { - this.defaultDateFormat = defaultDateFormat; - } - - public void setDefaultTimestampFormat(String defaultTimestampFormat) { - this.defaultTimestampFormat = defaultTimestampFormat; - } - - public void setPersistBatchSize(int persistBatchSize) { - this.persistBatchSize = persistBatchSize; - } - - public void setIgnoreHeader() { - setHasHeader(true, false); - } - - public void setAddPropertiesFromHeader() { - setHasHeader(true, true); - } - - public void setHasHeader(boolean hasHeader, boolean addPropertiesFromHeader) { - this.hasHeader = hasHeader; - this.addPropertiesFromHeader = addPropertiesFromHeader; - } - - public void setLogInfoFrequency(int logInfoFrequency) { - this.logInfoFrequency = logInfoFrequency; - } - - public void addIgnore() { - columnList.add(ignoreColumn); - } - - public void addProperty(String propertyName) { - addProperty(propertyName, null); - } - - public void addReference(String propertyName) { - addProperty(propertyName, null, true); - } - - public void addProperty(String propertyName, StringParser parser) { - addProperty(propertyName, parser, false); - } - - public void addDateTime(String propertyName, String dateTimeFormat) { - addDateTime(propertyName, dateTimeFormat, Locale.getDefault()); - } - - public void addDateTime(String propertyName, String dateTimeFormat, Locale locale) { - - ElPropertyValue elProp = descriptor.getElGetValue(propertyName); - if (!elProp.isDateTimeCapable()) { - throw new TextException("Property " + propertyName + " is not DateTime capable"); - } - - if (dateTimeFormat == null) { - dateTimeFormat = getDefaultDateTimeFormat(elProp.getJdbcType()); - } - - if (locale == null) { - locale = defaultLocale; - } - - SimpleDateFormat sdf = new SimpleDateFormat(dateTimeFormat, locale); - DateTimeParser parser = new DateTimeParser(sdf, dateTimeFormat, elProp); - - CsvColumn column = new CsvColumn(elProp, parser, false); - columnList.add(column); - } - - private String getDefaultDateTimeFormat(int jdbcType) { - switch (jdbcType) { - case Types.TIME: - return defaultTimeFormat; - case Types.DATE: - return defaultDateFormat; - case Types.TIMESTAMP: - return defaultTimestampFormat; - - default: - throw new RuntimeException("Expected java.sql.Types TIME,DATE or TIMESTAMP but got [" + jdbcType + "]"); - } - } - - public void addProperty(String propertyName, StringParser parser, boolean reference) { - - ElPropertyValue elProp = descriptor.getElGetValue(propertyName); - if (parser == null) { - parser = elProp.getStringParser(); - } - CsvColumn column = new CsvColumn(elProp, parser, reference); - columnList.add(column); - } - - public void process(Reader reader) throws Exception { - DefaultCsvCallback callback = new DefaultCsvCallback(persistBatchSize, logInfoFrequency); - process(reader, callback); - } - - public void process(Reader reader, CsvCallback callback) throws Exception { - - if (reader == null) { - throw new NullPointerException("reader is null?"); - } - if (callback == null) { - throw new NullPointerException("callback is null?"); - } - - CsvUtilReader utilReader = new CsvUtilReader(reader); - - callback.begin(server); - - int row = 0; - - if (hasHeader) { - String[] line = utilReader.readNext(); - if (addPropertiesFromHeader) { - addPropertiesFromHeader(line); - } - callback.readHeader(line); - } - - try { - do { - ++row; - String[] line = utilReader.readNext(); - if (line == null) { - --row; - break; - } - - if (callback.processLine(row, line)) { - // the line content is expected to be ok for processing - if (line.length != columnList.size()) { - // we have not got the expected number of columns - String msg = "Error at line " + row + ". Expected [" + columnList.size() + "] columns " - + "but instead we have [" + line.length + "]. Line[" + Arrays.toString(line) + "]"; - throw new TextException(msg); - } - - T bean = buildBeanFromLineContent(row, line); - - callback.processBean(row, line, bean); - - } - } while (true); - - callback.end(row); - - } catch (Exception e) { - // notify that an error occured so that any - // transaction can be rolled back if required - callback.endWithError(row, e); - throw e; - } - } - - private void addPropertiesFromHeader(String[] line) { - for (int i = 0; i < line.length; i++) { - ElPropertyValue elProp = descriptor.getElGetValue(line[i]); - if (elProp == null) { - throw new TextException("Property [" + line[i] + "] not found"); - } - - if (Types.TIME == elProp.getJdbcType()) { - addProperty(line[i], TIME_PARSER); - - } else if (isDateTimeType(elProp.getJdbcType())) { - addDateTime(line[i], null, null); - - } else if (elProp.isAssocProperty()) { - BeanPropertyAssocOne assocOne = (BeanPropertyAssocOne) elProp.getBeanProperty(); - String idProp = assocOne.getBeanDescriptor().getIdBinder().getIdProperty(); - addReference(line[i] + "." + idProp); - } else { - addProperty(line[i]); - } - } - } - - private boolean isDateTimeType(int t) { - if (t == Types.TIMESTAMP || t == Types.DATE || t == Types.TIME) { - return true; - } - return false; - } - - @SuppressWarnings("unchecked") - protected T buildBeanFromLineContent(int row, String[] line) { - - try { - EntityBean entityBean = descriptor.createEntityBean(); - T bean = (T) entityBean; - - int columnPos = 0; - for (; columnPos < line.length; columnPos++) { - convertAndSetColumn(columnPos, line[columnPos], entityBean); - } - - return bean; - - } catch (RuntimeException e) { - String msg = "Error at line: " + row + " line[" + Arrays.toString(line) + "]"; - throw new RuntimeException(msg, e); - } - } - - protected void convertAndSetColumn(int columnPos, String strValue, Object bean) { - - strValue = strValue.trim(); - - if (strValue.length() == 0 && treatEmptyStringAsNull) { - return; - } - - CsvColumn c = columnList.get(columnPos); - c.convertAndSet(strValue, bean); - } - - /** - * Processes a column in the csv content. - */ - public static class CsvColumn { - - private final ElPropertyValue elProp; - private final StringParser parser; - private final boolean ignore; - private final boolean reference; - - /** - * Constructor for the IGNORE column. - */ - private CsvColumn() { - this.elProp = null; - this.parser = null; - this.reference = false; - this.ignore = true; - } - - /** - * Construct with a property and parser. - */ - public CsvColumn(ElPropertyValue elProp, StringParser parser, boolean reference) { - this.elProp = elProp; - this.parser = parser; - this.reference = reference; - this.ignore = false; - } - - /** - * Convert the string to the appropriate value and set it to the bean. - */ - public void convertAndSet(String strValue, Object bean) { - - if (!ignore) { - Object value = parser.parse(strValue); - elProp.elSetValue(bean, value, true, reference); - } - } - } - - /** - * A StringParser for converting custom date/time/datetime strings into - * appropriate java types (Date, Calendar, SQL Date, Time, Timestamp, JODA - * etc). - */ - private static class DateTimeParser implements StringParser { - - private final DateFormat dateFormat; - private final ElPropertyValue elProp; - private final String format; - - DateTimeParser(DateFormat dateFormat, String format, ElPropertyValue elProp) { - this.dateFormat = dateFormat; - this.elProp = elProp; - this.format = format; - } - - public Object parse(String value) { - try { - Date dt = dateFormat.parse(value); - return elProp.parseDateTime(dt.getTime()); - - } catch (ParseException e) { - throw new TextException("Error parsing [" + value + "] using format[" + format + "]", e); - } - } - - } -} +package com.avaje.ebeaninternal.server.text.csv; + +import java.io.Reader; +import java.sql.Types; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.text.StringParser; +import com.avaje.ebean.text.TextException; +import com.avaje.ebean.text.TimeStringParser; +import com.avaje.ebean.text.csv.CsvCallback; +import com.avaje.ebean.text.csv.CsvReader; +import com.avaje.ebean.text.csv.DefaultCsvCallback; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; + +/** + * + * @author rbygrave + */ +public class TCsvReader implements CsvReader { + + // private static final Logger logger = + // Logger.getLogger(TCsvReader.class.getName()); + + private static final TimeStringParser TIME_PARSER = new TimeStringParser(); + + private final EbeanServer server; + + private final BeanDescriptor descriptor; + + private final List columnList = new ArrayList(); + + private final CsvColumn ignoreColumn = new CsvColumn(); + + private boolean treatEmptyStringAsNull = true; + + private boolean hasHeader; + + private int logInfoFrequency = 1000; + + private String defaultTimeFormat = "HH:mm:ss"; + private String defaultDateFormat = "yyyy-MM-dd"; + private String defaultTimestampFormat = "yyyy-MM-dd hh:mm:ss.fffffffff"; + private Locale defaultLocale = Locale.getDefault(); + + /** + * The batch size used for JDBC statement batching. + */ + protected int persistBatchSize = 30; + + private boolean addPropertiesFromHeader; + + // private String addHeaderDateTimeFormat; + // private Locale addHeaderLocale; + + public TCsvReader(EbeanServer server, BeanDescriptor descriptor) { + this.server = server; + this.descriptor = descriptor; + } + + public void setDefaultLocale(Locale defaultLocale) { + this.defaultLocale = defaultLocale; + } + + public void setDefaultTimeFormat(String defaultTimeFormat) { + this.defaultTimeFormat = defaultTimeFormat; + } + + public void setDefaultDateFormat(String defaultDateFormat) { + this.defaultDateFormat = defaultDateFormat; + } + + public void setDefaultTimestampFormat(String defaultTimestampFormat) { + this.defaultTimestampFormat = defaultTimestampFormat; + } + + public void setPersistBatchSize(int persistBatchSize) { + this.persistBatchSize = persistBatchSize; + } + + public void setIgnoreHeader() { + setHasHeader(true, false); + } + + public void setAddPropertiesFromHeader() { + setHasHeader(true, true); + } + + public void setHasHeader(boolean hasHeader, boolean addPropertiesFromHeader) { + this.hasHeader = hasHeader; + this.addPropertiesFromHeader = addPropertiesFromHeader; + } + + public void setLogInfoFrequency(int logInfoFrequency) { + this.logInfoFrequency = logInfoFrequency; + } + + public void addIgnore() { + columnList.add(ignoreColumn); + } + + public void addProperty(String propertyName) { + addProperty(propertyName, null); + } + + public void addReference(String propertyName) { + addProperty(propertyName, null, true); + } + + public void addProperty(String propertyName, StringParser parser) { + addProperty(propertyName, parser, false); + } + + public void addDateTime(String propertyName, String dateTimeFormat) { + addDateTime(propertyName, dateTimeFormat, Locale.getDefault()); + } + + public void addDateTime(String propertyName, String dateTimeFormat, Locale locale) { + + ElPropertyValue elProp = descriptor.getElGetValue(propertyName); + if (!elProp.isDateTimeCapable()) { + throw new TextException("Property " + propertyName + " is not DateTime capable"); + } + + if (dateTimeFormat == null) { + dateTimeFormat = getDefaultDateTimeFormat(elProp.getJdbcType()); + } + + if (locale == null) { + locale = defaultLocale; + } + + SimpleDateFormat sdf = new SimpleDateFormat(dateTimeFormat, locale); + DateTimeParser parser = new DateTimeParser(sdf, dateTimeFormat, elProp); + + CsvColumn column = new CsvColumn(elProp, parser, false); + columnList.add(column); + } + + private String getDefaultDateTimeFormat(int jdbcType) { + switch (jdbcType) { + case Types.TIME: + return defaultTimeFormat; + case Types.DATE: + return defaultDateFormat; + case Types.TIMESTAMP: + return defaultTimestampFormat; + + default: + throw new RuntimeException("Expected java.sql.Types TIME,DATE or TIMESTAMP but got [" + jdbcType + "]"); + } + } + + public void addProperty(String propertyName, StringParser parser, boolean reference) { + + ElPropertyValue elProp = descriptor.getElGetValue(propertyName); + if (parser == null) { + parser = elProp.getStringParser(); + } + CsvColumn column = new CsvColumn(elProp, parser, reference); + columnList.add(column); + } + + public void process(Reader reader) throws Exception { + DefaultCsvCallback callback = new DefaultCsvCallback(persistBatchSize, logInfoFrequency); + process(reader, callback); + } + + public void process(Reader reader, CsvCallback callback) throws Exception { + + if (reader == null) { + throw new NullPointerException("reader is null?"); + } + if (callback == null) { + throw new NullPointerException("callback is null?"); + } + + CsvUtilReader utilReader = new CsvUtilReader(reader); + + callback.begin(server); + + int row = 0; + + if (hasHeader) { + String[] line = utilReader.readNext(); + if (addPropertiesFromHeader) { + addPropertiesFromHeader(line); + } + callback.readHeader(line); + } + + try { + do { + ++row; + String[] line = utilReader.readNext(); + if (line == null) { + --row; + break; + } + + if (callback.processLine(row, line)) { + // the line content is expected to be ok for processing + if (line.length != columnList.size()) { + // we have not got the expected number of columns + String msg = "Error at line " + row + ". Expected [" + columnList.size() + "] columns " + + "but instead we have [" + line.length + "]. Line[" + Arrays.toString(line) + "]"; + throw new TextException(msg); + } + + T bean = buildBeanFromLineContent(row, line); + + callback.processBean(row, line, bean); + + } + } while (true); + + callback.end(row); + + } catch (Exception e) { + // notify that an error occured so that any + // transaction can be rolled back if required + callback.endWithError(row, e); + throw e; + } + } + + private void addPropertiesFromHeader(String[] line) { + for (int i = 0; i < line.length; i++) { + ElPropertyValue elProp = descriptor.getElGetValue(line[i]); + if (elProp == null) { + throw new TextException("Property [" + line[i] + "] not found"); + } + + if (Types.TIME == elProp.getJdbcType()) { + addProperty(line[i], TIME_PARSER); + + } else if (isDateTimeType(elProp.getJdbcType())) { + addDateTime(line[i], null, null); + + } else if (elProp.isAssocProperty()) { + BeanPropertyAssocOne assocOne = (BeanPropertyAssocOne) elProp.getBeanProperty(); + String idProp = assocOne.getBeanDescriptor().getIdBinder().getIdProperty(); + addReference(line[i] + "." + idProp); + } else { + addProperty(line[i]); + } + } + } + + private boolean isDateTimeType(int t) { + if (t == Types.TIMESTAMP || t == Types.DATE || t == Types.TIME) { + return true; + } + return false; + } + + @SuppressWarnings("unchecked") + protected T buildBeanFromLineContent(int row, String[] line) { + + try { + EntityBean entityBean = descriptor.createEntityBean(); + T bean = (T) entityBean; + + int columnPos = 0; + for (; columnPos < line.length; columnPos++) { + convertAndSetColumn(columnPos, line[columnPos], entityBean); + } + + return bean; + + } catch (RuntimeException e) { + String msg = "Error at line: " + row + " line[" + Arrays.toString(line) + "]"; + throw new RuntimeException(msg, e); + } + } + + protected void convertAndSetColumn(int columnPos, String strValue, Object bean) { + + strValue = strValue.trim(); + + if (strValue.length() == 0 && treatEmptyStringAsNull) { + return; + } + + CsvColumn c = columnList.get(columnPos); + c.convertAndSet(strValue, bean); + } + + /** + * Processes a column in the csv content. + */ + public static class CsvColumn { + + private final ElPropertyValue elProp; + private final StringParser parser; + private final boolean ignore; + private final boolean reference; + + /** + * Constructor for the IGNORE column. + */ + private CsvColumn() { + this.elProp = null; + this.parser = null; + this.reference = false; + this.ignore = true; + } + + /** + * Construct with a property and parser. + */ + public CsvColumn(ElPropertyValue elProp, StringParser parser, boolean reference) { + this.elProp = elProp; + this.parser = parser; + this.reference = reference; + this.ignore = false; + } + + /** + * Convert the string to the appropriate value and set it to the bean. + */ + public void convertAndSet(String strValue, Object bean) { + + if (!ignore) { + Object value = parser.parse(strValue); + elProp.elSetValue(bean, value, true, reference); + } + } + } + + /** + * A StringParser for converting custom date/time/datetime strings into + * appropriate java types (Date, Calendar, SQL Date, Time, Timestamp, JODA + * etc). + */ + private static class DateTimeParser implements StringParser { + + private final DateFormat dateFormat; + private final ElPropertyValue elProp; + private final String format; + + DateTimeParser(DateFormat dateFormat, String format, ElPropertyValue elProp) { + this.dateFormat = dateFormat; + this.elProp = elProp; + this.format = format; + } + + public Object parse(String value) { + try { + Date dt = dateFormat.parse(value); + return elProp.parseDateTime(dt.getTime()); + + } catch (ParseException e) { + throw new TextException("Error parsing [" + value + "] using format[" + format + "]", e); + } + } + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/csv/package-info.java b/src/main/java/com/avaje/ebeaninternal/server/text/csv/package-info.java index fee5b34ab..f55270085 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/csv/package-info.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/csv/package-info.java @@ -1,4 +1 @@ -/** - * CSV implementation. - */ package com.avaje.ebeaninternal.server.text.csv; \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java index 7b961a0b4..e6442e3b5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/DJsonContext.java @@ -1,326 +1,307 @@ -/** - * 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.text.json; - -import java.io.Reader; -import java.io.Writer; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import com.avaje.ebean.text.TextException; -import com.avaje.ebean.text.json.JsonContext; -import com.avaje.ebean.text.json.JsonElement; -import com.avaje.ebean.text.json.JsonReadOptions; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebean.text.json.JsonWriteOptions; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.type.EscapeJson; -import com.avaje.ebeaninternal.util.ParamTypeHelper; -import com.avaje.ebeaninternal.util.ParamTypeHelper.ManyType; -import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo; - -/** - * Default implementation of JsonContext. - * - * @author rbygrave - */ -public class DJsonContext implements JsonContext { - - private final SpiEbeanServer server; - - private final JsonValueAdapter dfltValueAdapter; - - private final boolean dfltPretty; - - public DJsonContext(SpiEbeanServer server, JsonValueAdapter dfltValueAdapter, boolean dfltPretty){ - this.server = server; - this.dfltValueAdapter = dfltValueAdapter; - this.dfltPretty = dfltPretty; - } - - public boolean isSupportedType(Type genericType) { - return server.isSupportedType(genericType); - } - - private ReadJsonSource createReader(Reader jsonReader) { - return new ReadJsonSourceReader(jsonReader, 256, 512); - } - - public T toBean(Class cls, String json){ - return toBean(cls, new ReadJsonSourceString(json), null); - } - - public T toBean(Class cls, Reader jsonReader) { - return toBean(cls, createReader(jsonReader), null); - } - - public T toBean(Class cls, String json, JsonReadOptions options){ - return toBean(cls, new ReadJsonSourceString(json), options); - } - - public T toBean(Class cls, Reader jsonReader, JsonReadOptions options) { - return toBean(cls, createReader(jsonReader), options); - } - - private T toBean(Class cls, ReadJsonSource src, JsonReadOptions options){ - - BeanDescriptor d = getDecriptor(cls); - ReadJsonContext ctx = new ReadJsonContext(src, dfltValueAdapter, options); - return d.jsonReadBean(ctx, null); - } - - public List toList(Class cls, String json){ - return toList(cls, new ReadJsonSourceString(json), null); - } - - public List toList(Class cls, String json, JsonReadOptions options){ - return toList(cls, new ReadJsonSourceString(json), options); - } - - public List toList(Class cls, Reader jsonReader){ - return toList(cls, createReader(jsonReader), null); - } - - public List toList(Class cls, Reader jsonReader, JsonReadOptions options){ - return toList(cls, createReader(jsonReader), options); - } - - private List toList(Class cls, ReadJsonSource src, JsonReadOptions options){ - - try { - BeanDescriptor d = getDecriptor(cls); - - List list = new ArrayList(); - - ReadJsonContext ctx = new ReadJsonContext(src, dfltValueAdapter, options); - ctx.readArrayBegin(); - do { - T bean = d.jsonReadBean(ctx, null); - if (bean != null){ - list.add(bean); - } - if (!ctx.readArrayNext()){ - break; - } - } while(true); - - return list; - } catch (RuntimeException e){ - throw new TextException("Error parsing "+src, e); - } - } - - - public Object toObject(Type genericType, String json, JsonReadOptions options) { - - TypeInfo info = ParamTypeHelper.getTypeInfo(genericType); - Class beanType = info.getBeanType(); - if (JsonElement.class.isAssignableFrom(beanType)){ - return InternalJsonParser.parse(json); - } - - ManyType manyType = info.getManyType(); - switch (manyType) { - case NONE: - return toBean(info.getBeanType(), json, options); - - case LIST: - return toList(info.getBeanType(), json, options); - - default: - String msg = "ManyType "+manyType+" not supported yet"; - throw new TextException(msg); - } - } - - public Object toObject(Type genericType, Reader json, JsonReadOptions options) { - - TypeInfo info = ParamTypeHelper.getTypeInfo(genericType); - Class beanType = info.getBeanType(); - if (JsonElement.class.isAssignableFrom(beanType)){ - return InternalJsonParser.parse(json); - } - - ManyType manyType = info.getManyType(); - switch (manyType) { - case NONE: - return toBean(info.getBeanType(), json, options); - - case LIST: - return toList(info.getBeanType(), json, options); - - default: - String msg = "ManyType "+manyType+" not supported yet"; - throw new TextException(msg); - } - } - - - public void toJsonWriter(Object o, Writer writer) { - toJsonWriter(o, writer, dfltPretty, null, null); - } - - public void toJsonWriter(Object o, Writer writer, boolean pretty) { - toJsonWriter(o, writer, pretty, null, null); - } - - public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options){ - toJsonWriter(o, writer, pretty, null, null); - } - - public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options, String callback) { - toJsonInternal(o, new WriteJsonBufferWriter(writer), pretty, options, callback); - } - - public String toJsonString(Object o){ - return toJsonString(o, dfltPretty, null); - } - - public String toJsonString(Object o, boolean pretty){ - return toJsonString(o, pretty, null); - } - - public String toJsonString(Object o, boolean pretty, JsonWriteOptions options){ - return toJsonString(o, pretty, options, null); - } - - public String toJsonString(Object o, boolean pretty, JsonWriteOptions options, String callback){ - WriteJsonBufferString b = new WriteJsonBufferString(); - toJsonInternal(o, b, pretty, options, callback); - return b.getBufferOutput(); - } - - @SuppressWarnings("unchecked") - private void toJsonInternal(Object o, WriteJsonBuffer buffer, boolean pretty, JsonWriteOptions options, String requestCallback){ - - if (o == null){ - buffer.append("null"); - } else if (o instanceof Number) { - buffer.append(o.toString()); - } else if (o instanceof Boolean) { - buffer.append(o.toString()); - } else if (o instanceof String) { - EscapeJson.escapeQuote(o.toString(), buffer); - } else if (o instanceof JsonElement) { - - } else if (o instanceof Map){ - toJsonFromMap((Map)o, buffer, pretty, options, requestCallback); - - } else if (o instanceof Collection){ - toJsonFromCollection((Collection)o, buffer, pretty, options, requestCallback); - - } else { - BeanDescriptor d = getDecriptor(o.getClass()); - WriteJsonContext ctx = new WriteJsonContext(buffer, pretty, dfltValueAdapter, options, requestCallback); - d.jsonWrite(ctx, o); - ctx.end(); - } - } - - - private void toJsonFromCollection(Collection c, WriteJsonBuffer buffer, boolean pretty, - JsonWriteOptions options, String requestCallback){ - - Iterator it = c.iterator(); - if (!it.hasNext()){ - buffer.append("[]"); - return; - } - - WriteJsonContext ctx = new WriteJsonContext(buffer, pretty, dfltValueAdapter, options, requestCallback); - - Object o = it.next(); - BeanDescriptor d = getDecriptor(o.getClass()); - - ctx.appendArrayBegin(); - d.jsonWrite(ctx, o); - while (it.hasNext()) { - ctx.appendComma(); - T t = it.next(); - d.jsonWrite(ctx, t); - } - ctx.appendArrayEnd(); - ctx.end(); - } - - private void toJsonFromMap(Map map, WriteJsonBuffer buffer, boolean pretty, - JsonWriteOptions options, String requestCallback){ - - if (map.isEmpty()){ - buffer.append("{}"); - return; - } - - WriteJsonContext ctx = new WriteJsonContext(buffer, pretty, dfltValueAdapter, options, requestCallback); - - Set> entrySet = map.entrySet(); - Iterator> it = entrySet.iterator(); - - Entry entry = it.next(); - - ctx.appendObjectBegin(); - toJsonMapKey(buffer, false, entry.getKey()); - toJsonMapValue(buffer, pretty, options, requestCallback, entry.getValue()); - - while (it.hasNext()) { - entry = it.next(); - ctx.appendComma(); - toJsonMapKey(buffer, pretty, entry.getKey()); - toJsonMapValue(buffer, pretty, options, requestCallback, entry.getValue()); - } - ctx.appendObjectEnd(); - ctx.end(); - } - - private void toJsonMapKey(WriteJsonBuffer buffer, boolean pretty, Object key) { - if (pretty){ - buffer.append("\n"); - } - buffer.append("\""); - buffer.append(key.toString()); - buffer.append("\":"); - } - - private void toJsonMapValue(WriteJsonBuffer buffer, boolean pretty, JsonWriteOptions options, String requestCallback, - Object value) { - - if (value == null){ - buffer.append("null"); - } else { - toJsonInternal(value, buffer, pretty, options, requestCallback); - } - } - - private BeanDescriptor getDecriptor(Class cls) { - BeanDescriptor d = server.getBeanDescriptor(cls); - if (d == null){ - String msg = "No BeanDescriptor found for "+cls; - throw new RuntimeException(msg); - } - return d; - } -} +package com.avaje.ebeaninternal.server.text.json; + +import java.io.Reader; +import java.io.Writer; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import com.avaje.ebean.text.TextException; +import com.avaje.ebean.text.json.JsonContext; +import com.avaje.ebean.text.json.JsonElement; +import com.avaje.ebean.text.json.JsonReadOptions; +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebean.text.json.JsonWriteOptions; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.type.EscapeJson; +import com.avaje.ebeaninternal.util.ParamTypeHelper; +import com.avaje.ebeaninternal.util.ParamTypeHelper.ManyType; +import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo; + +/** + * Default implementation of JsonContext. + * + * @author rbygrave + */ +public class DJsonContext implements JsonContext { + + private final SpiEbeanServer server; + + private final JsonValueAdapter dfltValueAdapter; + + private final boolean dfltPretty; + + public DJsonContext(SpiEbeanServer server, JsonValueAdapter dfltValueAdapter, boolean dfltPretty){ + this.server = server; + this.dfltValueAdapter = dfltValueAdapter; + this.dfltPretty = dfltPretty; + } + + public boolean isSupportedType(Type genericType) { + return server.isSupportedType(genericType); + } + + private ReadJsonSource createReader(Reader jsonReader) { + return new ReadJsonSourceReader(jsonReader, 256, 512); + } + + public T toBean(Class cls, String json){ + return toBean(cls, new ReadJsonSourceString(json), null); + } + + public T toBean(Class cls, Reader jsonReader) { + return toBean(cls, createReader(jsonReader), null); + } + + public T toBean(Class cls, String json, JsonReadOptions options){ + return toBean(cls, new ReadJsonSourceString(json), options); + } + + public T toBean(Class cls, Reader jsonReader, JsonReadOptions options) { + return toBean(cls, createReader(jsonReader), options); + } + + private T toBean(Class cls, ReadJsonSource src, JsonReadOptions options){ + + BeanDescriptor d = getDecriptor(cls); + ReadJsonContext ctx = new ReadJsonContext(src, dfltValueAdapter, options); + return d.jsonReadBean(ctx, null); + } + + public List toList(Class cls, String json){ + return toList(cls, new ReadJsonSourceString(json), null); + } + + public List toList(Class cls, String json, JsonReadOptions options){ + return toList(cls, new ReadJsonSourceString(json), options); + } + + public List toList(Class cls, Reader jsonReader){ + return toList(cls, createReader(jsonReader), null); + } + + public List toList(Class cls, Reader jsonReader, JsonReadOptions options){ + return toList(cls, createReader(jsonReader), options); + } + + private List toList(Class cls, ReadJsonSource src, JsonReadOptions options){ + + try { + BeanDescriptor d = getDecriptor(cls); + + List list = new ArrayList(); + + ReadJsonContext ctx = new ReadJsonContext(src, dfltValueAdapter, options); + ctx.readArrayBegin(); + do { + T bean = d.jsonReadBean(ctx, null); + if (bean != null){ + list.add(bean); + } + if (!ctx.readArrayNext()){ + break; + } + } while(true); + + return list; + } catch (RuntimeException e){ + throw new TextException("Error parsing "+src, e); + } + } + + + public Object toObject(Type genericType, String json, JsonReadOptions options) { + + TypeInfo info = ParamTypeHelper.getTypeInfo(genericType); + Class beanType = info.getBeanType(); + if (JsonElement.class.isAssignableFrom(beanType)){ + return InternalJsonParser.parse(json); + } + + ManyType manyType = info.getManyType(); + switch (manyType) { + case NONE: + return toBean(info.getBeanType(), json, options); + + case LIST: + return toList(info.getBeanType(), json, options); + + default: + String msg = "ManyType "+manyType+" not supported yet"; + throw new TextException(msg); + } + } + + public Object toObject(Type genericType, Reader json, JsonReadOptions options) { + + TypeInfo info = ParamTypeHelper.getTypeInfo(genericType); + Class beanType = info.getBeanType(); + if (JsonElement.class.isAssignableFrom(beanType)){ + return InternalJsonParser.parse(json); + } + + ManyType manyType = info.getManyType(); + switch (manyType) { + case NONE: + return toBean(info.getBeanType(), json, options); + + case LIST: + return toList(info.getBeanType(), json, options); + + default: + String msg = "ManyType "+manyType+" not supported yet"; + throw new TextException(msg); + } + } + + + public void toJsonWriter(Object o, Writer writer) { + toJsonWriter(o, writer, dfltPretty, null, null); + } + + public void toJsonWriter(Object o, Writer writer, boolean pretty) { + toJsonWriter(o, writer, pretty, null, null); + } + + public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options){ + toJsonWriter(o, writer, pretty, null, null); + } + + public void toJsonWriter(Object o, Writer writer, boolean pretty, JsonWriteOptions options, String callback) { + toJsonInternal(o, new WriteJsonBufferWriter(writer), pretty, options, callback); + } + + public String toJsonString(Object o){ + return toJsonString(o, dfltPretty, null); + } + + public String toJsonString(Object o, boolean pretty){ + return toJsonString(o, pretty, null); + } + + public String toJsonString(Object o, boolean pretty, JsonWriteOptions options){ + return toJsonString(o, pretty, options, null); + } + + public String toJsonString(Object o, boolean pretty, JsonWriteOptions options, String callback){ + WriteJsonBufferString b = new WriteJsonBufferString(); + toJsonInternal(o, b, pretty, options, callback); + return b.getBufferOutput(); + } + + @SuppressWarnings("unchecked") + private void toJsonInternal(Object o, WriteJsonBuffer buffer, boolean pretty, JsonWriteOptions options, String requestCallback){ + + if (o == null){ + buffer.append("null"); + } else if (o instanceof Number) { + buffer.append(o.toString()); + } else if (o instanceof Boolean) { + buffer.append(o.toString()); + } else if (o instanceof String) { + EscapeJson.escapeQuote(o.toString(), buffer); + } else if (o instanceof JsonElement) { + + } else if (o instanceof Map){ + toJsonFromMap((Map)o, buffer, pretty, options, requestCallback); + + } else if (o instanceof Collection){ + toJsonFromCollection((Collection)o, buffer, pretty, options, requestCallback); + + } else { + BeanDescriptor d = getDecriptor(o.getClass()); + WriteJsonContext ctx = new WriteJsonContext(buffer, pretty, dfltValueAdapter, options, requestCallback); + d.jsonWrite(ctx, o); + ctx.end(); + } + } + + + private void toJsonFromCollection(Collection c, WriteJsonBuffer buffer, boolean pretty, + JsonWriteOptions options, String requestCallback){ + + Iterator it = c.iterator(); + if (!it.hasNext()){ + buffer.append("[]"); + return; + } + + WriteJsonContext ctx = new WriteJsonContext(buffer, pretty, dfltValueAdapter, options, requestCallback); + + Object o = it.next(); + BeanDescriptor d = getDecriptor(o.getClass()); + + ctx.appendArrayBegin(); + d.jsonWrite(ctx, o); + while (it.hasNext()) { + ctx.appendComma(); + T t = it.next(); + d.jsonWrite(ctx, t); + } + ctx.appendArrayEnd(); + ctx.end(); + } + + private void toJsonFromMap(Map map, WriteJsonBuffer buffer, boolean pretty, + JsonWriteOptions options, String requestCallback){ + + if (map.isEmpty()){ + buffer.append("{}"); + return; + } + + WriteJsonContext ctx = new WriteJsonContext(buffer, pretty, dfltValueAdapter, options, requestCallback); + + Set> entrySet = map.entrySet(); + Iterator> it = entrySet.iterator(); + + Entry entry = it.next(); + + ctx.appendObjectBegin(); + toJsonMapKey(buffer, false, entry.getKey()); + toJsonMapValue(buffer, pretty, options, requestCallback, entry.getValue()); + + while (it.hasNext()) { + entry = it.next(); + ctx.appendComma(); + toJsonMapKey(buffer, pretty, entry.getKey()); + toJsonMapValue(buffer, pretty, options, requestCallback, entry.getValue()); + } + ctx.appendObjectEnd(); + ctx.end(); + } + + private void toJsonMapKey(WriteJsonBuffer buffer, boolean pretty, Object key) { + if (pretty){ + buffer.append("\n"); + } + buffer.append("\""); + buffer.append(key.toString()); + buffer.append("\":"); + } + + private void toJsonMapValue(WriteJsonBuffer buffer, boolean pretty, JsonWriteOptions options, String requestCallback, + Object value) { + + if (value == null){ + buffer.append("null"); + } else { + toJsonInternal(value, buffer, pretty, options, requestCallback); + } + } + + private BeanDescriptor getDecriptor(Class cls) { + BeanDescriptor d = server.getBeanDescriptor(cls); + if (d == null){ + String msg = "No BeanDescriptor found for "+cls; + throw new RuntimeException(msg); + } + return d; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java index 48c1f0b55..461f654f6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/DefaultJsonValueAdapter.java @@ -1,70 +1,51 @@ -/** - * 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.text.json; - -import java.sql.Date; -import java.sql.Timestamp; -import java.text.SimpleDateFormat; -import java.util.TimeZone; - -import com.avaje.ebean.text.json.JsonValueAdapter; - -public class DefaultJsonValueAdapter implements JsonValueAdapter { - - private final SimpleDateFormat dateTimeProto; - - public DefaultJsonValueAdapter(String dateTimeFormat){ - this.dateTimeProto = new SimpleDateFormat(dateTimeFormat); - this.dateTimeProto.setTimeZone(TimeZone.getTimeZone("UTC")); - } - - public DefaultJsonValueAdapter(){ - this("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - } - - private SimpleDateFormat dtFormat() { - return (SimpleDateFormat)dateTimeProto.clone(); - } - - public String jsonFromDate(Date date) { - return "\""+date.toString()+"\""; - } - - public String jsonFromTimestamp(Timestamp date) { - return "\""+dtFormat().format(date)+"\""; - } - - public Date jsonToDate(String jsonDate) { - return Date.valueOf(jsonDate); - } - - public Timestamp jsonToTimestamp(String jsonDateTime) { - try { - java.util.Date d = dtFormat().parse(jsonDateTime); - return new Timestamp(d.getTime()); - } catch (Exception e) { - String m = "Error parsing Datetime["+jsonDateTime+"]"; - throw new RuntimeException(m, e); - } - } - - - -} +package com.avaje.ebeaninternal.server.text.json; + +import java.sql.Date; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.TimeZone; + +import com.avaje.ebean.text.json.JsonValueAdapter; + +public class DefaultJsonValueAdapter implements JsonValueAdapter { + + private final SimpleDateFormat dateTimeProto; + + public DefaultJsonValueAdapter(String dateTimeFormat){ + this.dateTimeProto = new SimpleDateFormat(dateTimeFormat); + this.dateTimeProto.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + public DefaultJsonValueAdapter(){ + this("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + } + + private SimpleDateFormat dtFormat() { + return (SimpleDateFormat)dateTimeProto.clone(); + } + + public String jsonFromDate(Date date) { + return "\""+date.toString()+"\""; + } + + public String jsonFromTimestamp(Timestamp date) { + return "\""+dtFormat().format(date)+"\""; + } + + public Date jsonToDate(String jsonDate) { + return Date.valueOf(jsonDate); + } + + public Timestamp jsonToTimestamp(String jsonDateTime) { + try { + java.util.Date d = dtFormat().parse(jsonDateTime); + return new Timestamp(d.getTime()); + } catch (Exception e) { + String m = "Error parsing Datetime["+jsonDateTime+"]"; + throw new RuntimeException(m, e); + } + } + + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/PathStack.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/PathStack.java index 74743b9ec..42825abeb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/PathStack.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/PathStack.java @@ -1,45 +1,26 @@ -/** - * 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.text.json; - -import com.avaje.ebeaninternal.server.util.ArrayStack; - -public class PathStack extends ArrayStack { - - public String peekFullPath(String key){ - - String prefix = peekWithNull(); - if (prefix != null){ - return prefix+"."+key; - } else { - return key; - } - } - - public void pushPathKey(String key) { - - String prefix = peekWithNull(); - if (prefix != null){ - key = prefix+"."+key; - } - push(key); - } - -} +package com.avaje.ebeaninternal.server.text.json; + +import com.avaje.ebeaninternal.server.util.ArrayStack; + +public class PathStack extends ArrayStack { + + public String peekFullPath(String key){ + + String prefix = peekWithNull(); + if (prefix != null){ + return prefix+"."+key; + } else { + return key; + } + } + + public void pushPathKey(String key) { + + String prefix = peekWithNull(); + if (prefix != null){ + key = prefix+"."+key; + } + push(key); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadBasicJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadBasicJsonContext.java index 8177cd130..caefcb7ad 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadBasicJsonContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadBasicJsonContext.java @@ -1,269 +1,250 @@ -/** - * 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.text.json; - -import com.avaje.ebean.text.TextException; - -public class ReadBasicJsonContext implements ReadJsonInterface { - - private final ReadJsonSource src; - - private char tokenStart; - private String tokenKey; - - public ReadBasicJsonContext(ReadJsonSource src) { - this.src = src; - } - - public char getToken() { - return tokenStart; - } - - public String getTokenKey() { - return tokenKey; - } - - public boolean isTokenKey() { - return '\"' == tokenStart; - } - - public boolean isTokenObjectEnd() { - return '}' == tokenStart; - } - - public boolean readObjectBegin() { - readNextToken(); - if ('{' == tokenStart){ - return true; - } else if ('n' == tokenStart) { - return false; - } else if (']' == tokenStart) { - // an empty array - return false; - } - throw new RuntimeException("Expected object begin at "+src.getErrorHelp()); - } - - public boolean readKeyNext() { - readNextToken(); - if ('\"' == tokenStart){ - return true; - } else if ('}' == tokenStart) { - return false; - } - throw new RuntimeException("Expected '\"' or '}' at "+src.getErrorHelp()); - } - - public boolean readValueNext() { - readNextToken(); - if (',' == tokenStart){ - return true; - } else if ('}' == tokenStart) { - return false; - } - throw new RuntimeException("Expected ',' or '}' at "+src.getErrorHelp()+" but got "+tokenStart); - } - - public boolean readArrayBegin() { - readNextToken(); - if ('[' == tokenStart){ - return true; - } else if ('n' == tokenStart) { - return false; - } - throw new RuntimeException("Expected array begin at "+src.getErrorHelp()); - } - - public boolean readArrayNext() { - readNextToken(); - if (',' == tokenStart){ - return true; - } - if (']' == tokenStart){ - return false; - } - throw new RuntimeException("Expected ',' or ']' at "+src.getErrorHelp()); - } - - public void readNextToken() { - - ignoreWhiteSpace(); - - tokenStart = src.nextChar("EOF finding next token"); - switch (tokenStart) { - case '"': - internalReadKey(); - break; - case '{': break; - case '}': break; - case '[': break; // not expected - case ']': break; // not expected - case ',': break; // not expected - case ':': break; // not expected - case 'n': - internalReadNull(); - break; // not expected - - default: - throw new RuntimeException("Unexpected tokenStart["+tokenStart+"] "+src.getErrorHelp()); - } - - } - - public String readQuotedValue() { - - boolean escape = false; - StringBuilder sb = new StringBuilder(); - - do { - char ch = src.nextChar("EOF reading quoted value"); - if (escape) { - // in escape mode so just append the character - escape = false; - switch (ch) { - case 'n': - sb.append('\n'); - break; - case 'r': - sb.append('\r'); - break; - case 't': - sb.append('\t'); - break; - case 'f': - sb.append('\f'); - break; - case 'b': - sb.append('\b'); - break; - case '"': - sb.append('"'); - break; - - default: - sb.append('\\'); - sb.append(ch); - break; - } - - } else { - switch (ch) { - case '\\': - // put into 'escape' mode for next character - escape = true; - break; - case '"': - return sb.toString(); - - default: - sb.append(ch); - } - } - } while (true); - } - - public String readUnquotedValue(char c) { - String v = readUnquotedValueRaw(c); - if ("null".equals(v)){ - return null; - } else { - return v; - } - } - - private String readUnquotedValueRaw(char c) { - - StringBuilder sb = new StringBuilder(); - sb.append(c); - - do { - tokenStart = src.nextChar("EOF reading unquoted value"); - switch (tokenStart) { - case ',': - src.back(); - return sb.toString(); - - case '}': - src.back(); - return sb.toString(); - - case ' ': - return sb.toString(); - - case '\t': - return sb.toString(); - - case '\r': - return sb.toString(); - - case '\n': - return sb.toString(); - - default: - sb.append(tokenStart); - } - - } while (true); - - } - - private void internalReadNull() { - - StringBuilder sb = new StringBuilder(4); - sb.append(tokenStart); - for (int i = 0; i < 3; i++) { - char c = src.nextChar("EOF reading null "); - sb.append(c); - } - if (!"null".equals(sb.toString())){ - throw new TextException("Expected 'null' but got "+sb.toString()+" "+src.getErrorHelp()); - } - } - - private void internalReadKey() { - StringBuilder sb = new StringBuilder(); - do { - char c = src.nextChar("EOF reading key"); - if ('\"' == c){ - tokenKey = sb.toString(); - break; - } else { - sb.append(c); - } - } while (true); - - ignoreWhiteSpace(); - - char c = src.nextChar("EOF reading ':'"); - if (':' != c){ - throw new TextException("Expected to find colon after key at "+(src.pos()-1)+" but found ["+c+"]"+src.getErrorHelp()); - } - } - - public void ignoreWhiteSpace() { - src.ignoreWhiteSpace(); - } - - public char nextChar() { - tokenStart = src.nextChar("EOF getting nextChar for raw json"); - return tokenStart; - } - -} +package com.avaje.ebeaninternal.server.text.json; + +import com.avaje.ebean.text.TextException; + +public class ReadBasicJsonContext implements ReadJsonInterface { + + private final ReadJsonSource src; + + private char tokenStart; + private String tokenKey; + + public ReadBasicJsonContext(ReadJsonSource src) { + this.src = src; + } + + public char getToken() { + return tokenStart; + } + + public String getTokenKey() { + return tokenKey; + } + + public boolean isTokenKey() { + return '\"' == tokenStart; + } + + public boolean isTokenObjectEnd() { + return '}' == tokenStart; + } + + public boolean readObjectBegin() { + readNextToken(); + if ('{' == tokenStart){ + return true; + } else if ('n' == tokenStart) { + return false; + } else if (']' == tokenStart) { + // an empty array + return false; + } + throw new RuntimeException("Expected object begin at "+src.getErrorHelp()); + } + + public boolean readKeyNext() { + readNextToken(); + if ('\"' == tokenStart){ + return true; + } else if ('}' == tokenStart) { + return false; + } + throw new RuntimeException("Expected '\"' or '}' at "+src.getErrorHelp()); + } + + public boolean readValueNext() { + readNextToken(); + if (',' == tokenStart){ + return true; + } else if ('}' == tokenStart) { + return false; + } + throw new RuntimeException("Expected ',' or '}' at "+src.getErrorHelp()+" but got "+tokenStart); + } + + public boolean readArrayBegin() { + readNextToken(); + if ('[' == tokenStart){ + return true; + } else if ('n' == tokenStart) { + return false; + } + throw new RuntimeException("Expected array begin at "+src.getErrorHelp()); + } + + public boolean readArrayNext() { + readNextToken(); + if (',' == tokenStart){ + return true; + } + if (']' == tokenStart){ + return false; + } + throw new RuntimeException("Expected ',' or ']' at "+src.getErrorHelp()); + } + + public void readNextToken() { + + ignoreWhiteSpace(); + + tokenStart = src.nextChar("EOF finding next token"); + switch (tokenStart) { + case '"': + internalReadKey(); + break; + case '{': break; + case '}': break; + case '[': break; // not expected + case ']': break; // not expected + case ',': break; // not expected + case ':': break; // not expected + case 'n': + internalReadNull(); + break; // not expected + + default: + throw new RuntimeException("Unexpected tokenStart["+tokenStart+"] "+src.getErrorHelp()); + } + + } + + public String readQuotedValue() { + + boolean escape = false; + StringBuilder sb = new StringBuilder(); + + do { + char ch = src.nextChar("EOF reading quoted value"); + if (escape) { + // in escape mode so just append the character + escape = false; + switch (ch) { + case 'n': + sb.append('\n'); + break; + case 'r': + sb.append('\r'); + break; + case 't': + sb.append('\t'); + break; + case 'f': + sb.append('\f'); + break; + case 'b': + sb.append('\b'); + break; + case '"': + sb.append('"'); + break; + + default: + sb.append('\\'); + sb.append(ch); + break; + } + + } else { + switch (ch) { + case '\\': + // put into 'escape' mode for next character + escape = true; + break; + case '"': + return sb.toString(); + + default: + sb.append(ch); + } + } + } while (true); + } + + public String readUnquotedValue(char c) { + String v = readUnquotedValueRaw(c); + if ("null".equals(v)){ + return null; + } else { + return v; + } + } + + private String readUnquotedValueRaw(char c) { + + StringBuilder sb = new StringBuilder(); + sb.append(c); + + do { + tokenStart = src.nextChar("EOF reading unquoted value"); + switch (tokenStart) { + case ',': + src.back(); + return sb.toString(); + + case '}': + src.back(); + return sb.toString(); + + case ' ': + return sb.toString(); + + case '\t': + return sb.toString(); + + case '\r': + return sb.toString(); + + case '\n': + return sb.toString(); + + default: + sb.append(tokenStart); + } + + } while (true); + + } + + private void internalReadNull() { + + StringBuilder sb = new StringBuilder(4); + sb.append(tokenStart); + for (int i = 0; i < 3; i++) { + char c = src.nextChar("EOF reading null "); + sb.append(c); + } + if (!"null".equals(sb.toString())){ + throw new TextException("Expected 'null' but got "+sb.toString()+" "+src.getErrorHelp()); + } + } + + private void internalReadKey() { + StringBuilder sb = new StringBuilder(); + do { + char c = src.nextChar("EOF reading key"); + if ('\"' == c){ + tokenKey = sb.toString(); + break; + } else { + sb.append(c); + } + } while (true); + + ignoreWhiteSpace(); + + char c = src.nextChar("EOF reading ':'"); + if (':' != c){ + throw new TextException("Expected to find colon after key at "+(src.pos()-1)+" but found ["+c+"]"+src.getErrorHelp()); + } + } + + public void ignoreWhiteSpace() { + src.ignoreWhiteSpace(); + } + + public char nextChar() { + tokenStart = src.nextChar("EOF getting nextChar for raw json"); + return tokenStart; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonContext.java index 7b6f11099..bcca60b9b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonContext.java @@ -1,201 +1,182 @@ -/** - * 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.text.json; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.text.json.JsonElement; -import com.avaje.ebean.text.json.JsonReadBeanVisitor; -import com.avaje.ebean.text.json.JsonReadOptions; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.util.ArrayStack; - -public class ReadJsonContext extends ReadBasicJsonContext { - - private final Map> visitorMap; - - private final JsonValueAdapter valueAdapter; - - private final PathStack pathStack; - - private final ArrayStack beanState; - private ReadBeanState currentState; - - public ReadJsonContext(ReadJsonSource src, JsonValueAdapter dfltValueAdapter, JsonReadOptions options) { - super(src); - this.beanState = new ArrayStack(); - if (options == null){ - this.valueAdapter = dfltValueAdapter; - this.visitorMap = null; - this.pathStack = null; - } else { - this.valueAdapter = getValueAdapter(dfltValueAdapter, options.getValueAdapter()); - this.visitorMap = options.getVisitorMap(); - this.pathStack = (visitorMap == null || visitorMap.isEmpty()) ? null : new PathStack(); - } - } - - private JsonValueAdapter getValueAdapter(JsonValueAdapter dfltValueAdapter, JsonValueAdapter valueAdapter) { - return valueAdapter == null ? dfltValueAdapter : valueAdapter; - } - - public JsonValueAdapter getValueAdapter() { - return valueAdapter; - } - - public String readScalarValue() { - - ignoreWhiteSpace(); - - char prevChar = nextChar();//"EOF reading scalarValue?"); - if ('"' == prevChar){ - return readQuotedValue(); - } else { - return readUnquotedValue(prevChar); - } - } - - public void pushBean(Object bean, String path, BeanDescriptor beanDescriptor){ - currentState = new ReadBeanState(bean, beanDescriptor); - beanState.push(currentState); - if (pathStack != null){ - pathStack.pushPathKey(path); - } - } - - public ReadBeanState popBeanState() { - if (pathStack != null){ - String path = pathStack.peekWithNull(); - JsonReadBeanVisitor beanVisitor = visitorMap.get(path); - if (beanVisitor != null){ - currentState.visit(beanVisitor); - } - pathStack.pop(); - } - - // return the current ReadBeanState as we can't call setLoadedState() - // yet. We might bind master/detail beans together via mappedBy property - // so wait until after that before calling ReadBeanStatesetLoadedState(); - ReadBeanState s = currentState; - - beanState.pop(); - currentState = beanState.peekWithNull(); - return s; - } - - public void setProperty(String propertyName){ - currentState.setLoaded(propertyName); - } - - /** - * Got a key that doesn't map to a known property so read the json value - * which could be json primitive, object or array. - *

      - * Provide these values to a JsonReadBeanVisitor if registered. - *

      - */ - public JsonElement readUnmappedJson(String key) { - - JsonElement rawJsonValue = ReadJsonRawReader.readJsonElement(this); - if (visitorMap != null){ - currentState.addUnmappedJson(key, rawJsonValue); - } - return rawJsonValue; - } - - public static class ReadBeanState implements PropertyChangeListener { - - private final Object bean; - private final BeanDescriptor beanDescriptor; - private final EntityBeanIntercept ebi; - private final Set loadedProps; - private Map unmapped; - - private ReadBeanState(Object bean, BeanDescriptor beanDescriptor) { - this.bean = bean; - this.beanDescriptor = beanDescriptor; - if (bean instanceof EntityBean){ - this.ebi = ((EntityBean)bean)._ebean_getIntercept(); - this.loadedProps = new HashSet(); - } else { - this.ebi = null; - this.loadedProps = null; - } - } - public String toString(){ - return bean.getClass().getSimpleName()+" loaded:"+loadedProps; - } - - /** - * Add a loaded/set property to the set of loadedProps. - */ - public void setLoaded(String propertyName){ - if (ebi != null){ - loadedProps.add(propertyName); - } - } - - private void addUnmappedJson(String key, JsonElement value){ - if (unmapped == null){ - unmapped = new LinkedHashMap(); - } - unmapped.put(key, value); - } - - @SuppressWarnings("unchecked") - private void visit(JsonReadBeanVisitor beanVisitor) { - // listen for property change events so that - // we can update the loadedProps if necessary - if (ebi != null){ - ebi.addPropertyChangeListener(this); - } - beanVisitor.visit((T)bean, unmapped); - if (ebi != null){ - ebi.removePropertyChangeListener(this); - } - } - - public void setLoadedState(){ - if (ebi != null){ - // takes into account reference beans - beanDescriptor.setLoadedProps(ebi, loadedProps); - } - } - - public void propertyChange(PropertyChangeEvent evt) { - String propName = evt.getPropertyName(); - loadedProps.add(propName); - } - - public Object getBean() { - return bean; - } - - } - -} +package com.avaje.ebeaninternal.server.text.json; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.text.json.JsonElement; +import com.avaje.ebean.text.json.JsonReadBeanVisitor; +import com.avaje.ebean.text.json.JsonReadOptions; +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.util.ArrayStack; + +public class ReadJsonContext extends ReadBasicJsonContext { + + private final Map> visitorMap; + + private final JsonValueAdapter valueAdapter; + + private final PathStack pathStack; + + private final ArrayStack beanState; + private ReadBeanState currentState; + + public ReadJsonContext(ReadJsonSource src, JsonValueAdapter dfltValueAdapter, JsonReadOptions options) { + super(src); + this.beanState = new ArrayStack(); + if (options == null){ + this.valueAdapter = dfltValueAdapter; + this.visitorMap = null; + this.pathStack = null; + } else { + this.valueAdapter = getValueAdapter(dfltValueAdapter, options.getValueAdapter()); + this.visitorMap = options.getVisitorMap(); + this.pathStack = (visitorMap == null || visitorMap.isEmpty()) ? null : new PathStack(); + } + } + + private JsonValueAdapter getValueAdapter(JsonValueAdapter dfltValueAdapter, JsonValueAdapter valueAdapter) { + return valueAdapter == null ? dfltValueAdapter : valueAdapter; + } + + public JsonValueAdapter getValueAdapter() { + return valueAdapter; + } + + public String readScalarValue() { + + ignoreWhiteSpace(); + + char prevChar = nextChar();//"EOF reading scalarValue?"); + if ('"' == prevChar){ + return readQuotedValue(); + } else { + return readUnquotedValue(prevChar); + } + } + + public void pushBean(Object bean, String path, BeanDescriptor beanDescriptor){ + currentState = new ReadBeanState(bean, beanDescriptor); + beanState.push(currentState); + if (pathStack != null){ + pathStack.pushPathKey(path); + } + } + + public ReadBeanState popBeanState() { + if (pathStack != null){ + String path = pathStack.peekWithNull(); + JsonReadBeanVisitor beanVisitor = visitorMap.get(path); + if (beanVisitor != null){ + currentState.visit(beanVisitor); + } + pathStack.pop(); + } + + // return the current ReadBeanState as we can't call setLoadedState() + // yet. We might bind master/detail beans together via mappedBy property + // so wait until after that before calling ReadBeanStatesetLoadedState(); + ReadBeanState s = currentState; + + beanState.pop(); + currentState = beanState.peekWithNull(); + return s; + } + + public void setProperty(String propertyName){ + currentState.setLoaded(propertyName); + } + + /** + * Got a key that doesn't map to a known property so read the json value + * which could be json primitive, object or array. + *

      + * Provide these values to a JsonReadBeanVisitor if registered. + *

      + */ + public JsonElement readUnmappedJson(String key) { + + JsonElement rawJsonValue = ReadJsonRawReader.readJsonElement(this); + if (visitorMap != null){ + currentState.addUnmappedJson(key, rawJsonValue); + } + return rawJsonValue; + } + + public static class ReadBeanState implements PropertyChangeListener { + + private final Object bean; + private final BeanDescriptor beanDescriptor; + private final EntityBeanIntercept ebi; + private final Set loadedProps; + private Map unmapped; + + private ReadBeanState(Object bean, BeanDescriptor beanDescriptor) { + this.bean = bean; + this.beanDescriptor = beanDescriptor; + if (bean instanceof EntityBean){ + this.ebi = ((EntityBean)bean)._ebean_getIntercept(); + this.loadedProps = new HashSet(); + } else { + this.ebi = null; + this.loadedProps = null; + } + } + public String toString(){ + return bean.getClass().getSimpleName()+" loaded:"+loadedProps; + } + + /** + * Add a loaded/set property to the set of loadedProps. + */ + public void setLoaded(String propertyName){ + if (ebi != null){ + loadedProps.add(propertyName); + } + } + + private void addUnmappedJson(String key, JsonElement value){ + if (unmapped == null){ + unmapped = new LinkedHashMap(); + } + unmapped.put(key, value); + } + + @SuppressWarnings("unchecked") + private void visit(JsonReadBeanVisitor beanVisitor) { + // listen for property change events so that + // we can update the loadedProps if necessary + if (ebi != null){ + ebi.addPropertyChangeListener(this); + } + beanVisitor.visit((T)bean, unmapped); + if (ebi != null){ + ebi.removePropertyChangeListener(this); + } + } + + public void setLoadedState(){ + if (ebi != null){ + // takes into account reference beans + beanDescriptor.setLoadedProps(ebi, loadedProps); + } + } + + public void propertyChange(PropertyChangeEvent evt) { + String propName = evt.getPropertyName(); + loadedProps.add(propName); + } + + public Object getBean() { + return bean; + } + + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonRawReader.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonRawReader.java index 78448b24a..a8ef745ae 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonRawReader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonRawReader.java @@ -1,126 +1,107 @@ -/** - * 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.text.json; - -import com.avaje.ebean.text.json.JsonElementArray; -import com.avaje.ebean.text.json.JsonElementBoolean; -import com.avaje.ebean.text.json.JsonElementNull; -import com.avaje.ebean.text.json.JsonElementNumber; -import com.avaje.ebean.text.json.JsonElementObject; -import com.avaje.ebean.text.json.JsonElementString; -import com.avaje.ebean.text.json.JsonElement; - - - -public class ReadJsonRawReader { - - public static JsonElement readJsonElement(ReadJsonInterface ctx) { - return new ReadJsonRawReader(ctx).readJsonElement(); - } - - private final ReadJsonInterface ctx; - - private ReadJsonRawReader(ReadJsonInterface ctx){ - this.ctx = ctx; - } - - private JsonElement readJsonElement() { - return readValue(); - } - - private JsonElement readValue() { - - ctx.ignoreWhiteSpace(); - - char c = ctx.nextChar(); - - switch (c) { - case '{': - return readObject(); - - case '[': - return readArray(); - - case '"': - return readString(); - - default: - return readUnquoted(c); - } - } - - private JsonElement readArray() { - - JsonElementArray a = new JsonElementArray(); - - do { - JsonElement value = readValue(); - a.add(value); - if (!ctx.readArrayNext()){ - break; - } - } while(true); - - return a; - } - - private JsonElement readObject() { - - JsonElementObject o = new JsonElementObject(); - - do { - if (!ctx.readKeyNext()){ - break; - } else { - // we read a property key ... - String key = ctx.getTokenKey(); - JsonElement value = readValue(); - - o.put(key, value); - - if (!ctx.readValueNext()){ - break; - } - } - } while(true); - - return o; - } - - private JsonElement readString() { - String s = ctx.readQuotedValue(); - return new JsonElementString(s); - } - - private JsonElement readUnquoted(char c) { - String s = ctx.readUnquotedValue(c); - if ("null".equals(s)){ - return JsonElementNull.NULL; - - } else if ("true".equals(s)){ - return JsonElementBoolean.TRUE; - - } else if ("false".equals(s)) { - return JsonElementBoolean.FALSE; - - } - return new JsonElementNumber(s); - } -} +package com.avaje.ebeaninternal.server.text.json; + +import com.avaje.ebean.text.json.JsonElementArray; +import com.avaje.ebean.text.json.JsonElementBoolean; +import com.avaje.ebean.text.json.JsonElementNull; +import com.avaje.ebean.text.json.JsonElementNumber; +import com.avaje.ebean.text.json.JsonElementObject; +import com.avaje.ebean.text.json.JsonElementString; +import com.avaje.ebean.text.json.JsonElement; + + + +public class ReadJsonRawReader { + + public static JsonElement readJsonElement(ReadJsonInterface ctx) { + return new ReadJsonRawReader(ctx).readJsonElement(); + } + + private final ReadJsonInterface ctx; + + private ReadJsonRawReader(ReadJsonInterface ctx){ + this.ctx = ctx; + } + + private JsonElement readJsonElement() { + return readValue(); + } + + private JsonElement readValue() { + + ctx.ignoreWhiteSpace(); + + char c = ctx.nextChar(); + + switch (c) { + case '{': + return readObject(); + + case '[': + return readArray(); + + case '"': + return readString(); + + default: + return readUnquoted(c); + } + } + + private JsonElement readArray() { + + JsonElementArray a = new JsonElementArray(); + + do { + JsonElement value = readValue(); + a.add(value); + if (!ctx.readArrayNext()){ + break; + } + } while(true); + + return a; + } + + private JsonElement readObject() { + + JsonElementObject o = new JsonElementObject(); + + do { + if (!ctx.readKeyNext()){ + break; + } else { + // we read a property key ... + String key = ctx.getTokenKey(); + JsonElement value = readValue(); + + o.put(key, value); + + if (!ctx.readValueNext()){ + break; + } + } + } while(true); + + return o; + } + + private JsonElement readString() { + String s = ctx.readQuotedValue(); + return new JsonElementString(s); + } + + private JsonElement readUnquoted(char c) { + String s = ctx.readUnquotedValue(c); + if ("null".equals(s)){ + return JsonElementNull.NULL; + + } else if ("true".equals(s)){ + return JsonElementBoolean.TRUE; + + } else if ("false".equals(s)) { + return JsonElementBoolean.FALSE; + + } + return new JsonElementNumber(s); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSource.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSource.java index c1d71e65c..0d1e12486 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSource.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSource.java @@ -1,34 +1,15 @@ -/** - * 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.text.json; - -public interface ReadJsonSource { - - public char nextChar(String eofMsg); - - public void ignoreWhiteSpace(); - - public void back(); - - public int pos(); - - public String getErrorHelp(); - -} +package com.avaje.ebeaninternal.server.text.json; + +public interface ReadJsonSource { + + public char nextChar(String eofMsg); + + public void ignoreWhiteSpace(); + + public void back(); + + public int pos(); + + public String getErrorHelp(); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceReader.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceReader.java index 6e1236492..fe8b259a4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceReader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceReader.java @@ -1,102 +1,83 @@ -/** - * 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.text.json; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.Reader; - -import com.avaje.ebean.text.TextException; - -public class ReadJsonSourceReader implements ReadJsonSource { - - private final Reader reader; - - private char[] localBuffer; - - private int totalPos; - private int localPos; - private int localPosEnd; - - public ReadJsonSourceReader(Reader reader, int localBufferSize, int bufferSize) { - this.reader = new BufferedReader(reader,bufferSize); - this.localBuffer = new char[localBufferSize]; - } - - public String toString() { - return String.valueOf(localBuffer); - } - - - - public String getErrorHelp() { - int prev = localPos - 30; - if (prev < 0){ - prev = 0; - } - String c = new String(localBuffer, prev, (localPos-prev)); - return "pos:"+pos()+" preceding:"+c; - } - - public int pos() { - return totalPos+localPos; - } - - - public void ignoreWhiteSpace() { - do { - char c = nextChar("EOF ignoring whitespace"); - if (!Character.isWhitespace(c)){ - --localPos; - break; - } - } while(true); - } - - public void back() { - localPos--; - } - - public char nextChar(String eofMsg) { - if (localPos >= localPosEnd){ - if (!loadLocalBuffer()) { - throw new TextException(eofMsg+" at pos:"+(totalPos+localPos)); - } - } - return localBuffer[localPos++]; - } - - private boolean loadLocalBuffer() { - try { - localPosEnd = reader.read(localBuffer); - if (localPosEnd > 0){ - totalPos += localPos; - localPos = 0; - return true; - } else { - this.localBuffer = null; - return false; - } - - } catch (IOException e){ - throw new TextException(e); - } - } -} +package com.avaje.ebeaninternal.server.text.json; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; + +import com.avaje.ebean.text.TextException; + +public class ReadJsonSourceReader implements ReadJsonSource { + + private final Reader reader; + + private char[] localBuffer; + + private int totalPos; + private int localPos; + private int localPosEnd; + + public ReadJsonSourceReader(Reader reader, int localBufferSize, int bufferSize) { + this.reader = new BufferedReader(reader,bufferSize); + this.localBuffer = new char[localBufferSize]; + } + + public String toString() { + return String.valueOf(localBuffer); + } + + + + public String getErrorHelp() { + int prev = localPos - 30; + if (prev < 0){ + prev = 0; + } + String c = new String(localBuffer, prev, (localPos-prev)); + return "pos:"+pos()+" preceding:"+c; + } + + public int pos() { + return totalPos+localPos; + } + + + public void ignoreWhiteSpace() { + do { + char c = nextChar("EOF ignoring whitespace"); + if (!Character.isWhitespace(c)){ + --localPos; + break; + } + } while(true); + } + + public void back() { + localPos--; + } + + public char nextChar(String eofMsg) { + if (localPos >= localPosEnd){ + if (!loadLocalBuffer()) { + throw new TextException(eofMsg+" at pos:"+(totalPos+localPos)); + } + } + return localBuffer[localPos++]; + } + + private boolean loadLocalBuffer() { + try { + localPosEnd = reader.read(localBuffer); + if (localPosEnd > 0){ + totalPos += localPos; + localPos = 0; + return true; + } else { + this.localBuffer = null; + return false; + } + + } catch (IOException e){ + throw new TextException(e); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceString.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceString.java index f0bf55d4d..bcbda0961 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceString.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/ReadJsonSourceString.java @@ -1,73 +1,54 @@ -/** - * 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.text.json; - -import com.avaje.ebean.text.TextException; - -public class ReadJsonSourceString implements ReadJsonSource { - - private final String source; - private final int sourceLength; - private int pos; - - public ReadJsonSourceString(String source){ - this.source = source; - this.sourceLength = source.length(); - } - - public String getErrorHelp() { - int prev = pos - 50; - if (prev < 0){ - prev = 0; - } - String c = source.substring(prev, pos); - return "pos:"+pos+" precedingcontent:"+c; - } - - public String toString() { - return source; - } - - public int pos() { - return pos; - } - - public void back() { - pos--; - } - - public char nextChar(String eofMsg) { - if (pos >= sourceLength){ - throw new TextException(eofMsg+" at pos:"+pos); - } - return source.charAt(pos++); - } - - public void ignoreWhiteSpace() { - do { - char c = source.charAt(pos); - if (Character.isWhitespace(c)){ - ++pos; - } else { - break; - } - } while(true); - } -} +package com.avaje.ebeaninternal.server.text.json; + +import com.avaje.ebean.text.TextException; + +public class ReadJsonSourceString implements ReadJsonSource { + + private final String source; + private final int sourceLength; + private int pos; + + public ReadJsonSourceString(String source){ + this.source = source; + this.sourceLength = source.length(); + } + + public String getErrorHelp() { + int prev = pos - 50; + if (prev < 0){ + prev = 0; + } + String c = source.substring(prev, pos); + return "pos:"+pos+" precedingcontent:"+c; + } + + public String toString() { + return source; + } + + public int pos() { + return pos; + } + + public void back() { + pos--; + } + + public char nextChar(String eofMsg) { + if (pos >= sourceLength){ + throw new TextException(eofMsg+" at pos:"+pos); + } + return source.charAt(pos++); + } + + public void ignoreWhiteSpace() { + do { + char c = source.charAt(pos); + if (Character.isWhitespace(c)){ + ++pos; + } else { + break; + } + } while(true); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBuffer.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBuffer.java index 3635c4287..a16eb47f2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBuffer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBuffer.java @@ -1,27 +1,8 @@ -/** - * 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.text.json; - - -public interface WriteJsonBuffer extends Appendable { - - public WriteJsonBuffer append(String content); - +package com.avaje.ebeaninternal.server.text.json; + + +public interface WriteJsonBuffer extends Appendable { + + public WriteJsonBuffer append(String content); + } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferString.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferString.java index e6cba51b7..213f8d70f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferString.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferString.java @@ -1,59 +1,40 @@ -/** - * 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.text.json; - -import java.io.IOException; - -public class WriteJsonBufferString implements WriteJsonBuffer { - - private final StringBuilder buffer; - - public WriteJsonBufferString(){ - this.buffer = new StringBuilder(256); - } - - public WriteJsonBufferString append(CharSequence csq) throws IOException { - buffer.append(csq); - return this; - } - - public WriteJsonBufferString append(CharSequence csq, int start, int end) throws IOException { - buffer.append(csq, start, end); - return this; - } - - public WriteJsonBufferString append(char c) throws IOException { - buffer.append(c); - return this; - } - - public WriteJsonBufferString append(String content){ - buffer.append(content); - return this; - } - - public String getBufferOutput() { - return buffer.toString(); - } - - public String toString() { - return buffer.toString(); - } -} +package com.avaje.ebeaninternal.server.text.json; + +import java.io.IOException; + +public class WriteJsonBufferString implements WriteJsonBuffer { + + private final StringBuilder buffer; + + public WriteJsonBufferString(){ + this.buffer = new StringBuilder(256); + } + + public WriteJsonBufferString append(CharSequence csq) throws IOException { + buffer.append(csq); + return this; + } + + public WriteJsonBufferString append(CharSequence csq, int start, int end) throws IOException { + buffer.append(csq, start, end); + return this; + } + + public WriteJsonBufferString append(char c) throws IOException { + buffer.append(c); + return this; + } + + public WriteJsonBufferString append(String content){ + buffer.append(content); + return this; + } + + public String getBufferOutput() { + return buffer.toString(); + } + + public String toString() { + return buffer.toString(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferWriter.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferWriter.java index 467a3d5c9..422364529 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferWriter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonBufferWriter.java @@ -1,64 +1,45 @@ -/** - * 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.text.json; - -import java.io.IOException; -import java.io.Writer; - -import com.avaje.ebean.text.TextException; - -public class WriteJsonBufferWriter implements WriteJsonBuffer { - - private final Writer buffer; - - public WriteJsonBufferWriter(Writer buffer){ - this.buffer = buffer; - } - - public WriteJsonBufferWriter append(String content){ - try { - buffer.write(content); - return this; - } catch (IOException e) { - throw new TextException(e); - } - } - - public WriteJsonBufferWriter append(CharSequence csq) throws IOException { - return append(csq, 0, csq.length()); - } - - public WriteJsonBufferWriter append(CharSequence csq, int start, int end) throws IOException { - for (int i = start; i < end; i++) { - buffer.append(csq.charAt(i)); - } - return this; - } - - public WriteJsonBufferWriter append(char c) throws IOException { - try { - buffer.write(c); - return this; - } catch (IOException e) { - throw new TextException(e); - } - } - -} +package com.avaje.ebeaninternal.server.text.json; + +import java.io.IOException; +import java.io.Writer; + +import com.avaje.ebean.text.TextException; + +public class WriteJsonBufferWriter implements WriteJsonBuffer { + + private final Writer buffer; + + public WriteJsonBufferWriter(Writer buffer){ + this.buffer = buffer; + } + + public WriteJsonBufferWriter append(String content){ + try { + buffer.write(content); + return this; + } catch (IOException e) { + throw new TextException(e); + } + } + + public WriteJsonBufferWriter append(CharSequence csq) throws IOException { + return append(csq, 0, csq.length()); + } + + public WriteJsonBufferWriter append(CharSequence csq, int start, int end) throws IOException { + for (int i = start; i < end; i++) { + buffer.append(csq.charAt(i)); + } + return this; + } + + public WriteJsonBufferWriter append(char c) throws IOException { + try { + buffer.write(c); + return this; + } catch (IOException e) { + throw new TextException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonContext.java b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonContext.java index 4f73acca3..bbb33558c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/text/json/WriteJsonContext.java @@ -1,395 +1,376 @@ -/** - * 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.text.json; - -import java.util.Map; -import java.util.Set; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.text.PathProperties; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebean.text.json.JsonWriteBeanVisitor; -import com.avaje.ebean.text.json.JsonWriteOptions; -import com.avaje.ebean.text.json.JsonWriter; -import com.avaje.ebeaninternal.server.type.EscapeJson; -import com.avaje.ebeaninternal.server.type.ScalarType; -import com.avaje.ebeaninternal.server.util.ArrayStack; - - -public class WriteJsonContext implements JsonWriter { - - private final WriteJsonBuffer buffer; - - private final boolean pretty; - - private final JsonValueAdapter valueAdapter; - - private final ArrayStack parentBeans = new ArrayStack(); - - private final PathProperties pathProperties; - - private final Map> visitorMap; - - private final String callback; - - private final PathStack pathStack; - - private WriteBeanState beanState; - - private int depthOffset; - - boolean assocOne; - - public WriteJsonContext(WriteJsonBuffer buffer, boolean pretty, JsonValueAdapter dfltValueAdapter, - JsonWriteOptions options, String requestCallback){ - - this.buffer = buffer; - this.pretty = pretty; - this.pathStack = new PathStack(); - this.callback = getCallback(requestCallback, options); - if (options == null){ - this.valueAdapter = dfltValueAdapter; - this.visitorMap = null; - this.pathProperties = null; - - } else { - this.valueAdapter = getValueAdapter(dfltValueAdapter, options.getValueAdapter()); - this.visitorMap = emptyToNull(options.getVisitorMap()); - this.pathProperties = emptyToNull(options.getPathProperties()); - } - - if (callback != null){ - buffer.append(requestCallback).append("("); - } - } - - public void appendRawValue(String key, String rawJsonValue) { - appendKeyWithComma(key, true); - buffer.append(rawJsonValue); - } - - public void appendQuoteEscapeValue(String key, String valueToEscape) { - appendKeyWithComma(key, true); - EscapeJson.escapeQuote(valueToEscape, buffer); - } - - public void end() { - if (callback != null){ - buffer.append(")"); - } - } - - private Map emptyToNull(Map m){ - if ( m == null || m.isEmpty()) { - return null; - } else { - return m; - } - } - - private PathProperties emptyToNull(PathProperties m){ - if ( m == null || m.isEmpty()) { - return null; - } else { - return m; - } - } - - private String getCallback(String requestCallback, JsonWriteOptions options) { - if (requestCallback != null){ - return requestCallback; - } - if (options != null){ - return options.getCallback(); - } - return null; - } - - private JsonValueAdapter getValueAdapter(JsonValueAdapter dfltValueAdapter, JsonValueAdapter valueAdapter) { - return valueAdapter == null ? dfltValueAdapter : valueAdapter; - } - - /** - * Return the set of properties to write to JSON. If null is returned then - * the default will output the properties loaded for this bean. - */ - public Set getIncludeProperties() { - if (pathProperties != null){ - String path = pathStack.peekWithNull(); - return pathProperties.get(path); - } - return null; - } - - public JsonWriteBeanVisitor getBeanVisitor() { - if (visitorMap != null){ - String path = pathStack.peekWithNull(); - return visitorMap.get(path); - } - return null; - } - - public String getJson() { - return buffer.toString(); - } - - private void appendIndent(){ - - buffer.append("\n"); - int depth = depthOffset + parentBeans.size(); - for (int i = 0; i < depth; i++) { - buffer.append(" "); - } - } - - public void appendObjectBegin(){ - if (pretty && !assocOne){ - appendIndent(); - } - buffer.append("{"); - } - public void appendObjectEnd(){ - buffer.append("}"); - } - - public void appendArrayBegin(){ - if (pretty){ - appendIndent(); - } - buffer.append("["); - depthOffset++; - } - - public void appendArrayEnd(){ - depthOffset--; - if (pretty){ - appendIndent(); - } - buffer.append("]"); - } - - public void appendComma(){ - buffer.append(","); - } - - public void addDepthOffset(int offset){ - depthOffset += offset; - } - - public void beginAssocOneIsNull(String key) { - depthOffset++; - internalAppendKeyBegin(key); - appendNull(); - depthOffset--; - } - - public void beginAssocOne(String key) { - pathStack.pushPathKey(key); - - internalAppendKeyBegin(key); - assocOne = true; - } - - public void endAssocOne() { - - pathStack.pop(); - assocOne = false; - } - - public Boolean includeMany(String key) { - if (pathProperties != null){ - String fullPath = pathStack.peekFullPath(key); - return pathProperties.hasPath(fullPath); - } - return null; - } - - public void beginAssocMany(String key) { - - pathStack.pushPathKey(key); - - depthOffset--; - internalAppendKeyBegin(key); - depthOffset++; - buffer.append("["); - } - - public void endAssocMany(){ - - pathStack.pop(); - - if (pretty){ - depthOffset--; - appendIndent(); - depthOffset++; - } - buffer.append("]"); - } - - private void internalAppendKeyBegin(String key) { - if (!beanState.isFirstKey()){ - buffer.append(","); - } - if (pretty){ - appendIndent(); - } - appendKeyWithComma(key, false); - } - - public void appendNameValue(String key, ScalarType scalarType, T value) { - appendKeyWithComma(key, true); - scalarType.jsonWrite(buffer, value, getValueAdapter()); - } - - public void appendDiscriminator(String key, String discValue) { - appendKeyWithComma(key, true); - buffer.append("\""); - buffer.append(discValue); - buffer.append("\""); - } - - private void appendKeyWithComma(String key, boolean withComma) { - if (withComma){ - if (!beanState.isFirstKey()){ - buffer.append(","); - } - } - buffer.append("\""); - if(key == null) { - buffer.append("null"); - } else { - buffer.append(key); - } - buffer.append("\":"); - } - - public void appendNull(String key) { - appendKeyWithComma(key, true); - buffer.append("null"); - } - - public void appendNull() { - buffer.append("null"); - } - - public JsonValueAdapter getValueAdapter() { - return valueAdapter; - } - - public String toString() { - return buffer.toString(); - } - - public void popParentBean(){ - parentBeans.pop(); - } - - public void pushParentBean(Object parentBean){ - parentBeans.push(parentBean); - } - - public void popParentBeanMany(){ - parentBeans.pop(); - depthOffset--; - } - - public void pushParentBeanMany(Object parentBean){ - parentBeans.push(parentBean); - depthOffset++; - } - - public boolean isParentBean(Object bean){ - if (parentBeans.isEmpty()){ - return false; - } else { - return parentBeans.contains(bean); - } - } - - public WriteBeanState pushBeanState(Object bean) { - WriteBeanState newState = new WriteBeanState(bean); - WriteBeanState prevState = beanState; - beanState = newState; - return prevState; - } - - public void pushPreviousState(WriteBeanState previousState) { - this.beanState = previousState; - } - - public boolean isReferenceBean() { - return beanState.isReferenceBean(); - } - - public boolean includedProp(String name) { - return beanState.includedProp(name); - } - - public Set getLoadedProps() { - return beanState.getLoadedProps(); - } - - - public static class WriteBeanState { - - private final EntityBeanIntercept ebi; - private final Set loadedProps; - private final boolean referenceBean; - private boolean firstKeyOut; - - public WriteBeanState(Object bean) { - if (bean instanceof EntityBean){ - this.ebi = ((EntityBean)bean)._ebean_getIntercept(); - this.loadedProps = ebi.getLoadedProps(); - this.referenceBean = ebi.isReference(); - } else { - this.ebi = null; - this.loadedProps = null; - this.referenceBean = false; - } - } - - public Set getLoadedProps() { - return loadedProps; - } - - public boolean includedProp(String name) { - if (loadedProps == null || loadedProps.contains(name)){ - return true; - } else { - return false; - } - } - public boolean isReferenceBean() { - return referenceBean; - } - - public boolean isFirstKey() { - if (!firstKeyOut){ - firstKeyOut = true; - return true; - } else { - return false; - } - } - - } -} +package com.avaje.ebeaninternal.server.text.json; + +import java.util.Map; +import java.util.Set; + +import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.text.PathProperties; +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebean.text.json.JsonWriteBeanVisitor; +import com.avaje.ebean.text.json.JsonWriteOptions; +import com.avaje.ebean.text.json.JsonWriter; +import com.avaje.ebeaninternal.server.type.EscapeJson; +import com.avaje.ebeaninternal.server.type.ScalarType; +import com.avaje.ebeaninternal.server.util.ArrayStack; + + +public class WriteJsonContext implements JsonWriter { + + private final WriteJsonBuffer buffer; + + private final boolean pretty; + + private final JsonValueAdapter valueAdapter; + + private final ArrayStack parentBeans = new ArrayStack(); + + private final PathProperties pathProperties; + + private final Map> visitorMap; + + private final String callback; + + private final PathStack pathStack; + + private WriteBeanState beanState; + + private int depthOffset; + + boolean assocOne; + + public WriteJsonContext(WriteJsonBuffer buffer, boolean pretty, JsonValueAdapter dfltValueAdapter, + JsonWriteOptions options, String requestCallback){ + + this.buffer = buffer; + this.pretty = pretty; + this.pathStack = new PathStack(); + this.callback = getCallback(requestCallback, options); + if (options == null){ + this.valueAdapter = dfltValueAdapter; + this.visitorMap = null; + this.pathProperties = null; + + } else { + this.valueAdapter = getValueAdapter(dfltValueAdapter, options.getValueAdapter()); + this.visitorMap = emptyToNull(options.getVisitorMap()); + this.pathProperties = emptyToNull(options.getPathProperties()); + } + + if (callback != null){ + buffer.append(requestCallback).append("("); + } + } + + public void appendRawValue(String key, String rawJsonValue) { + appendKeyWithComma(key, true); + buffer.append(rawJsonValue); + } + + public void appendQuoteEscapeValue(String key, String valueToEscape) { + appendKeyWithComma(key, true); + EscapeJson.escapeQuote(valueToEscape, buffer); + } + + public void end() { + if (callback != null){ + buffer.append(")"); + } + } + + private Map emptyToNull(Map m){ + if ( m == null || m.isEmpty()) { + return null; + } else { + return m; + } + } + + private PathProperties emptyToNull(PathProperties m){ + if ( m == null || m.isEmpty()) { + return null; + } else { + return m; + } + } + + private String getCallback(String requestCallback, JsonWriteOptions options) { + if (requestCallback != null){ + return requestCallback; + } + if (options != null){ + return options.getCallback(); + } + return null; + } + + private JsonValueAdapter getValueAdapter(JsonValueAdapter dfltValueAdapter, JsonValueAdapter valueAdapter) { + return valueAdapter == null ? dfltValueAdapter : valueAdapter; + } + + /** + * Return the set of properties to write to JSON. If null is returned then + * the default will output the properties loaded for this bean. + */ + public Set getIncludeProperties() { + if (pathProperties != null){ + String path = pathStack.peekWithNull(); + return pathProperties.get(path); + } + return null; + } + + public JsonWriteBeanVisitor getBeanVisitor() { + if (visitorMap != null){ + String path = pathStack.peekWithNull(); + return visitorMap.get(path); + } + return null; + } + + public String getJson() { + return buffer.toString(); + } + + private void appendIndent(){ + + buffer.append("\n"); + int depth = depthOffset + parentBeans.size(); + for (int i = 0; i < depth; i++) { + buffer.append(" "); + } + } + + public void appendObjectBegin(){ + if (pretty && !assocOne){ + appendIndent(); + } + buffer.append("{"); + } + public void appendObjectEnd(){ + buffer.append("}"); + } + + public void appendArrayBegin(){ + if (pretty){ + appendIndent(); + } + buffer.append("["); + depthOffset++; + } + + public void appendArrayEnd(){ + depthOffset--; + if (pretty){ + appendIndent(); + } + buffer.append("]"); + } + + public void appendComma(){ + buffer.append(","); + } + + public void addDepthOffset(int offset){ + depthOffset += offset; + } + + public void beginAssocOneIsNull(String key) { + depthOffset++; + internalAppendKeyBegin(key); + appendNull(); + depthOffset--; + } + + public void beginAssocOne(String key) { + pathStack.pushPathKey(key); + + internalAppendKeyBegin(key); + assocOne = true; + } + + public void endAssocOne() { + + pathStack.pop(); + assocOne = false; + } + + public Boolean includeMany(String key) { + if (pathProperties != null){ + String fullPath = pathStack.peekFullPath(key); + return pathProperties.hasPath(fullPath); + } + return null; + } + + public void beginAssocMany(String key) { + + pathStack.pushPathKey(key); + + depthOffset--; + internalAppendKeyBegin(key); + depthOffset++; + buffer.append("["); + } + + public void endAssocMany(){ + + pathStack.pop(); + + if (pretty){ + depthOffset--; + appendIndent(); + depthOffset++; + } + buffer.append("]"); + } + + private void internalAppendKeyBegin(String key) { + if (!beanState.isFirstKey()){ + buffer.append(","); + } + if (pretty){ + appendIndent(); + } + appendKeyWithComma(key, false); + } + + public void appendNameValue(String key, ScalarType scalarType, T value) { + appendKeyWithComma(key, true); + scalarType.jsonWrite(buffer, value, getValueAdapter()); + } + + public void appendDiscriminator(String key, String discValue) { + appendKeyWithComma(key, true); + buffer.append("\""); + buffer.append(discValue); + buffer.append("\""); + } + + private void appendKeyWithComma(String key, boolean withComma) { + if (withComma){ + if (!beanState.isFirstKey()){ + buffer.append(","); + } + } + buffer.append("\""); + if(key == null) { + buffer.append("null"); + } else { + buffer.append(key); + } + buffer.append("\":"); + } + + public void appendNull(String key) { + appendKeyWithComma(key, true); + buffer.append("null"); + } + + public void appendNull() { + buffer.append("null"); + } + + public JsonValueAdapter getValueAdapter() { + return valueAdapter; + } + + public String toString() { + return buffer.toString(); + } + + public void popParentBean(){ + parentBeans.pop(); + } + + public void pushParentBean(Object parentBean){ + parentBeans.push(parentBean); + } + + public void popParentBeanMany(){ + parentBeans.pop(); + depthOffset--; + } + + public void pushParentBeanMany(Object parentBean){ + parentBeans.push(parentBean); + depthOffset++; + } + + public boolean isParentBean(Object bean){ + if (parentBeans.isEmpty()){ + return false; + } else { + return parentBeans.contains(bean); + } + } + + public WriteBeanState pushBeanState(Object bean) { + WriteBeanState newState = new WriteBeanState(bean); + WriteBeanState prevState = beanState; + beanState = newState; + return prevState; + } + + public void pushPreviousState(WriteBeanState previousState) { + this.beanState = previousState; + } + + public boolean isReferenceBean() { + return beanState.isReferenceBean(); + } + + public boolean includedProp(String name) { + return beanState.includedProp(name); + } + + public Set getLoadedProps() { + return beanState.getLoadedProps(); + } + + + public static class WriteBeanState { + + private final EntityBeanIntercept ebi; + private final Set loadedProps; + private final boolean referenceBean; + private boolean firstKeyOut; + + public WriteBeanState(Object bean) { + if (bean instanceof EntityBean){ + this.ebi = ((EntityBean)bean)._ebean_getIntercept(); + this.loadedProps = ebi.getLoadedProps(); + this.referenceBean = ebi.isReference(); + } else { + this.ebi = null; + this.loadedProps = null; + this.referenceBean = false; + } + } + + public Set getLoadedProps() { + return loadedProps; + } + + public boolean includedProp(String name) { + if (loadedProps == null || loadedProps.contains(name)){ + return true; + } else { + return false; + } + } + public boolean isReferenceBean() { + return referenceBean; + } + + public boolean isFirstKey() { + if (!firstKeyOut){ + firstKeyOut = true; + return true; + } else { + return false; + } + } + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDelta.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDelta.java index deb9e8dc2..497e29d45 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDelta.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDelta.java @@ -1,121 +1,102 @@ -/** - * 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.transaction; - -import java.io.DataInput; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.server.cluster.BinaryMessage; -import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -public class BeanDelta { - - private final List properties; - - private final BeanDescriptor beanDescriptor; - - private final Object id; - - public BeanDelta(BeanDescriptor beanDescriptor, Object id) { - this.beanDescriptor = beanDescriptor; - this.id = id; - this.properties = new ArrayList(); - } - - public BeanDescriptor getBeanDescriptor() { - return beanDescriptor; - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("BeanDelta["); - sb.append(beanDescriptor.getName()).append(":"); - sb.append(properties); - sb.append("]"); - return sb.toString(); - } - - public Object getId() { - return id; - } - - public void add(BeanProperty beanProperty, Object value) { - this.properties.add(new BeanDeltaProperty(beanProperty, value)); - } - - public void add(BeanDeltaProperty propertyDelta) { - this.properties.add(propertyDelta); - } - - public void apply(Object bean) { - - for (int i = 0; i < properties.size(); i++) { - properties.get(i).apply(bean); - } - } - - /** - * Read and return a BeanDelta from the binary input. - */ - public static BeanDelta readBinaryMessage(SpiEbeanServer server, DataInput dataInput) throws IOException { - - String descriptorId = dataInput.readUTF(); - BeanDescriptor desc = server.getBeanDescriptorById(descriptorId); - Object id = desc.getIdBinder().readData(dataInput); - BeanDelta bp = new BeanDelta(desc, id); - - int count = dataInput.readInt(); - for (int i = 0; i < count; i++) { - String propName = dataInput.readUTF(); - BeanProperty beanProperty = desc.getBeanProperty(propName); - Object value = beanProperty.getScalarType().readData(dataInput); - bp.add(beanProperty, value); - } - return bp; - } - - /** - * Write this bean delta in binary message format. - */ - public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { - - BinaryMessage m = new BinaryMessage(50); - - DataOutputStream os = m.getOs(); - os.writeInt(BinaryMessage.TYPE_BEANDELTA); - os.writeUTF(beanDescriptor.getDescriptorId()); - - beanDescriptor.getIdBinder().writeData(os, id); - os.writeInt(properties.size()); - - for (int i = 0; i < properties.size(); i++) { - properties.get(i).writeBinaryMessage(m); - } - - os.flush(); - msgList.add(m); - } -} +package com.avaje.ebeaninternal.server.transaction; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.cluster.BinaryMessage; +import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +public class BeanDelta { + + private final List properties; + + private final BeanDescriptor beanDescriptor; + + private final Object id; + + public BeanDelta(BeanDescriptor beanDescriptor, Object id) { + this.beanDescriptor = beanDescriptor; + this.id = id; + this.properties = new ArrayList(); + } + + public BeanDescriptor getBeanDescriptor() { + return beanDescriptor; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("BeanDelta["); + sb.append(beanDescriptor.getName()).append(":"); + sb.append(properties); + sb.append("]"); + return sb.toString(); + } + + public Object getId() { + return id; + } + + public void add(BeanProperty beanProperty, Object value) { + this.properties.add(new BeanDeltaProperty(beanProperty, value)); + } + + public void add(BeanDeltaProperty propertyDelta) { + this.properties.add(propertyDelta); + } + + public void apply(Object bean) { + + for (int i = 0; i < properties.size(); i++) { + properties.get(i).apply(bean); + } + } + + /** + * Read and return a BeanDelta from the binary input. + */ + public static BeanDelta readBinaryMessage(SpiEbeanServer server, DataInput dataInput) throws IOException { + + String descriptorId = dataInput.readUTF(); + BeanDescriptor desc = server.getBeanDescriptorById(descriptorId); + Object id = desc.getIdBinder().readData(dataInput); + BeanDelta bp = new BeanDelta(desc, id); + + int count = dataInput.readInt(); + for (int i = 0; i < count; i++) { + String propName = dataInput.readUTF(); + BeanProperty beanProperty = desc.getBeanProperty(propName); + Object value = beanProperty.getScalarType().readData(dataInput); + bp.add(beanProperty, value); + } + return bp; + } + + /** + * Write this bean delta in binary message format. + */ + public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { + + BinaryMessage m = new BinaryMessage(50); + + DataOutputStream os = m.getOs(); + os.writeInt(BinaryMessage.TYPE_BEANDELTA); + os.writeUTF(beanDescriptor.getDescriptorId()); + + beanDescriptor.getIdBinder().writeData(os, id); + os.writeInt(properties.size()); + + for (int i = 0; i < properties.size(); i++) { + properties.get(i).writeBinaryMessage(m); + } + + os.flush(); + msgList.add(m); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDeltaList.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDeltaList.java index d3f7293ce..1cdbb7f52 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDeltaList.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDeltaList.java @@ -1,61 +1,42 @@ -/** - * 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.transaction; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; - -public class BeanDeltaList { - - private final BeanDescriptor beanDescriptor; - - private final List deltaBeans = new ArrayList(); - - public BeanDeltaList(BeanDescriptor beanDescriptor) { - this.beanDescriptor = beanDescriptor; - } - - public String toString() { - return deltaBeans.toString(); - } - - public BeanDescriptor getBeanDescriptor() { - return beanDescriptor; - } - - public void add(BeanDelta b) { - deltaBeans.add(b); - } - - public List getDeltaBeans() { - return deltaBeans; - } - - public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { - for (int i = 0; i < deltaBeans.size(); i++) { - deltaBeans.get(i).writeBinaryMessage(msgList); - } - } - -} +package com.avaje.ebeaninternal.server.transaction; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +public class BeanDeltaList { + + private final BeanDescriptor beanDescriptor; + + private final List deltaBeans = new ArrayList(); + + public BeanDeltaList(BeanDescriptor beanDescriptor) { + this.beanDescriptor = beanDescriptor; + } + + public String toString() { + return deltaBeans.toString(); + } + + public BeanDescriptor getBeanDescriptor() { + return beanDescriptor; + } + + public void add(BeanDelta b) { + deltaBeans.add(b); + } + + public List getDeltaBeans() { + return deltaBeans; + } + + public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { + for (int i = 0; i < deltaBeans.size(); i++) { + deltaBeans.get(i).writeBinaryMessage(msgList); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDeltaMap.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDeltaMap.java index 774a4a76a..444d84dd1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDeltaMap.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDeltaMap.java @@ -1,67 +1,48 @@ -/** - * 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.transaction; - -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; - -public class BeanDeltaMap { - - private Map deltaMap = new HashMap(); - - public BeanDeltaMap() { - } - - public BeanDeltaMap(List deltaBeans) { - if (deltaBeans != null){ - for (int i = 0; i < deltaBeans.size(); i++) { - BeanDelta deltaBean = deltaBeans.get(i); - addBeanDelta(deltaBean); - } - } - } - - public String toString() { - return deltaMap.values().toString(); - } - - public void addBeanDelta(BeanDelta beanDelta){ - BeanDescriptor d = beanDelta.getBeanDescriptor(); - BeanDeltaList list = getDeltaBeanList(d); - list.add(beanDelta); - } - - public Collection deltaLists() { - return deltaMap.values(); - } - - private BeanDeltaList getDeltaBeanList(BeanDescriptor d) { - BeanDeltaList deltaList = deltaMap.get(d.getFullName()); - if (deltaList == null){ - deltaList = new BeanDeltaList(d); - deltaMap.put(d.getFullName(), deltaList); - } - return deltaList; - } -} +package com.avaje.ebeaninternal.server.transaction; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +public class BeanDeltaMap { + + private Map deltaMap = new HashMap(); + + public BeanDeltaMap() { + } + + public BeanDeltaMap(List deltaBeans) { + if (deltaBeans != null){ + for (int i = 0; i < deltaBeans.size(); i++) { + BeanDelta deltaBean = deltaBeans.get(i); + addBeanDelta(deltaBean); + } + } + } + + public String toString() { + return deltaMap.values().toString(); + } + + public void addBeanDelta(BeanDelta beanDelta){ + BeanDescriptor d = beanDelta.getBeanDescriptor(); + BeanDeltaList list = getDeltaBeanList(d); + list.add(beanDelta); + } + + public Collection deltaLists() { + return deltaMap.values(); + } + + private BeanDeltaList getDeltaBeanList(BeanDescriptor d) { + BeanDeltaList deltaList = deltaMap.get(d.getFullName()); + if (deltaList == null){ + deltaList = new BeanDeltaList(d); + deltaMap.put(d.getFullName(), deltaList); + } + return deltaList; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDeltaProperty.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDeltaProperty.java index 0ae45168f..510149684 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDeltaProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanDeltaProperty.java @@ -1,54 +1,35 @@ -/** - * 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.transaction; - -import java.io.DataOutputStream; -import java.io.IOException; - -import com.avaje.ebeaninternal.server.cluster.BinaryMessage; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; - -public class BeanDeltaProperty { - - private final BeanProperty beanProperty; - - private final Object value; - - public BeanDeltaProperty(BeanProperty beanProperty, Object value) { - this.beanProperty = beanProperty; - this.value = value; - } - - public String toString() { - return beanProperty.getName()+":"+value; - } - - public void apply(Object bean) { - beanProperty.setValue(bean, value); - } - - public void writeBinaryMessage(BinaryMessage m) throws IOException { - - DataOutputStream os = m.getOs(); - os.writeUTF(beanProperty.getName()); - beanProperty.getScalarType().writeData(os, value); - } - -} +package com.avaje.ebeaninternal.server.transaction; + +import java.io.DataOutputStream; +import java.io.IOException; + +import com.avaje.ebeaninternal.server.cluster.BinaryMessage; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; + +public class BeanDeltaProperty { + + private final BeanProperty beanProperty; + + private final Object value; + + public BeanDeltaProperty(BeanProperty beanProperty, Object value) { + this.beanProperty = beanProperty; + this.value = value; + } + + public String toString() { + return beanProperty.getName()+":"+value; + } + + public void apply(Object bean) { + beanProperty.setValue(bean, value); + } + + public void writeBinaryMessage(BinaryMessage m) throws IOException { + + DataOutputStream os = m.getOs(); + os.writeUTF(beanProperty.getName()); + beanProperty.getScalarType().writeData(os, value); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPathUpdate.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPathUpdate.java index b06c663c6..922e048b1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPathUpdate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPathUpdate.java @@ -1,43 +1,24 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.transaction; - -import java.io.Serializable; -import java.util.LinkedHashMap; -import java.util.Map; - -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; - -public class BeanPathUpdate { - - private final Map map = new LinkedHashMap(); - - public void add(BeanDescriptor desc, String path, Object id) { - - String key = desc.getFullName()+":"+path; - BeanPathUpdateIds pathIds = map.get(key); - if (pathIds == null){ - pathIds = new BeanPathUpdateIds(desc, path); - map.put(key, pathIds); - } - pathIds.addId((Serializable)id); - - } -} +package com.avaje.ebeaninternal.server.transaction; + +import java.io.Serializable; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +public class BeanPathUpdate { + + private final Map map = new LinkedHashMap(); + + public void add(BeanDescriptor desc, String path, Object id) { + + String key = desc.getFullName()+":"+path; + BeanPathUpdateIds pathIds = map.get(key); + if (pathIds == null){ + pathIds = new BeanPathUpdateIds(desc, path); + map.put(key, pathIds); + } + pathIds.addId((Serializable)id); + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPathUpdateIds.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPathUpdateIds.java index 2a0080874..59f2176e0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPathUpdateIds.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPathUpdateIds.java @@ -1,163 +1,144 @@ -/** - * 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.transaction; - -import java.io.DataInput; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.server.cluster.BinaryMessage; -import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.id.IdBinder; - -public class BeanPathUpdateIds { - - private transient BeanDescriptor beanDescriptor; - - private final String descriptorId; - - private String path; - - private ArrayList ids; - - /** - * Create the payload. - */ - public BeanPathUpdateIds(BeanDescriptor desc, String path) { - this.beanDescriptor = desc; - this.descriptorId = desc.getDescriptorId(); - this.path = path; - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - if (beanDescriptor != null) { - sb.append(beanDescriptor.getFullName()); - } else { - sb.append("descId:").append(descriptorId); - } - sb.append(" path:").append(path); - sb.append(" ids:").append(ids); - return sb.toString(); - } - - public static BeanPathUpdateIds readBinaryMessage(SpiEbeanServer server, DataInput dataInput) throws IOException { - - String descriptorId = dataInput.readUTF(); - String path = dataInput.readUTF(); - BeanDescriptor desc = server.getBeanDescriptorById(descriptorId); - BeanPathUpdateIds bp = new BeanPathUpdateIds(desc, path); - bp.read(dataInput); - return bp; - } - - private void read(DataInput dataInput) throws IOException { - - IdBinder idBinder = beanDescriptor.getIdBinder(); - ids = readIdList(dataInput, idBinder); - } - - - private ArrayList readIdList(DataInput dataInput, IdBinder idBinder) throws IOException { - - int count = dataInput.readInt(); - if (count < 1) { - return null; - } - ArrayList idList = new ArrayList(count); - for (int i = 0; i < count; i++) { - Object id = idBinder.readData(dataInput); - idList.add((Serializable) id); - } - return idList; - } - - /** - * Write the contents into a BinaryMessage form. - *

      - * For a RemoteBeanPersist with a large number of id's note that this is - * broken up into many BinaryMessages each with a maximum of 100 ids. This - * enables the contents of a large RemoteTransactionEvent to be split up - * across multiple Packets. - *

      - */ - public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { - - IdBinder idBinder = beanDescriptor.getIdBinder(); - - int count = ids == null ? 0 : ids.size(); - if (count > 0) { - int loop = 0; - int i = 0; - int eof = ids.size(); - do { - ++loop; - int endOfLoop = Math.min(eof, loop * 100); - - BinaryMessage m = new BinaryMessage(endOfLoop * 4 + 20); - - DataOutputStream os = m.getOs(); - os.writeInt(BinaryMessage.TYPE_BEANPATHUPDATE); - os.writeUTF(descriptorId); - os.writeUTF(path); - os.writeInt(count); - - for (; i < endOfLoop; i++) { - Serializable idValue = ids.get(i); - idBinder.writeData(os, idValue); - } - - os.flush(); - msgList.add(m); - - } while (i < eof); - } - } - - public void addId(Serializable id) { - ids.add(id); - } - - - public BeanDescriptor getBeanDescriptor() { - return beanDescriptor; - } - - /** - * Return the Descriptor Id. A more compact alternative to using the - * beanType. - */ - public String getDescriptorId() { - return descriptorId; - } - - public String getPath() { - return path; - } - - public List getIds() { - return ids; - } -} +package com.avaje.ebeaninternal.server.transaction; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.cluster.BinaryMessage; +import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.id.IdBinder; + +public class BeanPathUpdateIds { + + private transient BeanDescriptor beanDescriptor; + + private final String descriptorId; + + private String path; + + private ArrayList ids; + + /** + * Create the payload. + */ + public BeanPathUpdateIds(BeanDescriptor desc, String path) { + this.beanDescriptor = desc; + this.descriptorId = desc.getDescriptorId(); + this.path = path; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + if (beanDescriptor != null) { + sb.append(beanDescriptor.getFullName()); + } else { + sb.append("descId:").append(descriptorId); + } + sb.append(" path:").append(path); + sb.append(" ids:").append(ids); + return sb.toString(); + } + + public static BeanPathUpdateIds readBinaryMessage(SpiEbeanServer server, DataInput dataInput) throws IOException { + + String descriptorId = dataInput.readUTF(); + String path = dataInput.readUTF(); + BeanDescriptor desc = server.getBeanDescriptorById(descriptorId); + BeanPathUpdateIds bp = new BeanPathUpdateIds(desc, path); + bp.read(dataInput); + return bp; + } + + private void read(DataInput dataInput) throws IOException { + + IdBinder idBinder = beanDescriptor.getIdBinder(); + ids = readIdList(dataInput, idBinder); + } + + + private ArrayList readIdList(DataInput dataInput, IdBinder idBinder) throws IOException { + + int count = dataInput.readInt(); + if (count < 1) { + return null; + } + ArrayList idList = new ArrayList(count); + for (int i = 0; i < count; i++) { + Object id = idBinder.readData(dataInput); + idList.add((Serializable) id); + } + return idList; + } + + /** + * Write the contents into a BinaryMessage form. + *

      + * For a RemoteBeanPersist with a large number of id's note that this is + * broken up into many BinaryMessages each with a maximum of 100 ids. This + * enables the contents of a large RemoteTransactionEvent to be split up + * across multiple Packets. + *

      + */ + public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { + + IdBinder idBinder = beanDescriptor.getIdBinder(); + + int count = ids == null ? 0 : ids.size(); + if (count > 0) { + int loop = 0; + int i = 0; + int eof = ids.size(); + do { + ++loop; + int endOfLoop = Math.min(eof, loop * 100); + + BinaryMessage m = new BinaryMessage(endOfLoop * 4 + 20); + + DataOutputStream os = m.getOs(); + os.writeInt(BinaryMessage.TYPE_BEANPATHUPDATE); + os.writeUTF(descriptorId); + os.writeUTF(path); + os.writeInt(count); + + for (; i < endOfLoop; i++) { + Serializable idValue = ids.get(i); + idBinder.writeData(os, idValue); + } + + os.flush(); + msgList.add(m); + + } while (i < eof); + } + } + + public void addId(Serializable id) { + ids.add(id); + } + + + public BeanDescriptor getBeanDescriptor() { + return beanDescriptor; + } + + /** + * Return the Descriptor Id. A more compact alternative to using the + * beanType. + */ + public String getDescriptorId() { + return descriptorId; + } + + public String getPath() { + return path; + } + + public List getIds() { + return ids; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPersistIdMap.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPersistIdMap.java index 24f9e060c..0f295bdad 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPersistIdMap.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPersistIdMap.java @@ -1,69 +1,50 @@ -/** - * 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.transaction; - -import java.io.Serializable; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; - -import com.avaje.ebeaninternal.server.core.PersistRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; - -/** - * Organises the individual bean persist requests by type. - */ -public final class BeanPersistIdMap { - - private final Map beanMap = new LinkedHashMap(); - - public String toString() { - return beanMap.toString(); - } - - public boolean isEmpty() { - return beanMap.isEmpty(); - } - - public Collection values() { - return beanMap.values(); - } - - /** - * Add a Insert Update or Delete payload. - */ - public void add(BeanDescriptor desc, PersistRequest.Type type, Object id) { - - BeanPersistIds r = getPersistIds(desc); - r.addId(type, (Serializable)id); - } - - private BeanPersistIds getPersistIds(BeanDescriptor desc) { - String beanType = desc.getFullName(); - BeanPersistIds r = beanMap.get(beanType); - if (r == null){ - r = new BeanPersistIds(desc); - beanMap.put(beanType, r); - } - return r; - } - - -} +package com.avaje.ebeaninternal.server.transaction; + +import java.io.Serializable; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.avaje.ebeaninternal.server.core.PersistRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Organises the individual bean persist requests by type. + */ +public final class BeanPersistIdMap { + + private final Map beanMap = new LinkedHashMap(); + + public String toString() { + return beanMap.toString(); + } + + public boolean isEmpty() { + return beanMap.isEmpty(); + } + + public Collection values() { + return beanMap.values(); + } + + /** + * Add a Insert Update or Delete payload. + */ + public void add(BeanDescriptor desc, PersistRequest.Type type, Object id) { + + BeanPersistIds r = getPersistIds(desc); + r.addId(type, (Serializable)id); + } + + private BeanPersistIds getPersistIds(BeanDescriptor desc) { + String beanType = desc.getFullName(); + BeanPersistIds r = beanMap.get(beanType); + if (r == null){ + r = new BeanPersistIds(desc); + beanMap.put(beanType, r); + } + return r; + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPersistIds.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPersistIds.java index 3a826e0d5..eea4f3f5e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPersistIds.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/BeanPersistIds.java @@ -1,307 +1,288 @@ -/** - * 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.transaction; - -import java.io.DataInput; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -import com.avaje.ebean.event.BeanPersistListener; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.server.cluster.BinaryMessage; -import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; -import com.avaje.ebeaninternal.server.core.PersistRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.deploy.id.IdBinder; - -/** - * Wraps the information representing a Inserted Updated or Deleted Bean. - *

      - * This information is broadcast across the cluster so that remote BeanListeners - * are notified of the inserts updates and deletes that occured. - *

      - *

      - * You control it the data is broadcast and what data is broadcast by the - * BeanListener.getClusterData() method. It is guessed that often just the Id - * property or perhaps a few properties in a Map will be broadcast to reduce the - * size of data sent around the network. - *

      - */ -public class BeanPersistIds implements Serializable { - - private static final long serialVersionUID = 8389469180931531409L; - - private transient BeanDescriptor beanDescriptor; - - private final String descriptorId; - - private ArrayList insertIds; - private ArrayList updateIds; - private ArrayList deleteIds; - - /** - * Create the payload. - */ - public BeanPersistIds(BeanDescriptor desc) { - this.beanDescriptor = desc; - this.descriptorId = desc.getDescriptorId(); - } - - public static BeanPersistIds readBinaryMessage(SpiEbeanServer server, DataInput dataInput) throws IOException { - - String descriptorId = dataInput.readUTF(); - BeanDescriptor desc = server.getBeanDescriptorById(descriptorId); - BeanPersistIds bp = new BeanPersistIds(desc); - bp.read(dataInput); - return bp; - } - - private void read(DataInput dataInput) throws IOException { - - IdBinder idBinder = beanDescriptor.getIdBinder(); - - int iudType = dataInput.readInt(); - ArrayList idList = readIdList(dataInput, idBinder); - - switch (iudType) { - case 0: - insertIds = idList; - break; - case 1: - updateIds = idList; - break; - case 2: - deleteIds = idList; - break; - - default: - throw new RuntimeException("Invalid iudType "+iudType); - } - } - - /** - * Write the contents into a BinaryMessage form. - *

      - * For a RemoteBeanPersist with a large number of id's note that this is - * broken up into many BinaryMessages each with a maximum of 100 ids. This - * enables the contents of a large RemoteTransactionEvent to be split up - * across multiple Packets. - *

      - */ - public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { - - writeIdList(beanDescriptor, 0, insertIds, msgList); - writeIdList(beanDescriptor, 1, updateIds, msgList); - writeIdList(beanDescriptor, 2, deleteIds, msgList); - - } - - private ArrayList readIdList(DataInput dataInput, IdBinder idBinder) throws IOException { - - int count = dataInput.readInt(); - if (count < 1) { - return null; - } - ArrayList idList = new ArrayList(count); - for (int i = 0; i < count; i++) { - Object id = idBinder.readData(dataInput); - idList.add((Serializable) id); - } - return idList; - } - - /** - * Write a BinaryMessage containing the descriptorId, iudType and list of Id - * values. - *

      - * Note that a given BinaryMessage has a maximum of 100 Ids. This is due to - * the limit of UDP packet sizes. We break up the RemoteBeanPersist into - * potentially many smaller BinaryMessages which may be put into multiple - * Packets. - *

      - */ - private void writeIdList(BeanDescriptor desc, int iudType, ArrayList idList, - BinaryMessageList msgList) throws IOException { - - IdBinder idBinder = desc.getIdBinder(); - - int count = idList == null ? 0 : idList.size(); - if (count > 0) { - int loop = 0; - int i = 0; - int eof = idList.size(); - do { - ++loop; - int endOfLoop = Math.min(eof, loop * 100); - - BinaryMessage m = new BinaryMessage(endOfLoop * 4 + 20); - - DataOutputStream os = m.getOs(); - os.writeInt(BinaryMessage.TYPE_BEANIUD); - os.writeUTF(descriptorId); - os.writeInt(iudType); - os.writeInt(count); - - for (; i < endOfLoop; i++) { - Serializable idValue = idList.get(i); - idBinder.writeData(os, idValue); - } - - os.flush(); - msgList.add(m); - - } while (i < eof); - } - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - if (beanDescriptor != null) { - sb.append(beanDescriptor.getFullName()); - } else { - sb.append("descId:").append(descriptorId); - } - if (insertIds != null) { - sb.append(" insertIds:").append(insertIds); - } - if (updateIds != null) { - sb.append(" updateIds:").append(updateIds); - } - if (deleteIds != null) { - sb.append(" deleteIds:").append(deleteIds); - } - return sb.toString(); - } - - public void addId(PersistRequest.Type type, Serializable id) { - switch (type) { - case INSERT: - addInsertId(id); - break; - case UPDATE: - addUpdateId(id); - break; - case DELETE: - addDeleteId(id); - break; - - default: - break; - } - } - - private void addInsertId(Serializable id) { - if (insertIds == null) { - insertIds = new ArrayList(); - } - insertIds.add(id); - } - - private void addUpdateId(Serializable id) { - if (updateIds == null) { - updateIds = new ArrayList(); - } - updateIds.add(id); - } - - private void addDeleteId(Serializable id) { - if (deleteIds == null) { - deleteIds = new ArrayList(); - } - deleteIds.add(id); - } - - public BeanDescriptor getBeanDescriptor() { - return beanDescriptor; - } - - /** - * Return the Descriptor Id. A more compact alternative to using the - * beanType. - */ - public String getDescriptorId() { - return descriptorId; - } - - public List getInsertIds() { - return insertIds; - } - - public List getUpdateIds() { - return updateIds; - } - - public List getDeleteIds() { - return deleteIds; - } - - public void setBeanDescriptor(BeanDescriptor beanDescriptor) { - this.beanDescriptor = beanDescriptor; - } - - /** - * Notify the cache and local BeanPersistListener of this event that came - * from another server in the cluster. - */ - public void notifyCacheAndListener() { - - BeanPersistListener listener = beanDescriptor.getPersistListener(); - - // any change invalidates the query cache - beanDescriptor.queryCacheClear(); - - if (insertIds != null) { - if (listener != null) { - // notify listener - for (int i = 0; i < insertIds.size(); i++) { - listener.remoteInsert(insertIds.get(i)); - } - } - } - if (updateIds != null) { - for (int i = 0; i < updateIds.size(); i++) { - Serializable id = updateIds.get(i); - - // remove from cache - beanDescriptor.cacheRemove(id); - if (listener != null) { - // notify listener - listener.remoteInsert(id); - } - } - } - if (deleteIds != null) { - for (int i = 0; i < deleteIds.size(); i++) { - Serializable id = deleteIds.get(i); - - // remove from cache - beanDescriptor.cacheRemove(id); - if (listener != null) { - // notify listener - listener.remoteInsert(id); - } - } - } - - } -} +package com.avaje.ebeaninternal.server.transaction; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import com.avaje.ebean.event.BeanPersistListener; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.cluster.BinaryMessage; +import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; +import com.avaje.ebeaninternal.server.core.PersistRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.deploy.id.IdBinder; + +/** + * Wraps the information representing a Inserted Updated or Deleted Bean. + *

      + * This information is broadcast across the cluster so that remote BeanListeners + * are notified of the inserts updates and deletes that occured. + *

      + *

      + * You control it the data is broadcast and what data is broadcast by the + * BeanListener.getClusterData() method. It is guessed that often just the Id + * property or perhaps a few properties in a Map will be broadcast to reduce the + * size of data sent around the network. + *

      + */ +public class BeanPersistIds implements Serializable { + + private static final long serialVersionUID = 8389469180931531409L; + + private transient BeanDescriptor beanDescriptor; + + private final String descriptorId; + + private ArrayList insertIds; + private ArrayList updateIds; + private ArrayList deleteIds; + + /** + * Create the payload. + */ + public BeanPersistIds(BeanDescriptor desc) { + this.beanDescriptor = desc; + this.descriptorId = desc.getDescriptorId(); + } + + public static BeanPersistIds readBinaryMessage(SpiEbeanServer server, DataInput dataInput) throws IOException { + + String descriptorId = dataInput.readUTF(); + BeanDescriptor desc = server.getBeanDescriptorById(descriptorId); + BeanPersistIds bp = new BeanPersistIds(desc); + bp.read(dataInput); + return bp; + } + + private void read(DataInput dataInput) throws IOException { + + IdBinder idBinder = beanDescriptor.getIdBinder(); + + int iudType = dataInput.readInt(); + ArrayList idList = readIdList(dataInput, idBinder); + + switch (iudType) { + case 0: + insertIds = idList; + break; + case 1: + updateIds = idList; + break; + case 2: + deleteIds = idList; + break; + + default: + throw new RuntimeException("Invalid iudType "+iudType); + } + } + + /** + * Write the contents into a BinaryMessage form. + *

      + * For a RemoteBeanPersist with a large number of id's note that this is + * broken up into many BinaryMessages each with a maximum of 100 ids. This + * enables the contents of a large RemoteTransactionEvent to be split up + * across multiple Packets. + *

      + */ + public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { + + writeIdList(beanDescriptor, 0, insertIds, msgList); + writeIdList(beanDescriptor, 1, updateIds, msgList); + writeIdList(beanDescriptor, 2, deleteIds, msgList); + + } + + private ArrayList readIdList(DataInput dataInput, IdBinder idBinder) throws IOException { + + int count = dataInput.readInt(); + if (count < 1) { + return null; + } + ArrayList idList = new ArrayList(count); + for (int i = 0; i < count; i++) { + Object id = idBinder.readData(dataInput); + idList.add((Serializable) id); + } + return idList; + } + + /** + * Write a BinaryMessage containing the descriptorId, iudType and list of Id + * values. + *

      + * Note that a given BinaryMessage has a maximum of 100 Ids. This is due to + * the limit of UDP packet sizes. We break up the RemoteBeanPersist into + * potentially many smaller BinaryMessages which may be put into multiple + * Packets. + *

      + */ + private void writeIdList(BeanDescriptor desc, int iudType, ArrayList idList, + BinaryMessageList msgList) throws IOException { + + IdBinder idBinder = desc.getIdBinder(); + + int count = idList == null ? 0 : idList.size(); + if (count > 0) { + int loop = 0; + int i = 0; + int eof = idList.size(); + do { + ++loop; + int endOfLoop = Math.min(eof, loop * 100); + + BinaryMessage m = new BinaryMessage(endOfLoop * 4 + 20); + + DataOutputStream os = m.getOs(); + os.writeInt(BinaryMessage.TYPE_BEANIUD); + os.writeUTF(descriptorId); + os.writeInt(iudType); + os.writeInt(count); + + for (; i < endOfLoop; i++) { + Serializable idValue = idList.get(i); + idBinder.writeData(os, idValue); + } + + os.flush(); + msgList.add(m); + + } while (i < eof); + } + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + if (beanDescriptor != null) { + sb.append(beanDescriptor.getFullName()); + } else { + sb.append("descId:").append(descriptorId); + } + if (insertIds != null) { + sb.append(" insertIds:").append(insertIds); + } + if (updateIds != null) { + sb.append(" updateIds:").append(updateIds); + } + if (deleteIds != null) { + sb.append(" deleteIds:").append(deleteIds); + } + return sb.toString(); + } + + public void addId(PersistRequest.Type type, Serializable id) { + switch (type) { + case INSERT: + addInsertId(id); + break; + case UPDATE: + addUpdateId(id); + break; + case DELETE: + addDeleteId(id); + break; + + default: + break; + } + } + + private void addInsertId(Serializable id) { + if (insertIds == null) { + insertIds = new ArrayList(); + } + insertIds.add(id); + } + + private void addUpdateId(Serializable id) { + if (updateIds == null) { + updateIds = new ArrayList(); + } + updateIds.add(id); + } + + private void addDeleteId(Serializable id) { + if (deleteIds == null) { + deleteIds = new ArrayList(); + } + deleteIds.add(id); + } + + public BeanDescriptor getBeanDescriptor() { + return beanDescriptor; + } + + /** + * Return the Descriptor Id. A more compact alternative to using the + * beanType. + */ + public String getDescriptorId() { + return descriptorId; + } + + public List getInsertIds() { + return insertIds; + } + + public List getUpdateIds() { + return updateIds; + } + + public List getDeleteIds() { + return deleteIds; + } + + public void setBeanDescriptor(BeanDescriptor beanDescriptor) { + this.beanDescriptor = beanDescriptor; + } + + /** + * Notify the cache and local BeanPersistListener of this event that came + * from another server in the cluster. + */ + public void notifyCacheAndListener() { + + BeanPersistListener listener = beanDescriptor.getPersistListener(); + + // any change invalidates the query cache + beanDescriptor.queryCacheClear(); + + if (insertIds != null) { + if (listener != null) { + // notify listener + for (int i = 0; i < insertIds.size(); i++) { + listener.remoteInsert(insertIds.get(i)); + } + } + } + if (updateIds != null) { + for (int i = 0; i < updateIds.size(); i++) { + Serializable id = updateIds.get(i); + + // remove from cache + beanDescriptor.cacheRemove(id); + if (listener != null) { + // notify listener + listener.remoteInsert(id); + } + } + } + if (deleteIds != null) { + for (int i = 0; i < deleteIds.size(); i++) { + Serializable id = deleteIds.get(i); + + // remove from cache + beanDescriptor.cacheRemove(id); + if (listener != null) { + // notify listener + listener.remoteInsert(id); + } + } + } + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultPersistenceContext.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultPersistenceContext.java index 83a7c6dd2..a20db7c3d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultPersistenceContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultPersistenceContext.java @@ -1,183 +1,164 @@ -/** - * 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.transaction; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; - -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.api.Monitor; -import com.avaje.ebeaninternal.server.subclass.SubClassUtil; - -/** - * Default implementation of PersistenceContext. - *

      - * Ensures only one instance of a bean is used according to its type and unique - * id. - *

      - *

      - * PersistenceContext lives on a Transaction and as such is expected to only have - * a single thread accessing it at a time. This is not expected to be used concurrently. - *

      - *

      - * Duplicate beans are ones having the same type and unique id value. These are - * considered duplicates and replaced by the bean instance that was already - * loaded into the PersistanceContext. - *

      - */ -public final class DefaultPersistenceContext implements PersistenceContext { - - /** - * Map used hold caches. One cache per bean type. - */ - private final HashMap typeCache = new HashMap(); - - private final Monitor monitor = new Monitor(); - - /** - * Create a new PersistanceContext. - */ - public DefaultPersistenceContext() { - } - - /** - * Set an object into the PersistanceContext. - */ - public void put(Object id, Object bean) { - synchronized (monitor) { - getClassContext(bean.getClass()).put(id, bean); - } - } - - public Object putIfAbsent(Object id, Object bean){ - synchronized (monitor) { - return getClassContext(bean.getClass()).putIfAbsent(id, bean); - } - } - - - - /** - * Return an object given its type and unique id. - */ - public Object get(Class beanType, Object id) { - synchronized (monitor) { - return getClassContext(beanType).get(id); - } - } - - /** - * Return the number of beans of the given type in the persistence context. - */ - public int size(Class beanType) { - synchronized (monitor) { - ClassContext classMap = typeCache.get(beanType.getName()); - return classMap == null ? 0 : classMap.size(); - } - } - - /** - * Clear the PersistenceContext. - */ - public void clear() { - synchronized (monitor) { - typeCache.clear(); - } - } - - public void clear(Class beanType) { - synchronized (monitor) { - ClassContext classMap = typeCache.get(beanType.getName()); - if (classMap != null) { - classMap.clear(); - } - } - } - - public void clear(Class beanType, Object id) { - synchronized (monitor) { - ClassContext classMap = typeCache.get(beanType.getName()); - if (classMap != null && id != null) { - //id = getUid(beanType, id); - classMap.remove(id); - } - } - } - - public String toString() { - synchronized (monitor) { - StringBuilder sb = new StringBuilder(); - Iterator> it = typeCache.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry entry = it.next(); - if (entry.getValue().size() > 0){ - sb.append(entry.getKey()+":"+entry.getValue().size()+"; "); - } - } - return sb.toString(); - } - } - - private ClassContext getClassContext(Class beanType) { - - // strip off $$EntityBean.. suffix... - String clsName = SubClassUtil.getSuperClassName(beanType.getName()); - - ClassContext classMap = typeCache.get(clsName); - if (classMap == null) { - classMap = new ClassContext(); - typeCache.put(clsName, classMap); - } - return classMap; - } - - private static class ClassContext { - - private final WeakValueMap map = new WeakValueMap(); - - private Object get(Object id){ - return map.get(id); - } - - private Object putIfAbsent(Object id, Object bean){ - - return map.putIfAbsent(id, bean); - } - - private void put(Object id, Object b){ - map.put(id, b); - } - - private int size() { - return map.size(); - } - - private void clear(){ - map.clear(); - } - - private Object remove(Object id){ - return map.remove(id); - } - } - -} +package com.avaje.ebeaninternal.server.transaction; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebeaninternal.api.Monitor; +import com.avaje.ebeaninternal.server.subclass.SubClassUtil; + +/** + * Default implementation of PersistenceContext. + *

      + * Ensures only one instance of a bean is used according to its type and unique + * id. + *

      + *

      + * PersistenceContext lives on a Transaction and as such is expected to only have + * a single thread accessing it at a time. This is not expected to be used concurrently. + *

      + *

      + * Duplicate beans are ones having the same type and unique id value. These are + * considered duplicates and replaced by the bean instance that was already + * loaded into the PersistanceContext. + *

      + */ +public final class DefaultPersistenceContext implements PersistenceContext { + + /** + * Map used hold caches. One cache per bean type. + */ + private final HashMap typeCache = new HashMap(); + + private final Monitor monitor = new Monitor(); + + /** + * Create a new PersistanceContext. + */ + public DefaultPersistenceContext() { + } + + /** + * Set an object into the PersistanceContext. + */ + public void put(Object id, Object bean) { + synchronized (monitor) { + getClassContext(bean.getClass()).put(id, bean); + } + } + + public Object putIfAbsent(Object id, Object bean){ + synchronized (monitor) { + return getClassContext(bean.getClass()).putIfAbsent(id, bean); + } + } + + + + /** + * Return an object given its type and unique id. + */ + public Object get(Class beanType, Object id) { + synchronized (monitor) { + return getClassContext(beanType).get(id); + } + } + + /** + * Return the number of beans of the given type in the persistence context. + */ + public int size(Class beanType) { + synchronized (monitor) { + ClassContext classMap = typeCache.get(beanType.getName()); + return classMap == null ? 0 : classMap.size(); + } + } + + /** + * Clear the PersistenceContext. + */ + public void clear() { + synchronized (monitor) { + typeCache.clear(); + } + } + + public void clear(Class beanType) { + synchronized (monitor) { + ClassContext classMap = typeCache.get(beanType.getName()); + if (classMap != null) { + classMap.clear(); + } + } + } + + public void clear(Class beanType, Object id) { + synchronized (monitor) { + ClassContext classMap = typeCache.get(beanType.getName()); + if (classMap != null && id != null) { + //id = getUid(beanType, id); + classMap.remove(id); + } + } + } + + public String toString() { + synchronized (monitor) { + StringBuilder sb = new StringBuilder(); + Iterator> it = typeCache.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + if (entry.getValue().size() > 0){ + sb.append(entry.getKey()+":"+entry.getValue().size()+"; "); + } + } + return sb.toString(); + } + } + + private ClassContext getClassContext(Class beanType) { + + // strip off $$EntityBean.. suffix... + String clsName = SubClassUtil.getSuperClassName(beanType.getName()); + + ClassContext classMap = typeCache.get(clsName); + if (classMap == null) { + classMap = new ClassContext(); + typeCache.put(clsName, classMap); + } + return classMap; + } + + private static class ClassContext { + + private final WeakValueMap map = new WeakValueMap(); + + private Object get(Object id){ + return map.get(id); + } + + private Object putIfAbsent(Object id, Object bean){ + + return map.putIfAbsent(id, bean); + } + + private void put(Object id, Object b){ + map.put(id, b); + } + + private int size() { + return map.size(); + } + + private void clear(){ + map.clear(); + } + + private Object remove(Object id){ + return map.remove(id); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultTransactionThreadLocal.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultTransactionThreadLocal.java index a70f2410a..c95a12093 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultTransactionThreadLocal.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/DefaultTransactionThreadLocal.java @@ -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.transaction; - -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.transaction.TransactionMap.State; - -/** - * Used by EbeanMgr to store its Transactions in a ThreadLocal. This way the - * transaction objects don't have to passed around. - */ -public final class DefaultTransactionThreadLocal { - - private static ThreadLocal local = new ThreadLocal() { - protected synchronized TransactionMap initialValue() { - return new TransactionMap(); - } - }; - - /** - * Not allowed. - */ - private DefaultTransactionThreadLocal() { - } - - /** - * Return the current TransactionState for a given serverName. This is for the - * local thread of course. - */ - private static TransactionMap.State getState(String serverName) { - return local.get().getStateWithCreate(serverName); - } - - /** - * Set a new Transaction for this serverName and Thread. - */ - public static void set(String serverName, SpiTransaction trans) { - getState(serverName).set(trans); - } - - /** - * A mechanism to get the transaction out of the thread local by replacing it - * with a 'proxy'. - *

      - * Used for background fetching. Replaces the current transaction with a - * 'dummy' transaction. The current transaction is given to the background - * thread so it can continue the fetch. - *

      - */ - public static void replace(String serverName, SpiTransaction trans) { - getState(serverName).replace(trans); - } - - /** - * Return the current Transaction for this serverName and Thread. - */ - public static SpiTransaction get(String serverName) { - TransactionMap map = local.get(); - State state = map.getState(serverName); - SpiTransaction t = (state == null) ? null : state.transaction; - if (map.isEmpty()) { - local.remove(); - } - return t; - } - - /** - * Commit the current transaction. - */ - public static void commit(String serverName) { - TransactionMap map = local.get(); - State state = map.removeState(serverName); - if (state == null) { - throw new IllegalStateException("No current transaction for [" + serverName + "]"); - } - state.commit(); - if (map.isEmpty()) { - local.remove(); - } - } - - /** - * Rollback the current transaction. - */ - public static void rollback(String serverName) { - TransactionMap map = local.get(); - State state = map.removeState(serverName); - if (state == null) { - throw new IllegalStateException("No current transaction for [" + serverName + "]"); - } - state.rollback(); - if (map.isEmpty()) { - local.remove(); - } - } - - /** - * If the transaction has not been committed then roll it back. - *

      - * Designed to be put in a finally block instead of a rollback() in each catch - * block. - * - *

      -   * Ebean.beingTransaction();
      -   * try {
      -   *   // ... perform some actions in a single transaction
      -   * 
      -   *   Ebean.commitTransaction();
      -   * 
      -   * } finally {
      -   *   // ensure transaction ended. If some error occurred then rollback()
      -   *   Ebean.endTransaction();
      -   * }
      -   * 
      - * - *

      - */ - public static void end(String serverName) { - - TransactionMap map = local.get(); - State state = map.removeState(serverName); - if (state != null) { - state.end(); - } - if (map.isEmpty()) { - local.remove(); - } - } - -} +package com.avaje.ebeaninternal.server.transaction; + +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.server.transaction.TransactionMap.State; + +/** + * Used by EbeanMgr to store its Transactions in a ThreadLocal. This way the + * transaction objects don't have to passed around. + */ +public final class DefaultTransactionThreadLocal { + + private static ThreadLocal local = new ThreadLocal() { + protected synchronized TransactionMap initialValue() { + return new TransactionMap(); + } + }; + + /** + * Not allowed. + */ + private DefaultTransactionThreadLocal() { + } + + /** + * Return the current TransactionState for a given serverName. This is for the + * local thread of course. + */ + private static TransactionMap.State getState(String serverName) { + return local.get().getStateWithCreate(serverName); + } + + /** + * Set a new Transaction for this serverName and Thread. + */ + public static void set(String serverName, SpiTransaction trans) { + getState(serverName).set(trans); + } + + /** + * A mechanism to get the transaction out of the thread local by replacing it + * with a 'proxy'. + *

      + * Used for background fetching. Replaces the current transaction with a + * 'dummy' transaction. The current transaction is given to the background + * thread so it can continue the fetch. + *

      + */ + public static void replace(String serverName, SpiTransaction trans) { + getState(serverName).replace(trans); + } + + /** + * Return the current Transaction for this serverName and Thread. + */ + public static SpiTransaction get(String serverName) { + TransactionMap map = local.get(); + State state = map.getState(serverName); + SpiTransaction t = (state == null) ? null : state.transaction; + if (map.isEmpty()) { + local.remove(); + } + return t; + } + + /** + * Commit the current transaction. + */ + public static void commit(String serverName) { + TransactionMap map = local.get(); + State state = map.removeState(serverName); + if (state == null) { + throw new IllegalStateException("No current transaction for [" + serverName + "]"); + } + state.commit(); + if (map.isEmpty()) { + local.remove(); + } + } + + /** + * Rollback the current transaction. + */ + public static void rollback(String serverName) { + TransactionMap map = local.get(); + State state = map.removeState(serverName); + if (state == null) { + throw new IllegalStateException("No current transaction for [" + serverName + "]"); + } + state.rollback(); + if (map.isEmpty()) { + local.remove(); + } + } + + /** + * If the transaction has not been committed then roll it back. + *

      + * Designed to be put in a finally block instead of a rollback() in each catch + * block. + * + *

      +   * Ebean.beingTransaction();
      +   * try {
      +   *   // ... perform some actions in a single transaction
      +   * 
      +   *   Ebean.commitTransaction();
      +   * 
      +   * } finally {
      +   *   // ensure transaction ended. If some error occurred then rollback()
      +   *   Ebean.endTransaction();
      +   * }
      +   * 
      + * + *

      + */ + public static void end(String serverName) { + + TransactionMap map = local.get(); + State state = map.removeState(serverName); + if (state != null) { + state.end(); + } + if (map.isEmpty()) { + local.remove(); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/DeleteByIdMap.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/DeleteByIdMap.java index 03f3eb5a5..c6f036be3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/DeleteByIdMap.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/DeleteByIdMap.java @@ -1,95 +1,76 @@ -/** - * 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.transaction; - -import java.io.Serializable; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.avaje.ebeaninternal.server.core.PersistRequest; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; - -/** - * Beans deleted by Id used for updating L2 Cache and Lucene indexes. - */ -public final class DeleteByIdMap { - - private final Map beanMap = new LinkedHashMap(); - - public String toString() { - return beanMap.toString(); - } - - public void notifyCache() { - for (BeanPersistIds deleteIds : beanMap.values()) { - BeanDescriptor d = deleteIds.getBeanDescriptor(); - List idValues = deleteIds.getDeleteIds(); - if (idValues != null){ - d.queryCacheClear(); - for (int i = 0; i < idValues.size(); i++) { - d.cacheRemove(idValues.get(i)); - } - } - } - - } - - public boolean isEmpty() { - return beanMap.isEmpty(); - } - - public Collection values() { - return beanMap.values(); - } - - /** - * Add a Insert Update or Delete payload. - */ - public void add(BeanDescriptor desc, Object id) { - - BeanPersistIds r = getPersistIds(desc); - r.addId(PersistRequest.Type.DELETE, (Serializable)id); - } - - /** - * Add a List of Insert Update or Delete Id's. - */ - public void addList(BeanDescriptor desc, List idList) { - - BeanPersistIds r = getPersistIds(desc); - for (int i = 0; i < idList.size(); i++) { - r.addId(PersistRequest.Type.DELETE, (Serializable) idList.get(i)); - } - } - - private BeanPersistIds getPersistIds(BeanDescriptor desc) { - String beanType = desc.getFullName(); - BeanPersistIds r = beanMap.get(beanType); - if (r == null){ - r = new BeanPersistIds(desc); - beanMap.put(beanType, r); - } - return r; - } - - -} +package com.avaje.ebeaninternal.server.transaction; + +import java.io.Serializable; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.avaje.ebeaninternal.server.core.PersistRequest; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; + +/** + * Beans deleted by Id used for updating L2 Cache and Lucene indexes. + */ +public final class DeleteByIdMap { + + private final Map beanMap = new LinkedHashMap(); + + public String toString() { + return beanMap.toString(); + } + + public void notifyCache() { + for (BeanPersistIds deleteIds : beanMap.values()) { + BeanDescriptor d = deleteIds.getBeanDescriptor(); + List idValues = deleteIds.getDeleteIds(); + if (idValues != null){ + d.queryCacheClear(); + for (int i = 0; i < idValues.size(); i++) { + d.cacheRemove(idValues.get(i)); + } + } + } + + } + + public boolean isEmpty() { + return beanMap.isEmpty(); + } + + public Collection values() { + return beanMap.values(); + } + + /** + * Add a Insert Update or Delete payload. + */ + public void add(BeanDescriptor desc, Object id) { + + BeanPersistIds r = getPersistIds(desc); + r.addId(PersistRequest.Type.DELETE, (Serializable)id); + } + + /** + * Add a List of Insert Update or Delete Id's. + */ + public void addList(BeanDescriptor desc, List idList) { + + BeanPersistIds r = getPersistIds(desc); + for (int i = 0; i < idList.size(); i++) { + r.addId(PersistRequest.Type.DELETE, (Serializable) idList.get(i)); + } + } + + private BeanPersistIds getPersistIds(BeanDescriptor desc) { + String beanType = desc.getFullName(); + BeanPersistIds r = beanMap.get(beanType); + if (r == null){ + r = new BeanPersistIds(desc); + beanMap.put(beanType, r); + } + return r; + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/ExternalTransactionScopeManager.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/ExternalTransactionScopeManager.java index 1c5342d4b..106a7ef5e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/ExternalTransactionScopeManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/ExternalTransactionScopeManager.java @@ -1,67 +1,48 @@ -/** - * Copyright (C) 2009 the original author or 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.transaction; - -import com.avaje.ebean.config.ExternalTransactionManager; -import com.avaje.ebeaninternal.api.SpiTransaction; - -/** - * A TransactionScopeManager aware of external transaction managers. - */ -public class ExternalTransactionScopeManager extends TransactionScopeManager { - - final ExternalTransactionManager externalManager; - - /** - * Instantiates transaction scope manager. - * - * @param transactionManager the transaction manager - */ - public ExternalTransactionScopeManager(TransactionManager transactionManager, ExternalTransactionManager externalManager) { - super(transactionManager); - this.externalManager = externalManager; - } - - public void commit() { - DefaultTransactionThreadLocal.commit(serverName); - } - - - public void end() { - DefaultTransactionThreadLocal.end(serverName); - } - - public SpiTransaction get() { - - return (SpiTransaction)externalManager.getCurrentTransaction(); - } - - public void replace(SpiTransaction trans) { - DefaultTransactionThreadLocal.replace(serverName, trans); - } - - public void rollback() { - DefaultTransactionThreadLocal.rollback(serverName); - } - - public void set(SpiTransaction trans) { - DefaultTransactionThreadLocal.set(serverName, trans); - } -} +package com.avaje.ebeaninternal.server.transaction; + +import com.avaje.ebean.config.ExternalTransactionManager; +import com.avaje.ebeaninternal.api.SpiTransaction; + +/** + * A TransactionScopeManager aware of external transaction managers. + */ +public class ExternalTransactionScopeManager extends TransactionScopeManager { + + final ExternalTransactionManager externalManager; + + /** + * Instantiates transaction scope manager. + * + * @param transactionManager the transaction manager + */ + public ExternalTransactionScopeManager(TransactionManager transactionManager, ExternalTransactionManager externalManager) { + super(transactionManager); + this.externalManager = externalManager; + } + + public void commit() { + DefaultTransactionThreadLocal.commit(serverName); + } + + + public void end() { + DefaultTransactionThreadLocal.end(serverName); + } + + public SpiTransaction get() { + + return (SpiTransaction)externalManager.getCurrentTransaction(); + } + + public void replace(SpiTransaction trans) { + DefaultTransactionThreadLocal.replace(serverName, trans); + } + + public void rollback() { + DefaultTransactionThreadLocal.rollback(serverName); + } + + public void set(SpiTransaction trans) { + DefaultTransactionThreadLocal.set(serverName, trans); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/IndexEvent.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/IndexEvent.java index 557ff7330..9a42063fe 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/IndexEvent.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/IndexEvent.java @@ -1,68 +1,49 @@ -/** - * 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.transaction; - -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 IndexEvent { - - public static final int COMMIT_EVENT = 1; - public static final int OPTIMISE_EVENT = 2; - - private final int eventType; - private final String indexName; - - public IndexEvent(int eventType, String indexName) { - this.eventType = eventType; - this.indexName = indexName; - } - - public int getEventType() { - return eventType; - } - - public String getIndexName() { - return indexName; - } - - public static IndexEvent readBinaryMessage(DataInput dataInput) throws IOException { - - int eventType = dataInput.readInt(); - String indexName = dataInput.readUTF(); - return new IndexEvent(eventType, indexName); - } - - public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { - - BinaryMessage msg = new BinaryMessage(indexName.length()+10); - DataOutputStream os = msg.getOs(); - os.writeInt(BinaryMessage.TYPE_INDEX); - os.writeInt(eventType); - os.writeUTF(indexName); - - msgList.add(msg); - } - -} +package com.avaje.ebeaninternal.server.transaction; + +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 IndexEvent { + + public static final int COMMIT_EVENT = 1; + public static final int OPTIMISE_EVENT = 2; + + private final int eventType; + private final String indexName; + + public IndexEvent(int eventType, String indexName) { + this.eventType = eventType; + this.indexName = indexName; + } + + public int getEventType() { + return eventType; + } + + public String getIndexName() { + return indexName; + } + + public static IndexEvent readBinaryMessage(DataInput dataInput) throws IOException { + + int eventType = dataInput.readInt(); + String indexName = dataInput.readUTF(); + return new IndexEvent(eventType, indexName); + } + + public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { + + BinaryMessage msg = new BinaryMessage(indexName.length()+10); + DataOutputStream os = msg.getOs(); + os.writeInt(BinaryMessage.TYPE_INDEX); + os.writeInt(eventType); + os.writeUTF(indexName); + + msgList.add(msg); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/IndexInvalidate.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/IndexInvalidate.java index a4f3770c4..1f3ca7c7d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/IndexInvalidate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/IndexInvalidate.java @@ -1,73 +1,54 @@ -/** - * 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.transaction; - -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 IndexInvalidate { - - private final String indexName; - - public IndexInvalidate(String indexName) { - this.indexName = indexName; - } - - public String getIndexName() { - return indexName; - } - - @Override - public int hashCode() { - int hc = IndexInvalidate.class.hashCode(); - hc = hc * 31 + indexName.hashCode(); - return hc; - } - - @Override - public boolean equals(Object o){ - if (o instanceof IndexInvalidate == false){ - return false; - } - return indexName.equals(((IndexInvalidate)o).indexName); - } - - public static IndexInvalidate readBinaryMessage(DataInput dataInput) throws IOException { - - - String indexName = dataInput.readUTF(); - return new IndexInvalidate(indexName); - } - - public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { - - BinaryMessage msg = new BinaryMessage(indexName.length()+10); - DataOutputStream os = msg.getOs(); - os.writeInt(BinaryMessage.TYPE_INDEX_INVALIDATE); - os.writeUTF(indexName); - - msgList.add(msg); - } - -} +package com.avaje.ebeaninternal.server.transaction; + +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 IndexInvalidate { + + private final String indexName; + + public IndexInvalidate(String indexName) { + this.indexName = indexName; + } + + public String getIndexName() { + return indexName; + } + + @Override + public int hashCode() { + int hc = IndexInvalidate.class.hashCode(); + hc = hc * 31 + indexName.hashCode(); + return hc; + } + + @Override + public boolean equals(Object o){ + if (o instanceof IndexInvalidate == false){ + return false; + } + return indexName.equals(((IndexInvalidate)o).indexName); + } + + public static IndexInvalidate readBinaryMessage(DataInput dataInput) throws IOException { + + + String indexName = dataInput.readUTF(); + return new IndexInvalidate(indexName); + } + + public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { + + BinaryMessage msg = new BinaryMessage(indexName.length()+10); + DataOutputStream os = msg.getOs(); + os.writeInt(BinaryMessage.TYPE_INDEX_INVALIDATE); + os.writeUTF(indexName); + + msgList.add(msg); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java index e68c1edfe..20aa94dec 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/JdbcTransaction.java @@ -1,689 +1,670 @@ -/** - * 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.transaction; - -import java.sql.Connection; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; -import javax.persistence.RollbackException; - -import com.avaje.ebean.LogLevel; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.api.DerivedRelationshipData; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.api.TransactionEvent; -import com.avaje.ebeaninternal.server.persist.BatchControl; -import com.avaje.ebeaninternal.server.transaction.TransactionManager.OnQueryOnly; - -/** - * JDBC Connection based transaction. - */ -public class JdbcTransaction implements SpiTransaction { - - private static final Logger logger = Logger.getLogger(JdbcTransaction.class.getName()); - - private static final String illegalStateMessage = "Transaction is Inactive"; - - /** - * The associated TransactionManager. - */ - final protected TransactionManager manager; - - /** - * The transaction id. - */ - final String id; - - /** - * Flag to indicate if this was an explicitly created Transaction. - */ - final boolean explicit; - - /** - * Set to true if the connection has autoCommit=true initially. - */ - final boolean autoCommit; - - /** - * Behaviour for ending query only transactions. - */ - final OnQueryOnly onQueryOnly; - - /** - * The status of the transaction. - */ - boolean active; - - /** - * The underlying Connection. - */ - Connection connection; - - /** - * Used to queue up persist requests for batch execution. - */ - BatchControl batchControl; - - /** - * The event which holds persisted beans. - */ - TransactionEvent event; - - /** - * Holder of the objects fetched to ensure unique objects are used. - */ - PersistenceContext persistenceContext; - - /** - * Used to give developers more control over the insert update and delete - * functionality. - */ - boolean persistCascade = true; - - /** - * Flag used for performance to skip commit or rollback of query only - * transactions in read committed transaction isolation. - */ - boolean queryOnly = true; - - boolean localReadOnly; - - LogLevel logLevel; - - /** - * Set to true if using batch processing. - */ - boolean batchMode; - - int batchSize = -1; - - boolean batchFlushOnQuery = true; - - Boolean batchGetGeneratedKeys; - - Boolean batchFlushOnMixed; - - /** - * The depth used by batch processing to help the ordering of statements. - */ - int depth = 0; - - HashSet persistingBeans = new HashSet(); - HashSet deletingBeansHash; - - TransactionLogBuffer logBuffer; - - HashMap> derivedRelMap; - - private final Map userObjects = new ConcurrentHashMap(); - - /** - * Create a new JdbcTransaction. - */ - public JdbcTransaction(String id, boolean explicit, LogLevel logLevel, Connection connection, TransactionManager manager) { - try { - this.active = true; - this.id = id; - this.explicit = explicit; - this.logLevel = logLevel; - this.manager = manager; - this.connection = connection; - this.autoCommit = connection.getAutoCommit(); - if (this.autoCommit) { - connection.setAutoCommit(false); - } - this.onQueryOnly = manager == null ? OnQueryOnly.ROLLBACK : manager.getOnQueryOnly(); - this.persistenceContext = new DefaultPersistenceContext(); - - this.logBuffer = new TransactionLogBuffer(50, id); - - } catch (Exception e) { - throw new PersistenceException(e); - } - } - - public String toString() { - return "Trans[" + id + "]"; - } - - public List getDerivedRelationship(Object bean) { - if (derivedRelMap == null) { - return null; - } - Integer key = Integer.valueOf(System.identityHashCode(bean)); - return derivedRelMap.get(key); - } - - public void registerDerivedRelationship(DerivedRelationshipData derivedRelationship) { - if (derivedRelMap == null) { - derivedRelMap = new HashMap>(); - } - Integer key = Integer.valueOf(System.identityHashCode(derivedRelationship.getAssocBean())); - - List list = derivedRelMap.get(key); - if (list == null) { - list = new ArrayList(); - derivedRelMap.put(key, list); - } - list.add(derivedRelationship); - } - - /** - * Add a bean to the registed list. - *

      - * This is to handle bi-directional relationships where both sides Cascade. - *

      - */ - public void registerDeleteBean(Integer persistingBean) { - if (deletingBeansHash == null) { - deletingBeansHash = new HashSet(); - } - deletingBeansHash.add(persistingBean); - } - - /** - * Unregister the persisted bean. - */ - public void unregisterDeleteBean(Integer persistedBean) { - if (deletingBeansHash != null) { - deletingBeansHash.remove(persistedBean); - } - } - - /** - * Return true if this is a bean that has already been saved/deleted. - */ - public boolean isRegisteredDeleteBean(Integer persistingBean) { - if (deletingBeansHash == null) { - return false; - } else { - return deletingBeansHash.contains(persistingBean); - } - } - - /** - * Unregister the persisted bean. - */ - public void unregisterBean(Object bean) { - persistingBeans.remove(bean); - } - - /** - * Return true if this is a bean that has already been saved. This will - * register the bean if it is not already. - */ - public boolean isRegisteredBean(Object bean) { - return !persistingBeans.add(bean); - } - - /** - * Return the depth of the current persist request plus the diff. This has the - * effect of changing the current depth and returning the new value. Pass - * diff=0 to return the current depth. - *

      - * The depth of 0 is for the initial persist request. It is modified as the - * cascading of the save or delete traverses to the the associated Ones (-1) - * and associated Manys (+1). - *

      - *

      - * The depth is used to help the ordering of batched statements. - *

      - * - * @param diff - * the amount to add or subtract from the depth. - * @return the current depth plus the diff - */ - public int depth(int diff) { - depth += diff; - return depth; - } - - public boolean isReadOnly() { - if (!isActive()) { - throw new IllegalStateException(illegalStateMessage); - } - try { - return connection.isReadOnly(); - } catch (SQLException e) { - throw new PersistenceException(e); - } - } - - public void setReadOnly(boolean readOnly) { - if (!isActive()) { - throw new IllegalStateException(illegalStateMessage); - } - try { - localReadOnly = readOnly; - connection.setReadOnly(readOnly); - } catch (SQLException e) { - throw new PersistenceException(e); - } - } - - public void setBatchMode(boolean batchMode) { - if (!isActive()) { - throw new IllegalStateException(illegalStateMessage); - } - this.batchMode = batchMode; - } - - public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) { - this.batchGetGeneratedKeys = getGeneratedKeys; - if (batchControl != null) { - batchControl.setGetGeneratedKeys(getGeneratedKeys); - } - } - - public void setBatchFlushOnMixed(boolean batchFlushOnMixed) { - this.batchFlushOnMixed = batchFlushOnMixed; - if (batchControl != null) { - batchControl.setBatchFlushOnMixed(batchFlushOnMixed); - } - } - - /** - * Return the batchSize specifically set for this transaction or 0. - *

      - * Returning 0 implies to use the system wide default batch size. - *

      - */ - public int getBatchSize() { - return batchSize; - } - - public void setBatchSize(int batchSize) { - this.batchSize = batchSize; - if (batchControl != null) { - batchControl.setBatchSize(batchSize); - } - } - - public boolean isBatchFlushOnQuery() { - return batchFlushOnQuery; - } - - public void setBatchFlushOnQuery(boolean batchFlushOnQuery) { - this.batchFlushOnQuery = batchFlushOnQuery; - } - - /** - * Return true if this request should be batched. Returning false means that - * this request should be executed immediately. - */ - public boolean isBatchThisRequest() { - if (!explicit && depth <= 0) { - // implicit transaction ... no gain - // by batching where depth <= 0 - return false; - } - return batchMode; - } - - public BatchControl getBatchControl() { - return batchControl; - } - - /** - * Set the BatchControl to the transaction. This is done once per transaction - * on the first persist request. - */ - public void setBatchControl(BatchControl batchControl) { - queryOnly = false; - this.batchControl = batchControl; - // in case these parameters have already been set - if (batchGetGeneratedKeys != null) { - batchControl.setGetGeneratedKeys(batchGetGeneratedKeys); - } - if (batchSize != -1) { - batchControl.setBatchSize(batchSize); - } - if (batchFlushOnMixed != null) { - batchControl.setBatchFlushOnMixed(batchFlushOnMixed); - } - } - - /** - * Flush any queued persist requests. - *

      - * This is general will result in a number of batched PreparedStatements - * executing. - *

      - */ - public void flushBatch() { - if (!isActive()) { - throw new IllegalStateException(illegalStateMessage); - } - if (batchControl != null) { - batchControl.flush(); - } - } - - public void batchFlush() { - flushBatch(); - } - - /** - * Return the persistence context associated with this transaction. - */ - public PersistenceContext getPersistenceContext() { - return persistenceContext; - } - - /** - * Set the persistence context to this transaction. - *

      - * This could be considered similar to EJB3 Extended PersistanceContext. In - * that you get the PersistanceContext from a transaction, hold onto it, and - * then set it back later to a second transaction. - *

      - */ - public void setPersistenceContext(PersistenceContext context) { - if (!isActive()) { - throw new IllegalStateException(illegalStateMessage); - } - this.persistenceContext = context; - } - - /** - * Return the underlying TransactionEvent. - */ - public TransactionEvent getEvent() { - queryOnly = false; - if (event == null) { - event = new TransactionEvent(); - } - return event; - } - - /** - * Set whether transaction logging is on for this transaction. - */ - public void setLoggingOn(boolean loggingOn) { - if (loggingOn) { - logLevel = LogLevel.SQL; - } else { - logLevel = LogLevel.NONE; - } - } - - /** - * Return true if this was an explicitly created transaction. - */ - public boolean isExplicit() { - return explicit; - } - - public boolean isLogSql() { - return logLevel.ordinal() >= LogLevel.SQL.ordinal(); - } - - public boolean isLogSummary() { - return logLevel.ordinal() >= LogLevel.SUMMARY.ordinal(); - } - - public LogLevel getLogLevel() { - return logLevel; - } - - public void setLogLevel(LogLevel logLevel) { - this.logLevel = logLevel; - } - - /** - * Log a message to the transaction log - for PUBLIC use. - */ - public void log(String msg) { - if (isLogSummary()) { - logInternal(msg); - } - } - - /** - * Log a message to the transaction log - for Ebean INTERNAL use. The LogLevel - * should be explicitly checked before calling this method. - */ - public void logInternal(String msg) { - if (manager != null) { - if (logBuffer.add(msg)) { - // buffer full so flush it - manager.log(logBuffer); - logBuffer = logBuffer.newBuffer(); - } - } - } - - /** - * Return the transaction id. - */ - public String getId() { - return id; - } - - /** - * Return the underlying connection for internal use. - */ - public Connection getInternalConnection() { - if (!isActive()) { - throw new IllegalStateException(illegalStateMessage); - } - return connection; - } - - /** - * Return the underlying connection for public use. - */ - public Connection getConnection() { - queryOnly = false; - return getInternalConnection(); - } - - protected void deactivate() { - try { - if (localReadOnly) { - // reset readOnly status prior to returning to pool - connection.setReadOnly(false); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, "Error setting to readOnly?", e); - } - try { - if (this.autoCommit) { - // reset the autoCommit status prior to returning to pool - connection.setAutoCommit(true); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, "Error setting to readOnly?", e); - } - try { - connection.close(); - } catch (Exception ex) { - // the connection pool will automatically remove the - // connection if it does not pass the test - logger.log(Level.SEVERE, "Error closing connection", ex); - } - connection = null; - active = false; - } - - public TransactionLogBuffer getLogBuffer() { - return logBuffer; - } - - /** - * Notify the transaction manager. - */ - protected void notifyCommit() { - if (manager == null) { - return; - } - if (queryOnly) { - manager.notifyOfQueryOnly(true, this, null); - } else { - manager.notifyOfCommit(this); - } - } - - /** - * Rollback, Commit or Close for query only transaction. - *

      - * For a transaction that was used for queries only we can choose to either - * rollback or just close the connection for performance. - *

      - */ - private void commitQueryOnly() { - try { - switch (onQueryOnly) { - case ROLLBACK: - connection.rollback(); - break; - case COMMIT: - connection.commit(); - break; - case CLOSE_ON_READCOMMITTED: - // Connection is closed via deactivate() which follows - // This optimisation is only available at READ COMMITTED Isolation - break; - default: - connection.rollback(); - } - } catch (SQLException e) { - String m = "Error when ending a query only transaction via " + onQueryOnly; - logger.log(Level.SEVERE, m, e); - } - } - - /** - * Commit the transaction. - */ - public void commit() throws RollbackException { - if (!isActive()) { - throw new IllegalStateException(illegalStateMessage); - } - try { - if (queryOnly) { - // can rollback or just close for performance - commitQueryOnly(); - } else { - // commit - if (batchControl != null && !batchControl.isEmpty()) { - batchControl.flush(); - } - connection.commit(); - } - // these will not throw an exception - deactivate(); - notifyCommit(); - - } catch (Exception e) { - throw new RollbackException(e); - } - } - - /** - * Notify the transaction manager. - */ - protected void notifyRollback(Throwable cause) { - if (manager == null) { - return; - } - if (queryOnly) { - manager.notifyOfQueryOnly(false, this, cause); - } else { - manager.notifyOfRollback(this, cause); - } - } - - /** - * Rollback the transaction. - */ - public void rollback() throws PersistenceException { - rollback(null); - } - - /** - * Rollback the transaction. If there is a throwable it is logged as the cause - * in the transaction log. - */ - public void rollback(Throwable cause) throws PersistenceException { - if (!isActive()) { - throw new IllegalStateException(illegalStateMessage); - } - try { - connection.rollback(); - - // these will not throw an exception - deactivate(); - notifyRollback(cause); - - } catch (Exception ex) { - throw new PersistenceException(ex); - } - } - - /** - * If the transaction is active then perform rollback. - */ - public void end() throws PersistenceException { - if (isActive()) { - rollback(); - } - } - - /** - * Return true if the transaction is active. - */ - public boolean isActive() { - return active; - } - - public boolean isPersistCascade() { - return persistCascade; - } - - public void setPersistCascade(boolean persistCascade) { - this.persistCascade = persistCascade; - } - - public void addModification(String tableName, boolean inserts, boolean updates, boolean deletes) { - getEvent().add(tableName, inserts, updates, deletes); - } - - public void putUserObject(String name, Object value) { - userObjects.put(name, value); - } - - public Object getUserObject(String name) { - return userObjects.get(name); - } - - public final TransactionManager getTransactionManger() { - return manager; - } -} +package com.avaje.ebeaninternal.server.transaction; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; +import javax.persistence.RollbackException; + +import com.avaje.ebean.LogLevel; +import com.avaje.ebean.bean.PersistenceContext; +import com.avaje.ebeaninternal.api.DerivedRelationshipData; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.api.TransactionEvent; +import com.avaje.ebeaninternal.server.persist.BatchControl; +import com.avaje.ebeaninternal.server.transaction.TransactionManager.OnQueryOnly; + +/** + * JDBC Connection based transaction. + */ +public class JdbcTransaction implements SpiTransaction { + + private static final Logger logger = Logger.getLogger(JdbcTransaction.class.getName()); + + private static final String illegalStateMessage = "Transaction is Inactive"; + + /** + * The associated TransactionManager. + */ + final protected TransactionManager manager; + + /** + * The transaction id. + */ + final String id; + + /** + * Flag to indicate if this was an explicitly created Transaction. + */ + final boolean explicit; + + /** + * Set to true if the connection has autoCommit=true initially. + */ + final boolean autoCommit; + + /** + * Behaviour for ending query only transactions. + */ + final OnQueryOnly onQueryOnly; + + /** + * The status of the transaction. + */ + boolean active; + + /** + * The underlying Connection. + */ + Connection connection; + + /** + * Used to queue up persist requests for batch execution. + */ + BatchControl batchControl; + + /** + * The event which holds persisted beans. + */ + TransactionEvent event; + + /** + * Holder of the objects fetched to ensure unique objects are used. + */ + PersistenceContext persistenceContext; + + /** + * Used to give developers more control over the insert update and delete + * functionality. + */ + boolean persistCascade = true; + + /** + * Flag used for performance to skip commit or rollback of query only + * transactions in read committed transaction isolation. + */ + boolean queryOnly = true; + + boolean localReadOnly; + + LogLevel logLevel; + + /** + * Set to true if using batch processing. + */ + boolean batchMode; + + int batchSize = -1; + + boolean batchFlushOnQuery = true; + + Boolean batchGetGeneratedKeys; + + Boolean batchFlushOnMixed; + + /** + * The depth used by batch processing to help the ordering of statements. + */ + int depth = 0; + + HashSet persistingBeans = new HashSet(); + HashSet deletingBeansHash; + + TransactionLogBuffer logBuffer; + + HashMap> derivedRelMap; + + private final Map userObjects = new ConcurrentHashMap(); + + /** + * Create a new JdbcTransaction. + */ + public JdbcTransaction(String id, boolean explicit, LogLevel logLevel, Connection connection, TransactionManager manager) { + try { + this.active = true; + this.id = id; + this.explicit = explicit; + this.logLevel = logLevel; + this.manager = manager; + this.connection = connection; + this.autoCommit = connection.getAutoCommit(); + if (this.autoCommit) { + connection.setAutoCommit(false); + } + this.onQueryOnly = manager == null ? OnQueryOnly.ROLLBACK : manager.getOnQueryOnly(); + this.persistenceContext = new DefaultPersistenceContext(); + + this.logBuffer = new TransactionLogBuffer(50, id); + + } catch (Exception e) { + throw new PersistenceException(e); + } + } + + public String toString() { + return "Trans[" + id + "]"; + } + + public List getDerivedRelationship(Object bean) { + if (derivedRelMap == null) { + return null; + } + Integer key = Integer.valueOf(System.identityHashCode(bean)); + return derivedRelMap.get(key); + } + + public void registerDerivedRelationship(DerivedRelationshipData derivedRelationship) { + if (derivedRelMap == null) { + derivedRelMap = new HashMap>(); + } + Integer key = Integer.valueOf(System.identityHashCode(derivedRelationship.getAssocBean())); + + List list = derivedRelMap.get(key); + if (list == null) { + list = new ArrayList(); + derivedRelMap.put(key, list); + } + list.add(derivedRelationship); + } + + /** + * Add a bean to the registed list. + *

      + * This is to handle bi-directional relationships where both sides Cascade. + *

      + */ + public void registerDeleteBean(Integer persistingBean) { + if (deletingBeansHash == null) { + deletingBeansHash = new HashSet(); + } + deletingBeansHash.add(persistingBean); + } + + /** + * Unregister the persisted bean. + */ + public void unregisterDeleteBean(Integer persistedBean) { + if (deletingBeansHash != null) { + deletingBeansHash.remove(persistedBean); + } + } + + /** + * Return true if this is a bean that has already been saved/deleted. + */ + public boolean isRegisteredDeleteBean(Integer persistingBean) { + if (deletingBeansHash == null) { + return false; + } else { + return deletingBeansHash.contains(persistingBean); + } + } + + /** + * Unregister the persisted bean. + */ + public void unregisterBean(Object bean) { + persistingBeans.remove(bean); + } + + /** + * Return true if this is a bean that has already been saved. This will + * register the bean if it is not already. + */ + public boolean isRegisteredBean(Object bean) { + return !persistingBeans.add(bean); + } + + /** + * Return the depth of the current persist request plus the diff. This has the + * effect of changing the current depth and returning the new value. Pass + * diff=0 to return the current depth. + *

      + * The depth of 0 is for the initial persist request. It is modified as the + * cascading of the save or delete traverses to the the associated Ones (-1) + * and associated Manys (+1). + *

      + *

      + * The depth is used to help the ordering of batched statements. + *

      + * + * @param diff + * the amount to add or subtract from the depth. + * @return the current depth plus the diff + */ + public int depth(int diff) { + depth += diff; + return depth; + } + + public boolean isReadOnly() { + if (!isActive()) { + throw new IllegalStateException(illegalStateMessage); + } + try { + return connection.isReadOnly(); + } catch (SQLException e) { + throw new PersistenceException(e); + } + } + + public void setReadOnly(boolean readOnly) { + if (!isActive()) { + throw new IllegalStateException(illegalStateMessage); + } + try { + localReadOnly = readOnly; + connection.setReadOnly(readOnly); + } catch (SQLException e) { + throw new PersistenceException(e); + } + } + + public void setBatchMode(boolean batchMode) { + if (!isActive()) { + throw new IllegalStateException(illegalStateMessage); + } + this.batchMode = batchMode; + } + + public void setBatchGetGeneratedKeys(boolean getGeneratedKeys) { + this.batchGetGeneratedKeys = getGeneratedKeys; + if (batchControl != null) { + batchControl.setGetGeneratedKeys(getGeneratedKeys); + } + } + + public void setBatchFlushOnMixed(boolean batchFlushOnMixed) { + this.batchFlushOnMixed = batchFlushOnMixed; + if (batchControl != null) { + batchControl.setBatchFlushOnMixed(batchFlushOnMixed); + } + } + + /** + * Return the batchSize specifically set for this transaction or 0. + *

      + * Returning 0 implies to use the system wide default batch size. + *

      + */ + public int getBatchSize() { + return batchSize; + } + + public void setBatchSize(int batchSize) { + this.batchSize = batchSize; + if (batchControl != null) { + batchControl.setBatchSize(batchSize); + } + } + + public boolean isBatchFlushOnQuery() { + return batchFlushOnQuery; + } + + public void setBatchFlushOnQuery(boolean batchFlushOnQuery) { + this.batchFlushOnQuery = batchFlushOnQuery; + } + + /** + * Return true if this request should be batched. Returning false means that + * this request should be executed immediately. + */ + public boolean isBatchThisRequest() { + if (!explicit && depth <= 0) { + // implicit transaction ... no gain + // by batching where depth <= 0 + return false; + } + return batchMode; + } + + public BatchControl getBatchControl() { + return batchControl; + } + + /** + * Set the BatchControl to the transaction. This is done once per transaction + * on the first persist request. + */ + public void setBatchControl(BatchControl batchControl) { + queryOnly = false; + this.batchControl = batchControl; + // in case these parameters have already been set + if (batchGetGeneratedKeys != null) { + batchControl.setGetGeneratedKeys(batchGetGeneratedKeys); + } + if (batchSize != -1) { + batchControl.setBatchSize(batchSize); + } + if (batchFlushOnMixed != null) { + batchControl.setBatchFlushOnMixed(batchFlushOnMixed); + } + } + + /** + * Flush any queued persist requests. + *

      + * This is general will result in a number of batched PreparedStatements + * executing. + *

      + */ + public void flushBatch() { + if (!isActive()) { + throw new IllegalStateException(illegalStateMessage); + } + if (batchControl != null) { + batchControl.flush(); + } + } + + public void batchFlush() { + flushBatch(); + } + + /** + * Return the persistence context associated with this transaction. + */ + public PersistenceContext getPersistenceContext() { + return persistenceContext; + } + + /** + * Set the persistence context to this transaction. + *

      + * This could be considered similar to EJB3 Extended PersistanceContext. In + * that you get the PersistanceContext from a transaction, hold onto it, and + * then set it back later to a second transaction. + *

      + */ + public void setPersistenceContext(PersistenceContext context) { + if (!isActive()) { + throw new IllegalStateException(illegalStateMessage); + } + this.persistenceContext = context; + } + + /** + * Return the underlying TransactionEvent. + */ + public TransactionEvent getEvent() { + queryOnly = false; + if (event == null) { + event = new TransactionEvent(); + } + return event; + } + + /** + * Set whether transaction logging is on for this transaction. + */ + public void setLoggingOn(boolean loggingOn) { + if (loggingOn) { + logLevel = LogLevel.SQL; + } else { + logLevel = LogLevel.NONE; + } + } + + /** + * Return true if this was an explicitly created transaction. + */ + public boolean isExplicit() { + return explicit; + } + + public boolean isLogSql() { + return logLevel.ordinal() >= LogLevel.SQL.ordinal(); + } + + public boolean isLogSummary() { + return logLevel.ordinal() >= LogLevel.SUMMARY.ordinal(); + } + + public LogLevel getLogLevel() { + return logLevel; + } + + public void setLogLevel(LogLevel logLevel) { + this.logLevel = logLevel; + } + + /** + * Log a message to the transaction log - for PUBLIC use. + */ + public void log(String msg) { + if (isLogSummary()) { + logInternal(msg); + } + } + + /** + * Log a message to the transaction log - for Ebean INTERNAL use. The LogLevel + * should be explicitly checked before calling this method. + */ + public void logInternal(String msg) { + if (manager != null) { + if (logBuffer.add(msg)) { + // buffer full so flush it + manager.log(logBuffer); + logBuffer = logBuffer.newBuffer(); + } + } + } + + /** + * Return the transaction id. + */ + public String getId() { + return id; + } + + /** + * Return the underlying connection for internal use. + */ + public Connection getInternalConnection() { + if (!isActive()) { + throw new IllegalStateException(illegalStateMessage); + } + return connection; + } + + /** + * Return the underlying connection for public use. + */ + public Connection getConnection() { + queryOnly = false; + return getInternalConnection(); + } + + protected void deactivate() { + try { + if (localReadOnly) { + // reset readOnly status prior to returning to pool + connection.setReadOnly(false); + } + } catch (SQLException e) { + logger.log(Level.SEVERE, "Error setting to readOnly?", e); + } + try { + if (this.autoCommit) { + // reset the autoCommit status prior to returning to pool + connection.setAutoCommit(true); + } + } catch (SQLException e) { + logger.log(Level.SEVERE, "Error setting to readOnly?", e); + } + try { + connection.close(); + } catch (Exception ex) { + // the connection pool will automatically remove the + // connection if it does not pass the test + logger.log(Level.SEVERE, "Error closing connection", ex); + } + connection = null; + active = false; + } + + public TransactionLogBuffer getLogBuffer() { + return logBuffer; + } + + /** + * Notify the transaction manager. + */ + protected void notifyCommit() { + if (manager == null) { + return; + } + if (queryOnly) { + manager.notifyOfQueryOnly(true, this, null); + } else { + manager.notifyOfCommit(this); + } + } + + /** + * Rollback, Commit or Close for query only transaction. + *

      + * For a transaction that was used for queries only we can choose to either + * rollback or just close the connection for performance. + *

      + */ + private void commitQueryOnly() { + try { + switch (onQueryOnly) { + case ROLLBACK: + connection.rollback(); + break; + case COMMIT: + connection.commit(); + break; + case CLOSE_ON_READCOMMITTED: + // Connection is closed via deactivate() which follows + // This optimisation is only available at READ COMMITTED Isolation + break; + default: + connection.rollback(); + } + } catch (SQLException e) { + String m = "Error when ending a query only transaction via " + onQueryOnly; + logger.log(Level.SEVERE, m, e); + } + } + + /** + * Commit the transaction. + */ + public void commit() throws RollbackException { + if (!isActive()) { + throw new IllegalStateException(illegalStateMessage); + } + try { + if (queryOnly) { + // can rollback or just close for performance + commitQueryOnly(); + } else { + // commit + if (batchControl != null && !batchControl.isEmpty()) { + batchControl.flush(); + } + connection.commit(); + } + // these will not throw an exception + deactivate(); + notifyCommit(); + + } catch (Exception e) { + throw new RollbackException(e); + } + } + + /** + * Notify the transaction manager. + */ + protected void notifyRollback(Throwable cause) { + if (manager == null) { + return; + } + if (queryOnly) { + manager.notifyOfQueryOnly(false, this, cause); + } else { + manager.notifyOfRollback(this, cause); + } + } + + /** + * Rollback the transaction. + */ + public void rollback() throws PersistenceException { + rollback(null); + } + + /** + * Rollback the transaction. If there is a throwable it is logged as the cause + * in the transaction log. + */ + public void rollback(Throwable cause) throws PersistenceException { + if (!isActive()) { + throw new IllegalStateException(illegalStateMessage); + } + try { + connection.rollback(); + + // these will not throw an exception + deactivate(); + notifyRollback(cause); + + } catch (Exception ex) { + throw new PersistenceException(ex); + } + } + + /** + * If the transaction is active then perform rollback. + */ + public void end() throws PersistenceException { + if (isActive()) { + rollback(); + } + } + + /** + * Return true if the transaction is active. + */ + public boolean isActive() { + return active; + } + + public boolean isPersistCascade() { + return persistCascade; + } + + public void setPersistCascade(boolean persistCascade) { + this.persistCascade = persistCascade; + } + + public void addModification(String tableName, boolean inserts, boolean updates, boolean deletes) { + getEvent().add(tableName, inserts, updates, deletes); + } + + public void putUserObject(String name, Object value) { + userObjects.put(name, value); + } + + public Object getUserObject(String name) { + return userObjects.get(name); + } + + public final TransactionManager getTransactionManger() { + return manager; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/JtaTransaction.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/JtaTransaction.java index e1d42f2ac..d42d72d81 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/JtaTransaction.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/JtaTransaction.java @@ -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.transaction; - -import java.sql.SQLException; - -import javax.persistence.PersistenceException; -import javax.sql.DataSource; -import javax.transaction.Status; -import javax.transaction.UserTransaction; - -import com.avaje.ebean.LogLevel; - -/** - * Jta based transaction. - */ -public class JtaTransaction extends JdbcTransaction { - - private UserTransaction userTransaction; - - private DataSource dataSource; - - private boolean commmitted = false; - - private boolean newTransaction = false; - - - /** - * Create the JtaTransaction. - */ - public JtaTransaction(String id, boolean explicit, LogLevel logLevel, UserTransaction utx, DataSource ds, TransactionManager manager) { - super(id, explicit, logLevel, null, manager); - userTransaction = utx; - dataSource = ds; - - try { - newTransaction = userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION; - if (newTransaction) { - userTransaction.begin(); - } - } catch (Exception e) { - throw new PersistenceException(e); - } - - try { - // Open JDBC Connection - this.connection = dataSource.getConnection(); - if (connection == null) { - throw new PersistenceException("The DataSource returned a null connection."); - } - if (connection.getAutoCommit()) { - connection.setAutoCommit(false); - } - - } catch (SQLException e) { - throw new PersistenceException(e); - } - } - - /** - * Commit the transaction. - */ - public void commit() { - if (commmitted) { - throw new PersistenceException("This transaction has already been committed."); - } - try { - try { - if (newTransaction) { - userTransaction.commit(); - } - notifyCommit(); - } finally { - close(); - } - } catch (Exception e) { - throw new PersistenceException(e); - } - commmitted = true; - } - - public void rollback() { - rollback(null); - } - - /** - * Rollback the transaction. - */ - public void rollback(Throwable e) { - if (!commmitted) { - try { - try { - if (userTransaction != null) { - if (newTransaction) { - userTransaction.rollback(); - } else { - userTransaction.setRollbackOnly(); - } - } - notifyRollback(e); - } finally { - close(); - } - } catch (Exception ex) { - throw new PersistenceException(ex); - } - } - - } - - /** - * Close the underlying connection. - */ - private void close() throws SQLException { - if (connection != null) { - connection.close(); - connection = null; - } - } - -} +package com.avaje.ebeaninternal.server.transaction; + +import java.sql.SQLException; + +import javax.persistence.PersistenceException; +import javax.sql.DataSource; +import javax.transaction.Status; +import javax.transaction.UserTransaction; + +import com.avaje.ebean.LogLevel; + +/** + * Jta based transaction. + */ +public class JtaTransaction extends JdbcTransaction { + + private UserTransaction userTransaction; + + private DataSource dataSource; + + private boolean commmitted = false; + + private boolean newTransaction = false; + + + /** + * Create the JtaTransaction. + */ + public JtaTransaction(String id, boolean explicit, LogLevel logLevel, UserTransaction utx, DataSource ds, TransactionManager manager) { + super(id, explicit, logLevel, null, manager); + userTransaction = utx; + dataSource = ds; + + try { + newTransaction = userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION; + if (newTransaction) { + userTransaction.begin(); + } + } catch (Exception e) { + throw new PersistenceException(e); + } + + try { + // Open JDBC Connection + this.connection = dataSource.getConnection(); + if (connection == null) { + throw new PersistenceException("The DataSource returned a null connection."); + } + if (connection.getAutoCommit()) { + connection.setAutoCommit(false); + } + + } catch (SQLException e) { + throw new PersistenceException(e); + } + } + + /** + * Commit the transaction. + */ + public void commit() { + if (commmitted) { + throw new PersistenceException("This transaction has already been committed."); + } + try { + try { + if (newTransaction) { + userTransaction.commit(); + } + notifyCommit(); + } finally { + close(); + } + } catch (Exception e) { + throw new PersistenceException(e); + } + commmitted = true; + } + + public void rollback() { + rollback(null); + } + + /** + * Rollback the transaction. + */ + public void rollback(Throwable e) { + if (!commmitted) { + try { + try { + if (userTransaction != null) { + if (newTransaction) { + userTransaction.rollback(); + } else { + userTransaction.setRollbackOnly(); + } + } + notifyRollback(e); + } finally { + close(); + } + } catch (Exception ex) { + throw new PersistenceException(ex); + } + } + + } + + /** + * Close the underlying connection. + */ + private void close() throws SQLException { + if (connection != null) { + connection.close(); + connection = null; + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/JtaTransactionManager.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/JtaTransactionManager.java index 00e6717bb..3a36caf1d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/JtaTransactionManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/JtaTransactionManager.java @@ -1,249 +1,230 @@ -/** - * 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.transaction; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.naming.InitialContext; -import javax.naming.NamingException; -import javax.persistence.PersistenceException; -import javax.sql.DataSource; -import javax.transaction.HeuristicMixedException; -import javax.transaction.HeuristicRollbackException; -import javax.transaction.NotSupportedException; -import javax.transaction.RollbackException; -import javax.transaction.Status; -import javax.transaction.Synchronization; -import javax.transaction.SystemException; -import javax.transaction.TransactionSynchronizationRegistry; -import javax.transaction.UserTransaction; - -import com.avaje.ebean.LogLevel; -import com.avaje.ebean.config.ExternalTransactionManager; -import com.avaje.ebeaninternal.api.SpiTransaction; - -/** - * Hook into external JTA transaction manager. - * - * @author rbygrave - */ -public class JtaTransactionManager implements ExternalTransactionManager { - - private final static Logger logger = Logger.getLogger(JtaTransactionManager.class.getName()); - - private static final String EBEAN_TXN_RESOURCE = "EBEAN_TXN_RESOURCE"; - - /** - * The data source. - */ - private DataSource dataSource; - - /** - * The Ebean transaction manager. - */ - private TransactionManager transactionManager; - - /** - * The EbeanServer name. - */ - private String serverName; - - /** - * Instantiates a new spring aware transaction scope manager. - */ - public JtaTransactionManager() { - } - - /** - * Initialise this with the Ebean internal transaction manager. - */ - public void setTransactionManager(Object txnMgr) { - - // RB: At this stage not exposing TransactionManager to - // the public API and hence the Object type and casting here - - this.transactionManager = (TransactionManager) txnMgr; - this.dataSource = transactionManager.getDataSource(); - this.serverName = transactionManager.getServerName(); - } - - private TransactionSynchronizationRegistry getSyncRegistry() { - try { - InitialContext ctx = new InitialContext(); - return (TransactionSynchronizationRegistry)ctx.lookup("java:comp/TransactionSynchronizationRegistry"); - } catch (NamingException e){ - throw new PersistenceException(e); - } - } - - private UserTransaction getUserTransaction() { - try { - InitialContext ctx = new InitialContext(); - return (UserTransaction) ctx.lookup("java:comp/UserTransaction"); - } catch (NamingException e){ - // assuming CMT - return new DummyUserTransaction(); - } - } - - /** - * Looks for a current Spring managed transaction and wraps/returns that as a Ebean transaction. - *

      - * Returns null if there is no current spring transaction (lazy loading outside a spring txn etc). - *

      - */ - public Object getCurrentTransaction() { - - TransactionSynchronizationRegistry syncRegistry = getSyncRegistry(); - - SpiTransaction t = (SpiTransaction)syncRegistry.getResource(EBEAN_TXN_RESOURCE); - if (t != null){ - // we have already seen this transaction - return t; - } - - // check current Ebean transaction - SpiTransaction currentEbeanTransaction = DefaultTransactionThreadLocal.get(serverName); - if (currentEbeanTransaction != null){ - // NOT expecting this so log WARNING - String msg = "JTA Transaction - no current txn BUT using current Ebean one "+currentEbeanTransaction.getId(); - logger.log(Level.WARNING, msg); - return currentEbeanTransaction; - } - - UserTransaction ut = getUserTransaction(); - if (ut == null){ - // no current JTA transaction - if (logger.isLoggable(Level.FINE)){ - logger.fine("JTA Transaction - no current txn"); - } - return null; - } - - // This is a transaction that Ebean has not seen before. - - // "wrap" it in a Ebean specific JtaTransaction - String txnId = String.valueOf(System.currentTimeMillis()); - JtaTransaction newTrans = new JtaTransaction(txnId, true, LogLevel.NONE, ut, dataSource, transactionManager); - - // create and register transaction listener - JtaTxnListener txnListener = createJtaTxnListener(newTrans); - - syncRegistry.putResource(EBEAN_TXN_RESOURCE, newTrans); - syncRegistry.registerInterposedSynchronization(txnListener); - - // also put in Ebean ThreadLocal - DefaultTransactionThreadLocal.set(serverName, newTrans); - return newTrans; - } - - - /** - * Create a listener to register with JTA to enable Ebean to be - * notified when transactions commit and rollback. - *

      - * This is used by Ebean to notify it's appropriate listeners and maintain it's server - * cache etc. - *

      - */ - private JtaTxnListener createJtaTxnListener(SpiTransaction t) { - return new JtaTxnListener(transactionManager, t); - } - - private static class DummyUserTransaction implements UserTransaction { - - public void begin() throws NotSupportedException, SystemException { - } - - public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, - SecurityException, IllegalStateException, SystemException { - } - - public int getStatus() throws SystemException { - return 0; - } - - public void rollback() throws IllegalStateException, SecurityException, SystemException { - } - - public void setRollbackOnly() throws IllegalStateException, SystemException { - } - - public void setTransactionTimeout(int seconds) throws SystemException { - } - } - - /** - * A JTA Transaction Synchronization that we register to get notified when a - * managed transaction has been committed or rolled back. - *

      - * When Ebean is notified (of the commit/rollback) it can then manage its - * cache, notify BeanPersistListeners etc. - *

      - */ - private static class JtaTxnListener implements Synchronization { - - private final TransactionManager transactionManager; - - private final SpiTransaction transaction; - - private final String serverName; - - private JtaTxnListener(TransactionManager transactionManager, SpiTransaction t){ - this.transactionManager = transactionManager; - this.transaction = t; - this.serverName = transactionManager.getServerName(); - } - - public void beforeCompletion() { - // Future note: for JPA2 locking we will - // have beforeCommit events to fire - } - - public void afterCompletion(int status) { - - switch (status) { - case Status.STATUS_COMMITTED: - if (logger.isLoggable(Level.FINE)){ - logger.fine("Jta Txn ["+transaction.getId()+"] committed"); - } - transactionManager.notifyOfCommit(transaction); - // Remove this transaction object as it is completed - DefaultTransactionThreadLocal.replace(serverName, null); - break; - - case Status.STATUS_ROLLEDBACK: - if (logger.isLoggable(Level.FINE)){ - logger.fine("Jta Txn ["+transaction.getId()+"] rollback"); - } - transactionManager.notifyOfRollback(transaction, null); - // Remove this transaction object as it is completed - DefaultTransactionThreadLocal.replace(serverName, null); - break; - - default: - logger.fine("Jta Txn ["+transaction.getId()+"] status:"+status); - } - - } - } - -} +package com.avaje.ebeaninternal.server.transaction; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.persistence.PersistenceException; +import javax.sql.DataSource; +import javax.transaction.HeuristicMixedException; +import javax.transaction.HeuristicRollbackException; +import javax.transaction.NotSupportedException; +import javax.transaction.RollbackException; +import javax.transaction.Status; +import javax.transaction.Synchronization; +import javax.transaction.SystemException; +import javax.transaction.TransactionSynchronizationRegistry; +import javax.transaction.UserTransaction; + +import com.avaje.ebean.LogLevel; +import com.avaje.ebean.config.ExternalTransactionManager; +import com.avaje.ebeaninternal.api.SpiTransaction; + +/** + * Hook into external JTA transaction manager. + * + * @author rbygrave + */ +public class JtaTransactionManager implements ExternalTransactionManager { + + private final static Logger logger = Logger.getLogger(JtaTransactionManager.class.getName()); + + private static final String EBEAN_TXN_RESOURCE = "EBEAN_TXN_RESOURCE"; + + /** + * The data source. + */ + private DataSource dataSource; + + /** + * The Ebean transaction manager. + */ + private TransactionManager transactionManager; + + /** + * The EbeanServer name. + */ + private String serverName; + + /** + * Instantiates a new spring aware transaction scope manager. + */ + public JtaTransactionManager() { + } + + /** + * Initialise this with the Ebean internal transaction manager. + */ + public void setTransactionManager(Object txnMgr) { + + // RB: At this stage not exposing TransactionManager to + // the public API and hence the Object type and casting here + + this.transactionManager = (TransactionManager) txnMgr; + this.dataSource = transactionManager.getDataSource(); + this.serverName = transactionManager.getServerName(); + } + + private TransactionSynchronizationRegistry getSyncRegistry() { + try { + InitialContext ctx = new InitialContext(); + return (TransactionSynchronizationRegistry)ctx.lookup("java:comp/TransactionSynchronizationRegistry"); + } catch (NamingException e){ + throw new PersistenceException(e); + } + } + + private UserTransaction getUserTransaction() { + try { + InitialContext ctx = new InitialContext(); + return (UserTransaction) ctx.lookup("java:comp/UserTransaction"); + } catch (NamingException e){ + // assuming CMT + return new DummyUserTransaction(); + } + } + + /** + * Looks for a current Spring managed transaction and wraps/returns that as a Ebean transaction. + *

      + * Returns null if there is no current spring transaction (lazy loading outside a spring txn etc). + *

      + */ + public Object getCurrentTransaction() { + + TransactionSynchronizationRegistry syncRegistry = getSyncRegistry(); + + SpiTransaction t = (SpiTransaction)syncRegistry.getResource(EBEAN_TXN_RESOURCE); + if (t != null){ + // we have already seen this transaction + return t; + } + + // check current Ebean transaction + SpiTransaction currentEbeanTransaction = DefaultTransactionThreadLocal.get(serverName); + if (currentEbeanTransaction != null){ + // NOT expecting this so log WARNING + String msg = "JTA Transaction - no current txn BUT using current Ebean one "+currentEbeanTransaction.getId(); + logger.log(Level.WARNING, msg); + return currentEbeanTransaction; + } + + UserTransaction ut = getUserTransaction(); + if (ut == null){ + // no current JTA transaction + if (logger.isLoggable(Level.FINE)){ + logger.fine("JTA Transaction - no current txn"); + } + return null; + } + + // This is a transaction that Ebean has not seen before. + + // "wrap" it in a Ebean specific JtaTransaction + String txnId = String.valueOf(System.currentTimeMillis()); + JtaTransaction newTrans = new JtaTransaction(txnId, true, LogLevel.NONE, ut, dataSource, transactionManager); + + // create and register transaction listener + JtaTxnListener txnListener = createJtaTxnListener(newTrans); + + syncRegistry.putResource(EBEAN_TXN_RESOURCE, newTrans); + syncRegistry.registerInterposedSynchronization(txnListener); + + // also put in Ebean ThreadLocal + DefaultTransactionThreadLocal.set(serverName, newTrans); + return newTrans; + } + + + /** + * Create a listener to register with JTA to enable Ebean to be + * notified when transactions commit and rollback. + *

      + * This is used by Ebean to notify it's appropriate listeners and maintain it's server + * cache etc. + *

      + */ + private JtaTxnListener createJtaTxnListener(SpiTransaction t) { + return new JtaTxnListener(transactionManager, t); + } + + private static class DummyUserTransaction implements UserTransaction { + + public void begin() throws NotSupportedException, SystemException { + } + + public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, + SecurityException, IllegalStateException, SystemException { + } + + public int getStatus() throws SystemException { + return 0; + } + + public void rollback() throws IllegalStateException, SecurityException, SystemException { + } + + public void setRollbackOnly() throws IllegalStateException, SystemException { + } + + public void setTransactionTimeout(int seconds) throws SystemException { + } + } + + /** + * A JTA Transaction Synchronization that we register to get notified when a + * managed transaction has been committed or rolled back. + *

      + * When Ebean is notified (of the commit/rollback) it can then manage its + * cache, notify BeanPersistListeners etc. + *

      + */ + private static class JtaTxnListener implements Synchronization { + + private final TransactionManager transactionManager; + + private final SpiTransaction transaction; + + private final String serverName; + + private JtaTxnListener(TransactionManager transactionManager, SpiTransaction t){ + this.transactionManager = transactionManager; + this.transaction = t; + this.serverName = transactionManager.getServerName(); + } + + public void beforeCompletion() { + // Future note: for JPA2 locking we will + // have beforeCommit events to fire + } + + public void afterCompletion(int status) { + + switch (status) { + case Status.STATUS_COMMITTED: + if (logger.isLoggable(Level.FINE)){ + logger.fine("Jta Txn ["+transaction.getId()+"] committed"); + } + transactionManager.notifyOfCommit(transaction); + // Remove this transaction object as it is completed + DefaultTransactionThreadLocal.replace(serverName, null); + break; + + case Status.STATUS_ROLLEDBACK: + if (logger.isLoggable(Level.FINE)){ + logger.fine("Jta Txn ["+transaction.getId()+"] rollback"); + } + transactionManager.notifyOfRollback(transaction, null); + // Remove this transaction object as it is completed + DefaultTransactionThreadLocal.replace(serverName, null); + break; + + default: + logger.fine("Jta Txn ["+transaction.getId()+"] status:"+status); + } + + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/PostCommitProcessing.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/PostCommitProcessing.java index f2f7b49ff..41ec86499 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/PostCommitProcessing.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/PostCommitProcessing.java @@ -1,202 +1,183 @@ -/** - * 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.transaction; - -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.api.TransactionEvent; -import com.avaje.ebeaninternal.api.TransactionEventBeans; -import com.avaje.ebeaninternal.api.TransactionEventTable; -import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD; -import com.avaje.ebeaninternal.server.cluster.ClusterManager; -import com.avaje.ebeaninternal.server.core.PersistRequestBean; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; - -/** - * Performs post commit processing using a background thread. - *

      - * This includes Cluster notification, and BeanPersistListeners. - *

      - */ -public final class PostCommitProcessing { - - private static final Logger logger = Logger.getLogger(PostCommitProcessing.class.getName()); - - private final ClusterManager clusterManager; - - private final TransactionEvent event; - - private final String serverName; - - private final TransactionManager manager; - - private final List> persistBeanRequests; - - private final BeanPersistIdMap beanPersistIdMap; - -// private final BeanDeltaMap beanDeltaMap; - - private final RemoteTransactionEvent remoteTransactionEvent; - - private final DeleteByIdMap deleteByIdMap; - - /** - * Create for a TransactionManager and event. - */ - public PostCommitProcessing(ClusterManager clusterManager, TransactionManager manager, SpiTransaction transaction, TransactionEvent event) { - - this.clusterManager = clusterManager; - this.manager = manager; - this.serverName = manager.getServerName(); - this.event = event; - this.deleteByIdMap = event.getDeleteByIdMap(); - this.persistBeanRequests = createPersistBeanRequests(); - - this.beanPersistIdMap = createBeanPersistIdMap(); - //this.beanDeltaMap = new BeanDeltaMap(event.getBeanDeltas()); - - this.remoteTransactionEvent = createRemoteTransactionEvent(); - } - - public void notifyLocalCacheIndex() { - - // notify cache with bulk insert/update/delete statements - processTableEvents(event.getEventTables()); - - // notify cache with bean changes - event.notifyCache(); - } - - /** - * Table events are where SQL or external tools are used. In this case the - * cache is notified based on the table name (rather than bean type). - */ - private void processTableEvents(TransactionEventTable tableEvents) { - - if (tableEvents != null && !tableEvents.isEmpty()) { - // notify cache with table based changes - BeanDescriptorManager dm = manager.getBeanDescriptorManager(); - for (TableIUD tableIUD : tableEvents.values()) { - dm.cacheNotify(tableIUD); - } - } - } - - public void notifyCluster() { - if (remoteTransactionEvent != null && !remoteTransactionEvent.isEmpty()) { - // send the interesting events to the cluster - if (manager.getClusterDebugLevel() > 0 || logger.isLoggable(Level.FINE)) { - logger.info("Cluster Send: " + remoteTransactionEvent.toString()); - } - - clusterManager.broadcast(remoteTransactionEvent); - } - } - - public Runnable notifyPersistListeners() { - return new Runnable() { - public void run() { - localPersistListenersNotify(); - } - }; - } - - private void localPersistListenersNotify() { - if (persistBeanRequests != null) { - for (int i = 0; i < persistBeanRequests.size(); i++) { - persistBeanRequests.get(i).notifyLocalPersistListener(); - } - } - TransactionEventTable eventTables = event.getEventTables(); - if (eventTables != null && !eventTables.isEmpty()) { - BulkEventListenerMap map = manager.getBulkEventListenerMap(); - for (TableIUD tableIUD : eventTables.values()) { - map.process(tableIUD); - } - } - } - - private List> createPersistBeanRequests() { - TransactionEventBeans eventBeans = event.getEventBeans(); - if (eventBeans != null) { - return eventBeans.getRequests(); - } - return null; - } - - private BeanPersistIdMap createBeanPersistIdMap() { - - if (persistBeanRequests == null) { - return null; - } - - BeanPersistIdMap m = new BeanPersistIdMap(); - for (int i = 0; i < persistBeanRequests.size(); i++) { - persistBeanRequests.get(i).addToPersistMap(m); - } - return m; - } - - private RemoteTransactionEvent createRemoteTransactionEvent() { - - if (!clusterManager.isClustering()) { - return null; - } - - RemoteTransactionEvent remoteTransactionEvent = new RemoteTransactionEvent(serverName); - -// if (beanDeltaMap != null) { -// for (BeanDeltaList deltaList : beanDeltaMap.deltaLists()) { -// remoteTransactionEvent.addBeanDeltaList(deltaList); -// } -// } - - if (beanPersistIdMap != null) { - for (BeanPersistIds beanPersist : beanPersistIdMap.values()) { - remoteTransactionEvent.addBeanPersistIds(beanPersist); - } - } - - if (deleteByIdMap != null) { - remoteTransactionEvent.setDeleteByIdMap(deleteByIdMap); - } - - TransactionEventTable eventTables = event.getEventTables(); - if (eventTables != null && !eventTables.isEmpty()) { - for (TableIUD tableIUD : eventTables.values()) { - remoteTransactionEvent.addTableIUD(tableIUD); - } - } - - Set indexInvalidations = event.getIndexInvalidations(); - if (indexInvalidations != null) { - for (IndexInvalidate indexInvalidate : indexInvalidations) { - remoteTransactionEvent.addIndexInvalidate(indexInvalidate); - } - } - - return remoteTransactionEvent; - } - -} +package com.avaje.ebeaninternal.server.transaction; + +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.api.TransactionEvent; +import com.avaje.ebeaninternal.api.TransactionEventBeans; +import com.avaje.ebeaninternal.api.TransactionEventTable; +import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD; +import com.avaje.ebeaninternal.server.cluster.ClusterManager; +import com.avaje.ebeaninternal.server.core.PersistRequestBean; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; + +/** + * Performs post commit processing using a background thread. + *

      + * This includes Cluster notification, and BeanPersistListeners. + *

      + */ +public final class PostCommitProcessing { + + private static final Logger logger = Logger.getLogger(PostCommitProcessing.class.getName()); + + private final ClusterManager clusterManager; + + private final TransactionEvent event; + + private final String serverName; + + private final TransactionManager manager; + + private final List> persistBeanRequests; + + private final BeanPersistIdMap beanPersistIdMap; + +// private final BeanDeltaMap beanDeltaMap; + + private final RemoteTransactionEvent remoteTransactionEvent; + + private final DeleteByIdMap deleteByIdMap; + + /** + * Create for a TransactionManager and event. + */ + public PostCommitProcessing(ClusterManager clusterManager, TransactionManager manager, SpiTransaction transaction, TransactionEvent event) { + + this.clusterManager = clusterManager; + this.manager = manager; + this.serverName = manager.getServerName(); + this.event = event; + this.deleteByIdMap = event.getDeleteByIdMap(); + this.persistBeanRequests = createPersistBeanRequests(); + + this.beanPersistIdMap = createBeanPersistIdMap(); + //this.beanDeltaMap = new BeanDeltaMap(event.getBeanDeltas()); + + this.remoteTransactionEvent = createRemoteTransactionEvent(); + } + + public void notifyLocalCacheIndex() { + + // notify cache with bulk insert/update/delete statements + processTableEvents(event.getEventTables()); + + // notify cache with bean changes + event.notifyCache(); + } + + /** + * Table events are where SQL or external tools are used. In this case the + * cache is notified based on the table name (rather than bean type). + */ + private void processTableEvents(TransactionEventTable tableEvents) { + + if (tableEvents != null && !tableEvents.isEmpty()) { + // notify cache with table based changes + BeanDescriptorManager dm = manager.getBeanDescriptorManager(); + for (TableIUD tableIUD : tableEvents.values()) { + dm.cacheNotify(tableIUD); + } + } + } + + public void notifyCluster() { + if (remoteTransactionEvent != null && !remoteTransactionEvent.isEmpty()) { + // send the interesting events to the cluster + if (manager.getClusterDebugLevel() > 0 || logger.isLoggable(Level.FINE)) { + logger.info("Cluster Send: " + remoteTransactionEvent.toString()); + } + + clusterManager.broadcast(remoteTransactionEvent); + } + } + + public Runnable notifyPersistListeners() { + return new Runnable() { + public void run() { + localPersistListenersNotify(); + } + }; + } + + private void localPersistListenersNotify() { + if (persistBeanRequests != null) { + for (int i = 0; i < persistBeanRequests.size(); i++) { + persistBeanRequests.get(i).notifyLocalPersistListener(); + } + } + TransactionEventTable eventTables = event.getEventTables(); + if (eventTables != null && !eventTables.isEmpty()) { + BulkEventListenerMap map = manager.getBulkEventListenerMap(); + for (TableIUD tableIUD : eventTables.values()) { + map.process(tableIUD); + } + } + } + + private List> createPersistBeanRequests() { + TransactionEventBeans eventBeans = event.getEventBeans(); + if (eventBeans != null) { + return eventBeans.getRequests(); + } + return null; + } + + private BeanPersistIdMap createBeanPersistIdMap() { + + if (persistBeanRequests == null) { + return null; + } + + BeanPersistIdMap m = new BeanPersistIdMap(); + for (int i = 0; i < persistBeanRequests.size(); i++) { + persistBeanRequests.get(i).addToPersistMap(m); + } + return m; + } + + private RemoteTransactionEvent createRemoteTransactionEvent() { + + if (!clusterManager.isClustering()) { + return null; + } + + RemoteTransactionEvent remoteTransactionEvent = new RemoteTransactionEvent(serverName); + +// if (beanDeltaMap != null) { +// for (BeanDeltaList deltaList : beanDeltaMap.deltaLists()) { +// remoteTransactionEvent.addBeanDeltaList(deltaList); +// } +// } + + if (beanPersistIdMap != null) { + for (BeanPersistIds beanPersist : beanPersistIdMap.values()) { + remoteTransactionEvent.addBeanPersistIds(beanPersist); + } + } + + if (deleteByIdMap != null) { + remoteTransactionEvent.setDeleteByIdMap(deleteByIdMap); + } + + TransactionEventTable eventTables = event.getEventTables(); + if (eventTables != null && !eventTables.isEmpty()) { + for (TableIUD tableIUD : eventTables.values()) { + remoteTransactionEvent.addTableIUD(tableIUD); + } + } + + Set indexInvalidations = event.getIndexInvalidations(); + if (indexInvalidations != null) { + for (IndexInvalidate indexInvalidate : indexInvalidations) { + remoteTransactionEvent.addIndexInvalidate(indexInvalidate); + } + } + + return remoteTransactionEvent; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/RemoteTransactionEvent.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/RemoteTransactionEvent.java index 81e1ace8f..07f28cfaf 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/RemoteTransactionEvent.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/RemoteTransactionEvent.java @@ -1,200 +1,181 @@ -/** - * 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.transaction; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD; -import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; - -public class RemoteTransactionEvent implements Runnable { - - private List beanPersistList = new ArrayList(); - - private List tableList; - - private List beanDeltaLists; - - private BeanDeltaMap beanDeltaMap; - - private List indexEventList; - - private Set indexInvalidations; - - private DeleteByIdMap deleteByIdMap; - - private String serverName; - - private transient SpiEbeanServer server; - - public RemoteTransactionEvent(String serverName) { - this.serverName = serverName; - } - - public RemoteTransactionEvent(SpiEbeanServer server) { - this.server = server; - } - - public void run() { - server.remoteTransactionEvent(this); - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - if (beanDeltaMap != null){ - sb.append(beanDeltaMap); - } - sb.append(beanPersistList); - if (tableList != null){ - sb.append(tableList); - } - return sb.toString(); - } - - public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { - - if (indexInvalidations != null){ - for (IndexInvalidate indexInvalidate : indexInvalidations) { - indexInvalidate.writeBinaryMessage(msgList); - } - } - - if (tableList != null){ - for (int i = 0; i < tableList.size(); i++) { - tableList.get(i).writeBinaryMessage(msgList); - } - } - - if (deleteByIdMap != null){ - for (BeanPersistIds deleteIds : deleteByIdMap.values()) { - deleteIds.writeBinaryMessage(msgList); - } - } - - if (beanPersistList != null){ - for (int i = 0; i < beanPersistList.size(); i++) { - beanPersistList.get(i).writeBinaryMessage(msgList); - } - } - - if (beanDeltaLists != null){ - for (int i = 0; i < beanDeltaLists.size(); i++) { - beanDeltaLists.get(i).writeBinaryMessage(msgList); - } - } - - if (indexEventList != null){ - for (int i = 0; i < indexEventList.size(); i++) { - indexEventList.get(i).writeBinaryMessage(msgList); - } - } - } - - public boolean isEmpty() { - return beanPersistList.isEmpty() && (tableList == null || tableList.isEmpty()); - } - - public void addBeanPersistIds(BeanPersistIds beanPersist){ - beanPersistList.add(beanPersist); - } - - public void addIndexInvalidate(IndexInvalidate indexInvalidate){ - if (indexInvalidations == null){ - indexInvalidations = new HashSet(); - } - indexInvalidations.add(indexInvalidate); - } - - public void addTableIUD(TableIUD tableIud){ - if (tableList == null){ - tableList = new ArrayList(4); - } - tableList.add(tableIud); - } - - public void addBeanDeltaList(BeanDeltaList deltaList){ - if (beanDeltaLists == null){ - beanDeltaLists = new ArrayList(); - } - beanDeltaLists.add(deltaList); - } - - public void addBeanDelta(BeanDelta beanDelta){ - if (beanDeltaMap == null){ - beanDeltaMap = new BeanDeltaMap(); - } - beanDeltaMap.addBeanDelta(beanDelta); - } - - public void addIndexEvent(IndexEvent indexEvent){ - if (indexEventList == null){ - indexEventList = new ArrayList(2); - } - indexEventList.add(indexEvent); - } - - public String getServerName() { - return serverName; - } - - public SpiEbeanServer getServer() { - return server; - } - - public void setServer(SpiEbeanServer server) { - this.server = server; - } - - public DeleteByIdMap getDeleteByIdMap() { - return deleteByIdMap; - } - - public void setDeleteByIdMap(DeleteByIdMap deleteByIdMap) { - this.deleteByIdMap = deleteByIdMap; - } - - public Set getIndexInvalidations() { - return indexInvalidations; - } - - public List getIndexEventList() { - return indexEventList; - } - - public List getTableIUDList() { - return tableList; - } - - public List getBeanPersistList() { - return beanPersistList; - } - - public List getBeanDeltaLists() { - if (beanDeltaMap != null){ - beanDeltaLists.addAll(beanDeltaMap.deltaLists()); - } - return beanDeltaLists; - } -} +package com.avaje.ebeaninternal.server.transaction; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD; +import com.avaje.ebeaninternal.server.cluster.BinaryMessageList; + +public class RemoteTransactionEvent implements Runnable { + + private List beanPersistList = new ArrayList(); + + private List tableList; + + private List beanDeltaLists; + + private BeanDeltaMap beanDeltaMap; + + private List indexEventList; + + private Set indexInvalidations; + + private DeleteByIdMap deleteByIdMap; + + private String serverName; + + private transient SpiEbeanServer server; + + public RemoteTransactionEvent(String serverName) { + this.serverName = serverName; + } + + public RemoteTransactionEvent(SpiEbeanServer server) { + this.server = server; + } + + public void run() { + server.remoteTransactionEvent(this); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + if (beanDeltaMap != null){ + sb.append(beanDeltaMap); + } + sb.append(beanPersistList); + if (tableList != null){ + sb.append(tableList); + } + return sb.toString(); + } + + public void writeBinaryMessage(BinaryMessageList msgList) throws IOException { + + if (indexInvalidations != null){ + for (IndexInvalidate indexInvalidate : indexInvalidations) { + indexInvalidate.writeBinaryMessage(msgList); + } + } + + if (tableList != null){ + for (int i = 0; i < tableList.size(); i++) { + tableList.get(i).writeBinaryMessage(msgList); + } + } + + if (deleteByIdMap != null){ + for (BeanPersistIds deleteIds : deleteByIdMap.values()) { + deleteIds.writeBinaryMessage(msgList); + } + } + + if (beanPersistList != null){ + for (int i = 0; i < beanPersistList.size(); i++) { + beanPersistList.get(i).writeBinaryMessage(msgList); + } + } + + if (beanDeltaLists != null){ + for (int i = 0; i < beanDeltaLists.size(); i++) { + beanDeltaLists.get(i).writeBinaryMessage(msgList); + } + } + + if (indexEventList != null){ + for (int i = 0; i < indexEventList.size(); i++) { + indexEventList.get(i).writeBinaryMessage(msgList); + } + } + } + + public boolean isEmpty() { + return beanPersistList.isEmpty() && (tableList == null || tableList.isEmpty()); + } + + public void addBeanPersistIds(BeanPersistIds beanPersist){ + beanPersistList.add(beanPersist); + } + + public void addIndexInvalidate(IndexInvalidate indexInvalidate){ + if (indexInvalidations == null){ + indexInvalidations = new HashSet(); + } + indexInvalidations.add(indexInvalidate); + } + + public void addTableIUD(TableIUD tableIud){ + if (tableList == null){ + tableList = new ArrayList(4); + } + tableList.add(tableIud); + } + + public void addBeanDeltaList(BeanDeltaList deltaList){ + if (beanDeltaLists == null){ + beanDeltaLists = new ArrayList(); + } + beanDeltaLists.add(deltaList); + } + + public void addBeanDelta(BeanDelta beanDelta){ + if (beanDeltaMap == null){ + beanDeltaMap = new BeanDeltaMap(); + } + beanDeltaMap.addBeanDelta(beanDelta); + } + + public void addIndexEvent(IndexEvent indexEvent){ + if (indexEventList == null){ + indexEventList = new ArrayList(2); + } + indexEventList.add(indexEvent); + } + + public String getServerName() { + return serverName; + } + + public SpiEbeanServer getServer() { + return server; + } + + public void setServer(SpiEbeanServer server) { + this.server = server; + } + + public DeleteByIdMap getDeleteByIdMap() { + return deleteByIdMap; + } + + public void setDeleteByIdMap(DeleteByIdMap deleteByIdMap) { + this.deleteByIdMap = deleteByIdMap; + } + + public Set getIndexInvalidations() { + return indexInvalidations; + } + + public List getIndexEventList() { + return indexEventList; + } + + public List getTableIUDList() { + return tableList; + } + + public List getBeanPersistList() { + return beanPersistList; + } + + public List getBeanDeltaLists() { + if (beanDeltaMap != null){ + beanDeltaLists.addAll(beanDeltaMap.deltaLists()); + } + return beanDeltaLists; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogBuffer.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogBuffer.java index fd158dc0f..6a0abc570 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogBuffer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogBuffer.java @@ -1,121 +1,102 @@ -/** - * 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.transaction; - -import java.util.ArrayList; -import java.util.List; - -/** - * Buffer of transaction messages. - *

      - * For performance reasons we add all the transaction messages to an instance of - * TransactionLogBuffer and then when the buffer is full or the transaction ends - * we send the buffer to the transaction manager to log. - *

      - * - * @author rbygrave - * - */ -public class TransactionLogBuffer { - - private final String transactionId; - - private final ArrayList buffer; - - private final int maxSize; - - private int currentSize; - - /** - * Create the buffer with a maxSize and transaction id. - */ - public TransactionLogBuffer(int maxSize, String transactionId) { - this.maxSize = maxSize; - this.transactionId = transactionId; - this.buffer = new ArrayList(maxSize); - } - - /** - * Create new buffer using the same configuration. - */ - public TransactionLogBuffer newBuffer() { - return new TransactionLogBuffer(maxSize, transactionId); - } - - /** - * Return the transaction id. - */ - public String getTransactionId() { - return transactionId; - } - - /** - * Add a message to the buffer. - */ - public boolean add(String msg) { - buffer.add(new LogEntry(msg)); - return ++currentSize >= maxSize; - } - - /** - * Return true if the buffer is empty. - */ - public boolean isEmpty() { - return buffer.isEmpty(); - } - - /** - * Return all the messages. - */ - public List messages() { - return buffer; - } - - /** - * Entry in the buffer. - */ - public class LogEntry { - - private final long timestamp; - private final String msg; - - /** - * Construct an entry. - */ - public LogEntry(String msg) { - this.timestamp = System.currentTimeMillis(); - this.msg = msg; - } - - /** - * Return the time the entry was put into the buffer. - */ - public long getTimestamp() { - return timestamp; - } - - /** - * Return the text of the message. - */ - public String getMsg() { - return msg; - } - } -} +package com.avaje.ebeaninternal.server.transaction; + +import java.util.ArrayList; +import java.util.List; + +/** + * Buffer of transaction messages. + *

      + * For performance reasons we add all the transaction messages to an instance of + * TransactionLogBuffer and then when the buffer is full or the transaction ends + * we send the buffer to the transaction manager to log. + *

      + * + * @author rbygrave + * + */ +public class TransactionLogBuffer { + + private final String transactionId; + + private final ArrayList buffer; + + private final int maxSize; + + private int currentSize; + + /** + * Create the buffer with a maxSize and transaction id. + */ + public TransactionLogBuffer(int maxSize, String transactionId) { + this.maxSize = maxSize; + this.transactionId = transactionId; + this.buffer = new ArrayList(maxSize); + } + + /** + * Create new buffer using the same configuration. + */ + public TransactionLogBuffer newBuffer() { + return new TransactionLogBuffer(maxSize, transactionId); + } + + /** + * Return the transaction id. + */ + public String getTransactionId() { + return transactionId; + } + + /** + * Add a message to the buffer. + */ + public boolean add(String msg) { + buffer.add(new LogEntry(msg)); + return ++currentSize >= maxSize; + } + + /** + * Return true if the buffer is empty. + */ + public boolean isEmpty() { + return buffer.isEmpty(); + } + + /** + * Return all the messages. + */ + public List messages() { + return buffer; + } + + /** + * Entry in the buffer. + */ + public class LogEntry { + + private final long timestamp; + private final String msg; + + /** + * Construct an entry. + */ + public LogEntry(String msg) { + this.timestamp = System.currentTimeMillis(); + this.msg = msg; + } + + /** + * Return the time the entry was put into the buffer. + */ + public long getTimestamp() { + return timestamp; + } + + /** + * Return the text of the message. + */ + public String getMsg() { + return msg; + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogManager.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogManager.java index a9d423f7d..192c41fb0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogManager.java @@ -1,58 +1,39 @@ -/** - * 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.transaction; - -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebeaninternal.server.transaction.log.FileTransactionLoggerWrapper; -import com.avaje.ebeaninternal.server.transaction.log.JuliTransactionLogger; - -/** - * Manages the transaction logs. - */ -public class TransactionLogManager { - - private final TransactionLogWriter logWriter; - - /** - * Create the TransactionLogger. - *

      - * DevNote: This registers a shutdown hook to flush and close the - * sharedLogger. Alternate option would be to flush() the log after each - * write to the log. - *

      - */ - public TransactionLogManager(ServerConfig serverConfig) { - - if (serverConfig.isLoggingToJavaLogger()){ - logWriter = new JuliTransactionLogger(); - } else { - logWriter = new FileTransactionLoggerWrapper(serverConfig); - } - } - - public void shutdown() { - logWriter.shutdown(); - } - - public void log(TransactionLogBuffer logBuffer) { - logWriter.log(logBuffer); - } - -} +package com.avaje.ebeaninternal.server.transaction; + +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebeaninternal.server.transaction.log.FileTransactionLoggerWrapper; +import com.avaje.ebeaninternal.server.transaction.log.JuliTransactionLogger; + +/** + * Manages the transaction logs. + */ +public class TransactionLogManager { + + private final TransactionLogWriter logWriter; + + /** + * Create the TransactionLogger. + *

      + * DevNote: This registers a shutdown hook to flush and close the + * sharedLogger. Alternate option would be to flush() the log after each + * write to the log. + *

      + */ + public TransactionLogManager(ServerConfig serverConfig) { + + if (serverConfig.isLoggingToJavaLogger()){ + logWriter = new JuliTransactionLogger(); + } else { + logWriter = new FileTransactionLoggerWrapper(serverConfig); + } + } + + public void shutdown() { + logWriter.shutdown(); + } + + public void log(TransactionLogBuffer logBuffer) { + logWriter.log(logBuffer); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogWriter.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogWriter.java index 7690aaacf..9b1ec91e9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogWriter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionLogWriter.java @@ -1,37 +1,18 @@ -/** - * 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.transaction; - -/** - * Write transaction log events to a file or other destination. - */ -public interface TransactionLogWriter { - - /** - * Log all the messages in the buffer. - */ - public void log(TransactionLogBuffer logBuffer); - - /** - * Shutdown the writer. - */ - public void shutdown(); - -} +package com.avaje.ebeaninternal.server.transaction; + +/** + * Write transaction log events to a file or other destination. + */ +public interface TransactionLogWriter { + + /** + * Log all the messages in the buffer. + */ + public void log(TransactionLogBuffer logBuffer); + + /** + * Shutdown the writer. + */ + public void shutdown(); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java index d667e5443..1b9c48b74 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java @@ -1,560 +1,541 @@ -/** - * 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.transaction; - -import java.sql.Connection; -import java.sql.SQLException; -import java.util.List; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; -import javax.sql.DataSource; - -import com.avaje.ebean.BackgroundExecutor; -import com.avaje.ebean.LogLevel; -import com.avaje.ebean.TxIsolation; -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebean.event.TransactionEventListener; -import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.api.TransactionEvent; -import com.avaje.ebeaninternal.api.TransactionEventTable; -import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD; -import com.avaje.ebeaninternal.server.cluster.ClusterManager; -import com.avaje.ebeaninternal.server.core.BootupClasses; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; - -/** - * Manages transactions. - *

      - * Keeps the Cache, Cluster and Lucene indexes in synch when transactions are - * committed. - *

      - */ -public class TransactionManager { - - private static final Logger logger = Logger.getLogger(TransactionManager.class.getName()); - - /** - * The behaviour desired when ending a query only transaction. - */ - public enum OnQueryOnly { - - /** - * Rollback the transaction. - */ - ROLLBACK, - - /** - * Just close the transaction. - */ - CLOSE_ON_READCOMMITTED, - - /** - * Commit the transaction - */ - COMMIT - } - - private final BeanDescriptorManager beanDescriptorManager; - - private LogLevel logLevel; - - /** - * The logger. - */ - private final TransactionLogManager transLogger; - - /** - * Prefix for transaction id's (logging). - */ - private final String prefix; - - private final String externalTransPrefix; - - /** - * The dataSource of connections. - */ - private final DataSource dataSource; - - /** - * Flag to indicate the default Isolation is READ COMMITTED. This enables us - * to close queryOnly transactions rather than commit or rollback them. - */ - private final OnQueryOnly onQueryOnly; - - /** - * The default batchMode for transactions. - */ - private final boolean defaultBatchMode; - - private final BackgroundExecutor backgroundExecutor; - - private final ClusterManager clusterManager; - - private final int commitDebugLevel; - - private final String serverName; - - /** - * Id's for transaction logging. - */ - private AtomicLong transactionCounter = new AtomicLong(1000); - - private int clusterDebugLevel; - - private final BulkEventListenerMap bulkEventListenerMap; - - private TransactionEventListener[] transactionEventListeners; - - /** - * Create the TransactionManager - */ - public TransactionManager(ClusterManager clusterManager, BackgroundExecutor backgroundExecutor, ServerConfig config, - BeanDescriptorManager descMgr, BootupClasses bootupClasses) { - - this.beanDescriptorManager = descMgr; - this.clusterManager = clusterManager; - this.serverName = config.getName(); - - this.logLevel = config.getLoggingLevel(); - this.transLogger = new TransactionLogManager(config); - this.backgroundExecutor = backgroundExecutor; - this.dataSource = config.getDataSource(); - this.bulkEventListenerMap = new BulkEventListenerMap(config.getBulkTableEventListeners()); - - List transactionEventListeners = bootupClasses.getTransactionEventListeners(); - this.transactionEventListeners = transactionEventListeners.toArray(new TransactionEventListener[transactionEventListeners.size()]); - - // log some transaction events using a java util logger - this.commitDebugLevel = GlobalProperties.getInt("ebean.commit.debuglevel", 0); - this.clusterDebugLevel = GlobalProperties.getInt("ebean.cluster.debuglevel", 0); - - this.defaultBatchMode = config.isPersistBatching(); - - this.prefix = GlobalProperties.get("transaction.prefix", ""); - this.externalTransPrefix = GlobalProperties.get("transaction.prefix", "e"); - - String value = GlobalProperties.get("transaction.onqueryonly", "ROLLBACK").toUpperCase().trim(); - this.onQueryOnly = getOnQueryOnly(value, dataSource); - } - - public void shutdown() { - transLogger.shutdown(); - } - - public BeanDescriptorManager getBeanDescriptorManager() { - return beanDescriptorManager; - } - - public BulkEventListenerMap getBulkEventListenerMap() { - return bulkEventListenerMap; - } - - /** - * Return the logging level for transactions. - */ - public LogLevel getTransactionLogLevel(){ - return logLevel; - } - - /** - * Set the log level for transactions. - */ - public void setTransactionLogLevel(LogLevel logLevel){ - this.logLevel = logLevel; - } - - /** - * Return the behaviour to use when a query only transaction is committed. - *

      - * There is a potential optimisation available when read committed is the default - * isolation level. If it is, then Connections used only for queries do not require - * commit or rollback but instead can just be put back into the pool via close(). - *

      - *

      - * If the Isolation level is higher (say SERIALIZABLE) then Connections used - * just for queries do need to be committed or rollback after the query. - *

      - */ - private OnQueryOnly getOnQueryOnly(String onQueryOnly, DataSource ds) { - - - if (onQueryOnly.equals("COMMIT")){ - return OnQueryOnly.COMMIT; - } - if (onQueryOnly.startsWith("CLOSE")){ - if (!isReadCommitedIsolation(ds)){ - String m = "transaction.queryonlyclose is true but the transaction Isolation Level is not READ_COMMITTED"; - throw new PersistenceException(m); - } else { - return OnQueryOnly.CLOSE_ON_READCOMMITTED; - } - } - // default to rollback - return OnQueryOnly.ROLLBACK; - } - - /** - * Return true if the isolation level is read committed. - */ - private boolean isReadCommitedIsolation(DataSource ds) { - - Connection c = null; - try { - c = ds.getConnection(); - - int isolationLevel = c.getTransactionIsolation(); - return (isolationLevel == Connection.TRANSACTION_READ_COMMITTED); - - } catch (SQLException ex) { - String m = "Errored trying to determine the default Isolation Level"; - throw new PersistenceException(m, ex); - - } finally { - try { - if (c != null) { - c.close(); - } - } catch (SQLException ex) { - logger.log(Level.SEVERE, "closing connection", ex); - } - } - } - - public String getServerName() { - return serverName; - } - - public DataSource getDataSource() { - return dataSource; - } - - /** - * Return the cluster debug level. - */ - public int getClusterDebugLevel() { - return clusterDebugLevel; - } - - /** - * Set the cluster debug level. - */ - public void setClusterDebugLevel(int clusterDebugLevel) { - this.clusterDebugLevel = clusterDebugLevel; - } - - /** - * Defines the type of behaviour to use when closing a transaction that was used to query data only. - */ - public OnQueryOnly getOnQueryOnly() { - return onQueryOnly; - } - - /** - * Return the TransactionLogger used by this TransactionManager. - */ - public TransactionLogManager getLogger() { - return transLogger; - } - - public void log(TransactionLogBuffer logBuffer){ - if (!logBuffer.isEmpty()){ - transLogger.log(logBuffer); - } - } - - /** - * Wrap the externally supplied Connection. - */ - public SpiTransaction wrapExternalConnection(Connection c) { - - return wrapExternalConnection(externalTransPrefix + c.hashCode(), c); - } - - /** - * Wrap an externally supplied Connection with a known transaction id. - */ - public SpiTransaction wrapExternalConnection(String id, Connection c) { - - ExternalJdbcTransaction t = new ExternalJdbcTransaction(id, true, logLevel, c, this); - - // set the default batch mode. This can be on for - // jdbc drivers that support getGeneratedKeys - if (defaultBatchMode){ - t.setBatchMode(true); - } - - return t; - } - - /** - * Create a new Transaction. - */ - public SpiTransaction createTransaction(boolean explicit, int isolationLevel) { - Connection c = null; - try { - c = dataSource.getConnection(); - long id = transactionCounter.incrementAndGet(); - - JdbcTransaction t = new JdbcTransaction(prefix + id, explicit, logLevel, c, this); - - // set the default batch mode. This can be on for - // jdbc drivers that support getGeneratedKeys - if (defaultBatchMode){ - t.setBatchMode(true); - } - if (isolationLevel > -1) { - c.setTransactionIsolation(isolationLevel); - } - - if (commitDebugLevel >= 3){ - String msg = "Transaction ["+t.getId()+"] begin"; - if (isolationLevel > -1){ - TxIsolation txi = TxIsolation.fromLevel(isolationLevel); - msg += " isolationLevel["+txi+"]"; - } - logger.info(msg); - } - - return t; - - } catch (SQLException ex) { - // close connection on failed creation - try { - if (c != null){ - c.close(); - } - } catch (SQLException e) { - logger.log(Level.SEVERE,"Error closing failed connection", e); - } - throw new PersistenceException(ex); - } - } - - public SpiTransaction createQueryTransaction() { - Connection c = null; - try { - c = dataSource.getConnection(); - long id = transactionCounter.incrementAndGet(); - - JdbcTransaction t = new JdbcTransaction(prefix + id, false, logLevel, c, this); - - // set the default batch mode. Can be true for - // jdbc drivers that support getGeneratedKeys - if (defaultBatchMode){ - t.setBatchMode(true); - } - - if (commitDebugLevel >= 3){ - logger.info("Transaction ["+t.getId()+"] begin - queryOnly"); - } - - return t; - - } catch (PersistenceException ex) { - // close the connection and re-throw the exception - try { - if (c != null) { - c.close(); - } - } catch (SQLException e) { - logger.log(Level.SEVERE,"Error closing failed connection", e); - } - throw ex; - - } catch (SQLException ex) { - // don't need to close connection in this case - throw new PersistenceException(ex); - } - } - - /** - * Process a local rolled back transaction. - */ - public void notifyOfRollback(SpiTransaction transaction, Throwable cause) { - - try { - for (TransactionEventListener listener : transactionEventListeners) { - listener.postTransactionRollback(transaction, cause); - } - - if (transaction.isLogSummary() || commitDebugLevel >= 1) { - String msg = "Rollback"; - if (cause != null){ - msg += " error: "+formatThrowable(cause); - } - if (transaction.isLogSummary()) { - transaction.logInternal(msg); - } - - if (commitDebugLevel >= 1){ - logger.info("Transaction ["+transaction.getId()+"] "+msg); - } - } - - log(transaction.getLogBuffer()); - } catch (Exception ex) { - String m = "Potentially Transaction Log incomplete due to error:"; - logger.log(Level.SEVERE, m, ex); - } - } - - /** - * Query only transaction in read committed isolation. - */ - public void notifyOfQueryOnly(boolean onCommit, SpiTransaction transaction, Throwable cause) { - - try { - if (commitDebugLevel >= 2){ - String msg; - if (onCommit){ - msg = "Commit queryOnly"; - - } else { - msg = "Rollback queryOnly"; - if (cause != null){ - msg += " error: "+formatThrowable(cause); - } - } - if (transaction.isLogSummary()) { - transaction.logInternal(msg); - } - logger.info("Transaction ["+transaction.getId()+"] "+msg); - } - - log(transaction.getLogBuffer()); - - } catch (Exception ex) { - String m = "Potentially Transaction Log incomplete due to error:"; - logger.log(Level.SEVERE, m, ex); - } - } - - private String formatThrowable(Throwable e){ - if (e == null){ - return ""; - } - StringBuilder sb = new StringBuilder(); - formatThrowable(e, sb); - return sb.toString(); - } - - private void formatThrowable(Throwable e, StringBuilder sb){ - - sb.append(e.toString()); - StackTraceElement[] stackTrace = e.getStackTrace(); - if (stackTrace.length > 0){ - sb.append(" stack0: "); - sb.append(stackTrace[0]); - } - Throwable cause = e.getCause(); - if (cause != null){ - sb.append(" cause: "); - formatThrowable(cause, sb); - } - } - - /** - * Process a local committed transaction. - */ - public void notifyOfCommit(SpiTransaction transaction) { - - try { - - log(transaction.getLogBuffer()); - - PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, transaction, - transaction.getEvent()); - - postCommit.notifyLocalCacheIndex(); - postCommit.notifyCluster(); - - // cluster and text indexing - backgroundExecutor.execute(postCommit.notifyPersistListeners()); - - for (TransactionEventListener listener : transactionEventListeners) { - listener.postTransactionCommit(transaction); - } - - if (commitDebugLevel >= 1) { - logger.info("Transaction ["+transaction.getId()+"] commit"); - } - } catch (Exception ex) { - String m = "NotifyOfCommit failed. Cache/Lucene potentially not notified."; - logger.log(Level.SEVERE, m, ex); - } - } - - - - - /** - * Process a Transaction that comes from another framework or local code. - *

      - * For cases where raw SQL/JDBC or other frameworks are used this can - * invalidate the appropriate parts of the cache. - *

      - */ - public void externalModification(TransactionEventTable tableEvents) { - - TransactionEvent event = new TransactionEvent(); - event.add(tableEvents); - - PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, null, event); - - // invalidate parts of local cache and index - postCommit.notifyLocalCacheIndex(); - - backgroundExecutor.execute(postCommit.notifyPersistListeners()); - } - - - /** - * Notify local BeanPersistListeners etc of events from another server in the cluster. - */ - public void remoteTransactionEvent(RemoteTransactionEvent remoteEvent) { - - if (clusterDebugLevel > 0 || logger.isLoggable(Level.FINE)){ - logger.info("Cluster Received: "+remoteEvent.toString()); - } - - List tableIUDList = remoteEvent.getTableIUDList(); - if (tableIUDList != null){ - for (int i = 0; i < tableIUDList.size(); i++) { - TableIUD tableIUD = tableIUDList.get(i); - beanDescriptorManager.cacheNotify(tableIUD); - } - } - - List beanPersistList = remoteEvent.getBeanPersistList(); - if (beanPersistList != null){ - for (int i = 0; i < beanPersistList.size(); i++) { - BeanPersistIds beanPersist = beanPersistList.get(i); - beanPersist.notifyCacheAndListener(); - } - } - - } - - -} +package com.avaje.ebeaninternal.server.transaction; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; +import javax.sql.DataSource; + +import com.avaje.ebean.BackgroundExecutor; +import com.avaje.ebean.LogLevel; +import com.avaje.ebean.TxIsolation; +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.event.TransactionEventListener; +import com.avaje.ebeaninternal.api.SpiTransaction; +import com.avaje.ebeaninternal.api.TransactionEvent; +import com.avaje.ebeaninternal.api.TransactionEventTable; +import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD; +import com.avaje.ebeaninternal.server.cluster.ClusterManager; +import com.avaje.ebeaninternal.server.core.BootupClasses; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; + +/** + * Manages transactions. + *

      + * Keeps the Cache, Cluster and Lucene indexes in synch when transactions are + * committed. + *

      + */ +public class TransactionManager { + + private static final Logger logger = Logger.getLogger(TransactionManager.class.getName()); + + /** + * The behaviour desired when ending a query only transaction. + */ + public enum OnQueryOnly { + + /** + * Rollback the transaction. + */ + ROLLBACK, + + /** + * Just close the transaction. + */ + CLOSE_ON_READCOMMITTED, + + /** + * Commit the transaction + */ + COMMIT + } + + private final BeanDescriptorManager beanDescriptorManager; + + private LogLevel logLevel; + + /** + * The logger. + */ + private final TransactionLogManager transLogger; + + /** + * Prefix for transaction id's (logging). + */ + private final String prefix; + + private final String externalTransPrefix; + + /** + * The dataSource of connections. + */ + private final DataSource dataSource; + + /** + * Flag to indicate the default Isolation is READ COMMITTED. This enables us + * to close queryOnly transactions rather than commit or rollback them. + */ + private final OnQueryOnly onQueryOnly; + + /** + * The default batchMode for transactions. + */ + private final boolean defaultBatchMode; + + private final BackgroundExecutor backgroundExecutor; + + private final ClusterManager clusterManager; + + private final int commitDebugLevel; + + private final String serverName; + + /** + * Id's for transaction logging. + */ + private AtomicLong transactionCounter = new AtomicLong(1000); + + private int clusterDebugLevel; + + private final BulkEventListenerMap bulkEventListenerMap; + + private TransactionEventListener[] transactionEventListeners; + + /** + * Create the TransactionManager + */ + public TransactionManager(ClusterManager clusterManager, BackgroundExecutor backgroundExecutor, ServerConfig config, + BeanDescriptorManager descMgr, BootupClasses bootupClasses) { + + this.beanDescriptorManager = descMgr; + this.clusterManager = clusterManager; + this.serverName = config.getName(); + + this.logLevel = config.getLoggingLevel(); + this.transLogger = new TransactionLogManager(config); + this.backgroundExecutor = backgroundExecutor; + this.dataSource = config.getDataSource(); + this.bulkEventListenerMap = new BulkEventListenerMap(config.getBulkTableEventListeners()); + + List transactionEventListeners = bootupClasses.getTransactionEventListeners(); + this.transactionEventListeners = transactionEventListeners.toArray(new TransactionEventListener[transactionEventListeners.size()]); + + // log some transaction events using a java util logger + this.commitDebugLevel = GlobalProperties.getInt("ebean.commit.debuglevel", 0); + this.clusterDebugLevel = GlobalProperties.getInt("ebean.cluster.debuglevel", 0); + + this.defaultBatchMode = config.isPersistBatching(); + + this.prefix = GlobalProperties.get("transaction.prefix", ""); + this.externalTransPrefix = GlobalProperties.get("transaction.prefix", "e"); + + String value = GlobalProperties.get("transaction.onqueryonly", "ROLLBACK").toUpperCase().trim(); + this.onQueryOnly = getOnQueryOnly(value, dataSource); + } + + public void shutdown() { + transLogger.shutdown(); + } + + public BeanDescriptorManager getBeanDescriptorManager() { + return beanDescriptorManager; + } + + public BulkEventListenerMap getBulkEventListenerMap() { + return bulkEventListenerMap; + } + + /** + * Return the logging level for transactions. + */ + public LogLevel getTransactionLogLevel(){ + return logLevel; + } + + /** + * Set the log level for transactions. + */ + public void setTransactionLogLevel(LogLevel logLevel){ + this.logLevel = logLevel; + } + + /** + * Return the behaviour to use when a query only transaction is committed. + *

      + * There is a potential optimisation available when read committed is the default + * isolation level. If it is, then Connections used only for queries do not require + * commit or rollback but instead can just be put back into the pool via close(). + *

      + *

      + * If the Isolation level is higher (say SERIALIZABLE) then Connections used + * just for queries do need to be committed or rollback after the query. + *

      + */ + private OnQueryOnly getOnQueryOnly(String onQueryOnly, DataSource ds) { + + + if (onQueryOnly.equals("COMMIT")){ + return OnQueryOnly.COMMIT; + } + if (onQueryOnly.startsWith("CLOSE")){ + if (!isReadCommitedIsolation(ds)){ + String m = "transaction.queryonlyclose is true but the transaction Isolation Level is not READ_COMMITTED"; + throw new PersistenceException(m); + } else { + return OnQueryOnly.CLOSE_ON_READCOMMITTED; + } + } + // default to rollback + return OnQueryOnly.ROLLBACK; + } + + /** + * Return true if the isolation level is read committed. + */ + private boolean isReadCommitedIsolation(DataSource ds) { + + Connection c = null; + try { + c = ds.getConnection(); + + int isolationLevel = c.getTransactionIsolation(); + return (isolationLevel == Connection.TRANSACTION_READ_COMMITTED); + + } catch (SQLException ex) { + String m = "Errored trying to determine the default Isolation Level"; + throw new PersistenceException(m, ex); + + } finally { + try { + if (c != null) { + c.close(); + } + } catch (SQLException ex) { + logger.log(Level.SEVERE, "closing connection", ex); + } + } + } + + public String getServerName() { + return serverName; + } + + public DataSource getDataSource() { + return dataSource; + } + + /** + * Return the cluster debug level. + */ + public int getClusterDebugLevel() { + return clusterDebugLevel; + } + + /** + * Set the cluster debug level. + */ + public void setClusterDebugLevel(int clusterDebugLevel) { + this.clusterDebugLevel = clusterDebugLevel; + } + + /** + * Defines the type of behaviour to use when closing a transaction that was used to query data only. + */ + public OnQueryOnly getOnQueryOnly() { + return onQueryOnly; + } + + /** + * Return the TransactionLogger used by this TransactionManager. + */ + public TransactionLogManager getLogger() { + return transLogger; + } + + public void log(TransactionLogBuffer logBuffer){ + if (!logBuffer.isEmpty()){ + transLogger.log(logBuffer); + } + } + + /** + * Wrap the externally supplied Connection. + */ + public SpiTransaction wrapExternalConnection(Connection c) { + + return wrapExternalConnection(externalTransPrefix + c.hashCode(), c); + } + + /** + * Wrap an externally supplied Connection with a known transaction id. + */ + public SpiTransaction wrapExternalConnection(String id, Connection c) { + + ExternalJdbcTransaction t = new ExternalJdbcTransaction(id, true, logLevel, c, this); + + // set the default batch mode. This can be on for + // jdbc drivers that support getGeneratedKeys + if (defaultBatchMode){ + t.setBatchMode(true); + } + + return t; + } + + /** + * Create a new Transaction. + */ + public SpiTransaction createTransaction(boolean explicit, int isolationLevel) { + Connection c = null; + try { + c = dataSource.getConnection(); + long id = transactionCounter.incrementAndGet(); + + JdbcTransaction t = new JdbcTransaction(prefix + id, explicit, logLevel, c, this); + + // set the default batch mode. This can be on for + // jdbc drivers that support getGeneratedKeys + if (defaultBatchMode){ + t.setBatchMode(true); + } + if (isolationLevel > -1) { + c.setTransactionIsolation(isolationLevel); + } + + if (commitDebugLevel >= 3){ + String msg = "Transaction ["+t.getId()+"] begin"; + if (isolationLevel > -1){ + TxIsolation txi = TxIsolation.fromLevel(isolationLevel); + msg += " isolationLevel["+txi+"]"; + } + logger.info(msg); + } + + return t; + + } catch (SQLException ex) { + // close connection on failed creation + try { + if (c != null){ + c.close(); + } + } catch (SQLException e) { + logger.log(Level.SEVERE,"Error closing failed connection", e); + } + throw new PersistenceException(ex); + } + } + + public SpiTransaction createQueryTransaction() { + Connection c = null; + try { + c = dataSource.getConnection(); + long id = transactionCounter.incrementAndGet(); + + JdbcTransaction t = new JdbcTransaction(prefix + id, false, logLevel, c, this); + + // set the default batch mode. Can be true for + // jdbc drivers that support getGeneratedKeys + if (defaultBatchMode){ + t.setBatchMode(true); + } + + if (commitDebugLevel >= 3){ + logger.info("Transaction ["+t.getId()+"] begin - queryOnly"); + } + + return t; + + } catch (PersistenceException ex) { + // close the connection and re-throw the exception + try { + if (c != null) { + c.close(); + } + } catch (SQLException e) { + logger.log(Level.SEVERE,"Error closing failed connection", e); + } + throw ex; + + } catch (SQLException ex) { + // don't need to close connection in this case + throw new PersistenceException(ex); + } + } + + /** + * Process a local rolled back transaction. + */ + public void notifyOfRollback(SpiTransaction transaction, Throwable cause) { + + try { + for (TransactionEventListener listener : transactionEventListeners) { + listener.postTransactionRollback(transaction, cause); + } + + if (transaction.isLogSummary() || commitDebugLevel >= 1) { + String msg = "Rollback"; + if (cause != null){ + msg += " error: "+formatThrowable(cause); + } + if (transaction.isLogSummary()) { + transaction.logInternal(msg); + } + + if (commitDebugLevel >= 1){ + logger.info("Transaction ["+transaction.getId()+"] "+msg); + } + } + + log(transaction.getLogBuffer()); + } catch (Exception ex) { + String m = "Potentially Transaction Log incomplete due to error:"; + logger.log(Level.SEVERE, m, ex); + } + } + + /** + * Query only transaction in read committed isolation. + */ + public void notifyOfQueryOnly(boolean onCommit, SpiTransaction transaction, Throwable cause) { + + try { + if (commitDebugLevel >= 2){ + String msg; + if (onCommit){ + msg = "Commit queryOnly"; + + } else { + msg = "Rollback queryOnly"; + if (cause != null){ + msg += " error: "+formatThrowable(cause); + } + } + if (transaction.isLogSummary()) { + transaction.logInternal(msg); + } + logger.info("Transaction ["+transaction.getId()+"] "+msg); + } + + log(transaction.getLogBuffer()); + + } catch (Exception ex) { + String m = "Potentially Transaction Log incomplete due to error:"; + logger.log(Level.SEVERE, m, ex); + } + } + + private String formatThrowable(Throwable e){ + if (e == null){ + return ""; + } + StringBuilder sb = new StringBuilder(); + formatThrowable(e, sb); + return sb.toString(); + } + + private void formatThrowable(Throwable e, StringBuilder sb){ + + sb.append(e.toString()); + StackTraceElement[] stackTrace = e.getStackTrace(); + if (stackTrace.length > 0){ + sb.append(" stack0: "); + sb.append(stackTrace[0]); + } + Throwable cause = e.getCause(); + if (cause != null){ + sb.append(" cause: "); + formatThrowable(cause, sb); + } + } + + /** + * Process a local committed transaction. + */ + public void notifyOfCommit(SpiTransaction transaction) { + + try { + + log(transaction.getLogBuffer()); + + PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, transaction, + transaction.getEvent()); + + postCommit.notifyLocalCacheIndex(); + postCommit.notifyCluster(); + + // cluster and text indexing + backgroundExecutor.execute(postCommit.notifyPersistListeners()); + + for (TransactionEventListener listener : transactionEventListeners) { + listener.postTransactionCommit(transaction); + } + + if (commitDebugLevel >= 1) { + logger.info("Transaction ["+transaction.getId()+"] commit"); + } + } catch (Exception ex) { + String m = "NotifyOfCommit failed. Cache/Lucene potentially not notified."; + logger.log(Level.SEVERE, m, ex); + } + } + + + + + /** + * Process a Transaction that comes from another framework or local code. + *

      + * For cases where raw SQL/JDBC or other frameworks are used this can + * invalidate the appropriate parts of the cache. + *

      + */ + public void externalModification(TransactionEventTable tableEvents) { + + TransactionEvent event = new TransactionEvent(); + event.add(tableEvents); + + PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, null, event); + + // invalidate parts of local cache and index + postCommit.notifyLocalCacheIndex(); + + backgroundExecutor.execute(postCommit.notifyPersistListeners()); + } + + + /** + * Notify local BeanPersistListeners etc of events from another server in the cluster. + */ + public void remoteTransactionEvent(RemoteTransactionEvent remoteEvent) { + + if (clusterDebugLevel > 0 || logger.isLoggable(Level.FINE)){ + logger.info("Cluster Received: "+remoteEvent.toString()); + } + + List tableIUDList = remoteEvent.getTableIUDList(); + if (tableIUDList != null){ + for (int i = 0; i < tableIUDList.size(); i++) { + TableIUD tableIUD = tableIUDList.get(i); + beanDescriptorManager.cacheNotify(tableIUD); + } + } + + List beanPersistList = remoteEvent.getBeanPersistList(); + if (beanPersistList != null){ + for (int i = 0; i < beanPersistList.size(); i++) { + BeanPersistIds beanPersist = beanPersistList.get(i); + beanPersist.notifyCacheAndListener(); + } + } + + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionMap.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionMap.java index d579f69ec..053c14162 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionMap.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionMap.java @@ -1,137 +1,118 @@ -/** - * 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.transaction; - -import java.util.HashMap; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.api.SpiTransaction; - - -/** - * Current transactions mapped by server name. - */ -public class TransactionMap { - - /** - * Map of State by serverName. - */ - private HashMap map = new HashMap(); - - public String toString() { - return map.toString(); - } - - public boolean isEmpty() { - return map.isEmpty(); - } - - /** - * Return the State for a given serverName. - */ - public State getState(String serverName) { - - return map.get(serverName); - } - - /** - * Return the State for a given serverName. - */ - public State getStateWithCreate(String serverName) { - - State state = map.get(serverName); - if (state == null){ - state = new State(); - map.put(serverName, state); - } - return state; - } - - /** - * Remove and return the State for a given serverName. - */ - public State removeState(String serverName) { - return map.remove(serverName); - } - - /** - * The transaction and whether it is active. - */ - public static class State { - - SpiTransaction transaction; - - public String toString() { - return "txn["+transaction+"]"; - } - - public SpiTransaction get() { - return transaction; - } - - /** - * Set the transaction. This will now be the current transaction. - */ - public void set(SpiTransaction trans) { - - if (transaction != null && transaction.isActive()){ - String m = "The existing transaction is still active?"; - throw new PersistenceException(m); - } - transaction = trans; - } - - - /** - * Commit the transaction. - */ - public void commit() { - transaction.commit(); - transaction = null; - } - - /** - * Rollback the transaction. - */ - public void rollback() { - transaction.rollback(); - transaction = null; - } - - /** - * End the transaction. - */ - public void end() { - if (transaction != null){ - transaction.end(); - transaction = null; - } - } - - /** - * Used to replace transaction with a proxy. - */ - public void replace(SpiTransaction trans) { - transaction = trans; - } - - } -} +package com.avaje.ebeaninternal.server.transaction; + +import java.util.HashMap; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.api.SpiTransaction; + + +/** + * Current transactions mapped by server name. + */ +public class TransactionMap { + + /** + * Map of State by serverName. + */ + private HashMap map = new HashMap(); + + public String toString() { + return map.toString(); + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * Return the State for a given serverName. + */ + public State getState(String serverName) { + + return map.get(serverName); + } + + /** + * Return the State for a given serverName. + */ + public State getStateWithCreate(String serverName) { + + State state = map.get(serverName); + if (state == null){ + state = new State(); + map.put(serverName, state); + } + return state; + } + + /** + * Remove and return the State for a given serverName. + */ + public State removeState(String serverName) { + return map.remove(serverName); + } + + /** + * The transaction and whether it is active. + */ + public static class State { + + SpiTransaction transaction; + + public String toString() { + return "txn["+transaction+"]"; + } + + public SpiTransaction get() { + return transaction; + } + + /** + * Set the transaction. This will now be the current transaction. + */ + public void set(SpiTransaction trans) { + + if (transaction != null && transaction.isActive()){ + String m = "The existing transaction is still active?"; + throw new PersistenceException(m); + } + transaction = trans; + } + + + /** + * Commit the transaction. + */ + public void commit() { + transaction.commit(); + transaction = null; + } + + /** + * Rollback the transaction. + */ + public void rollback() { + transaction.rollback(); + transaction = null; + } + + /** + * End the transaction. + */ + public void end() { + if (transaction != null){ + transaction.end(); + transaction = null; + } + } + + /** + * Used to replace transaction with a proxy. + */ + public void replace(SpiTransaction trans) { + transaction = trans; + } + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/WeakValueMap.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/WeakValueMap.java index 723de3632..51d50bcbf 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/WeakValueMap.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/WeakValueMap.java @@ -1,152 +1,133 @@ -/** - * 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.transaction; - -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; -import java.util.HashMap; -import java.util.Map; - -/** - * A Weak value map designed for use with DefaultPersistenceContext. - *

      - * This provides the mechanism where entries in the persistence context will be - * automatically removed when they are not referenced externally. - *

      - * - * @author mario, rbygrave - */ -public class WeakValueMap { - - protected final ReferenceQueue refQueue = new ReferenceQueue(); - - /** - * Backing map. - */ - private final Map> backing; - - /** - * Hold the key with the value for expunge purposes. - */ - private static class WeakReferenceWithKey extends WeakReference { - - private final K key; - - public WeakReferenceWithKey(K key, V referent, ReferenceQueue q) { - super(referent, q); - this.key = key; - } - - public K getKey() { - return key; - } - } - - public WeakValueMap() { - this.backing = new HashMap>(); - } - - private WeakReferenceWithKey createReference(K key, V value) { - return new WeakReferenceWithKey(key, value, refQueue); - } - - @SuppressWarnings({ "rawtypes" }) - private void expunge() { - - Reference ref; - - while ((ref = refQueue.poll()) != null) { - backing.remove(((WeakReferenceWithKey) ref).getKey()); - } - } - - /** - * Put the key value pair if there is not already a matching entry. If there - * is an existing entry then return that instead. - */ - public Object putIfAbsent(K key, V value) { - expunge(); - - Reference ref = backing.get(key); - if (ref != null) { - V existingValue = ref.get(); - if (existingValue != null) { - // it is not absent - return existingValue; - } - } - // put the new value and return null - // indicating the put was successful - backing.put(key, createReference(key, value)); - return null; - } - - public void put(K key, V value) { - expunge(); - - backing.put(key, createReference(key, value)); - } - - public V get(K key) { - expunge(); - - Reference v = backing.get(key); - return v == null ? null : v.get(); - } - - public int size() { - expunge(); - - return backing.size(); - } - - public boolean isEmpty() { - expunge(); - - return backing.isEmpty(); - } - - public boolean containsKey(Object key) { - expunge(); - - return backing.containsKey(key); - } - - public V remove(K key) { - expunge(); - - Reference v = backing.remove(key); - return v == null ? null : v.get(); - } - - public void clear() { - expunge(); - backing.clear(); - expunge(); - } - - public String toString() { - expunge(); - - return backing.toString(); - } - -} +package com.avaje.ebeaninternal.server.transaction; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Map; + +/** + * A Weak value map designed for use with DefaultPersistenceContext. + *

      + * This provides the mechanism where entries in the persistence context will be + * automatically removed when they are not referenced externally. + *

      + * + * @author mario, rbygrave + */ +public class WeakValueMap { + + protected final ReferenceQueue refQueue = new ReferenceQueue(); + + /** + * Backing map. + */ + private final Map> backing; + + /** + * Hold the key with the value for expunge purposes. + */ + private static class WeakReferenceWithKey extends WeakReference { + + private final K key; + + public WeakReferenceWithKey(K key, V referent, ReferenceQueue q) { + super(referent, q); + this.key = key; + } + + public K getKey() { + return key; + } + } + + public WeakValueMap() { + this.backing = new HashMap>(); + } + + private WeakReferenceWithKey createReference(K key, V value) { + return new WeakReferenceWithKey(key, value, refQueue); + } + + @SuppressWarnings({ "rawtypes" }) + private void expunge() { + + Reference ref; + + while ((ref = refQueue.poll()) != null) { + backing.remove(((WeakReferenceWithKey) ref).getKey()); + } + } + + /** + * Put the key value pair if there is not already a matching entry. If there + * is an existing entry then return that instead. + */ + public Object putIfAbsent(K key, V value) { + expunge(); + + Reference ref = backing.get(key); + if (ref != null) { + V existingValue = ref.get(); + if (existingValue != null) { + // it is not absent + return existingValue; + } + } + // put the new value and return null + // indicating the put was successful + backing.put(key, createReference(key, value)); + return null; + } + + public void put(K key, V value) { + expunge(); + + backing.put(key, createReference(key, value)); + } + + public V get(K key) { + expunge(); + + Reference v = backing.get(key); + return v == null ? null : v.get(); + } + + public int size() { + expunge(); + + return backing.size(); + } + + public boolean isEmpty() { + expunge(); + + return backing.isEmpty(); + } + + public boolean containsKey(Object key) { + expunge(); + + return backing.containsKey(key); + } + + public V remove(K key) { + expunge(); + + Reference v = backing.remove(key); + return v == null ? null : v.get(); + } + + public void clear() { + expunge(); + backing.clear(); + expunge(); + } + + public String toString() { + expunge(); + + return backing.toString(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/FileTransactionLogger.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/FileTransactionLogger.java index f97129d34..2d0bb5a8a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/FileTransactionLogger.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/FileTransactionLogger.java @@ -1,401 +1,382 @@ -/** - * Copyright (C) 2010 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.transaction.log; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.util.List; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; - -import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer; -import com.avaje.ebeaninternal.server.transaction.TransactionLogWriter; -import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer.LogEntry; - -/** - * Default transaction logger implementation. - *

      - * File based logger that can switch daily. It will include the date in the - * files name. - *

      - *

      - * Administration Note: If log file switching fails it will send the error to - * standard out and standard err print streams. This is assumed to be rather - * unlikely but possible. - *

      - */ -public class FileTransactionLogger implements Runnable, TransactionLogWriter { - - private static final Logger logger = Logger.getLogger(FileTransactionLogger.class.getName()); - - /** - * Used to print stack trace. - */ - private static final String atString = " at "; - - /** - * The newLineChar used instead of NL or CRNL for printing stack traces. - */ - private final String newLinePlaceholder = "\\r\\n"; - - /** - * The maximum number of stack lines to output. This is just for transaction - * logging so 5 is fine. - */ - private final int maxStackTraceLines = 5; - - /** - * Queue that transactions put their LogBuffers onto and this LogBufferWriter - * pulls LogBuffers from. - */ - private final ConcurrentLinkedQueue logBufferQueue = new ConcurrentLinkedQueue(); - - private final Object queueMonitor = new Object(); - - /** - * Thread that is the sole writer of logBuffer entries to the file. - */ - private final Thread logWriterThread; - - private final String threadName; - - /** - * The path of the log file. - */ - private final String filepath; - - /** - * The delimiter to use. - */ - private final String deliminator = ", "; - - /** - * The prefix of the log file name. - */ - private final String logFileName; - - /** - * The file suffix for the logs. - */ - private final String logFileSuffix; - - /** - * Shutdown flag. - */ - private volatile boolean shutdown; - private volatile boolean shutdownComplete; - - /** - * The output stream. - */ - private PrintStream out; - - /** - * The current file path. - */ - private String currentPath; - - /** - * Counter thats incremented when maxBytesPerFile is hit. - */ - private int fileCounter; - - /** - * Roughly the max bytes written to a file before we switch. Switch Daily and - * on hitting max bytes. - */ - private long maxBytesPerFile; - - /** - * Roughly the bytes written to a file. - */ - private long bytesWritten; - - public FileTransactionLogger(String threadName, String dir, String logFileName, int maxBytesPerFile) { - this(threadName, dir, logFileName, "log", maxBytesPerFile); - } - - public FileTransactionLogger(String threadName, String dir, String logFileName, String suffix, int maxBytesPerFile) { - this.threadName = threadName; - this.logFileName = logFileName; - this.logFileSuffix = "." + suffix; - this.maxBytesPerFile = maxBytesPerFile; - - try { - // get the directory where the log files are going to go - filepath = makeDirIfRequired(dir); - - switchFile(LogTime.nextDay()); - - } catch (Exception e) { - System.out.println("FATAL ERROR: init of FileLogger: " + e.getMessage()); - System.err.println("FATAL ERROR: init of FileLogger: " + e.getMessage()); - throw new RuntimeException(e); - } - - logWriterThread = new Thread(this, threadName); - logWriterThread.setDaemon(true); - } - - protected void finalize() throws Throwable { - close(); - super.finalize(); - } - - public void start() { - logWriterThread.start(); - } - - public void shutdown() { - - shutdown = true; - - synchronized (logWriterThread) { - try { - // wait max 20 seconds - logWriterThread.wait(20000); - logger.fine("Shutdown LogBufferWriter " + threadName + " shutdownComplete:" + shutdownComplete); - - } catch (InterruptedException e) { - logger.fine("InterruptedException:" + e); - } - } - - if (!shutdownComplete) { - String m = "WARNING: Shutdown of LogBufferWriter " + threadName + " not completed."; - System.err.println(m); - logger.warning(m); - } - - } - - public void run() { - - int missCount = 0; - - while (!shutdown || missCount < 10) { - if (missCount > 50) { - - if (out != null) { - out.flush(); - } - try { - Thread.sleep(20); - } catch (InterruptedException e) { - logger.log(Level.INFO, "Interrupted TxnLogBufferWriter", e); - } - } - synchronized (queueMonitor) { - if (logBufferQueue.isEmpty()) { - ++missCount; - } else { - TransactionLogBuffer buffer = logBufferQueue.remove(); - write(buffer); - missCount = 0; - } - } - } - - close(); - shutdownComplete = true; - - synchronized (logWriterThread) { - logWriterThread.notifyAll(); - } - } - - public void log(TransactionLogBuffer logBuffer) { - logBufferQueue.add(logBuffer); - } - - private void write(TransactionLogBuffer logBuffer) { - - // check to see if we need to switch file? - LogTime logTime = LogTime.get(); - if (logTime.isNextDay()) { - logTime = LogTime.nextDay(); - switchFile(logTime); - } - - if (bytesWritten > maxBytesPerFile) { - ++fileCounter; - switchFile(logTime); - } - - String txnId = logBuffer.getTransactionId(); - - List messages = logBuffer.messages(); - for (int i = 0; i < messages.size(); i++) { - LogEntry msg = messages.get(i); - printMessage(logTime, txnId, msg); - } - } - - private void printMessage(LogTime logTime, String txnId, LogEntry logEntry) { - - String msg = logEntry.getMsg(); - int len = msg.length(); - if (len == 0) { - return; - } - - // add overhead + content - bytesWritten += 16; - bytesWritten += len; - - if (txnId != null) { - bytesWritten += 7; - bytesWritten += txnId.length(); - out.append("txn["); - out.append(txnId); - out.append("]"); - out.append(deliminator); - } - - out.append(logTime.getTimestamp(logEntry.getTimestamp())); - out.append(deliminator); - out.append(msg).append(" "); - out.append("\n"); - } - - /** - * Recursively output the Throwable stack trace to the log. - * - * @param sb - * the buffer to write the stack trace to - * @param e - * the source throwable - * @param isCause - * flag to indicate if this is the top level throwable or a cause - */ - protected void printThrowable(StringBuilder sb, Throwable e, boolean isCause) { - if (e != null) { - if (isCause) { - sb.append("Caused by: "); - } - sb.append(e.getClass().getName()); - sb.append(":"); - sb.append(e.getMessage()).append(newLinePlaceholder); - - StackTraceElement[] ste = e.getStackTrace(); - int outputStackLines = ste.length; - int notShownCount = 0; - if (ste.length > maxStackTraceLines) { - outputStackLines = maxStackTraceLines; - notShownCount = ste.length - outputStackLines; - } - for (int i = 0; i < outputStackLines; i++) { - sb.append(atString); - sb.append(ste[i].toString()).append(newLinePlaceholder); - } - if (notShownCount > 0) { - sb.append(" ... "); - sb.append(notShownCount); - sb.append(" more").append(newLinePlaceholder); - } - Throwable cause = e.getCause(); - if (cause != null) { - printThrowable(sb, cause, true); - } - } - } - - private String newFileName(LogTime logTime) { - return filepath + File.separator + logFileName + logTime.getYMD() + "-" + fileCounter + logFileSuffix; - } - - /** - * Switch the file to log to. - */ - protected void switchFile(LogTime logTime) { - - try { - long currentFileLength = 0; - String newFilePath = null; - - // skip a file if it already has max bytes - do { - newFilePath = newFileName(logTime); - File f = new File(newFilePath); - if (!f.exists()) { - currentFileLength = 0; - } else { - if (f.length() < maxBytesPerFile * 0.8) { - currentFileLength = f.length(); - } else { - ++fileCounter; - newFilePath = null; - } - } - } while (newFilePath == null); - - if (!newFilePath.equals(currentPath)) { - PrintStream newOut = new PrintStream(new BufferedOutputStream(new FileOutputStream(newFilePath, true))); - - close(); - - bytesWritten = currentFileLength; - currentPath = newFilePath; - out = newOut; - } - - } catch (IOException e) { - e.printStackTrace(); - logger.log(Level.SEVERE, "Error switch log file", e); - } - } - - /** - * Close the file output stream being used for logging. - */ - private void close() { - if (out != null) { - out.flush(); - out.close(); - } - } - - /** - * Returns the directory path of the log file. - */ - protected String makeDirIfRequired(String dir) { - - File f = new File(dir); - if (f.exists()) { - if (!f.isDirectory()) { - String msg = "Transaction logs directory is a file? " + dir; - throw new PersistenceException(msg); - } - } else { - if (!f.mkdirs()) { - String msg = "Failed to create transaction logs directory " + dir; - logger.log(Level.SEVERE, msg); - } - } - return dir; - } - -} +package com.avaje.ebeaninternal.server.transaction.log; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; + +import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer; +import com.avaje.ebeaninternal.server.transaction.TransactionLogWriter; +import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer.LogEntry; + +/** + * Default transaction logger implementation. + *

      + * File based logger that can switch daily. It will include the date in the + * files name. + *

      + *

      + * Administration Note: If log file switching fails it will send the error to + * standard out and standard err print streams. This is assumed to be rather + * unlikely but possible. + *

      + */ +public class FileTransactionLogger implements Runnable, TransactionLogWriter { + + private static final Logger logger = Logger.getLogger(FileTransactionLogger.class.getName()); + + /** + * Used to print stack trace. + */ + private static final String atString = " at "; + + /** + * The newLineChar used instead of NL or CRNL for printing stack traces. + */ + private final String newLinePlaceholder = "\\r\\n"; + + /** + * The maximum number of stack lines to output. This is just for transaction + * logging so 5 is fine. + */ + private final int maxStackTraceLines = 5; + + /** + * Queue that transactions put their LogBuffers onto and this LogBufferWriter + * pulls LogBuffers from. + */ + private final ConcurrentLinkedQueue logBufferQueue = new ConcurrentLinkedQueue(); + + private final Object queueMonitor = new Object(); + + /** + * Thread that is the sole writer of logBuffer entries to the file. + */ + private final Thread logWriterThread; + + private final String threadName; + + /** + * The path of the log file. + */ + private final String filepath; + + /** + * The delimiter to use. + */ + private final String deliminator = ", "; + + /** + * The prefix of the log file name. + */ + private final String logFileName; + + /** + * The file suffix for the logs. + */ + private final String logFileSuffix; + + /** + * Shutdown flag. + */ + private volatile boolean shutdown; + private volatile boolean shutdownComplete; + + /** + * The output stream. + */ + private PrintStream out; + + /** + * The current file path. + */ + private String currentPath; + + /** + * Counter thats incremented when maxBytesPerFile is hit. + */ + private int fileCounter; + + /** + * Roughly the max bytes written to a file before we switch. Switch Daily and + * on hitting max bytes. + */ + private long maxBytesPerFile; + + /** + * Roughly the bytes written to a file. + */ + private long bytesWritten; + + public FileTransactionLogger(String threadName, String dir, String logFileName, int maxBytesPerFile) { + this(threadName, dir, logFileName, "log", maxBytesPerFile); + } + + public FileTransactionLogger(String threadName, String dir, String logFileName, String suffix, int maxBytesPerFile) { + this.threadName = threadName; + this.logFileName = logFileName; + this.logFileSuffix = "." + suffix; + this.maxBytesPerFile = maxBytesPerFile; + + try { + // get the directory where the log files are going to go + filepath = makeDirIfRequired(dir); + + switchFile(LogTime.nextDay()); + + } catch (Exception e) { + System.out.println("FATAL ERROR: init of FileLogger: " + e.getMessage()); + System.err.println("FATAL ERROR: init of FileLogger: " + e.getMessage()); + throw new RuntimeException(e); + } + + logWriterThread = new Thread(this, threadName); + logWriterThread.setDaemon(true); + } + + protected void finalize() throws Throwable { + close(); + super.finalize(); + } + + public void start() { + logWriterThread.start(); + } + + public void shutdown() { + + shutdown = true; + + synchronized (logWriterThread) { + try { + // wait max 20 seconds + logWriterThread.wait(20000); + logger.fine("Shutdown LogBufferWriter " + threadName + " shutdownComplete:" + shutdownComplete); + + } catch (InterruptedException e) { + logger.fine("InterruptedException:" + e); + } + } + + if (!shutdownComplete) { + String m = "WARNING: Shutdown of LogBufferWriter " + threadName + " not completed."; + System.err.println(m); + logger.warning(m); + } + + } + + public void run() { + + int missCount = 0; + + while (!shutdown || missCount < 10) { + if (missCount > 50) { + + if (out != null) { + out.flush(); + } + try { + Thread.sleep(20); + } catch (InterruptedException e) { + logger.log(Level.INFO, "Interrupted TxnLogBufferWriter", e); + } + } + synchronized (queueMonitor) { + if (logBufferQueue.isEmpty()) { + ++missCount; + } else { + TransactionLogBuffer buffer = logBufferQueue.remove(); + write(buffer); + missCount = 0; + } + } + } + + close(); + shutdownComplete = true; + + synchronized (logWriterThread) { + logWriterThread.notifyAll(); + } + } + + public void log(TransactionLogBuffer logBuffer) { + logBufferQueue.add(logBuffer); + } + + private void write(TransactionLogBuffer logBuffer) { + + // check to see if we need to switch file? + LogTime logTime = LogTime.get(); + if (logTime.isNextDay()) { + logTime = LogTime.nextDay(); + switchFile(logTime); + } + + if (bytesWritten > maxBytesPerFile) { + ++fileCounter; + switchFile(logTime); + } + + String txnId = logBuffer.getTransactionId(); + + List messages = logBuffer.messages(); + for (int i = 0; i < messages.size(); i++) { + LogEntry msg = messages.get(i); + printMessage(logTime, txnId, msg); + } + } + + private void printMessage(LogTime logTime, String txnId, LogEntry logEntry) { + + String msg = logEntry.getMsg(); + int len = msg.length(); + if (len == 0) { + return; + } + + // add overhead + content + bytesWritten += 16; + bytesWritten += len; + + if (txnId != null) { + bytesWritten += 7; + bytesWritten += txnId.length(); + out.append("txn["); + out.append(txnId); + out.append("]"); + out.append(deliminator); + } + + out.append(logTime.getTimestamp(logEntry.getTimestamp())); + out.append(deliminator); + out.append(msg).append(" "); + out.append("\n"); + } + + /** + * Recursively output the Throwable stack trace to the log. + * + * @param sb + * the buffer to write the stack trace to + * @param e + * the source throwable + * @param isCause + * flag to indicate if this is the top level throwable or a cause + */ + protected void printThrowable(StringBuilder sb, Throwable e, boolean isCause) { + if (e != null) { + if (isCause) { + sb.append("Caused by: "); + } + sb.append(e.getClass().getName()); + sb.append(":"); + sb.append(e.getMessage()).append(newLinePlaceholder); + + StackTraceElement[] ste = e.getStackTrace(); + int outputStackLines = ste.length; + int notShownCount = 0; + if (ste.length > maxStackTraceLines) { + outputStackLines = maxStackTraceLines; + notShownCount = ste.length - outputStackLines; + } + for (int i = 0; i < outputStackLines; i++) { + sb.append(atString); + sb.append(ste[i].toString()).append(newLinePlaceholder); + } + if (notShownCount > 0) { + sb.append(" ... "); + sb.append(notShownCount); + sb.append(" more").append(newLinePlaceholder); + } + Throwable cause = e.getCause(); + if (cause != null) { + printThrowable(sb, cause, true); + } + } + } + + private String newFileName(LogTime logTime) { + return filepath + File.separator + logFileName + logTime.getYMD() + "-" + fileCounter + logFileSuffix; + } + + /** + * Switch the file to log to. + */ + protected void switchFile(LogTime logTime) { + + try { + long currentFileLength = 0; + String newFilePath = null; + + // skip a file if it already has max bytes + do { + newFilePath = newFileName(logTime); + File f = new File(newFilePath); + if (!f.exists()) { + currentFileLength = 0; + } else { + if (f.length() < maxBytesPerFile * 0.8) { + currentFileLength = f.length(); + } else { + ++fileCounter; + newFilePath = null; + } + } + } while (newFilePath == null); + + if (!newFilePath.equals(currentPath)) { + PrintStream newOut = new PrintStream(new BufferedOutputStream(new FileOutputStream(newFilePath, true))); + + close(); + + bytesWritten = currentFileLength; + currentPath = newFilePath; + out = newOut; + } + + } catch (IOException e) { + e.printStackTrace(); + logger.log(Level.SEVERE, "Error switch log file", e); + } + } + + /** + * Close the file output stream being used for logging. + */ + private void close() { + if (out != null) { + out.flush(); + out.close(); + } + } + + /** + * Returns the directory path of the log file. + */ + protected String makeDirIfRequired(String dir) { + + File f = new File(dir); + if (f.exists()) { + if (!f.isDirectory()) { + String msg = "Transaction logs directory is a file? " + dir; + throw new PersistenceException(msg); + } + } else { + if (!f.mkdirs()) { + String msg = "Failed to create transaction logs directory " + dir; + logger.log(Level.SEVERE, msg); + } + } + return dir; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/FileTransactionLoggerWrapper.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/FileTransactionLoggerWrapper.java index dbc6f95e2..ef38480c7 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/FileTransactionLoggerWrapper.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/FileTransactionLoggerWrapper.java @@ -1,101 +1,82 @@ -/** - * 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.transaction.log; - -import java.util.logging.Logger; - -import com.avaje.ebean.config.GlobalProperties; -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer; -import com.avaje.ebeaninternal.server.transaction.TransactionLogWriter; - -/** - * Wraps a FileTransactionLogger to provide delayed initialisation. - *

      - * This means that it should only create the log file WHEN there is something - * actually logged. This means the logLevel can start at NONE and then change at - * runtime (and the log file will then be initialised). NOTE that a volatile - * with double checked locking is used to make this transition thread safe. - *

      - * - * @author rbygrave - * - */ -public class FileTransactionLoggerWrapper implements TransactionLogWriter { - - private static final Logger logger = Logger.getLogger(FileTransactionLoggerWrapper.class.getName()); - - private final String serverName; - private final String dir; - private final int maxFileSize; - - private volatile FileTransactionLogger logWriter; - - public FileTransactionLoggerWrapper(ServerConfig serverConfig) { - - String evalDir = serverConfig.getLoggingDirectoryWithEval(); - this.dir = evalDir != null ? evalDir : "logs"; - this.maxFileSize = GlobalProperties.getInt("ebean.logging.maxFileSize", 100 * 1024 * 1024); - this.serverName = serverConfig.getName(); - } - - private FileTransactionLogger initialiseLogger() { - - synchronized (this) { - // double check locking here so logWriter NEEDS to be volatile!! - FileTransactionLogger writer = this.logWriter; - if (writer != null) { - return writer; - } - - String middleName = GlobalProperties.get("ebean.logging.filename", "_txn_"); - String logPrefix = serverName + middleName; - String threadName = "Ebean-" + serverName + "-TxnLogWriter"; - - // create the real logger and start it - FileTransactionLogger newLogWriter = new FileTransactionLogger(threadName, dir, logPrefix, maxFileSize); - - // assignment of volatile field - this.logWriter = newLogWriter; - - // start background thread for the writer - newLogWriter.start(); - logger.info("Transaction logs in: " + dir); - return newLogWriter; - } - } - - public void log(TransactionLogBuffer logBuffer) { - // volatile read - FileTransactionLogger writer = this.logWriter; - if (writer == null) { - writer = initialiseLogger(); - } - writer.log(logBuffer); - } - - public void shutdown() { - if (logWriter != null) { - logWriter.shutdown(); - } - - } - -} +package com.avaje.ebeaninternal.server.transaction.log; + +import java.util.logging.Logger; + +import com.avaje.ebean.config.GlobalProperties; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer; +import com.avaje.ebeaninternal.server.transaction.TransactionLogWriter; + +/** + * Wraps a FileTransactionLogger to provide delayed initialisation. + *

      + * This means that it should only create the log file WHEN there is something + * actually logged. This means the logLevel can start at NONE and then change at + * runtime (and the log file will then be initialised). NOTE that a volatile + * with double checked locking is used to make this transition thread safe. + *

      + * + * @author rbygrave + * + */ +public class FileTransactionLoggerWrapper implements TransactionLogWriter { + + private static final Logger logger = Logger.getLogger(FileTransactionLoggerWrapper.class.getName()); + + private final String serverName; + private final String dir; + private final int maxFileSize; + + private volatile FileTransactionLogger logWriter; + + public FileTransactionLoggerWrapper(ServerConfig serverConfig) { + + String evalDir = serverConfig.getLoggingDirectoryWithEval(); + this.dir = evalDir != null ? evalDir : "logs"; + this.maxFileSize = GlobalProperties.getInt("ebean.logging.maxFileSize", 100 * 1024 * 1024); + this.serverName = serverConfig.getName(); + } + + private FileTransactionLogger initialiseLogger() { + + synchronized (this) { + // double check locking here so logWriter NEEDS to be volatile!! + FileTransactionLogger writer = this.logWriter; + if (writer != null) { + return writer; + } + + String middleName = GlobalProperties.get("ebean.logging.filename", "_txn_"); + String logPrefix = serverName + middleName; + String threadName = "Ebean-" + serverName + "-TxnLogWriter"; + + // create the real logger and start it + FileTransactionLogger newLogWriter = new FileTransactionLogger(threadName, dir, logPrefix, maxFileSize); + + // assignment of volatile field + this.logWriter = newLogWriter; + + // start background thread for the writer + newLogWriter.start(); + logger.info("Transaction logs in: " + dir); + return newLogWriter; + } + } + + public void log(TransactionLogBuffer logBuffer) { + // volatile read + FileTransactionLogger writer = this.logWriter; + if (writer == null) { + writer = initialiseLogger(); + } + writer.log(logBuffer); + } + + public void shutdown() { + if (logWriter != null) { + logWriter.shutdown(); + } + + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/JuliTransactionLogger.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/JuliTransactionLogger.java index 4bd35e3c5..e5a6ce91b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/JuliTransactionLogger.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/JuliTransactionLogger.java @@ -1,67 +1,48 @@ -/** - * 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.transaction.log; - -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer; -import com.avaje.ebeaninternal.server.transaction.TransactionLogWriter; -import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer.LogEntry; - -/** - * A transactionLogger that uses a java.util.logging.Logger. - *

      - * See {@link ServerConfig#setUseJuliTransactionLogger(boolean)} - *

      - * @author rbygrave - */ -public class JuliTransactionLogger implements TransactionLogWriter { - - private static Logger logger = Logger.getLogger(JuliTransactionLogger.class.getName()); - - public void log(TransactionLogBuffer logBuffer) { - - String txnId = logBuffer.getTransactionId(); - - List messages = logBuffer.messages(); - for (int i = 0; i < messages.size(); i++) { - LogEntry logEntry = messages.get(i); - log(txnId, logEntry); - } - } - - public void shutdown() { - } - - private void log(String txnId, LogEntry entry) { - - String message = entry.getMsg(); - if (txnId != null && message != null && !message.startsWith("Trans[")){ - message = "Trans["+txnId+"] "+message; - } - - logger.log(Level.INFO, message); - } - - -} +package com.avaje.ebeaninternal.server.transaction.log; + +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer; +import com.avaje.ebeaninternal.server.transaction.TransactionLogWriter; +import com.avaje.ebeaninternal.server.transaction.TransactionLogBuffer.LogEntry; + +/** + * A transactionLogger that uses a java.util.logging.Logger. + *

      + * See {@link ServerConfig#setUseJuliTransactionLogger(boolean)} + *

      + * @author rbygrave + */ +public class JuliTransactionLogger implements TransactionLogWriter { + + private static Logger logger = Logger.getLogger(JuliTransactionLogger.class.getName()); + + public void log(TransactionLogBuffer logBuffer) { + + String txnId = logBuffer.getTransactionId(); + + List messages = logBuffer.messages(); + for (int i = 0; i < messages.size(); i++) { + LogEntry logEntry = messages.get(i); + log(txnId, logEntry); + } + } + + public void shutdown() { + } + + private void log(String txnId, LogEntry entry) { + + String message = entry.getMsg(); + if (txnId != null && message != null && !message.startsWith("Trans[")){ + message = "Trans["+txnId+"] "+message; + } + + logger.log(Level.INFO, message); + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/LogTime.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/LogTime.java index c9175ea1f..7c4287ba0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/LogTime.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/LogTime.java @@ -1,192 +1,173 @@ -/** - * 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.transaction.log; - -import java.util.Calendar; -import java.util.GregorianCalendar; - -/** - * Utility object used to in logging time. - *

      - * Used in place of SimpleDateFormat which requires synchronization. Determines - * if the day has changed and returns a format of the current time and day. - *

      - */ -public class LogTime { - - private static final String[] sep = { ":", "." }; - - private static LogTime day; - static { - day = new LogTime(); - } - - public static LogTime get() { - return day; - } - - public static LogTime nextDay() { - LogTime d = new LogTime(); - day = d; - return d; - } - - public static LogTime getWithCheck() { - LogTime d = day; - if (d.isNextDay()) { - return nextDay(); - } else { - return d; - } - } - - private final String ymd; - - private final long startMidnight; - - private final long startTomorrow; - - /** - * Because every variable is private final the constructor should be thread - * safe (In JDK5+). - */ - private LogTime() { - - GregorianCalendar now = new GregorianCalendar(); - - now.set(Calendar.HOUR_OF_DAY, 0); - now.set(Calendar.MINUTE, 0); - now.set(Calendar.SECOND, 0); - now.set(Calendar.MILLISECOND, 0); - - this.startMidnight = now.getTime().getTime(); - this.ymd = getDayDerived(now); - - now.add(Calendar.DATE, 1); - this.startTomorrow = now.getTime().getTime(); - } - - /** - * Return true if we have moved into tomorrow. This is used to trigger log - * switching if required. - */ - public boolean isNextDay() { - return (System.currentTimeMillis() >= startTomorrow); - } - - /** - * Return the Year Month Day for today. - */ - public String getYMD() { - return ymd; - } - - /** - * Return the current time specify the separators. - *

      - * The separators is a String[2] with the first string separating hours - * minutes and seconds and the second separating seconds from millis. - *

      - *

      - * The default is {":","."} - *

      - */ - public String getNow(String[] separators) { - - return getTimestamp(System.currentTimeMillis(), separators); - - } - - public String getTimestamp(long systime) { - - StringBuilder sb = new StringBuilder(); - getTime(sb, systime, startMidnight, sep); - return sb.toString(); - } - - public String getTimestamp(long systime, String[] separators) { - - StringBuilder sb = new StringBuilder(); - getTime(sb, systime, startMidnight, separators); - return sb.toString(); - } - - /** - * Returns the current time. - *

      - * Format used is hours:minutes:seconds.millis - *

      - */ - public String getNow() { - return getNow(sep); - } - - /** - * Set the derived day information. - */ - private String getDayDerived(Calendar now) { - - int nowyear = now.get(Calendar.YEAR); - int nowmonth = now.get(Calendar.MONTH); - int nowday = now.get(Calendar.DAY_OF_MONTH); - - nowmonth++; - - StringBuilder sb = new StringBuilder(); - - format(sb, nowyear, 4); - format(sb, nowmonth, 2); - format(sb, nowday, 2); - - return sb.toString(); - } - - private void getTime(StringBuilder sb, long time, long midnight, String[] separator) { - - long rem = time - midnight;// startMidnight; - - long millis = rem % 1000; - rem = rem / 1000; - long secs = rem % 60; - rem = rem / 60; - long mins = rem % 60; - rem = rem / 60; - long hrs = rem; - - format(sb, hrs, 2); - sb.append(separator[0]); - format(sb, mins, 2); - sb.append(separator[0]); - format(sb, secs, 2); - sb.append(separator[1]); - format(sb, millis, 3); - } - - private void format(StringBuilder sb, long value, int places) { - String format = Long.toString(value); - - int pad = places - format.length(); - for (int i = 0; i < pad; i++) { - sb.append("0"); - } - sb.append(format); - } - -} +package com.avaje.ebeaninternal.server.transaction.log; + +import java.util.Calendar; +import java.util.GregorianCalendar; + +/** + * Utility object used to in logging time. + *

      + * Used in place of SimpleDateFormat which requires synchronization. Determines + * if the day has changed and returns a format of the current time and day. + *

      + */ +public class LogTime { + + private static final String[] sep = { ":", "." }; + + private static LogTime day; + static { + day = new LogTime(); + } + + public static LogTime get() { + return day; + } + + public static LogTime nextDay() { + LogTime d = new LogTime(); + day = d; + return d; + } + + public static LogTime getWithCheck() { + LogTime d = day; + if (d.isNextDay()) { + return nextDay(); + } else { + return d; + } + } + + private final String ymd; + + private final long startMidnight; + + private final long startTomorrow; + + /** + * Because every variable is private final the constructor should be thread + * safe (In JDK5+). + */ + private LogTime() { + + GregorianCalendar now = new GregorianCalendar(); + + now.set(Calendar.HOUR_OF_DAY, 0); + now.set(Calendar.MINUTE, 0); + now.set(Calendar.SECOND, 0); + now.set(Calendar.MILLISECOND, 0); + + this.startMidnight = now.getTime().getTime(); + this.ymd = getDayDerived(now); + + now.add(Calendar.DATE, 1); + this.startTomorrow = now.getTime().getTime(); + } + + /** + * Return true if we have moved into tomorrow. This is used to trigger log + * switching if required. + */ + public boolean isNextDay() { + return (System.currentTimeMillis() >= startTomorrow); + } + + /** + * Return the Year Month Day for today. + */ + public String getYMD() { + return ymd; + } + + /** + * Return the current time specify the separators. + *

      + * The separators is a String[2] with the first string separating hours + * minutes and seconds and the second separating seconds from millis. + *

      + *

      + * The default is {":","."} + *

      + */ + public String getNow(String[] separators) { + + return getTimestamp(System.currentTimeMillis(), separators); + + } + + public String getTimestamp(long systime) { + + StringBuilder sb = new StringBuilder(); + getTime(sb, systime, startMidnight, sep); + return sb.toString(); + } + + public String getTimestamp(long systime, String[] separators) { + + StringBuilder sb = new StringBuilder(); + getTime(sb, systime, startMidnight, separators); + return sb.toString(); + } + + /** + * Returns the current time. + *

      + * Format used is hours:minutes:seconds.millis + *

      + */ + public String getNow() { + return getNow(sep); + } + + /** + * Set the derived day information. + */ + private String getDayDerived(Calendar now) { + + int nowyear = now.get(Calendar.YEAR); + int nowmonth = now.get(Calendar.MONTH); + int nowday = now.get(Calendar.DAY_OF_MONTH); + + nowmonth++; + + StringBuilder sb = new StringBuilder(); + + format(sb, nowyear, 4); + format(sb, nowmonth, 2); + format(sb, nowday, 2); + + return sb.toString(); + } + + private void getTime(StringBuilder sb, long time, long midnight, String[] separator) { + + long rem = time - midnight;// startMidnight; + + long millis = rem % 1000; + rem = rem / 1000; + long secs = rem % 60; + rem = rem / 60; + long mins = rem % 60; + rem = rem / 60; + long hrs = rem; + + format(sb, hrs, 2); + sb.append(separator[0]); + format(sb, mins, 2); + sb.append(separator[0]); + format(sb, secs, 2); + sb.append(separator[1]); + format(sb, millis, 3); + } + + private void format(StringBuilder sb, long value, int places) { + String format = Long.toString(value); + + int pad = places - format.length(); + for (int i = 0; i < pad; i++) { + sb.append("0"); + } + sb.append(format); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/SimpleLogger.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/SimpleLogger.java index 5b7df335f..6758f17e3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/log/SimpleLogger.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/log/SimpleLogger.java @@ -1,322 +1,303 @@ -/** - * 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.transaction.log; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.PrintStream; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.persistence.PersistenceException; - - -/** - * File based logger that can switch daily. It will include the date in the - * files name. - *

      - * Administration Note: If log file switching fails it will send the error to - * standard out and standard err print streams. This is assumed to be rather - * unlikely but possible. - *

      - */ -public class SimpleLogger { - - private static final Logger logger = Logger.getLogger(SimpleLogger.class.getName()); - - /** - * Used to print stack trace. - */ - private static final String atString = " at "; - - /** - * The output stream. - */ - private PrintStream out; - - /** - * Whether to append or replace. - */ - private boolean doAppend = true; - - /** - * Flag to indicate the logger if the logger has been closed. - */ - private boolean open = true; - - /** - * The current file path. - */ - private String currentPath; - - /** - * The path of the log file. - */ - private final String filepath; - - /** - * Set to true if using daily file switching. - */ - private final boolean useFileSwitching; - - /** - * The maximum number of stack lines to output. - * This is just for transaction logging so 5 is fine. - */ - private final int maxStackTraceLines = 5; - - /** - * The delimiter to use. - */ - private final String deliminator; - - /** - * Object used to synch the file switching. - */ - private final Object fileMonitor = new Object(); - - /** - * The prefix of the log file name. - */ - private final String logFileName; - - /** - * The file suffix for the logs. - */ - private final String logFileSuffix; - - /** - * The newLineChar used instead of NL or CRNL. - */ - private final String newLineChar = "\\r\\n"; - - private final boolean csv; - - /** - * Create a logger with a logFileName and useFileSwitching flag. - * - * @param dir - * the sub directory to put the log. Can be null. - * @param logFileName - * the prefix log file name. - * @param useFileSwitching - * if true then use daily file switching. - */ - public SimpleLogger(String dir, String logFileName, boolean useFileSwitching, String suffix) { - this.logFileName = logFileName; - this.useFileSwitching = useFileSwitching; - this.logFileSuffix = "."+suffix; - csv = "csv".equalsIgnoreCase(suffix); - deliminator = csv ? "," : ", "; - try { - - // get the directory where the log files are going to go - filepath = makeDirIfRequired(dir); - - switchFile(LogTime.nextDay()); - - } catch (Exception e) { - // Not going to use logger to show logger exceptions... - // Using standard out and standard err instead. - System.out.println("FATAL ERROR: init of FileLogger: " + e.getMessage()); - System.err.println("FATAL ERROR: init of FileLogger: " + e.getMessage()); - throw new RuntimeException(e); - } - } - - public SimpleLogger(String dir, String logFileName, boolean useFileSwitching) { - this(dir, logFileName, useFileSwitching, "log"); - } - - protected void finalize() throws Throwable { - close(); - super.finalize(); - } - - /** - * Close the file output stream being used for logging. - */ - public void close() { - if (open) { - out.flush(); - out.close(); - open = false; - } - } - - public void log(String msg) { - log(null, msg, null); - } - - public void log(String msg, Throwable e) { - log(null, msg, e); - } - - /** - * Log the event to the file. - */ - public void log(String transId, String msg, Throwable e) { - - // check to see if we need to switch file? - LogTime logTime = LogTime.get(); - if (logTime.isNextDay()) { - logTime = LogTime.nextDay(); - try { - switchFile(logTime); - } catch (Exception ex) { - // This is a pretty serious error... not logging it though as - // this could be recursive so just going to send to std err - ex.printStackTrace(); - } - } - - // prefix of transID and timestamp ~ 40 chars - int roughSize = 40; - if (msg != null) { - roughSize += msg.length(); - } - if (e != null) { - roughSize += 200; - } - - StringBuilder line = new StringBuilder(roughSize); - if (transId != null){ - line.append("trans[").append(transId).append("]").append(deliminator); - } - - if (csv){ - line.append("\"'"); - } - line.append(logTime.getNow()); - if (csv){ - line.append("'\""); - } - line.append(deliminator); - - if (msg != null) { - line.append(msg).append(" "); - } - - printThrowable(line, e, false); - - String lineString = line.toString(); - - synchronized (fileMonitor) { - out.println(lineString); - // without flush automatic close() *MUST* be called - out.flush(); - } - } - - /** - * Recursively output the Throwable stack trace to the log. - * - * @param sb - * the buffer to write the stack trace to - * @param e - * the source throwable - * @param isCause - * flag to indicate if this is the top level throwable or a cause - */ - protected void printThrowable(StringBuilder sb, Throwable e, boolean isCause) { - if (e != null) { - if (isCause) { - sb.append("Caused by: "); - } - sb.append(e.getClass().getName()); - sb.append(":"); - sb.append(e.getMessage()).append(newLineChar); - - StackTraceElement[] ste = e.getStackTrace(); - int outputStackLines = ste.length; - int notShownCount = 0; - if (ste.length > maxStackTraceLines) { - outputStackLines = maxStackTraceLines; - notShownCount = ste.length - outputStackLines; - } - for (int i = 0; i < outputStackLines; i++) { - sb.append(atString); - sb.append(ste[i].toString()).append(newLineChar); - } - if (notShownCount > 0) { - sb.append(" ... "); - sb.append(notShownCount); - sb.append(" more").append(newLineChar); - } - Throwable cause = e.getCause(); - if (cause != null) { - printThrowable(sb, cause, true); - } - } - } - - /** - * Creates a new file and sets the file logging output to be directed to the - * new file. - * - * @exception Exception - * indicates a problem writing to the new log file. - */ - protected void switchFile(LogTime logTime) throws Exception { - - String newFilePath = filepath + File.separator + logFileName; - - if (useFileSwitching) { - // For file switching include the date in the file name - newFilePath = newFilePath + logTime.getYMD() + logFileSuffix; - - } else { - newFilePath = newFilePath + logFileSuffix; - } - - // Try to open an output stream to the file - synchronized (fileMonitor) { - - if (!newFilePath.equals(currentPath)) { - currentPath = newFilePath; - - out = new PrintStream(new BufferedOutputStream(new FileOutputStream(newFilePath, - doAppend))); - } - } - } - - /** - * Returns the directory path of the log file. - */ - protected String makeDirIfRequired(String dir) { - - File f = new File(dir); - if (f.exists()){ - if (!f.isDirectory()){ - String msg = "Transaction logs directory is a file? "+dir; - throw new PersistenceException(msg); - } - } else { - if (!f.mkdirs()) { - String msg = "Failed to create transaction logs directory "+dir; - logger.log(Level.SEVERE, msg); - } - } - return dir; - } - -} +package com.avaje.ebeaninternal.server.transaction.log; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.persistence.PersistenceException; + + +/** + * File based logger that can switch daily. It will include the date in the + * files name. + *

      + * Administration Note: If log file switching fails it will send the error to + * standard out and standard err print streams. This is assumed to be rather + * unlikely but possible. + *

      + */ +public class SimpleLogger { + + private static final Logger logger = Logger.getLogger(SimpleLogger.class.getName()); + + /** + * Used to print stack trace. + */ + private static final String atString = " at "; + + /** + * The output stream. + */ + private PrintStream out; + + /** + * Whether to append or replace. + */ + private boolean doAppend = true; + + /** + * Flag to indicate the logger if the logger has been closed. + */ + private boolean open = true; + + /** + * The current file path. + */ + private String currentPath; + + /** + * The path of the log file. + */ + private final String filepath; + + /** + * Set to true if using daily file switching. + */ + private final boolean useFileSwitching; + + /** + * The maximum number of stack lines to output. + * This is just for transaction logging so 5 is fine. + */ + private final int maxStackTraceLines = 5; + + /** + * The delimiter to use. + */ + private final String deliminator; + + /** + * Object used to synch the file switching. + */ + private final Object fileMonitor = new Object(); + + /** + * The prefix of the log file name. + */ + private final String logFileName; + + /** + * The file suffix for the logs. + */ + private final String logFileSuffix; + + /** + * The newLineChar used instead of NL or CRNL. + */ + private final String newLineChar = "\\r\\n"; + + private final boolean csv; + + /** + * Create a logger with a logFileName and useFileSwitching flag. + * + * @param dir + * the sub directory to put the log. Can be null. + * @param logFileName + * the prefix log file name. + * @param useFileSwitching + * if true then use daily file switching. + */ + public SimpleLogger(String dir, String logFileName, boolean useFileSwitching, String suffix) { + this.logFileName = logFileName; + this.useFileSwitching = useFileSwitching; + this.logFileSuffix = "."+suffix; + csv = "csv".equalsIgnoreCase(suffix); + deliminator = csv ? "," : ", "; + try { + + // get the directory where the log files are going to go + filepath = makeDirIfRequired(dir); + + switchFile(LogTime.nextDay()); + + } catch (Exception e) { + // Not going to use logger to show logger exceptions... + // Using standard out and standard err instead. + System.out.println("FATAL ERROR: init of FileLogger: " + e.getMessage()); + System.err.println("FATAL ERROR: init of FileLogger: " + e.getMessage()); + throw new RuntimeException(e); + } + } + + public SimpleLogger(String dir, String logFileName, boolean useFileSwitching) { + this(dir, logFileName, useFileSwitching, "log"); + } + + protected void finalize() throws Throwable { + close(); + super.finalize(); + } + + /** + * Close the file output stream being used for logging. + */ + public void close() { + if (open) { + out.flush(); + out.close(); + open = false; + } + } + + public void log(String msg) { + log(null, msg, null); + } + + public void log(String msg, Throwable e) { + log(null, msg, e); + } + + /** + * Log the event to the file. + */ + public void log(String transId, String msg, Throwable e) { + + // check to see if we need to switch file? + LogTime logTime = LogTime.get(); + if (logTime.isNextDay()) { + logTime = LogTime.nextDay(); + try { + switchFile(logTime); + } catch (Exception ex) { + // This is a pretty serious error... not logging it though as + // this could be recursive so just going to send to std err + ex.printStackTrace(); + } + } + + // prefix of transID and timestamp ~ 40 chars + int roughSize = 40; + if (msg != null) { + roughSize += msg.length(); + } + if (e != null) { + roughSize += 200; + } + + StringBuilder line = new StringBuilder(roughSize); + if (transId != null){ + line.append("trans[").append(transId).append("]").append(deliminator); + } + + if (csv){ + line.append("\"'"); + } + line.append(logTime.getNow()); + if (csv){ + line.append("'\""); + } + line.append(deliminator); + + if (msg != null) { + line.append(msg).append(" "); + } + + printThrowable(line, e, false); + + String lineString = line.toString(); + + synchronized (fileMonitor) { + out.println(lineString); + // without flush automatic close() *MUST* be called + out.flush(); + } + } + + /** + * Recursively output the Throwable stack trace to the log. + * + * @param sb + * the buffer to write the stack trace to + * @param e + * the source throwable + * @param isCause + * flag to indicate if this is the top level throwable or a cause + */ + protected void printThrowable(StringBuilder sb, Throwable e, boolean isCause) { + if (e != null) { + if (isCause) { + sb.append("Caused by: "); + } + sb.append(e.getClass().getName()); + sb.append(":"); + sb.append(e.getMessage()).append(newLineChar); + + StackTraceElement[] ste = e.getStackTrace(); + int outputStackLines = ste.length; + int notShownCount = 0; + if (ste.length > maxStackTraceLines) { + outputStackLines = maxStackTraceLines; + notShownCount = ste.length - outputStackLines; + } + for (int i = 0; i < outputStackLines; i++) { + sb.append(atString); + sb.append(ste[i].toString()).append(newLineChar); + } + if (notShownCount > 0) { + sb.append(" ... "); + sb.append(notShownCount); + sb.append(" more").append(newLineChar); + } + Throwable cause = e.getCause(); + if (cause != null) { + printThrowable(sb, cause, true); + } + } + } + + /** + * Creates a new file and sets the file logging output to be directed to the + * new file. + * + * @exception Exception + * indicates a problem writing to the new log file. + */ + protected void switchFile(LogTime logTime) throws Exception { + + String newFilePath = filepath + File.separator + logFileName; + + if (useFileSwitching) { + // For file switching include the date in the file name + newFilePath = newFilePath + logTime.getYMD() + logFileSuffix; + + } else { + newFilePath = newFilePath + logFileSuffix; + } + + // Try to open an output stream to the file + synchronized (fileMonitor) { + + if (!newFilePath.equals(currentPath)) { + currentPath = newFilePath; + + out = new PrintStream(new BufferedOutputStream(new FileOutputStream(newFilePath, + doAppend))); + } + } + } + + /** + * Returns the directory path of the log file. + */ + protected String makeDirIfRequired(String dir) { + + File f = new File(dir); + if (f.exists()){ + if (!f.isDirectory()){ + String msg = "Transaction logs directory is a file? "+dir; + throw new PersistenceException(msg); + } + } else { + if (!f.mkdirs()) { + String msg = "Failed to create transaction logs directory "+dir; + logger.log(Level.SEVERE, msg); + } + } + return dir; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/BeanToDbMap.java b/src/main/java/com/avaje/ebeaninternal/server/type/BeanToDbMap.java index ccf908f1b..0339d5e2c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/BeanToDbMap.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/BeanToDbMap.java @@ -1,103 +1,84 @@ -/** - * 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.type; - -import java.util.HashMap; - -/** - * Used to map Bean values to DB values. - *

      - * Useful for building Enum converters where you want to map the DB values an - * Enum gets converter to. - *

      - * - * @param - * The Bean value type - * @param - * The DB value type - */ -public class BeanToDbMap { - - final HashMap keyMap; - - final HashMap valueMap; - - final boolean allowNulls; - - /** - * Construct with allowNulls defaulting to false. - */ - public BeanToDbMap() { - this(false); - } - - /** - * Construct with allowNulls setting. - *

      - * If allowNulls is false then an IllegalArgumentException is thrown by - * either the getDBValue or getBeanValue methods if not matching Bean or DB - * value is found. - *

      - */ - public BeanToDbMap(boolean allowNulls) { - this.allowNulls = allowNulls; - keyMap = new HashMap(); - valueMap = new HashMap(); - } - - /** - * Add a bean value and DB value pair. - */ - public BeanToDbMap add(B beanValue, D dbValue) { - keyMap.put(beanValue, dbValue); - valueMap.put(dbValue, beanValue); - return this; - } - - /** - * Return the DB value given the bean value. - */ - public D getDbValue(B beanValue) { - if (beanValue == null){ - return null; - } - D dbValue = keyMap.get(beanValue); - if (dbValue == null && !allowNulls) { - String msg = "DB value for " + beanValue + " not found in "+valueMap; - throw new IllegalArgumentException(msg); - } - return dbValue; - } - - /** - * Return the Bean value given the DB value. - */ - public B getBeanValue(D dbValue) { - if (dbValue == null){ - return null; - } - B beanValue = valueMap.get(dbValue); - if (beanValue == null && !allowNulls) { - String msg = "Bean value for " + dbValue + " not found in "+valueMap; - throw new IllegalArgumentException(msg); - } - return beanValue; - } -} +package com.avaje.ebeaninternal.server.type; + +import java.util.HashMap; + +/** + * Used to map Bean values to DB values. + *

      + * Useful for building Enum converters where you want to map the DB values an + * Enum gets converter to. + *

      + * + * @param + * The Bean value type + * @param + * The DB value type + */ +public class BeanToDbMap { + + final HashMap keyMap; + + final HashMap valueMap; + + final boolean allowNulls; + + /** + * Construct with allowNulls defaulting to false. + */ + public BeanToDbMap() { + this(false); + } + + /** + * Construct with allowNulls setting. + *

      + * If allowNulls is false then an IllegalArgumentException is thrown by + * either the getDBValue or getBeanValue methods if not matching Bean or DB + * value is found. + *

      + */ + public BeanToDbMap(boolean allowNulls) { + this.allowNulls = allowNulls; + keyMap = new HashMap(); + valueMap = new HashMap(); + } + + /** + * Add a bean value and DB value pair. + */ + public BeanToDbMap add(B beanValue, D dbValue) { + keyMap.put(beanValue, dbValue); + valueMap.put(dbValue, beanValue); + return this; + } + + /** + * Return the DB value given the bean value. + */ + public D getDbValue(B beanValue) { + if (beanValue == null){ + return null; + } + D dbValue = keyMap.get(beanValue); + if (dbValue == null && !allowNulls) { + String msg = "DB value for " + beanValue + " not found in "+valueMap; + throw new IllegalArgumentException(msg); + } + return dbValue; + } + + /** + * Return the Bean value given the DB value. + */ + public B getBeanValue(D dbValue) { + if (dbValue == null){ + return null; + } + B beanValue = valueMap.get(dbValue); + if (beanValue == null && !allowNulls) { + String msg = "Bean value for " + dbValue + " not found in "+valueMap; + throw new IllegalArgumentException(msg); + } + return beanValue; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundProperty.java b/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundProperty.java index a28c95ba7..bc20eef20 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundProperty.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundProperty.java @@ -1,101 +1,82 @@ -/** - * 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.type; - -import com.avaje.ebean.config.CompoundTypeProperty; - -/** - * Wraps a CompoundTypeProperty with it's type and parent for nested compound - * types. - * - * @author rbygrave - */ -public class CtCompoundProperty { - - private final String relativeName; - - private final CtCompoundProperty parent; - - private final CtCompoundType compoundType; - - @SuppressWarnings({ "rawtypes" }) - private final CompoundTypeProperty property; - - public CtCompoundProperty(String relativeName, CtCompoundProperty parent, CtCompoundType ctType, - CompoundTypeProperty property) { - - this.relativeName = relativeName; - this.parent = parent; - this.compoundType = ctType; - this.property = property; - } - - /** - * The property name relative to the root of the compound type. - */ - public String getRelativeName() { - return relativeName; - } - - /** - * The property name local to its type. - */ - public String getPropertyName() { - return property.getName(); - } - - public String toString() { - return relativeName; - } - - @SuppressWarnings("unchecked") - public Object getValue(Object valueObject) { - if (valueObject == null) { - return null; - } - if (parent != null) { - valueObject = parent.getValue(valueObject); - } - return property.getValue(valueObject); - } - - /** - * Set a scalar value that is used to build the immutable compound value - * object. - *

      - * When all the scalar values have been collected then the compound value - * object is built and this can be recursive for nested compound types. - *

      - */ - public Object setValue(Object bean, Object value) { - - // compoundType and propertyName should be correct depth - Object compoundValue = ImmutableCompoundTypeBuilder.set(compoundType, property.getName(), value); - - if (compoundValue != null && parent != null) { - // Continue up the tree - return parent.setValue(bean, compoundValue); - - } else { - return compoundValue; - } - } - -} +package com.avaje.ebeaninternal.server.type; + +import com.avaje.ebean.config.CompoundTypeProperty; + +/** + * Wraps a CompoundTypeProperty with it's type and parent for nested compound + * types. + * + * @author rbygrave + */ +public class CtCompoundProperty { + + private final String relativeName; + + private final CtCompoundProperty parent; + + private final CtCompoundType compoundType; + + @SuppressWarnings({ "rawtypes" }) + private final CompoundTypeProperty property; + + public CtCompoundProperty(String relativeName, CtCompoundProperty parent, CtCompoundType ctType, + CompoundTypeProperty property) { + + this.relativeName = relativeName; + this.parent = parent; + this.compoundType = ctType; + this.property = property; + } + + /** + * The property name relative to the root of the compound type. + */ + public String getRelativeName() { + return relativeName; + } + + /** + * The property name local to its type. + */ + public String getPropertyName() { + return property.getName(); + } + + public String toString() { + return relativeName; + } + + @SuppressWarnings("unchecked") + public Object getValue(Object valueObject) { + if (valueObject == null) { + return null; + } + if (parent != null) { + valueObject = parent.getValue(valueObject); + } + return property.getValue(valueObject); + } + + /** + * Set a scalar value that is used to build the immutable compound value + * object. + *

      + * When all the scalar values have been collected then the compound value + * object is built and this can be recursive for nested compound types. + *

      + */ + public Object setValue(Object bean, Object value) { + + // compoundType and propertyName should be correct depth + Object compoundValue = ImmutableCompoundTypeBuilder.set(compoundType, property.getName(), value); + + if (compoundValue != null && parent != null) { + // Continue up the tree + return parent.setValue(bean, compoundValue); + + } else { + return compoundValue; + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundPropertyElAdapter.java b/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundPropertyElAdapter.java index bd224745a..3a07f8e1d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundPropertyElAdapter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundPropertyElAdapter.java @@ -1,158 +1,139 @@ -/** - * 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.type; - -import com.avaje.ebean.text.StringFormatter; -import com.avaje.ebean.text.StringParser; -import com.avaje.ebeaninternal.server.deploy.BeanProperty; -import com.avaje.ebeaninternal.server.el.ElPropertyValue; - -/** - * Adapter for CtCompoundProperty to ElPropertyValue. - *

      - * This is used for non-scalar properties of a Compound Value Object. These only - * occur in nested compound types. - *

      - * - * @author rbygrave - */ -public class CtCompoundPropertyElAdapter implements ElPropertyValue { - - private final CtCompoundProperty prop; - - private int deployOrder; - - public CtCompoundPropertyElAdapter(CtCompoundProperty prop) { - this.prop = prop; - } - - public void setDeployOrder(int deployOrder) { - this.deployOrder = deployOrder; - } - - public Object elConvertType(Object value) { - return value; - } - - public Object elGetReference(Object bean) { - return bean; - } - - public Object elGetValue(Object bean) { - return prop.getValue(bean); - } - - public void elSetReference(Object bean) { - // prop.setValue(bean, value) - } - - public void elSetValue(Object bean, Object value, boolean populate, boolean reference) { - prop.setValue(bean, value); - } - - public int getDeployOrder() { - return deployOrder; - } - - public String getAssocOneIdExpr(String prefix, String operator) { - throw new RuntimeException("Not Supported or Expected"); - } - - public Object[] getAssocOneIdValues(Object bean) { - throw new RuntimeException("Not Supported or Expected"); - } - - public String getAssocIdInExpr(String prefix) { - throw new RuntimeException("Not Supported or Expected"); - } - - public String getAssocIdInValueExpr(int size) { - throw new RuntimeException("Not Supported or Expected"); - } - - public BeanProperty getBeanProperty() { - return null; - } - - public StringFormatter getStringFormatter() { - return null; - } - - public StringParser getStringParser() { - return null; - } - - public boolean isDbEncrypted() { - return false; - } - - public boolean isLocalEncrypted() { - return false; - } - - public boolean isAssocId() { - return false; - } - - public boolean isAssocProperty() { - return false; - } - - public boolean isDateTimeCapable() { - return false; - } - - public int getJdbcType() { - return 0; - } - - public Object parseDateTime(long systemTimeMillis) { - throw new RuntimeException("Not Supported or Expected"); - } - - public boolean containsMany() { - return false; - } - - public boolean containsManySince(String sinceProperty) { - return containsMany(); - } - - public String getDbColumn() { - return null; - } - - public String getElPlaceholder(boolean encrypted) { - return null; - } - - public String getElPrefix() { - return null; - } - - public String getName() { - return prop.getPropertyName(); - } - - public String getElName() { - return prop.getPropertyName(); - } - -} +package com.avaje.ebeaninternal.server.type; + +import com.avaje.ebean.text.StringFormatter; +import com.avaje.ebean.text.StringParser; +import com.avaje.ebeaninternal.server.deploy.BeanProperty; +import com.avaje.ebeaninternal.server.el.ElPropertyValue; + +/** + * Adapter for CtCompoundProperty to ElPropertyValue. + *

      + * This is used for non-scalar properties of a Compound Value Object. These only + * occur in nested compound types. + *

      + * + * @author rbygrave + */ +public class CtCompoundPropertyElAdapter implements ElPropertyValue { + + private final CtCompoundProperty prop; + + private int deployOrder; + + public CtCompoundPropertyElAdapter(CtCompoundProperty prop) { + this.prop = prop; + } + + public void setDeployOrder(int deployOrder) { + this.deployOrder = deployOrder; + } + + public Object elConvertType(Object value) { + return value; + } + + public Object elGetReference(Object bean) { + return bean; + } + + public Object elGetValue(Object bean) { + return prop.getValue(bean); + } + + public void elSetReference(Object bean) { + // prop.setValue(bean, value) + } + + public void elSetValue(Object bean, Object value, boolean populate, boolean reference) { + prop.setValue(bean, value); + } + + public int getDeployOrder() { + return deployOrder; + } + + public String getAssocOneIdExpr(String prefix, String operator) { + throw new RuntimeException("Not Supported or Expected"); + } + + public Object[] getAssocOneIdValues(Object bean) { + throw new RuntimeException("Not Supported or Expected"); + } + + public String getAssocIdInExpr(String prefix) { + throw new RuntimeException("Not Supported or Expected"); + } + + public String getAssocIdInValueExpr(int size) { + throw new RuntimeException("Not Supported or Expected"); + } + + public BeanProperty getBeanProperty() { + return null; + } + + public StringFormatter getStringFormatter() { + return null; + } + + public StringParser getStringParser() { + return null; + } + + public boolean isDbEncrypted() { + return false; + } + + public boolean isLocalEncrypted() { + return false; + } + + public boolean isAssocId() { + return false; + } + + public boolean isAssocProperty() { + return false; + } + + public boolean isDateTimeCapable() { + return false; + } + + public int getJdbcType() { + return 0; + } + + public Object parseDateTime(long systemTimeMillis) { + throw new RuntimeException("Not Supported or Expected"); + } + + public boolean containsMany() { + return false; + } + + public boolean containsManySince(String sinceProperty) { + return containsMany(); + } + + public String getDbColumn() { + return null; + } + + public String getElPlaceholder(boolean encrypted) { + return null; + } + + public String getElPrefix() { + return null; + } + + public String getName() { + return prop.getPropertyName(); + } + + public String getElName() { + return prop.getPropertyName(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundType.java b/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundType.java index ba6547d19..a929f2e35 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundType.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundType.java @@ -1,281 +1,262 @@ -/** - * 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.type; - -import java.sql.SQLException; -import java.util.LinkedHashMap; -import java.util.Map; - -import com.avaje.ebean.config.CompoundType; -import com.avaje.ebean.config.CompoundTypeProperty; -import com.avaje.ebean.text.json.JsonElement; -import com.avaje.ebean.text.json.JsonElementObject; -import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; -import com.avaje.ebeaninternal.server.text.json.WriteJsonContext.WriteBeanState; - -/** - * The internal representation of a Compound Type (Immutable Compound Value - * Object). - * - * @author rbygrave - * - * @param - * The Type of the "Immutable Compound Value Object". - */ -public final class CtCompoundType implements ScalarDataReader { - - private final Class cvoClass; - - private final CompoundType cvoType; - - private final Map> propertyMap; - - private final ScalarDataReader[] propReaders; - - private final CompoundTypeProperty[] properties; - - public CtCompoundType(Class cvoClass, CompoundType cvoType, ScalarDataReader[] propReaders) { - - this.cvoClass = cvoClass; - this.cvoType = cvoType; - this.properties = cvoType.getProperties(); - this.propReaders = propReaders; - - this.propertyMap = new LinkedHashMap>(); - for (CompoundTypeProperty cp: properties) { - propertyMap.put(cp.getName(), cp); - } - } - - public String toString() { - return cvoClass.toString(); - } - - public Class getCompoundTypeClass() { - return cvoClass; - } - - public V create(Object[] propertyValues) { - return cvoType.create(propertyValues); - } - - - public V create(Map valueMap) { - - if (valueMap.size() != properties.length) { - // not enough elements in the map - return null; - } - - // we expect the map to contain a value for - // each property and that the values are the - // correct type - Object[] propertyValues = new Object[properties.length]; - for (int i = 0; i < properties.length; i++) { - propertyValues[i] = valueMap.get(properties[i].getName()); - if (propertyValues[i] == null) { - String m = "Null value for " + properties[i].getName() + " in map " + valueMap; - throw new RuntimeException(m); - } - } - - return create(propertyValues); - } - - public CompoundTypeProperty[] getProperties() { - - return cvoType.getProperties(); - } - - public Object[] getPropertyValues(V valueObject) { - - Object[] values = new Object[properties.length]; - for (int i = 0; i < properties.length; i++) { - values[i] = properties[i].getValue(valueObject); - } - return values; - } - - public V read(DataReader source) throws SQLException { - - boolean nullValue = false; - Object[] values = new Object[propReaders.length]; - - for (int i = 0; i < propReaders.length; i++) { - Object o = propReaders[i].read(source); - values[i] = o; - if (o == null){ - nullValue = true; - } - } - - if (nullValue){ - return null; - } - - return create(values); - } - - public void loadIgnore(DataReader dataReader) { - for (int i = 0; i < propReaders.length; i++) { - propReaders[i].loadIgnore(dataReader); - } - } - - public void bind(DataBind b, V value) throws SQLException { - - CompoundTypeProperty[] props = cvoType.getProperties(); - - for (int i = 0; i < props.length; i++) { - Object o = props[i].getValue(value); - propReaders[i].bind(b, o); - } - } - - /** - * Recursively accumulate all the scalar types (in depth first order). - *

      - * This creates a flat list of scalars even when compound types are embedded - * inside compound types. - *

      - */ - public void accumulateScalarTypes(String parent, CtCompoundTypeScalarList list) { - - CompoundTypeProperty[] props = cvoType.getProperties(); - - for (int i = 0; i < propReaders.length; i++) { - String propName = getFullPropName(parent, props[i].getName()); - - list.addCompoundProperty(propName, this, props[i]); - - propReaders[i].accumulateScalarTypes(propName, list); - } - - } - - /** - * Return the full property name (for compound types embedded in other - * compound types). - * - * @param parent - * the parent property name - * @param propName - * the local property name - */ - private String getFullPropName(String parent, String propName) { - if (parent == null) { - return propName; - } else { - return parent + "." + propName; - } - } - - public Object jsonRead(ReadJsonContext ctx) { - - if (!ctx.readObjectBegin()) { - // the object is null - return null; - } - - JsonElementObject jsonObject = new JsonElementObject(); - do { - if (!ctx.readKeyNext()){ - break; - } else { - // we read a property key ... - String propName = ctx.getTokenKey(); - JsonElement unmappedJson = ctx.readUnmappedJson(propName); - jsonObject.put(propName, unmappedJson); - - if (!ctx.readValueNext()){ - break; - } - } - } while(true); - - return readJsonElementObject(ctx, jsonObject); - } - - private Object readJsonElementObject(ReadJsonContext ctx, JsonElementObject jsonObject){ - - boolean nullValue = false; - Object[] values = new Object[propReaders.length]; - - for (int i = 0; i < propReaders.length; i++) { - String propName = properties[i].getName(); - JsonElement jsonElement = jsonObject.get(propName); - - if (propReaders[i] instanceof CtCompoundType) { - values[i] = ((CtCompoundType)propReaders[i]).readJsonElementObject(ctx, (JsonElementObject)jsonElement); - - } else { - values[i] = ((ScalarType)propReaders[i]).jsonFromString(jsonElement.toPrimitiveString(), ctx.getValueAdapter()); - } - if (values[i] == null){ - nullValue = true; - } - } - - if (nullValue){ - return null; - } - - return create(values); - } - - - public void jsonWrite(WriteJsonContext ctx, Object valueObject, String propertyName) { - - if (valueObject == null){ - ctx.beginAssocOneIsNull(propertyName); - - } else { - ctx.pushParentBean(valueObject); - ctx.beginAssocOne(propertyName); - jsonWriteProps(ctx, valueObject, propertyName); - ctx.endAssocOne(); - ctx.popParentBean(); - } - } - - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private void jsonWriteProps(WriteJsonContext ctx, Object valueObject, String propertyName) { - - ctx.appendObjectBegin(); - WriteBeanState prevState = ctx.pushBeanState(valueObject); - - for (int i = 0; i < properties.length; i++) { - String propName = properties[i].getName(); - Object value = properties[i].getValue((V)valueObject); - if (propReaders[i] instanceof CtCompoundType) { - ((CtCompoundType)propReaders[i]).jsonWrite(ctx, value, propName); - - } else { - ctx.appendNameValue(propName, (ScalarType)propReaders[i], value); - } - } - - ctx.pushPreviousState(prevState); - ctx.appendObjectEnd(); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.SQLException; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.avaje.ebean.config.CompoundType; +import com.avaje.ebean.config.CompoundTypeProperty; +import com.avaje.ebean.text.json.JsonElement; +import com.avaje.ebean.text.json.JsonElementObject; +import com.avaje.ebeaninternal.server.text.json.ReadJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJsonContext; +import com.avaje.ebeaninternal.server.text.json.WriteJsonContext.WriteBeanState; + +/** + * The internal representation of a Compound Type (Immutable Compound Value + * Object). + * + * @author rbygrave + * + * @param + * The Type of the "Immutable Compound Value Object". + */ +public final class CtCompoundType implements ScalarDataReader { + + private final Class cvoClass; + + private final CompoundType cvoType; + + private final Map> propertyMap; + + private final ScalarDataReader[] propReaders; + + private final CompoundTypeProperty[] properties; + + public CtCompoundType(Class cvoClass, CompoundType cvoType, ScalarDataReader[] propReaders) { + + this.cvoClass = cvoClass; + this.cvoType = cvoType; + this.properties = cvoType.getProperties(); + this.propReaders = propReaders; + + this.propertyMap = new LinkedHashMap>(); + for (CompoundTypeProperty cp: properties) { + propertyMap.put(cp.getName(), cp); + } + } + + public String toString() { + return cvoClass.toString(); + } + + public Class getCompoundTypeClass() { + return cvoClass; + } + + public V create(Object[] propertyValues) { + return cvoType.create(propertyValues); + } + + + public V create(Map valueMap) { + + if (valueMap.size() != properties.length) { + // not enough elements in the map + return null; + } + + // we expect the map to contain a value for + // each property and that the values are the + // correct type + Object[] propertyValues = new Object[properties.length]; + for (int i = 0; i < properties.length; i++) { + propertyValues[i] = valueMap.get(properties[i].getName()); + if (propertyValues[i] == null) { + String m = "Null value for " + properties[i].getName() + " in map " + valueMap; + throw new RuntimeException(m); + } + } + + return create(propertyValues); + } + + public CompoundTypeProperty[] getProperties() { + + return cvoType.getProperties(); + } + + public Object[] getPropertyValues(V valueObject) { + + Object[] values = new Object[properties.length]; + for (int i = 0; i < properties.length; i++) { + values[i] = properties[i].getValue(valueObject); + } + return values; + } + + public V read(DataReader source) throws SQLException { + + boolean nullValue = false; + Object[] values = new Object[propReaders.length]; + + for (int i = 0; i < propReaders.length; i++) { + Object o = propReaders[i].read(source); + values[i] = o; + if (o == null){ + nullValue = true; + } + } + + if (nullValue){ + return null; + } + + return create(values); + } + + public void loadIgnore(DataReader dataReader) { + for (int i = 0; i < propReaders.length; i++) { + propReaders[i].loadIgnore(dataReader); + } + } + + public void bind(DataBind b, V value) throws SQLException { + + CompoundTypeProperty[] props = cvoType.getProperties(); + + for (int i = 0; i < props.length; i++) { + Object o = props[i].getValue(value); + propReaders[i].bind(b, o); + } + } + + /** + * Recursively accumulate all the scalar types (in depth first order). + *

      + * This creates a flat list of scalars even when compound types are embedded + * inside compound types. + *

      + */ + public void accumulateScalarTypes(String parent, CtCompoundTypeScalarList list) { + + CompoundTypeProperty[] props = cvoType.getProperties(); + + for (int i = 0; i < propReaders.length; i++) { + String propName = getFullPropName(parent, props[i].getName()); + + list.addCompoundProperty(propName, this, props[i]); + + propReaders[i].accumulateScalarTypes(propName, list); + } + + } + + /** + * Return the full property name (for compound types embedded in other + * compound types). + * + * @param parent + * the parent property name + * @param propName + * the local property name + */ + private String getFullPropName(String parent, String propName) { + if (parent == null) { + return propName; + } else { + return parent + "." + propName; + } + } + + public Object jsonRead(ReadJsonContext ctx) { + + if (!ctx.readObjectBegin()) { + // the object is null + return null; + } + + JsonElementObject jsonObject = new JsonElementObject(); + do { + if (!ctx.readKeyNext()){ + break; + } else { + // we read a property key ... + String propName = ctx.getTokenKey(); + JsonElement unmappedJson = ctx.readUnmappedJson(propName); + jsonObject.put(propName, unmappedJson); + + if (!ctx.readValueNext()){ + break; + } + } + } while(true); + + return readJsonElementObject(ctx, jsonObject); + } + + private Object readJsonElementObject(ReadJsonContext ctx, JsonElementObject jsonObject){ + + boolean nullValue = false; + Object[] values = new Object[propReaders.length]; + + for (int i = 0; i < propReaders.length; i++) { + String propName = properties[i].getName(); + JsonElement jsonElement = jsonObject.get(propName); + + if (propReaders[i] instanceof CtCompoundType) { + values[i] = ((CtCompoundType)propReaders[i]).readJsonElementObject(ctx, (JsonElementObject)jsonElement); + + } else { + values[i] = ((ScalarType)propReaders[i]).jsonFromString(jsonElement.toPrimitiveString(), ctx.getValueAdapter()); + } + if (values[i] == null){ + nullValue = true; + } + } + + if (nullValue){ + return null; + } + + return create(values); + } + + + public void jsonWrite(WriteJsonContext ctx, Object valueObject, String propertyName) { + + if (valueObject == null){ + ctx.beginAssocOneIsNull(propertyName); + + } else { + ctx.pushParentBean(valueObject); + ctx.beginAssocOne(propertyName); + jsonWriteProps(ctx, valueObject, propertyName); + ctx.endAssocOne(); + ctx.popParentBean(); + } + } + + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void jsonWriteProps(WriteJsonContext ctx, Object valueObject, String propertyName) { + + ctx.appendObjectBegin(); + WriteBeanState prevState = ctx.pushBeanState(valueObject); + + for (int i = 0; i < properties.length; i++) { + String propName = properties[i].getName(); + Object value = properties[i].getValue((V)valueObject); + if (propReaders[i] instanceof CtCompoundType) { + ((CtCompoundType)propReaders[i]).jsonWrite(ctx, value, propName); + + } else { + ctx.appendNameValue(propName, (ScalarType)propReaders[i], value); + } + } + + ctx.pushPreviousState(prevState); + ctx.appendObjectEnd(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundTypeScalarList.java b/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundTypeScalarList.java index 6125483de..88d4aa943 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundTypeScalarList.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/CtCompoundTypeScalarList.java @@ -1,88 +1,69 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.type; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Set; -import java.util.Map.Entry; - -import com.avaje.ebean.config.CompoundTypeProperty; -import com.avaje.ebeaninternal.server.query.SplitName; - -/** - * Used to build a flat list of all the scalar types nested in a compound type. - * - * @author rbygrave - */ -public final class CtCompoundTypeScalarList { - - private final LinkedHashMap> scalarProps = new LinkedHashMap>(); - - private final LinkedHashMap compoundProperties = new LinkedHashMap(); - - /** - * Return the list of non-scalar properties. These occur when compound types are nested. - */ - public List getNonScalarProperties() { - - List nonScalarProps = new ArrayList(); - - for (String propKey: compoundProperties.keySet()) { - if (!scalarProps.containsKey(propKey)){ - nonScalarProps.add(compoundProperties.get(propKey)); - } - } - - return nonScalarProps; - } - - /** - * Register a property with it's associated compound type and relative name. - */ - public void addCompoundProperty(String propName, CtCompoundType t, CompoundTypeProperty prop) { - - CtCompoundProperty parent = null; - String[] split = SplitName.split(propName); - if (split[0] != null){ - parent = compoundProperties.get(split[0]); - } - - CtCompoundProperty p = new CtCompoundProperty(propName, parent, t, prop); - compoundProperties.put(propName, p); - } - - /** - * Register a scalarType used in the compound type with its given property name. - */ - public void addScalarType(String propName, ScalarType scalar){ - scalarProps.put(propName, scalar); - } - - public CtCompoundProperty getCompoundType(String propName) { - return compoundProperties.get(propName); - } - - public Set>> entries() { - return scalarProps.entrySet(); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Set; +import java.util.Map.Entry; + +import com.avaje.ebean.config.CompoundTypeProperty; +import com.avaje.ebeaninternal.server.query.SplitName; + +/** + * Used to build a flat list of all the scalar types nested in a compound type. + * + * @author rbygrave + */ +public final class CtCompoundTypeScalarList { + + private final LinkedHashMap> scalarProps = new LinkedHashMap>(); + + private final LinkedHashMap compoundProperties = new LinkedHashMap(); + + /** + * Return the list of non-scalar properties. These occur when compound types are nested. + */ + public List getNonScalarProperties() { + + List nonScalarProps = new ArrayList(); + + for (String propKey: compoundProperties.keySet()) { + if (!scalarProps.containsKey(propKey)){ + nonScalarProps.add(compoundProperties.get(propKey)); + } + } + + return nonScalarProps; + } + + /** + * Register a property with it's associated compound type and relative name. + */ + public void addCompoundProperty(String propName, CtCompoundType t, CompoundTypeProperty prop) { + + CtCompoundProperty parent = null; + String[] split = SplitName.split(propName); + if (split[0] != null){ + parent = compoundProperties.get(split[0]); + } + + CtCompoundProperty p = new CtCompoundProperty(propName, parent, t, prop); + compoundProperties.put(propName, p); + } + + /** + * Register a scalarType used in the compound type with its given property name. + */ + public void addScalarType(String propName, ScalarType scalar){ + scalarProps.put(propName, scalar); + } + + public CtCompoundProperty getCompoundType(String propName) { + return compoundProperties.get(propName); + } + + public Set>> entries() { + return scalarProps.entrySet(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/DataBind.java b/src/main/java/com/avaje/ebeaninternal/server/type/DataBind.java index 8f15bdb8b..8897be957 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/DataBind.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/DataBind.java @@ -1,147 +1,128 @@ -/** - * 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.type; - -import java.io.ByteArrayInputStream; -import java.io.Reader; -import java.io.StringReader; -import java.math.BigDecimal; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.Time; -import java.sql.Timestamp; - -public class DataBind { - - private final PreparedStatement pstmt; - - private int pos; - - public DataBind(PreparedStatement pstmt) { - this.pstmt = pstmt; - } - - public void close() throws SQLException { - pstmt.close(); - } - - public int currentPos() { - return pos; - } - - public void resetPos() { - pos = 0; - } - - public void setObject(Object value) throws SQLException { - pstmt.setObject(++pos, value); - } - - public void setObject(Object value, int sqlType) throws SQLException { - pstmt.setObject(++pos, value, sqlType); - } - - public void setNull(int jdbcType) throws SQLException { - pstmt.setNull(++pos, jdbcType); - } - - public int nextPos() { - return ++pos; - } - - public int decrementPos() { - return ++pos; - } - - public int executeUpdate() throws SQLException { - return pstmt.executeUpdate(); - } - - public PreparedStatement getPstmt() { - return pstmt; - } - - public void setString(String s) throws SQLException { - pstmt.setString(++pos, s); - } - - public void setInt(int i) throws SQLException { - pstmt.setInt(++pos, i); - } - - public void setLong(long i) throws SQLException { - pstmt.setLong(++pos, i); - } - - public void setShort(short i) throws SQLException { - pstmt.setShort(++pos, i); - } - - public void setFloat(float i) throws SQLException { - pstmt.setFloat(++pos, i); - } - - public void setDouble(double i) throws SQLException { - pstmt.setDouble(++pos, i); - } - - public void setBigDecimal(BigDecimal v) throws SQLException { - pstmt.setBigDecimal(++pos, v); - } - - public void setDate(java.sql.Date v) throws SQLException { - pstmt.setDate(++pos, v); - } - - public void setTimestamp(Timestamp v) throws SQLException { - pstmt.setTimestamp(++pos, v); - } - - public void setTime(Time v) throws SQLException { - pstmt.setTime(++pos, v); - } - - public void setBoolean(boolean v) throws SQLException { - pstmt.setBoolean(++pos, v); - } - - public void setBytes(byte[] v) throws SQLException { - pstmt.setBytes(++pos, v); - } - - public void setByte(byte v) throws SQLException { - pstmt.setByte(++pos, v); - } - - public void setChar(char v) throws SQLException { - pstmt.setString(++pos, String.valueOf(v)); - } - - public void setBlob(byte[] bytes) throws SQLException { - ByteArrayInputStream is = new ByteArrayInputStream(bytes); - pstmt.setBinaryStream(++pos, is, bytes.length); - } - - public void setClob(String content) throws SQLException { - Reader reader = new StringReader(content); - pstmt.setCharacterStream(++pos, reader, content.length()); - } - +package com.avaje.ebeaninternal.server.type; + +import java.io.ByteArrayInputStream; +import java.io.Reader; +import java.io.StringReader; +import java.math.BigDecimal; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; + +public class DataBind { + + private final PreparedStatement pstmt; + + private int pos; + + public DataBind(PreparedStatement pstmt) { + this.pstmt = pstmt; + } + + public void close() throws SQLException { + pstmt.close(); + } + + public int currentPos() { + return pos; + } + + public void resetPos() { + pos = 0; + } + + public void setObject(Object value) throws SQLException { + pstmt.setObject(++pos, value); + } + + public void setObject(Object value, int sqlType) throws SQLException { + pstmt.setObject(++pos, value, sqlType); + } + + public void setNull(int jdbcType) throws SQLException { + pstmt.setNull(++pos, jdbcType); + } + + public int nextPos() { + return ++pos; + } + + public int decrementPos() { + return ++pos; + } + + public int executeUpdate() throws SQLException { + return pstmt.executeUpdate(); + } + + public PreparedStatement getPstmt() { + return pstmt; + } + + public void setString(String s) throws SQLException { + pstmt.setString(++pos, s); + } + + public void setInt(int i) throws SQLException { + pstmt.setInt(++pos, i); + } + + public void setLong(long i) throws SQLException { + pstmt.setLong(++pos, i); + } + + public void setShort(short i) throws SQLException { + pstmt.setShort(++pos, i); + } + + public void setFloat(float i) throws SQLException { + pstmt.setFloat(++pos, i); + } + + public void setDouble(double i) throws SQLException { + pstmt.setDouble(++pos, i); + } + + public void setBigDecimal(BigDecimal v) throws SQLException { + pstmt.setBigDecimal(++pos, v); + } + + public void setDate(java.sql.Date v) throws SQLException { + pstmt.setDate(++pos, v); + } + + public void setTimestamp(Timestamp v) throws SQLException { + pstmt.setTimestamp(++pos, v); + } + + public void setTime(Time v) throws SQLException { + pstmt.setTime(++pos, v); + } + + public void setBoolean(boolean v) throws SQLException { + pstmt.setBoolean(++pos, v); + } + + public void setBytes(byte[] v) throws SQLException { + pstmt.setBytes(++pos, v); + } + + public void setByte(byte v) throws SQLException { + pstmt.setByte(++pos, v); + } + + public void setChar(char v) throws SQLException { + pstmt.setString(++pos, String.valueOf(v)); + } + + public void setBlob(byte[] bytes) throws SQLException { + ByteArrayInputStream is = new ByteArrayInputStream(bytes); + pstmt.setBinaryStream(++pos, is, bytes.length); + } + + public void setClob(String content) throws SQLException { + Reader reader = new StringReader(content); + pstmt.setCharacterStream(++pos, reader, content.length()); + } + } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/DataEncryptSupport.java b/src/main/java/com/avaje/ebeaninternal/server/type/DataEncryptSupport.java index 8f73faade..65f6da28b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/DataEncryptSupport.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/DataEncryptSupport.java @@ -1,62 +1,43 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.type; - -import com.avaje.ebean.config.Encryptor; -import com.avaje.ebean.config.EncryptKey; -import com.avaje.ebean.config.EncryptKeyManager; - -public class DataEncryptSupport { - - private final EncryptKeyManager encryptKeyManager; - private final Encryptor encryptor; - private final String table; - private final String column; - - public DataEncryptSupport(EncryptKeyManager encryptKeyManager, Encryptor encryptor, String table, String column) { - this.encryptKeyManager = encryptKeyManager; - this.encryptor = encryptor; - this.table = table; - this.column = column; - } - - public byte[] encrypt(byte[] data){ - - EncryptKey key = encryptKeyManager.getEncryptKey(table, column); - return encryptor.encrypt(data, key); - } - - public byte[] decrypt(byte[] data){ - - EncryptKey key = encryptKeyManager.getEncryptKey(table, column); - return encryptor.decrypt(data, key); - } - - public String decryptObject(byte[] data) { - EncryptKey key = encryptKeyManager.getEncryptKey(table, column); - return encryptor.decryptString(data, key); - } - - public byte[] encryptObject(String formattedValue) { - EncryptKey key = encryptKeyManager.getEncryptKey(table, column); - return encryptor.encryptString(formattedValue, key); - } - -} +package com.avaje.ebeaninternal.server.type; + +import com.avaje.ebean.config.Encryptor; +import com.avaje.ebean.config.EncryptKey; +import com.avaje.ebean.config.EncryptKeyManager; + +public class DataEncryptSupport { + + private final EncryptKeyManager encryptKeyManager; + private final Encryptor encryptor; + private final String table; + private final String column; + + public DataEncryptSupport(EncryptKeyManager encryptKeyManager, Encryptor encryptor, String table, String column) { + this.encryptKeyManager = encryptKeyManager; + this.encryptor = encryptor; + this.table = table; + this.column = column; + } + + public byte[] encrypt(byte[] data){ + + EncryptKey key = encryptKeyManager.getEncryptKey(table, column); + return encryptor.encrypt(data, key); + } + + public byte[] decrypt(byte[] data){ + + EncryptKey key = encryptKeyManager.getEncryptKey(table, column); + return encryptor.decrypt(data, key); + } + + public String decryptObject(byte[] data) { + EncryptKey key = encryptKeyManager.getEncryptKey(table, column); + return encryptor.decryptString(data, key); + } + + public byte[] encryptObject(String formattedValue) { + EncryptKey key = encryptKeyManager.getEncryptKey(table, column); + return encryptor.encryptString(formattedValue, key); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/DataReader.java b/src/main/java/com/avaje/ebeaninternal/server/type/DataReader.java index 2950273d4..65fa162fb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/DataReader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/DataReader.java @@ -1,74 +1,55 @@ -/** - * 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.type; - -import java.math.BigDecimal; -import java.sql.Array; -import java.sql.SQLException; - -public interface DataReader { - - public void close() throws SQLException; - - public boolean next() throws SQLException; - - public void resetColumnPosition(); - - public void incrementPos(int increment); - - public byte[] getBinaryBytes() throws SQLException; - - public byte[] getBlobBytes() throws SQLException; - - public String getStringFromStream() throws SQLException; - - public String getStringClob() throws SQLException; - - public String getString() throws SQLException; - - public Boolean getBoolean() throws SQLException; - - public Byte getByte() throws SQLException; - - public Short getShort() throws SQLException; - - public Integer getInt() throws SQLException; - - public Long getLong() throws SQLException; - - public Float getFloat() throws SQLException; - - public Double getDouble() throws SQLException; - - public byte[] getBytes() throws SQLException; - - public java.sql.Date getDate() throws SQLException; - - public java.sql.Time getTime() throws SQLException; - - public java.sql.Timestamp getTimestamp() throws SQLException; - - public BigDecimal getBigDecimal() throws SQLException; - - public Array getArray() throws SQLException; - - public Object getObject() throws SQLException; - -} +package com.avaje.ebeaninternal.server.type; + +import java.math.BigDecimal; +import java.sql.Array; +import java.sql.SQLException; + +public interface DataReader { + + public void close() throws SQLException; + + public boolean next() throws SQLException; + + public void resetColumnPosition(); + + public void incrementPos(int increment); + + public byte[] getBinaryBytes() throws SQLException; + + public byte[] getBlobBytes() throws SQLException; + + public String getStringFromStream() throws SQLException; + + public String getStringClob() throws SQLException; + + public String getString() throws SQLException; + + public Boolean getBoolean() throws SQLException; + + public Byte getByte() throws SQLException; + + public Short getShort() throws SQLException; + + public Integer getInt() throws SQLException; + + public Long getLong() throws SQLException; + + public Float getFloat() throws SQLException; + + public Double getDouble() throws SQLException; + + public byte[] getBytes() throws SQLException; + + public java.sql.Date getDate() throws SQLException; + + public java.sql.Time getTime() throws SQLException; + + public java.sql.Timestamp getTimestamp() throws SQLException; + + public BigDecimal getBigDecimal() throws SQLException; + + public Array getArray() throws SQLException; + + public Object getObject() throws SQLException; + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeFactory.java b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeFactory.java index 085ffb818..dab788a76 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeFactory.java @@ -1,165 +1,146 @@ -/** - * 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.type; - -import java.math.BigInteger; -import java.sql.Types; -import java.util.Calendar; - -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * Helper to create some default ScalarType objects for Booleans, - * java.util.Date, java.util.Calendar etc. - */ -public class DefaultTypeFactory { - - private final ServerConfig serverConfig; - - public DefaultTypeFactory(ServerConfig serverConfig) { - this.serverConfig = serverConfig; - } - - private ScalarType createBoolean(String trueValue, String falseValue) { - - try { - // first try Integer based boolean - Integer intTrue = BasicTypeConverter.toInteger(trueValue); - Integer intFalse = BasicTypeConverter.toInteger(falseValue); - - return new ScalarTypeBoolean.IntBoolean(intTrue, intFalse); - - } catch (NumberFormatException e) { - } - - // treat as Varchar/String based boolean - return new ScalarTypeBoolean.StringBoolean(trueValue, falseValue); - - } - - /** - * Create the ScalarType for mapping Booleans. For some databases this is a - * native data type and for others Booleans will be converted to Y/N or 0/1 - * etc. - */ - public ScalarType createBoolean() { - - if (serverConfig == null) { - return new ScalarTypeBoolean.Native(); - } - String trueValue = serverConfig.getDatabaseBooleanTrue(); - String falseValue = serverConfig.getDatabaseBooleanFalse(); - - if (falseValue != null && trueValue != null) { - // explicit integer or string based booleans - return createBoolean(trueValue, falseValue); - } - - // determine based on database platform configuration - int booleanDbType = serverConfig.getDatabasePlatform().getBooleanDbType(); - - // Some dbs use BIT e.g. MySQL - if (booleanDbType == Types.BIT) { - return new ScalarTypeBoolean.BitBoolean(); - } - - if (booleanDbType == Types.INTEGER) { - return new ScalarTypeBoolean.IntBoolean(1, 0); - } - if (booleanDbType == Types.VARCHAR) { - return new ScalarTypeBoolean.StringBoolean("T", "F"); - } - - if (booleanDbType == Types.BOOLEAN) { - return new ScalarTypeBoolean.Native(); - } - - // assume the JDBC driver can convert the type - return new ScalarTypeBoolean.Native(); - } - - /** - * Create the default ScalarType for java.util.Date. - */ - public ScalarType createUtilDate() { - // by default map anonymous java.util.Date to java.sql.Timestamp. - // String mapType = - // properties.getProperty("type.mapping.java.util.Date","timestamp"); - int utilDateType = getTemporalMapType("timestamp"); - - return createUtilDate(utilDateType); - } - - /** - * Create a ScalarType for java.util.Date explicitly specifying the type to - * map to. - */ - public ScalarType createUtilDate(int utilDateType) { - - switch (utilDateType) { - case Types.DATE: - return new ScalarTypeUtilDate.DateType(); - - case Types.TIMESTAMP: - return new ScalarTypeUtilDate.TimestampType(); - - default: - throw new RuntimeException("Invalid type " + utilDateType); - } - } - - /** - * Create the default ScalarType for java.util.Calendar. - */ - public ScalarType createCalendar() { - // by default map anonymous java.util.Calendar to java.sql.Timestamp. - // String mapType = - // properties.getProperty("type.mapping.java.util.Calendar", - // "timestamp"); - int jdbcType = getTemporalMapType("timestamp"); - - return createCalendar(jdbcType); - } - - /** - * Create a ScalarType for java.util.Calendar explicitly specifying the type - * to map to. - */ - public ScalarType createCalendar(int jdbcType) { - - return new ScalarTypeCalendar(jdbcType); - } - - private int getTemporalMapType(String mapType) { - if (mapType.equalsIgnoreCase("date")) { - return java.sql.Types.DATE; - } - return java.sql.Types.TIMESTAMP; - } - - /** - * Create a ScalarType for java.math.BigInteger. - */ - public ScalarType createMathBigInteger() { - - return new ScalarTypeMathBigInteger(); - } -} +package com.avaje.ebeaninternal.server.type; + +import java.math.BigInteger; +import java.sql.Types; +import java.util.Calendar; + +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * Helper to create some default ScalarType objects for Booleans, + * java.util.Date, java.util.Calendar etc. + */ +public class DefaultTypeFactory { + + private final ServerConfig serverConfig; + + public DefaultTypeFactory(ServerConfig serverConfig) { + this.serverConfig = serverConfig; + } + + private ScalarType createBoolean(String trueValue, String falseValue) { + + try { + // first try Integer based boolean + Integer intTrue = BasicTypeConverter.toInteger(trueValue); + Integer intFalse = BasicTypeConverter.toInteger(falseValue); + + return new ScalarTypeBoolean.IntBoolean(intTrue, intFalse); + + } catch (NumberFormatException e) { + } + + // treat as Varchar/String based boolean + return new ScalarTypeBoolean.StringBoolean(trueValue, falseValue); + + } + + /** + * Create the ScalarType for mapping Booleans. For some databases this is a + * native data type and for others Booleans will be converted to Y/N or 0/1 + * etc. + */ + public ScalarType createBoolean() { + + if (serverConfig == null) { + return new ScalarTypeBoolean.Native(); + } + String trueValue = serverConfig.getDatabaseBooleanTrue(); + String falseValue = serverConfig.getDatabaseBooleanFalse(); + + if (falseValue != null && trueValue != null) { + // explicit integer or string based booleans + return createBoolean(trueValue, falseValue); + } + + // determine based on database platform configuration + int booleanDbType = serverConfig.getDatabasePlatform().getBooleanDbType(); + + // Some dbs use BIT e.g. MySQL + if (booleanDbType == Types.BIT) { + return new ScalarTypeBoolean.BitBoolean(); + } + + if (booleanDbType == Types.INTEGER) { + return new ScalarTypeBoolean.IntBoolean(1, 0); + } + if (booleanDbType == Types.VARCHAR) { + return new ScalarTypeBoolean.StringBoolean("T", "F"); + } + + if (booleanDbType == Types.BOOLEAN) { + return new ScalarTypeBoolean.Native(); + } + + // assume the JDBC driver can convert the type + return new ScalarTypeBoolean.Native(); + } + + /** + * Create the default ScalarType for java.util.Date. + */ + public ScalarType createUtilDate() { + // by default map anonymous java.util.Date to java.sql.Timestamp. + // String mapType = + // properties.getProperty("type.mapping.java.util.Date","timestamp"); + int utilDateType = getTemporalMapType("timestamp"); + + return createUtilDate(utilDateType); + } + + /** + * Create a ScalarType for java.util.Date explicitly specifying the type to + * map to. + */ + public ScalarType createUtilDate(int utilDateType) { + + switch (utilDateType) { + case Types.DATE: + return new ScalarTypeUtilDate.DateType(); + + case Types.TIMESTAMP: + return new ScalarTypeUtilDate.TimestampType(); + + default: + throw new RuntimeException("Invalid type " + utilDateType); + } + } + + /** + * Create the default ScalarType for java.util.Calendar. + */ + public ScalarType createCalendar() { + // by default map anonymous java.util.Calendar to java.sql.Timestamp. + // String mapType = + // properties.getProperty("type.mapping.java.util.Calendar", + // "timestamp"); + int jdbcType = getTemporalMapType("timestamp"); + + return createCalendar(jdbcType); + } + + /** + * Create a ScalarType for java.util.Calendar explicitly specifying the type + * to map to. + */ + public ScalarType createCalendar(int jdbcType) { + + return new ScalarTypeCalendar(jdbcType); + } + + private int getTemporalMapType(String mapType) { + if (mapType.equalsIgnoreCase("date")) { + return java.sql.Types.DATE; + } + return java.sql.Types.TIMESTAMP; + } + + /** + * Create a ScalarType for java.math.BigInteger. + */ + public ScalarType createMathBigInteger() { + + return new ScalarTypeMathBigInteger(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java index 606de24de..742fb4537 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java @@ -1,22 +1,3 @@ -/** - * Copyright (C) 2006 Robin Bygrave - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ package com.avaje.ebeaninternal.server.type; import java.lang.reflect.Field; diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/EnumToDbValueMap.java b/src/main/java/com/avaje/ebeaninternal/server/type/EnumToDbValueMap.java index 35676886d..d481f5025 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/EnumToDbValueMap.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/EnumToDbValueMap.java @@ -1,153 +1,134 @@ -/** - * 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.type; - -import java.sql.SQLException; -import java.util.Iterator; -import java.util.LinkedHashMap; - -/** - * Used to map Bean values to DB values. - *

      - * Useful for building Enum converters where you want to map the DB values an - * Enum gets converter to. - *

      - */ -public abstract class EnumToDbValueMap { - - public static EnumToDbValueMap create(boolean integerType) { - return integerType ? new EnumToDbIntegerMap() : new EnumToDbStringMap(); - } - - final LinkedHashMap keyMap; - - final LinkedHashMap valueMap; - - final boolean allowNulls; - - final boolean isIntegerType; - - /** - * Construct with allowNulls defaulting to false. - */ - public EnumToDbValueMap() { - this(false, false); - } - - /** - * Construct with allowNulls setting. - *

      - * If allowNulls is false then an IllegalArgumentException is thrown by - * either the getDBValue or getBeanValue methods if not matching Bean or DB - * value is found. - *

      - */ - public EnumToDbValueMap(boolean allowNulls, boolean isIntegerType) { - this.allowNulls = allowNulls; - this.isIntegerType = isIntegerType; - keyMap = new LinkedHashMap(); - valueMap = new LinkedHashMap(); - } - - /** - * Return true if this is mapping to integers, false - * if mapping to Strings. - */ - public boolean isIntegerType() { - return isIntegerType; - } - - /** - * Return the DB values. - */ - public Iterator dbValues() { - return valueMap.keySet().iterator(); - } - - /** - * Return the bean 'key' value. - */ - public Iterator beanValues() { - return valueMap.values().iterator(); - } - - /** - * Bind using the correct database type. - */ - public abstract void bind(DataBind b, Object value) throws SQLException; - - /** - * Read using the correct database type. - */ - public abstract Object read(DataReader dataReader) throws SQLException; - - /** - * Return the database type. - */ - public abstract int getDbType(); - - /** - * Add name value pair where the dbValue is the raw string and may need to - * be converted (to an Integer for example). - */ - public abstract EnumToDbValueMap add(Object beanValue, String dbValue); - - /** - * Add a bean value and DB value pair. - *

      - * The dbValue will be converted to an Integer if isIntegerType is true; - *

      - */ - protected void addInternal(Object beanValue, T dbValue) { - - keyMap.put(beanValue, dbValue); - valueMap.put(dbValue, beanValue); - } - - /** - * Return the DB value given the bean value. - */ - public T getDbValue(Object beanValue) { - if (beanValue == null) { - return null; - } - T dbValue = keyMap.get(beanValue); - if (dbValue == null && !allowNulls) { - String msg = "DB value for " + beanValue + " not found in " + valueMap; - throw new IllegalArgumentException(msg); - } - return dbValue; - } - - /** - * Return the Bean value given the DB value. - */ - public Object getBeanValue(T dbValue) { - if (dbValue == null) { - return null; - } - Object beanValue = valueMap.get(dbValue); - if (beanValue == null && !allowNulls) { - String msg = "Bean value for " + dbValue + " not found in " + valueMap; - throw new IllegalArgumentException(msg); - } - return beanValue; - } -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.SQLException; +import java.util.Iterator; +import java.util.LinkedHashMap; + +/** + * Used to map Bean values to DB values. + *

      + * Useful for building Enum converters where you want to map the DB values an + * Enum gets converter to. + *

      + */ +public abstract class EnumToDbValueMap { + + public static EnumToDbValueMap create(boolean integerType) { + return integerType ? new EnumToDbIntegerMap() : new EnumToDbStringMap(); + } + + final LinkedHashMap keyMap; + + final LinkedHashMap valueMap; + + final boolean allowNulls; + + final boolean isIntegerType; + + /** + * Construct with allowNulls defaulting to false. + */ + public EnumToDbValueMap() { + this(false, false); + } + + /** + * Construct with allowNulls setting. + *

      + * If allowNulls is false then an IllegalArgumentException is thrown by + * either the getDBValue or getBeanValue methods if not matching Bean or DB + * value is found. + *

      + */ + public EnumToDbValueMap(boolean allowNulls, boolean isIntegerType) { + this.allowNulls = allowNulls; + this.isIntegerType = isIntegerType; + keyMap = new LinkedHashMap(); + valueMap = new LinkedHashMap(); + } + + /** + * Return true if this is mapping to integers, false + * if mapping to Strings. + */ + public boolean isIntegerType() { + return isIntegerType; + } + + /** + * Return the DB values. + */ + public Iterator dbValues() { + return valueMap.keySet().iterator(); + } + + /** + * Return the bean 'key' value. + */ + public Iterator beanValues() { + return valueMap.values().iterator(); + } + + /** + * Bind using the correct database type. + */ + public abstract void bind(DataBind b, Object value) throws SQLException; + + /** + * Read using the correct database type. + */ + public abstract Object read(DataReader dataReader) throws SQLException; + + /** + * Return the database type. + */ + public abstract int getDbType(); + + /** + * Add name value pair where the dbValue is the raw string and may need to + * be converted (to an Integer for example). + */ + public abstract EnumToDbValueMap add(Object beanValue, String dbValue); + + /** + * Add a bean value and DB value pair. + *

      + * The dbValue will be converted to an Integer if isIntegerType is true; + *

      + */ + protected void addInternal(Object beanValue, T dbValue) { + + keyMap.put(beanValue, dbValue); + valueMap.put(dbValue, beanValue); + } + + /** + * Return the DB value given the bean value. + */ + public T getDbValue(Object beanValue) { + if (beanValue == null) { + return null; + } + T dbValue = keyMap.get(beanValue); + if (dbValue == null && !allowNulls) { + String msg = "DB value for " + beanValue + " not found in " + valueMap; + throw new IllegalArgumentException(msg); + } + return dbValue; + } + + /** + * Return the Bean value given the DB value. + */ + public Object getBeanValue(T dbValue) { + if (dbValue == null) { + return null; + } + Object beanValue = valueMap.get(dbValue); + if (beanValue == null && !allowNulls) { + String msg = "Bean value for " + dbValue + " not found in " + valueMap; + throw new IllegalArgumentException(msg); + } + return beanValue; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/EscapeJson.java b/src/main/java/com/avaje/ebeaninternal/server/type/EscapeJson.java index 01c994ea9..320234777 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/EscapeJson.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/EscapeJson.java @@ -1,130 +1,111 @@ -/** - * 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.type; - -import java.io.IOException; - -import com.avaje.ebean.text.TextException; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; - -public class EscapeJson { - - /** - * Escape and quote the string value. - */ - public static String escapeQuote(String value) { - if (value == null) { - return "null"; - } - - StringBuilder sb = new StringBuilder(value.length() + 2); - sb.append("\""); - escapeAppend(value, sb); - sb.append("\""); - return sb.toString(); - } - - /** - * Escape quotes, \, /, \r, \n, \b, \f, \t and characters (U+0000 through - * U+001F). - */ - public static String escape(String s) { - if (s == null) { - return null; - } - - StringBuilder sb = new StringBuilder(); - escapeAppend(s, sb); - return sb.toString(); - - } - - public static void escape(String value, WriteJsonBuffer sb) { - if (value == null) { - sb.append("null"); - } else { - escapeAppend(value, sb); - } - } - - public static void escapeQuote(String value, WriteJsonBuffer sb) { - if (value == null) { - sb.append("null"); - } else { - sb.append("\""); - escapeAppend(value, sb); - sb.append("\""); - } - } - - /** - * Escape quotes, \, /, \r, \n, \b, \f, \t and characters (U+0000 through - * U+001F). - */ - public static void escapeAppend(String s, Appendable sb) { - - try { - for (int i = 0; i < s.length(); i++) { - char ch = s.charAt(i); - switch (ch) { - case '"': - sb.append("\\\""); - break; - case '\\': - sb.append("\\\\"); - break; - case '\b': - sb.append("\\b"); - break; - case '\f': - sb.append("\\f"); - break; - case '\n': - sb.append("\\n"); - break; - case '\r': - sb.append("\\r"); - break; - case '\t': - sb.append("\\t"); - break; - case '/': - sb.append("\\/"); - break; - default: - if ((ch >= '\u0000' && ch <= '\u001F') || (ch >= '\u007F' && ch <= '\u009F') - || (ch >= '\u2000' && ch <= '\u20FF')) { - - String hs = Integer.toHexString(ch); - sb.append("\\u"); - for (int j = 0; j < 4 - hs.length(); j++) { - sb.append('0'); - } - sb.append(hs.toUpperCase()); - } else { - sb.append(ch); - } - } - } - } catch (IOException e) { - throw new TextException(e); - } - } -} +package com.avaje.ebeaninternal.server.type; + +import java.io.IOException; + +import com.avaje.ebean.text.TextException; +import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; + +public class EscapeJson { + + /** + * Escape and quote the string value. + */ + public static String escapeQuote(String value) { + if (value == null) { + return "null"; + } + + StringBuilder sb = new StringBuilder(value.length() + 2); + sb.append("\""); + escapeAppend(value, sb); + sb.append("\""); + return sb.toString(); + } + + /** + * Escape quotes, \, /, \r, \n, \b, \f, \t and characters (U+0000 through + * U+001F). + */ + public static String escape(String s) { + if (s == null) { + return null; + } + + StringBuilder sb = new StringBuilder(); + escapeAppend(s, sb); + return sb.toString(); + + } + + public static void escape(String value, WriteJsonBuffer sb) { + if (value == null) { + sb.append("null"); + } else { + escapeAppend(value, sb); + } + } + + public static void escapeQuote(String value, WriteJsonBuffer sb) { + if (value == null) { + sb.append("null"); + } else { + sb.append("\""); + escapeAppend(value, sb); + sb.append("\""); + } + } + + /** + * Escape quotes, \, /, \r, \n, \b, \f, \t and characters (U+0000 through + * U+001F). + */ + public static void escapeAppend(String s, Appendable sb) { + + try { + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + switch (ch) { + case '"': + sb.append("\\\""); + break; + case '\\': + sb.append("\\\\"); + break; + case '\b': + sb.append("\\b"); + break; + case '\f': + sb.append("\\f"); + break; + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '\t': + sb.append("\\t"); + break; + case '/': + sb.append("\\/"); + break; + default: + if ((ch >= '\u0000' && ch <= '\u001F') || (ch >= '\u007F' && ch <= '\u009F') + || (ch >= '\u2000' && ch <= '\u20FF')) { + + String hs = Integer.toHexString(ch); + sb.append("\\u"); + for (int j = 0; j < 4 - hs.length(); j++) { + sb.append('0'); + } + sb.append(hs.toUpperCase()); + } else { + sb.append(ch); + } + } + } + } catch (IOException e) { + throw new TextException(e); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ImmutableCompoundTypeBuilder.java b/src/main/java/com/avaje/ebeaninternal/server/type/ImmutableCompoundTypeBuilder.java index 06556fbab..bfb7feed9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ImmutableCompoundTypeBuilder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ImmutableCompoundTypeBuilder.java @@ -1,117 +1,98 @@ -/** - * 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.type; - -import java.util.HashMap; -import java.util.Map; - -/** - * Used to build Immutable Compound Value objects. - *

      - * The individual values are collected for a given type and when they have all - * been collected then the immutable compound value object is created and - * returned. - *

      - * - * @author rbygrave - * - */ -public final class ImmutableCompoundTypeBuilder { - - private static ThreadLocal local = new ThreadLocal() { - protected synchronized ImmutableCompoundTypeBuilder initialValue() { - return new ImmutableCompoundTypeBuilder(); - } - }; - - private Map, Entry> entryMap = new HashMap, Entry>(); - - /** - * Clear the cache of partial compound objects. - */ - public static void clear() { - local.get().entryMap.clear(); - } - - /** - * Set the value for the property of a compound type. - *

      - * If this is the last value required for the compound type then the - * compound type is created and returned, otherwise null is returned (and we - * need more values set). - *

      - */ - public static Object set(CtCompoundType ct, String propName, Object value) { - return local.get().setValue(ct, propName, value); - } - - private Object setValue(CtCompoundType ct, String propName, Object value) { - - Entry e = getEntry(ct); - Object compoundValue = e.set(propName, value); - if (compoundValue != null) { - removeEntry(ct); - } - return compoundValue; - } - - /** - * Once we have built a compound value we remove the entry. - */ - private void removeEntry(CtCompoundType ct) { - entryMap.remove(ct.getCompoundTypeClass()); - } - - /** - * Get the Entry which contains the values collected so far for this type. - */ - private Entry getEntry(CtCompoundType ct) { - Entry e = entryMap.get(ct.getCompoundTypeClass()); - if (e == null) { - e = new Entry(ct); - entryMap.put(ct.getCompoundTypeClass(), e); - } - return e; - } - - /** - * Holds the values collected so far for a given compound type. - */ - private static class Entry { - - private final CtCompoundType ct; - - private final Map valueMap; - - private Entry(CtCompoundType ct) { - this.ct = ct; - this.valueMap = new HashMap(); - } - - private Object set(String propName, Object value) { - // collect the values... - valueMap.put(propName, value); - - // when got all the values this returns the - // compound value, otherwise null - return ct.create(valueMap); - } - } -} +package com.avaje.ebeaninternal.server.type; + +import java.util.HashMap; +import java.util.Map; + +/** + * Used to build Immutable Compound Value objects. + *

      + * The individual values are collected for a given type and when they have all + * been collected then the immutable compound value object is created and + * returned. + *

      + * + * @author rbygrave + * + */ +public final class ImmutableCompoundTypeBuilder { + + private static ThreadLocal local = new ThreadLocal() { + protected synchronized ImmutableCompoundTypeBuilder initialValue() { + return new ImmutableCompoundTypeBuilder(); + } + }; + + private Map, Entry> entryMap = new HashMap, Entry>(); + + /** + * Clear the cache of partial compound objects. + */ + public static void clear() { + local.get().entryMap.clear(); + } + + /** + * Set the value for the property of a compound type. + *

      + * If this is the last value required for the compound type then the + * compound type is created and returned, otherwise null is returned (and we + * need more values set). + *

      + */ + public static Object set(CtCompoundType ct, String propName, Object value) { + return local.get().setValue(ct, propName, value); + } + + private Object setValue(CtCompoundType ct, String propName, Object value) { + + Entry e = getEntry(ct); + Object compoundValue = e.set(propName, value); + if (compoundValue != null) { + removeEntry(ct); + } + return compoundValue; + } + + /** + * Once we have built a compound value we remove the entry. + */ + private void removeEntry(CtCompoundType ct) { + entryMap.remove(ct.getCompoundTypeClass()); + } + + /** + * Get the Entry which contains the values collected so far for this type. + */ + private Entry getEntry(CtCompoundType ct) { + Entry e = entryMap.get(ct.getCompoundTypeClass()); + if (e == null) { + e = new Entry(ct); + entryMap.put(ct.getCompoundTypeClass(), e); + } + return e; + } + + /** + * Holds the values collected so far for a given compound type. + */ + private static class Entry { + + private final CtCompoundType ct; + + private final Map valueMap; + + private Entry(CtCompoundType ct) { + this.ct = ct; + this.valueMap = new HashMap(); + } + + private Object set(String propName, Object value) { + // collect the values... + valueMap.put(propName, value); + + // when got all the values this returns the + // compound value, otherwise null + return ct.create(valueMap); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/LongToTimestampConverter.java b/src/main/java/com/avaje/ebeaninternal/server/type/LongToTimestampConverter.java index 4cf3cc385..745d6f10d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/LongToTimestampConverter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/LongToTimestampConverter.java @@ -1,43 +1,24 @@ -/** - * Copyright (C) 2009 Authors - * - * This file is part of Ebean. - * - * Ebean is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Ebean is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Ebean; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -package com.avaje.ebeaninternal.server.type; - -import java.sql.Timestamp; - -import com.avaje.ebean.config.ScalarTypeConverter; - -public class LongToTimestampConverter implements ScalarTypeConverter{ - - public Long getNullValue() { - return null; - } - - public Timestamp unwrapValue(Long beanType) { - - return new Timestamp(beanType.longValue()); - } - - public Long wrapValue(Timestamp scalarType) { - - return scalarType.getTime(); - } - - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.Timestamp; + +import com.avaje.ebean.config.ScalarTypeConverter; + +public class LongToTimestampConverter implements ScalarTypeConverter{ + + public Long getNullValue() { + return null; + } + + public Timestamp unwrapValue(Long beanType) { + + return new Timestamp(beanType.longValue()); + } + + public Long wrapValue(Timestamp scalarType) { + + return scalarType.getTime(); + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/RsetDataReader.java b/src/main/java/com/avaje/ebeaninternal/server/type/RsetDataReader.java index 87faa97ea..7da2384cd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/RsetDataReader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/RsetDataReader.java @@ -1,262 +1,243 @@ -/** - * 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.type; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.math.BigDecimal; -import java.sql.Array; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.Date; -import java.sql.Ref; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Time; -import java.sql.Timestamp; - -import com.avaje.ebeaninternal.server.core.Message; - -public class RsetDataReader implements DataReader { - - private static final int bufferSize = 512; - - static final int clobBufferSize = 512; - - static final int stringInitialSize = 512; - - private final ResultSet rset; - - protected int pos; - - public RsetDataReader(ResultSet rset) { - this.rset = rset; - } - - public void close() throws SQLException { - rset.close(); - } - - public boolean next() throws SQLException { - return rset.next(); - } - - public void resetColumnPosition() { - pos = 0; - } - - public void incrementPos(int increment){ - pos += increment; - } - - protected int pos() { - return ++pos; - } - - public Array getArray() throws SQLException { - return rset.getArray(pos()); - } - - - public InputStream getAsciiStream() throws SQLException { - return rset.getAsciiStream(pos()); - } - - public Object getObject() throws SQLException { - return rset.getObject(pos()); - } - - public BigDecimal getBigDecimal() throws SQLException { - return rset.getBigDecimal(pos()); - } - - - public InputStream getBinaryStream() throws SQLException { - return rset.getBinaryStream(pos()); - } - - public Boolean getBoolean() throws SQLException { - boolean v = rset.getBoolean(pos()); - if (rset.wasNull()){ - return null; - } - return Boolean.valueOf(v); - } - - public Byte getByte() throws SQLException { - byte v = rset.getByte(pos()); - if (rset.wasNull()){ - return null; - } - return Byte.valueOf(v); - } - - public byte[] getBytes() throws SQLException { - return rset.getBytes(pos()); - } - - public Date getDate() throws SQLException { - return rset.getDate(pos()); - } - - public Double getDouble() throws SQLException { - double v = rset.getDouble(pos()); - if (rset.wasNull()){ - return null; - } - return Double.valueOf(v); - } - - public Float getFloat() throws SQLException { - float v = rset.getFloat(pos()); - if (rset.wasNull()){ - return null; - } - return Float.valueOf(v); - } - - public Integer getInt() throws SQLException { - int v = rset.getInt(pos()); - if (rset.wasNull()){ - return null; - } - return Integer.valueOf(v); - } - - - public Long getLong() throws SQLException { - long v = rset.getLong(pos()); - if (rset.wasNull()){ - return null; - } - return Long.valueOf(v); - } - - - public Ref getRef() throws SQLException { - return rset.getRef(pos()); - } - - - public Short getShort() throws SQLException { - short s = rset.getShort(pos()); - if (rset.wasNull()){ - return null; - } - return Short.valueOf(s); - } - - - public String getString() throws SQLException { - return rset.getString(pos()); - } - - - public Time getTime() throws SQLException { - return rset.getTime(pos()); - } - - - public Timestamp getTimestamp() throws SQLException { - return rset.getTimestamp(pos()); - } - - public String getStringFromStream() throws SQLException { - Reader reader = rset.getCharacterStream(pos()); - if (reader == null) { - return null; - } - return readStringLob(reader); - } - - public String getStringClob() throws SQLException { - - Clob clob = rset.getClob(pos()); - if (clob == null) { - return null; - } - Reader reader = clob.getCharacterStream(); - if (reader == null) { - return null; - } - return readStringLob(reader); - } - - protected String readStringLob(Reader reader) throws SQLException { - - char[] buffer = new char[clobBufferSize]; - int readLength = 0; - StringBuilder out = new StringBuilder(stringInitialSize); - try { - while ((readLength = reader.read(buffer)) != -1) { - out.append(buffer, 0, readLength); - } - reader.close(); - } catch (IOException e) { - throw new SQLException(Message.msg("persist.clob.io", e.getMessage())); - } - - return out.toString(); - } - - public byte[] getBinaryBytes() throws SQLException { - InputStream in = rset.getBinaryStream(pos()); - return getBinaryLob(in); - } - - public byte[] getBlobBytes() throws SQLException { - Blob blob = rset.getBlob(pos()); - if (blob == null) { - return null; - } - InputStream in = blob.getBinaryStream(); - return getBinaryLob(in); - } - - protected byte[] getBinaryLob(InputStream in) throws SQLException { - - try { - if (in == null) { - return null; - } - ByteArrayOutputStream out = new ByteArrayOutputStream(); - - byte[] buf = new byte[bufferSize]; - int len; - while ((len = in.read(buf, 0, buf.length)) != -1) { - out.write(buf, 0, len); - } - byte[] data = out.toByteArray(); - - if (data.length == 0) { - data = null; - } - in.close(); - out.close(); - return data; - - } catch (IOException e) { - throw new SQLException(e.getClass().getName() + ":" + e.getMessage()); - } - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; + +import com.avaje.ebeaninternal.server.core.Message; + +public class RsetDataReader implements DataReader { + + private static final int bufferSize = 512; + + static final int clobBufferSize = 512; + + static final int stringInitialSize = 512; + + private final ResultSet rset; + + protected int pos; + + public RsetDataReader(ResultSet rset) { + this.rset = rset; + } + + public void close() throws SQLException { + rset.close(); + } + + public boolean next() throws SQLException { + return rset.next(); + } + + public void resetColumnPosition() { + pos = 0; + } + + public void incrementPos(int increment){ + pos += increment; + } + + protected int pos() { + return ++pos; + } + + public Array getArray() throws SQLException { + return rset.getArray(pos()); + } + + + public InputStream getAsciiStream() throws SQLException { + return rset.getAsciiStream(pos()); + } + + public Object getObject() throws SQLException { + return rset.getObject(pos()); + } + + public BigDecimal getBigDecimal() throws SQLException { + return rset.getBigDecimal(pos()); + } + + + public InputStream getBinaryStream() throws SQLException { + return rset.getBinaryStream(pos()); + } + + public Boolean getBoolean() throws SQLException { + boolean v = rset.getBoolean(pos()); + if (rset.wasNull()){ + return null; + } + return Boolean.valueOf(v); + } + + public Byte getByte() throws SQLException { + byte v = rset.getByte(pos()); + if (rset.wasNull()){ + return null; + } + return Byte.valueOf(v); + } + + public byte[] getBytes() throws SQLException { + return rset.getBytes(pos()); + } + + public Date getDate() throws SQLException { + return rset.getDate(pos()); + } + + public Double getDouble() throws SQLException { + double v = rset.getDouble(pos()); + if (rset.wasNull()){ + return null; + } + return Double.valueOf(v); + } + + public Float getFloat() throws SQLException { + float v = rset.getFloat(pos()); + if (rset.wasNull()){ + return null; + } + return Float.valueOf(v); + } + + public Integer getInt() throws SQLException { + int v = rset.getInt(pos()); + if (rset.wasNull()){ + return null; + } + return Integer.valueOf(v); + } + + + public Long getLong() throws SQLException { + long v = rset.getLong(pos()); + if (rset.wasNull()){ + return null; + } + return Long.valueOf(v); + } + + + public Ref getRef() throws SQLException { + return rset.getRef(pos()); + } + + + public Short getShort() throws SQLException { + short s = rset.getShort(pos()); + if (rset.wasNull()){ + return null; + } + return Short.valueOf(s); + } + + + public String getString() throws SQLException { + return rset.getString(pos()); + } + + + public Time getTime() throws SQLException { + return rset.getTime(pos()); + } + + + public Timestamp getTimestamp() throws SQLException { + return rset.getTimestamp(pos()); + } + + public String getStringFromStream() throws SQLException { + Reader reader = rset.getCharacterStream(pos()); + if (reader == null) { + return null; + } + return readStringLob(reader); + } + + public String getStringClob() throws SQLException { + + Clob clob = rset.getClob(pos()); + if (clob == null) { + return null; + } + Reader reader = clob.getCharacterStream(); + if (reader == null) { + return null; + } + return readStringLob(reader); + } + + protected String readStringLob(Reader reader) throws SQLException { + + char[] buffer = new char[clobBufferSize]; + int readLength = 0; + StringBuilder out = new StringBuilder(stringInitialSize); + try { + while ((readLength = reader.read(buffer)) != -1) { + out.append(buffer, 0, readLength); + } + reader.close(); + } catch (IOException e) { + throw new SQLException(Message.msg("persist.clob.io", e.getMessage())); + } + + return out.toString(); + } + + public byte[] getBinaryBytes() throws SQLException { + InputStream in = rset.getBinaryStream(pos()); + return getBinaryLob(in); + } + + public byte[] getBlobBytes() throws SQLException { + Blob blob = rset.getBlob(pos()); + if (blob == null) { + return null; + } + InputStream in = blob.getBinaryStream(); + return getBinaryLob(in); + } + + protected byte[] getBinaryLob(InputStream in) throws SQLException { + + try { + if (in == null) { + return null; + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + byte[] buf = new byte[bufferSize]; + int len; + while ((len = in.read(buf, 0, buf.length)) != -1) { + out.write(buf, 0, len); + } + byte[] data = out.toByteArray(); + + if (data.length == 0) { + data = null; + } + in.close(); + out.close(); + return data; + + } catch (IOException e) { + throw new SQLException(e.getClass().getName() + ":" + e.getMessage()); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/RsetDataReaderIndexed.java b/src/main/java/com/avaje/ebeaninternal/server/type/RsetDataReaderIndexed.java index a61768c55..18f500e70 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/RsetDataReaderIndexed.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/RsetDataReaderIndexed.java @@ -1,47 +1,28 @@ -/** - * 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.type; - -import java.sql.ResultSet; - -public class RsetDataReaderIndexed extends RsetDataReader { - - private final int[] rsetIndexPositions; - - public RsetDataReaderIndexed(ResultSet rset, int[] rsetIndexPositions, boolean rowNumberIncluded) { - super(rset); - if (!rowNumberIncluded){ - this.rsetIndexPositions = rsetIndexPositions; - } else { - this.rsetIndexPositions = new int[rsetIndexPositions.length+1]; - for (int i = 0; i < rsetIndexPositions.length; i++) { - // increment all the column indexes by 1 - this.rsetIndexPositions[i+1] = rsetIndexPositions[i]+1; - } - } - } - - @Override - protected int pos() { - int i = pos++; - return rsetIndexPositions[i]; - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.ResultSet; + +public class RsetDataReaderIndexed extends RsetDataReader { + + private final int[] rsetIndexPositions; + + public RsetDataReaderIndexed(ResultSet rset, int[] rsetIndexPositions, boolean rowNumberIncluded) { + super(rset); + if (!rowNumberIncluded){ + this.rsetIndexPositions = rsetIndexPositions; + } else { + this.rsetIndexPositions = new int[rsetIndexPositions.length+1]; + for (int i = 0; i < rsetIndexPositions.length; i++) { + // increment all the column indexes by 1 + this.rsetIndexPositions[i+1] = rsetIndexPositions[i]+1; + } + } + } + + @Override + protected int pos() { + int i = pos++; + return rsetIndexPositions[i]; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalaOptionTypeConverter.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalaOptionTypeConverter.java index 09146b6eb..5491bfb5e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalaOptionTypeConverter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalaOptionTypeConverter.java @@ -1,61 +1,42 @@ -/** - * 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.type; - -import scala.Option; - -import com.avaje.ebean.config.ScalarTypeConverter; - -/** - * A type converter to support scala.Option. - * - * @author rbygrave - * - * @param the underlying type - */ -public class ScalaOptionTypeConverter implements ScalarTypeConverter, S>{ - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Option getNullValue() { - return (scala.Option)scala.None$.MODULE$; - } - - public S unwrapValue(Option beanType) { - - if (beanType.isEmpty()){ - return null; - } else { - return beanType.get(); - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Option wrapValue(S scalarType) { - if (scalarType == null){ - return (scala.Option)scala.None$.MODULE$; - } - if (scalarType instanceof scala.Some){ - return (Option)scalarType; - } - return new scala.Some(scalarType); - } - - -} +package com.avaje.ebeaninternal.server.type; + +import scala.Option; + +import com.avaje.ebean.config.ScalarTypeConverter; + +/** + * A type converter to support scala.Option. + * + * @author rbygrave + * + * @param the underlying type + */ +public class ScalaOptionTypeConverter implements ScalarTypeConverter, S>{ + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Option getNullValue() { + return (scala.Option)scala.None$.MODULE$; + } + + public S unwrapValue(Option beanType) { + + if (beanType.isEmpty()){ + return null; + } else { + return beanType.get(); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Option wrapValue(S scalarType) { + if (scalarType == null){ + return (scala.Option)scala.None$.MODULE$; + } + if (scalarType instanceof scala.Some){ + return (Option)scalarType; + } + return new scala.Some(scalarType); + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarDataReader.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarDataReader.java index f60d1eed2..a0af4aed8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarDataReader.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarDataReader.java @@ -1,51 +1,32 @@ -/** - * 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.type; - -import java.sql.SQLException; - -/** - * Reads from and binds to database columns. - * - * @author rbygrave - */ -public interface ScalarDataReader { - - /** - * Read and return the appropriate value from the dataReader. - */ - public T read(DataReader dataReader) throws SQLException; - - /** - * Ignore typically by moving the index position. - */ - public void loadIgnore(DataReader dataReader); - - /** - * Bind the value to the underlying preparedStatement. - */ - public void bind(DataBind b, T value) throws SQLException; - - /** - * Accumulate all the scalar types used by an immutable compound value type. - */ - public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list); - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.SQLException; + +/** + * Reads from and binds to database columns. + * + * @author rbygrave + */ +public interface ScalarDataReader { + + /** + * Read and return the appropriate value from the dataReader. + */ + public T read(DataReader dataReader) throws SQLException; + + /** + * Ignore typically by moving the index position. + */ + public void loadIgnore(DataReader dataReader); + + /** + * Bind the value to the underlying preparedStatement. + */ + public void bind(DataBind b, T value) throws SQLException; + + /** + * Accumulate all the scalar types used by an immutable compound value type. + */ + public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java index c9986501d..a1d9981a8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarType.java @@ -1,204 +1,185 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; - -import com.avaje.ebean.text.StringFormatter; -import com.avaje.ebean.text.StringParser; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; - -/** - * Describes a scalar type. - *

      - * Scalar in the sense that the types are not compound types. Scalar types only - * map to a single database column. - *

      - *

      - * These types fall into two categories. Types that are mapped natively to JDBC - * types and the rest. Types that map to native JDBC types do not require any - * data type conversion to be persisted to the database. These are java types - * that map via java.sql.Types. - *

      - *

      - * Types that are not native to JDBC require some conversion. These include some - * common java types such as java.util.Date, java.util.Calendar, - * java.math.BigInteger. - *

      - *

      - * Note that Booleans may be native for some databases and require conversion on - * other databases. - *

      - */ -public interface ScalarType extends StringParser, StringFormatter, ScalarDataReader { - - /** - * Return the default DB column length for this type. - *

      - * If a BeanProperty has no explicit length defined then this length should - * be assigned. - *

      - *

      - * This is primarily to support defining a length on Enum types (to - * supplement defining the length on the BeanProperty directly). - *

      - */ - public int getLength(); - - /** - * Return true if the type is native to JDBC. - *

      - * If it is native to JDBC then its values/instances do not need to be - * converted to and from an associated JDBC type. - *

      - */ - public boolean isJdbcNative(); - - /** - * Return the type as per java.sql.Types that this maps to. - *

      - * This type should be consistent with the toJdbcType() method in converting - * the type to the appropriate type for binding to preparedStatements. - *

      - */ - public int getJdbcType(); - - /** - * Return the type that matches the bean property type. - *

      - * This represents the 'logical' type rather than the JDBC type this maps - * to. - *

      - */ - public Class getType(); - - /** - * Read the value from the resultSet and convert if necessary to the logical - * bean property value. - */ - public T read(DataReader dataReader) throws SQLException; - - /** - * Ignore the reading of this value. Typically this means moving the index - * position in the ResultSet. - */ - public void loadIgnore(DataReader dataReader); - - /** - * Convert (if necessary) and bind the value to the preparedStatement. - *

      - * value may need to be converted from the logical bean property type to the - * JDBC type. - *

      - */ - public void bind(DataBind b, T value) throws SQLException; - - /** - * Convert the value as necessary to the JDBC type. - *

      - * Note that this should also match the type as per the getJdbcType() - * method. - *

      - *

      - * This is typically used when the matching type is used in a where clause - * and we use this to ensure it is an appropriate jdbc type. - *

      - */ - public Object toJdbcType(Object value); - - /** - * Convert the value as necessary to the logical Bean type. - *

      - * The type as per the bean property. - *

      - *

      - * This is used to automatically convert id values (typically from a string - * to a int, long or UUID). - *

      - */ - public T toBeanType(Object value); - - /** - * Convert the type into a string representation. - *

      - * Reciprocal of parse(). - *

      - */ - public String formatValue(T v); - - /** - * Convert the type into a string representation. - *

      - * This assumes the value is of the correct type. - *

      - *

      - * This is so that ScalarType also implements the StringFormatter interface. - *

      - */ - public String format(Object v); - - /** - * Convert the string value to the appropriate java object. - *

      - * Mostly used to support CSV, JSON and XML parsing. - *

      - *

      - * Reciprocal of formatValue(). - *

      - */ - public T parse(String value); - - /** - * Convert the systemTimeMillis into the appropriate java object. - *

      - * For non dateTime types this will throw an exception. - *

      - */ - public T parseDateTime(long dateTime); - - /** - * Return true if the type can accept long systemTimeMillis input. - *

      - * This is used to determine if is is sensible to use the - * {@link #parseDateTime(long)} method. - *

      - *

      - * This includes the Date, Calendar, sql Date, Time, Timestamp, JODA types - * as well as Long, BigDecimal and String (although it generally is not - * expected to parse systemTimeMillis to a String or BigDecimal). - *

      - */ - public boolean isDateTimeCapable(); - - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx); - - public String jsonToString(T value, JsonValueAdapter ctx); - - public T jsonFromString(String value, JsonValueAdapter ctx); - - public Object readData(DataInput dataInput) throws IOException; - - public void writeData(DataOutput dataOutput, Object v) throws IOException; - -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; + +import com.avaje.ebean.text.StringFormatter; +import com.avaje.ebean.text.StringParser; +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; + +/** + * Describes a scalar type. + *

      + * Scalar in the sense that the types are not compound types. Scalar types only + * map to a single database column. + *

      + *

      + * These types fall into two categories. Types that are mapped natively to JDBC + * types and the rest. Types that map to native JDBC types do not require any + * data type conversion to be persisted to the database. These are java types + * that map via java.sql.Types. + *

      + *

      + * Types that are not native to JDBC require some conversion. These include some + * common java types such as java.util.Date, java.util.Calendar, + * java.math.BigInteger. + *

      + *

      + * Note that Booleans may be native for some databases and require conversion on + * other databases. + *

      + */ +public interface ScalarType extends StringParser, StringFormatter, ScalarDataReader { + + /** + * Return the default DB column length for this type. + *

      + * If a BeanProperty has no explicit length defined then this length should + * be assigned. + *

      + *

      + * This is primarily to support defining a length on Enum types (to + * supplement defining the length on the BeanProperty directly). + *

      + */ + public int getLength(); + + /** + * Return true if the type is native to JDBC. + *

      + * If it is native to JDBC then its values/instances do not need to be + * converted to and from an associated JDBC type. + *

      + */ + public boolean isJdbcNative(); + + /** + * Return the type as per java.sql.Types that this maps to. + *

      + * This type should be consistent with the toJdbcType() method in converting + * the type to the appropriate type for binding to preparedStatements. + *

      + */ + public int getJdbcType(); + + /** + * Return the type that matches the bean property type. + *

      + * This represents the 'logical' type rather than the JDBC type this maps + * to. + *

      + */ + public Class getType(); + + /** + * Read the value from the resultSet and convert if necessary to the logical + * bean property value. + */ + public T read(DataReader dataReader) throws SQLException; + + /** + * Ignore the reading of this value. Typically this means moving the index + * position in the ResultSet. + */ + public void loadIgnore(DataReader dataReader); + + /** + * Convert (if necessary) and bind the value to the preparedStatement. + *

      + * value may need to be converted from the logical bean property type to the + * JDBC type. + *

      + */ + public void bind(DataBind b, T value) throws SQLException; + + /** + * Convert the value as necessary to the JDBC type. + *

      + * Note that this should also match the type as per the getJdbcType() + * method. + *

      + *

      + * This is typically used when the matching type is used in a where clause + * and we use this to ensure it is an appropriate jdbc type. + *

      + */ + public Object toJdbcType(Object value); + + /** + * Convert the value as necessary to the logical Bean type. + *

      + * The type as per the bean property. + *

      + *

      + * This is used to automatically convert id values (typically from a string + * to a int, long or UUID). + *

      + */ + public T toBeanType(Object value); + + /** + * Convert the type into a string representation. + *

      + * Reciprocal of parse(). + *

      + */ + public String formatValue(T v); + + /** + * Convert the type into a string representation. + *

      + * This assumes the value is of the correct type. + *

      + *

      + * This is so that ScalarType also implements the StringFormatter interface. + *

      + */ + public String format(Object v); + + /** + * Convert the string value to the appropriate java object. + *

      + * Mostly used to support CSV, JSON and XML parsing. + *

      + *

      + * Reciprocal of formatValue(). + *

      + */ + public T parse(String value); + + /** + * Convert the systemTimeMillis into the appropriate java object. + *

      + * For non dateTime types this will throw an exception. + *

      + */ + public T parseDateTime(long dateTime); + + /** + * Return true if the type can accept long systemTimeMillis input. + *

      + * This is used to determine if is is sensible to use the + * {@link #parseDateTime(long)} method. + *

      + *

      + * This includes the Date, Calendar, sql Date, Time, Timestamp, JODA types + * as well as Long, BigDecimal and String (although it generally is not + * expected to parse systemTimeMillis to a String or BigDecimal). + *

      + */ + public boolean isDateTimeCapable(); + + public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx); + + public String jsonToString(T value, JsonValueAdapter ctx); + + public T jsonFromString(String value, JsonValueAdapter ctx); + + public Object readData(DataInput dataInput) throws IOException; + + public void writeData(DataOutput dataOutput, Object v) throws IOException; + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBase.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBase.java index a74940667..096ea6d80 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBase.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBase.java @@ -1,101 +1,82 @@ -/** - * 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.type; - -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; - - - -/** - * Base ScalarType object. - */ -public abstract class ScalarTypeBase implements ScalarType { - - protected final Class type; - protected final boolean jdbcNative; - protected final int jdbcType; - - public ScalarTypeBase(Class type, boolean jdbcNative, int jdbcType) { - this.type = type; - this.jdbcNative = jdbcNative; - this.jdbcType = jdbcType; - } - - /** - * Just return 0. - */ - public int getLength() { - return 0; - } - - public boolean isJdbcNative() { - return jdbcNative; - } - - public int getJdbcType() { - return jdbcType; - } - - public Class getType() { - return type; - } - - @SuppressWarnings("unchecked") - public String format(Object v) { - return formatValue((T)v); - } - - /** - * Return true if the value is null. - */ - public boolean isDbNull(Object value) { - return value == null; - } - - /** - * Returns the value that was passed in. - */ - public Object getDbNullValue(Object value) { - return value; - } - - public void loadIgnore(DataReader dataReader) { - dataReader.incrementPos(1); - } - - public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { - list.addScalarType(propName, this); - } - - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { - String v = jsonToString(value, ctx); - buffer.append(v); - } - - public String jsonToString(T value, JsonValueAdapter ctx) { - return formatValue(value); - } - - public T jsonFromString(String value, JsonValueAdapter ctx) { - return parse(value); - } - -} +package com.avaje.ebeaninternal.server.type; + +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; + + + +/** + * Base ScalarType object. + */ +public abstract class ScalarTypeBase implements ScalarType { + + protected final Class type; + protected final boolean jdbcNative; + protected final int jdbcType; + + public ScalarTypeBase(Class type, boolean jdbcNative, int jdbcType) { + this.type = type; + this.jdbcNative = jdbcNative; + this.jdbcType = jdbcType; + } + + /** + * Just return 0. + */ + public int getLength() { + return 0; + } + + public boolean isJdbcNative() { + return jdbcNative; + } + + public int getJdbcType() { + return jdbcType; + } + + public Class getType() { + return type; + } + + @SuppressWarnings("unchecked") + public String format(Object v) { + return formatValue((T)v); + } + + /** + * Return true if the value is null. + */ + public boolean isDbNull(Object value) { + return value == null; + } + + /** + * Returns the value that was passed in. + */ + public Object getDbNullValue(Object value) { + return value; + } + + public void loadIgnore(DataReader dataReader) { + dataReader.incrementPos(1); + } + + public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { + list.addScalarType(propName, this); + } + + public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { + String v = jsonToString(value, ctx); + buffer.append(v); + } + + public String jsonToString(T value, JsonValueAdapter ctx) { + return formatValue(value); + } + + public T jsonFromString(String value, JsonValueAdapter ctx) { + return parse(value); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java index b936299ed..b11c01c2c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDate.java @@ -1,124 +1,105 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.Date; -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; - -/** - * Base class for Date types. - */ -public abstract class ScalarTypeBaseDate extends ScalarTypeBase { - - public ScalarTypeBaseDate(Class type, boolean jdbcNative, int jdbcType) { - super(type, jdbcNative, jdbcType); - } - - public abstract java.sql.Date convertToDate(T t); - - public abstract T convertFromDate(java.sql.Date ts); - - public void bind(DataBind b, T value) throws SQLException { - if (value == null){ - b.setNull(Types.DATE); - } else { - Date date = convertToDate(value); - b.setDate(date); - } - } - - public T read(DataReader dataReader) throws SQLException { - - Date ts = dataReader.getDate(); - if (ts == null){ - return null; - } else { - return convertFromDate(ts); - } - } - - public String formatValue(T t) { - Date date = convertToDate(t); - return date.toString(); - } - - public T parse(String value) { - Date date = Date.valueOf(value); - return convertFromDate(date); - } - - public T parseDateTime(long systemTimeMillis) { - Date ts = new Date(systemTimeMillis); - return convertFromDate(ts); - } - - public boolean isDateTimeCapable() { - return true; - } - - @Override - public String jsonToString(T value, JsonValueAdapter ctx) { - Date date = convertToDate(value); - return ctx.jsonFromDate(date); - } - - @Override - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { - String s = jsonToString(value, ctx); - buffer.append(s); - } - - @Override - public T jsonFromString(String value, JsonValueAdapter ctx) { - Date ts = ctx.jsonToDate(value); - return convertFromDate(ts); - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - long val = dataInput.readLong(); - Date date = new Date(val); - return convertFromDate(date); - } - } - - @SuppressWarnings("unchecked") - public void writeData(DataOutput dataOutput, Object v) throws IOException { - - T value = (T)v; - if (value == null){ - dataOutput.writeBoolean(false); - } else { - dataOutput.writeBoolean(true); - Date date = convertToDate(value); - dataOutput.writeLong(date.getTime()); - } - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.Date; +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; + +/** + * Base class for Date types. + */ +public abstract class ScalarTypeBaseDate extends ScalarTypeBase { + + public ScalarTypeBaseDate(Class type, boolean jdbcNative, int jdbcType) { + super(type, jdbcNative, jdbcType); + } + + public abstract java.sql.Date convertToDate(T t); + + public abstract T convertFromDate(java.sql.Date ts); + + public void bind(DataBind b, T value) throws SQLException { + if (value == null){ + b.setNull(Types.DATE); + } else { + Date date = convertToDate(value); + b.setDate(date); + } + } + + public T read(DataReader dataReader) throws SQLException { + + Date ts = dataReader.getDate(); + if (ts == null){ + return null; + } else { + return convertFromDate(ts); + } + } + + public String formatValue(T t) { + Date date = convertToDate(t); + return date.toString(); + } + + public T parse(String value) { + Date date = Date.valueOf(value); + return convertFromDate(date); + } + + public T parseDateTime(long systemTimeMillis) { + Date ts = new Date(systemTimeMillis); + return convertFromDate(ts); + } + + public boolean isDateTimeCapable() { + return true; + } + + @Override + public String jsonToString(T value, JsonValueAdapter ctx) { + Date date = convertToDate(value); + return ctx.jsonFromDate(date); + } + + @Override + public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { + String s = jsonToString(value, ctx); + buffer.append(s); + } + + @Override + public T jsonFromString(String value, JsonValueAdapter ctx) { + Date ts = ctx.jsonToDate(value); + return convertFromDate(ts); + } + + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + long val = dataInput.readLong(); + Date date = new Date(val); + return convertFromDate(date); + } + } + + @SuppressWarnings("unchecked") + public void writeData(DataOutput dataOutput, Object v) throws IOException { + + T value = (T)v; + if (value == null){ + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + Date date = convertToDate(value); + dataOutput.writeLong(date.getTime()); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java index 1aebd6098..9c6d3f0e2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseDateTime.java @@ -1,124 +1,105 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.sql.Types; - -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; - -/** - * Base type for DateTime types. - */ -public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { - - public ScalarTypeBaseDateTime(Class type, boolean jdbcNative, int jdbcType) { - super(type, jdbcNative, jdbcType); - } - - public abstract Timestamp convertToTimestamp(T t); - - public abstract T convertFromTimestamp(Timestamp ts); - - public void bind(DataBind b, T value) throws SQLException { - if (value == null){ - b.setNull(Types.TIMESTAMP); - } else { - Timestamp ts = convertToTimestamp(value); - b.setTimestamp(ts); - } - } - - public T read(DataReader dataReader) throws SQLException { - - Timestamp ts = dataReader.getTimestamp(); - if (ts == null){ - return null; - } else { - return convertFromTimestamp(ts); - } - } - - public String formatValue(T t) { - Timestamp ts = convertToTimestamp(t); - return ts.toString(); - } - - public T parse(String value) { - Timestamp ts = Timestamp.valueOf(value); - return convertFromTimestamp(ts); - } - - public T parseDateTime(long systemTimeMillis) { - Timestamp ts = new Timestamp(systemTimeMillis); - return convertFromTimestamp(ts); - } - - public boolean isDateTimeCapable() { - return true; - } - - @Override - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { - String v = jsonToString(value, ctx); - buffer.append(v); - } - - @Override - public String jsonToString(T value, JsonValueAdapter ctx) { - Timestamp ts = convertToTimestamp(value); - return ctx.jsonFromTimestamp(ts); - } - - @Override - public T jsonFromString(String value, JsonValueAdapter ctx) { - Timestamp ts = ctx.jsonToTimestamp(value); - return convertFromTimestamp(ts); - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - long val = dataInput.readLong(); - Timestamp ts = new Timestamp(val); - return convertFromTimestamp(ts); - } - } - - @SuppressWarnings("unchecked") - public void writeData(DataOutput dataOutput, Object v) throws IOException { - - T value = (T)v; - if (value == null){ - dataOutput.writeBoolean(false); - } else { - dataOutput.writeBoolean(true); - Timestamp ts = convertToTimestamp(value); - dataOutput.writeLong(ts.getTime()); - } - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.sql.Types; + +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; + +/** + * Base type for DateTime types. + */ +public abstract class ScalarTypeBaseDateTime extends ScalarTypeBase { + + public ScalarTypeBaseDateTime(Class type, boolean jdbcNative, int jdbcType) { + super(type, jdbcNative, jdbcType); + } + + public abstract Timestamp convertToTimestamp(T t); + + public abstract T convertFromTimestamp(Timestamp ts); + + public void bind(DataBind b, T value) throws SQLException { + if (value == null){ + b.setNull(Types.TIMESTAMP); + } else { + Timestamp ts = convertToTimestamp(value); + b.setTimestamp(ts); + } + } + + public T read(DataReader dataReader) throws SQLException { + + Timestamp ts = dataReader.getTimestamp(); + if (ts == null){ + return null; + } else { + return convertFromTimestamp(ts); + } + } + + public String formatValue(T t) { + Timestamp ts = convertToTimestamp(t); + return ts.toString(); + } + + public T parse(String value) { + Timestamp ts = Timestamp.valueOf(value); + return convertFromTimestamp(ts); + } + + public T parseDateTime(long systemTimeMillis) { + Timestamp ts = new Timestamp(systemTimeMillis); + return convertFromTimestamp(ts); + } + + public boolean isDateTimeCapable() { + return true; + } + + @Override + public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { + String v = jsonToString(value, ctx); + buffer.append(v); + } + + @Override + public String jsonToString(T value, JsonValueAdapter ctx) { + Timestamp ts = convertToTimestamp(value); + return ctx.jsonFromTimestamp(ts); + } + + @Override + public T jsonFromString(String value, JsonValueAdapter ctx) { + Timestamp ts = ctx.jsonToTimestamp(value); + return convertFromTimestamp(ts); + } + + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + long val = dataInput.readLong(); + Timestamp ts = new Timestamp(val); + return convertFromTimestamp(ts); + } + } + + @SuppressWarnings("unchecked") + public void writeData(DataOutput dataOutput, Object v) throws IOException { + + T value = (T)v; + if (value == null){ + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + Timestamp ts = convertToTimestamp(value); + dataOutput.writeLong(ts.getTime()); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java index c0935b847..eb63fbd40 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBaseVarchar.java @@ -1,139 +1,120 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebean.text.TextException; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; - -/** - * Base ScalarType for types which converts to and from a VARCHAR database column. - */ -public abstract class ScalarTypeBaseVarchar extends ScalarTypeBase { - - public ScalarTypeBaseVarchar(Class type) { - super(type, false, Types.VARCHAR); - } - - public ScalarTypeBaseVarchar(Class type, boolean jdbcNative, int jdbcType) { - super(type, jdbcNative, jdbcType); - } - - public abstract String formatValue(T v); - - public abstract T parse(String value); - - public abstract T convertFromDbString(String dbValue); - - public abstract String convertToDbString(T beanValue); - - public void bind(DataBind b, T value) throws SQLException { - if (value == null){ - b.setNull(Types.VARCHAR); - - } else { - String s = convertToDbString(value); - b.setString(s); - } - } - - public T read(DataReader dataReader) throws SQLException { - String s = dataReader.getString(); - if (s == null){ - return null; - } else { - return convertFromDbString(s); - } - } - - @SuppressWarnings("unchecked") - public T toBeanType(Object value) { - if (value == null){ - return null; - } - if (value instanceof String){ - return parse((String)value); - } - return (T)value; - } - - public Object toJdbcType(Object value){ - if (value instanceof String){ - return parse((String)value); - } - return value; - } - - public T parseDateTime(long systemTimeMillis) { - throw new TextException("Not Supported"); - } - - public boolean isDateTimeCapable() { - return false; - } - - @SuppressWarnings("unchecked") - public String format(Object v) { - return formatValue((T) v); - } - - public T jsonFromString(String value, JsonValueAdapter ctx) { - return parse(value); - } - - @Override - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { - String s = format(value); - EscapeJson.escapeQuote(s, buffer); - } - - public String toJsonString(Object value, JsonValueAdapter ctx) { - String s = format(value); - return EscapeJson.escapeQuote(s); - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - String val = dataInput.readUTF(); - return convertFromDbString(val); - } - } - - @SuppressWarnings("unchecked") - public void writeData(DataOutput dataOutput, Object v) throws IOException { - - T value = (T)v; - if (value == null){ - dataOutput.writeBoolean(false); - } else { - dataOutput.writeBoolean(true); - String s = convertToDbString(value); - dataOutput.writeUTF(s); - } - } -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebean.text.TextException; +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; + +/** + * Base ScalarType for types which converts to and from a VARCHAR database column. + */ +public abstract class ScalarTypeBaseVarchar extends ScalarTypeBase { + + public ScalarTypeBaseVarchar(Class type) { + super(type, false, Types.VARCHAR); + } + + public ScalarTypeBaseVarchar(Class type, boolean jdbcNative, int jdbcType) { + super(type, jdbcNative, jdbcType); + } + + public abstract String formatValue(T v); + + public abstract T parse(String value); + + public abstract T convertFromDbString(String dbValue); + + public abstract String convertToDbString(T beanValue); + + public void bind(DataBind b, T value) throws SQLException { + if (value == null){ + b.setNull(Types.VARCHAR); + + } else { + String s = convertToDbString(value); + b.setString(s); + } + } + + public T read(DataReader dataReader) throws SQLException { + String s = dataReader.getString(); + if (s == null){ + return null; + } else { + return convertFromDbString(s); + } + } + + @SuppressWarnings("unchecked") + public T toBeanType(Object value) { + if (value == null){ + return null; + } + if (value instanceof String){ + return parse((String)value); + } + return (T)value; + } + + public Object toJdbcType(Object value){ + if (value instanceof String){ + return parse((String)value); + } + return value; + } + + public T parseDateTime(long systemTimeMillis) { + throw new TextException("Not Supported"); + } + + public boolean isDateTimeCapable() { + return false; + } + + @SuppressWarnings("unchecked") + public String format(Object v) { + return formatValue((T) v); + } + + public T jsonFromString(String value, JsonValueAdapter ctx) { + return parse(value); + } + + @Override + public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { + String s = format(value); + EscapeJson.escapeQuote(s, buffer); + } + + public String toJsonString(Object value, JsonValueAdapter ctx) { + String s = format(value); + return EscapeJson.escapeQuote(s); + } + + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + String val = dataInput.readUTF(); + return convertFromDbString(val); + } + } + + @SuppressWarnings("unchecked") + public void writeData(DataOutput dataOutput, Object v) throws IOException { + + T value = (T)v; + if (value == null){ + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + String s = convertToDbString(value); + dataOutput.writeUTF(s); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java index 8b01b43f6..bc3e1e25a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBigDecimal.java @@ -1,97 +1,78 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.math.BigDecimal; -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for BigDecimal. - */ -public class ScalarTypeBigDecimal extends ScalarTypeBase { - - public ScalarTypeBigDecimal() { - super(BigDecimal.class, true, Types.DECIMAL); - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - double val = dataInput.readDouble(); - return new BigDecimal(val); - } - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - - BigDecimal b = (BigDecimal)v; - if (b == null){ - dataOutput.writeBoolean(false); - } else { - dataOutput.writeBoolean(true); - dataOutput.writeDouble(b.doubleValue()); - } - } - - public void bind(DataBind b, BigDecimal value) throws SQLException { - if (value == null){ - b.setNull(Types.DECIMAL); - } else { - b.setBigDecimal(value); - } - } - - public BigDecimal read(DataReader dataReader) throws SQLException { - - return dataReader.getBigDecimal(); - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.toBigDecimal(value); - } - - public BigDecimal toBeanType(Object value) { - return BasicTypeConverter.toBigDecimal(value); - } - - public String formatValue(BigDecimal t) { - return t.toPlainString(); - } - - public BigDecimal parse(String value) { - return new BigDecimal(value); - } - - public BigDecimal parseDateTime(long systemTimeMillis) { - return BigDecimal.valueOf(systemTimeMillis); - } - - public boolean isDateTimeCapable() { - return true; - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.math.BigDecimal; +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for BigDecimal. + */ +public class ScalarTypeBigDecimal extends ScalarTypeBase { + + public ScalarTypeBigDecimal() { + super(BigDecimal.class, true, Types.DECIMAL); + } + + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + double val = dataInput.readDouble(); + return new BigDecimal(val); + } + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + + BigDecimal b = (BigDecimal)v; + if (b == null){ + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeDouble(b.doubleValue()); + } + } + + public void bind(DataBind b, BigDecimal value) throws SQLException { + if (value == null){ + b.setNull(Types.DECIMAL); + } else { + b.setBigDecimal(value); + } + } + + public BigDecimal read(DataReader dataReader) throws SQLException { + + return dataReader.getBigDecimal(); + } + + public Object toJdbcType(Object value) { + return BasicTypeConverter.toBigDecimal(value); + } + + public BigDecimal toBeanType(Object value) { + return BasicTypeConverter.toBigDecimal(value); + } + + public String formatValue(BigDecimal t) { + return t.toPlainString(); + } + + public BigDecimal parse(String value) { + return new BigDecimal(value); + } + + public BigDecimal parseDateTime(long systemTimeMillis) { + return BigDecimal.valueOf(systemTimeMillis); + } + + public boolean isDateTimeCapable() { + return true; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java index 7ffd809ac..f26d43e21 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBoolean.java @@ -1,310 +1,291 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebean.text.TextException; -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for Boolean and boolean. - *

      - * This may or may not be a native jdbc type depending on the database and jdbc - * driver. - *

      - */ -public class ScalarTypeBoolean { - - public static class Native extends BooleanBase { - - /** - * Native Boolean database type. - */ - public Native() { - super(true, Types.BOOLEAN); - } - - public Boolean toBeanType(Object value) { - return BasicTypeConverter.toBoolean(value); - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.convert(value, jdbcType); - } - - public void bind(DataBind b, Boolean value) throws SQLException { - if (value == null) { - b.setNull(Types.BOOLEAN); - } else { - b.setBoolean(value); - } - - } - - public Boolean read(DataReader dataReader) throws SQLException { - return dataReader.getBoolean(); - } - } - - /** - * The Class BitBoolean converts a JDBC type BIT to a java boolean - * - *

      - * Sometimes booleans may be mapped to the JDBC type BIT. To use - * the BitBoolean specify type.boolean.dbtype="bit" in the ebean configuration - *

      - */ - public static class BitBoolean extends BooleanBase { - - /** - * Native Boolean database type. - */ - public BitBoolean() { - super(true, Types.BIT); - } - - public Boolean toBeanType(Object value) { - return BasicTypeConverter.toBoolean(value); - } - - public Object toJdbcType(Object value) { - // use JDBC driver to convert boolean to bit - return BasicTypeConverter.toBoolean(value); - } - - public void bind(DataBind b, Boolean value) throws SQLException { - if (value == null) { - b.setNull(Types.BIT); - } else { - // use JDBC driver to convert boolean to bit - b.setBoolean(value); - } - } - - public Boolean read(DataReader dataReader) throws SQLException { - return dataReader.getBoolean(); - } - - } - - /** - * Converted to/from an Integer in the Database. - */ - public static class IntBoolean extends BooleanBase { - - private final Integer trueValue; - private final Integer falseValue; - - public IntBoolean(Integer trueValue, Integer falseValue) { - super(false, Types.INTEGER); - this.trueValue = trueValue; - this.falseValue = falseValue; - } - - @Override - public int getLength() { - return 1; - } - - public void bind(DataBind b, Boolean value) throws SQLException { - if (value == null) { - b.setNull(Types.INTEGER); - } else { - b.setInt(toInteger(value)); - } - } - - public Boolean read(DataReader dataReader) throws SQLException { - Integer i = dataReader.getInt(); - if (i == null){ - return null; - } - if (i.equals(trueValue)){ - return Boolean.TRUE; - } else { - return Boolean.FALSE; - } - } - - public Object toJdbcType(Object value) { - return toInteger(value); - } - - /** - * Convert the Boolean value to the db value. - */ - public Integer toInteger(Object value) { - if (value == null) { - return null; - } - Boolean b = (Boolean) value; - if (b.booleanValue()) { - return trueValue; - } else { - return falseValue; - } - } - - /** - * Convert the db value to the Boolean value. - */ - public Boolean toBeanType(Object value) { - if (value == null) { - return null; - } - if (value instanceof Boolean){ - return (Boolean)value; - } - if (trueValue.equals(value)) { - return Boolean.TRUE; - } else { - return Boolean.FALSE; - } - } - - } - - /** - * Converted to/from an Integer in the Database. - */ - public static class StringBoolean extends BooleanBase { - - private final String trueValue; - private final String falseValue; - - public StringBoolean(String trueValue, String falseValue) { - super(false, Types.VARCHAR); - this.trueValue = trueValue; - this.falseValue = falseValue; - } - - @Override - public int getLength() { - // typically this will return 1 - return Math.max(trueValue.length(), falseValue.length()); - } - - public void bind(DataBind b, Boolean value) throws SQLException { - if (value == null) { - b.setNull(Types.VARCHAR); - } else { - b.setString(toString(value)); - } - } - - public Boolean read(DataReader dataReader) throws SQLException { - String string = dataReader.getString(); - if (string == null) { - return null; - } - - if (string.equals(trueValue)){ - return Boolean.TRUE; - } else { - return Boolean.FALSE; - } - } - - public Object toJdbcType(Object value) { - return toString(value); - } - - /** - * Convert the Boolean value to the db value. - */ - public String toString(Object value) { - if (value == null) { - return null; - } - Boolean b = (Boolean) value; - if (b.booleanValue()) { - return trueValue; - } else { - return falseValue; - } - } - - /** - * Convert the db value to the Boolean value. - */ - public Boolean toBeanType(Object value) { - if (value == null) { - return null; - } - if (value instanceof Boolean){ - return (Boolean)value; - } - if (trueValue.equals(value)) { - return Boolean.TRUE; - } else { - return Boolean.FALSE; - } - } - } - - public static abstract class BooleanBase extends ScalarTypeBase { - - public BooleanBase(boolean jdbcNative, int jdbcType) { - super(Boolean.class, jdbcNative, jdbcType); - } - - public String formatValue(Boolean t) { - return t.toString(); - } - - public Boolean parse(String value) { - return Boolean.valueOf(value); - } - - public Boolean parseDateTime(long systemTimeMillis) { - throw new TextException("Not Supported"); - } - - public boolean isDateTimeCapable() { - return false; - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - boolean val = dataInput.readBoolean(); - return Boolean.valueOf(val); - } - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - - Boolean val = (Boolean) v; - if (val == null) { - dataOutput.writeBoolean(false); - } else { - dataOutput.writeBoolean(true); - dataOutput.writeBoolean(val.booleanValue()); - } - } - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebean.text.TextException; +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for Boolean and boolean. + *

      + * This may or may not be a native jdbc type depending on the database and jdbc + * driver. + *

      + */ +public class ScalarTypeBoolean { + + public static class Native extends BooleanBase { + + /** + * Native Boolean database type. + */ + public Native() { + super(true, Types.BOOLEAN); + } + + public Boolean toBeanType(Object value) { + return BasicTypeConverter.toBoolean(value); + } + + public Object toJdbcType(Object value) { + return BasicTypeConverter.convert(value, jdbcType); + } + + public void bind(DataBind b, Boolean value) throws SQLException { + if (value == null) { + b.setNull(Types.BOOLEAN); + } else { + b.setBoolean(value); + } + + } + + public Boolean read(DataReader dataReader) throws SQLException { + return dataReader.getBoolean(); + } + } + + /** + * The Class BitBoolean converts a JDBC type BIT to a java boolean + * + *

      + * Sometimes booleans may be mapped to the JDBC type BIT. To use + * the BitBoolean specify type.boolean.dbtype="bit" in the ebean configuration + *

      + */ + public static class BitBoolean extends BooleanBase { + + /** + * Native Boolean database type. + */ + public BitBoolean() { + super(true, Types.BIT); + } + + public Boolean toBeanType(Object value) { + return BasicTypeConverter.toBoolean(value); + } + + public Object toJdbcType(Object value) { + // use JDBC driver to convert boolean to bit + return BasicTypeConverter.toBoolean(value); + } + + public void bind(DataBind b, Boolean value) throws SQLException { + if (value == null) { + b.setNull(Types.BIT); + } else { + // use JDBC driver to convert boolean to bit + b.setBoolean(value); + } + } + + public Boolean read(DataReader dataReader) throws SQLException { + return dataReader.getBoolean(); + } + + } + + /** + * Converted to/from an Integer in the Database. + */ + public static class IntBoolean extends BooleanBase { + + private final Integer trueValue; + private final Integer falseValue; + + public IntBoolean(Integer trueValue, Integer falseValue) { + super(false, Types.INTEGER); + this.trueValue = trueValue; + this.falseValue = falseValue; + } + + @Override + public int getLength() { + return 1; + } + + public void bind(DataBind b, Boolean value) throws SQLException { + if (value == null) { + b.setNull(Types.INTEGER); + } else { + b.setInt(toInteger(value)); + } + } + + public Boolean read(DataReader dataReader) throws SQLException { + Integer i = dataReader.getInt(); + if (i == null){ + return null; + } + if (i.equals(trueValue)){ + return Boolean.TRUE; + } else { + return Boolean.FALSE; + } + } + + public Object toJdbcType(Object value) { + return toInteger(value); + } + + /** + * Convert the Boolean value to the db value. + */ + public Integer toInteger(Object value) { + if (value == null) { + return null; + } + Boolean b = (Boolean) value; + if (b.booleanValue()) { + return trueValue; + } else { + return falseValue; + } + } + + /** + * Convert the db value to the Boolean value. + */ + public Boolean toBeanType(Object value) { + if (value == null) { + return null; + } + if (value instanceof Boolean){ + return (Boolean)value; + } + if (trueValue.equals(value)) { + return Boolean.TRUE; + } else { + return Boolean.FALSE; + } + } + + } + + /** + * Converted to/from an Integer in the Database. + */ + public static class StringBoolean extends BooleanBase { + + private final String trueValue; + private final String falseValue; + + public StringBoolean(String trueValue, String falseValue) { + super(false, Types.VARCHAR); + this.trueValue = trueValue; + this.falseValue = falseValue; + } + + @Override + public int getLength() { + // typically this will return 1 + return Math.max(trueValue.length(), falseValue.length()); + } + + public void bind(DataBind b, Boolean value) throws SQLException { + if (value == null) { + b.setNull(Types.VARCHAR); + } else { + b.setString(toString(value)); + } + } + + public Boolean read(DataReader dataReader) throws SQLException { + String string = dataReader.getString(); + if (string == null) { + return null; + } + + if (string.equals(trueValue)){ + return Boolean.TRUE; + } else { + return Boolean.FALSE; + } + } + + public Object toJdbcType(Object value) { + return toString(value); + } + + /** + * Convert the Boolean value to the db value. + */ + public String toString(Object value) { + if (value == null) { + return null; + } + Boolean b = (Boolean) value; + if (b.booleanValue()) { + return trueValue; + } else { + return falseValue; + } + } + + /** + * Convert the db value to the Boolean value. + */ + public Boolean toBeanType(Object value) { + if (value == null) { + return null; + } + if (value instanceof Boolean){ + return (Boolean)value; + } + if (trueValue.equals(value)) { + return Boolean.TRUE; + } else { + return Boolean.FALSE; + } + } + } + + public static abstract class BooleanBase extends ScalarTypeBase { + + public BooleanBase(boolean jdbcNative, int jdbcType) { + super(Boolean.class, jdbcNative, jdbcType); + } + + public String formatValue(Boolean t) { + return t.toString(); + } + + public Boolean parse(String value) { + return Boolean.valueOf(value); + } + + public Boolean parseDateTime(long systemTimeMillis) { + throw new TextException("Not Supported"); + } + + public boolean isDateTimeCapable() { + return false; + } + + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + boolean val = dataInput.readBoolean(); + return Boolean.valueOf(val); + } + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + + Boolean val = (Boolean) v; + if (val == null) { + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeBoolean(val.booleanValue()); + } + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java index 5021c0822..502e37b10 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeByte.java @@ -1,97 +1,78 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebean.text.TextException; -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for Byte. - */ -public class ScalarTypeByte extends ScalarTypeBase { - - public ScalarTypeByte() { - super(Byte.class, true,Types.TINYINT ); - } - - public void bind(DataBind b, Byte value) throws SQLException { - if (value == null){ - b.setNull(Types.TINYINT); - } else { - b.setByte(value); - } - } - - public Byte read(DataReader dataReader) throws SQLException { - return dataReader.getByte(); - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.toByte(value); - } - - public Byte toBeanType(Object value) { - return BasicTypeConverter.toByte(value); - } - - - public String formatValue(Byte t) { - return t.toString(); - } - - public Byte parse(String value) { - throw new TextException("Not supported"); - } - - public Byte parseDateTime(long systemTimeMillis) { - throw new TextException("Not Supported"); - } - - public boolean isDateTimeCapable() { - return false; - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - byte val = dataInput.readByte(); - return Byte.valueOf(val); - } - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - - Byte val = (Byte) v; - if (val == null) { - dataOutput.writeBoolean(false); - } else { - dataOutput.writeBoolean(true); - dataOutput.writeByte(val); - } - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebean.text.TextException; +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for Byte. + */ +public class ScalarTypeByte extends ScalarTypeBase { + + public ScalarTypeByte() { + super(Byte.class, true,Types.TINYINT ); + } + + public void bind(DataBind b, Byte value) throws SQLException { + if (value == null){ + b.setNull(Types.TINYINT); + } else { + b.setByte(value); + } + } + + public Byte read(DataReader dataReader) throws SQLException { + return dataReader.getByte(); + } + + public Object toJdbcType(Object value) { + return BasicTypeConverter.toByte(value); + } + + public Byte toBeanType(Object value) { + return BasicTypeConverter.toByte(value); + } + + + public String formatValue(Byte t) { + return t.toString(); + } + + public Byte parse(String value) { + throw new TextException("Not supported"); + } + + public Byte parseDateTime(long systemTimeMillis) { + throw new TextException("Not Supported"); + } + + public boolean isDateTimeCapable() { + return false; + } + + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + byte val = dataInput.readByte(); + return Byte.valueOf(val); + } + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + + Byte val = (Byte) v; + if (val == null) { + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeByte(val); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java index e0944412b..34992b06c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBase.java @@ -1,102 +1,83 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; - -import com.avaje.ebean.text.TextException; - -/** - * Base type for binary types. - */ -public abstract class ScalarTypeBytesBase extends ScalarTypeBase { - - protected ScalarTypeBytesBase(boolean jdbcNative, int jdbcType) { - super(byte[].class, jdbcNative, jdbcType); - } - - public Object convertFromBytes(byte[] bytes) { - return bytes; - } - - public byte[] convertToBytes(Object value) { - return (byte[]) value; - } - - public void bind(DataBind b, byte[] value) throws SQLException { - if (value == null) { - b.setNull(jdbcType); - } else { - b.setBytes(value); - } - } - - public Object toJdbcType(Object value) { - return value; - } - - public byte[] toBeanType(Object value) { - return (byte[])value; - } - - - public String formatValue(byte[] t) { - throw new TextException("Not supported"); - } - - public byte[] parse(String value) { - throw new TextException("Not supported"); - } - - public byte[] parseDateTime(long systemTimeMillis) { - throw new TextException("Not supported"); - } - - public boolean isDateTimeCapable() { - return false; - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - int len = dataInput.readInt(); - byte[] buf = new byte[len]; - dataInput.readFully(buf, 0, buf.length); - return buf; - } - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - if (v == null){ - dataOutput.writeBoolean(false); - } else { - byte[] bytes = convertToBytes(v); - dataOutput.writeInt(bytes.length); - dataOutput.write(bytes); - } - } - - - -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; + +import com.avaje.ebean.text.TextException; + +/** + * Base type for binary types. + */ +public abstract class ScalarTypeBytesBase extends ScalarTypeBase { + + protected ScalarTypeBytesBase(boolean jdbcNative, int jdbcType) { + super(byte[].class, jdbcNative, jdbcType); + } + + public Object convertFromBytes(byte[] bytes) { + return bytes; + } + + public byte[] convertToBytes(Object value) { + return (byte[]) value; + } + + public void bind(DataBind b, byte[] value) throws SQLException { + if (value == null) { + b.setNull(jdbcType); + } else { + b.setBytes(value); + } + } + + public Object toJdbcType(Object value) { + return value; + } + + public byte[] toBeanType(Object value) { + return (byte[])value; + } + + + public String formatValue(byte[] t) { + throw new TextException("Not supported"); + } + + public byte[] parse(String value) { + throw new TextException("Not supported"); + } + + public byte[] parseDateTime(long systemTimeMillis) { + throw new TextException("Not supported"); + } + + public boolean isDateTimeCapable() { + return false; + } + + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + int len = dataInput.readInt(); + byte[] buf = new byte[len]; + dataInput.readFully(buf, 0, buf.length); + return buf; + } + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + if (v == null){ + dataOutput.writeBoolean(false); + } else { + byte[] bytes = convertToBytes(v); + dataOutput.writeInt(bytes.length); + dataOutput.write(bytes); + } + } + + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBinary.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBinary.java index bb43ed412..254361a38 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBinary.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBinary.java @@ -1,38 +1,19 @@ -/** - * 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.type; - -import java.sql.SQLException; -import java.sql.Types; - -/** - * ScalarType for Types.BINARY to byte[]. - */ -public class ScalarTypeBytesBinary extends ScalarTypeBytesBase { - - public ScalarTypeBytesBinary() { - super(true, Types.BINARY); - } - - public byte[] read(DataReader dataReader) throws SQLException { - return dataReader.getBytes(); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.SQLException; +import java.sql.Types; + +/** + * ScalarType for Types.BINARY to byte[]. + */ +public class ScalarTypeBytesBinary extends ScalarTypeBytesBase { + + public ScalarTypeBytesBinary() { + super(true, Types.BINARY); + } + + public byte[] read(DataReader dataReader) throws SQLException { + return dataReader.getBytes(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBlob.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBlob.java index b72d7230a..b6454babf 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBlob.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesBlob.java @@ -1,39 +1,20 @@ -/** - * 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.type; - -import java.sql.SQLException; -import java.sql.Types; - -/** - * ScalarType for BLOB. - */ -public class ScalarTypeBytesBlob extends ScalarTypeBytesBase { - - public ScalarTypeBytesBlob() { - super(true, Types.BLOB); - } - - public byte[] read(DataReader dataReader) throws SQLException { - - return dataReader.getBlobBytes(); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.SQLException; +import java.sql.Types; + +/** + * ScalarType for BLOB. + */ +public class ScalarTypeBytesBlob extends ScalarTypeBytesBase { + + public ScalarTypeBytesBlob() { + super(true, Types.BLOB); + } + + public byte[] read(DataReader dataReader) throws SQLException { + + return dataReader.getBlobBytes(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java index 251b2a5e5..1e69168f0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesEncrypted.java @@ -1,137 +1,118 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; - -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; - -/** - * Encrypted ScalarType that wraps a byte[] types. - * - * @author rbygrave - * - */ -public class ScalarTypeBytesEncrypted implements ScalarType { - - private final ScalarTypeBytesBase baseType; - - private final DataEncryptSupport dataEncryptSupport; - - - public ScalarTypeBytesEncrypted(ScalarTypeBytesBase baseType, DataEncryptSupport dataEncryptSupport) { - this.baseType = baseType; - this.dataEncryptSupport = dataEncryptSupport; - } - - public void bind(DataBind b, byte[] value) throws SQLException { - value = dataEncryptSupport.encrypt(value); - baseType.bind(b, value); - } - - public int getJdbcType() { - return baseType.getJdbcType(); - } - - public int getLength() { - return baseType.getLength(); - } - - public Class getType() { - return byte[].class; - } - - public boolean isDateTimeCapable() { - return baseType.isDateTimeCapable(); - } - - public boolean isJdbcNative() { - return baseType.isJdbcNative(); - } - - public void loadIgnore(DataReader dataReader) { - baseType.loadIgnore(dataReader); - } - - public String format(Object v) { - throw new RuntimeException("Not used"); - } - - public String formatValue(byte[] v) { - throw new RuntimeException("Not used"); - } - - public byte[] parse(String value) { - return baseType.parse(value); - } - - public byte[] parseDateTime(long systemTimeMillis) { - return baseType.parseDateTime(systemTimeMillis); - } - - public byte[] read(DataReader dataReader) throws SQLException { - - byte[] data = baseType.read(dataReader); - data = dataEncryptSupport.decrypt(data); - return data; - } - - public byte[] toBeanType(Object value) { - return baseType.toBeanType(value); - } - - public Object toJdbcType(Object value) { - return baseType.toJdbcType(value); - } - - public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { - baseType.accumulateScalarTypes(propName, list); - } - - public void jsonWrite(WriteJsonBuffer buffer, byte[] value, JsonValueAdapter ctx) { - baseType.jsonWrite(buffer, value, ctx); - } - - public String jsonToString(byte[] value, JsonValueAdapter ctx) { - return baseType.jsonToString(value, ctx); - } - - public byte[] jsonFromString(String value, JsonValueAdapter ctx) { - return baseType.jsonFromString(value, ctx); - } - - public Object readData(DataInput dataInput) throws IOException { - int len = dataInput.readInt(); - byte[] value = new byte[len]; - dataInput.readFully(value); - return value; - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - byte[] value = (byte[])v; - dataOutput.writeInt(value.length); - dataOutput.write(value); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; + +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; + +/** + * Encrypted ScalarType that wraps a byte[] types. + * + * @author rbygrave + * + */ +public class ScalarTypeBytesEncrypted implements ScalarType { + + private final ScalarTypeBytesBase baseType; + + private final DataEncryptSupport dataEncryptSupport; + + + public ScalarTypeBytesEncrypted(ScalarTypeBytesBase baseType, DataEncryptSupport dataEncryptSupport) { + this.baseType = baseType; + this.dataEncryptSupport = dataEncryptSupport; + } + + public void bind(DataBind b, byte[] value) throws SQLException { + value = dataEncryptSupport.encrypt(value); + baseType.bind(b, value); + } + + public int getJdbcType() { + return baseType.getJdbcType(); + } + + public int getLength() { + return baseType.getLength(); + } + + public Class getType() { + return byte[].class; + } + + public boolean isDateTimeCapable() { + return baseType.isDateTimeCapable(); + } + + public boolean isJdbcNative() { + return baseType.isJdbcNative(); + } + + public void loadIgnore(DataReader dataReader) { + baseType.loadIgnore(dataReader); + } + + public String format(Object v) { + throw new RuntimeException("Not used"); + } + + public String formatValue(byte[] v) { + throw new RuntimeException("Not used"); + } + + public byte[] parse(String value) { + return baseType.parse(value); + } + + public byte[] parseDateTime(long systemTimeMillis) { + return baseType.parseDateTime(systemTimeMillis); + } + + public byte[] read(DataReader dataReader) throws SQLException { + + byte[] data = baseType.read(dataReader); + data = dataEncryptSupport.decrypt(data); + return data; + } + + public byte[] toBeanType(Object value) { + return baseType.toBeanType(value); + } + + public Object toJdbcType(Object value) { + return baseType.toJdbcType(value); + } + + public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { + baseType.accumulateScalarTypes(propName, list); + } + + public void jsonWrite(WriteJsonBuffer buffer, byte[] value, JsonValueAdapter ctx) { + baseType.jsonWrite(buffer, value, ctx); + } + + public String jsonToString(byte[] value, JsonValueAdapter ctx) { + return baseType.jsonToString(value, ctx); + } + + public byte[] jsonFromString(String value, JsonValueAdapter ctx) { + return baseType.jsonFromString(value, ctx); + } + + public Object readData(DataInput dataInput) throws IOException { + int len = dataInput.readInt(); + byte[] value = new byte[len]; + dataInput.readFully(value); + return value; + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + byte[] value = (byte[])v; + dataOutput.writeInt(value.length); + dataOutput.write(value); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesLongVarbinary.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesLongVarbinary.java index b5f51452c..ce529a7f8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesLongVarbinary.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesLongVarbinary.java @@ -1,37 +1,18 @@ -/** - * 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.type; - -import java.sql.SQLException; -import java.sql.Types; - -/** - * ScalarType for Longvarbinary. - */ -public class ScalarTypeBytesLongVarbinary extends ScalarTypeBytesBase { - - public ScalarTypeBytesLongVarbinary() { - super(true, Types.LONGVARBINARY); - } - - public byte[] read(DataReader dataReader) throws SQLException { - return dataReader.getBinaryBytes(); - } -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.SQLException; +import java.sql.Types; + +/** + * ScalarType for Longvarbinary. + */ +public class ScalarTypeBytesLongVarbinary extends ScalarTypeBytesBase { + + public ScalarTypeBytesLongVarbinary() { + super(true, Types.LONGVARBINARY); + } + + public byte[] read(DataReader dataReader) throws SQLException { + return dataReader.getBinaryBytes(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesVarbinary.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesVarbinary.java index 80645d2c6..40c9db80c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesVarbinary.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeBytesVarbinary.java @@ -1,38 +1,19 @@ -/** - * 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.type; - -import java.sql.SQLException; -import java.sql.Types; - -/** - * ScalarType for Types.VARBINARY to byte[]. - */ -public class ScalarTypeBytesVarbinary extends ScalarTypeBytesBase { - - public ScalarTypeBytesVarbinary() { - super(true, Types.VARBINARY); - } - - public byte[] read(DataReader dataReader) throws SQLException { - return dataReader.getBytes(); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.SQLException; +import java.sql.Types; + +/** + * ScalarType for Types.VARBINARY to byte[]. + */ +public class ScalarTypeBytesVarbinary extends ScalarTypeBytesBase { + + public ScalarTypeBytesVarbinary() { + super(true, Types.VARBINARY); + } + + public byte[] read(DataReader dataReader) throws SQLException { + return dataReader.getBytes(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java index ee229a5d3..4655f4d93 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCalendar.java @@ -1,74 +1,55 @@ -/** - * 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.type; - -import java.sql.Date; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.sql.Types; -import java.util.Calendar; - -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for java.util.Calendar. - */ -public class ScalarTypeCalendar extends ScalarTypeBaseDateTime { - - public ScalarTypeCalendar(int jdbcType) { - super(Calendar.class, false, jdbcType); - } - - public void bind(DataBind b, Calendar value) throws SQLException { - if (value == null){ - b.setNull(Types.TIMESTAMP); - } else { - Calendar date = (Calendar)value; - if (jdbcType == Types.TIMESTAMP){ - Timestamp timestamp = new Timestamp(date.getTimeInMillis()); - b.setTimestamp(timestamp); - } else { - Date d = new Date(date.getTimeInMillis()); - b.setDate(d); - } - } - } - - @Override - public Calendar convertFromTimestamp(Timestamp ts) { - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(ts.getTime()); - return calendar; - } - - @Override - public Timestamp convertToTimestamp(Calendar t) { - return new Timestamp(t.getTimeInMillis()); - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.convert(value, jdbcType); - } - - public Calendar toBeanType(Object value) { - return BasicTypeConverter.toCalendar(value); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.Date; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Calendar; + +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for java.util.Calendar. + */ +public class ScalarTypeCalendar extends ScalarTypeBaseDateTime { + + public ScalarTypeCalendar(int jdbcType) { + super(Calendar.class, false, jdbcType); + } + + public void bind(DataBind b, Calendar value) throws SQLException { + if (value == null){ + b.setNull(Types.TIMESTAMP); + } else { + Calendar date = (Calendar)value; + if (jdbcType == Types.TIMESTAMP){ + Timestamp timestamp = new Timestamp(date.getTimeInMillis()); + b.setTimestamp(timestamp); + } else { + Date d = new Date(date.getTimeInMillis()); + b.setDate(d); + } + } + } + + @Override + public Calendar convertFromTimestamp(Timestamp ts) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(ts.getTime()); + return calendar; + } + + @Override + public Timestamp convertToTimestamp(Calendar t) { + return new Timestamp(t.getTimeInMillis()); + } + + public Object toJdbcType(Object value) { + return BasicTypeConverter.convert(value, jdbcType); + } + + public Calendar toBeanType(Object value) { + return BasicTypeConverter.toCalendar(value); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeChar.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeChar.java index b74e69b3f..c69474b47 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeChar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeChar.java @@ -1,92 +1,73 @@ -/** - * 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.type; - -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for char. - */ -public class ScalarTypeChar extends ScalarTypeBaseVarchar { - - public ScalarTypeChar() { - super(char.class, false, Types.VARCHAR); - } - - @Override - public Character convertFromDbString(String dbValue) { - return dbValue.charAt(0); - } - - @Override - public String convertToDbString(Character beanValue) { - return beanValue.toString(); - } - - public void bind(DataBind b, Character value) throws SQLException { - if (value == null){ - b.setNull(Types.VARCHAR); - } else { - String s = BasicTypeConverter.toString(value); - b.setString(s); - } - } - - public Character read(DataReader dataReader) throws SQLException { - String string = dataReader.getString(); - if (string == null || string.length()==0){ - return null; - } else { - return string.charAt(0); - } - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.toString(value); - } - - public Character toBeanType(Object value) { - String s = BasicTypeConverter.toString(value); - return s.charAt(0); - } - - public String formatValue(Character t) { - return t.toString(); - } - - public Character parse(String value) { - return value.charAt(0); - } - - @Override - public Character jsonFromString(String value, JsonValueAdapter ctx) { - return value.charAt(0); - } - - @Override - public String jsonToString(Character value, JsonValueAdapter ctx) { - return EscapeJson.escapeQuote(value.toString()); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for char. + */ +public class ScalarTypeChar extends ScalarTypeBaseVarchar { + + public ScalarTypeChar() { + super(char.class, false, Types.VARCHAR); + } + + @Override + public Character convertFromDbString(String dbValue) { + return dbValue.charAt(0); + } + + @Override + public String convertToDbString(Character beanValue) { + return beanValue.toString(); + } + + public void bind(DataBind b, Character value) throws SQLException { + if (value == null){ + b.setNull(Types.VARCHAR); + } else { + String s = BasicTypeConverter.toString(value); + b.setString(s); + } + } + + public Character read(DataReader dataReader) throws SQLException { + String string = dataReader.getString(); + if (string == null || string.length()==0){ + return null; + } else { + return string.charAt(0); + } + } + + public Object toJdbcType(Object value) { + return BasicTypeConverter.toString(value); + } + + public Character toBeanType(Object value) { + String s = BasicTypeConverter.toString(value); + return s.charAt(0); + } + + public String formatValue(Character t) { + return t.toString(); + } + + public Character parse(String value) { + return value.charAt(0); + } + + @Override + public Character jsonFromString(String value, JsonValueAdapter ctx) { + return value.charAt(0); + } + + @Override + public String jsonToString(Character value, JsonValueAdapter ctx) { + return EscapeJson.escapeQuote(value.toString()); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java index 4d96b89e0..83cea2c8c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCharArray.java @@ -1,92 +1,73 @@ -/** - * 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.type; - -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for char[]. - */ -public class ScalarTypeCharArray extends ScalarTypeBaseVarchar{ - - public ScalarTypeCharArray() { - super(char[].class, false, Types.VARCHAR); - } - - @Override - public char[] convertFromDbString(String dbValue) { - return dbValue.toCharArray(); - } - - @Override - public String convertToDbString(char[] beanValue) { - return new String(beanValue); - } - - public void bind(DataBind b, char[] value) throws SQLException { - if (value == null){ - b.setNull(Types.VARCHAR); - } else { - String s = BasicTypeConverter.toString(value); - b.setString(s); - } - } - - public char[] read(DataReader dataReader) throws SQLException { - String string = dataReader.getString(); - if (string == null){ - return null; - } else { - return string.toCharArray(); - } - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.toString(value); - } - - public char[] toBeanType(Object value) { - String s = BasicTypeConverter.toString(value); - return s.toCharArray(); - } - - public String formatValue(char[] t) { - return String.valueOf(t); - } - - public char[] parse(String value) { - return value.toCharArray(); - } - - @Override - public char[] jsonFromString(String value, JsonValueAdapter ctx) { - return value.toCharArray(); - } - - @Override - public String jsonToString(char[] value, JsonValueAdapter ctx) { - return EscapeJson.escapeQuote(String.valueOf(value)); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for char[]. + */ +public class ScalarTypeCharArray extends ScalarTypeBaseVarchar{ + + public ScalarTypeCharArray() { + super(char[].class, false, Types.VARCHAR); + } + + @Override + public char[] convertFromDbString(String dbValue) { + return dbValue.toCharArray(); + } + + @Override + public String convertToDbString(char[] beanValue) { + return new String(beanValue); + } + + public void bind(DataBind b, char[] value) throws SQLException { + if (value == null){ + b.setNull(Types.VARCHAR); + } else { + String s = BasicTypeConverter.toString(value); + b.setString(s); + } + } + + public char[] read(DataReader dataReader) throws SQLException { + String string = dataReader.getString(); + if (string == null){ + return null; + } else { + return string.toCharArray(); + } + } + + public Object toJdbcType(Object value) { + return BasicTypeConverter.toString(value); + } + + public char[] toBeanType(Object value) { + String s = BasicTypeConverter.toString(value); + return s.toCharArray(); + } + + public String formatValue(char[] t) { + return String.valueOf(t); + } + + public char[] parse(String value) { + return value.toCharArray(); + } + + @Override + public char[] jsonFromString(String value, JsonValueAdapter ctx) { + return value.toCharArray(); + } + + @Override + public String jsonToString(char[] value, JsonValueAdapter ctx) { + return EscapeJson.escapeQuote(String.valueOf(value)); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeClass.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeClass.java index f8e55979b..22f0450ae 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeClass.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeClass.java @@ -1,66 +1,47 @@ -/** - * Copyright (C) 2010 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.type; - -import javax.persistence.PersistenceException; - -/** - * ScalarType for Class that persists it to VARCHAR column. - * - * @author emcgreal - * @author rbygrave - */ -@SuppressWarnings({ "rawtypes" }) -public class ScalarTypeClass extends ScalarTypeBaseVarchar { - - public ScalarTypeClass() { - super(Class.class); - } - - @Override - public int getLength() { - return 255; - } - - @Override - public Class convertFromDbString(String dbValue) { - return parse(dbValue); - } - - @Override - public String convertToDbString(Class beanValue) { - return beanValue.getCanonicalName(); - } - - public String formatValue(Class v) { - return v.getCanonicalName(); - } - - public Class parse(String value) { - try { - return Class.forName(value); - } catch (Exception e) { - String msg = "Unable to find Class "+value; - throw new PersistenceException(msg, e); - } - } - - -} +package com.avaje.ebeaninternal.server.type; + +import javax.persistence.PersistenceException; + +/** + * ScalarType for Class that persists it to VARCHAR column. + * + * @author emcgreal + * @author rbygrave + */ +@SuppressWarnings({ "rawtypes" }) +public class ScalarTypeClass extends ScalarTypeBaseVarchar { + + public ScalarTypeClass() { + super(Class.class); + } + + @Override + public int getLength() { + return 255; + } + + @Override + public Class convertFromDbString(String dbValue) { + return parse(dbValue); + } + + @Override + public String convertToDbString(Class beanValue) { + return beanValue.getCanonicalName(); + } + + public String formatValue(Class v) { + return v.getCanonicalName(); + } + + public Class parse(String value) { + try { + return Class.forName(value); + } catch (Exception e) { + String msg = "Unable to find Class "+value; + throw new PersistenceException(msg, e); + } + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeClob.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeClob.java index 8fe791d3b..c545c1a43 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeClob.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeClob.java @@ -1,95 +1,76 @@ -/** - * 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.type; - -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for String. - */ -public class ScalarTypeClob extends ScalarTypeBaseVarchar { - - static final int clobBufferSize = 512; - - static final int stringInitialSize = 512; - - protected ScalarTypeClob(boolean jdbcNative, int jdbcType) { - super(String.class, jdbcNative, jdbcType); - } - - public ScalarTypeClob() { - super(String.class, true, Types.CLOB); - } - - @Override - public String convertFromDbString(String dbValue) { - return dbValue; - } - - @Override - public String convertToDbString(String beanValue) { - return beanValue; - } - - public void bind(DataBind b, String value) throws SQLException { - if (value == null) { - b.setNull(Types.VARCHAR); - } else { - b.setString(value); - } - } - - public String read(DataReader dataReader) throws SQLException { - - return dataReader.getStringClob(); - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.toString(value); - } - - public String toBeanType(Object value) { - return BasicTypeConverter.toString(value); - } - - - public String formatValue(String t) { - return t; - } - - public String parse(String value) { - return value; - } - - @Override - public String jsonFromString(String value, JsonValueAdapter ctx) { - return value; - } - - @Override - public String jsonToString(String value, JsonValueAdapter ctx) { - return EscapeJson.escapeQuote(value); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for String. + */ +public class ScalarTypeClob extends ScalarTypeBaseVarchar { + + static final int clobBufferSize = 512; + + static final int stringInitialSize = 512; + + protected ScalarTypeClob(boolean jdbcNative, int jdbcType) { + super(String.class, jdbcNative, jdbcType); + } + + public ScalarTypeClob() { + super(String.class, true, Types.CLOB); + } + + @Override + public String convertFromDbString(String dbValue) { + return dbValue; + } + + @Override + public String convertToDbString(String beanValue) { + return beanValue; + } + + public void bind(DataBind b, String value) throws SQLException { + if (value == null) { + b.setNull(Types.VARCHAR); + } else { + b.setString(value); + } + } + + public String read(DataReader dataReader) throws SQLException { + + return dataReader.getStringClob(); + } + + public Object toJdbcType(Object value) { + return BasicTypeConverter.toString(value); + } + + public String toBeanType(Object value) { + return BasicTypeConverter.toString(value); + } + + + public String formatValue(String t) { + return t; + } + + public String parse(String value) { + return value; + } + + @Override + public String jsonFromString(String value, JsonValueAdapter ctx) { + return value; + } + + @Override + public String jsonToString(String value, JsonValueAdapter ctx) { + return EscapeJson.escapeQuote(value); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCurrency.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCurrency.java index d20eb1c08..d952a9fe3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCurrency.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeCurrency.java @@ -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.type; - -import java.util.Currency; - -/** - * ScalarType for java.util.Currency which converts to and from a VARCHAR database column. - */ -public class ScalarTypeCurrency extends ScalarTypeBaseVarchar { - - public ScalarTypeCurrency() { - super(Currency.class); - } - - @Override - public int getLength() { - return 3; - } - - @Override - public Currency convertFromDbString(String dbValue) { - return Currency.getInstance(dbValue); - } - - @Override - public String convertToDbString(Currency beanValue) { - return ((Currency)beanValue).getCurrencyCode(); - } - - public String formatValue(Currency v) { - return v.toString(); - } - - public Currency parse(String value) { - return Currency.getInstance(value); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.util.Currency; + +/** + * ScalarType for java.util.Currency which converts to and from a VARCHAR database column. + */ +public class ScalarTypeCurrency extends ScalarTypeBaseVarchar { + + public ScalarTypeCurrency() { + super(Currency.class); + } + + @Override + public int getLength() { + return 3; + } + + @Override + public Currency convertFromDbString(String dbValue) { + return Currency.getInstance(dbValue); + } + + @Override + public String convertToDbString(Currency beanValue) { + return ((Currency)beanValue).getCurrencyCode(); + } + + public String formatValue(Currency v) { + return v.toString(); + } + + public Currency parse(String value) { + return Currency.getInstance(value); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDate.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDate.java index c4c364cab..0da8b8e44 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDate.java @@ -1,67 +1,48 @@ -/** - * 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.type; - -import java.sql.Date; -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for java.sql.Date. - */ -public class ScalarTypeDate extends ScalarTypeBaseDate { - - public ScalarTypeDate() { - super(Date.class, true, Types.DATE); - } - - @Override - public Date convertFromDate(Date date) { - return date; - } - - @Override - public Date convertToDate(Date t) { - return t; - } - - public void bind(DataBind b, java.sql.Date value) throws SQLException { - if (value == null){ - b.setNull(Types.DATE); - } else { - b.setDate(value); - } - } - - public java.sql.Date read(DataReader dataReader) throws SQLException { - return dataReader.getDate(); - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.toDate(value); - } - - public java.sql.Date toBeanType(Object value) { - return BasicTypeConverter.toDate(value); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.Date; +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for java.sql.Date. + */ +public class ScalarTypeDate extends ScalarTypeBaseDate { + + public ScalarTypeDate() { + super(Date.class, true, Types.DATE); + } + + @Override + public Date convertFromDate(Date date) { + return date; + } + + @Override + public Date convertToDate(Date t) { + return t; + } + + public void bind(DataBind b, java.sql.Date value) throws SQLException { + if (value == null){ + b.setNull(Types.DATE); + } else { + b.setDate(value); + } + } + + public java.sql.Date read(DataReader dataReader) throws SQLException { + return dataReader.getDate(); + } + + public Object toJdbcType(Object value) { + return BasicTypeConverter.toDate(value); + } + + public java.sql.Date toBeanType(Object value) { + return BasicTypeConverter.toDate(value); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDouble.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDouble.java index 6f59c59cc..751a84405 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDouble.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeDouble.java @@ -1,104 +1,85 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for Double and double. - */ -public class ScalarTypeDouble extends ScalarTypeBase { - - public ScalarTypeDouble() { - super(Double.class, true, Types.DOUBLE); - } - - public void bind(DataBind b, Double value) throws SQLException { - if (value == null){ - b.setNull(Types.DOUBLE); - } else { - b.setDouble(value.doubleValue()); - } - } - - public Double read(DataReader dataReader) throws SQLException { - - return dataReader.getDouble(); - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.toDouble(value); - } - - public Double toBeanType(Object value) { - return BasicTypeConverter.toDouble(value); - } - - - public String formatValue(Double t) { - return t.toString(); - } - - public Double parse(String value) { - return Double.valueOf(value); - } - - public Double parseDateTime(long systemTimeMillis) { - return Double.valueOf(systemTimeMillis); - } - - public boolean isDateTimeCapable() { - return true; - } - - public String toJsonString(Double value) { - if(value.isInfinite() || value.isNaN()) { - return "null"; - } else { - return value.toString(); - } - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - double val = dataInput.readDouble(); - return Double.valueOf(val); - } - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - - Double value = (Double)v; - if (value == null){ - dataOutput.writeBoolean(false); - } else { - dataOutput.writeBoolean(true); - dataOutput.writeDouble(value.doubleValue()); - } - } -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for Double and double. + */ +public class ScalarTypeDouble extends ScalarTypeBase { + + public ScalarTypeDouble() { + super(Double.class, true, Types.DOUBLE); + } + + public void bind(DataBind b, Double value) throws SQLException { + if (value == null){ + b.setNull(Types.DOUBLE); + } else { + b.setDouble(value.doubleValue()); + } + } + + public Double read(DataReader dataReader) throws SQLException { + + return dataReader.getDouble(); + } + + public Object toJdbcType(Object value) { + return BasicTypeConverter.toDouble(value); + } + + public Double toBeanType(Object value) { + return BasicTypeConverter.toDouble(value); + } + + + public String formatValue(Double t) { + return t.toString(); + } + + public Double parse(String value) { + return Double.valueOf(value); + } + + public Double parseDateTime(long systemTimeMillis) { + return Double.valueOf(systemTimeMillis); + } + + public boolean isDateTimeCapable() { + return true; + } + + public String toJsonString(Double value) { + if(value.isInfinite() || value.isNaN()) { + return "null"; + } else { + return value.toString(); + } + } + + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + double val = dataInput.readDouble(); + return Double.valueOf(val); + } + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + + Double value = (Double)v; + if (value == null){ + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeDouble(value.doubleValue()); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEncryptedWrapper.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEncryptedWrapper.java index 845d5fab7..6ff361ae9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEncryptedWrapper.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEncryptedWrapper.java @@ -1,138 +1,119 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; - -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; - -public class ScalarTypeEncryptedWrapper implements ScalarType { - - private final ScalarType wrapped; - - private final DataEncryptSupport dataEncryptSupport; - - private final ScalarTypeBytesBase byteArrayType; - - public ScalarTypeEncryptedWrapper(ScalarType wrapped, ScalarTypeBytesBase byteArrayType, DataEncryptSupport dataEncryptSupport) { - this.wrapped = wrapped; - this.byteArrayType = byteArrayType; - this.dataEncryptSupport = dataEncryptSupport; - } - - public Object readData(DataInput dataInput) throws IOException { - return wrapped.readData(dataInput); - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - wrapped.writeData(dataOutput, v); - } - - public T read(DataReader dataReader) throws SQLException { - - byte[] data = dataReader.getBytes(); - String formattedValue = dataEncryptSupport.decryptObject(data); - if (formattedValue == null){ - return null; - } - return wrapped.parse(formattedValue); - } - - private byte[] encrypt(T value){ - String formatValue = wrapped.formatValue(value); - return dataEncryptSupport.encryptObject(formatValue); - } - - public void bind(DataBind b, T value) throws SQLException { - - byte[] encryptedValue = encrypt(value); - byteArrayType.bind(b, encryptedValue); - } - - public int getJdbcType() { - return byteArrayType.getJdbcType(); - } - - public int getLength() { - return byteArrayType.getLength(); - } - - public Class getType() { - return wrapped.getType(); - } - - public boolean isDateTimeCapable() { - return wrapped.isDateTimeCapable(); - } - - public boolean isJdbcNative() { - return false; - } - - public void loadIgnore(DataReader dataReader) { - wrapped.loadIgnore(dataReader); - } - - @SuppressWarnings("unchecked") - public String format(Object v) { - return formatValue((T)v); - } - - public String formatValue(T v) { - return wrapped.formatValue(v); - } - - public T parse(String value) { - return wrapped.parse(value); - } - - public T parseDateTime(long systemTimeMillis) { - return wrapped.parseDateTime(systemTimeMillis); - } - - public T toBeanType(Object value) { - return wrapped.toBeanType(value); - } - - public Object toJdbcType(Object value) { - return wrapped.toJdbcType(value); - } - - public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { - wrapped.accumulateScalarTypes(propName, list); - } - - public String jsonToString(T value, JsonValueAdapter ctx) { - return wrapped.jsonToString(value, ctx); - } - - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { - wrapped.jsonWrite(buffer, value, ctx); - } - - public T jsonFromString(String value, JsonValueAdapter ctx) { - return wrapped.jsonFromString(value, ctx); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; + +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; + +public class ScalarTypeEncryptedWrapper implements ScalarType { + + private final ScalarType wrapped; + + private final DataEncryptSupport dataEncryptSupport; + + private final ScalarTypeBytesBase byteArrayType; + + public ScalarTypeEncryptedWrapper(ScalarType wrapped, ScalarTypeBytesBase byteArrayType, DataEncryptSupport dataEncryptSupport) { + this.wrapped = wrapped; + this.byteArrayType = byteArrayType; + this.dataEncryptSupport = dataEncryptSupport; + } + + public Object readData(DataInput dataInput) throws IOException { + return wrapped.readData(dataInput); + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + wrapped.writeData(dataOutput, v); + } + + public T read(DataReader dataReader) throws SQLException { + + byte[] data = dataReader.getBytes(); + String formattedValue = dataEncryptSupport.decryptObject(data); + if (formattedValue == null){ + return null; + } + return wrapped.parse(formattedValue); + } + + private byte[] encrypt(T value){ + String formatValue = wrapped.formatValue(value); + return dataEncryptSupport.encryptObject(formatValue); + } + + public void bind(DataBind b, T value) throws SQLException { + + byte[] encryptedValue = encrypt(value); + byteArrayType.bind(b, encryptedValue); + } + + public int getJdbcType() { + return byteArrayType.getJdbcType(); + } + + public int getLength() { + return byteArrayType.getLength(); + } + + public Class getType() { + return wrapped.getType(); + } + + public boolean isDateTimeCapable() { + return wrapped.isDateTimeCapable(); + } + + public boolean isJdbcNative() { + return false; + } + + public void loadIgnore(DataReader dataReader) { + wrapped.loadIgnore(dataReader); + } + + @SuppressWarnings("unchecked") + public String format(Object v) { + return formatValue((T)v); + } + + public String formatValue(T v) { + return wrapped.formatValue(v); + } + + public T parse(String value) { + return wrapped.parse(value); + } + + public T parseDateTime(long systemTimeMillis) { + return wrapped.parseDateTime(systemTimeMillis); + } + + public T toBeanType(Object value) { + return wrapped.toBeanType(value); + } + + public Object toJdbcType(Object value) { + return wrapped.toJdbcType(value); + } + + public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { + wrapped.accumulateScalarTypes(propName, list); + } + + public String jsonToString(T value, JsonValueAdapter ctx) { + return wrapped.jsonToString(value, ctx); + } + + public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { + wrapped.jsonWrite(buffer, value, ctx); + } + + public T jsonFromString(String value, JsonValueAdapter ctx) { + return wrapped.jsonFromString(value, ctx); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEnumStandard.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEnumStandard.java index 416ee977a..694cdac22 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEnumStandard.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEnumStandard.java @@ -1,281 +1,262 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Types; -import java.util.EnumSet; - -import com.avaje.ebean.text.TextException; -import com.avaje.ebean.text.json.JsonValueAdapter; - - -/** - * JPA standard based Enum scalar type. - *

      - * Converts between bean Enum types to database string or integer columns. - *

      - *

      - * The limitation of this class is that it converts the Enum to either the ordinal - * or string value of the Enum. If you wish to convert the Enum to some other value - * then you should look at the Ebean specific @EnumMapping. - *

      - */ -public class ScalarTypeEnumStandard { - - @SuppressWarnings({"rawtypes","unchecked"}) - public static class StringEnum extends EnumBase implements ScalarTypeEnum { - - private final int length; - - /** - * Create a ScalarTypeEnum. - */ - public StringEnum(Class enumType) { - super(enumType, false, Types.VARCHAR); - this.length = maxValueLength(enumType); - } - - /** - * Return the IN values for DB constraint construction. - */ - public String getContraintInValues(){ - - StringBuilder sb = new StringBuilder(); - - sb.append("("); - Object[] ea = enumType.getEnumConstants(); - for (int i = 0; i < ea.length; i++) { - Enum e = (Enum)ea[i]; - if (i > 0){ - sb.append(","); - } - sb.append("'").append(e.toString()).append("'"); - } - sb.append(")"); - - return sb.toString(); - } - - private int maxValueLength(Class enumType){ - - int maxLen = 0; - - Object[] ea = enumType.getEnumConstants(); - for (int i = 0; i < ea.length; i++) { - Enum e = (Enum)ea[i]; - maxLen = Math.max(maxLen, e.toString().length()); - } - - return maxLen; - } - - public int getLength() { - return length; - } - - public void bind(DataBind b, Object value) throws SQLException { - if (value == null){ - b.setNull(Types.VARCHAR); - } else { - b.setString(value.toString()); - } - } - - public Object read(DataReader dataReader) throws SQLException { - - String string = dataReader.getString(); - if (string == null){ - return null; - } else { - return Enum.valueOf(enumType, string); - } - } - - /** - * Convert the Boolean value to the db value. - */ - public Object toJdbcType(Object beanValue) { - if (beanValue == null) { - return null; - } - Enum e = (Enum)beanValue; - return e.toString(); - } - - public Object toBeanType(Object dbValue) { - if (dbValue == null) { - return null; - } - - return Enum.valueOf(enumType, (String)dbValue); - } - - } - - @SuppressWarnings({"rawtypes","unchecked"}) - public static class OrdinalEnum extends EnumBase implements ScalarTypeEnum { - - private final Object[] enumArray; - - /** - * Create a ScalarTypeEnum. - */ - public OrdinalEnum(Class enumType) { - super(enumType, false, Types.INTEGER); - this.enumArray = EnumSet.allOf(enumType).toArray(); - } - - /** - * Return the IN values for DB constraint construction. - */ - public String getContraintInValues(){ - - StringBuilder sb = new StringBuilder(); - - sb.append("("); - for (int i = 0; i < enumArray.length; i++) { - Enum e = (Enum)enumArray[i]; - if (i > 0){ - sb.append(","); - } - sb.append(e.ordinal()); - } - sb.append(")"); - - return sb.toString(); - } - - - public void bind(DataBind b, Object value) throws SQLException { - if (value == null){ - b.setNull(Types.INTEGER); - } else { - Enum e = (Enum)value; - b.setInt(e.ordinal()); - } - } - - public Object read(DataReader dataReader) throws SQLException { - - Integer ordinal = dataReader.getInt(); - if (ordinal == null){ - return null; - } else { - if (ordinal < 0 || ordinal >= enumArray.length){ - String m = "Unexpected ordinal ["+ordinal+"] out of range ["+enumArray.length+"]"; - throw new IllegalStateException(m); - } - return enumArray[ordinal]; - } - } - - /** - * Convert the Boolean value to the db value. - */ - public Object toJdbcType(Object beanValue) { - if (beanValue == null) { - return null; - } - Enum e = (Enum)beanValue; - return e.ordinal(); - } - - /** - * Convert the db value to the Boolean value. - */ - public Object toBeanType(Object dbValue) { - if (dbValue == null) { - return null; - } - - int ordinal = ((Integer)dbValue).intValue(); - if (ordinal < 0 || ordinal >= enumArray.length){ - String m = "Unexpected ordinal ["+ordinal+"] out of range ["+enumArray.length+"]"; - throw new IllegalStateException(m); - } - return enumArray[ordinal]; - } - - } - - @SuppressWarnings({"rawtypes","unchecked"}) - public abstract static class EnumBase extends ScalarTypeBase { - - protected final Class enumType; - - public EnumBase(Class type, boolean jdbcNative, int jdbcType) { - super(type, jdbcNative, jdbcType); - this.enumType = type; - } - - public String format(Object t) { - return t.toString(); - } - - public String formatValue(Object t) { - return t.toString(); - } - - public Object parse(String value) { - return Enum.valueOf(enumType, value); - } - - public Object parseDateTime(long systemTimeMillis) { - throw new TextException("Not Supported"); - } - - public boolean isDateTimeCapable() { - return false; - } - - @Override - public Object jsonFromString(String value, JsonValueAdapter ctx) { - return parse(value); - } - - @Override - public String jsonToString(Object value, JsonValueAdapter ctx) { - return EscapeJson.escapeQuote(value.toString()); - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - String s = dataInput.readUTF(); - return parse(s); - } - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - if (v == null){ - dataOutput.writeBoolean(false); - } else { - dataOutput.writeBoolean(true); - dataOutput.writeUTF(format(v)); - } - } - - } -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; +import java.util.EnumSet; + +import com.avaje.ebean.text.TextException; +import com.avaje.ebean.text.json.JsonValueAdapter; + + +/** + * JPA standard based Enum scalar type. + *

      + * Converts between bean Enum types to database string or integer columns. + *

      + *

      + * The limitation of this class is that it converts the Enum to either the ordinal + * or string value of the Enum. If you wish to convert the Enum to some other value + * then you should look at the Ebean specific @EnumMapping. + *

      + */ +public class ScalarTypeEnumStandard { + + @SuppressWarnings({"rawtypes","unchecked"}) + public static class StringEnum extends EnumBase implements ScalarTypeEnum { + + private final int length; + + /** + * Create a ScalarTypeEnum. + */ + public StringEnum(Class enumType) { + super(enumType, false, Types.VARCHAR); + this.length = maxValueLength(enumType); + } + + /** + * Return the IN values for DB constraint construction. + */ + public String getContraintInValues(){ + + StringBuilder sb = new StringBuilder(); + + sb.append("("); + Object[] ea = enumType.getEnumConstants(); + for (int i = 0; i < ea.length; i++) { + Enum e = (Enum)ea[i]; + if (i > 0){ + sb.append(","); + } + sb.append("'").append(e.toString()).append("'"); + } + sb.append(")"); + + return sb.toString(); + } + + private int maxValueLength(Class enumType){ + + int maxLen = 0; + + Object[] ea = enumType.getEnumConstants(); + for (int i = 0; i < ea.length; i++) { + Enum e = (Enum)ea[i]; + maxLen = Math.max(maxLen, e.toString().length()); + } + + return maxLen; + } + + public int getLength() { + return length; + } + + public void bind(DataBind b, Object value) throws SQLException { + if (value == null){ + b.setNull(Types.VARCHAR); + } else { + b.setString(value.toString()); + } + } + + public Object read(DataReader dataReader) throws SQLException { + + String string = dataReader.getString(); + if (string == null){ + return null; + } else { + return Enum.valueOf(enumType, string); + } + } + + /** + * Convert the Boolean value to the db value. + */ + public Object toJdbcType(Object beanValue) { + if (beanValue == null) { + return null; + } + Enum e = (Enum)beanValue; + return e.toString(); + } + + public Object toBeanType(Object dbValue) { + if (dbValue == null) { + return null; + } + + return Enum.valueOf(enumType, (String)dbValue); + } + + } + + @SuppressWarnings({"rawtypes","unchecked"}) + public static class OrdinalEnum extends EnumBase implements ScalarTypeEnum { + + private final Object[] enumArray; + + /** + * Create a ScalarTypeEnum. + */ + public OrdinalEnum(Class enumType) { + super(enumType, false, Types.INTEGER); + this.enumArray = EnumSet.allOf(enumType).toArray(); + } + + /** + * Return the IN values for DB constraint construction. + */ + public String getContraintInValues(){ + + StringBuilder sb = new StringBuilder(); + + sb.append("("); + for (int i = 0; i < enumArray.length; i++) { + Enum e = (Enum)enumArray[i]; + if (i > 0){ + sb.append(","); + } + sb.append(e.ordinal()); + } + sb.append(")"); + + return sb.toString(); + } + + + public void bind(DataBind b, Object value) throws SQLException { + if (value == null){ + b.setNull(Types.INTEGER); + } else { + Enum e = (Enum)value; + b.setInt(e.ordinal()); + } + } + + public Object read(DataReader dataReader) throws SQLException { + + Integer ordinal = dataReader.getInt(); + if (ordinal == null){ + return null; + } else { + if (ordinal < 0 || ordinal >= enumArray.length){ + String m = "Unexpected ordinal ["+ordinal+"] out of range ["+enumArray.length+"]"; + throw new IllegalStateException(m); + } + return enumArray[ordinal]; + } + } + + /** + * Convert the Boolean value to the db value. + */ + public Object toJdbcType(Object beanValue) { + if (beanValue == null) { + return null; + } + Enum e = (Enum)beanValue; + return e.ordinal(); + } + + /** + * Convert the db value to the Boolean value. + */ + public Object toBeanType(Object dbValue) { + if (dbValue == null) { + return null; + } + + int ordinal = ((Integer)dbValue).intValue(); + if (ordinal < 0 || ordinal >= enumArray.length){ + String m = "Unexpected ordinal ["+ordinal+"] out of range ["+enumArray.length+"]"; + throw new IllegalStateException(m); + } + return enumArray[ordinal]; + } + + } + + @SuppressWarnings({"rawtypes","unchecked"}) + public abstract static class EnumBase extends ScalarTypeBase { + + protected final Class enumType; + + public EnumBase(Class type, boolean jdbcNative, int jdbcType) { + super(type, jdbcNative, jdbcType); + this.enumType = type; + } + + public String format(Object t) { + return t.toString(); + } + + public String formatValue(Object t) { + return t.toString(); + } + + public Object parse(String value) { + return Enum.valueOf(enumType, value); + } + + public Object parseDateTime(long systemTimeMillis) { + throw new TextException("Not Supported"); + } + + public boolean isDateTimeCapable() { + return false; + } + + @Override + public Object jsonFromString(String value, JsonValueAdapter ctx) { + return parse(value); + } + + @Override + public String jsonToString(Object value, JsonValueAdapter ctx) { + return EscapeJson.escapeQuote(value.toString()); + } + + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + String s = dataInput.readUTF(); + return parse(s); + } + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + if (v == null){ + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeUTF(format(v)); + } + } + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEnumWithMapping.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEnumWithMapping.java index f31153d8a..55fdecc84 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEnumWithMapping.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeEnumWithMapping.java @@ -1,101 +1,82 @@ -/** - * 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.type; - -import java.sql.SQLException; -import java.util.Iterator; - -/** - * Additional control over mapping to DB values. - */ -@SuppressWarnings({ "unchecked", "rawtypes" }) -public class ScalarTypeEnumWithMapping extends ScalarTypeEnumStandard.EnumBase implements ScalarType, ScalarTypeEnum { - - private final EnumToDbValueMap beanDbMap; - - private final int length; - - /** - * Create with an explicit mapping of bean to database values. - */ - public ScalarTypeEnumWithMapping(EnumToDbValueMap beanDbMap, Class enumType, int length) { - super(enumType, false, beanDbMap.getDbType()); - this.beanDbMap = beanDbMap; - this.length = length; - } - - /** - * Return the IN values for DB constraint construction. - */ - public String getContraintInValues() { - - StringBuilder sb = new StringBuilder(); - - int i = 0; - - sb.append("("); - - Iterator it = beanDbMap.dbValues(); - while (it.hasNext()) { - Object dbValue = it.next(); - if (i++ > 0) { - sb.append(","); - } - if (!beanDbMap.isIntegerType()) { - sb.append("'"); - } - sb.append(dbValue.toString()); - if (!beanDbMap.isIntegerType()) { - sb.append("'"); - } - } - - sb.append(")"); - - return sb.toString(); - } - - /** - * Return the DB column length for storing the enum value. - *

      - * This is for enum's mapped to strings. - *

      - */ - public int getLength() { - return length; - } - - public void bind(DataBind b, Object value) throws SQLException { - beanDbMap.bind(b, value); - } - - public Object read(DataReader dataReader) throws SQLException { - return beanDbMap.read(dataReader); - } - - public Object toBeanType(Object dbValue) { - return beanDbMap.getBeanValue(dbValue); - } - - public Object toJdbcType(Object beanValue) { - return beanDbMap.getDbValue(beanValue); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.SQLException; +import java.util.Iterator; + +/** + * Additional control over mapping to DB values. + */ +@SuppressWarnings({ "unchecked", "rawtypes" }) +public class ScalarTypeEnumWithMapping extends ScalarTypeEnumStandard.EnumBase implements ScalarType, ScalarTypeEnum { + + private final EnumToDbValueMap beanDbMap; + + private final int length; + + /** + * Create with an explicit mapping of bean to database values. + */ + public ScalarTypeEnumWithMapping(EnumToDbValueMap beanDbMap, Class enumType, int length) { + super(enumType, false, beanDbMap.getDbType()); + this.beanDbMap = beanDbMap; + this.length = length; + } + + /** + * Return the IN values for DB constraint construction. + */ + public String getContraintInValues() { + + StringBuilder sb = new StringBuilder(); + + int i = 0; + + sb.append("("); + + Iterator it = beanDbMap.dbValues(); + while (it.hasNext()) { + Object dbValue = it.next(); + if (i++ > 0) { + sb.append(","); + } + if (!beanDbMap.isIntegerType()) { + sb.append("'"); + } + sb.append(dbValue.toString()); + if (!beanDbMap.isIntegerType()) { + sb.append("'"); + } + } + + sb.append(")"); + + return sb.toString(); + } + + /** + * Return the DB column length for storing the enum value. + *

      + * This is for enum's mapped to strings. + *

      + */ + public int getLength() { + return length; + } + + public void bind(DataBind b, Object value) throws SQLException { + beanDbMap.bind(b, value); + } + + public Object read(DataReader dataReader) throws SQLException { + return beanDbMap.read(dataReader); + } + + public Object toBeanType(Object dbValue) { + return beanDbMap.getBeanValue(dbValue); + } + + public Object toJdbcType(Object beanValue) { + return beanDbMap.getDbValue(beanValue); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeFloat.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeFloat.java index 4872a8378..516c1bd43 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeFloat.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeFloat.java @@ -1,103 +1,84 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for Float and float. - */ -public class ScalarTypeFloat extends ScalarTypeBase { - - public ScalarTypeFloat() { - super(Float.class, true, Types.REAL); - } - - public void bind(DataBind b, Float value) throws SQLException { - if (value == null){ - b.setNull(Types.REAL); - } else { - b.setFloat(value.floatValue()); - } - } - - public Float read(DataReader dataReader) throws SQLException { - - return dataReader.getFloat(); - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.toFloat(value); - } - - public Float toBeanType(Object value) { - return BasicTypeConverter.toFloat(value); - } - - public String formatValue(Float t) { - return t.toString(); - } - - public Float parse(String value) { - return Float.valueOf(value); - } - - public Float parseDateTime(long systemTimeMillis) { - return Float.valueOf(systemTimeMillis); - } - - public boolean isDateTimeCapable() { - return true; - } - - public String toJsonString(Float value) { - if(value.isInfinite() || value.isNaN()) { - return "null"; - } else { - return value.toString(); - } - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - float val = dataInput.readFloat(); - return Float.valueOf(val); - } - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - - Float value = (Float)v; - if (value == null){ - dataOutput.writeBoolean(false); - } else { - dataOutput.writeBoolean(true); - dataOutput.writeFloat(value.floatValue()); - } - } -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for Float and float. + */ +public class ScalarTypeFloat extends ScalarTypeBase { + + public ScalarTypeFloat() { + super(Float.class, true, Types.REAL); + } + + public void bind(DataBind b, Float value) throws SQLException { + if (value == null){ + b.setNull(Types.REAL); + } else { + b.setFloat(value.floatValue()); + } + } + + public Float read(DataReader dataReader) throws SQLException { + + return dataReader.getFloat(); + } + + public Object toJdbcType(Object value) { + return BasicTypeConverter.toFloat(value); + } + + public Float toBeanType(Object value) { + return BasicTypeConverter.toFloat(value); + } + + public String formatValue(Float t) { + return t.toString(); + } + + public Float parse(String value) { + return Float.valueOf(value); + } + + public Float parseDateTime(long systemTimeMillis) { + return Float.valueOf(systemTimeMillis); + } + + public boolean isDateTimeCapable() { + return true; + } + + public String toJsonString(Float value) { + if(value.isInfinite() || value.isNaN()) { + return "null"; + } else { + return value.toString(); + } + } + + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + float val = dataInput.readFloat(); + return Float.valueOf(val); + } + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + + Float value = (Float)v; + if (value == null){ + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeFloat(value.floatValue()); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeInteger.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeInteger.java index 2e521908f..6c67ff1c6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeInteger.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeInteger.java @@ -1,93 +1,74 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebean.text.TextException; -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for Integer and int. - */ -public class ScalarTypeInteger extends ScalarTypeBase { - - public ScalarTypeInteger() { - super(Integer.class, true, Types.INTEGER); - } - - public void bind(DataBind b, Integer value) throws SQLException { - if (value == null){ - b.setNull(Types.INTEGER); - } else { - b.setInt(value.intValue()); - } - } - - public Integer read(DataReader dataReader) throws SQLException { - - return dataReader.getInt(); - } - - public Object readData(DataInput dataInput) throws IOException { - return Integer.valueOf(dataInput.readInt()); - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - dataOutput.writeInt((Integer) v); - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.toInteger(value); - } - - public Integer toBeanType(Object value) { - return BasicTypeConverter.toInteger(value); - } - - public String formatValue(Integer v) { - return v.toString(); - } - - public Integer parse(String value) { - return Integer.valueOf(value); - } - - public Integer parseDateTime(long systemTimeMillis) { - throw new TextException("Not Supported"); - } - - public boolean isDateTimeCapable() { - return false; - } - - public String jsonToString(Integer value, JsonValueAdapter ctx) { - return value.toString(); - } - - public Integer jsonFromString(String value, JsonValueAdapter ctx) { - return Integer.valueOf(value); - } -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebean.text.TextException; +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for Integer and int. + */ +public class ScalarTypeInteger extends ScalarTypeBase { + + public ScalarTypeInteger() { + super(Integer.class, true, Types.INTEGER); + } + + public void bind(DataBind b, Integer value) throws SQLException { + if (value == null){ + b.setNull(Types.INTEGER); + } else { + b.setInt(value.intValue()); + } + } + + public Integer read(DataReader dataReader) throws SQLException { + + return dataReader.getInt(); + } + + public Object readData(DataInput dataInput) throws IOException { + return Integer.valueOf(dataInput.readInt()); + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + dataOutput.writeInt((Integer) v); + } + + public Object toJdbcType(Object value) { + return BasicTypeConverter.toInteger(value); + } + + public Integer toBeanType(Object value) { + return BasicTypeConverter.toInteger(value); + } + + public String formatValue(Integer v) { + return v.toString(); + } + + public Integer parse(String value) { + return Integer.valueOf(value); + } + + public Integer parseDateTime(long systemTimeMillis) { + throw new TextException("Not Supported"); + } + + public boolean isDateTimeCapable() { + return false; + } + + public String jsonToString(Integer value, JsonValueAdapter ctx) { + return value.toString(); + } + + public Integer jsonFromString(String value, JsonValueAdapter ctx) { + return Integer.valueOf(value); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaDateMidnight.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaDateMidnight.java index 6940a8f76..ba6c8cc81 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaDateMidnight.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaDateMidnight.java @@ -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.type; - -import java.sql.Date; -import java.sql.Types; - -import org.joda.time.DateMidnight; - -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for Joda DateMidnight. This maps to a JDBC Date. - */ -public class ScalarTypeJodaDateMidnight extends ScalarTypeBaseDate { - - /** - * Instantiates a new scalar type joda date midnight. - */ - public ScalarTypeJodaDateMidnight() { - super(DateMidnight.class, false, Types.DATE); - } - - @Override - public DateMidnight convertFromDate(Date ts) { - return new DateMidnight(ts.getTime()); - } - - @Override - public Date convertToDate(DateMidnight t) { - return new Date(t.getMillis()); - } - - public Object toJdbcType(Object value) { - if (value instanceof DateMidnight){ - return new Date(((DateMidnight)value).getMillis()); - } - return BasicTypeConverter.toDate(value); - } - - public DateMidnight toBeanType(Object value) { - if (value instanceof java.util.Date){ - return new DateMidnight(((java.util.Date)value).getTime()); - } - return (DateMidnight)value; - } -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.Date; +import java.sql.Types; + +import org.joda.time.DateMidnight; + +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for Joda DateMidnight. This maps to a JDBC Date. + */ +public class ScalarTypeJodaDateMidnight extends ScalarTypeBaseDate { + + /** + * Instantiates a new scalar type joda date midnight. + */ + public ScalarTypeJodaDateMidnight() { + super(DateMidnight.class, false, Types.DATE); + } + + @Override + public DateMidnight convertFromDate(Date ts) { + return new DateMidnight(ts.getTime()); + } + + @Override + public Date convertToDate(DateMidnight t) { + return new Date(t.getMillis()); + } + + public Object toJdbcType(Object value) { + if (value instanceof DateMidnight){ + return new Date(((DateMidnight)value).getMillis()); + } + return BasicTypeConverter.toDate(value); + } + + public DateMidnight toBeanType(Object value) { + if (value instanceof java.util.Date){ + return new DateMidnight(((java.util.Date)value).getTime()); + } + return (DateMidnight)value; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaDateTime.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaDateTime.java index 8e622b7ba..8ddb187a4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaDateTime.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaDateTime.java @@ -1,62 +1,43 @@ -/** - * 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.type; - -import java.sql.Timestamp; -import java.sql.Types; - -import org.joda.time.DateTime; - -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for Joda DateTime. This maps to a JDBC Timestamp. - */ -public class ScalarTypeJodaDateTime extends ScalarTypeBaseDateTime { - - public ScalarTypeJodaDateTime() { - super(DateTime.class, false, Types.TIMESTAMP); - } - - @Override - public DateTime convertFromTimestamp(Timestamp ts) { - return new DateTime(ts.getTime()); - } - - @Override - public Timestamp convertToTimestamp(DateTime t) { - return new Timestamp(t.getMillis()); - } - - public Object toJdbcType(Object value) { - if (value instanceof DateTime){ - return new Timestamp(((DateTime)value).getMillis()); - } - return BasicTypeConverter.toTimestamp(value); - } - - public DateTime toBeanType(Object value) { - if (value instanceof java.util.Date){ - return new DateTime(((java.util.Date)value).getTime()); - } - return (DateTime)value; - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.Timestamp; +import java.sql.Types; + +import org.joda.time.DateTime; + +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for Joda DateTime. This maps to a JDBC Timestamp. + */ +public class ScalarTypeJodaDateTime extends ScalarTypeBaseDateTime { + + public ScalarTypeJodaDateTime() { + super(DateTime.class, false, Types.TIMESTAMP); + } + + @Override + public DateTime convertFromTimestamp(Timestamp ts) { + return new DateTime(ts.getTime()); + } + + @Override + public Timestamp convertToTimestamp(DateTime t) { + return new Timestamp(t.getMillis()); + } + + public Object toJdbcType(Object value) { + if (value instanceof DateTime){ + return new Timestamp(((DateTime)value).getMillis()); + } + return BasicTypeConverter.toTimestamp(value); + } + + public DateTime toBeanType(Object value) { + if (value instanceof java.util.Date){ + return new DateTime(((java.util.Date)value).getTime()); + } + return (DateTime)value; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDate.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDate.java index fd72f3fa1..3ad217090 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDate.java @@ -1,66 +1,47 @@ -/** - * 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.type; - -import java.sql.Date; -import java.sql.Types; - -import org.joda.time.LocalDate; - -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for Joda LocalDate. This maps to a JDBC Date. - */ -public class ScalarTypeJodaLocalDate extends ScalarTypeBaseDate { - - public ScalarTypeJodaLocalDate() { - super(LocalDate.class, false, Types.DATE); - } - - @Override - public LocalDate convertFromDate(Date ts) { - return new LocalDate(((java.util.Date)ts).getTime()); - } - - @Override - public Date convertToDate(LocalDate t) { - return new java.sql.Date(t.toDateMidnight().getMillis()); - } - - public Object toJdbcType(Object value) { - if (value instanceof LocalDate){ - return new java.sql.Date(((LocalDate)value).toDateMidnight().getMillis()); - } - return BasicTypeConverter.toDate(value); - } - - public LocalDate toBeanType(Object value) { - if (value instanceof java.util.Date){ - return new LocalDate(((java.util.Date)value).getTime()); - } - return (LocalDate)value; - } - - public LocalDate parseDateTime(long systemTimeMillis) { - return new LocalDate(systemTimeMillis); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.Date; +import java.sql.Types; + +import org.joda.time.LocalDate; + +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for Joda LocalDate. This maps to a JDBC Date. + */ +public class ScalarTypeJodaLocalDate extends ScalarTypeBaseDate { + + public ScalarTypeJodaLocalDate() { + super(LocalDate.class, false, Types.DATE); + } + + @Override + public LocalDate convertFromDate(Date ts) { + return new LocalDate(((java.util.Date)ts).getTime()); + } + + @Override + public Date convertToDate(LocalDate t) { + return new java.sql.Date(t.toDateMidnight().getMillis()); + } + + public Object toJdbcType(Object value) { + if (value instanceof LocalDate){ + return new java.sql.Date(((LocalDate)value).toDateMidnight().getMillis()); + } + return BasicTypeConverter.toDate(value); + } + + public LocalDate toBeanType(Object value) { + if (value instanceof java.util.Date){ + return new LocalDate(((java.util.Date)value).getTime()); + } + return (LocalDate)value; + } + + public LocalDate parseDateTime(long systemTimeMillis) { + return new LocalDate(systemTimeMillis); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDateTime.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDateTime.java index d59dd59d6..848c7a4e8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDateTime.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDateTime.java @@ -1,66 +1,47 @@ -/** - * 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.type; - -import java.sql.Timestamp; -import java.sql.Types; - -import org.joda.time.LocalDateTime; - -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for Joda LocalDateTime. This maps to a JDBC Timestamp. - */ -public class ScalarTypeJodaLocalDateTime extends ScalarTypeBaseDateTime { - - public ScalarTypeJodaLocalDateTime() { - super(LocalDateTime.class, false, Types.TIMESTAMP); - } - - @Override - public LocalDateTime convertFromTimestamp(Timestamp ts) { - return new LocalDateTime(ts.getTime()); - } - - @Override - public Timestamp convertToTimestamp(LocalDateTime t) { - return new Timestamp(t.toDateTime().getMillis()); - } - - public Object toJdbcType(Object value) { - if (value instanceof LocalDateTime){ - return new Timestamp(((LocalDateTime)value).toDateTime().getMillis()); - } - return BasicTypeConverter.toTimestamp(value); - } - - public LocalDateTime toBeanType(Object value) { - if (value instanceof java.util.Date){ - return new LocalDateTime(((java.util.Date)value).getTime()); - } - return (LocalDateTime)value; - } - - public LocalDateTime parseDateTime(long systemTimeMillis) { - return new LocalDateTime(systemTimeMillis); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.Timestamp; +import java.sql.Types; + +import org.joda.time.LocalDateTime; + +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for Joda LocalDateTime. This maps to a JDBC Timestamp. + */ +public class ScalarTypeJodaLocalDateTime extends ScalarTypeBaseDateTime { + + public ScalarTypeJodaLocalDateTime() { + super(LocalDateTime.class, false, Types.TIMESTAMP); + } + + @Override + public LocalDateTime convertFromTimestamp(Timestamp ts) { + return new LocalDateTime(ts.getTime()); + } + + @Override + public Timestamp convertToTimestamp(LocalDateTime t) { + return new Timestamp(t.toDateTime().getMillis()); + } + + public Object toJdbcType(Object value) { + if (value instanceof LocalDateTime){ + return new Timestamp(((LocalDateTime)value).toDateTime().getMillis()); + } + return BasicTypeConverter.toTimestamp(value); + } + + public LocalDateTime toBeanType(Object value) { + if (value instanceof java.util.Date){ + return new LocalDateTime(((java.util.Date)value).getTime()); + } + return (LocalDateTime)value; + } + + public LocalDateTime parseDateTime(long systemTimeMillis) { + return new LocalDateTime(systemTimeMillis); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalTime.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalTime.java index c51dc46e7..d726c6027 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalTime.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalTime.java @@ -1,111 +1,92 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Time; -import java.sql.Types; - -import org.joda.time.DateTimeZone; -import org.joda.time.LocalTime; - -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for Joda LocalTime. This maps to a JDBC Time. - */ -public class ScalarTypeJodaLocalTime extends ScalarTypeBase { - - public ScalarTypeJodaLocalTime() { - super(LocalTime.class, false, Types.TIME); - } - - public void bind(DataBind b, LocalTime value) throws SQLException { - if (value == null){ - b.setNull(Types.TIME); - } else { - Time sqlTime = new Time(value.getMillisOfDay()); - b.setTime(sqlTime); - } - } - - public LocalTime read(DataReader dataReader) throws SQLException { - - Time sqlTime = dataReader.getTime(); - if (sqlTime == null){ - return null; - } else { - return new LocalTime(sqlTime, DateTimeZone.UTC); - } - } - - public Object toJdbcType(Object value) { - if (value instanceof LocalTime){ - return new Time(((LocalTime)value).getMillisOfDay()); - } - return BasicTypeConverter.toTime(value); - } - - public LocalTime toBeanType(Object value) { - if (value instanceof java.util.Date){ - return new LocalTime(value, DateTimeZone.UTC); - } - return (LocalTime)value; - } - - public String formatValue(LocalTime v) { - return v.toString(); - } - - public LocalTime parse(String value) { - return new LocalTime(value); - } - - public LocalTime parseDateTime(long systemTimeMillis) { - return new LocalTime(systemTimeMillis); - } - - public boolean isDateTimeCapable() { - return true; - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - String val = dataInput.readUTF(); - return parse(val); - } - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - - Time value = (Time)v; - if (value == null){ - dataOutput.writeBoolean(false); - } else { - dataOutput.writeBoolean(true); - dataOutput.writeUTF(format(value)); - } - } -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Types; + +import org.joda.time.DateTimeZone; +import org.joda.time.LocalTime; + +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for Joda LocalTime. This maps to a JDBC Time. + */ +public class ScalarTypeJodaLocalTime extends ScalarTypeBase { + + public ScalarTypeJodaLocalTime() { + super(LocalTime.class, false, Types.TIME); + } + + public void bind(DataBind b, LocalTime value) throws SQLException { + if (value == null){ + b.setNull(Types.TIME); + } else { + Time sqlTime = new Time(value.getMillisOfDay()); + b.setTime(sqlTime); + } + } + + public LocalTime read(DataReader dataReader) throws SQLException { + + Time sqlTime = dataReader.getTime(); + if (sqlTime == null){ + return null; + } else { + return new LocalTime(sqlTime, DateTimeZone.UTC); + } + } + + public Object toJdbcType(Object value) { + if (value instanceof LocalTime){ + return new Time(((LocalTime)value).getMillisOfDay()); + } + return BasicTypeConverter.toTime(value); + } + + public LocalTime toBeanType(Object value) { + if (value instanceof java.util.Date){ + return new LocalTime(value, DateTimeZone.UTC); + } + return (LocalTime)value; + } + + public String formatValue(LocalTime v) { + return v.toString(); + } + + public LocalTime parse(String value) { + return new LocalTime(value); + } + + public LocalTime parseDateTime(long systemTimeMillis) { + return new LocalTime(systemTimeMillis); + } + + public boolean isDateTimeCapable() { + return true; + } + + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + String val = dataInput.readUTF(); + return parse(val); + } + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + + Time value = (Time)v; + if (value == null){ + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeUTF(format(value)); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLdapBoolean.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLdapBoolean.java index a8edb0a9b..6fb897d66 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLdapBoolean.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLdapBoolean.java @@ -1,32 +1,13 @@ -/** - * 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.type; - - -/** - * ScalarType for LDAP Boolean. - */ -public class ScalarTypeLdapBoolean extends ScalarTypeBoolean.StringBoolean { - - public ScalarTypeLdapBoolean() { - super("TRUE", "FALSE"); - } - -} +package com.avaje.ebeaninternal.server.type; + + +/** + * ScalarType for LDAP Boolean. + */ +public class ScalarTypeLdapBoolean extends ScalarTypeBoolean.StringBoolean { + + public ScalarTypeLdapBoolean() { + super("TRUE", "FALSE"); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLdapDate.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLdapDate.java index 1ecb33026..c04e9cde0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLdapDate.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLdapDate.java @@ -1,160 +1,141 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Types; -import java.text.SimpleDateFormat; -import java.util.Date; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; - -/** - * Wrapper type that wraps all java.sql.Date types for LDAP. - * - * @author rbygrave - */ -public class ScalarTypeLdapDate implements ScalarType { - - private static final String timestampLDAPFormat = "yyyyMMddHHmmss'Z'"; - - private final ScalarType baseType; - - public ScalarTypeLdapDate(ScalarType baseType) { - this.baseType = baseType; - } - - public T toBeanType(Object value) { - if (value == null){ - return null; - } - if (value instanceof String == false){ - String msg = "Expecting a String type but got "+value.getClass()+" value["+value+"]"; - throw new PersistenceException(msg); - } - try { - SimpleDateFormat sdf = new SimpleDateFormat(timestampLDAPFormat); - Date date = sdf.parse((String)value); - - return baseType.parseDateTime(date.getTime()); - - } catch (Exception e) { - String msg = "Error parsing LDAP timestamp "+value; - throw new PersistenceException(msg, e); - } - } - - public Object toJdbcType(Object value) { - - if (value == null){ - return null; - } - - Object ts = baseType.toJdbcType(value); - if (ts instanceof java.sql.Date == false){ - String msg = "Expecting a java.sql.Date type but got "+value.getClass()+" value["+value+"]"; - throw new PersistenceException(msg); - } - - java.sql.Date t = (java.sql.Date)ts; - SimpleDateFormat sdf = new SimpleDateFormat(timestampLDAPFormat); - return sdf.format(t); - } - - - public void bind(DataBind b, T value) throws SQLException { - baseType.bind(b, value); - } - - public int getJdbcType() { - return Types.VARCHAR; - } - - public int getLength() { - return baseType.getLength(); - } - - public Class getType() { - return baseType.getType(); - } - - public boolean isDateTimeCapable() { - return baseType.isDateTimeCapable(); - } - - public boolean isJdbcNative() { - return false; - } - - public void loadIgnore(DataReader dataReader) { - baseType.loadIgnore(dataReader); - } - - public String format(Object v) { - return baseType.format(v); - } - - public String formatValue(T t) { - return baseType.formatValue(t); - } - - public T parse(String value) { - return baseType.parse(value); - } - - public T parseDateTime(long systemTimeMillis) { - return baseType.parseDateTime(systemTimeMillis); - } - - public T read(DataReader dataReader) throws SQLException { - return baseType.read(dataReader); - } - - public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { - baseType.accumulateScalarTypes(propName, list); - } - - public String jsonToString(T value, JsonValueAdapter ctx) { - return baseType.jsonToString(value, ctx); - } - - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { - baseType.jsonWrite(buffer, value, ctx); - } - - public T jsonFromString(String value, JsonValueAdapter ctx) { - return baseType.jsonFromString(value, ctx); - } - - public Object readData(DataInput dataInput) throws IOException { - return baseType.readData(dataInput); - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - baseType.writeData(dataOutput, v); - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; + +/** + * Wrapper type that wraps all java.sql.Date types for LDAP. + * + * @author rbygrave + */ +public class ScalarTypeLdapDate implements ScalarType { + + private static final String timestampLDAPFormat = "yyyyMMddHHmmss'Z'"; + + private final ScalarType baseType; + + public ScalarTypeLdapDate(ScalarType baseType) { + this.baseType = baseType; + } + + public T toBeanType(Object value) { + if (value == null){ + return null; + } + if (value instanceof String == false){ + String msg = "Expecting a String type but got "+value.getClass()+" value["+value+"]"; + throw new PersistenceException(msg); + } + try { + SimpleDateFormat sdf = new SimpleDateFormat(timestampLDAPFormat); + Date date = sdf.parse((String)value); + + return baseType.parseDateTime(date.getTime()); + + } catch (Exception e) { + String msg = "Error parsing LDAP timestamp "+value; + throw new PersistenceException(msg, e); + } + } + + public Object toJdbcType(Object value) { + + if (value == null){ + return null; + } + + Object ts = baseType.toJdbcType(value); + if (ts instanceof java.sql.Date == false){ + String msg = "Expecting a java.sql.Date type but got "+value.getClass()+" value["+value+"]"; + throw new PersistenceException(msg); + } + + java.sql.Date t = (java.sql.Date)ts; + SimpleDateFormat sdf = new SimpleDateFormat(timestampLDAPFormat); + return sdf.format(t); + } + + + public void bind(DataBind b, T value) throws SQLException { + baseType.bind(b, value); + } + + public int getJdbcType() { + return Types.VARCHAR; + } + + public int getLength() { + return baseType.getLength(); + } + + public Class getType() { + return baseType.getType(); + } + + public boolean isDateTimeCapable() { + return baseType.isDateTimeCapable(); + } + + public boolean isJdbcNative() { + return false; + } + + public void loadIgnore(DataReader dataReader) { + baseType.loadIgnore(dataReader); + } + + public String format(Object v) { + return baseType.format(v); + } + + public String formatValue(T t) { + return baseType.formatValue(t); + } + + public T parse(String value) { + return baseType.parse(value); + } + + public T parseDateTime(long systemTimeMillis) { + return baseType.parseDateTime(systemTimeMillis); + } + + public T read(DataReader dataReader) throws SQLException { + return baseType.read(dataReader); + } + + public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { + baseType.accumulateScalarTypes(propName, list); + } + + public String jsonToString(T value, JsonValueAdapter ctx) { + return baseType.jsonToString(value, ctx); + } + + public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { + baseType.jsonWrite(buffer, value, ctx); + } + + public T jsonFromString(String value, JsonValueAdapter ctx) { + return baseType.jsonFromString(value, ctx); + } + + public Object readData(DataInput dataInput) throws IOException { + return baseType.readData(dataInput); + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + baseType.writeData(dataOutput, v); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLdapTimestamp.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLdapTimestamp.java index 48c2c1c73..e7321b103 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLdapTimestamp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLdapTimestamp.java @@ -1,160 +1,141 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.sql.Types; -import java.text.SimpleDateFormat; -import java.util.Date; - -import javax.persistence.PersistenceException; - -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; - -/** - * Wrapper type that wraps all java.sql.Timestamp types for LDAP. - * - * @author rbygrave - */ -public class ScalarTypeLdapTimestamp implements ScalarType { - - private static final String timestampLDAPFormat = "yyyyMMddHHmmss'Z'"; - - private final ScalarType baseType; - - public ScalarTypeLdapTimestamp(ScalarType baseType) { - this.baseType = baseType; - } - - public T toBeanType(Object value) { - if (value == null){ - return null; - } - if (value instanceof String == false){ - String msg = "Expecting a String type but got "+value.getClass()+" value["+value+"]"; - throw new PersistenceException(msg); - } - try { - SimpleDateFormat sdf = new SimpleDateFormat(timestampLDAPFormat); - Date date = sdf.parse((String)value); - - return baseType.parseDateTime(date.getTime()); - - } catch (Exception e) { - String msg = "Error parsing LDAP timestamp "+value; - throw new PersistenceException(msg, e); - } - } - - public Object toJdbcType(Object value) { - - if (value == null){ - return null; - } - - Object ts = baseType.toJdbcType(value); - if (ts instanceof java.sql.Timestamp == false){ - String msg = "Expecting a Timestamp type but got "+value.getClass()+" value["+value+"]"; - throw new PersistenceException(msg); - } - - Timestamp t = (Timestamp)ts; - SimpleDateFormat sdf = new SimpleDateFormat(timestampLDAPFormat); - return sdf.format(t); - } - - - public void bind(DataBind b, T value) throws SQLException { - baseType.bind(b, value); - } - - public int getJdbcType() { - return Types.VARCHAR; - } - - public int getLength() { - return baseType.getLength(); - } - - public Class getType() { - return baseType.getType(); - } - - public boolean isDateTimeCapable() { - return baseType.isDateTimeCapable(); - } - - public boolean isJdbcNative() { - return false; - } - - public void loadIgnore(DataReader dataReader) { - baseType.loadIgnore(dataReader); - } - - public String format(Object v) { - return baseType.format(v); - } - - public String formatValue(T t) { - return baseType.formatValue(t); - } - - public T parse(String value) { - return baseType.parse(value); - } - - public T parseDateTime(long systemTimeMillis) { - return baseType.parseDateTime(systemTimeMillis); - } - - public T read(DataReader dataReader) throws SQLException { - return baseType.read(dataReader); - } - - public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { - baseType.accumulateScalarTypes(propName, list); - } - - public String jsonToString(T value, JsonValueAdapter ctx) { - return baseType.jsonToString(value, ctx); - } - - public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { - baseType.jsonWrite(buffer, value, ctx); - } - - public T jsonFromString(String value, JsonValueAdapter ctx) { - return baseType.jsonFromString(value, ctx); - } - - public Object readData(DataInput dataInput) throws IOException { - return baseType.readData(dataInput); - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - baseType.writeData(dataOutput, v); - } -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.sql.Types; +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; + +/** + * Wrapper type that wraps all java.sql.Timestamp types for LDAP. + * + * @author rbygrave + */ +public class ScalarTypeLdapTimestamp implements ScalarType { + + private static final String timestampLDAPFormat = "yyyyMMddHHmmss'Z'"; + + private final ScalarType baseType; + + public ScalarTypeLdapTimestamp(ScalarType baseType) { + this.baseType = baseType; + } + + public T toBeanType(Object value) { + if (value == null){ + return null; + } + if (value instanceof String == false){ + String msg = "Expecting a String type but got "+value.getClass()+" value["+value+"]"; + throw new PersistenceException(msg); + } + try { + SimpleDateFormat sdf = new SimpleDateFormat(timestampLDAPFormat); + Date date = sdf.parse((String)value); + + return baseType.parseDateTime(date.getTime()); + + } catch (Exception e) { + String msg = "Error parsing LDAP timestamp "+value; + throw new PersistenceException(msg, e); + } + } + + public Object toJdbcType(Object value) { + + if (value == null){ + return null; + } + + Object ts = baseType.toJdbcType(value); + if (ts instanceof java.sql.Timestamp == false){ + String msg = "Expecting a Timestamp type but got "+value.getClass()+" value["+value+"]"; + throw new PersistenceException(msg); + } + + Timestamp t = (Timestamp)ts; + SimpleDateFormat sdf = new SimpleDateFormat(timestampLDAPFormat); + return sdf.format(t); + } + + + public void bind(DataBind b, T value) throws SQLException { + baseType.bind(b, value); + } + + public int getJdbcType() { + return Types.VARCHAR; + } + + public int getLength() { + return baseType.getLength(); + } + + public Class getType() { + return baseType.getType(); + } + + public boolean isDateTimeCapable() { + return baseType.isDateTimeCapable(); + } + + public boolean isJdbcNative() { + return false; + } + + public void loadIgnore(DataReader dataReader) { + baseType.loadIgnore(dataReader); + } + + public String format(Object v) { + return baseType.format(v); + } + + public String formatValue(T t) { + return baseType.formatValue(t); + } + + public T parse(String value) { + return baseType.parse(value); + } + + public T parseDateTime(long systemTimeMillis) { + return baseType.parseDateTime(systemTimeMillis); + } + + public T read(DataReader dataReader) throws SQLException { + return baseType.read(dataReader); + } + + public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { + baseType.accumulateScalarTypes(propName, list); + } + + public String jsonToString(T value, JsonValueAdapter ctx) { + return baseType.jsonToString(value, ctx); + } + + public void jsonWrite(WriteJsonBuffer buffer, T value, JsonValueAdapter ctx) { + baseType.jsonWrite(buffer, value, ctx); + } + + public T jsonFromString(String value, JsonValueAdapter ctx) { + return baseType.jsonFromString(value, ctx); + } + + public Object readData(DataInput dataInput) throws IOException { + return baseType.readData(dataInput); + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + baseType.writeData(dataOutput, v); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLocale.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLocale.java index 9afb54521..0c6d90e10 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLocale.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLocale.java @@ -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.type; - -import java.util.Locale; - -/** - * ScalarType for java.util.Currency which converts to and from a VARCHAR - * database column. - */ -public class ScalarTypeLocale extends ScalarTypeBaseVarchar { - - public ScalarTypeLocale() { - super(Locale.class); - } - - @Override - public int getLength() { - return 20; - } - - @Override - public Locale convertFromDbString(String dbValue) { - return parse(dbValue); - } - - @Override - public String convertToDbString(Locale beanValue) { - return ((Locale) beanValue).toString(); - } - - public String formatValue(Locale t) { - return t.toString(); - } - - public Locale parse(String value) { - - int pos1 = -1; - int pos2 = -1; - - for (int i = 0; i < value.length(); i++) { - char c = value.charAt(i); - if (c == '_') { - if (pos1 > -1) { - pos2 = i; - break; - } else { - pos1 = i; - } - } - } - if (pos1 == -1) { - return new Locale(value); - } - String language = value.substring(0, pos1); - if (pos2 == -1) { - String country = value.substring(pos1 + 1); - return new Locale(language, country); - } else { - String country = value.substring(pos1 + 1, pos2); - String variant = value.substring(pos2 + 1); - return new Locale(language, country, variant); - } - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.util.Locale; + +/** + * ScalarType for java.util.Currency which converts to and from a VARCHAR + * database column. + */ +public class ScalarTypeLocale extends ScalarTypeBaseVarchar { + + public ScalarTypeLocale() { + super(Locale.class); + } + + @Override + public int getLength() { + return 20; + } + + @Override + public Locale convertFromDbString(String dbValue) { + return parse(dbValue); + } + + @Override + public String convertToDbString(Locale beanValue) { + return ((Locale) beanValue).toString(); + } + + public String formatValue(Locale t) { + return t.toString(); + } + + public Locale parse(String value) { + + int pos1 = -1; + int pos2 = -1; + + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (c == '_') { + if (pos1 > -1) { + pos2 = i; + break; + } else { + pos1 = i; + } + } + } + if (pos1 == -1) { + return new Locale(value); + } + String language = value.substring(0, pos1); + if (pos2 == -1) { + String country = value.substring(pos1 + 1); + return new Locale(language, country); + } else { + String country = value.substring(pos1 + 1, pos2); + String variant = value.substring(pos2 + 1); + return new Locale(language, country, variant); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLong.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLong.java index c66f306fc..1b586b714 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLong.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLong.java @@ -1,95 +1,76 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for Long and long. - */ -public class ScalarTypeLong extends ScalarTypeBase { - - public ScalarTypeLong() { - super(Long.class, true, Types.BIGINT); - } - - public void bind(DataBind b, Long value) throws SQLException { - if (value == null){ - b.setNull(Types.BIGINT); - } else { - b.setLong(value.longValue()); - } - } - - public Long read(DataReader dataReader) throws SQLException { - - return dataReader.getLong(); - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.toLong(value); - } - - public Long toBeanType(Object value) { - return BasicTypeConverter.toLong(value); - } - - public String formatValue(Long t) { - return t.toString(); - } - - public Long parse(String value) { - return Long.valueOf(value); - } - - public Long parseDateTime(long systemTimeMillis) { - return Long.valueOf(systemTimeMillis); - } - - public boolean isDateTimeCapable() { - return true; - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - long val = dataInput.readLong(); - return Long.valueOf(val); - } - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - - Long value = (Long)v; - if (value == null){ - dataOutput.writeBoolean(false); - } else { - dataOutput.writeBoolean(true); - dataOutput.writeLong(value.longValue()); - } - } -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for Long and long. + */ +public class ScalarTypeLong extends ScalarTypeBase { + + public ScalarTypeLong() { + super(Long.class, true, Types.BIGINT); + } + + public void bind(DataBind b, Long value) throws SQLException { + if (value == null){ + b.setNull(Types.BIGINT); + } else { + b.setLong(value.longValue()); + } + } + + public Long read(DataReader dataReader) throws SQLException { + + return dataReader.getLong(); + } + + public Object toJdbcType(Object value) { + return BasicTypeConverter.toLong(value); + } + + public Long toBeanType(Object value) { + return BasicTypeConverter.toLong(value); + } + + public String formatValue(Long t) { + return t.toString(); + } + + public Long parse(String value) { + return Long.valueOf(value); + } + + public Long parseDateTime(long systemTimeMillis) { + return Long.valueOf(systemTimeMillis); + } + + public boolean isDateTimeCapable() { + return true; + } + + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + long val = dataInput.readLong(); + return Long.valueOf(val); + } + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + + Long value = (Long)v; + if (value == null){ + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeLong(value.longValue()); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLongToTimestamp.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLongToTimestamp.java index 813dc5502..d01d67469 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLongToTimestamp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLongToTimestamp.java @@ -1,29 +1,10 @@ -/** - * 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.type; - -import java.sql.Timestamp; - -public class ScalarTypeLongToTimestamp extends ScalarTypeWrapper { - - public ScalarTypeLongToTimestamp() { - super(Long.class, new ScalarTypeTimestamp(), new LongToTimestampConverter()); - } -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.Timestamp; + +public class ScalarTypeLongToTimestamp extends ScalarTypeWrapper { + + public ScalarTypeLongToTimestamp() { + super(Long.class, new ScalarTypeTimestamp(), new LongToTimestampConverter()); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLongVarchar.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLongVarchar.java index 0012ebecf..be0462095 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLongVarchar.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeLongVarchar.java @@ -1,39 +1,20 @@ -/** - * 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.type; - -import java.sql.SQLException; -import java.sql.Types; - -/** - * ScalarType for String. - */ -public class ScalarTypeLongVarchar extends ScalarTypeClob { - - public ScalarTypeLongVarchar() { - super(true, Types.LONGVARCHAR); - } - - @Override - public String read(DataReader dataReader) throws SQLException { - - return dataReader.getStringFromStream(); - } -} +package com.avaje.ebeaninternal.server.type; + +import java.sql.SQLException; +import java.sql.Types; + +/** + * ScalarType for String. + */ +public class ScalarTypeLongVarchar extends ScalarTypeClob { + + public ScalarTypeLongVarchar() { + super(true, Types.LONGVARCHAR); + } + + @Override + public String read(DataReader dataReader) throws SQLException { + + return dataReader.getStringFromStream(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeMathBigInteger.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeMathBigInteger.java index 6a689adb0..e97b50222 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeMathBigInteger.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeMathBigInteger.java @@ -1,102 +1,83 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.math.BigInteger; -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for java.math.BigInteger. - */ -public class ScalarTypeMathBigInteger extends ScalarTypeBase { - - public ScalarTypeMathBigInteger() { - super(BigInteger.class, false, Types.BIGINT); - } - - public void bind(DataBind b, BigInteger value) throws SQLException { - if (value == null){ - b.setNull(Types.BIGINT); - } else { - b.setLong(value.longValue()); - } - } - - public BigInteger read(DataReader dataReader) throws SQLException { - - Long l = dataReader.getLong(); - if (l == null){ - return null; - } - return new BigInteger(String.valueOf(l)); - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.toLong(value); - } - - public BigInteger toBeanType(Object value) { - return BasicTypeConverter.toMathBigInteger(value); - } - - - public String formatValue(BigInteger v) { - return v.toString(); - } - - public BigInteger parse(String value) { - return new BigInteger(value); - } - - public BigInteger parseDateTime(long systemTimeMillis) { - return BigInteger.valueOf(systemTimeMillis); - } - - public boolean isDateTimeCapable() { - return true; - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - long val = dataInput.readLong(); - return Long.valueOf(val); - } - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - - Long value = (Long)v; - if (value == null){ - dataOutput.writeBoolean(false); - } else { - dataOutput.writeBoolean(true); - dataOutput.writeLong(value.longValue()); - } - } - -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.math.BigInteger; +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for java.math.BigInteger. + */ +public class ScalarTypeMathBigInteger extends ScalarTypeBase { + + public ScalarTypeMathBigInteger() { + super(BigInteger.class, false, Types.BIGINT); + } + + public void bind(DataBind b, BigInteger value) throws SQLException { + if (value == null){ + b.setNull(Types.BIGINT); + } else { + b.setLong(value.longValue()); + } + } + + public BigInteger read(DataReader dataReader) throws SQLException { + + Long l = dataReader.getLong(); + if (l == null){ + return null; + } + return new BigInteger(String.valueOf(l)); + } + + public Object toJdbcType(Object value) { + return BasicTypeConverter.toLong(value); + } + + public BigInteger toBeanType(Object value) { + return BasicTypeConverter.toMathBigInteger(value); + } + + + public String formatValue(BigInteger v) { + return v.toString(); + } + + public BigInteger parse(String value) { + return new BigInteger(value); + } + + public BigInteger parseDateTime(long systemTimeMillis) { + return BigInteger.valueOf(systemTimeMillis); + } + + public boolean isDateTimeCapable() { + return true; + } + + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + long val = dataInput.readLong(); + return Long.valueOf(val); + } + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + + Long value = (Long)v; + if (value == null){ + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeLong(value.longValue()); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeScalaDouble.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeScalaDouble.java index 0b509581d..4c5f7e8a3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeScalaDouble.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeScalaDouble.java @@ -1,47 +1,28 @@ -/** - * 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.type; - -import scala.Double; - -import com.avaje.ebean.config.ScalarTypeConverter; - -public class ScalarTypeScalaDouble extends ScalarTypeWrapper { - - public ScalarTypeScalaDouble() { - super(Object.class, new ScalarTypeDouble(), new Converter()); - } - - static class Converter implements ScalarTypeConverter { - - public Double getNullValue() { - return null; - } - - public Object wrapValue(java.lang.Double scalarType) { - return scalarType; - } - - public java.lang.Double unwrapValue(Object beanType) { - return ((scala.Double)beanType).toDouble(); - } - - } -} +package com.avaje.ebeaninternal.server.type; + +import scala.Double; + +import com.avaje.ebean.config.ScalarTypeConverter; + +public class ScalarTypeScalaDouble extends ScalarTypeWrapper { + + public ScalarTypeScalaDouble() { + super(Object.class, new ScalarTypeDouble(), new Converter()); + } + + static class Converter implements ScalarTypeConverter { + + public Double getNullValue() { + return null; + } + + public Object wrapValue(java.lang.Double scalarType) { + return scalarType; + } + + public java.lang.Double unwrapValue(Object beanType) { + return ((scala.Double)beanType).toDouble(); + } + + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeShort.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeShort.java index adbe881bf..40f9a3940 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeShort.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeShort.java @@ -1,96 +1,77 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebean.text.TextException; -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for Short and short. - */ -public class ScalarTypeShort extends ScalarTypeBase { - - public ScalarTypeShort() { - super(Short.class, true, Types.SMALLINT); - } - - public void bind(DataBind b, Short value) throws SQLException { - if (value == null){ - b.setNull(Types.SMALLINT); - } else { - b.setShort(value.shortValue()); - } - } - - public Short read(DataReader dataReader) throws SQLException { - - return dataReader.getShort(); - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.toShort(value); - } - - public Short toBeanType(Object value) { - return BasicTypeConverter.toShort(value); - } - - public String formatValue(Short v) { - return v.toString(); - } - - public Short parse(String value) { - return Short.valueOf(value); - } - - public Short parseDateTime(long systemTimeMillis) { - throw new TextException("Not Supported"); - } - - public boolean isDateTimeCapable() { - return false; - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - short val = dataInput.readShort(); - return Short.valueOf(val); - } - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - - Short value = (Short)v; - if (value == null){ - dataOutput.writeBoolean(false); - } else { - dataOutput.writeBoolean(true); - dataOutput.writeShort(value.shortValue()); - } - } -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebean.text.TextException; +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; + +/** + * ScalarType for Short and short. + */ +public class ScalarTypeShort extends ScalarTypeBase { + + public ScalarTypeShort() { + super(Short.class, true, Types.SMALLINT); + } + + public void bind(DataBind b, Short value) throws SQLException { + if (value == null){ + b.setNull(Types.SMALLINT); + } else { + b.setShort(value.shortValue()); + } + } + + public Short read(DataReader dataReader) throws SQLException { + + return dataReader.getShort(); + } + + public Object toJdbcType(Object value) { + return BasicTypeConverter.toShort(value); + } + + public Short toBeanType(Object value) { + return BasicTypeConverter.toShort(value); + } + + public String formatValue(Short v) { + return v.toString(); + } + + public Short parse(String value) { + return Short.valueOf(value); + } + + public Short parseDateTime(long systemTimeMillis) { + throw new TextException("Not Supported"); + } + + public boolean isDateTimeCapable() { + return false; + } + + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + short val = dataInput.readShort(); + return Short.valueOf(val); + } + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + + Short value = (Short)v; + if (value == null){ + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeShort(value.shortValue()); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeString.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeString.java index 048f51d34..254cd12fa 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeString.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeString.java @@ -1,115 +1,96 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Types; - -import com.avaje.ebean.text.json.JsonValueAdapter; -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; -import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; - -/** - * ScalarType for String. - */ -public class ScalarTypeString extends ScalarTypeBase { - - public ScalarTypeString() { - super(String.class, true, Types.VARCHAR); - } - - public void bind(DataBind b, String value) throws SQLException { - if (value == null){ - b.setNull(Types.VARCHAR); - } else { - b.setString(value); - } - } - - public String read(DataReader dataReader) throws SQLException { - - return dataReader.getString(); - } - - public Object toJdbcType(Object value) { - return BasicTypeConverter.toString(value); - } - - public String toBeanType(Object value) { - return BasicTypeConverter.toString(value); - } - - public String formatValue(String t) { - return t; - } - - public String parse(String value) { - return value; - } - - public String parseDateTime(long systemTimeMillis) { - return String.valueOf(systemTimeMillis); - } - - public boolean isDateTimeCapable() { - return true; - } - - - @Override - public void jsonWrite(WriteJsonBuffer buffer, String value, JsonValueAdapter ctx) { - String s = format(value); - EscapeJson.escapeQuote(s, buffer); - } - - @Override - public String jsonFromString(String value, JsonValueAdapter ctx) { - return value; - } - - @Override - public String jsonToString(String value, JsonValueAdapter ctx) { - return EscapeJson.escapeQuote(value); - } - - public Object readData(DataInput dataInput) throws IOException { - if (!dataInput.readBoolean()) { - return null; - } else { - return dataInput.readUTF(); - } - } - - public void writeData(DataOutput dataOutput, Object v) throws IOException { - - String value = (String)v; - if (value == null){ - dataOutput.writeBoolean(false); - } else { - dataOutput.writeBoolean(true); - dataOutput.writeUTF(value); - } - } - - -} +package com.avaje.ebeaninternal.server.type; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; + +import com.avaje.ebean.text.json.JsonValueAdapter; +import com.avaje.ebeaninternal.server.core.BasicTypeConverter; +import com.avaje.ebeaninternal.server.text.json.WriteJsonBuffer; + +/** + * ScalarType for String. + */ +public class ScalarTypeString extends ScalarTypeBase { + + public ScalarTypeString() { + super(String.class, true, Types.VARCHAR); + } + + public void bind(DataBind b, String value) throws SQLException { + if (value == null){ + b.setNull(Types.VARCHAR); + } else { + b.setString(value); + } + } + + public String read(DataReader dataReader) throws SQLException { + + return dataReader.getString(); + } + + public Object toJdbcType(Object value) { + return BasicTypeConverter.toString(value); + } + + public String toBeanType(Object value) { + return BasicTypeConverter.toString(value); + } + + public String formatValue(String t) { + return t; + } + + public String parse(String value) { + return value; + } + + public String parseDateTime(long systemTimeMillis) { + return String.valueOf(systemTimeMillis); + } + + public boolean isDateTimeCapable() { + return true; + } + + + @Override + public void jsonWrite(WriteJsonBuffer buffer, String value, JsonValueAdapter ctx) { + String s = format(value); + EscapeJson.escapeQuote(s, buffer); + } + + @Override + public String jsonFromString(String value, JsonValueAdapter ctx) { + return value; + } + + @Override + public String jsonToString(String value, JsonValueAdapter ctx) { + return EscapeJson.escapeQuote(value); + } + + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + return dataInput.readUTF(); + } + } + + public void writeData(DataOutput dataOutput, Object v) throws IOException { + + String value = (String)v; + if (value == null){ + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeUTF(value); + } + } + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeTime.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeTime.java index 0049e3988..01a245a08 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeTime.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeTime.java @@ -1,98 +1,79 @@ -/** - * 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.type; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.sql.Time; -import java.sql.Types; - -import com.avaje.ebeaninternal.server.core.BasicTypeConverter; - -/** - * ScalarType for java.sql.Time. - */ -public class ScalarTypeTime extends ScalarTypeBase