package org.geotools.arcsde.session; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Vector; import java.util.logging.Logger; import org.geotools.arcsde.ArcSdeException; import org.geotools.arcsde.logging.Loggers; import org.geotools.arcsde.versioning.ArcSdeVersionHandler; import com.esri.sde.sdk.client.SeColumnDefinition; import com.esri.sde.sdk.client.SeConnection; import com.esri.sde.sdk.client.SeDBMSInfo; import com.esri.sde.sdk.client.SeDelete; import com.esri.sde.sdk.client.SeError; import com.esri.sde.sdk.client.SeException; import com.esri.sde.sdk.client.SeFilter; import com.esri.sde.sdk.client.SeInsert; import com.esri.sde.sdk.client.SeLayer; import com.esri.sde.sdk.client.SeObjectId; import com.esri.sde.sdk.client.SeQuery; import com.esri.sde.sdk.client.SeQueryInfo; import com.esri.sde.sdk.client.SeRasterColumn; import com.esri.sde.sdk.client.SeRegistration; import com.esri.sde.sdk.client.SeRelease; import com.esri.sde.sdk.client.SeRow; import com.esri.sde.sdk.client.SeSqlConstruct; import com.esri.sde.sdk.client.SeState; import com.esri.sde.sdk.client.SeStreamOp; import com.esri.sde.sdk.client.SeTable; import com.esri.sde.sdk.client.SeUpdate; import com.esri.sde.sdk.client.SeVersion; public class Commands { private static final Logger LOGGER = Loggers.getLogger("org.geotools.arcsde.session"); /** * Creates either a direct child state of parentStateId, or a sibling being an exact copy of * parentStatId if either the state can't be closed because its in use or parentStateId does not * belong to the current user. */ public static final class CreateVersionStateCommand extends Command<SeState> { private final long parentStateId; public CreateVersionStateCommand(final long parentStateId) { this.parentStateId = parentStateId; } @Override public SeState execute(ISession session, SeConnection connection) throws SeException, IOException { SeState parentState = new SeState(connection, new SeObjectId(parentStateId)); SeState realParent = null; boolean mergeParentToRealParent = false; if (parentState.isOpen()) { // only closed states can have child states try { parentState.close(); realParent = parentState; } catch (SeException e) { final int errorCode = e.getSeError().getSdeError(); if (SeError.SE_STATE_INUSE == errorCode || SeError.SE_NO_PERMISSIONS == errorCode) { // it's not our state or somebody's editing it so we // need to clone the parent, // starting from the parent of the parent realParent = new SeState(connection, parentState.getParentId()); mergeParentToRealParent = true; } else { throw e; } } } else { realParent = parentState; } // create the new state SeState newState = new SeState(connection); newState.create(realParent.getId()); if (mergeParentToRealParent) { // a sibling of parentStateId was created instead of a // child, we need to merge the changes // in parentStateId to the new state so they refer to the // same content. // SE_state_merge applies changes to a parent state to // create a new merged state. // The new state is the child of the parent state with the // changes of the second state. // Both input states must have the same parent state. // When a row has been changed in both parent and second // states, the row from the changes state is used. // The parent and changes states must be open or owned by // the current user unless the current user is the ArcSDE // DBA. newState.merge(realParent.getId(), parentState.getId()); } return newState; } } /** * Command to create, prepare, and execute a query. */ public static final class CreateAndExecuteQueryCommand extends Command<SeQuery> { private final String[] propertyNames; private final SeSqlConstruct sql; public CreateAndExecuteQueryCommand(final String[] propertyNames, final SeSqlConstruct sql) { this.propertyNames = propertyNames; this.sql = sql; } @Override public SeQuery execute(final ISession session, final SeConnection connection) throws SeException, IOException { SeQuery query = new SeQuery(connection, propertyNames, sql); query.prepareQuery(); query.execute(); return query; } } public static final class CreateSeStateCommand extends Command<SeState> { private final SeObjectId stateId; public CreateSeStateCommand(final SeObjectId stateId) { this.stateId = stateId; } @Override public SeState execute(final ISession session, final SeConnection connection) throws SeException, IOException { return new SeState(connection, stateId); } } /** * A command to close an {@link SeStreamOp stream} (ie, {@link SeDelete}, {@link SeInsert}, * {@link SeQuery}, {@link SeUpdate}). */ public static final class CloseStreamCommand extends Command<Object> { private final SeStreamOp stream; public CloseStreamCommand(final SeStreamOp stream) { this.stream = stream; } @Override public Void execute(final ISession session, final SeConnection connection) throws SeException, IOException { stream.close(); return null; } } public static final class CloseStateCommand extends Command<Object> { private final SeState state; public CloseStateCommand(final SeState state) { this.state = state; } @Override public Void execute(final ISession session, final SeConnection connection) throws SeException, IOException { state.close(); return null; } } public static final class DescribeTableCommand extends Command<SeColumnDefinition[]> { private final SeTable table; public DescribeTableCommand(SeTable table) { this.table = table; } @Override public SeColumnDefinition[] execute(final ISession session, final SeConnection connection) throws SeException, IOException { return table.describe(); } } public static class CreateSeTableCommand extends Command<SeTable> { private final String qualifiedName; public CreateSeTableCommand(final String qualifiedName) { this.qualifiedName = qualifiedName; } @Override public SeTable execute(final ISession session, final SeConnection connection) throws SeException, IOException { return new SeTable(connection, qualifiedName); } } /** * Command to check a connection is alive */ public static final Command<Void> TEST_SERVER = new Command<Void>() { @Override public Void execute(final ISession session, final SeConnection connection) throws SeException, IOException { connection.testServer(Session.TEST_SERVER_ROUNDTRIP_INTERVAL_SECONDS); return null; } }; /** * Command to fetch the default version */ public static final Command<SeVersion> GET_DEFAULT_VERSION = new GetVersionCommand( SeVersion.SE_QUALIFIED_DEFAULT_VERSION_NAME); /** * Command to fetch a version. * * @author Gabriel Roldan */ public static final class GetVersionCommand extends Command<SeVersion> { private String versionName; public GetVersionCommand(final String versionName) { this.versionName = versionName; } @Override public SeVersion execute(ISession session, SeConnection connection) throws SeException, IOException { final SeVersion version; try { version = new SeVersion(connection, versionName); } catch (SeException cause) { if (cause.getSeError().getSdeError() == -126) { ArrayList<String> available = new ArrayList<String>(); try { SeVersion[] versionList = connection.getVersionList(null); for (SeVersion v : versionList) { available.add(v.getName()); } throw new ArcSdeException("Specified ArcSDE version does not exist: " + versionName + ". Available versions are: " + available, cause); } catch (SeException ignorable) { // hum... ignore throw new ArcSdeException("Specified ArcSDE version does not exist: " + versionName, cause); } } else { throw cause; } } version.getInfo(); return version; } } public static final class GetTableCommand extends Command<SeTable> { private final String tableName; public GetTableCommand(final String tableName) { this.tableName = tableName; } @Override public SeTable execute(ISession session, SeConnection connection) throws SeException, IOException { SeTable table = new SeTable(connection, tableName); try { table.describe(); } catch (SeException e) { throw new IOException("Table does not exist: " + tableName); } return table; } } public static final class GetLayerCommand extends Command<SeLayer> { private final SeTable table; public GetLayerCommand(final SeTable table) { this.table = table; } /** * @return the SeLayer corresponding to the provided SeTable, or {@code null} if the table * has no shape column */ public SeLayer execute(ISession session, SeConnection connection) throws SeException, IOException { final String shapeColumn = getShapeColumn(table); if (shapeColumn == null) { return null; } final String layerName = table.getQualifiedName(); SeLayer layer = new SeLayer(connection, layerName, shapeColumn); return layer; } private String getShapeColumn(SeTable table) throws ArcSdeException { try { for (SeColumnDefinition aDef : table.describe()) { if (aDef.getType() == SeColumnDefinition.TYPE_SHAPE) { return aDef.getName(); } } } catch (SeException e) { throw new ArcSdeException("Exception describing table " + table.getName(), e); } return null; } } public static class FetchRowCommand extends Command<SdeRow> { private final SeQuery query; private final SdeRow holder; public FetchRowCommand(final SeQuery query, final SdeRow holder) { this.query = query; this.holder = holder; } @Override public SdeRow execute(ISession session, SeConnection connection) throws SeException, IOException { SeRow row = query.fetch(); if (row == null) { return null; } else { holder.setRow(row); } return holder; } } public static final Command<Void> START_TRANSACTION = new Command<Void>() { @Override public Void execute(final ISession session, final SeConnection connection) throws SeException, IOException { connection.setTransactionAutoCommit(0); connection.startTransaction(); return null; } }; public static final Command<List<String>> GET_RASTER_COLUMN_NAMES = new Command<List<String>>() { @SuppressWarnings("unchecked") @Override public List<String> execute(final ISession session, final SeConnection connection) throws SeException, IOException { final Vector<SeRasterColumn> rasterColumns = connection.getRasterColumns(); List<String> names = new ArrayList<String>(rasterColumns.size()); for (SeRasterColumn col : rasterColumns) { names.add(col.getQualifiedTableName()); } return names; } }; public static final Command<Void> COMMIT_TRANSACTION = new CommitTransaction(); public static final class CommitTransaction extends Command<Void> { @Override public Void execute(final ISession session, final SeConnection connection) throws SeException, IOException { connection.commitTransaction(); return null; } } public static final Command<Void> ROLLBACK_TRANSACTION = new Command<Void>() { @Override public Void execute(final ISession session, final SeConnection connection) throws SeException, IOException { connection.rollbackTransaction(); return null; } }; public static final class CloseConnection extends Command<Void> { @Override public Void execute(final ISession session, final SeConnection connection) throws SeException, IOException { try { connection.close(); LOGGER.fine(session.toString() + " successfully closed"); } catch (Exception ignore) { LOGGER.fine("Problem closing " + session.toString() + ": " + ignore.getMessage()); } return null; } }; public static final Command<Void> CLOSE_CONNECTION = new CloseConnection(); public static final Command<List<SeLayer>> GET_LAYERS = new Command<List<SeLayer>>() { @SuppressWarnings("unchecked") @Override public List<SeLayer> execute(final ISession session, final SeConnection connection) throws SeException, IOException { return connection.getLayers(); } }; public static final Command<String> GET_USER = new Command<String>() { @Override public String execute(final ISession session, final SeConnection connection) throws SeException, IOException { return connection.getUser(); } }; public static final Command<SeRelease> GET_RELEASE = new Command<SeRelease>() { @Override public SeRelease execute(final ISession session, final SeConnection connection) throws SeException, IOException { return connection.getRelease(); } }; public static final Command<String> GET_DATABASENAME = new Command<String>() { @Override public String execute(final ISession session, final SeConnection connection) throws SeException, IOException { return connection.getDatabaseName(); } }; public static final Command<SeDBMSInfo> GET_DBMS_INFO = new Command<SeDBMSInfo>() { @Override public SeDBMSInfo execute(final ISession session, final SeConnection connection) throws SeException, IOException { return connection.getDBMSInfo(); } }; public static final Command<SeInsert> CREATE_SEINSERT = new Command<SeInsert>() { @Override public SeInsert execute(final ISession session, final SeConnection connection) throws SeException, IOException { return new SeInsert(connection); } }; public static final Command<SeUpdate> CREATE_SEUPDATE = new Command<SeUpdate>() { @Override public SeUpdate execute(final ISession session, final SeConnection connection) throws SeException, IOException { return new SeUpdate(connection); } }; public static final Command<SeDelete> CREATE_SEDELETE = new Command<SeDelete>() { @Override public SeDelete execute(final ISession session, final SeConnection connection) throws SeException, IOException { return new SeDelete(connection); } }; public static final class CreateSeRegistrationCommand extends Command<SeRegistration> { private String typeName; public CreateSeRegistrationCommand(final String typeName) { this.typeName = typeName; } @Override public SeRegistration execute(final ISession session, final SeConnection connection) throws SeException, IOException { return new SeRegistration(connection, typeName); } } /** * * @author groldan */ public static final class PrepareQueryCommand extends Command<SeQuery> { private final SeFilter[] spatialConstraints; private final SeQueryInfo qInfo; private ArcSdeVersionHandler version; public PrepareQueryCommand(final SeQueryInfo qInfo, final SeFilter[] spatialConstraints, final ArcSdeVersionHandler version) { this.qInfo = qInfo; this.spatialConstraints = spatialConstraints; this.version = version; } @Override public SeQuery execute(ISession session, SeConnection connection) throws SeException, IOException { final SeQuery seQuery = new SeQuery(connection); version.setUpStream(session, seQuery); seQuery.prepareQueryInfo(qInfo); if (spatialConstraints.length > 0) { final boolean setReturnGeometryMasks = false; seQuery.setSpatialConstraints(SeQuery.SE_OPTIMIZE, setReturnGeometryMasks, spatialConstraints); } return seQuery; } } }