blob: 75fb02de4a478e7657510a1b2ca68cc5ce898207 [file] [log] [blame]
/*
* Copyright (c) 2015, 2018 Christian W. Damus and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Christian W. Damus - Initial API and implementation
*
*/
package org.eclipse.ocl.examples.pivot.tests;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.DiagnosticChain;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.Diagnostician;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.ocl.pivot.internal.delegate.OCLDelegateDomain;
import org.eclipse.ocl.pivot.internal.utilities.GlobalEnvironmentFactory;
import org.eclipse.ocl.pivot.uml.UMLStandaloneSetup;
import org.eclipse.ocl.pivot.uml.internal.es2as.UML2AS;
import org.eclipse.ocl.pivot.utilities.ParserException;
import org.eclipse.uml2.common.util.UML2Util;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Profile;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.uml2.uml.util.UMLUtil;
/**
* A test case that demonstrates a memory leak in the OCL Validation infrastructure.
*/
public class LeakTests extends PivotTestCaseWithAutoTearDown
{
/**
* This is based on the way Papyrus does validation.
*/
static class MyDiagnostician extends Diagnostician
{
private boolean validatingStereotype;
@Override
public Map<Object, Object> createDefaultContext() {
Map<Object, Object> context = super.createDefaultContext();
if (context != null) {
OCLDelegateDomain.initializePivotOnlyDiagnosticianContext(context);
}
return context;
}
@Override
public BasicDiagnostic createDefaultDiagnostic(EObject eObject) {
ResourceSet resourceSet = eObject.eResource().getResourceSet();
if (resourceSet != null) {
OCLDelegateDomain.initializePivotOnlyDiagnosticianResourceSet(resourceSet);
}
return super.createDefaultDiagnostic(eObject);
}
protected boolean doValidateStereotypeApplications(EObject eObject, DiagnosticChain diagnostics, Map<Object, Object> context) {
if (validatingStereotype) {
// this function is called recursively. Avoid trying to obtain stereotype applications, if we are
// already examining a stereotype
return true;
}
List<EObject> stereotypeApplications = eObject instanceof Element ? ((Element) eObject).getStereotypeApplications() : Collections.<EObject> emptyList();
if (!stereotypeApplications.isEmpty()) {
Iterator<EObject> i = stereotypeApplications.iterator();
boolean result;
validatingStereotype = true;
try {
result = validate(i.next(), diagnostics, context);
while (i.hasNext() && (result || diagnostics != null)) {
result &= validate(i.next(), diagnostics, context);
}
} finally {
validatingStereotype = false;
}
return result;
} else {
return true;
}
}
@Override
protected boolean doValidateContents(EObject eObject, DiagnosticChain diagnostics, Map<Object, Object> context) {
boolean result = doValidateStereotypeApplications(eObject, diagnostics, context);
if (result || diagnostics != null) {
result &= super.doValidateContents(eObject, diagnostics, context);
}
return result;
}
@Override
public boolean validate(EObject eObject, DiagnosticChain diagnostics, Map<Object, Object> context) {
if (!context.containsKey(this)) {
// put instance of the UMLDiagnostician into context map to identify first invocation
// (validate is called recursively)
context.put(this, null);
BasicDiagnostic newDiagChain = createDefaultDiagnostic(eObject);
boolean ok = super.validate(eObject, newDiagChain, context);
// replace markers here instead of using a validation adapter, see
// bug 410457 - [Validation] Ghost markers when validating profile constraints
// bug 410119 - [Validation] markers related to stereotype applications are not updated in diagrams
// bug 410059 - [Validation] delete subtree does not remove markers associated with stereotypes
for (Diagnostic d : newDiagChain.getChildren()) {
Object data[] = d.getData().toArray();
if (data.length > 0) {
Object target = data[0];
if (target instanceof EObject) {
EObject base = UMLUtil.getBaseElement((EObject) target);
if (base != null) {
data[0] = base;
}
}
}
diagnostics.add(new BasicDiagnostic(d.getSeverity(), d.getSource(), d.getCode(), d.getMessage(), data));
}
return ok;
} else {
return super.validate(eObject, diagnostics, context);
}
}
}
/**
* A test that demonstrates the memory leak from validation of the model.
* @throws ParserException
*/
public void testValidateProfileLeak() throws InterruptedException, ParserException { // Bug 459276
GlobalEnvironmentFactory.disposeInstance();
// PartialModels.PARTIAL_MODELS.setState(true);
// UML2AS.CONVERT_RESOURCE.setState(true);
UMLStandaloneSetup.init();
ResourceSet resourceSet = new ResourceSetImpl();
UML2AS.initializeUML(resourceSet);
URI testModelURI = getTestModelURI("models/uml/Bug459276.uml");
EClass package1 = UMLPackage.Literals.PACKAGE;
Package umlModel = UML2Util.load(resourceSet, testModelURI, package1);
EcoreUtil.resolveAll(resourceSet);
Profile umlProfile = umlModel.getAppliedProfile("j2ee");
assertNotNull("No UML Profile for leak test", umlProfile);
// Validate the model
Diagnostician diagnostician = new MyDiagnostician();
diagnostician.validate(umlModel);
// It doesn't matter what the results of validation are, only that
// OCL constraints were parsed by the OCL validation delegate
GlobalEnvironmentFactory globalEnvironmentFactory = GlobalEnvironmentFactory.getInstance();
org.eclipse.ocl.pivot.Profile asProfile = globalEnvironmentFactory.getASOf(org.eclipse.ocl.pivot.Profile.class, umlProfile);
assertNotNull("No AS Profile for leak", asProfile);
WeakReference<org.eclipse.uml2.uml.Profile> umlProfileRef = new WeakReference<org.eclipse.uml2.uml.Profile>(umlProfile);
WeakReference<org.eclipse.ocl.pivot.Profile> asProfileRef = new WeakReference<org.eclipse.ocl.pivot.Profile>(asProfile);
assertNotNull("No UML Profile for leak test", umlProfileRef.get());
assertNotNull("No AS Profile for leak", asProfileRef.get());
//
// Eliminate our references
//
diagnostician = null; // There is no "dispose" API
umlModel = null;
umlProfile = null;
asProfile = null;
disposeResourceSet(resourceSet);
resourceSet = null;
globalEnvironmentFactory = null;
//
// Garbage collect and check that there are no other references
//
System.gc();
assertNull("UML Profile has leaked", umlProfileRef.get());
assertNull("AS Profile has leaked", asProfileRef.get());
}
}