
org.jbundle.base.db.client.ClientTable Maven / Gradle / Ivy
/*
* Copyright © 2012 jbundle.org. All rights reserved.
*/
package org.jbundle.base.db.client;
/**
* Copyright © 2012 tourgeek.com. All Rights Reserved.
* [email protected]
*/
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.util.Vector;
import org.jbundle.base.db.BaseDatabase;
import org.jbundle.base.db.BaseTable;
import org.jbundle.base.db.DatabaseException;
import org.jbundle.base.db.KeyArea;
import org.jbundle.base.db.Record;
import org.jbundle.base.db.SQLParams;
import org.jbundle.base.db.event.FileListener;
import org.jbundle.base.model.DBConstants;
import org.jbundle.base.model.DBParams;
import org.jbundle.base.model.Utility;
import org.jbundle.model.DBException;
import org.jbundle.model.RemoteException;
import org.jbundle.model.db.Rec;
import org.jbundle.thin.base.db.FieldList;
import org.jbundle.thin.base.db.buff.BaseBuffer;
import org.jbundle.thin.base.db.buff.VectorBuffer;
import org.jbundle.thin.base.db.client.CachedRemoteTable;
import org.jbundle.thin.base.remote.RemoteTable;
import org.jbundle.thin.base.util.ThinUtil;
/**
* The client side of the SocketTable <--> RemoteTable socket.
* NOTE: Since BaseTable is suppose to be thread-safe, you have to be careful when
* you call the RemoteTable since Ejb can only have one session - you must synchronize
* to the server to be safe:
* synchronized (record.getRecordOwner().getTask().getServer())
* { // In case this is called from another task
*/
public class ClientTable extends BaseTable
{
/**
* The TableSessionObject session.
*/
protected RemoteTable m_tableRemote = null;
/**
* Last modified bookmark. This is the hint returned from remote add call and returned on getLastModified.
*/
protected Object m_LastModifiedBookmark = null;
/**
* Direction of keys at last open
*/
protected boolean m_bDirectionCurrent = DBConstants.ASCENDING;
/**
* Constructor.
*/
public ClientTable()
{
super();
}
/**
* Constructor.
* @param database The database to add this table to.
* @param record The record to connect to this table.
*/
public ClientTable(BaseDatabase database, Record record)
{
this();
this.init(database, record);
}
/**
* Init this table.
* Add this table to the database and hook this table to the record.
* @param database The database to add this table to.
* @param record The record to connect to this table.
*/
public void init(BaseDatabase database, Record record)
{
super.init(database, record);
// Call super to set the table property (without calling remote).
super.setProperty(DBParams.SUPRESSREMOTEDBMESSAGES, DBConstants.TRUE);
}
/**
* Free this table object.
* Don't call this directly, freeing the record will free the table correctly.
* Client table just calls the remote freeTable() method.
*/
public void free()
{
try {
if (m_tableRemote != null)
{
synchronized (this.getSyncObject())
{ // In case this is called from another task
m_tableRemote.freeRemoteSession();
}
}
} catch (RemoteException ex) {
ex.printStackTrace();
}
m_tableRemote = null;
super.free();
}
/**
* Create a new empty record.
* Discard the current fields, init all the fields and display them,
* and set the status to EDIT_ADD.
*/
public void addNew() throws DBException
{
super.addNew(); // Don't access server... clear fields locally.
}
/**
* Do the physical Open on this table (requery the table).
* @exception DBException File exception.
*/
public void doOpen() throws DBException
{
try {
// FROM is automatic, since the remote BaseRecord is exactly the same as this one
// ORDER BY
KeyArea keyArea = this.getRecord().getKeyArea(-1); // Current index
String strKeyArea = null;
boolean bDirection = DBConstants.ASCENDING;
if (true)
{
strKeyArea = keyArea.getKeyName();
bDirection = keyArea.getKeyField(DBConstants.MAIN_KEY_FIELD).getKeyOrder();
}
m_bDirectionCurrent = bDirection;
// Open mode
int iOpenMode = this.getRecord().getOpenMode();
// SELECT (fields to select)
String strFields = null;
if (true) // All selected?
{
strFields = this.getRecord().getSQLFields(DBConstants.SQL_SELECT_TYPE, true);
if (strFields.equals(" *"))
strFields = null; // Select all
}
// WHERE XYZ >=
Object objInitialKey = null;
if (true)
{
BaseBuffer buffer = new VectorBuffer(null);
this.getRecord().handleInitialKey();
if (keyArea.isModified(DBConstants.START_SELECT_KEY))
{ // Anything set?
keyArea.setupKeyBuffer(buffer, DBConstants.START_SELECT_KEY, false);
objInitialKey = buffer.getPhysicalData();
buffer.addNextString(Integer.toString(keyArea.lastModified(DBConstants.START_SELECT_KEY, false))); // Largest modified field.
}
}
// WHERE XYZ <=
Object objEndKey = null;
if (true)
{
BaseBuffer buffer = new VectorBuffer(null);
this.getRecord().handleEndKey();
if (keyArea.isModified(DBConstants.END_SELECT_KEY))
{ // Anything set?
keyArea.setupKeyBuffer(buffer, DBConstants.END_SELECT_KEY, false);
objEndKey = buffer.getPhysicalData();
buffer.addNextString(Integer.toString(keyArea.lastModified(DBConstants.END_SELECT_KEY, false))); // Largest modified field.
}
}
// WHERE XYZ
byte[] byBehaviorData = null;
boolean bDirty = false;
if (this.getRecord().getListener() != null)
{
ByteArrayOutputStream baOut = new ByteArrayOutputStream();
ObjectOutputStream daOut = new ObjectOutputStream(baOut);
FileListener listener = (FileListener)this.getRecord().getListener();
while (listener != null)
{
if ((listener.getMasterSlaveFlag() & FileListener.RUN_IN_SLAVE) != 0) // Should exist in a SERVER environment
if ((listener.getMasterSlaveFlag() & FileListener.DONT_REPLICATE_TO_SLAVE) == 0) // Yes, replicate to the SERVER environment
if (listener.isEnabledListener()) // Yes, only replicate enabled listeners.
{ // There should be a copy of this on the server
bDirty = true;
daOut.writeUTF(listener.getClass().getName());
listener.initRemoteStub(daOut);
}
listener = (FileListener)listener.getNextListener();
}
daOut.flush();
if (bDirty)
byBehaviorData = baOut.toByteArray();
daOut.close();
baOut.close();
}
this.checkCacheMode(Boolean.TRUE); // Make sure the cache is set up correctly for this type of query (typically needed)
synchronized (this.getSyncObject())
{ // In case this is called from another task
m_tableRemote.open(strKeyArea, iOpenMode, bDirection, strFields, objInitialKey, objEndKey, byBehaviorData);
}
} catch (Exception ex) {
throw DatabaseException.toDatabaseException(ex);
}
}
/**
* Make sure the cache is set up correctly for this type of query.
* @param boolShouldBeCached Default cache mode (if null, no default assumed).
*/
public void checkCacheMode(Boolean boolShouldBeCached)
{
try {
boolean bCurrentlyCached = (this.getRemoteTableType(CachedRemoteTable.class) != null);
if ((this.getRecord().getOpenMode() & DBConstants.OPEN_CACHE_RECORDS) == DBConstants.OPEN_CACHE_RECORDS)
boolShouldBeCached = Boolean.TRUE;
// if ((this.getRecord().getOpenMode() & DBConstants.OPEN_READ_ONLY) == DBConstants.OPEN_READ_ONLY);
// bShouldBeCached = true;
if ((this.getRecord().getOpenMode() & DBConstants.OPEN_DONT_CACHE) == DBConstants.OPEN_DONT_CACHE)
boolShouldBeCached = Boolean.FALSE;
if (boolShouldBeCached == null)
return; // Not specified, Don't change.
if ((!bCurrentlyCached) && (boolShouldBeCached.booleanValue()))
{ // Add a cache
Utility.getLogger().info("Cache ON: " + this.getRecord().getTableNames(false));
this.setRemoteTable(new CachedRemoteTable(m_tableRemote));
}
else if ((bCurrentlyCached) && (!boolShouldBeCached.booleanValue()))
{ // Remove the cache
Utility.getLogger().info("Cache OFF: " + this.getRecord().getTableNames(false));
// RemoteTable tableRemote = this.getRemoteTableType(org.jbundle.model.Remote.class);
RemoteTable tableRemote = this.getRemoteTableType(CachedRemoteTable.class).getRemoteTableType(null);
((CachedRemoteTable)m_tableRemote).setRemoteTable(null);
((CachedRemoteTable)m_tableRemote).free();
this.setRemoteTable(tableRemote);
}
} catch (RemoteException ex) {
// Never for this usage
}
}
/**
* Close this table.
* This is not implemented for a ClientTable, since the TableSessionObject always does
* a close before each open.
*/
public void close()
{
// Remote Close is done automatically at open
super.close();
}
/**
* Create/Clear the current object (Always called from the record class).
* This is not implemented for a ClientTable, since the TableSessionObject always does
* an addNew before an add() call.
* @exception DBException File exception.
*/
public void doAddNew() throws DBException
{
}
/**
* Add this record (Always called from the record class).
* Make the remote add call with the current data.
* @param record The record to add.
* @exception DBException File exception.
*/
public void doAdd(Record record) throws DBException
{
m_LastModifiedBookmark = null;
try {
BaseBuffer buffer = (BaseBuffer)m_dataSource;
Object data = buffer.getPhysicalData();
synchronized (this.getSyncObject())
{ // In case this is called from another task
m_LastModifiedBookmark = m_tableRemote.add(data, this.getRecord().getOpenMode());
}
} catch (Exception ex) {
throw DatabaseException.toDatabaseException(ex);
}
}
/**
* Lock the current record.
* Will not make the remote call if the remote db doesn't have lock capability.
* The remote set method automatically does an edit before calling.
* This method responds differently depending on what open mode the record is in:
* OPEN_DONT_LOCK - A physical lock is not done. This is usually where deadlocks are possible
* (such as screens) and where transactions are in use (and locks are not needed).
* OPEN_LOCK_ON_EDIT - Holds a lock until an update or close. (Update crucial data, or hold records for processing)
* Returns false is someone already has a lock on this record.
* OPEN_WAIT_FOR_LOCK - Don't return from edit until you get a lock. (ie., Add to the total).
* Returns false if someone has a hard lock or time runs out.
* @return true if successful, false if lock failed.
* @exception DBException FILE_NOT_OPEN
* @exception DBException INVALID_RECORD - Record not current.
*/
public int doEdit() throws DBException
{
try {
if ((!SQLParams.DB_EDIT_NOT_SUPPORTED.equals(((ClientDatabase)this.getDatabase()).getRemoteProperty(SQLParams.EDIT_DB_PROPERTY, false))) //+ Remote db supports edit
|| ((this.getRecord().getOpenMode() & DBConstants.OPEN_LOCK_ON_EDIT_STRATEGY) != 0))
{
synchronized (this.getSyncObject())
{ // In case this is called from another task
int iOpenMode = this.getRecord().getOpenMode() | DBConstants.OPEN_ERROR_ON_DIRTY_LOCK_TYPE; // Make sure server doesn't refresh on dirty (I will do the refresh)
return m_tableRemote.edit(iOpenMode); // Only call if edit is supported by remote db
}
}
else
return DBConstants.NORMAL_RETURN;
} catch (Exception ex) {
throw DatabaseException.toDatabaseException(ex);
}
}
/**
* Update this record (Always called from the record class).
* @param record The record to add.
* @exception DBException File exception.
*/
public void doSet(Record record) throws DBException
{
try {
BaseBuffer buffer = (BaseBuffer)m_dataSource;
Object data = buffer.getPhysicalData();
synchronized (this.getSyncObject())
{ // In case this is called from another task
m_tableRemote.set(data, this.getRecord().getOpenMode());
}
} catch (Exception ex) {
throw DatabaseException.toDatabaseException(ex);
}
}
/**
* Delete this record (Always called from the record class).
* @exception DBException File exception.
*/
public void doRemove() throws DBException
{
try {
synchronized (this.getSyncObject())
{ // In case this is called from another task
m_tableRemote.remove(null, this.getRecord().getOpenMode());
}
} catch (Exception ex) {
throw DatabaseException.toDatabaseException(ex);
}
}
/**
* Move the position of the record.
* @param iRelPosition - Relative position positive or negative or FIRST_RECORD/LAST_RECORD.
* @return NORMAL_RETURN - The following are NOT mutually exclusive
* @exception DBException File exception.
*/
public int doMove(int iRelPosition) throws DBException
{
this.checkCacheMode(Boolean.TRUE); // Make sure the cache is set up correctly for this type of query (typically needed)
int iErrorCode = DBConstants.NORMAL_RETURN;
try {
Object objData = null;
synchronized (this.getSyncObject())
{ // In case this is called from another task
objData = m_tableRemote.doMove(iRelPosition, 1);
}
if (objData instanceof Vector)
{
Vector
© 2015 - 2025 Weber Informatics LLC | Privacy Policy