Copy directories and preserve permissions

The following command will create a recursive copy of the current working directory preserving all file permissions and owners:

mkdir /var/backup/michael
cd /home/michael
tar cf - . | (cd /var/backup/michael && tar xBfp -)

Comes in handy while moving whole directory trees from one disk to a new one.

| Comments (0) »

18-Mar-07


Using Hibernate with Oracle Spatial

Oracle Spatial Datatype JGeometry from the spatial api (which can be found here) can easily be used with hibernate through a custom dialect and a custom type that delegates to an instance from oracle sdo api. Everything that is needed is here:

Mapping Spatial Oracle type SDO_GEOMETRY to JGeometry

The only quirk with this solution as of February 2007 is the fact, it won’t work with null columns. The fault lies in nullSafeSet. Either can the JGeometry delegate be null or, what is worse, preparedStatement.setNull( i, Types.OTHER); will fail with an invalid column type. The correct version of this method is as follows:

public void nullSafeSet( PreparedStatement preparedStatement, Object o, int i) throws HibernateException, SQLException {
  if( o == null) {
    preparedStatement.setNull(i, Types.STRUCT, "MDSYS.SDO_GEOMETRY");
  } else {
    if( o instanceof JGeometryType) {
      JGeometryType gt = (JGeometryType) o;
      OracleConnection oc = (OracleConnection) preparedStatement.getConnection().getMetaData().getConnection();
      if(gt.getJGeometry() == null)
        preparedStatement.setNull(i, Types.STRUCT, "MDSYS.SDO_GEOMETRY");
      else
        preparedStatement.setObject( i, JGeometry.store( (JGeometry) (gt).getJGeometry(), oc));   
    }
  }
}

Furthermore, i think the spatial dialect should be registered with the following class:

public class OracleSpatialDialect extends Oracle9Dialect {
	public OracleSpatialDialect() {
    super();
      registerColumnType( Types.OTHER, "MDSYS.SDO_GEOMETRY");
  }
}

Kudos to Joel Schuster from Navisys for the adapter class!

Update 2013-12-17

The linked JGeometryType is broken regarding it’s equals and hashCode contract.

I’m using the following implementation:

import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
 
import javax.sql.PooledConnection;
 
import oracle.jdbc.driver.OracleConnection;
import oracle.spatial.geometry.JGeometry;
import oracle.sql.STRUCT;
 
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;
 
/**
 * This object is a UserType that represents an SDO_GEOMETRY type for use with
 * Oracle 10g databases and the Oracle Spatial Libraries
 * 
 * It represents an SDO_GEOMETRY database type by wrapping the
 * oracle.spatial.geometry.JGeometry type and implementing
 * org.hibernate.usertype.UserType.
 * 
 * This class should be used with the OracleSpacialDialect class.
 * 
 * (NOTE: I tried just extending the JGeometry instead of aggregating it, that
 * doesn't work. The static load returns a JGeometry that can't be cast to the
 * sub-class)
 * 
 * {@linkplain http://info.michael-simons.eu/2007/02/05/using-hibernate-with-oracle-spatial/}
 * {@linkplain http://community.jboss.org/wiki/mappingspatialoracletypesdogeometrytojgeometry}
 * 
 * @author Joel Schuster - The NAVSYS Corporation
 * @author michael.simons
 */
public class JGeometryType implements UserType, Serializable {
	private static final long serialVersionUID = 1L;
	private JGeometry geometryInstance = null;
 
	/**
	 * This default constructor does create an instance of 1 point at origin
	 */
	public JGeometryType() {
		geometryInstance = new JGeometry( 0, 0, 0);
	}
 
	public JGeometryType( JGeometry geometryInstance) {
		this.geometryInstance = geometryInstance;
	}
 
	public JGeometryType( JGeometryType geometryType) {
		this.geometryInstance = geometryType.getJGeometry();
	}
	/* User Type Information */
	/*
	 * Note that the type that is reported is OTHER (1111) not STRUCT (2002),
	 * see: org.hibernate.dialect.Dialect
	 */
	private static final int[] SQL_TYPES = { Types.OTHER };
 
	public int[] sqlTypes() {
		return SQL_TYPES;
	}
 
	public Class<? extends Object> returnedClass() {
		return JGeometryType.class;
	}
 
	/**
	 * This method gives back the equals functionality that was deprecated by
	 * using the equals that's needed for the UserType
	 */
	@Override
	public boolean equals( Object arg0) {
		return equals( this, arg0);
	}
 
	public boolean equals( Object obj1, Object obj2) throws HibernateException {
		boolean rv = false;
		if(obj1==null && obj2 == null) {
			rv = true;
		// check we are dealing with non-null objects of the correct type 
		} else if( obj1 instanceof JGeometryType && obj2 instanceof JGeometryType && obj1 != null && obj2 != null) {
			JGeometry geo1 = ( (JGeometryType) obj1).getJGeometry();
			JGeometry geo2 = ( (JGeometryType) obj2).getJGeometry();
 
			// check that they are the same type 
			if( geo1.getType() != geo2.getType()) {
				rv = false;
			} else { // go through the different types and check for equality 
				switch( geo1.getType()) {
				case JGeometry.GTYPE_POINT:
					rv = geo1.getJavaPoint().equals( geo2.getJavaPoint());
					break;
				case JGeometry.GTYPE_MULTIPOINT:				
				case JGeometry.GTYPE_MULTICURVE:
				case JGeometry.GTYPE_MULTIPOLYGON:
				case JGeometry.GTYPE_POLYGON:
				case JGeometry.GTYPE_CURVE: 
					rv = Arrays.equals( geo1.getOrdinatesOfElements(), geo2.getOrdinatesOfElements());
					break;
				default: 
					throw new UnsupportedOperationException(String.format("Geometry type %d is not supported", geo1.getType()));
				}			
			}
		} 
 
		return rv;
	}
 
	public int hashCode(Object o) throws HibernateException {
		final JGeometry geo1 = ((JGeometryType) o).geometryInstance;		
		int rv = -1;
 
		switch(geo1.getType()) {
		case JGeometry.GTYPE_POINT: 
			rv = geo1.getJavaPoint().hashCode();
			break;
		case JGeometry.GTYPE_MULTIPOINT:				
		case JGeometry.GTYPE_MULTICURVE:
		case JGeometry.GTYPE_MULTIPOLYGON:
		case JGeometry.GTYPE_POLYGON:
		case JGeometry.GTYPE_CURVE:
			rv = Arrays.hashCode(geo1.getOrdinatesOfElements());
			break;
		default: 
			throw new UnsupportedOperationException(String.format("Geometry type %d is not supported", geo1.getType()));
		}			
		return rv;
	}
 
	/* calls the load method */
	@Override
	public Object nullSafeGet( ResultSet resultSet, String[] strings, final SessionImplementor sessionImplementor, Object o) throws HibernateException, SQLException {
		final STRUCT geometry = (STRUCT) resultSet.getObject( strings[0]);
		return resultSet.wasNull() || geometry == null ? null : new JGeometryType(JGeometry.load( geometry));
	}
 
	/* calls the store method */
	@Override
	public void nullSafeSet( PreparedStatement preparedStatement, Object o, int i, SessionImplementor sessionImplementor) throws HibernateException, SQLException {
		if( o == null) {
			preparedStatement.setNull(i, Types.STRUCT, "MDSYS.SDO_GEOMETRY");
		} else {
			if( o instanceof JGeometryType) {
				JGeometryType gt = (JGeometryType) o;				
				final Connection hlp = preparedStatement.getConnection().getMetaData().getConnection();
				OracleConnection oc = null;
				if(hlp instanceof OracleConnection)
					oc = (OracleConnection) hlp;
				else if(hlp instanceof PooledConnection)
					oc = (OracleConnection) ((PooledConnection)hlp).getConnection();
				if(gt.getJGeometry() == null)
					preparedStatement.setNull(i, Types.STRUCT, "MDSYS.SDO_GEOMETRY");
				else
					preparedStatement.setObject( i, JGeometry.store( (JGeometry) (gt).getJGeometry(), oc));   
			}
		}
	}
 
	/* uses the 'copy' constructor */
	public Object deepCopy( Object o) throws HibernateException {
		if( o == null)
			return null;
		if( o instanceof JGeometryType) {
			return new JGeometryType( ( (JGeometryType) o).getJGeometry());
		} else {
			return null;
		}
	}
 
	public boolean isMutable() {
		return false;
	}
 
	public Serializable disassemble( Object o) throws HibernateException {
		return (Serializable) deepCopy( o);
	}
 
	public Object assemble( Serializable serializable, Object o) throws HibernateException {
		return deepCopy( serializable);
	}
 
	public Object replace( Object o, Object o1, Object o2) throws HibernateException {
		return (JGeometryType) o;
	}
 
	/* accessor */
	public JGeometry getJGeometry() {
		return geometryInstance;
	}
}

| Comments (17) »

05-Feb-07


On writing binary data from within Oracle Forms 6i

There is this nice project of ours from 2002…. developed with Oracle Forms 6i and database PL/SQL Procedures. This project runs fine with an estimated 6gig database. Plain old client server model.

We have some XML / XSLT based HTML reports that are processed – thanks to Oracle 9i – directly in the database with packages dbms_xmlgen and xmlparser. Data is collected in a mere handful of so called “object views”, that are denormalized, hierarchical views on data.

After receiving the assignment to generate pdf it was no big problem at all to write some java classes around iText and dom4j to generate new reports.
I decided agains xsl-fo as i found no inexpensive tools for easily designing adequate stylesheets. For the worse, xsl-fo lacks several important features, i.e. collapsing borders. My customers would have laughed at best if i had presented the prototype…

So i have this java classes – all in the database for sure, no need for java on the client side -, i have added code to my package and i thought it would be easy to write the resulting blob with Forms 6i to the client…

Bah! There is no such easy thing in Forms. I remembered havin’ problems reading a clob, as the whole bunch of dbms_lob functionality doesn’t work in forms… So my workaround for creating text files from clobs was on the database side:

PROCEDURE  read_export(
  p_export_id  IN     NUMBER,
  p_menge      IN OUT BINARY_INTEGER,
  p_offset     IN     BINARY_INTEGER,
  p_buffer     IN OUT VARCHAR2
) IS
 clInhalt  CLOB;
 BEGIN
  BEGIN
    SELECT processed_export
      INTO clInhalt
      FROM xmlt_exporte
     WHERE export_id = p_export_id;
    DBMS_LOB.READ(clInhalt, p_menge, p_offset, p_buffer);
  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      p_menge:= 0;
  END;
EXCEPTION
  WHEN OTHERS THEN
    p_menge := 0;
END;

and in a forms on client side:

fhDateiOut := text_io.fopen(vcPath || vcFile, 'W');   	  	   	
LOOP			
  pck_xml.read_export(p_export_id, i, offset, vcBuffer);
  EXIT WHEN i = 0;
  offset := offset + i;
  text_io.put(fhDateiOut, vcBuffer);
END LOOP;					   
text_io.fclose(fhDateiOut);

(text_io is a forms standard package).

This works quite well.. So i thought, well, d2kwutil has some functions to write binary files, so i copy the database procedure, use dbms_lob.read with blob and raw and i’m done…
Yeah, i was done. Well fucked off.

I have no idea in which way dbms_lob.read converts blob data to raw, but it’s everything but the original. So after banging my head against the wall, finding things like “use software developed in this century” in forums, i wrote the following java code:

final BASE64Encoder enc = new BASE64Encoder();
 
final CLOB rv = CLOB.createTemporary(tmp.getJavaSqlConnection(), true, CLOB.DURATION_SESSION);
 
int bufSize = 1024*1024*1;
 
final BufferedInputStream in = new BufferedInputStream(tmp.getBinaryStream(), bufSize * 2);        
final BufferedOutputStream out = new BufferedOutputStream(rv.setAsciiStream(0L), bufSize);
 
enc.encode(in, out);
out.flush();
out.close();

which runs in the database, takes a blob as parameter and base64 encodes it to a clob. This clob is written to my export table and read by forms in the above way. Then i have a little base64.exe along with my forms that is called from within forms that decodes the file….

So much trouble just for writing binary data from server to client… it’s a twoliner with c or java.

| Comments (25) »

29-Jan-07