blob: 527f78b218c8c20dd2b828653c6366a97c44b238 [file] [log] [blame]
/**
* Copyright (c) 2009, 2019 Mia-Software 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:
* Gregoire DUPE (Mia-Software) - initial API and implementation
* Nicolas Bros (Mia-Software)
*/
package org.eclipse.modisco.infra.query.core.java.internal;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.plugin.EcorePlugin;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.modisco.infra.common.core.internal.resource.MoDiscoResourceSet;
import org.eclipse.modisco.infra.common.core.logging.MoDiscoLogger;
import org.eclipse.modisco.infra.query.JavaModelQuery;
import org.eclipse.modisco.infra.query.ModelQuery;
import org.eclipse.modisco.infra.query.ModelQuerySet;
import org.eclipse.modisco.infra.query.QueryPackage;
import org.eclipse.modisco.infra.query.core.AbstractModelQuery;
import org.eclipse.modisco.infra.query.core.IModelQueryFactory;
import org.eclipse.modisco.infra.query.core.ModelQuerySetCatalog;
import org.eclipse.modisco.infra.query.core.exception.BundleClassPathException;
import org.eclipse.modisco.infra.query.core.exception.ExecutionEnvironmentException;
import org.eclipse.modisco.infra.query.core.exception.MissingBundleException;
import org.eclipse.modisco.infra.query.core.exception.ModelQueryException;
import org.eclipse.modisco.infra.query.core.exception.ModelQueryExecutionException;
import org.eclipse.modisco.infra.query.core.exception.ProjectDisabledException;
import org.eclipse.modisco.infra.query.core.internal.Activator;
import org.eclipse.modisco.infra.query.core.java.IJavaModelQuery;
import org.eclipse.osgi.util.ManifestElement;
import org.eclipse.pde.core.plugin.IPluginAttribute;
import org.eclipse.pde.core.plugin.IPluginElement;
import org.eclipse.pde.core.plugin.IPluginExtension;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.IPluginObject;
import org.eclipse.pde.core.plugin.PluginRegistry;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
/**
* @deprecated replaced by EMF Facet, cf. https://bugs.eclipse.org/bugs/show_bug.cgi?id=470578
*/
@Deprecated
public class JavaModelQueryFactory implements IModelQueryFactory {
private static final long DELAY = 1000;
private static IResourceChangeListener changeListener = null;
private static HashMap<IProject, Bundle> bundleMap = new HashMap<IProject, Bundle>();
private static HashMap<IProject, Long> disabledProjects = new HashMap<IProject, Long>();
private static HashMap<IProject, ModelQueryException> disabledProjectsCause = new HashMap<IProject, ModelQueryException>();
public JavaModelQueryFactory() {
if (JavaModelQueryFactory.changeListener == null) {
JavaModelQueryFactory.changeListener = JavaModelQueryFactory.initChangeListener();
}
}
public AbstractModelQuery create(final ModelQuery modelQuery, final Bundle bundle)
throws ModelQueryException {
if (!(modelQuery instanceof JavaModelQuery)) {
throw new ModelQueryException("Wrong kind of ModelQuery: " //$NON-NLS-1$
+ modelQuery.getClass().getSimpleName() + " found, " //$NON-NLS-1$
+ JavaModelQuery.class.getSimpleName() + " expected."); //$NON-NLS-1$
}
AbstractModelQuery javaModelQueryInst = null;
IProject project = null;
try {
Bundle localBundle;
JavaModelQuery javaModelQuery = (JavaModelQuery) modelQuery;
if (javaModelQuery.getModelQuerySet() == null) {
throw new IllegalStateException("JavaModelQuery '" //$NON-NLS-1$
+ javaModelQuery.getName() + " 'is not in any query set"); //$NON-NLS-1$
}
if (bundle == null) {
final IWorkspace ws = ResourcesPlugin.getWorkspace();
final String querySetName = modelQuery.getModelQuerySet().getName();
URI uri = ModelQuerySetCatalog.getSingleton().getURI(querySetName);
if (uri == null) {
throw new IllegalStateException("QuerySet not found: " + querySetName); //$NON-NLS-1$
}
Path querySetPath = new Path(uri.toPlatformString(true));
IResource file = ws.getRoot().findMember(querySetPath);
if (file == null) {
throw new IllegalStateException(
"QuerySet file not found: " + querySetPath.toString()); //$NON-NLS-1$
}
project = file.getProject();
localBundle = JavaModelQueryFactory.bundleMap.get(project);
checkProjectEnabled(project);
if (localBundle == null) {
localBundle = Activator.getDefault().installBundle(project);
loadMetamodelsFromPluginXml(project, localBundle);
JavaModelQueryFactory.bundleMap.put(project, localBundle);
// reload the query
ModelQuerySet oldModelQuerySet = javaModelQuery.getModelQuerySet();
if (oldModelQuerySet == null) {
throw new IllegalStateException("JavaModelQuery '" //$NON-NLS-1$
+ javaModelQuery.getName() + " 'is not in any query set"); //$NON-NLS-1$
}
ModelQuerySet modelQuerySet = ModelQuerySetCatalog.getSingleton()
.getModelQuerySet(querySetName);
if (modelQuerySet == null) {
throw new IllegalStateException("Couln't find a QuerySet named '" //$NON-NLS-1$
+ querySetName + "'"); //$NON-NLS-1$
}
javaModelQuery = (JavaModelQuery) modelQuerySet.getQuery(javaModelQuery
.getName());
if (javaModelQuery == null) {
throw new IllegalStateException("Couldn't reload the JavaModelQuery"); //$NON-NLS-1$
}
}
checkBundleClassPath(project, localBundle);
checkRequiredExecutionEnvironment(localBundle);
checkDependencies(localBundle);
} else {
localBundle = bundle;
}
String className = javaModelQuery.getImplementationClassName();
if (className == null || className.length() == 0) {
throw new ModelQueryExecutionException("implementationClassName is empty"); //$NON-NLS-1$
}
Class<?> javaModelQueryClass = localBundle.loadClass(className);
if (!IJavaModelQuery.class.isAssignableFrom(javaModelQueryClass)) {
throw new Exception(className + " does not implement " //$NON-NLS-1$
+ IJavaModelQuery.class.getSimpleName() + "."); //$NON-NLS-1$
}
javaModelQueryInst = createJavaModelQueryImpl(javaModelQuery, javaModelQueryClass);
javaModelQueryInst.setCheckResult(bundle != null);
} catch (ExecutionEnvironmentException e) {
if (project != null) {
synchronized (JavaModelQueryFactory.disabledProjects) {
JavaModelQueryFactory.disabledProjects.put(project,
new Long(System.currentTimeMillis()));
}
}
throw e;
} catch (MissingBundleException e) {
if (project != null) {
synchronized (JavaModelQueryFactory.disabledProjects) {
JavaModelQueryFactory.disabledProjects.put(project,
new Long(System.currentTimeMillis()));
}
}
throw e;
} catch (BundleClassPathException e) {
if (project != null) {
synchronized (JavaModelQueryFactory.disabledProjects) {
JavaModelQueryFactory.disabledProjects.put(project,
new Long(System.currentTimeMillis()));
}
}
throw e;
} catch (ProjectDisabledException e) {
throw e;
} catch (Exception e) {
ModelQueryException mqe = new ModelQueryException("Failed to load the model query: " //$NON-NLS-1$
+ modelQuery.getModelQuerySet().getName() + "::" //$NON-NLS-1$
+ modelQuery.getName(), e);
MoDiscoLogger.logError(mqe, Activator.getDefault());
throw mqe;
}
return javaModelQueryInst;
}
private void checkProjectEnabled(final IProject project) throws ProjectDisabledException {
synchronized (JavaModelQueryFactory.disabledProjects) {
Long timestamp = JavaModelQueryFactory.disabledProjects.get(project);
if (timestamp != null) {
long sinceLastTry = System.currentTimeMillis() - timestamp.longValue();
if (sinceLastTry < JavaModelQueryFactory.DELAY) {
throw new ProjectDisabledException(
JavaModelQueryFactory.disabledProjectsCause.get(project));
}
JavaModelQueryFactory.disabledProjectsCause.remove(project);
JavaModelQueryFactory.disabledProjects.remove(project);
}
}
}
private void checkDependencies(final Bundle localBundle) throws BundleException,
MissingBundleException {
String dependencies = (String) localBundle.getHeaders().get(
org.osgi.framework.Constants.REQUIRE_BUNDLE);
ManifestElement[] manifestElements = ManifestElement.parseHeader(
org.osgi.framework.Constants.REQUIRE_BUNDLE, dependencies);
for (ManifestElement manifestElement : manifestElements) {
String bundleName = manifestElement.getValue();
if (Platform.getBundle(bundleName) == null) {
throw new MissingBundleException(bundleName);
}
}
}
private void checkRequiredExecutionEnvironment(final Bundle localBundle)
throws ExecutionEnvironmentException {
String requiredExecEnv = (String) localBundle.getHeaders().get(
org.osgi.framework.Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT);
Properties p = System.getProperties();
String availableExecEnv = (String) p
.get(org.osgi.framework.Constants.FRAMEWORK_EXECUTIONENVIRONMENT);
if (!availableExecEnv.contains(requiredExecEnv)) {
throw new ExecutionEnvironmentException(requiredExecEnv, availableExecEnv);
}
}
/**
* Check that the output location of the Java project containing the query
* implementation is also on the Bundle-ClassPath of the project. Otherwise,
* The Java queries implementations it contains will not be found.
*/
private void checkBundleClassPath(final IProject project, final Bundle localBundle)
throws BundleClassPathException {
boolean found = false;
IPath outputLocation = null;
try {
IJavaProject javaProject = JavaCore.create(project);
if (javaProject != null) {
outputLocation = javaProject.getOutputLocation();
Object header = localBundle.getHeaders().get(
org.osgi.framework.Constants.BUNDLE_CLASSPATH);
if (header instanceof String) {
String bundleClasspath = (String) header;
String[] bundleClassPathElements = bundleClasspath.split(","); //$NON-NLS-1$
for (String bundleClassPathEntry : bundleClassPathElements) {
if (project.getFullPath().append(bundleClassPathEntry.trim())
.equals(outputLocation)) {
found = true;
}
}
}
}
} catch (Exception e) {
MoDiscoLogger.logError(e, "Error checking bundle classpath", Activator.getDefault()); //$NON-NLS-1$
return;
}
if (!found) {
throw new BundleClassPathException(project, outputLocation, localBundle);
}
}
/** This methods is dedicated to isolate the "Unchecked cast" warning. */
@SuppressWarnings("unchecked")
private AbstractModelQuery createJavaModelQueryImpl(final JavaModelQuery javaModelQuery,
final Class<?> javaModelQueryClass) throws InstantiationException,
IllegalAccessException {
IJavaModelQuery<EObject, ?> javaModelQueryInst;
javaModelQueryInst = (IJavaModelQuery<EObject, ?>) javaModelQueryClass.newInstance();
AbstractModelQuery adapter = new JavaModelQueryAdapter(javaModelQuery, javaModelQueryInst);
return adapter;
}
public EClass getManagedModelQueryType() {
return QueryPackage.eINSTANCE.getJavaModelQuery();
}
static HashMap<IProject, Bundle> getBundleMap() {
return JavaModelQueryFactory.bundleMap;
}
/**
* Add a change listener that when a class is modified, removes the
* containing project from the bundle map, so that it gets re-installed when
* a query is executed.
*/
private static IResourceChangeListener initChangeListener() {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IResourceChangeListener listener = new IResourceChangeListener() {
public void resourceChanged(final IResourceChangeEvent event) {
if (event != null) {
checkDelta(event.getDelta());
}
}
private void checkDelta(final IResourceDelta delta) {
if (delta != null) {
for (IResourceDelta subdelta : delta.getAffectedChildren()) {
checkDelta(subdelta);
}
IResource resource = delta.getResource();
if (resource != null) {
if ("class".equals(resource.getFileExtension())) { //$NON-NLS-1$
IProject project = resource.getProject();
Set<IProject> listened = JavaModelQueryFactory.getBundleMap().keySet();
if (listened.contains(project)) {
if (project.getLocation() != null) {
JavaModelQueryFactory.getBundleMap().remove(project);
}
}
}
}
}
}
};
workspace.addResourceChangeListener(listener);
return listener;
// workspace.removeResourceChangeListener(listener);
}
/**
* Load the {@link EPackage}s containing the metamodel implementations
* defined in the <code>plugin.xml</code> of the given project.
*/
void loadMetamodelsFromPluginXml(final IProject project, final Bundle bundle) {
final String generatedPackageExtensionPoint = EcorePlugin.getPlugin().getSymbolicName()
+ "." //$NON-NLS-1$
+ EcorePlugin.GENERATED_PACKAGE_PPID;
IPluginModelBase pluginModel = PluginRegistry.findModel(project);
IPluginExtension[] extensions = pluginModel.getExtensions().getExtensions();
for (IPluginExtension pluginExtension : extensions) {
if (generatedPackageExtensionPoint.equals(pluginExtension.getPoint())) {
IPluginObject[] children = pluginExtension.getChildren();
for (IPluginObject child : children) {
if (child instanceof IPluginElement) {
IPluginElement pluginElement = (IPluginElement) child;
IPluginAttribute[] attributes = pluginElement.getAttributes();
String uri = null;
String className = null;
for (IPluginAttribute pluginAttribute : attributes) {
if ("class".equalsIgnoreCase(pluginAttribute.getName())) { //$NON-NLS-1$
className = pluginAttribute.getValue();
} else if ("uri".equalsIgnoreCase(pluginAttribute.getName())) { //$NON-NLS-1$
uri = pluginAttribute.getValue();
}
}
if (className == null || uri == null) {
MoDiscoLogger.logError("Error in extension " //$NON-NLS-1$
+ EcorePlugin.GENERATED_PACKAGE_PPID
+ ": couldn't hotload metamodel", Activator.getDefault()); //$NON-NLS-1$
}
loadEPackage(bundle, uri, className);
}
}
}
}
}
private void loadEPackage(final Bundle bundle, final String uri, final String className) {
try {
Class<?> ePackageClass = bundle.loadClass(className);
Field instanceField = ePackageClass.getField("eINSTANCE"); //$NON-NLS-1$
EPackage ePackage = (EPackage) instanceField.get(null);
EPackage.Registry.INSTANCE.put(ePackage.getNsURI(), ePackage);
MoDiscoResourceSet.getResourceSetSingleton().aResourceHasBeenLoaded(
ePackage.eResource());
} catch (Exception e) {
MoDiscoLogger.logError(e, "Failed to load an EPackage: " + uri, //$NON-NLS-1$
Activator.getDefault());
}
}
}