/*
* 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.domain.model;
import static java.util.Collections.unmodifiableList;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.base.Strings;
import de.unioninvestment.eai.portal.portlet.crud.config.DatabaseQueryConfig;
import de.unioninvestment.eai.portal.portlet.crud.config.StatementConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import com.vaadin.data.util.sqlcontainer.RowId;
import com.vaadin.data.util.sqlcontainer.SQLContainer;
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.container.FreeformQueryEventWrapper;
import de.unioninvestment.eai.portal.portlet.crud.domain.container.QueryCursorDataStream;
import de.unioninvestment.eai.portal.portlet.crud.domain.database.ConnectionPool;
import de.unioninvestment.eai.portal.portlet.crud.domain.exception.BusinessException;
import de.unioninvestment.eai.portal.portlet.crud.domain.model.container.DataStream;
import de.unioninvestment.eai.portal.support.vaadin.mvp.EventBus;
import de.unioninvestment.eai.portal.support.vaadin.table.DatabaseQueryDelegate;
/**
* Repräsentation eines auf SQL Anweisungen basierten Vaadin Containers als
* Backend für die Tabellenansicht.
*
* @author markus.bonsch
*
*/
public class DatabaseQueryContainer extends AbstractDatabaseContainer {
private static final List<OrderBy> EMPTY_ORDER_BY = unmodifiableList(new LinkedList<OrderBy>());
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory
.getLogger(DatabaseQueryContainer.class);
private final String query;
private final DatabaseQueryConfig config;
private boolean insertable;
private boolean updateable;
private boolean deleteable;
private final List<String> primaryKeys;
private final ConnectionPool connectionPool;
private DatabaseQueryDelegate databaseQueryDelegate;
@SuppressWarnings("unused")
private String currentUsername;
private final Integer pageLength;
private final Integer sizeValidTimeout;
private final Integer exportPageLength;
private boolean orderByPrimaryKeys;
/**
* Erzeugt bei der Initialisierung eine neue {@link SQLContainer} Instanz
* auf Basis der übergebenen SQLQuery.
*
* @param datasource
* Das DataSource-Kürzel
* @param sqlQuery
* Das select für die Query-Ansicht
* @param insertable
* ob insert-Statements erlaubt sind
* @param updateable
* ob update-Statements erlaubt sind
* @param deleteable
* ob delete-Statements erlaubt sind
* @param primaryKeys
* Liste der Primarykeys
* @param connectionPool
* Der zu verwendende ConnectionPool
* @param currentUsername
* Aktueller Benutzername
* @param filterPolicy
* @param sizeValidTimeout
* Cachttimeout für die Anzahl aller selektierten Einträge
* @param pageLength
* Anzahl der Einträge pro Seite
* @param orderByPrimaryKeys
* Es wird unabhängig von der aktuellen Sortierung immer auch
* nach den Primärschlüsselspalten sortiert. Dies ist nur bei
* Queries notwendig, die Daten nicht in einer konsistenten
* Reihenfolge liefern.
*/
public DatabaseQueryContainer(EventBus eventBus, DatabaseQueryConfig config,
String sqlQuery, boolean insertable, boolean updateable,
boolean deleteable, List<String> primaryKeys,
ConnectionPool connectionPool, String currentUsername,
Map<String, String> displayPattern,
List<ContainerOrder> defaultOrder, FilterPolicy filterPolicy,
int pageLength, int exportPageLength, Integer sizeValidTimeout,
boolean orderByPrimaryKeys) {
super(eventBus, displayPattern, defaultOrder, filterPolicy);
this.insertable = insertable;
this.updateable = updateable;
this.deleteable = deleteable;
this.pageLength = pageLength;
this.exportPageLength = exportPageLength;
this.sizeValidTimeout = sizeValidTimeout;
this.orderByPrimaryKeys = orderByPrimaryKeys;
Assert.notNull(config.getDatasource(), "DataSource is required");
Assert.notNull(sqlQuery, "SQL-Query is required");
this.config = config;
this.datasource = config.getDatasource();
this.tablename = config.getTablename();
this.query = sqlQuery;
this.primaryKeys = primaryKeys;
this.connectionPool = connectionPool;
this.currentUsername = currentUsername;
checkThatPrimaryKeysExistForEditing();
}
private void checkThatPrimaryKeysExistForEditing() {
if (isEditable()) {
if (primaryKeys == null || primaryKeys.isEmpty()) {
throw new BusinessException(
"portlet.crud.error.primaryKeysRequired");
}
}
}
@Override
protected String getNoTypeInformationForColumnMessage(String name) {
return MessageFormat.format("Could not retrieve type information for column ''{0}'' from query ''{1}''. Does it exist in the backend?", name, query);
}
private boolean isEditable() {
return insertable || updateable || deleteable;
}
private FreeformQueryEventWrapper getFreeformQueryEventWrapper() {
return (FreeformQueryEventWrapper) this.queryDelegate;
}
@Override
protected SQLContainerEventWrapper createVaadinContainer() {
try {
queryDelegate = new FreeformQueryEventWrapper(this, query,
connectionPool, getOnInsertEventRouter(),
getOnUpdateEventRouter(), getOnDeleteEventRouter(),
orderByPrimaryKeys,
primaryKeys.toArray(new String[primaryKeys.size()]));
this.getFreeformQueryEventWrapper().setDelegate(
databaseQueryDelegate);
SQLContainerEventWrapper sqlContainerEventWrapper = new SQLContainerEventWrapper(
queryDelegate, this, getOnCreateEventRouter());
sqlContainerEventWrapper.setPageLength(pageLength);
sqlContainerEventWrapper
.setSizeValidMilliSeconds((sizeValidTimeout * 1000));
// sqlContainerEventWrapper.setDebugMode(false);
return sqlContainerEventWrapper;
} catch (SQLException e) {
LOG.warn(e.getLocalizedMessage(), e);
throw new BusinessException("portlet.crud.error.wrongQuery",
datasource, query);
} catch (RuntimeException e) {
LOG.warn(e.getLocalizedMessage(), e);
throw new BusinessException("portlet.crud.error.wrongQuery",
datasource, query);
}
}
@Override
public boolean isInsertable() {
return insertable;
}
@Override
public boolean isUpdateable() {
return updateable;
}
@Override
public boolean isDeleteable() {
return deleteable;
}
@Override
public List<String> getPrimaryKeyColumns() {
return primaryKeys;
}
/**
* @param databaseQueryDelegate
* wird aus Scripting erstellt
*/
public void setDatabaseQueryDelegate(
DatabaseQueryDelegate databaseQueryDelegate) {
this.databaseQueryDelegate = databaseQueryDelegate;
}
public DatabaseQueryDelegate getDatabaseQueryDelegate() {
return databaseQueryDelegate;
}
@Override
public void commit() {
markRowsWithModifiedLobsAsModified();
super.commit();
clearAdditionalFields();
}
private void markRowsWithModifiedLobsAsModified() {
for (Entry<ContainerRowId, Map<String, ContainerBlob>> rowBlobs : blobFields
.entrySet()) {
for (ContainerBlob blob : rowBlobs.getValue().values()) {
if (blob.isModified()) {
getVaadinContainer().markRowAsModified(
rowBlobs.getKey().getInternalId());
}
}
}
for (Entry<ContainerRowId, Map<String, ContainerClob>> rowClobs : clobFields
.entrySet()) {
for (ContainerClob clob : rowClobs.getValue().values()) {
if (clob.isModified()) {
getVaadinContainer().markRowAsModified(
rowClobs.getKey().getInternalId());
}
}
}
}
@Override
public void rollback() {
try {
super.rollback();
} finally {
clearAdditionalFields();
}
}
@Override
public ContainerClob getCLob(ContainerRowId containerRowId,
String columnName) {
if (clobFields.containsKey(containerRowId)) {
if (clobFields.get(containerRowId).containsKey(columnName)) {
return clobFields.get(containerRowId).get(columnName);
}
}
if (containerRowId.getInternalId() instanceof TemporaryRowId) {
ContainerClob clob = new ContainerClob();
setCLob(containerRowId, columnName, clob);
return clob;
}
ContainerClob clob = new ContainerClob(
this.getFreeformQueryEventWrapper(), containerRowId, columnName);
setCLob(containerRowId, columnName, clob);
return clob;
}
@Override
public boolean isBLobEmpty(ContainerRowId rowId, String columnName) {
return this.getFreeformQueryEventWrapper().hasBlobData(
(RowId) rowId.getInternalId(), columnName);
}
@Override
public ContainerBlob getBLob(ContainerRowId containerRowId,
String columnName) {
if (blobFields.containsKey(containerRowId)) {
if (blobFields.get(containerRowId).containsKey(columnName)) {
return blobFields.get(containerRowId).get(columnName);
}
}
if (containerRowId.getInternalId() instanceof TemporaryRowId) {
ContainerBlob blob = new ContainerBlob();
setBLob(containerRowId, columnName, blob);
return blob;
}
ContainerBlob blob = new ContainerBlob(
this.getFreeformQueryEventWrapper(), containerRowId, columnName);
setBLob(containerRowId, columnName, blob);
return blob;
}
/**
* For unit test
*
* @param queryDelegate
*/
void setQueryDelegate(FreeformQueryEventWrapper queryDelegate) {
this.queryDelegate = queryDelegate;
}
@Override
public void withExportSettings(ExportWithExportSettings exportCallback) {
try {
getVaadinContainer().setPageLength(exportPageLength);
super.withExportSettings(exportCallback);
} finally {
getVaadinContainer().setPageLength(pageLength);
}
}
public StatementHelper getCurrentQuery(boolean preserveOrder) {
// just to initialize if not already done
getVaadinContainer();
if (preserveOrder) {
return databaseQueryDelegate.getQueryStatement(0, 0);
} else {
List<OrderBy> previousOrder = databaseQueryDelegate.getOrderBy();
try {
databaseQueryDelegate.setOrderBy(EMPTY_ORDER_BY);
return databaseQueryDelegate.getQueryStatement(0, 0);
} finally {
databaseQueryDelegate.setOrderBy(previousOrder);
}
}
}
@Override
public DataStream getStream() {
return new QueryCursorDataStream(getVaadinContainer(), connectionPool, getCurrentQuery(true));
}
}