/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import java.util.Vector;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWDataField;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWModel;
import org.eclipse.persistence.tools.workbench.mappingsmodel.ProblemConstants;
import org.eclipse.persistence.tools.workbench.mappingsmodel.db.MWColumn;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWDescriptorLockingPolicy;
import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWColumnHandle;
import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWHandle;
import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWHandle.NodeReferenceScrubber;
import org.eclipse.persistence.tools.workbench.utility.CollectionTools;
import org.eclipse.persistence.tools.workbench.utility.iterators.CloneIterator;
import org.eclipse.persistence.tools.workbench.utility.iterators.TransformationIterator;
import org.eclipse.persistence.tools.workbench.utility.node.Node;
import org.eclipse.persistence.descriptors.AllFieldsLockingPolicy;
import org.eclipse.persistence.descriptors.ChangedFieldsLockingPolicy;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.descriptors.FieldsLockingPolicy;
import org.eclipse.persistence.descriptors.SelectedFieldsLockingPolicy;
import org.eclipse.persistence.descriptors.TimestampLockingPolicy;
import org.eclipse.persistence.descriptors.VersionLockingPolicy;
import org.eclipse.persistence.mappings.converters.ObjectTypeConverter;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLCompositeCollectionMapping;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;
import org.eclipse.persistence.oxm.mappings.XMLDirectMapping;
public final class MWTableDescriptorLockingPolicy extends MWDescriptorLockingPolicy {
private MWColumnHandle versionLockingColumnHandle;
public final static String VERSION_LOCKING_COLUMN_PROPERTY = "versionLockingColumn";
private volatile String optimisticLockingType;
public final static String OPTIMISTIC_LOCKING_TYPE_PROPERTY = "optimisticLockingType";
public final static String OPTIMISTIC_COLUMNS_LOCKING_TYPE = "Columns Locking";
public final static String OPTIMISTIC_VERSION_LOCKING_TYPE = "Version Locking";
public final static String DEFAULT_OPTIMISTIC_LOCKING_TYPE = OPTIMISTIC_VERSION_LOCKING_TYPE;
private volatile String optimisticColumnsLockingType;
public final static String OPTIMISTIC_COLUMNS_LOCKING_TYPE_PROPERTY = "optimisticColumnsLockingType";
public final static String OPTIMISTIC_COLUMNS_ALL_COLUMNS = "All Columns";
public final static String OPTIMISTIC_COLUMNS_CHANGED_COLUMNS = "Changed Columns";
public final static String OPTIMISTIC_COLUMNS_SELECTED_COLUMNS = "Selected Columns";
public final static String DEFAULT_OPTIMISTIC_COLUMNS_LOCKING_TYPE = OPTIMISTIC_COLUMNS_ALL_COLUMNS;
private Collection columnLockColumnHandles;
public final static String COLUMN_LOCK_COLUMNS_COLLECTION = "columnLockColumns";
private NodeReferenceScrubber columnLockColumnScrubber;
// ********** constructors **********
protected MWTableDescriptorLockingPolicy() {
super();
}
public MWTableDescriptorLockingPolicy(MWRelationalTransactionalPolicy descriptor) {
super(descriptor);
}
// ********** initialization **********
protected void initialize(Node parent) {
super.initialize(parent);
this.versionLockingColumnHandle = new MWColumnHandle(this, this.buildVersionLockingColumnScrubber());
this.columnLockColumnHandles = new Vector();
}
// ********** containment hierarchy **********
protected void addChildrenTo(List children) {
super.addChildrenTo(children);
children.add(this.versionLockingColumnHandle);
synchronized (this.columnLockColumnHandles) { children.addAll(this.columnLockColumnHandles); }
}
private NodeReferenceScrubber buildVersionLockingColumnScrubber() {
return new NodeReferenceScrubber() {
public void nodeReferenceRemoved(Node node, MWHandle handle) {
MWTableDescriptorLockingPolicy.this.setVersionLockingColumn(null);
}
public String toString() {
return "MWTableDescriptorLockingPolicy.buildVersionLockingColumnScrubber()";
}
};
}
private NodeReferenceScrubber columnLockColumnScrubber() {
if (this.columnLockColumnScrubber == null) {
this.columnLockColumnScrubber = this.buildColumnLockColumnScrubber();
}
return this.columnLockColumnScrubber;
}
private NodeReferenceScrubber buildColumnLockColumnScrubber() {
return new NodeReferenceScrubber() {
public void nodeReferenceRemoved(Node node, MWHandle handle) {
MWTableDescriptorLockingPolicy.this.removeColumnLockColumnHandle((MWColumnHandle) handle);
}
public String toString() {
return "MWTableDescriptorLockingPolicy.buildColumnLockColumnScrubber()";
}
};
}
// ********** accessors **********
public MWTableDescriptor getOwningTableDescriptor() {
return (MWTableDescriptor) getOwningDescriptor();
}
public MWColumn getVersionLockingColumn() {
return this.versionLockingColumnHandle.getColumn();
}
public void setVersionLockingColumn(MWColumn column) {
if ((column != null) && (this.getLockingType() != OPTIMISTIC_LOCKING)) {
throw new IllegalStateException("LockingType must be Optimistic Locking");
}
MWColumn old = this.versionLockingColumnHandle.getColumn();
this.versionLockingColumnHandle.setColumn(column);
this.firePropertyChanged(VERSION_LOCKING_COLUMN_PROPERTY, old, column);
}
public MWDataField getVersionLockField() {
return this.getVersionLockingColumn();
}
public void setVersionLockField(MWDataField lockField) {
this.setVersionLockingColumn((MWColumn) lockField);
}
// ***** columnLockColumns
private Iterator columnLockColumnHandles() {
return new CloneIterator(this.columnLockColumnHandles) {
protected void remove(Object current) {
MWTableDescriptorLockingPolicy.this.removeColumnLockColumnHandle((MWColumnHandle) current);
}
};
}
void removeColumnLockColumnHandle(MWColumnHandle handle) {
this.columnLockColumnHandles.remove(handle);
this.fireItemRemoved(COLUMN_LOCK_COLUMNS_COLLECTION, handle.getColumn());
}
public Iterator columnLockColumns() {
return new TransformationIterator(this.columnLockColumnHandles()) {
protected Object transform(Object next) {
return ((MWColumnHandle) next).getColumn();
}
};
}
public int columnLockColumnsSize() {
return this.columnLockColumnHandles.size();
}
public void addColumnLockColumn(MWColumn column) {
this.columnLockColumnHandles.add(new MWColumnHandle(this, column, this.columnLockColumnScrubber()));
this.fireItemAdded(COLUMN_LOCK_COLUMNS_COLLECTION, column);
}
public void removeColumnLockColumn(MWColumn column) {
for (Iterator stream = this.columnLockColumns(); stream.hasNext(); ) {
if (stream.next() == column) {
stream.remove();
return;
}
}
throw new IllegalArgumentException(column.toString());
}
private void clearColumnLockColumns() {
for (Iterator stream = this.columnLockColumnHandles(); stream.hasNext(); ) {
stream.next();
stream.remove();
}
}
public Iterator columnLockColumnsNames() {
return new TransformationIterator(this.columnLockColumns()) {
protected Object transform(Object next) {
return ((MWColumn) next).qualifiedName();
}
};
}
public void setLockingType(String newLockingType) {
String oldLockingType = getLockingType();
super.setLockingType(newLockingType);
if (attributeValueHasChanged(newLockingType, oldLockingType)) {
if (newLockingType != OPTIMISTIC_LOCKING) {
setOptimisticLockingType(null);
}
else {
setOptimisticLockingType(DEFAULT_OPTIMISTIC_LOCKING_TYPE);
}
}
}
public String getOptimisticColumnsLockingType() {
return this.optimisticColumnsLockingType;
}
public void setOptimisticColumnsLockingType(String fieldsType){
String oldValue = this.optimisticColumnsLockingType;
this.optimisticColumnsLockingType = fieldsType;
firePropertyChanged(OPTIMISTIC_COLUMNS_LOCKING_TYPE_PROPERTY, oldValue, this.optimisticColumnsLockingType);
if (attributeValueHasChanged(oldValue, this.optimisticColumnsLockingType)) {
if (this.optimisticColumnsLockingType == OPTIMISTIC_COLUMNS_SELECTED_COLUMNS) {
//do nothing
}
else {
clearColumnLockColumns();
}
}
}
public String getOptimisticLockingType() {
return this.optimisticLockingType;
}
public void setOptimisticLockingType(String optimisticLockingType)
{
String oldLockingType = this.optimisticLockingType;
this.optimisticLockingType = optimisticLockingType;
firePropertyChanged(OPTIMISTIC_LOCKING_TYPE_PROPERTY, oldLockingType, this.optimisticLockingType);
if (attributeValueHasChanged(oldLockingType, this.optimisticLockingType)) {
if (this.optimisticLockingType == OPTIMISTIC_VERSION_LOCKING_TYPE) {
this.setOptimisticColumnsLockingType(null);
setOptimisticVersionLockingType(DEFAULT_OPTIMISTIC_VERSION_LOCKING_TYPE);
this.clearColumnLockColumns();
}
else if (this.optimisticLockingType == null) {
this.setOptimisticColumnsLockingType(null);
setOptimisticVersionLockingType(null);
this.setVersionLockField(null);
this.clearColumnLockColumns();
}
else {
this.setOptimisticColumnsLockingType(DEFAULT_OPTIMISTIC_COLUMNS_LOCKING_TYPE);
this.setVersionLockField(null);
this.setOptimisticVersionLockingType(null);
}
}
}
// ************* problems *************
protected void addProblemsTo(List problems) {
super.addProblemsTo(problems);
this.selectedColumnsLockingNotMapped(problems);
this.selectedColumnsLockingContainsPrimaryKey(problems);
}
//override for column locking types don't want to do check this if we are column locking
protected void checkWriteLockFieldWritable(List newProblems) {
if (this.getOptimisticLockingType() == OPTIMISTIC_VERSION_LOCKING_TYPE) {
super.checkWriteLockFieldWritable(newProblems);
}
}
private void selectedColumnsLockingNotMapped(List newProblems) {
if (this.getLockingType() == OPTIMISTIC_LOCKING
&& this.getOptimisticLockingType() == OPTIMISTIC_COLUMNS_LOCKING_TYPE
&& this.getOptimisticColumnsLockingType() == OPTIMISTIC_COLUMNS_SELECTED_COLUMNS)
{
List unmappedColumnNames = new ArrayList();
List invalidColumnNames = new ArrayList();
for (Iterator stream = columnLockColumns(); stream.hasNext(); ) {
MWColumn column = (MWColumn) stream.next();
if ( ! CollectionTools.contains(this.getOwningTableDescriptor().allAssociatedColumns(), column)) {
invalidColumnNames.add(column.getName());
}
if (this.getOwningTableDescriptor().allWritableMappingsForField(column).size() == 0) {
unmappedColumnNames.add(column.getName());
}
}
if (unmappedColumnNames.size() > 0) {
newProblems.add(this.buildProblem(ProblemConstants.DESCRIPTOR_LOCKING_SELECTED_FIELDS_NOT_MAPPED, unmappedColumnNames));
}
if (invalidColumnNames.size() > 0) {
newProblems.add(this.buildProblem(ProblemConstants.DESCRIPTOR_LOCKING_SELECTED_FIELDS_NOT_VALID, invalidColumnNames));
}
}
}
protected void checkLockFieldSpecifiedForLockingPolicy(List newProblems) {
if (this.getLockingType() == OPTIMISTIC_LOCKING
&& this.getOptimisticLockingType() == OPTIMISTIC_VERSION_LOCKING_TYPE)
{
if (this.getVersionLockField() == null) {
newProblems.add(this.buildProblem(ProblemConstants.DESCRIPTOR_LOCKING_VERSION_LOCK_FIELD_NOT_SPECIFIED));
}
else if ( ! CollectionTools.contains(this.getOwningTableDescriptor().allAssociatedColumns(), this.getVersionLockField())) {
newProblems.add(this.buildProblem(ProblemConstants.DESCRIPTOR_LOCKING_VERSION_LOCK_FIELD_NOT_VALID));
}
}
}
private void selectedColumnsLockingContainsPrimaryKey(List newProblems) {
if(this.getLockingType() == OPTIMISTIC_LOCKING
&& this.getOptimisticLockingType() == OPTIMISTIC_COLUMNS_LOCKING_TYPE
&& this.getOptimisticColumnsLockingType() == OPTIMISTIC_COLUMNS_SELECTED_COLUMNS)
{
Collection primaryKeys = CollectionTools.collection(this.getOwningTableDescriptor().primaryKeys());
ArrayList illegalColumnNames = new ArrayList();
for (Iterator stream = columnLockColumns(); stream.hasNext(); ) {
MWColumn column = (MWColumn) stream.next();
if (primaryKeys.contains(column)) {
illegalColumnNames.add(column.getName());
}
}
if (illegalColumnNames.size() > 0) {
newProblems.add(this.buildProblem(ProblemConstants.DESCRIPTOR_LOCKING_SELECTED_FIELDS_ARE_PKS, illegalColumnNames));
}
}
}
// ************* runtime conversion *************
public void adjustRuntimeDescriptor(ClassDescriptor runtimeDescriptor) {
super.adjustRuntimeDescriptor(runtimeDescriptor);
if (getLockingType() == OPTIMISTIC_LOCKING) {
if (getOptimisticLockingType() == OPTIMISTIC_VERSION_LOCKING_TYPE) {
VersionLockingPolicy lockingPolicy;
if (getOptimisticVersionLockingType() == OPTIMISTIC_VERSION_VERSION) {
lockingPolicy = new VersionLockingPolicy();
} else {
lockingPolicy = new TimestampLockingPolicy();
if (this.usesLocalTime()) {
((TimestampLockingPolicy) lockingPolicy).useLocalTime();
} else {
((TimestampLockingPolicy) lockingPolicy).useServerTime();
}
}
if (getVersionLockField() != null) {
lockingPolicy.setWriteLockFieldName(getVersionLockField().runtimeField().getQualifiedName());
}
lockingPolicy.setIsStoredInCache(shouldStoreVersionInCache());
runtimeDescriptor.setOptimisticLockingPolicy(lockingPolicy);
} else if (getOptimisticLockingType() == OPTIMISTIC_COLUMNS_LOCKING_TYPE) {
FieldsLockingPolicy lockingPolicy;
if (getOptimisticColumnsLockingType() == OPTIMISTIC_COLUMNS_ALL_COLUMNS) {
lockingPolicy = new AllFieldsLockingPolicy();
} else if (getOptimisticColumnsLockingType() == OPTIMISTIC_COLUMNS_CHANGED_COLUMNS) {
lockingPolicy = new ChangedFieldsLockingPolicy();
} else {
lockingPolicy = new SelectedFieldsLockingPolicy();
((SelectedFieldsLockingPolicy) lockingPolicy).setLockFieldNames(CollectionTools.vector(columnLockColumnsNames()));
}
runtimeDescriptor.setOptimisticLockingPolicy(lockingPolicy);
}
}
}
// ************* TopLink methods *************
public static XMLDescriptor buildDescriptor() {
XMLDescriptor descriptor = new XMLDescriptor();
descriptor.setJavaClass(MWTableDescriptorLockingPolicy.class);
descriptor.getInheritancePolicy().setParentClass(MWDescriptorLockingPolicy.class);
XMLCompositeObjectMapping versionLockingColumnHandleMapping = new XMLCompositeObjectMapping();
versionLockingColumnHandleMapping.setAttributeName("versionLockingColumnHandle");
versionLockingColumnHandleMapping.setGetMethodName("getVersionLockingColumnHandleForTopLink");
versionLockingColumnHandleMapping.setSetMethodName("setVersionLockingColumnHandleForTopLink");
versionLockingColumnHandleMapping.setReferenceClass(MWColumnHandle.class);
versionLockingColumnHandleMapping.setXPath("version-locking-column-handle");
descriptor.addMapping(versionLockingColumnHandleMapping);
ObjectTypeConverter optimisticLockingTypeConverter = new ObjectTypeConverter();
optimisticLockingTypeConverter.addConversionValue(
MWTableDescriptorLockingPolicy.OPTIMISTIC_COLUMNS_LOCKING_TYPE,
MWTableDescriptorLockingPolicy.OPTIMISTIC_COLUMNS_LOCKING_TYPE);
optimisticLockingTypeConverter.addConversionValue(
MWTableDescriptorLockingPolicy.OPTIMISTIC_VERSION_LOCKING_TYPE,
MWTableDescriptorLockingPolicy.OPTIMISTIC_VERSION_LOCKING_TYPE);
XMLDirectMapping optimisticLockingTypeMapping = new XMLDirectMapping();
optimisticLockingTypeMapping.setAttributeName("optimisticLockingType");
optimisticLockingTypeMapping.setXPath("optimistic-locking-type/text()");
optimisticLockingTypeMapping.setConverter(optimisticLockingTypeConverter);
descriptor.addMapping(optimisticLockingTypeMapping);
ObjectTypeConverter optimisticColumnsLockingTypeConverter = new ObjectTypeConverter();
optimisticColumnsLockingTypeConverter.addConversionValue(
MWTableDescriptorLockingPolicy.OPTIMISTIC_COLUMNS_ALL_COLUMNS,
MWTableDescriptorLockingPolicy.OPTIMISTIC_COLUMNS_ALL_COLUMNS);
optimisticColumnsLockingTypeConverter.addConversionValue(
MWTableDescriptorLockingPolicy.OPTIMISTIC_COLUMNS_CHANGED_COLUMNS,
MWTableDescriptorLockingPolicy.OPTIMISTIC_COLUMNS_CHANGED_COLUMNS);
optimisticColumnsLockingTypeConverter.addConversionValue(
MWTableDescriptorLockingPolicy.OPTIMISTIC_COLUMNS_SELECTED_COLUMNS,
MWTableDescriptorLockingPolicy.OPTIMISTIC_COLUMNS_SELECTED_COLUMNS);
XMLDirectMapping optimisticColumnsLockingTypeMapping = new XMLDirectMapping();
optimisticColumnsLockingTypeMapping.setAttributeName("optimisticColumnsLockingType");
optimisticColumnsLockingTypeMapping.setXPath("optimistic-columns-locking-type/text()");
optimisticColumnsLockingTypeMapping.setConverter(optimisticColumnsLockingTypeConverter);
descriptor.addMapping(optimisticColumnsLockingTypeMapping);
XMLCompositeCollectionMapping columnLockColumnHandlesMapping = new XMLCompositeCollectionMapping();
columnLockColumnHandlesMapping.setAttributeName("columnLockColumnHandles");
columnLockColumnHandlesMapping.setGetMethodName("getColumnLockColumnHandlesForTopLink");
columnLockColumnHandlesMapping.setSetMethodName("setColumnLockColumnHandlesForTopLink");
columnLockColumnHandlesMapping.setReferenceClass(MWColumnHandle.class);
columnLockColumnHandlesMapping.setXPath("column-lock-column-handles/column-handle");
descriptor.addMapping(columnLockColumnHandlesMapping);
return descriptor;
}
/**
* check for null
*/
private MWColumnHandle getVersionLockingColumnHandleForTopLink() {
return (this.versionLockingColumnHandle.getColumn() == null) ? null : this.versionLockingColumnHandle;
}
private void setVersionLockingColumnHandleForTopLink(MWColumnHandle handle) {
NodeReferenceScrubber scrubber = this.buildVersionLockingColumnScrubber();
this.versionLockingColumnHandle = ((handle == null) ? new MWColumnHandle(this, scrubber) : handle.setScrubber(scrubber));
}
/**
* sort the collection for TopLink
*/
private Collection getColumnLockColumnHandlesForTopLink() {
synchronized (this.columnLockColumnHandles) {
return new TreeSet(this.columnLockColumnHandles);
}
}
private void setColumnLockColumnHandlesForTopLink(Collection handles) {
for (Iterator stream = handles.iterator(); stream.hasNext(); ) {
((MWColumnHandle) stream.next()).setScrubber(this.columnLockColumnScrubber());
}
this.columnLockColumnHandles = handles;
}
}