/* * Copyright 2004 Blandware (http://www.blandware.com) * * 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.jboss.seam.wiki.util; import org.hibernate.Hibernate; import org.hibernate.HibernateException; import org.hibernate.cfg.Environment; import org.hibernate.engine.SessionImplementor; import org.hibernate.type.Type; import org.hibernate.usertype.CompositeUserType; import org.hibernate.util.PropertiesHelper; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.sql.Blob; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * <p>This is wrapper for both BinaryType and BlobType in order to give developer the ability to switch them via config</p> * <p>Returned class is <code>byte[]</code>, that's why we should make conversion of BLOB</p> * <p>User hibernate.binary_or_blob hibernate property in order to manage behaviour</p> * <p/> * <p><a href="BinaryBlobType.java.html"><i>View Source</i></a></p> * * @author Andrey Grebnev <a href="mailto:andrey.grebnev@blandware.com"><andrey.grebnev@blandware.com></a> * @version $Revision: 1.2 $ $Date: 2006/03/12 14:06:47 $ */ public class BinaryBlobType implements CompositeUserType { /** * Default bufer size in order to copy InputStream to byte[] */ protected static final int DEFAULT_BUFFER_SIZE = 1024 * 4; /** * If it is </code>true</code> we use <code>BlobType</code>, otherwise <code>BinaryType</code> */ protected boolean isBlob = false; /** * Creates new instance of BinaryBlobType */ public BinaryBlobType() { isBlob = "blob".equalsIgnoreCase(PropertiesHelper.getString("hibernate.binary_or_blob", Environment.getProperties(), "binary")); } /** * Get the "property names" that may be used in a * query. * * @return an array of "property names" */ public String[] getPropertyNames() { return new String[]{"value"}; } /** * Get the corresponding "property types". * * @return an array of Hibernate types */ public Type[] getPropertyTypes() { if (isBlob) return new Type[]{Hibernate.BLOB}; else return new Type[]{Hibernate.BINARY}; } /** * Get the value of a property. * * @param component an instance of class mapped by this "type" * @param property * @return the property value * @throws org.hibernate.HibernateException * */ public Object getPropertyValue(Object component, int property) throws HibernateException { return component; } /** * Set the value of a property. * * @param component an instance of class mapped by this "type" * @param property * @param value the value to set * @throws org.hibernate.HibernateException * */ public void setPropertyValue(Object component, int property, Object value) throws HibernateException { } /** * The class returned by <tt>nullSafeGet()</tt>. * * @return Class */ public Class returnedClass() { return Hibernate.BINARY.getReturnedClass(); } /** * Compare two instances of the class mapped by this type for persistence "equality". * Equality of the persistent state. * * @param x * @param y * @return boolean * @throws org.hibernate.HibernateException * */ public boolean equals(Object x, Object y) throws HibernateException { return Hibernate.BINARY.isEqual(x, y); } /** * Get a hashcode for the instance, consistent with persistence "equality" */ public int hashCode(Object x) throws HibernateException { return Hibernate.BINARY.getHashCode(x, null); } /** * Retrieve an instance of the mapped class from a JDBC resultset. Implementors * should handle possibility of null values. * * @param rs a JDBC result set * @param names the column names * @param session * @param owner the containing entity * @return Object * @throws org.hibernate.HibernateException * * @throws java.sql.SQLException */ public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException { if (isBlob) { Blob blob = (Blob)Hibernate.BLOB.nullSafeGet(rs, names, session, owner); if (blob == null) return null; else return copyData(blob.getBinaryStream()); } else { return Hibernate.BINARY.nullSafeGet(rs, names, session, owner); } } /** * Write an instance of the mapped class to a prepared statement. Implementors * should handle possibility of null values. A multi-column type should be written * to parameters starting from <tt>index</tt>. * * @param st a JDBC prepared statement * @param value the object to write * @param index statement parameter index * @param session * @throws org.hibernate.HibernateException * * @throws java.sql.SQLException */ public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException { if (isBlob) { if (value == null) Hibernate.BLOB.nullSafeSet(st, value, index, session); else { Blob blob = Hibernate.createBlob((byte[])value); Hibernate.BLOB.nullSafeSet(st, blob, index, session); } } else { Hibernate.BINARY.nullSafeSet(st, value, index, session); } } /** * Return a deep copy of the persistent state, stopping at entities and at collections. * * @param value generally a collection element or entity field * @return Object a copy * @throws org.hibernate.HibernateException * */ public Object deepCopy(Object value) throws HibernateException { return Hibernate.BINARY.deepCopy(value, null, null); } /** * Check if objects of this type mutable. * * @return boolean */ public boolean isMutable() { return Hibernate.BINARY.isMutable(); } /** * Transform the object into its cacheable representation. At the very least this * method should perform a deep copy. That may not be enough for some implementations, * however; for example, associations must be cached as identifier values. (optional * operation) * * @param value the object to be cached * @param session * @return a cachable representation of the object * @throws org.hibernate.HibernateException * */ public Serializable disassemble(Object value, SessionImplementor session) throws HibernateException { return Hibernate.BINARY.disassemble(value, session, null); } /** * Reconstruct an object from the cacheable representation. At the very least this * method should perform a deep copy. (optional operation) * * @param cached the object to be cached * @param session * @param owner the owner of the cached object * @return a reconstructed object from the cachable representation * @throws org.hibernate.HibernateException * */ public Object assemble(Serializable cached, SessionImplementor session, Object owner) throws HibernateException { return Hibernate.BINARY.assemble(cached, session, owner); } /** * During merge, replace the existing (target) value in the entity we are merging to * with a new (original) value from the detached entity we are merging. For immutable * objects, or null values, it is safe to simply return the first parameter. For * mutable objects, it is safe to return a copy of the first parameter. However, since * composite user types often define component values, it might make sense to recursively * replace component values in the target object. * * @param original * @param target * @param session * @param owner * @return first parameter * @throws org.hibernate.HibernateException * */ public Object replace(Object original, Object target, SessionImplementor session, Object owner) throws HibernateException { return Hibernate.BINARY.replace(original, target, session, owner, null); } /** * Copy data from InputStream into byte[] * * @param input source * @return the resulted array */ protected byte[] copyData(InputStream input) { ByteArrayOutputStream output = null; try { output = new ByteArrayOutputStream(); byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; int n = 0; while (-1 != (n = input.read(buffer))) { output.write(buffer, 0, n); } return output.toByteArray(); } catch (IOException ex) { throw new RuntimeException("Cannot copy data from InputStream into byte[]", ex); } finally { try { input.close(); } catch (IOException ex2) { //do nothing } try { output.close(); } catch (IOException ex2) { //do nothing } } } }