001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/io/sdeapi/Transaction.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005       Department of Geography, University of Bonn
006     and
007       lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035    ----------------------------------------------------------------------------*/
036    package org.deegree.io.sdeapi;
037    
038    import java.io.ByteArrayInputStream;
039    import java.util.ArrayList;
040    import java.util.Date;
041    import java.util.HashMap;
042    import java.util.Iterator;
043    import java.util.Vector;
044    
045    import org.deegree.framework.log.ILogger;
046    import org.deegree.framework.log.LoggerFactory;
047    import org.deegree.framework.util.TimeTools;
048    import org.deegree.model.spatialschema.Geometry;
049    
050    import com.esri.sde.sdk.client.SeColumnDefinition;
051    import com.esri.sde.sdk.client.SeConnection;
052    import com.esri.sde.sdk.client.SeCoordinateReference;
053    import com.esri.sde.sdk.client.SeDelete;
054    import com.esri.sde.sdk.client.SeException;
055    import com.esri.sde.sdk.client.SeExtent;
056    import com.esri.sde.sdk.client.SeInsert;
057    import com.esri.sde.sdk.client.SeLayer;
058    import com.esri.sde.sdk.client.SeRow;
059    import com.esri.sde.sdk.client.SeShape;
060    import com.esri.sde.sdk.client.SeTable;
061    import com.esri.sde.sdk.client.SeUpdate;
062    
063    /**
064     * the class offers access to the transactional behavior of the a connection to ArcSDE
065     *
066     * @version $Revision: 18195 $ $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
067     */
068    public class Transaction {
069    
070        private static ILogger LOG = LoggerFactory.getLogger( Transaction.class );
071    
072        // Connection to SDE
073        private SeConnection conn = null;
074    
075        // Currently opened Layer and associated Table
076        private SeLayer layer = null;
077    
078        private HashMap<String, SeColumnDefinition> colDefs = null;
079    
080        private ArrayList<SeColumnDefinition> colDefsList = null;
081    
082        // Current Spatial Filter - a BoundingBox
083        private SeShape spatialFilter = null;
084    
085        /**
086         * Creates a new SpatialQuery object.
087         *
088         * @param server
089         * @param port
090         * @param database
091         * @param user
092         * @param password
093         *
094         * @throws SeException
095         */
096        public Transaction( String server, int port, String database, String user, String password ) throws SeException {
097            openConnection( server, port, database, user, password );
098        }
099    
100        /**
101         * Connect to the ArcSDE server <br>
102         * throws SeException
103         *
104         * @param server
105         * @param port
106         * @param database
107         * @param user
108         * @param password
109         * @throws SeException
110         */
111        public void openConnection( String server, int port, String database, String user, String password )
112                                throws SeException {
113    
114            conn = new SeConnection( server, port, database, user, password );
115    
116        }
117    
118        /**
119         * Close the current connection to the ArcSDE server <br>
120         * throws SeException
121         *
122         * @throws SeException
123         */
124        public void closeConnection()
125                                throws SeException {
126            conn.close();
127        }
128    
129        /**
130         * Set a SDE layer to work on and appropriate table <br>
131         * throws SeException
132         *
133         * @param layername
134         * @throws SeException
135         */
136        public void setLayer( String layername )
137                                throws SeException {
138    
139            Vector<?> layerList = conn.getLayers();
140            String spatialCol = "";
141    
142            for ( int i = 0; i < layerList.size(); i++ ) {
143                SeLayer layer = (SeLayer) layerList.elementAt( i );
144    
145                if ( layer.getQualifiedName().trim().equalsIgnoreCase( layername ) ) {
146                    spatialCol = layer.getSpatialColumn();
147                    break;
148                }
149            }
150    
151            layer = new SeLayer( conn, layername, spatialCol );
152            SeTable table = new SeTable( conn, layer.getQualifiedName() );
153            SeColumnDefinition[] cols = table.describe();
154            colDefs = new HashMap<String, SeColumnDefinition>();
155            colDefsList = new ArrayList<SeColumnDefinition>();
156            for ( int i = 0; i < cols.length; i++ ) {
157                colDefs.put( cols[i].getName(), cols[i] );
158                colDefsList.add( cols[i] );
159            }
160    
161        }
162    
163        /**
164         * Set a SpatialFilter to Query (BoundingBox) <br>
165         * throws SeException
166         *
167         * @param minx
168         * @param miny
169         * @param maxx
170         * @param maxy
171         * @throws SeException
172         */
173        public void setSpatialFilter( double minx, double miny, double maxx, double maxy )
174                                throws SeException {
175    
176            spatialFilter = new SeShape( layer.getCoordRef() );
177    
178            SeExtent extent = new SeExtent( minx, miny, maxx, maxy );
179            spatialFilter.generateRectangle( extent );
180    
181        }
182    
183        /**
184         * inserts a feature into the ArcSDE
185         *
186         * @param inRow
187         *            feature/row to be inserted
188         *
189         * @throws SeException
190         * @throws DeegreeSeException
191         */
192        public void insertFeature( HashMap<?, ?> inRow )
193                                throws SeException, DeegreeSeException {
194    
195            ArrayList<String> list = new ArrayList<String>();
196            // get all fields of the row where the values are not null
197            for ( int i = 0; i < colDefsList.size(); i++ ) {
198                SeColumnDefinition cd = colDefsList.get( i );
199                if ( inRow.get( cd.getName() ) != null || inRow.get( cd.getName().toUpperCase() ) != null ) {
200                    list.add( cd.getName() );
201                }
202            }
203            String[] columns = list.toArray( new String[list.size()] );
204    
205            SeInsert insert = null;
206            try {
207                // create an insert object
208                insert = new SeInsert( conn );
209                insert.intoTable( layer.getName(), columns );
210                insert.setWriteMode( true );
211    
212                SeRow row = insert.getRowToSet();
213                SeColumnDefinition[] cols = row.getColumns();
214    
215                // get reference system
216                SeCoordinateReference coordref = layer.getCoordRef();
217                for ( int i = 0; i < cols.length; i++ ) {
218                    Object o = inRow.get( cols[i].getName() );
219                    if ( o == null ) {
220                        o = inRow.get( cols[i].getName().toUpperCase() );
221                    }
222                    if ( o != null ) {
223                        int type = cols[i].getType();
224                        row = setValue( row, i, type, o, coordref );
225                    }
226                }
227                // perform insert operation
228                insert.execute();
229                insert.flushBufferedWrites();
230            } catch ( SeException e ) {
231                throw e;
232            } finally {
233                // Making sure the insert stream was closed. If the stream isn't closed,
234                // the resources used by the stream will be held/locked by the stream
235                // until the associated connection is closed.
236                try {
237                    insert.close();
238                } catch ( SeException se ) {
239                    se.printStackTrace();
240                }
241            }
242    
243        }
244    
245        /**
246         * fills the passed row with the also passed value considering its type
247         *
248         * @param row
249         *            SDE row to insert
250         * @param pos
251         *            position where to set the value in the row
252         * @param type
253         *            value type
254         * @param value
255         *            value to insert
256         */
257        private SeRow setValue( SeRow row, int pos, int type, Object value, SeCoordinateReference crs )
258                                throws SeException, DeegreeSeException {
259    
260            switch ( type ) {
261            case SeColumnDefinition.TYPE_BLOB: {
262                if ( value == null ) {
263                    row.setBlob( pos, null );
264                } else {
265                    row.setBlob( pos, (ByteArrayInputStream) value );
266                }
267                break;
268            }
269            case SeColumnDefinition.TYPE_DATE: {
270                if ( value != null && value instanceof String ) {
271                    value = TimeTools.createCalendar( (String) value ).getTime();
272                }
273                row.setDate( pos, (Date) value );
274                break;
275            }
276            case SeColumnDefinition.TYPE_FLOAT64: {
277                if ( value != null && value instanceof String ) {
278                    value = new Double( (String) value );
279                }
280                row.setDouble( pos, (Double) value );
281                break;
282            }
283            case SeColumnDefinition.TYPE_FLOAT32: {
284                if ( value != null && value instanceof String ) {
285                    value = new Float( (String) value );
286                }
287                row.setFloat( pos, (Float) value );
288                break;
289            }
290            case SeColumnDefinition.TYPE_INT32: {
291                if ( value != null && value instanceof String ) {
292                    value = new Integer( (String) value );
293                }
294                row.setInteger( pos, (Integer) value );
295                break;
296            }
297            case SeColumnDefinition.TYPE_RASTER: {
298                row.setBlob( pos, (ByteArrayInputStream) value );
299                break;
300            }
301            case SeColumnDefinition.TYPE_SHAPE: {
302                // TODO
303                /*
304                 * if (value != null && value instanceof String) { // if value is a string try to
305                 * convert it into GML try { value = GMLGeometryAdapter.wrap( (String)value ); } catch
306                 * (Exception e) { throw new SeInvalidShapeException( "the passed value "+ "isn't a GML
307                 * geometry\n" + e); } } if (value != null && value instanceof GMLGeometry) { // if
308                 * value is a GML convert it into a deegree geometry try { value =
309                 * GMLGeometryAdapter.wrap( ((GMLGeometry)value).getAsElement() ); } catch (Exception e) {
310                 * throw new SeInvalidShapeException( "the passed value/GML "+ "can't be transformed "+ "
311                 * to a deegree geometry\n" + e); } }
312                 */
313                try {
314                    if ( value != null ) {
315                        SeShape shp = SDEAdapter.export( (Geometry) value, crs );
316                        row.setShape( pos, shp );
317                    } else {
318                        row.setShape( pos, null );
319                    }
320                } catch ( Exception e ) {
321                    throw new DeegreeSeException( "the passed geometry can't  " + "be transformed to a SeShape\n" + e );
322                }
323                break;
324            }
325            case SeColumnDefinition.TYPE_INT16: {
326                if ( value != null && value instanceof String ) {
327                    value = new Short( (String) value );
328                }
329                row.setShort( pos, (Short) value );
330                break;
331            }
332            case SeColumnDefinition.TYPE_STRING: {
333                row.setString( pos, (String) value );
334                break;
335            }
336            }
337    
338            return row;
339        }
340    
341        /**
342         * updates a feature of the ArcSDE
343         *
344         * @param inRow
345         *            update data
346         * @param where
347         *            none spatial condtions to limit the targeted rows
348         * @param extent
349         *            spatial condtion to limit the targeted rows (not considered yet)
350         *
351         * @throws SeException
352         * @throws DeegreeSeException
353         */
354        public void updateFeature( HashMap<?, ?> inRow, String where, Geometry extent )
355                                throws SeException, DeegreeSeException {
356    
357            ArrayList<String> list = new ArrayList<String>();
358    
359            // get all fields of the row where the values are not null
360            Iterator<?> iterator = inRow.keySet().iterator();
361            while ( iterator.hasNext() ) {
362                Object o = iterator.next();
363                if ( o != null ) {
364                    list.add( (String) o );
365                }
366            }
367            String[] columns = list.toArray( new String[list.size()] );
368    
369            // get rows to be updated
370            // SeQuery query = new SeQuery( conn, columns, sqlCons );
371            SeUpdate update = new SeUpdate( conn );
372            SeTable table = new SeTable( conn, layer.getQualifiedName() );
373            // TODO use also spatial conditions
374            update.toTable( table.getName(), columns, where.trim() );
375            update.setWriteMode( true );
376    
377            SeRow row = update.getRowToSet();
378            SeCoordinateReference coordref = layer.getCoordRef();
379    
380            if ( row != null ) {
381                // while ( row != null ) {
382                for ( int i = 0; i < columns.length; i++ ) {
383                    int type = colDefs.get( columns[i] ).getType();
384                    row = setValue( row, i, type, inRow.get( columns[i] ), coordref );
385                }
386                // row = update.getRowToSet();
387                // }
388                update.execute();
389            } else {
390                LOG.logWarning( "No rows fetched/updated" );
391            }
392    
393            update.close();
394    
395        }
396    
397        /**
398         * deletes a feature from the ArcSDE
399         *
400         * @param where
401         *            none spatial condtions to limit the targeted rows
402         * @param extent
403         *            spatial condtion to limit the targeted rows (not considered yet)
404         *
405         * @throws SeException
406         */
407        public void deleteFeature( String where, Geometry extent )
408                                throws SeException {
409    
410            // TODO use also spatial conditions
411            SeDelete delete = new SeDelete( conn );
412            delete.fromTable( layer.getQualifiedName(), where );
413            delete.close();
414    
415        }
416    
417    }