/*
* � Copyright IBM Corp. 2010, 2015
*
* 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 com.ibm.xsp.extlib.relational.jdbc.model;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.List;
import javax.faces.context.FacesContext;
import javax.sql.RowSet;
import javax.sql.rowset.CachedRowSet;
import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.FacesExceptionEx;
import com.ibm.xsp.extlib.model.AbstractViewRowData;
import com.ibm.xsp.extlib.model.DataAccessor;
import com.ibm.xsp.extlib.relational.util.JdbcUtil;
import com.ibm.xsp.model.DataObject;
/**
* Data accessor holding JDBC results in a cached row set.
* <p>
* </p>
* @author Philippe Riand
*/
public class JdbcRowSetAccessor extends DataAccessor implements Externalizable, DataObject {
private static final long serialVersionUID = 1L;
public static class JDBCRow extends AbstractViewRowData {
private static final long serialVersionUID = 1L;
private RowSet rowSet;
private int index;
public JDBCRow(RowSet rowSet, int index) {
this.rowSet = rowSet;
this.index = index;
}
public RowSet getRowSet() {
return rowSet;
}
public int getIndex() {
return index;
}
public CachedRowSet getCacheRowSet() {
return JdbcRowSetAccessor.getCachedRowSet(getRowSet());
}
protected void ensureIndex() throws SQLException {
int rsIndex = rowSet.getRow()-1;
if(rsIndex!=index) {
rowSet.absolute(index+1);
}
}
// =====================================================
// ViewRowData methods
@Override
public Object getColumnValue(String name) {
try {
if(StringUtil.isEmpty(name)) {
return null;
}
ensureIndex();
return rowSet.getObject(name);
} catch(SQLException ex) {
throw new FacesExceptionEx(ex);
}
}
@Override
public void setColumnValue(String name, Object value) {
try {
ensureIndex();
rowSet.updateObject(name, value);
} catch(SQLException ex) {
throw new FacesExceptionEx(ex);
}
}
@Override
public boolean isReadOnly(String name) {
return rowSet.isReadOnly();
}
public boolean isRowDeleted() {
try {
ensureIndex();
return rowSet.rowDeleted();
} catch(SQLException ex) {
throw new FacesExceptionEx(ex);
}
}
public boolean isRowInserted() {
try {
ensureIndex();
return rowSet.rowInserted();
} catch(SQLException ex) {
throw new FacesExceptionEx(ex);
}
}
public boolean isRowUpdated() {
try {
ensureIndex();
return rowSet.rowUpdated();
} catch(SQLException ex) {
throw new FacesExceptionEx(ex);
}
}
}
private String connectionManager;
private String connectionName;
private String connectionUrl;
private String query;
private List<Object> parameters;
private RowSet rowSet;
public JdbcRowSetAccessor() {} // Serializable
public JdbcRowSetAccessor(JdbcRowSetSource ds) {
super(ds);
this.connectionManager = ds.getConnectionManager();
this.connectionName = ds.getConnectionName();
this.connectionUrl = ds.getConnectionUrl();
this.query = ds.findSqlQuery();
this.parameters = SqlParameter.computeParameterValues(ds.getSqlParameters());
this.rowSet = createRowSet(ds);
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
public String getConnectionManager() {
return connectionManager;
}
public void setConnectionManager(String connectionManager) {
this.connectionManager = connectionManager;
}
public String getConnectionName() {
return connectionName;
}
public void setConnectionName(String connectionName) {
this.connectionName = connectionName;
}
public String getConnectionUrl() {
return connectionUrl;
}
public void setConnectionUrl(String connectionUrl) {
this.connectionUrl = connectionUrl;
}
public List<Object> getParameters() {
return parameters;
}
public void setParameters(List<Object> parameters) {
this.parameters = parameters;
}
public RowSet getRowSet() {
return rowSet;
}
public void setRowSet(RowSet rowSet) {
this.rowSet = rowSet;
}
//@Override
public void writeExternal(ObjectOutput out) throws IOException {
//super.writeExternal(out);
out.writeObject(connectionManager);
out.writeObject(connectionName);
out.writeObject(connectionUrl);
out.writeObject(query);
out.writeObject(rowSet);
out.writeObject(parameters);
}
//@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
//super.readExternal(in);
connectionManager = (String)in.readObject();
connectionName = (String)in.readObject();
connectionUrl = (String)in.readObject();
query = (String)in.readObject();
rowSet = (RowSet)in.readObject();
parameters = (List<Object>)in.readObject();
}
//////////////////////////////////////////////////////////////////////
// Accessor methods
//////////////////////////////////////////////////////////////////////
@Override
public int getCount() {
return getCachedRowSet().size();
}
@Override
public Object get(int index) {
return new JDBCRow(rowSet,index);
}
//////////////////////////////////////////////////////////////////////
// Data Object
//////////////////////////////////////////////////////////////////////
public Object getValue(Object key) {
try {
if(key instanceof Number) {
return getRowSet().getObject(((Number)key).intValue());
}
if(key instanceof String) {
return getRowSet().getObject((String)key);
}
} catch (SQLException e) {
String msg = "Error while accessing RowSet column, key={0}"; // $NLX-JdbcRowSetAccessor.Errorwhileaccessing0columnkey1-1$
msg = StringUtil.format(msg,key);
// Note, this key is used elsewhere in this plugin
// "Error while accessing RowSet column, key={0}"
//String msg = com.ibm.xsp.extlib.relational.ResourceHandler.getSpecialAudienceString("JdbcRowSetAccessor.Errorwhileaccessing0columnkey1"); //$NON-NLS-1$
//msg = StringUtil.format(msg,key);
throw new FacesExceptionEx(e,msg);
}
String msg = "Invalid RowSet key {0}"; // $NLX-JdbcRowSetAccessor.Invalid0key1-1$
msg = StringUtil.format(msg,key);
// Note, this key is used elsewhere in this plugin
// "Invalid RowSet key {0}"
//String msg = com.ibm.xsp.extlib.relational.ResourceHandler.getSpecialAudienceString("JdbcRowSetAccessor.Invalid0key1"); //$NON-NLS-1$
//msg = StringUtil.format(msg,key);
throw new FacesExceptionEx(msg);
}
public void setValue(Object key, Object value) {
try {
if(key instanceof Number) {
getRowSet().updateObject(((Number)key).intValue(),value);
return;
}
if(key instanceof String) {
getRowSet().updateObject((String)key,value);
return;
}
} catch (SQLException e) {
// Note, this key is used elsewhere in this plugin
// "Error while accessing RowSet column, key={0}"
String msg = com.ibm.xsp.extlib.relational.RelationalResourceHandler.getSpecialAudienceString("JdbcRowSetAccessor.Errorwhileaccessing0columnkey1"); //$NON-NLS-1$
msg = StringUtil.format(msg,key);
throw new FacesExceptionEx(e,msg); // $NLX-JdbcRowSetAccessor.Errorwhileaccessing0columnkey1.1-1$ $NON-NLS-2$
}
// "Invalid RowSet key {0}"
String msg = com.ibm.xsp.extlib.relational.RelationalResourceHandler.getSpecialAudienceString("JdbcRowSetAccessor.Invalid0key1"); //$NON-NLS-1$
msg = StringUtil.format(msg,key);
throw new FacesExceptionEx(msg);
}
public Class<?> getType(Object key) {
return null;
}
public boolean isReadOnly(Object key) {
return getRowSet().isReadOnly();
}
//////////////////////////////////////////////////////////////////////
// Row deletion
//////////////////////////////////////////////////////////////////////
// Deleting a row with a particular id
@Override
public void deleteRow(String rowId) {
int index = Integer.parseInt(rowId);
deleteRow(index);
}
public void deleteRow(int index) {
try {
RowSet rs = getRowSet();
rs.absolute(index+1);
rs.deleteRow();
} catch(SQLException ex) {
throw new FacesExceptionEx(ex,"Error while deleting the row #{0}",index); // $NLX-JdbcRowSetAccessor.Errorwhiledeletingtherow0-1$
}
}
//////////////////////////////////////////////////////////////////////
// Some utilities to deal with records
//////////////////////////////////////////////////////////////////////
public static class JDBCRecord implements DataObject, Serializable {
private static final long serialVersionUID = 1L;
private boolean newRow;
private int index;
private String[] names;
private Object[] values;
public JDBCRecord(int index, boolean newRow, String[] names, Object[] values) {
this.index = index;
this.newRow = newRow;
this.names = names;
this.values = values;
}
public Object getValue(Object key) {
if(key instanceof Number) {
return values[((Number)key).intValue()-1];
}
if(key instanceof String) {
String n = (String)key;
for(int i=0; i<names.length; i++) {
if(names[i].equalsIgnoreCase(n)) {
return values[i];
}
}
}
return null;
}
public void setValue(Object key, Object value) {
if(key instanceof Number) {
values[((Number)key).intValue()] = value;
return;
}
if(key instanceof String) {
String n = (String)key;
for(int i=0; i<names.length; i++) {
if(names[i].equalsIgnoreCase(n)) {
values[i] = value;
return;
}
}
}
}
public Class<?> getType(Object key) {
return null;
}
public boolean isReadOnly(Object key) {
return false;
}
}
public JDBCRecord newRow(int index) throws SQLException {
return createRow(index,true);
}
public JDBCRecord getRow(int index) throws SQLException {
return createRow(index,false);
}
public void saveRow(JDBCRecord record) throws SQLException {
if(!rowSet.absolute(record.index+1)) {
// PHIL do not throw an exception here but stay at the current place
// This handles the case when the result set doesn't have any row.
// Just move to the last record.
//throw new SQLException(StringUtil.format("Cannot go to row #{0}",record.index));
rowSet.afterLast();
}
if(record.newRow) {
rowSet.moveToInsertRow();
}
for(int i=0; i<record.values.length; i++) {
rowSet.updateObject(i+1,record.values[i]);
}
if(record.newRow) {
rowSet.insertRow();
rowSet.moveToCurrentRow();
} else {
rowSet.updateRow();
}
}
private JDBCRecord createRow(int index, boolean newRow) throws SQLException {
ResultSetMetaData md = rowSet.getMetaData();
int colCount = md.getColumnCount();
String[] names = new String[colCount];
for(int i=0; i<colCount; i++) {
names[i] = md.getColumnName(i+1);
}
Object[] values = new Object[colCount];
if(!newRow) {
if(!rowSet.absolute(index+1)) {
throw new SQLException(StringUtil.format("Cannot go to record #{0}",index)); // $NLX-JdbcRowSetAccessor.Cannotgotorecord0-1$
}
for(int i=0; i<colCount; i++) {
values[i] = rowSet.getObject(i+1);
}
}
JDBCRecord record = new JDBCRecord(index,newRow,names,values);
return record;
}
//////////////////////////////////////////////////////////////////////
// CachedRowSet
//////////////////////////////////////////////////////////////////////
private static CachedRowSet getCachedRowSet(RowSet rowSet) {
return (CachedRowSet)rowSet;
}
private CachedRowSet getCachedRowSet() {
return getCachedRowSet(getRowSet());
}
public void acceptChanges() throws SQLException {
getCachedRowSet().acceptChanges(findConnection());
}
public RowSet createRowSet(JdbcRowSetSource ds) {
try {
RowSet rowSet = ds.createRowSet();
// Set the maximum rows to be retrieved
int maxRows = ds.getMaxRows();
rowSet.setMaxRows(maxRows);
// Get the query and apply it to the row set
String query = getQuery();
rowSet.setCommand(query);
// Set the request parameters if any
if(parameters!=null) {
for(int i=0; i<parameters.size(); i++) {
Object p = parameters.get(i);
rowSet.setObject(i+1, p);
}
}
// Then execute the query
// Cast to CachedRoowSet until we get JNDI on
//rowSet.execute();
getCachedRowSet(rowSet).execute(findConnection());
return rowSet;
} catch(Exception ex) {
// "Error while reading the relational data"
String msg = com.ibm.xsp.extlib.relational.RelationalResourceHandler.getSpecialAudienceString(
"JdbcDataBlockAccessor.Errorwhilereadingtherelationaldat"); //$NON-NLS-1$
throw new FacesExceptionEx(ex,msg);
}
}
protected Connection findConnection() throws SQLException {
if(StringUtil.isNotEmpty(connectionUrl)) {
return DriverManager.getConnection(connectionUrl);
}
if(StringUtil.isNotEmpty(connectionName)) {
return JdbcUtil.createNamedConnection(FacesContext.getCurrentInstance(), connectionName);
}
if(StringUtil.isNotEmpty(connectionManager)) {
return JdbcUtil.createManagedConnection(FacesContext.getCurrentInstance(),getDataSource()!=null?getDataSource().getComponent():null,connectionManager);
}
// "No \"connectionManager\", \"connectionName\" or \"connectionUrl\" is provided"
String msg = com.ibm.xsp.extlib.relational.RelationalResourceHandler.getSpecialAudienceString("JdbcDataBlockAccessor.No01or2isprovided"); //$NON-NLS-1$
throw new SQLException(msg); // $NLX--1$ $NON-NLS-2$ $NON-NLS-3$ $NON-NLS-4$
}
}