/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.ejb.plugins.cmp.jdbc2.schema;
import org.jboss.ejb.EntityContainer;
import org.jboss.ejb.GlobalTxEntityMap;
import org.jboss.ejb.GlobalTxEntityMap.GlobalTxSynchronization;
import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCEntityMetaData;
import org.jboss.ejb.plugins.cmp.jdbc2.bridge.JDBCEntityBridge2;
import org.jboss.ejb.plugins.cmp.jdbc2.bridge.JDBCCMRFieldBridge2;
import org.jboss.deployment.DeploymentException;
import javax.ejb.EJBException;
import javax.transaction.RollbackException;
import javax.transaction.Synchronization;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import java.sql.SQLException;
/**
* @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
* @version <tt>$Revision: 100259 $</tt>
*/
public class Schema
{
private final String viewsTxLocalKey;
private EntityTable[] entityTables;
private RelationTable[] relationTables;
private GlobalTxEntityMap txLocal = EntityContainer.getGlobalTxEntityMap();
public Schema(String ejbModuleName)
{
this.viewsTxLocalKey = ejbModuleName + ".schema.views";
}
public EntityTable createEntityTable(JDBCEntityMetaData metadata, JDBCEntityBridge2 entity)
throws DeploymentException
{
if(entityTables == null)
{
entityTables = new EntityTable[1];
}
else
{
EntityTable[] tmp = entityTables;
entityTables = new EntityTable[tmp.length + 1];
System.arraycopy(tmp, 0, entityTables, 0, tmp.length);
}
EntityTable table = new EntityTable(metadata, entity, this, entityTables.length - 1);
entityTables[entityTables.length - 1] = table;
return table;
}
public RelationTable createRelationTable(JDBCCMRFieldBridge2 leftField, JDBCCMRFieldBridge2 rightField)
throws DeploymentException
{
if(relationTables == null)
{
relationTables = new RelationTable[1];
}
else
{
RelationTable[] tmp = relationTables;
relationTables = new RelationTable[tmp.length + 1];
System.arraycopy(tmp, 0, relationTables, 0, tmp.length);
}
RelationTable table = new RelationTable(leftField, rightField, this, relationTables.length - 1);
relationTables[relationTables.length - 1] = table;
return table;
}
public Table.View getView(EntityTable table)
{
Views views = getViews();
Table.View view = views.entityViews[table.getTableId()];
if(view == null)
{
view = table.createView(views.tx);
views.entityViews[table.getTableId()] = view;
}
return view;
}
public Table.View getView(RelationTable table)
{
Views views = getViews();
Table.View view = views.relationViews[table.getTableId()];
if(view == null)
{
view = table.createView(views.tx);
views.relationViews[table.getTableId()] = view;
}
return view;
}
public void flush()
{
Views views = getViews();
Table.View[] relationViews = views.relationViews;
if(relationViews != null)
{
for(int i = 0; i < relationViews.length; ++i)
{
final Table.View view = relationViews[i];
if(view != null)
{
try
{
view.flushDeleted(views);
}
catch(SQLException e)
{
throw new EJBException("Failed to delete many-to-many relationships: " + e.getMessage(), e);
}
}
}
}
final Table.View[] entityViews = views.entityViews;
for(int i = 0; i < entityViews.length; ++i)
{
Table.View view = entityViews[i];
if(view != null)
{
try
{
view.flushDeleted(views);
}
catch(SQLException e)
{
throw new EJBException("Failed to delete instances: " + e.getMessage(), e);
}
}
}
for(int i = 0; i < entityViews.length; ++i)
{
Table.View view = entityViews[i];
if(view != null)
{
try
{
view.flushCreated(views);
}
catch(SQLException e)
{
throw new EJBException("Failed to create instances: " + e.getMessage(), e);
}
}
}
for(int i = 0; i < entityViews.length; ++i)
{
Table.View view = entityViews[i];
if(view != null)
{
try
{
view.flushUpdated();
}
catch(SQLException e)
{
throw new EJBException("Failed to update instances: " + e.getMessage(), e);
}
}
}
if(relationViews != null)
{
for(int i = 0; i < relationViews.length; ++i)
{
final Table.View view = relationViews[i];
if(view != null)
{
try
{
view.flushCreated(views);
}
catch(SQLException e)
{
throw new EJBException("Failed to create many-to-many relationships: " + e.getMessage(), e);
}
}
}
}
}
private Views getViews()
{
Transaction tx = txLocal.getTransaction();
GlobalTxSynchronization globalSync;
try
{
globalSync = txLocal.getGlobalSynchronization(tx);
}
catch(RollbackException e)
{
throw new EJBException("Transaction already marked to roll back: " + e.getMessage(), e);
}
catch(SystemException e)
{
throw new IllegalStateException("Failed to register transaction synchronization: " + e.getMessage());
}
if(globalSync == null)
throw new IllegalStateException("Global transaction synchronization is not available for transaction " + tx);
Views views = (Views) globalSync.getTxLocal(viewsTxLocalKey);
if(views == null)
{
views = new Views(tx);
globalSync.putTxLocal(viewsTxLocalKey, views);
globalSync.addSynchronization(new SchemaSynchronization(views));
}
return views;
}
// Inner
public class Views
{
public final Transaction tx;
public final Table.View[] entityViews;
public final Table.View[] relationViews;
public Views(Transaction tx)
{
this.tx = tx;
this.entityViews = new Table.View[entityTables.length];
this.relationViews = relationTables == null ? null : new Table.View[relationTables.length];
}
}
private class SchemaSynchronization implements Synchronization
{
private final Views views;
public SchemaSynchronization(Views views)
{
this.views = views;
}
public void beforeCompletion()
{
flush();
for(int i = 0; i < views.entityViews.length; ++i)
{
Table.View view = views.entityViews[i];
if(view != null)
{
view.beforeCompletion();
}
}
}
public void afterCompletion(int status)
{
if(status == Status.STATUS_MARKED_ROLLBACK ||
status == Status.STATUS_ROLLEDBACK ||
status == Status.STATUS_ROLLING_BACK)
{
for(int i = 0; i < views.entityViews.length; ++i)
{
Table.View view = views.entityViews[i];
if(view != null)
{
view.rolledback();
}
}
}
else
{
for(int i = 0; i < views.entityViews.length; ++i)
{
Table.View view = views.entityViews[i];
if(view != null)
{
view.committed();
}
}
}
}
}
}