org.compass.gps.device.jdbc.ResultSetJdbcGpsDevice Maven / Gradle / Ivy
Show all versions of compass Show documentation
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed 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 org.compass.gps.device.jdbc;
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.Iterator;
import java.util.List;
import org.compass.core.CompassException;
import org.compass.core.CompassSession;
import org.compass.core.Resource;
import org.compass.core.config.CommonMetaDataLookup;
import org.compass.core.mapping.Cascade;
import org.compass.core.spi.InternalCompass;
import org.compass.core.spi.InternalCompassSession;
import org.compass.gps.CompassGpsException;
import org.compass.gps.IndexPlan;
import org.compass.gps.device.jdbc.mapping.AutoGenerateMapping;
import org.compass.gps.device.jdbc.mapping.ColumnMapping;
import org.compass.gps.device.jdbc.mapping.ColumnToPropertyMapping;
import org.compass.gps.device.jdbc.mapping.ResultSetToResourceMapping;
import org.compass.gps.device.jdbc.mapping.VersionColumnMapping;
import org.compass.gps.device.jdbc.snapshot.ConfigureSnapshotEvent;
import org.compass.gps.device.jdbc.snapshot.CreateAndUpdateSnapshotEvent;
import org.compass.gps.device.jdbc.snapshot.DeleteSnapshotEvent;
import org.compass.gps.device.jdbc.snapshot.JdbcAliasRowSnapshot;
import org.compass.gps.device.jdbc.snapshot.JdbcAliasSnapshot;
import org.compass.gps.device.jdbc.snapshot.JdbcSnapshot;
/**
* A gps device that index a jdbc ResultSet
to a set of Compass
* Resource
s. Each Resource
maps to a
* ResultSet
row. The device can handle multiple
* ResultSet
s.
*
* The device holds a list of
* {@link org.compass.gps.device.jdbc.mapping.ResultSetToResourceMapping}s
* (or derived classes like
* {@link org.compass.gps.device.jdbc.mapping.TableToResourceMapping}).
* Each one has all the required mappings setting to map the
* ResultSet
with all it's rows to the set of corresponding
* Resource
s.
*
* The device can perform active data base mirroring. The mirror operation is
* enabled only if the mirror flag is enabled, and will execute against each
* mapping that
* {@link org.compass.gps.device.jdbc.mapping.ResultSetToResourceMapping#supportsVersioning()}.
*
* The autoDetectVersionColumnSqlType
setting (which defauls to
* true
) will automatically set the version column jdbc type for
* mappings that support versioning.
*
* @author kimchy
* @see org.compass.gps.device.jdbc.mapping.ResultSetToResourceMapping
* @see org.compass.gps.device.jdbc.mapping.TableToResourceMapping
*/
public class ResultSetJdbcGpsDevice extends AbstractJdbcActiveMirrorGpsDevice {
protected List mappings = new ArrayList();
private JdbcSnapshot snapshot;
private boolean autoDetectVersionColumnSqlType = true;
protected JdbcSnapshot getJdbcSnapshot() {
return this.snapshot;
}
/**
* performs operations on startup, such as auto generation of mappings for
* mappings that implement the {@link AutoGenerateMapping}, auto detection
* of version column jdbc type, and {@link JdbcSnapshot} loading (using the
* {@link org.compass.gps.device.jdbc.snapshot.JdbcSnapshotPersister}).
*/
protected void doStart() throws CompassGpsException {
super.doStart();
// call auto generate for mappings that implement the AutoGenerate
// interface
for (Iterator it = mappings.iterator(); it.hasNext();) {
ResultSetToResourceMapping rsMapping = (ResultSetToResourceMapping) it.next();
if (rsMapping instanceof AutoGenerateMapping) {
((AutoGenerateMapping) rsMapping).generateMappings(dataSource);
}
}
// support for meta data lookup
CommonMetaDataLookup commonMetaDataLookup = new CommonMetaDataLookup(((InternalCompass) compassGps
.getIndexCompass()).getMetaData());
for (Iterator it = mappings.iterator(); it.hasNext();) {
ResultSetToResourceMapping rsMapping = (ResultSetToResourceMapping) it.next();
rsMapping.setAlias(commonMetaDataLookup.lookupAliasName(rsMapping.getAlias()));
for (Iterator it1 = rsMapping.mappingsIt(); it1.hasNext();) {
List columns = (List) it1.next();
for (Iterator it2 = columns.iterator(); it2.hasNext();) {
ColumnMapping columnMapping = (ColumnMapping) it2.next();
if (columnMapping instanceof ColumnToPropertyMapping) {
ColumnToPropertyMapping columnToPropertyMapping = (ColumnToPropertyMapping) columnMapping;
columnToPropertyMapping.setPropertyName(commonMetaDataLookup
.lookupMetaDataName(columnToPropertyMapping.getPropertyName()));
}
}
}
}
// double check that all the result set mapping have Compass::Core
// resource mapping
for (Iterator it = mappings.iterator(); it.hasNext();) {
ResultSetToResourceMapping rsMapping = (ResultSetToResourceMapping) it.next();
if (!compassGps.hasMappingForEntityForMirror(rsMapping.getAlias(), Cascade.ALL)) {
throw new IllegalStateException(
buildMessage("No resource mapping defined in gps mirror compass for alias ["
+ rsMapping.getAlias() + "]. Did you defined a jdbc mapping builder?"));
}
if (!compassGps.hasMappingForEntityForIndex(rsMapping.getAlias())) {
throw new IllegalStateException(
buildMessage("No resource mapping defined in gps index compass for alias ["
+ rsMapping.getAlias() + "]. Did you defined a jdbc mapping builder?"));
}
}
if (isAutoDetectVersionColumnSqlType()) {
if (log.isInfoEnabled()) {
log.info(buildMessage("Auto detecting version column sql types"));
}
// set the version databse type
Connection connection = JdbcUtils.getConnection(dataSource);
PreparedStatement ps = null;
ResultSet rs = null;
try {
for (Iterator it = mappings.iterator(); it.hasNext();) {
ResultSetToResourceMapping mapping = (ResultSetToResourceMapping) it.next();
if (!mapping.supportsVersioning()) {
continue;
}
ps = connection.prepareStatement(mapping.getVersionQuery());
ps.setFetchSize(1);
rs = ps.executeQuery();
ResultSetMetaData metaData = rs.getMetaData();
for (Iterator verIt = mapping.versionMappingsIt(); verIt.hasNext();) {
VersionColumnMapping versionMapping = (VersionColumnMapping) verIt.next();
int columnIndex;
if (versionMapping.isUsingColumnIndex()) {
columnIndex = versionMapping.getColumnIndex();
} else {
columnIndex = JdbcUtils.getColumnIndexFromColumnName(metaData, versionMapping
.getColumnName());
}
versionMapping.setSqlType(metaData.getColumnType(columnIndex));
}
}
} catch (SQLException e) {
throw new JdbcGpsDeviceException(buildMessage("Failed to find version column type"), e);
} finally {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(ps);
JdbcUtils.closeConnection(connection);
}
}
if (isMirrorDataChanges()) {
if (log.isInfoEnabled()) {
log.info(buildMessage("Using mirroring, loading snapshot data"));
}
// set up the snapshot
snapshot = getSnapshotPersister().load();
for (Iterator it = mappings.iterator(); it.hasNext();) {
ResultSetToResourceMapping mapping = (ResultSetToResourceMapping) it.next();
if (mapping.supportsVersioning() && snapshot.getAliasSnapshot(mapping.getAlias()) == null) {
if (log.isDebugEnabled()) {
log.debug(buildMessage("Alias [" + mapping.getAlias() + "] not found in snapshot data, creating..."));
}
JdbcAliasSnapshot aliasSnapshot = new JdbcAliasSnapshot(mapping.getAlias());
snapshot.putAliasSnapshot(aliasSnapshot);
}
}
// configure the snapshot event listener
Connection connection = JdbcUtils.getConnection(dataSource);
try {
getSnapshotEventListener().configure(new ConfigureSnapshotEvent(connection, dialect, mappings));
} finally {
JdbcUtils.closeConnection(connection);
}
}
if (log.isDebugEnabled()) {
for (Iterator it = mappings.iterator(); it.hasNext();) {
log.debug(buildMessage("Using DB Mapping " + it.next()));
}
}
}
/**
* Saves the {@link JdbcSnapshot}.
*/
protected void doStop() throws CompassGpsException {
getSnapshotPersister().save(snapshot);
super.doStop();
}
protected void doIndex(CompassSession session, IndexPlan indexPlan) throws CompassGpsException {
// TODO take into account the index plan
// reset the snapshot data before we perform the index operation
snapshot = new JdbcSnapshot();
for (Iterator it = mappings.iterator(); it.hasNext();) {
ResultSetToResourceMapping mapping = (ResultSetToResourceMapping) it.next();
if (mapping.supportsVersioning()) {
JdbcAliasSnapshot aliasSnapshot = new JdbcAliasSnapshot(mapping.getAlias());
snapshot.putAliasSnapshot(aliasSnapshot);
}
}
super.doIndex(session);
// save the sanpshot data
getSnapshotPersister().save(snapshot);
}
/**
* Returns the array of index execution with a size of the number of
* mappings.
*/
protected IndexExecution[] doGetIndexExecutions(Connection connection) throws SQLException, JdbcGpsDeviceException {
IndexExecution[] indexExecutions = new IndexExecution[mappings.size()];
for (int i = 0; i < indexExecutions.length; i++) {
ResultSetToResourceMapping mapping = (ResultSetToResourceMapping) mappings.get(i);
indexExecutions[i] = new IndexExecution(mapping, mapping.getSelectQuery());
}
return indexExecutions;
}
/**
* Index the given ResultSet
row into a Compass
* Resource
.
*/
protected Object processRowValue(Object description, ResultSet rs, CompassSession session) throws SQLException,
CompassException {
if (log.isDebugEnabled()) {
StringBuffer sb = new StringBuffer();
sb.append(buildMessage("Indexing data row with values "));
ResultSetMetaData metaData = rs.getMetaData();
for (int i = 1; i <= metaData.getColumnCount(); i++) {
sb.append("[").append(metaData.getColumnName(i)).append(":");
String value = rs.getString(i);
if (rs.wasNull()) {
value = "(null)";
}
sb.append(value);
sb.append("] ");
}
log.debug(sb.toString());
}
ResultSetToResourceMapping mapping = (ResultSetToResourceMapping) description;
JdbcAliasRowSnapshot rowSnapshot = null;
if (shouldMirrorDataChanges() && mapping.supportsVersioning()) {
rowSnapshot = new JdbcAliasRowSnapshot();
}
Resource resource = ((InternalCompassSession) session).getCompass().getResourceFactory().createResource(mapping.getAlias());
ResultSetRowMarshallHelper marshallHelper = new ResultSetRowMarshallHelper(mapping, session, dialect, resource,
rowSnapshot);
marshallHelper.marshallResultSet(rs);
if (shouldMirrorDataChanges() && mapping.supportsVersioning()) {
snapshot.getAliasSnapshot(mapping.getAlias()).putRow(rowSnapshot);
}
return resource;
}
/**
* Performs the data change mirroring operation.
*/
public synchronized void performMirroring() throws JdbcGpsDeviceException {
if (!shouldMirrorDataChanges() || isPerformingIndexOperation()) {
return;
}
if (snapshot == null) {
throw new IllegalStateException(
buildMessage("Versioning data was not properly initialized, did you index the device or loaded the data?"));
}
Connection connection = JdbcUtils.getConnection(dataSource);
PreparedStatement ps = null;
ResultSet rs = null;
boolean dirtySnapshot = false;
try {
for (Iterator it = mappings.iterator(); it.hasNext();) {
ResultSetToResourceMapping mapping = (ResultSetToResourceMapping) it.next();
if (!mapping.supportsVersioning()) {
continue;
}
JdbcAliasSnapshot oldAliasSnapshot = snapshot.getAliasSnapshot(mapping.getAlias());
if (oldAliasSnapshot == null) {
log.warn(buildMessage("No snapshot for alias [" + mapping.getAlias()
+ "] even though there should be support for versioning ignoring the alias"));
continue;
}
JdbcAliasSnapshot newAliasSnapshot = new JdbcAliasSnapshot(mapping.getAlias());
ArrayList createdRows = new ArrayList();
ArrayList updatedRows = new ArrayList();
ArrayList deletedRows = new ArrayList();
if (log.isDebugEnabled()) {
log.debug(buildMessage("Executing version query [" + mapping.getVersionQuery() + "]"));
}
ps = connection.prepareStatement(mapping.getVersionQuery());
if (getFetchSize() > 0) {
ps.setFetchSize(getFetchSize());
}
rs = ps.executeQuery();
while (rs.next()) {
if (log.isDebugEnabled()) {
StringBuffer sb = new StringBuffer();
sb.append(buildMessage("Version row with values "));
ResultSetMetaData metaData = rs.getMetaData();
for (int i = 1; i <= metaData.getColumnCount(); i++) {
sb.append("[").append(metaData.getColumnName(i)).append(":");
String value = rs.getString(i);
if (rs.wasNull()) {
value = "(null)";
}
sb.append(value);
sb.append("] ");
}
log.debug(sb.toString());
}
JdbcAliasRowSnapshot newRowSnapshot = new JdbcAliasRowSnapshot();
ResultSetRowMarshallHelper marshallHelper = new ResultSetRowMarshallHelper(mapping, dialect,
newRowSnapshot, compassGps.getMirrorCompass());
marshallHelper.marshallResultSet(rs);
// new and old have the same ids
JdbcAliasRowSnapshot oldRowSnapshot = oldAliasSnapshot.getRow(newRowSnapshot);
// new row or updated row
if (oldRowSnapshot == null) {
createdRows.add(newRowSnapshot);
} else if (oldRowSnapshot.isOlderThan(newRowSnapshot)) {
updatedRows.add(newRowSnapshot);
}
newAliasSnapshot.putRow(newRowSnapshot);
}
for (Iterator oldRowIt = oldAliasSnapshot.rowSnapshotIt(); oldRowIt.hasNext();) {
JdbcAliasRowSnapshot tmpRow = (JdbcAliasRowSnapshot) oldRowIt.next();
// deleted row
if (newAliasSnapshot.getRow(tmpRow) == null) {
deletedRows.add(tmpRow);
}
}
if (!createdRows.isEmpty() || !updatedRows.isEmpty()) {
dirtySnapshot = true;
getSnapshotEventListener().onCreateAndUpdate(
new CreateAndUpdateSnapshotEvent(connection, dialect, mapping, createdRows, updatedRows,
compassGps));
}
if (!deletedRows.isEmpty()) {
dirtySnapshot = true;
getSnapshotEventListener().onDelete(
new DeleteSnapshotEvent(connection, dialect, mapping, deletedRows, compassGps));
}
snapshot.putAliasSnapshot(newAliasSnapshot);
}
} catch (SQLException e) {
throw new JdbcGpsDeviceException(buildMessage("Failed while mirroring data changes"), e);
} finally {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(ps);
JdbcUtils.closeConnection(connection);
}
if (isSaveSnapshotAfterMirror() && dirtySnapshot) {
getSnapshotPersister().save(snapshot);
}
}
/**
* Adds a mapping to be indexed and mirrored.
*/
public void addMapping(ResultSetToResourceMapping mapping) {
this.mappings.add(mapping);
}
/**
* Adds an array of mappings to be indexed and mirrored.
*/
public void setMappings(ResultSetToResourceMapping[] mappingsArr) {
for (int i = 0; i < mappingsArr.length; i++) {
addMapping(mappingsArr[i]);
}
}
/**
* Should the device auto detect the version columns jdbc type.
*/
public boolean isAutoDetectVersionColumnSqlType() {
return autoDetectVersionColumnSqlType;
}
/**
* Sets if the device auto detect the version columns jdbc type.
*/
public void setAutoDetectVersionColumnSqlType(boolean autoDetectVersionColumnSqlType) {
this.autoDetectVersionColumnSqlType = autoDetectVersionColumnSqlType;
}
}