/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.translator.coherence;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.resource.ResourceException;
import org.teiid.language.ColumnReference;
import org.teiid.language.Command;
import org.teiid.language.Comparison.Operator;
import org.teiid.language.Condition;
import org.teiid.language.Delete;
import org.teiid.language.Expression;
import org.teiid.language.ExpressionValueSource;
import org.teiid.language.Insert;
import org.teiid.language.Literal;
import org.teiid.language.SetClause;
import org.teiid.language.Update;
import org.teiid.metadata.Column;
import org.teiid.metadata.ForeignKey;
import org.teiid.metadata.RuntimeMetadata;
import org.teiid.metadata.Table;
import org.teiid.resource.adapter.coherence.CoherenceConnection;
import org.teiid.resource.adapter.coherence.CoherenceFilterUtil;
import org.teiid.translator.DataNotAvailableException;
import org.teiid.translator.ExecutionContext;
import org.teiid.translator.TranslatorException;
import org.teiid.translator.UpdateExecution;
import org.teiid.translator.coherence.util.ObjectSourceMethodManager;
import org.teiid.translator.coherence.visitor.CoherenceVisitor;
import org.teiid.translator.coherence.visitor.DeleteVisitor;
/**
* Please see the user's guide for a full description of capabilities, etc.
*
* Description/Assumptions:
*
*/
public class CoherenceUpdateExecution implements UpdateExecution {
protected CoherenceConnection connection;
protected RuntimeMetadata metadata;
protected ExecutionContext context;
protected Command command;
protected SourceCacheAdapter cacheTranslator;
protected CoherenceVisitor visitor = null;
protected int result;
public CoherenceUpdateExecution(Command command,
CoherenceConnection coherenceConnection,
RuntimeMetadata metadata, ExecutionContext context, SourceCacheAdapter cacheTranslator) {
this.connection = coherenceConnection;
this.metadata = metadata;
this.context = context;
this.command = command;
this.cacheTranslator = cacheTranslator;
this.visitor = new CoherenceVisitor(metadata);
}
/** execute generic update-class (either an update, delete, or insert)
* operation and returns a count of affected rows. Since underlying
* Coherence operations (and this connector) can modify at most one cache
* object at a time, this will always return 1. It will never
* actually return 0, because if an operation fails, a
* ConnectorException will be thrown instead.
* here for the sake of efficiency.
*/
@Override
public void execute() throws TranslatorException {
if (command instanceof Update) {
executeUpdate();
}
else if (command instanceof Delete) {
executeDelete();
}
else if (command instanceof Insert) {
executeInsert();
}
// else {
// final String msg = LDAPPlugin.Util.getString("LDAPUpdateExecution.incorrectCommandError"); //$NON-NLS-1$
// throw new TranslatorException(msg);
// }
}
@Override
public int[] getUpdateCounts() throws DataNotAvailableException,
TranslatorException {
return new int[] {1};
}
/**
* Private method to perform the inserting of an object into the cache
* @throws TranslatorException
*/
private void executeInsert()
throws TranslatorException {
Insert icommand = (Insert) command;
Table t = metadata.getTable(icommand.getTable().getMetadataObject().getFullName());
// if the table has a foreign key, its must be a child (contained) object in the root
if (t.getForeignKeys() != null && t.getForeignKeys().size() > 0) {
this.addChildObject(t);
return;
}
String pkColName = null;
// process the top level object
List<Column> pk = t.getPrimaryKey().getColumns();
if (pk == null || pk.isEmpty()) {
final String msg = CoherencePlugin.Util.getString("CoherenceUpdateExecution.noPrimaryKeyDefinedOnTable", new Object[] {t.getName()}); //$NON-NLS-1$
throw new TranslatorException(msg);
}
pkColName = visitor.getNameFromElement(pk.get(0));
Object newObject = cacheTranslator.createObject(icommand.getColumns(),
((ExpressionValueSource)icommand.getValueSource()).getValues(),
this.visitor,
t);
// get the key value to use to for adding to the cache
Object keyvalue = ObjectSourceMethodManager.getValue("get" + pkColName, newObject);
// add to cache
try {
this.connection.add(keyvalue, newObject);
} catch (ResourceException e) {
throw new TranslatorException(e);
}
}
private void addChildObject(Table t) throws TranslatorException {
List<ForeignKey> fks = t.getForeignKeys();
ForeignKey fk = fks.get(0);
Table parentTable = fk.getParent();
// the name of the method to obtain the collection is the nameInSource of the foreginKey
String parentToChildMethod = fk.getNameInSource();
if(parentToChildMethod == null) {
final String msg = CoherencePlugin.Util.getString("CoherenceUpdateExecution.noNameInSourceForForeingKey", new Object[] {fk.getName()}); //$NON-NLS-1$
throw new TranslatorException(msg);
}
// there must only be 1 column in the primary key
String parentColName = visitor.getNameFromElement(fk.getPrimaryKey().getColumns().get(0));
Insert icommand = (Insert) command;
List<ColumnReference> insertElementList = icommand.getColumns();
List<Expression> insertValueList = ((ExpressionValueSource)icommand.getValueSource()).getValues();
if(insertElementList.size() != insertValueList.size()) {
throw new TranslatorException("Error: columns.size and values.size are not the same.");
}
ColumnReference insertElement;
String[] nameOfElement = new String[insertElementList.size()];
int parentValueLoc = -1;
for (int i=0; i < insertElementList.size(); i++) {
insertElement = insertElementList.get(i);
// // call utility class to get NameInSource/Name of element
nameOfElement[i]= visitor.getNameFromElement(insertElement.getMetadataObject());
// match the parent column to the colum in the insert statement
if (nameOfElement[i].equalsIgnoreCase(parentColName)) {
parentValueLoc = i;
}
}
if (parentColName != null && parentValueLoc == -1) {
final String msg = CoherencePlugin.Util.getString("CoherenceUpdateExecution.noColumnMatchedForeignColumn", new Object[] {t.getName(), parentColName}); //$NON-NLS-1$
throw new TranslatorException(msg);
}
// get the parent key and find the root object
Object parentValue = insertValueList.get(parentValueLoc);
Object val;
if(parentValue instanceof Literal) {
Literal literalValue = (Literal)parentValue;
val = literalValue.getValue();
} else {
val = parentValue;
}
Object parentObject = null;
// get the parent object from the cache
try {
List<Object> result = this.connection.get( CoherenceFilterUtil.createCompareFilter(parentColName, val, Operator.EQ, val.getClass()) );
// visitor.createFilter(parentColName + " = " + val));
if (result == null || result.isEmpty()) {
final String msg = CoherencePlugin.Util.getString("CoherenceUpdateExecution.noobjectfound", new Object[] {parentTable.getName(), parentColName, val}); //$NON-NLS-1$
throw new TranslatorException(msg);
}
parentObject = result.get(0);
} catch (ResourceException e) {
throw new TranslatorException(e);
}
// create and load the child object data
Object newChildObject = cacheTranslator.createObject(insertElementList,
insertValueList,
this.visitor,
t);
///--- questions
/// --- how to not process - setvalue for parent column
/// --- need to get the key value off the object of the parent
// get the key value to use to for adding to the cache
Object parentContainer = ObjectSourceMethodManager.getValue("get" + parentToChildMethod, parentObject);
if (parentContainer == null) {
final String msg = CoherencePlugin.Util.getString("CoherenceUpdateExecution.noParentContainerObjectFound", new Object[] {parentTable.getName(), parentToChildMethod}); //$NON-NLS-1$
throw new TranslatorException(msg);
}
if (parentContainer.getClass().isArray() ) {
} else if (parentContainer instanceof Collection) {
Collection c = (Collection) parentContainer;
c.add(newChildObject);
} else if ( parentContainer instanceof Map) {
Map m = (Map) parentContainer;
m.put(1, newChildObject);
}
try {
this.connection.update(parentValue, parentObject);
} catch (ResourceException e) {
throw new TranslatorException(e);
}
}
private void executeDelete()
throws TranslatorException {
DeleteVisitor visitor = new DeleteVisitor(metadata);
visitor.visitNode((Delete) command);
if(visitor.getException() != null) {
throw visitor.getException();
}
if (visitor.getKeys() == null || visitor.getKeys().isEmpty()) {
final String msg = CoherencePlugin.Util.getString("CoherenceUpdateExecution.objectNotDeleted", new Object[] {visitor.getTableName()}); //$NON-NLS-1$
throw new TranslatorException(msg);
}
for (java.util.Iterator it=visitor.getKeys().iterator(); it.hasNext();) {
Object key = it.next();
try {
this.connection.remove(key);
} catch (ResourceException e) {
throw new TranslatorException(e);
}
}
}
// Private method to actually do an update operation.
private void executeUpdate()
throws TranslatorException {
Update ucommand = (Update) command;
Table t = metadata.getTable(ucommand.getTable().getMetadataObject().getFullName());
// List<ForeignKey> fks = t.getForeignKeys();
// if the table has a foreign key, its must be a child (contained) object in the root
if (t.getForeignKeys() != null && t.getForeignKeys().size() > 0) {
updateChildObject(t);
return;
}
}
private void updateChildObject(Table t) throws TranslatorException {
List<ForeignKey> fks = t.getForeignKeys();
ForeignKey fk = fks.get(0);
Table parentTable = fk.getParent();
// the name of the method to obtain the collection is the nameInSource of the foreginKey
String parentToChildMethod = fk.getNameInSource();
if(parentToChildMethod == null) {
final String msg = CoherencePlugin.Util.getString("CoherenceUpdateExecution.noNameInSourceForForeingKey", new Object[] {fk.getName()}); //$NON-NLS-1$
throw new TranslatorException(msg);
}
// there must only be 1 column in the primary key
String parentColName = visitor.getNameFromElement(fk.getPrimaryKey().getColumns().get(0));
List<SetClause> updateList = ((Update)command).getChanges();
Condition criteria = ((Update)command).getWhere();
ColumnReference leftElement;
Expression rightExpr;
String nameLeftElement;
Object valueRightExpr;
// iterate through the supplied list of updates (each of
// which is an ICompareCriteria with an IElement on the left
// side and an IExpression on the right, per the Connector
// API).
for (int i=0; i < updateList.size(); i++) {
SetClause setClause = updateList.get(i);
// trust that connector API is right and left side
// will always be an IElement
leftElement = setClause.getSymbol();
// call utility method to get NameInSource/Name for element
nameLeftElement = visitor.getNameFromElement(leftElement.getMetadataObject());
// get right expression - if it is not a literal we
// can't handle that so throw an exception
rightExpr = setClause.getValue();
// if (!(rightExpr instanceof Literal)) {
// final String msg = CoherencePlugin.Util.getString("LDAPUpdateExecution.valueNotLiteralError",nameLeftElement); //$NON-NLS-1$
// throw new TranslatorException(msg);
// }
valueRightExpr = ((Literal)rightExpr).getValue();
// add in the modification as a replacement - meaning
// any existing value(s) for this attribute will
// be replaced by the new value. If the attribute
// didn't exist, it will automatically be created
// TODO - since null is a valid attribute
// value, we don't do any special handling of it right
// now. But maybe null should mean to delete an
// attribute?
}
}
// cancel here by closing the copy of the ldap context (if it was
// initialized, which is only true if execute() was previously called)
// calling close on already closed context is safe per
// javax.naming.Context javadoc so we won't worry about this also
// happening in our close method
public void cancel() throws TranslatorException {
close();
}
// close here by closing the copy of the ldap context (if it was
// initialized, which is only true if execute() was previously called)
// calling close on already closed context is safe per
// javax.naming.Context javadoc so we won't worry about this also
// happening in our close method
public void close() {
}
}