/**
* Copyright (C) 2012 JBoss Inc
*
* 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.dashboard.database.hibernate;
import com.mchange.v2.c3p0.C3P0ProxyConnection;
import com.mchange.v2.c3p0.impl.NewProxyConnection;
import org.apache.commons.dbcp.PoolableConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
import org.jboss.dashboard.CoreServices;
import org.jboss.dashboard.commons.cdi.CDIBeanLocator;
import org.jboss.dashboard.commons.misc.ReflectionUtils;
import org.jboss.jca.adapters.jdbc.WrappedConnection;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* Utility class to help with binary objects in Oracle
*/
@ApplicationScoped
@Named("LOBHelper")
public class LOBHelper {
private static transient Logger log = LoggerFactory.getLogger(LOBHelper.class.getName());
private static final String ORACLE_CLASS = "oracle.sql.BLOB";
private static final String ORACLE_TEMP_METHOD = "createTemporary";
private static final String ORACLE_DURATION__SESSION = "DURATION_SESSION";
private static final String ORACLE_JDBC_ORACLE_CONNECTION = "oracle.jdbc.OracleConnection";
private static final String ORACLE_OPEN_METHOD = "open";
private static final String ORACLE_MODE__READWRITE = "MODE_READWRITE";
private static final String ORACLE_GET_BINARY_OUTPUT_STREAM = "getBinaryOutputStream";
private static final String ORACLE_CLOSE = "close";
public static LOBHelper lookup() {
return (LOBHelper) CDIBeanLocator.getBeanByName("LOBHelper");
}
public static abstract class ValueWriter {
public abstract void writeValue(OutputStream os, Object value) throws IOException;
public abstract void writeValue(PreparedStatement st, Object value, int index) throws SQLException;
}
private Method getMethod(Class clazz, String method, Class c1, Class c2, Class c3) throws NoSuchMethodException {
int nParams = 0;
if (c3 != null) nParams = 3;
else if (c2 != null) nParams = 2;
else if (c1 != null) nParams = 1;
Class types[] = new Class[nParams];
if (c3 != null) {
types[2] = c3;
}
if (c2 != null) {
types[1] = c2;
}
if (c1 != null) {
types[0] = c1;
}
return clazz.getDeclaredMethod(method, types);
}
public void oracleNullSafeSet(PreparedStatement statement, Object value, int index, ValueWriter vw) throws HibernateException, SQLException {
try {
// Invoke by reflection the Oracle classes
Class oracleBlobClass = Class.forName(ORACLE_CLASS);
Method createTempMethod = getMethod(oracleBlobClass, ORACLE_TEMP_METHOD, Connection.class, Boolean.TYPE, Integer.TYPE);
Field durationSession = oracleBlobClass.getField(ORACLE_DURATION__SESSION);
Object arglist[] = new Object[3];
Connection conn = statement.getConnection();
arglist[0] = conn;
arglist[1] = Boolean.TRUE;
arglist[2] = durationSession.get(null);
Object tempBlob = null;
// Needed to avoid JBoss AS class loading issues...
Class connClassInCurrentClassLoader = Class.forName(conn.getClass().getName());
// Direct Oracle connection
if (Class.forName(ORACLE_JDBC_ORACLE_CONNECTION).isAssignableFrom(connClassInCurrentClassLoader)) {
tempBlob = createTempMethod.invoke(null, arglist); // null is valid because of static method
}
// JBoss AS data source wrapper connection.
else if (WrappedConnection.class.isAssignableFrom(connClassInCurrentClassLoader)) {
arglist[0] = ReflectionUtils.invokeMethod(conn, "getUnderlyingConnection", null);
tempBlob = createTempMethod.invoke(null, arglist); // null is valid because of static method
}
// C3P0 pool managed connection.
else if (NewProxyConnection.class.isAssignableFrom(connClassInCurrentClassLoader)) {
NewProxyConnection castCon = (NewProxyConnection) conn;
arglist[0] = C3P0ProxyConnection.RAW_CONNECTION;
tempBlob = castCon.rawConnectionOperation(createTempMethod, C3P0ProxyConnection.RAW_CONNECTION, arglist);
}
// Apache's DBCP pool managed connection.
else if (PoolableConnection.class.isAssignableFrom(connClassInCurrentClassLoader)) {
arglist[0] = ((PoolableConnection) statement.getConnection()).getDelegate();
tempBlob = createTempMethod.invoke(null, arglist); // null is valid because of static method
} else {
boolean throwException = true;
//check if we are running in websphere
try {
String wasConnectionWrapper = "com.ibm.ws.rsadapter.jdbc.WSJdbcConnection";
Class wasConnectionWrapperClass = Class.forName(wasConnectionWrapper);
if (wasConnectionWrapperClass.isAssignableFrom(connClassInCurrentClassLoader)) {
String helperClass = "com.ibm.websphere.rsadapter.WSCallHelper";
Class helper = Class.forName(helperClass);
Method nativeConnMethod = helper.getMethod("getNativeConnection", new Class[]{Object.class});
Connection nativeConn = (Connection) nativeConnMethod.invoke(null, conn);
if (Class.forName(ORACLE_JDBC_ORACLE_CONNECTION).isAssignableFrom(nativeConn.getClass())) {
arglist[0] = nativeConn;
tempBlob = createTempMethod.invoke(null, arglist);
throwException = false;
}
}
} catch (Throwable e) {
e.printStackTrace();
// do nothing
}
if(throwException){
throw new HibernateException("JDBC connection object must be a oracle.jdbc.OracleConnection " +
"a " + WrappedConnection.class.getName() +
"a " + PoolableConnection.class.getName() +
"or a " + NewProxyConnection.class.getName() +
". Connection class is " + connClassInCurrentClassLoader.getName());
}
}
Method openMethod = getMethod(oracleBlobClass, ORACLE_OPEN_METHOD, Integer.TYPE, null, null);
Field fieldReadWrite = oracleBlobClass.getField(ORACLE_MODE__READWRITE);
arglist = new Object[1];
arglist[0] = fieldReadWrite.get(null); //null is valid because of static field
openMethod.invoke(tempBlob, arglist);
Method getOutputStreamMethod = oracleBlobClass.getDeclaredMethod(ORACLE_GET_BINARY_OUTPUT_STREAM, null);
OutputStream os = (OutputStream) getOutputStreamMethod.invoke(tempBlob, null);
try {
vw.writeValue(os, value);
os.flush();
} finally {
os.close();
}
Method closeMethod = oracleBlobClass.getDeclaredMethod(ORACLE_CLOSE, null);
closeMethod.invoke(tempBlob, null);
statement.setBlob(index, (Blob) tempBlob);
} catch (Exception e) {
throw new HibernateException("Error in oracleNullSafeSet", e);
}
}
public void nullSafeSet(PreparedStatement st, Object value, int index, ValueWriter vw) throws HibernateException, SQLException {
if (log.isDebugEnabled())
log.debug("Setting value of class " + (value == null ? "<null>" : value.getClass().getName()));
if (CoreServices.lookup().getHibernateInitializer().isOracleDatabase()) {
oracleNullSafeSet(st, value, index, vw);
} else {
vw.writeValue(st, value, index);
}
}
}