/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package de.unioninvestment.eai.portal.portlet.crud.scripting.model; import groovy.lang.Closure; import groovy.lang.GString; import groovy.lang.MissingPropertyException; import java.sql.Connection; import java.sql.SQLException; import java.util.List; import org.codehaus.groovy.runtime.InvokerInvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.datasource.SingleConnectionDataSource; import com.vaadin.data.Container.Filter; import com.vaadin.data.util.sqlcontainer.RowId; import com.vaadin.data.util.sqlcontainer.RowItem; import com.vaadin.data.util.sqlcontainer.TemporaryRowId; import com.vaadin.data.util.sqlcontainer.query.OrderBy; import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; import de.unioninvestment.eai.portal.portlet.crud.domain.model.DataContainer; import de.unioninvestment.eai.portal.portlet.crud.domain.model.DatabaseContainerRow; import de.unioninvestment.eai.portal.portlet.crud.domain.model.user.CurrentUser; import de.unioninvestment.eai.portal.portlet.crud.domain.support.AuditLogger; import de.unioninvestment.eai.portal.portlet.crud.scripting.database.ExtendedSql; import de.unioninvestment.eai.portal.support.scripting.ScriptingException; import de.unioninvestment.eai.portal.support.vaadin.table.DatabaseQueryDelegate; /** * Implementation des FreeformStatementDelegate für das CRUD Portlet. * * @author markus.bonsch * */ public class ScriptDatabaseModificationsDelegate implements DatabaseQueryDelegate { private static final long serialVersionUID = 1L; private static final Logger LOG = LoggerFactory .getLogger(ScriptDatabaseModificationsDelegate.class); private final ScriptDatabaseContainer scriptContainer; private DataContainer container; private final StatementWrapper insertStatement; private final StatementWrapper updateStatement; private final StatementWrapper deleteStatement; private AuditLogger auditLogger; private DatabaseQueryDelegate queryDelegate; /** * Konstruktor. * * @param container * Datenbankcontainer * @param queryDelegate * Datenbankspezifische Klasse für Queries */ public ScriptDatabaseModificationsDelegate(final DataContainer container, DatabaseQueryDelegate queryDelegate) { this(container, null, null, null, null, null, queryDelegate); } /** * Konstruktor für editierbare Tabellen. * * @param container * Datenbankcontainer * @param insertStatement * Die Closure für INSERT Statements * @param updateStatement * Die Closure für UPDATE Statements * @param deleteStatement * Die Closure für DELETE Statements * @param queryDelegate * Datenbankspezifische Klasse für Queries */ public ScriptDatabaseModificationsDelegate(DataContainer container, StatementWrapper insertStatement, StatementWrapper updateStatement, StatementWrapper deleteStatement, ScriptDatabaseContainer scriptContainer, CurrentUser currentUser, DatabaseQueryDelegate queryDelegate) { this.container = container; this.insertStatement = insertStatement; this.updateStatement = updateStatement; this.deleteStatement = deleteStatement; this.scriptContainer = scriptContainer; this.queryDelegate = queryDelegate; this.auditLogger = new AuditLogger(currentUser); } @Override @Deprecated public String getQueryString(int offset, int limit) { return queryDelegate.getQueryString(offset, limit); } @Override public StatementHelper getQueryStatement(int offset, int limit) { return queryDelegate.getQueryStatement(offset, limit); } @Override public StatementHelper getIndexStatement(RowId rowId) { return queryDelegate.getIndexStatement(rowId); } @Override @Deprecated public String getCountQuery() { return queryDelegate.getCountQuery(); } @Override public StatementHelper getCountStatement() { return queryDelegate.getCountStatement(); } @Override @Deprecated public String getContainsRowQueryString(Object... keys) { return queryDelegate.getContainsRowQueryString(keys); } @Override public StatementHelper getContainsRowQueryStatement(Object... keys) { return queryDelegate.getContainsRowQueryStatement(keys); } @Override public StatementHelper getRowByIdStatement(RowId rowId) { return queryDelegate.getRowByIdStatement(rowId); } @Override public void setFilters(List<Filter> filters) { queryDelegate.setFilters(filters); } @Override public void setOrderBy(List<OrderBy> orderBys) { queryDelegate.setOrderBy(orderBys); } @Override public List<OrderBy> getOrderBy() { return queryDelegate.getOrderBy(); } @Override public int storeRow(Connection conn, RowItem row) throws SQLException { if (isNewRow(row)) { assertInsertIsPossible(); ExtendedSql sql = createSingleConnectionSql(conn); switch (insertStatement.getType()) { case SQL: GString insertGString = (GString) callClosureWithContainer( insertStatement.getStatementClosure(), row, sql); executeInsert(sql, insertGString); break; case SCRIPT: callClosureWithContainer(insertStatement.getStatementClosure(), row, sql); break; default: throw new UnsupportedOperationException( "Unbekannter Statement-Typ: " + insertStatement.getType()); } return 1; } else { assertUpdateIsPossible(); ExtendedSql sql = createSingleConnectionSql(conn); switch (updateStatement.getType()) { case SQL: GString updateGString = (GString) callClosureWithContainer( updateStatement.getStatementClosure(), row, sql); return executeUpdate(sql, updateGString); case SCRIPT: Object rowCount = callClosureWithContainer( updateStatement.getStatementClosure(), row, sql); if (rowCount != null && rowCount instanceof Integer) { return (Integer) rowCount; } else { throw new ScriptingException(null, "error.statement.illegalreturnvalue"); } default: throw new UnsupportedOperationException( "Unbekannter Statement-Typ: " + updateStatement.getType()); } } } private int executeUpdate(ExtendedSql sql, GString statementGString) throws SQLException { try { auditLogger.audit(statementGString.toString()); return sql.executeUpdate(statementGString); } finally { sql.close(); } } private void executeInsert(ExtendedSql sql, GString insertGString) throws SQLException { try { auditLogger.audit(insertGString.toString()); sql.execute(insertGString); } finally { sql.close(); } } protected ExtendedSql createSingleConnectionSql(Connection conn) { return new ExtendedSql(new SingleConnectionDataSource(conn, true)); } /** * Ruft die übergebende Closure auf und liefert ihre Rückgabe zurück. Die * Closure kann in ihrem Code auf {@code row} zugreifen. * * @param closure * die auszuführende Closure * @param row * zu bearbeitende Zeile * @return das Ergebnis des Closure-Aufrufs * @throws SQLException * wird bei jedem auftretenden Fehler geworfen, um ein Rollback * zu garantieren. */ private Object callClosureWithContainer(Closure<?> closure, RowItem row, ExtendedSql sql) throws SQLException { DatabaseContainerRow containerRow = (DatabaseContainerRow) container .convertItemToRow(row, false, true); ScriptRow scriptRow = new ScriptRow(containerRow); return callClosureAndHandleExceptions(closure, scriptContainer, scriptRow, sql); } private Object callClosureAndHandleExceptions(Closure<?> closure, Object... args) throws SQLException { try { return closure.call(args); } catch (MissingPropertyException e) { throw new SQLException("Unbekanntes Attribut '" + e.getProperty() + "'."); } catch (InvokerInvocationException e) { Throwable cause = e.getCause(); if (cause != null && cause instanceof SQLException) { throw (SQLException) cause; } else { throw new SQLException("Problem beim Skript-Aufruf.", e); } } catch (Exception e) { LOG.debug(e.getMessage(), e); throw new SQLException("Problem beim Skript-Aufruf.", e); } } private boolean isNewRow(RowItem row) { return row.getId() instanceof TemporaryRowId; } private void assertInsertIsPossible() { if (insertStatement == null) { throw new UnsupportedOperationException( "Insert is not possible - no INSERT string given"); } } private void assertUpdateIsPossible() { if (updateStatement == null) { throw new UnsupportedOperationException( "Update is not possible - no UPDATE string given"); } } private void assertDeleteIsPossible() { if (deleteStatement == null) { throw new UnsupportedOperationException( "Delete is not possible - no DELETE string given"); } } @Override public boolean removeRow(Connection conn, RowItem row) throws SQLException { assertDeleteIsPossible(); ExtendedSql sql = createSingleConnectionSql(conn); switch (deleteStatement.getType()) { case SQL: GString deleteGString = (GString) callClosureWithContainer( deleteStatement.getStatementClosure(), row, sql); return executeUpdate(sql, deleteGString) > 0; case SCRIPT: Object rowCount = callClosureWithContainer( deleteStatement.getStatementClosure(), row, sql); if (rowCount != null && rowCount instanceof Integer) { return (Integer) rowCount > 0; } else { throw new ScriptingException(null, "error.statement.illegalreturnvalue"); } default: throw new UnsupportedOperationException( "Unbekannter Statement-Typ: " + deleteStatement.getType()); } } }