All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bimserver.plugins.PluginManager Maven / Gradle / Ivy

Go to download

Shared libraries, common parent for both BIMserver and BimServerClientLib

There is a newer version: 2.0.0
Show newest version
package org.bimserver.plugins;

/******************************************************************************
 * Copyright (C) 2009-2018  BIMserver.org
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see {@literal}.
 *****************************************************************************/

import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.zip.ZipEntry;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.bimserver.emf.MetaDataManager;
import org.bimserver.emf.Schema;
import org.bimserver.interfaces.objects.SPluginBundle;
import org.bimserver.interfaces.objects.SPluginBundleType;
import org.bimserver.interfaces.objects.SPluginBundleVersion;
import org.bimserver.interfaces.objects.SPluginInformation;
import org.bimserver.interfaces.objects.SPluginType;
import org.bimserver.models.store.Parameter;
import org.bimserver.models.store.ServiceDescriptor;
import org.bimserver.plugins.classloaders.DelegatingClassLoader;
import org.bimserver.plugins.classloaders.EclipsePluginClassloader;
import org.bimserver.plugins.classloaders.FileJarClassLoader;
import org.bimserver.plugins.classloaders.JarClassLoader;
import org.bimserver.plugins.classloaders.PublicFindClassClassLoader;
import org.bimserver.plugins.deserializers.DeserializeException;
import org.bimserver.plugins.deserializers.DeserializerPlugin;
import org.bimserver.plugins.deserializers.StreamingDeserializerPlugin;
import org.bimserver.plugins.modelchecker.ModelCheckerPlugin;
import org.bimserver.plugins.modelcompare.ModelComparePlugin;
import org.bimserver.plugins.modelmerger.ModelMergerPlugin;
import org.bimserver.plugins.objectidms.ObjectIDM;
import org.bimserver.plugins.objectidms.ObjectIDMException;
import org.bimserver.plugins.objectidms.ObjectIDMPlugin;
import org.bimserver.plugins.queryengine.QueryEnginePlugin;
import org.bimserver.plugins.renderengine.RenderEnginePlugin;
import org.bimserver.plugins.serializers.MessagingSerializerPlugin;
import org.bimserver.plugins.serializers.MessagingStreamingSerializerPlugin;
import org.bimserver.plugins.serializers.SerializerPlugin;
import org.bimserver.plugins.serializers.StreamingSerializerPlugin;
import org.bimserver.plugins.services.BimServerClientInterface;
import org.bimserver.plugins.services.NewExtendedDataOnProjectHandler;
import org.bimserver.plugins.services.NewExtendedDataOnRevisionHandler;
import org.bimserver.plugins.services.NewRevisionHandler;
import org.bimserver.plugins.services.ServicePlugin;
import org.bimserver.plugins.stillimagerenderer.StillImageRenderPlugin;
import org.bimserver.plugins.web.WebModulePlugin;
import org.bimserver.shared.AuthenticationInfo;
import org.bimserver.shared.BimServerClientFactory;
import org.bimserver.shared.ChannelConnectionException;
import org.bimserver.shared.ServiceFactory;
import org.bimserver.shared.exceptions.PluginException;
import org.bimserver.shared.exceptions.ServiceException;
import org.bimserver.shared.exceptions.UserException;
import org.bimserver.shared.meta.SServicesMap;
import org.bimserver.utils.FakeClosingInputStream;
import org.bimserver.utils.PathUtils;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.collection.CollectResult;
import org.eclipse.aether.collection.DependencyCollectionException;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.resolution.ArtifactDescriptorException;
import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
import org.eclipse.aether.resolution.ArtifactDescriptorResult;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PluginManager implements PluginManagerInterface {
	private static final Logger LOGGER = LoggerFactory.getLogger(PluginManager.class);
	private static Unmarshaller PLUGIN_DESCRIPTOR_UNMARSHALLER;
	private final Map, Set> implementations = new LinkedHashMap<>();
	private final Map pluginToPluginContext = new HashMap<>();

	private final Map pluginBundleIdentifierToPluginBundle = new HashMap<>();
	private final Map pluginBundleVersionIdentifierToPluginBundle = new HashMap<>();
	private final Map pluginBundleIdentifierToCurrentPluginBundleVersionIdentifier = new HashMap<>();

	private final Path tempDir;
	private final String baseClassPath;
	private final ServiceFactory serviceFactory;
	private final NotificationsManagerInterface notificationsManagerInterface;
	private final SServicesMap servicesMap;
	private final Path pluginsDir;
	private PluginChangeListener pluginChangeListener;
	private BimServerClientFactory bimServerClientFactory;
	private MetaDataManager metaDataManager;
	private MavenPluginRepository mavenPluginRepository;
	private final List jarClassLoaders = new ArrayList<>();
	private BasicServerInfoProvider basicServerInfoProvider;

	static {
		try {
			JAXBContext jaxbContext = JAXBContext.newInstance(PluginDescriptor.class);
			PLUGIN_DESCRIPTOR_UNMARSHALLER = jaxbContext.createUnmarshaller();
		} catch (JAXBException e) {
			LOGGER.error("", e);
		}
	}
	
	public PluginManager(Path tempDir, Path pluginsDir, MavenPluginRepository mavenPluginRepository, String baseClassPath, ServiceFactory serviceFactory, NotificationsManagerInterface notificationsManagerInterface,
			SServicesMap servicesMap, BasicServerInfoProvider basicServerInfoProvider) {
		this.mavenPluginRepository = mavenPluginRepository;
		this.basicServerInfoProvider = basicServerInfoProvider;
		LOGGER.debug("Creating new PluginManager");
		this.pluginsDir = pluginsDir;
		this.tempDir = tempDir;
		this.baseClassPath = baseClassPath;
		this.serviceFactory = serviceFactory;
		this.notificationsManagerInterface = notificationsManagerInterface;
		this.servicesMap = servicesMap;

		if (pluginsDir != null) {
			if (!Files.isDirectory(pluginsDir)) {
				try {
					Files.createDirectories(pluginsDir);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	// public void installLocalPlugins() {
	// if (pluginsDir != null) {
	// if (!Files.isDirectory(pluginsDir)) {
	// try {
	// Files.createDirectories(pluginsDir);
	// } catch (IOException e) {
	// e.printStackTrace();
	// }
	// } else {
	// try {
	// for (Path file : PathUtils.list(pluginsDir)) {
	// try {
	// PluginBundleVersionIdentifier pluginBundleVersionIdentifier =
	// PluginBundleVersionIdentifier.fromFileName(file.getFileName().toString());
	// SPluginBundle extractPluginBundleFromJar =
	// extractPluginBundleFromJar(file);
	// install(pluginBundleVersionIdentifier,
	// pluginBundleVersionIdentifier.getPluginBundleIdentifier(),
	// pluginBundleVersionIdentifier, file, pomFile, plugins, false);
	//// loadPluginsFromJar(pluginBundleVersionIdentifier, file,
	// extractPluginBundleFromJar(file),
	// extractPluginBundleVersionFromJar(file));
	// LOGGER.info("Loading " +
	// pluginBundleVersionIdentifier.getHumanReadable());
	// } catch (PluginException e) {
	// LOGGER.error("", e);
	// }
	// }
	// } catch (IOException e) {
	// LOGGER.error("", e);
	// }
	// }
	// }
	// }

	public void loadPluginsFromEclipseProjectNoExceptions(Path projectRoot) {
		try {
			loadPluginsFromEclipseProject(projectRoot);
		} catch (PluginException e) {
			LOGGER.error("", e);
		}
	}

	private PublicFindClassClassLoader loadDependencies(Set bimServerDependencies, Model model, PublicFindClassClassLoader previous) throws FileNotFoundException, IOException {
		List dependencies = model.getDependencies();
		Iterator it = dependencies.iterator();

		Path workspaceDir = Paths.get("..");
		bimServerDependencies.add(new org.bimserver.plugins.Dependency(workspaceDir.resolve("PluginBase/target/classes")));
		bimServerDependencies.add(new org.bimserver.plugins.Dependency(workspaceDir.resolve("Shared/target/classes")));

		while (it.hasNext()) {
			org.apache.maven.model.Dependency depend = it.next();
			try {
				if (depend.getGroupId().equals("org.opensourcebim") && (depend.getArtifactId().equals("shared") || depend.getArtifactId().equals("pluginbase") || depend.getArtifactId().equals("ifcplugins"))) {
					// Skip this one, because we have already
					// TODO we might want to check the version though
					continue;
				}
				if (depend.isOptional() || "test".equals(depend.getScope())) {
					continue;
				}
				Dependency dependency2 = new Dependency(new DefaultArtifact(depend.getGroupId() + ":" + depend.getArtifactId() + ":jar:" + depend.getVersion()), "compile");
				DelegatingClassLoader depDelLoader = new DelegatingClassLoader(previous);

				if (!dependency2.getArtifact().isSnapshot()) {
					if (dependency2.getArtifact().getFile() != null) {
						bimServerDependencies.add(new org.bimserver.plugins.Dependency(dependency2.getArtifact().getFile().toPath()));
						loadDependencies(dependency2.getArtifact().getFile().toPath(), depDelLoader);
					} else {
						ArtifactRequest request = new ArtifactRequest();
						request.setArtifact(dependency2.getArtifact());
						request.setRepositories(mavenPluginRepository.getRepositories());
						try {
							ArtifactResult resolveArtifact = mavenPluginRepository.getSystem().resolveArtifact(mavenPluginRepository.getSession(), request);
							if (resolveArtifact.getArtifact().getFile() != null) {
								bimServerDependencies.add(new org.bimserver.plugins.Dependency(resolveArtifact.getArtifact().getFile().toPath()));
								loadDependencies(resolveArtifact.getArtifact().getFile().toPath(), depDelLoader);
							} else {
								// TODO error?
							}
						} catch (ArtifactResolutionException e) {
							e.printStackTrace();
						}
					}
				} else {
					// Snapshot projects linked in Eclipse
					ArtifactRequest request = new ArtifactRequest();
					if ((!"test".equals(dependency2.getScope()) && !dependency2.getArtifact().isSnapshot())) {
						request.setArtifact(dependency2.getArtifact());
						request.setRepositories(mavenPluginRepository.getLocalRepositories());
						try {
							ArtifactResult resolveArtifact = mavenPluginRepository.getSystem().resolveArtifact(mavenPluginRepository.getSession(), request);
							if (resolveArtifact.getArtifact().getFile() != null) {
								bimServerDependencies.add(new org.bimserver.plugins.Dependency(resolveArtifact.getArtifact().getFile().toPath()));
								loadDependencies(resolveArtifact.getArtifact().getFile().toPath(), depDelLoader);
							} else {
								// TODO error?
							}
						} catch (Exception e) {
							LOGGER.info(dependency2.getArtifact().toString());
							e.printStackTrace();
						}

						// bimServerDependencies.add(new
						// org.bimserver.plugins.Dependency(resolveArtifact.getArtifact().getFile().toPath()));
					}
				}
				ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
				descriptorRequest.setArtifact(dependency2.getArtifact());
				descriptorRequest.setRepositories(mavenPluginRepository.getRepositories());
				ArtifactDescriptorResult descriptorResult = mavenPluginRepository.getSystem().readArtifactDescriptor(mavenPluginRepository.getSession(), descriptorRequest);

				CollectRequest collectRequest = new CollectRequest();
				collectRequest.setRootArtifact(descriptorResult.getArtifact());
				collectRequest.setDependencies(descriptorResult.getDependencies());
				collectRequest.setManagedDependencies(descriptorResult.getManagedDependencies());
				collectRequest.setRepositories(descriptorResult.getRepositories());
				DependencyNode node = mavenPluginRepository.getSystem().collectDependencies(mavenPluginRepository.getSession(), collectRequest).getRoot();

				DependencyRequest dependencyRequest = new DependencyRequest();
				dependencyRequest.setRoot(node);

				CollectResult collectResult = mavenPluginRepository.getSystem().collectDependencies(mavenPluginRepository.getSession(), collectRequest);

				PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
				// collectResult.getRoot().accept(new
				// ConsoleDependencyGraphDumper());
				collectResult.getRoot().accept(nlg);

				try {
					mavenPluginRepository.getSystem().resolveDependencies(mavenPluginRepository.getSession(), dependencyRequest);
				} catch (DependencyResolutionException e) {
					// Ignore
				}

				for (DependencyNode dependencyNode : nlg.getNodes()) {
					ArtifactRequest newRequest = new ArtifactRequest(dependencyNode);
					newRequest.setRepositories(mavenPluginRepository.getRepositories());
					ArtifactResult resolveArtifact = mavenPluginRepository.getSystem().resolveArtifact(mavenPluginRepository.getSession(), newRequest);

					Artifact artifact = resolveArtifact.getArtifact();
					Path jarFile = Paths.get(artifact.getFile().getAbsolutePath());

					loadDependencies(jarFile, depDelLoader);

					Artifact versionArtifact = new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(), "pom", artifact.getVersion());

					ArtifactRequest request = new ArtifactRequest();
					request.setArtifact(versionArtifact);
					request.setRepositories(mavenPluginRepository.getRepositories());

					// try {
					// ArtifactResult resolveArtifact =
					// mavenPluginRepository.getSystem().resolveArtifact(mavenPluginRepository.getSession(),
					// request);
					// File depPomFile =
					// resolveArtifact.getArtifact().getFile();
					// if (depPomFile != null) {
					// MavenXpp3Reader mavenreader = new MavenXpp3Reader();
					// Model depModel = null;
					// try (FileReader reader = new FileReader(depPomFile)) {
					// try {
					// depModel = mavenreader.read(reader);
					// } catch (XmlPullParserException e) {
					// e.printStackTrace();
					// }
					// }
					// previous = loadDependencies(bimServerDependencies,
					// depModel, previous);
					// } else {
					// LOGGER.info("Artifact not found " + versionArtifact);
					// }
					// } catch (ArtifactResolutionException e1) {
					// LOGGER.error(e1.getMessage());
					// }

					// EclipsePluginClassloader depLoader = new
					// EclipsePluginClassloader(depDelLoader, projectRoot);

					bimServerDependencies.add(new org.bimserver.plugins.Dependency(jarFile));

				}
				previous = depDelLoader;
			} catch (DependencyCollectionException e) {
				e.printStackTrace();
			} catch (ArtifactDescriptorException e2) {
				e2.printStackTrace();
			} catch (ArtifactResolutionException e) {
				e.printStackTrace();
			}
		}
		return previous;
	}

	public PluginBundle loadJavaProject(Path projectRoot, Path pomFile, Path pluginFolder, PluginDescriptor pluginDescriptor) throws PluginException, FileNotFoundException, IOException, XmlPullParserException {
		MavenXpp3Reader mavenreader = new MavenXpp3Reader();
		Model model = null;
		try (FileReader reader = new FileReader(pomFile.toFile())) {
			model = mavenreader.read(reader);
		}
		PluginBundleVersionIdentifier pluginBundleVersionIdentifier = new PluginBundleVersionIdentifier(model.getGroupId(), model.getArtifactId(), model.getVersion());

		if (pluginBundleIdentifierToPluginBundle.containsKey(pluginBundleVersionIdentifier.getPluginBundleIdentifier())) {
			throw new PluginException("Plugin " + pluginBundleVersionIdentifier.getPluginBundleIdentifier().getHumanReadable() + " already loaded (version "
					+ pluginBundleIdentifierToPluginBundle.get(pluginBundleVersionIdentifier.getPluginBundleIdentifier()).getPluginBundleVersion().getVersion() + ")");
		}
		DelegatingClassLoader delegatingClassLoader = new DelegatingClassLoader(getClass().getClassLoader());
		PublicFindClassClassLoader previous = new PublicFindClassClassLoader(getClass().getClassLoader()) {
			@Override
			public Class findClass(String name) throws ClassNotFoundException {
				return null;
			}

			@Override
			public URL findResource(String name) {
				return null;
			}

			@Override
			public void dumpStructure(int indent) {
			}
		};

		Set bimServerDependencies = new HashSet<>();

		pluginBundleVersionIdentifier = new PluginBundleVersionIdentifier(new PluginBundleIdentifier(model.getGroupId(), model.getArtifactId()), model.getVersion());

		previous = loadDependencies(bimServerDependencies, model, previous);

		delegatingClassLoader.add(previous);
		// Path libFolder = projectRoot.resolve("lib");
		// loadDependencies(libFolder, delegatingClassLoader);
		EclipsePluginClassloader pluginClassloader = new EclipsePluginClassloader(delegatingClassLoader, projectRoot);
		// pluginClassloader.dumpStructure(0);

		ResourceLoader resourceLoader = new ResourceLoader() {
			@Override
			public InputStream load(String name) {
				try {
					return Files.newInputStream(pluginFolder.resolve(name));
				} catch (IOException e) {
					e.printStackTrace();
				}
				return null;
			}
		};

		SPluginBundle sPluginBundle = new SPluginBundle();
		if (model.getOrganization() == null) {
			throw new PluginException("Plugis are required to have an organization in the pom.xml file");
		}
		sPluginBundle.setOrganization(model.getOrganization().getName());
		sPluginBundle.setName(model.getName());

		SPluginBundleVersion sPluginBundleVersion = createPluginBundleVersionFromMavenModel(model, true);

		Path icon = projectRoot.resolve("icon.png");
		if (Files.exists(icon)) {
			byte[] iconBytes = Files.readAllBytes(icon);
			sPluginBundleVersion.setIcon(iconBytes);
		}

		sPluginBundle.setInstalledVersion(sPluginBundleVersion);

		return loadPlugins(pluginBundleVersionIdentifier, resourceLoader, pluginClassloader, projectRoot.toUri(), projectRoot.resolve("target/classes").toString(), pluginDescriptor, PluginSourceType.ECLIPSE_PROJECT, bimServerDependencies,
				sPluginBundle, sPluginBundleVersion);
	}

	public PluginBundle loadPluginsFromEclipseProject(Path projectRoot) throws PluginException {
		try {
			if (!Files.isDirectory(projectRoot)) {
				throw new PluginException("No directory: " + projectRoot.toString());
			}
			final Path pluginFolder = projectRoot.resolve("plugin");
			if (!Files.isDirectory(pluginFolder)) {
				throw new PluginException("No 'plugin' directory found in " + projectRoot.toString());
			}
			Path pluginFile = pluginFolder.resolve("plugin.xml");
			if (!Files.exists(pluginFile)) {
				throw new PluginException("No 'plugin.xml' found in " + pluginFolder.toString());
			}

			PluginDescriptor pluginDescriptor = getPluginDescriptor(Files.newInputStream(pluginFile));

			Path pomFile = projectRoot.resolve("pom.xml");
			if (!Files.exists(pomFile)) {
				throw new PluginException("No pom.xml found in " + projectRoot);
			}
			// Path packageFile = projectRoot.resolve("package.json");

			// if (Files.exists(packageFile)) {
			// return loadJavaScriptProject(projectRoot, packageFile,
			// pluginFolder, pluginDescriptor);
			// } else if (Files.exists(pomFile)) {
			PluginBundle pluginBundle = loadJavaProject(projectRoot, pomFile, pluginFolder, pluginDescriptor);
			// } else {
			// throw new PluginException("No pom.xml or package.json found in "
			// + projectRoot.toString());
			// }

			List plugins = new ArrayList<>();
			processPluginDescriptor(pluginDescriptor, plugins);

			for (SPluginInformation sPluginInformation : plugins) {
				if (sPluginInformation.isEnabled()) {
					// For local plugins, we assume to install for all users
					sPluginInformation.setInstallForAllUsers(true);
					sPluginInformation.setInstallForNewUsers(true);

					PluginContext pluginContext = pluginBundle.getPluginContext(sPluginInformation.getIdentifier());
					if (pluginContext == null) {
						throw new PluginException("No plugin context found for " + sPluginInformation.getIdentifier());
					}
					pluginContext.getPlugin().init(pluginContext);
				}
			}

			try {
				long pluginBundleVersionId = pluginChangeListener.pluginBundleInstalled(pluginBundle);
				for (SPluginInformation sPluginInformation : plugins) {
					if (sPluginInformation.isEnabled()) {
						PluginContext pluginContext = pluginBundle.getPluginContext(sPluginInformation.getIdentifier());
						pluginChangeListener.pluginInstalled(pluginBundleVersionId, pluginContext, sPluginInformation);
					}
				}
			} catch (Exception e) {
				LOGGER.error("", e);
				throw new PluginException(e);
			}
			return pluginBundle;
		} catch (JAXBException e) {
			throw new PluginException(e);
		} catch (FileNotFoundException e) {
			throw new PluginException(e);
		} catch (IOException e) {
			throw new PluginException(e);
		} catch (XmlPullParserException e) {
			throw new PluginException(e);
		}
	}

//	@SuppressWarnings("unused")
//	private PluginBundle loadJavaScriptProject(Path projectRoot, Path packageFile, Path pluginFolder, PluginDescriptor pluginDescriptor) {
//		ObjectNode packageModel;
//		try {
//			packageModel = OBJECT_MAPPER.readValue(packageFile.toFile(), ObjectNode.class);
//			SPluginBundle sPluginBundle = new SPluginBundle();
//
//			if (!packageModel.has("organization")) {
//				throw new PluginException("package.json does not contain 'organization'");
//			}
//
//			sPluginBundle.setOrganization(packageModel.get("organization").asText());
//			sPluginBundle.setName(packageModel.get("name").asText());
//
//			SPluginBundleVersion sPluginBundleVersion = new SPluginBundleVersion();
//			sPluginBundleVersion.setType(SPluginBundleType.GITHUB);
//
//			if (!packageModel.has("organization")) {
//				throw new PluginException("package.json does not contain 'groupId'");
//			}
//			sPluginBundleVersion.setGroupId(packageModel.get("groupId").asText());
//
//			if (!packageModel.has("organization")) {
//				throw new PluginException("package.json does not contain 'artifactId'");
//			}
//			sPluginBundleVersion.setArtifactId(packageModel.get("artifactId").asText());
//
//			if (!packageModel.has("organization")) {
//				throw new PluginException("package.json does not contain 'version'");
//			}
//			sPluginBundleVersion.setVersion(packageModel.get("version").asText());
//
//			if (!packageModel.has("organization")) {
//				throw new PluginException("package.json does not contain 'description'");
//			}
//			sPluginBundleVersion.setDescription(packageModel.get("description").asText());
//
//			sPluginBundleVersion.setRepository("local");
//			sPluginBundleVersion.setType(SPluginBundleType.LOCAL_DEV);
//			sPluginBundleVersion.setMismatch(false); // TODO
//
//			sPluginBundle.setInstalledVersion(sPluginBundleVersion);
//
//			PluginBundleVersionIdentifier pluginBundleVersionIdentifier = new PluginBundleVersionIdentifier(new PluginBundleIdentifier(packageModel.get("groupId").asText(), packageModel.get("artifactId").asText()),
//					packageModel.get("version").asText());
//
//			ResourceLoader resourceLoader = new ResourceLoader() {
//				@Override
//				public InputStream load(String name) {
//					try {
//						return Files.newInputStream(pluginFolder.resolve(name));
//					} catch (IOException e) {
//						e.printStackTrace();
//					}
//					return null;
//				}
//			};
//
//			return loadPlugins(pluginBundleVersionIdentifier, resourceLoader, null, projectRoot.toUri(), null, pluginDescriptor, PluginSourceType.ECLIPSE_PROJECT, null, sPluginBundle, sPluginBundleVersion);
//		} catch (JsonParseException e1) {
//			e1.printStackTrace();
//		} catch (JsonMappingException e1) {
//			e1.printStackTrace();
//		} catch (IOException e1) {
//			e1.printStackTrace();
//		} catch (PluginException e) {
//			e.printStackTrace();
//		}
//		return null;
//	}

	private void loadDependencies(Path libFile, DelegatingClassLoader classLoader) throws FileNotFoundException, IOException {
		if (libFile.getFileName().toString().toLowerCase().endsWith(".jar")) {
			FileJarClassLoader jarClassLoader = new FileJarClassLoader(this, classLoader, libFile);
			jarClassLoaders.add(jarClassLoader);
			classLoader.add(jarClassLoader);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private PluginBundle loadPlugins(PluginBundleVersionIdentifier pluginBundleVersionIdentifier, ResourceLoader resourceLoader, ClassLoader classLoader, URI location, String classLocation, PluginDescriptor pluginDescriptor,
			PluginSourceType pluginType, Set dependencies, SPluginBundle sPluginBundle, SPluginBundleVersion sPluginBundleVersion) throws PluginException {
		sPluginBundle.setInstalledVersion(sPluginBundleVersion);
		PluginBundle pluginBundle = new PluginBundleImpl(pluginBundleVersionIdentifier, sPluginBundle, sPluginBundleVersion, pluginDescriptor);

		if (classLoader != null && classLoader instanceof Closeable) {
			pluginBundle.addCloseable((Closeable) classLoader);
		}

		for (AbstractPlugin pluginImplementation : pluginDescriptor.getPlugins()) {
			if (pluginImplementation instanceof JavaPlugin) {
				JavaPlugin javaPlugin = (JavaPlugin) pluginImplementation;
				String interfaceClassName = javaPlugin.getInterfaceClass().trim().replace("\n", "");
				try {
					Class interfaceClass = getClass().getClassLoader().loadClass(interfaceClassName);
					if (javaPlugin.getImplementationClass() != null) {
						String implementationClassName = javaPlugin.getImplementationClass().trim().replace("\n", "");
						try {
							Class implementationClass = classLoader.loadClass(implementationClassName);
							Plugin plugin = (Plugin) implementationClass.newInstance();
							pluginBundle.add(loadPlugin(pluginBundle, interfaceClass, location, classLocation, plugin, classLoader, pluginType, pluginImplementation, dependencies, plugin.getClass().getName()));
						} catch (NoClassDefFoundError e) {
							throw new PluginException("Implementation class '" + implementationClassName + "' not found", e);
						} catch (ClassNotFoundException e) {
							throw new PluginException("Implementation class '" + e.getMessage() + "' not found in " + location, e);
						} catch (InstantiationException e) {
							throw new PluginException(e);
						} catch (IllegalAccessException e) {
							throw new PluginException(e);
						}
					}
				} catch (ClassNotFoundException e) {
					throw new PluginException("Interface class '" + interfaceClassName + "' not found", e);
				} catch (Error e) {
					throw new PluginException(e);
				}
			} else if (pluginImplementation instanceof org.bimserver.plugins.WebModulePlugin) {
				org.bimserver.plugins.WebModulePlugin webModulePlugin = (org.bimserver.plugins.WebModulePlugin) pluginImplementation;
				JsonWebModule jsonWebModule = new JsonWebModule(webModulePlugin);
				pluginBundle.add(loadPlugin(pluginBundle, WebModulePlugin.class, location, classLocation, jsonWebModule, classLoader, pluginType, pluginImplementation, dependencies, webModulePlugin.getIdentifier()));
			}
		}
		pluginBundleIdentifierToPluginBundle.put(pluginBundleVersionIdentifier.getPluginBundleIdentifier(), pluginBundle);
		pluginBundleVersionIdentifierToPluginBundle.put(pluginBundleVersionIdentifier, pluginBundle);
		pluginBundleIdentifierToCurrentPluginBundleVersionIdentifier.put(pluginBundleVersionIdentifier.getPluginBundleIdentifier(), pluginBundleVersionIdentifier);

		return pluginBundle;
	}

	public PluginDescriptor getPluginDescriptor(InputStream inputStream) throws JAXBException, IOException {
		try {
			PluginDescriptor pluginDescriptor = (PluginDescriptor) PLUGIN_DESCRIPTOR_UNMARSHALLER.unmarshal(inputStream);
			return pluginDescriptor;
		} finally {
			inputStream.close();
		}
	}

	public PluginDescriptor getPluginDescriptor(byte[] bytes) throws JAXBException, IOException {
		return getPluginDescriptor(new ByteArrayInputStream(bytes));
	}

	// public void loadAllPluginsFromDirectoryOfJars(Path directory) throws
	// PluginException, IOException {
	// LOGGER.debug("Loading all plugins from " + directory.toString());
	// if (!Files.isDirectory(directory)) {
	// throw new PluginException("No directory: " + directory.toString());
	// }
	// for (Path file : PathUtils.list(directory)) {
	// if (file.getFileName().toString().toLowerCase().endsWith(".jar")) {
	// try {
	// PluginBundleVersionIdentifier pluginBundleVersionIdentifier =
	// PluginBundleVersionIdentifier.fromFileName(file.getFileName().toString());
	//
	// loadPluginsFromJar(pluginBundleVersionIdentifier, file,
	// extractPluginBundleFromJar(file),
	// extractPluginBundleVersionFromJar(file));
	// } catch (PluginException e) {
	// LOGGER.error("", e);
	// }
	// }
	// }
	// }

	public SPluginBundle extractPluginBundleFromJar(Path jarFilePath) throws PluginException {
		String filename = jarFilePath.getFileName().toString();
		PluginBundleVersionIdentifier pluginBundleVersionIdentifier = PluginBundleVersionIdentifier.fromFileName(filename);
		try (JarFile jarFile = new JarFile(jarFilePath.toFile())) {
			String pomLocation = "META-INF/maven/" + pluginBundleVersionIdentifier.getPluginBundleIdentifier().getGroupId() + "/" + pluginBundleVersionIdentifier.getPluginBundleIdentifier().getArtifactId() + "/" + "pom.xml";
			ZipEntry pomEntry = jarFile.getEntry(pomLocation);
			if (pomEntry == null) {
				throw new PluginException("No pom.xml found in JAR file " + jarFilePath.toString() + ", " + pomLocation);
			}
			MavenXpp3Reader mavenreader = new MavenXpp3Reader();

			Model model = mavenreader.read(jarFile.getInputStream(pomEntry));
			SPluginBundle sPluginBundle = new SPluginBundle();
			sPluginBundle.setOrganization(model.getOrganization().getName());
			sPluginBundle.setName(model.getName());
			return sPluginBundle;
		} catch (IOException e) {
			throw new PluginException(e);
		} catch (XmlPullParserException e) {
			throw new PluginException(e);
		}
	}

	public SPluginBundleVersion extractPluginBundleVersionFromJar(Path jarFilePath, boolean isLocal) throws PluginException {
		String filename = jarFilePath.getFileName().toString();
		PluginBundleVersionIdentifier pluginBundleVersionIdentifier = PluginBundleVersionIdentifier.fromFileName(filename);
		PluginBundleIdentifier pluginBundleIdentifier = pluginBundleVersionIdentifier.getPluginBundleIdentifier();
		try (JarFile jarFile = new JarFile(jarFilePath.toFile())) {
			ZipEntry pomEntry = jarFile.getEntry("META-INF/maven/" + pluginBundleIdentifier.getGroupId() + "/" + pluginBundleIdentifier.getArtifactId() + "/" + "pom.xml");
			if (pomEntry == null) {
				throw new PluginException("No pom.xml found in JAR file " + jarFilePath.toString());
			}
			MavenXpp3Reader mavenreader = new MavenXpp3Reader();

			Model model = mavenreader.read(jarFile.getInputStream(pomEntry));
			SPluginBundleVersion sPluginBundleVersion = createPluginBundleVersionFromMavenModel(model, isLocal);
			return sPluginBundleVersion;
		} catch (IOException e) {
			throw new PluginException(e);
		} catch (XmlPullParserException e) {
			throw new PluginException(e);
		}
	}

	private SPluginBundleVersion createPluginBundleVersionFromMavenModel(Model model, boolean isLocalDev) {
		SPluginBundleVersion sPluginBundleVersion = new SPluginBundleVersion();
		sPluginBundleVersion.setType(isLocalDev ? SPluginBundleType.LOCAL_DEV : SPluginBundleType.MAVEN);
		sPluginBundleVersion.setGroupId(model.getGroupId());
		sPluginBundleVersion.setArtifactId(model.getArtifactId());
		sPluginBundleVersion.setVersion(model.getVersion());
		sPluginBundleVersion.setDescription(model.getDescription());
		sPluginBundleVersion.setRepository("local");
		sPluginBundleVersion.setMismatch(false); // TODO
		sPluginBundleVersion.setOrganization(model.getOrganization().getName());
		sPluginBundleVersion.setName(model.getName());
		return sPluginBundleVersion;
	}

	public PluginBundle loadPluginsFromJar(PluginBundleVersionIdentifier pluginBundleVersionIdentifier, Path file, SPluginBundle sPluginBundle, SPluginBundleVersion pluginBundleVersion, ClassLoader parentClassLoader)
			throws PluginException {
		PluginBundleIdentifier pluginBundleIdentifier = pluginBundleVersionIdentifier.getPluginBundleIdentifier();
		if (pluginBundleIdentifierToPluginBundle.containsKey(pluginBundleIdentifier)) {
			throw new PluginException("Plugin " + pluginBundleIdentifier.getHumanReadable() + " already loaded (version " + pluginBundleIdentifierToPluginBundle.get(pluginBundleIdentifier).getPluginBundleVersion().getVersion() + ")");
		}
		LOGGER.debug("Loading plugins from " + file.toString());
		if (!Files.exists(file)) {
			throw new PluginException("Not a file: " + file.toString());
		}
		FileJarClassLoader jarClassLoader = null;
		try {
			jarClassLoader = new FileJarClassLoader(this, parentClassLoader, file);
			jarClassLoaders.add(jarClassLoader);
			final JarClassLoader finalLoader = jarClassLoader;
			InputStream pluginStream = jarClassLoader.getResourceAsStream("plugin/plugin.xml");
			if (pluginStream == null) {
				jarClassLoader.close();
				throw new PluginException("No plugin/plugin.xml found in " + file.getFileName().toString());
			}
			PluginDescriptor pluginDescriptor = getPluginDescriptor(pluginStream);
			if (pluginDescriptor == null) {
				jarClassLoader.close();
				throw new PluginException("No plugin descriptor could be created");
			}
			LOGGER.debug(pluginDescriptor.toString());

			URI fileUri = file.toAbsolutePath().toUri();
			URI jarUri = new URI("jar:" + fileUri.toString());

			ResourceLoader resourceLoader = new ResourceLoader() {
				@Override
				public InputStream load(String name) {
					return finalLoader.getResourceAsStream(name);
				}
			};

			return loadPlugins(pluginBundleVersionIdentifier, resourceLoader, jarClassLoader, jarUri, file.toAbsolutePath().toString(), pluginDescriptor, PluginSourceType.JAR_FILE, new HashSet(),
					sPluginBundle, pluginBundleVersion);
		} catch (Exception e) {
			if (jarClassLoader != null) {
				try {
					jarClassLoader.close();
				} catch (IOException e1) {
					LOGGER.error("", e1);
				}
			}
			throw new PluginException(e);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private  Map getPlugins(Class requiredInterfaceClass, boolean onlyEnabled) {
		Map plugins = new HashMap<>();
		for (Class interfaceClass : implementations.keySet()) {
			if (requiredInterfaceClass.isAssignableFrom(interfaceClass)) {
				for (PluginContext pluginContext : implementations.get(interfaceClass)) {
					if (!onlyEnabled || pluginContext.isEnabled()) {
						plugins.put(pluginContext, (T) pluginContext.getPlugin());
					}
				}
			}
		}
		return plugins;
	}

	public Map getAllObjectIDMPlugins(boolean onlyEnabled) {
		return getPlugins(ObjectIDMPlugin.class, onlyEnabled);
	}

	public Map getAllRenderEnginePlugins(boolean onlyEnabled) {
		return getPlugins(RenderEnginePlugin.class, onlyEnabled);
	}

	public Map getAllStillImageRenderPlugins(boolean onlyEnabled) {
		return getPlugins(StillImageRenderPlugin.class, onlyEnabled);
	}

	public Map getAllQueryEnginePlugins(boolean onlyEnabled) {
		return getPlugins(QueryEnginePlugin.class, onlyEnabled);
	}

	public Map getAllSerializerPlugins(boolean onlyEnabled) {
		return getPlugins(SerializerPlugin.class, onlyEnabled);
	}

	public Map getAllMessagingSerializerPlugins(boolean onlyEnabled) {
		return getPlugins(MessagingSerializerPlugin.class, onlyEnabled);
	}

	public Map getAllMessagingStreamingSerializerPlugins(boolean onlyEnabled) {
		return getPlugins(MessagingStreamingSerializerPlugin.class, onlyEnabled);
	}

	public Map getAllDeserializerPlugins(boolean onlyEnabled) {
		return getPlugins(DeserializerPlugin.class, onlyEnabled);
	}

	public Map getAllStreamingDeserializerPlugins(boolean onlyEnabled) {
		return getPlugins(StreamingDeserializerPlugin.class, onlyEnabled);
	}

	public Map getAllStreamingSeserializerPlugins(boolean onlyEnabled) {
		return getPlugins(StreamingSerializerPlugin.class, onlyEnabled);
	}

	public Map getAllPlugins(boolean onlyEnabled) {
		return getPlugins(Plugin.class, onlyEnabled);
	}

	public PluginContext getPluginContext(Plugin plugin) {
		PluginContext pluginContext = pluginToPluginContext.get(plugin);
		if (pluginContext == null) {
			throw new RuntimeException("No plugin context found for " + plugin);
		}
		return pluginContext;
	}

	public void enablePlugin(String name) {
		for (Set pluginContexts : implementations.values()) {
			for (PluginContext pluginContext : pluginContexts) {
				if (pluginContext.getPlugin().getClass().getName().equals(name)) {
					pluginContext.setEnabled(true, true);
				}
			}
		}
	}

	public void disablePlugin(String name) {
		for (Set pluginContexts : implementations.values()) {
			for (PluginContext pluginContext : pluginContexts) {
				if (pluginContext.getPlugin().getClass().getName().equals(name)) {
					pluginContext.setEnabled(false, true);
				}
			}
		}
	}

	public Plugin getPlugin(String identifier, boolean onlyEnabled) {
		for (Set pluginContexts : implementations.values()) {
			for (PluginContext pluginContext : pluginContexts) {
				if (pluginContext.getIdentifier().equals(identifier)) {
					if (!onlyEnabled || pluginContext.isEnabled()) {
						return pluginContext.getPlugin();
					}
				}
			}
		}
		return null;
	}

	public boolean isEnabled(String className) {
		return getPlugin(className, true) != null;
	}

	public void setPluginChangeListener(PluginChangeListener pluginChangeListener) {
		this.pluginChangeListener = pluginChangeListener;
	}

	public Collection getAllDeserializerPlugins(String extension, boolean onlyEnabled) {
		Collection allDeserializerPlugins = getAllDeserializerPlugins(onlyEnabled).values();
		Iterator iterator = allDeserializerPlugins.iterator();
		while (iterator.hasNext()) {
			DeserializerPlugin deserializerPlugin = iterator.next();
			if (!deserializerPlugin.canHandleExtension(extension)) {
				iterator.remove();
			}
		}
		return allDeserializerPlugins;
	}

	public DeserializerPlugin requireDeserializer(String extension) throws DeserializeException {
		Collection allDeserializerPlugins = getAllDeserializerPlugins(extension, true);
		if (allDeserializerPlugins.size() == 0) {
			throw new DeserializeException("No deserializers found for type '" + extension + "'");
		} else {
			return allDeserializerPlugins.iterator().next();
		}
	}

	public Path getTempDir() {
		if (!Files.isDirectory(tempDir)) {
			try {
				Files.createDirectories(tempDir);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return tempDir;
	}

	public PluginContext loadPlugin(PluginBundle pluginBundle, Class interfaceClass, URI location, String classLocation, Plugin plugin, ClassLoader classLoader, PluginSourceType pluginType,
			AbstractPlugin pluginImplementation, Set dependencies, String identifier) throws PluginException {
		LOGGER.debug("Loading plugin " + plugin.getClass().getSimpleName() + " of type " + interfaceClass.getSimpleName());
		if (!Plugin.class.isAssignableFrom(interfaceClass)) {
			throw new PluginException("Given interface class (" + interfaceClass.getName() + ") must be a subclass of " + Plugin.class.getName());
		}
		if (!implementations.containsKey(interfaceClass)) {
			implementations.put(interfaceClass, new LinkedHashSet());
		}
		Set set = (Set) implementations.get(interfaceClass);
		try {
			PluginContext pluginContext = new PluginContext(this, pluginBundle, interfaceClass, classLoader, pluginType, pluginImplementation.getDescription(), location, plugin, classLocation, dependencies, identifier);
			pluginToPluginContext.put(plugin, pluginContext);
			set.add(pluginContext);
			return pluginContext;
		} catch (IOException e) {
			throw new PluginException(e);
		}
	}

	/**
	 * This method will initialize all the loaded plugins
	 * 
	 * @throws PluginException
	 */
	public void initAllLoadedPlugins() throws PluginException {
		LOGGER.debug("Initializig all loaded plugins");
		for (Class pluginClass : implementations.keySet()) {
			Set set = implementations.get(pluginClass);
			for (PluginContext pluginContext : set) {
				try {
					pluginContext.initialize();
				} catch (Throwable e) {
					LOGGER.error("", e);
					pluginContext.setEnabled(false, false);
				}
			}
		}
	}

	/*
	 * Returns a complete classpath for all loaded plugins
	 */
	public String getCompleteClassPath() {
		StringBuilder sb = new StringBuilder();
		if (baseClassPath != null) {
			sb.append(baseClassPath + File.pathSeparator);
		}
		for (Class pluginClass : implementations.keySet()) {
			Set set = implementations.get(pluginClass);
			for (PluginContext pluginContext : set) {
				sb.append(pluginContext.getClassLocation() + File.pathSeparator);
			}
		}
		return sb.toString();
	}

	public DeserializerPlugin getFirstDeserializer(String extension, Schema schema, boolean onlyEnabled) throws PluginException {
		Collection allDeserializerPlugins = getAllDeserializerPlugins(extension, onlyEnabled);
		Iterator iterator = allDeserializerPlugins.iterator();
		while (iterator.hasNext()) {
			DeserializerPlugin next = iterator.next();
			if (!next.getSupportedSchemas().contains(schema)) {
				iterator.remove();
			}
		}
		if (allDeserializerPlugins.size() == 0) {
			throw new PluginException("No deserializers with extension " + extension + " found");
		}
		return allDeserializerPlugins.iterator().next();
	}

	public StreamingDeserializerPlugin getFirstStreamingDeserializer(String extension, Schema schema, boolean onlyEnabled) throws PluginException {
		Map allDeserializerPlugins = getAllStreamingDeserializerPlugins(onlyEnabled);
		Iterator iterator = allDeserializerPlugins.values().iterator();
		while (iterator.hasNext()) {
			StreamingDeserializerPlugin next = iterator.next();
			if (!next.getSupportedSchemas().contains(schema)) {
				iterator.remove();
			}
		}
		if (allDeserializerPlugins.size() == 0) {
			throw new PluginException("No deserializers with extension " + extension + " found");
		}
		return allDeserializerPlugins.values().iterator().next();
	}

	public ObjectIDMPlugin getObjectIDMByName(String className, boolean onlyEnabled) {
		return getPluginByClassName(ObjectIDMPlugin.class, className, onlyEnabled);
	}

	public RenderEnginePlugin getRenderEnginePlugin(String className, boolean onlyEnabled) {
		return getPluginByClassName(RenderEnginePlugin.class, className, onlyEnabled);
	}

	private  T getPluginByClassName(Class clazz, String className, boolean onlyEnabled) {
		Collection allPlugins = getPlugins(clazz, onlyEnabled).values();
		for (T t : allPlugins) {
			if (t.getClass().getName().equals(className)) {
				return t;
			}
		}
		return null;
	}

	public QueryEnginePlugin getQueryEngine(String className, boolean onlyEnabled) {
		return getPluginByClassName(QueryEnginePlugin.class, className, onlyEnabled);
	}

	public void loadAllPluginsFromEclipseWorkspace(Path file, boolean showExceptions) throws PluginException, IOException {
		if (file != null && Files.isDirectory(file)) {
			for (Path project : PathUtils.list(file)) {
				if (Files.isDirectory(project)) {
					Path pluginDir = project.resolve("plugin");
					if (Files.exists(pluginDir)) {
						Path pluginFile = pluginDir.resolve("plugin.xml");
						if (Files.exists(pluginFile)) {
							if (showExceptions) {
								loadPluginsFromEclipseProject(project);
							} else {
								loadPluginsFromEclipseProjectNoExceptions(project);
							}
						}
					}
				}
			}
		}
	}

	public void loadAllPluginsFromEclipseWorkspaces(Path directory, boolean showExceptions) throws PluginException, IOException {
		if (!Files.isDirectory(directory)) {
			return;
		}
		if (Files.exists(directory.resolve("plugin/plugin.xml"))) {
			if (showExceptions) {
				loadPluginsFromEclipseProject(directory);
			} else {
				loadPluginsFromEclipseProjectNoExceptions(directory);
			}
		}
		loadAllPluginsFromEclipseWorkspace(directory, showExceptions);
		for (Path workspace : PathUtils.list(directory)) {
			if (Files.isDirectory(workspace)) {
				loadAllPluginsFromEclipseWorkspace(workspace, showExceptions);
			}
		}
	}

	public Map getAllModelMergerPlugins(boolean onlyEnabled) {
		return getPlugins(ModelMergerPlugin.class, onlyEnabled);
	}

	public Map getAllModelComparePlugins(boolean onlyEnabled) {
		return getPlugins(ModelComparePlugin.class, onlyEnabled);
	}

	public ModelMergerPlugin getModelMergerPlugin(String className, boolean onlyEnabled) {
		return getPluginByClassName(ModelMergerPlugin.class, className, onlyEnabled);
	}

	public ModelComparePlugin getModelComparePlugin(String className, boolean onlyEnabled) {
		return getPluginByClassName(ModelComparePlugin.class, className, onlyEnabled);
	}

	public Map getAllServicePlugins(boolean onlyEnabled) {
		return getPlugins(ServicePlugin.class, onlyEnabled);
	}

	public ServicePlugin getServicePlugin(String className, boolean onlyEnabled) {
		return getPluginByClassName(ServicePlugin.class, className, onlyEnabled);
	}

	public ServiceFactory getServiceFactory() {
		return serviceFactory;
	}

	public void registerNewRevisionHandler(long uoid, ServiceDescriptor serviceDescriptor, NewRevisionHandler newRevisionHandler) {
		if (notificationsManagerInterface != null) {
			notificationsManagerInterface.registerInternalNewRevisionHandler(uoid, serviceDescriptor, newRevisionHandler);
		}
	}

	public void unregisterNewRevisionHandler(long uoid, ServiceDescriptor serviceDescriptor) {
		if (notificationsManagerInterface != null) {
			notificationsManagerInterface.unregisterInternalNewRevisionHandler(uoid, serviceDescriptor.getIdentifier());
		}
	}

	public SServicesMap getServicesMap() {
		return servicesMap;
	}

	// public StillImageRenderPlugin getFirstStillImageRenderPlugin() throws
	// PluginException {
	// Collection allPlugins =
	// getAllStillImageRenderPlugins(true).values();
	// if (allPlugins.size() == 0) {
	// throw new PluginException("No still image render plugins found");
	// }
	// StillImageRenderPlugin plugin = allPlugins.iterator().next();
	// if (!plugin.isInitialized()) {
	// plugin.init(this);
	// }
	// return plugin;
	//
	// }

	public Parameter getParameter(PluginContext pluginContext, String name) {
		return null;
	}

	public SerializerPlugin getSerializerPlugin(String className, boolean onlyEnabled) {
		return (SerializerPlugin) getPlugin(className, onlyEnabled);
	}

	public MessagingSerializerPlugin getMessagingSerializerPlugin(String className, boolean onlyEnabled) {
		return (MessagingSerializerPlugin) getPlugin(className, onlyEnabled);
	}

	public WebModulePlugin getWebModulePlugin(String className, boolean onlyEnabled) {
		return (WebModulePlugin) getPlugin(className, onlyEnabled);
	}

	public Map getAllWebPlugins(boolean onlyEnabled) {
		return getPlugins(WebModulePlugin.class, onlyEnabled);
	}

	public Map getAllModelCheckerPlugins(boolean onlyEnabled) {
		return getPlugins(ModelCheckerPlugin.class, onlyEnabled);
	}

	public ModelCheckerPlugin getModelCheckerPlugin(String className, boolean onlyEnabled) {
		return getPluginByClassName(ModelCheckerPlugin.class, className, onlyEnabled);
	}

	public BimServerClientInterface getLocalBimServerClientInterface(AuthenticationInfo tokenAuthentication) throws ServiceException, ChannelConnectionException {
		return bimServerClientFactory.create(tokenAuthentication);
	}

	public void setBimServerClientFactory(BimServerClientFactory bimServerClientFactory) {
		this.bimServerClientFactory = bimServerClientFactory;
	}

	public void registerNewExtendedDataOnProjectHandler(long uoid, ServiceDescriptor serviceDescriptor, NewExtendedDataOnProjectHandler newExtendedDataHandler) {
		if (notificationsManagerInterface != null) {
			notificationsManagerInterface.registerInternalNewExtendedDataOnProjectHandler(uoid, serviceDescriptor, newExtendedDataHandler);
		}
	}

	public void registerNewExtendedDataOnRevisionHandler(long uoid, ServiceDescriptor serviceDescriptor, NewExtendedDataOnRevisionHandler newExtendedDataHandler) {
		if (notificationsManagerInterface != null) {
			notificationsManagerInterface.registerInternalNewExtendedDataOnRevisionHandler(uoid, serviceDescriptor, newExtendedDataHandler);
		}
	}

	public DeserializerPlugin getDeserializerPlugin(String pluginClassName, boolean onlyEnabled) {
		return getPluginByClassName(DeserializerPlugin.class, pluginClassName, onlyEnabled);
	}

	public StreamingDeserializerPlugin getStreamingDeserializerPlugin(String pluginClassName, boolean onlyEnabled) {
		return getPluginByClassName(StreamingDeserializerPlugin.class, pluginClassName, onlyEnabled);
	}

	public StreamingSerializerPlugin getStreamingSerializerPlugin(String pluginClassName, boolean onlyEnabled) {
		return getPluginByClassName(StreamingSerializerPlugin.class, pluginClassName, onlyEnabled);
	}

	public MetaDataManager getMetaDataManager() {
		return metaDataManager;
	}

	public void setMetaDataManager(MetaDataManager metaDataManager) {
		this.metaDataManager = metaDataManager;
	}

	public FileSystem getOrCreateFileSystem(URI uri) throws IOException {
		FileSystem fileSystem = null;
		try {
			fileSystem = FileSystems.getFileSystem(uri);
		} catch (FileSystemNotFoundException e) {
			Map env = new HashMap<>();
			env.put("create", "true");
			fileSystem = FileSystems.newFileSystem(uri, env, null);
			LOGGER.debug("Created VFS for " + uri);
		}
		return fileSystem;
	}

	public MessagingStreamingSerializerPlugin getMessagingStreamingSerializerPlugin(String className, boolean onlyEnabled) {
		return (MessagingStreamingSerializerPlugin) getPlugin(className, onlyEnabled);
	}

	public List getPluginInformationFromJar(Path file) throws PluginException, FileNotFoundException, IOException, JAXBException {
		try (JarFile jarFile = new JarFile(file.toFile())) {
			ZipEntry entry = jarFile.getEntry("plugin/plugin.xml");
			if (entry == null) {
				throw new PluginException("No plugin/plugin.xml found in " + file.getFileName().toString());
			}
			InputStream pluginStream = jarFile.getInputStream(entry);
			return getPluginInformationFromPluginFile(pluginStream);
		}
	}

	public List getPluginInformationFromJar(InputStream jarInputStream) throws PluginException, FileNotFoundException, IOException, JAXBException {
		try (JarInputStream jarInputStream2 = new JarInputStream(jarInputStream)) {
			JarEntry next = jarInputStream2.getNextJarEntry();
			while (next != null) {
				if (next.getName().equals("plugin/plugin.xml")) {
					return getPluginInformationFromPluginFile(jarInputStream2);
				}
				next = jarInputStream2.getNextJarEntry();
			}
		}
		return null;
	}

	public List getPluginInformationFromPluginFile(InputStream inputStream) throws PluginException, FileNotFoundException, IOException, JAXBException {
		PluginDescriptor pluginDescriptor = getPluginDescriptor(inputStream);
		if (pluginDescriptor == null) {
			throw new PluginException("No plugin descriptor could be created");
		}
		List list = new ArrayList<>();
		processPluginDescriptor(pluginDescriptor, list);
		return list;
	}

	private void processPluginDescriptor(PluginDescriptor pluginDescriptor, List list) {
		for (AbstractPlugin pluginImplementation : pluginDescriptor.getPlugins()) {
			if (pluginImplementation instanceof JavaPlugin) {
				JavaPlugin javaPlugin = (JavaPlugin) pluginImplementation;
				SPluginInformation sPluginInformation = new SPluginInformation();
				String name = javaPlugin.getName();
				// TODO when all plugins have a name, this code can go
				if (name == null) {
					name = javaPlugin.getImplementationClass();
				}
				sPluginInformation.setName(name);
				sPluginInformation.setDescription(javaPlugin.getDescription());
				sPluginInformation.setEnabled(true);

				// For java plugins we use the implementation class as
				// identifier
				sPluginInformation.setIdentifier(javaPlugin.getImplementationClass());

				sPluginInformation.setType(getPluginTypeFromClass(javaPlugin.getInterfaceClass()));
				list.add(sPluginInformation);
			} else if (pluginImplementation instanceof org.bimserver.plugins.WebModulePlugin) {
				org.bimserver.plugins.WebModulePlugin webModulePlugin = (org.bimserver.plugins.WebModulePlugin) pluginImplementation;
				SPluginInformation sPluginInformation = new SPluginInformation();
				sPluginInformation.setIdentifier(webModulePlugin.getIdentifier());
				sPluginInformation.setName(webModulePlugin.getName());
				sPluginInformation.setDescription(webModulePlugin.getDescription());
				sPluginInformation.setType(SPluginType.WEB_MODULE);
				sPluginInformation.setEnabled(true);
				list.add(sPluginInformation);
			}
		}
	}

	public List getPluginInformationFromPluginFile(Path file) throws PluginException, FileNotFoundException, IOException, JAXBException {
		List list = new ArrayList<>();
		try (InputStream pluginStream = Files.newInputStream(file)) {
			PluginDescriptor pluginDescriptor = getPluginDescriptor(pluginStream);
			if (pluginDescriptor == null) {
				throw new PluginException("No plugin descriptor could be created");
			}
			processPluginDescriptor(pluginDescriptor, list);
		}
		return list;
	}

	public SPluginType getPluginTypeFromClass(String className) {
		switch (className) {
		case "org.bimserver.plugins.deserializers.DeserializerPlugin":
			return SPluginType.DESERIALIZER;
		case "org.bimserver.plugins.deserializers.StreamingDeserializerPlugin":
			return SPluginType.DESERIALIZER;
		case "org.bimserver.plugins.serializers.SerializerPlugin":
			return SPluginType.SERIALIZER;
		case "org.bimserver.plugins.serializers.StreamingSerializerPlugin":
			return SPluginType.SERIALIZER;
		case "org.bimserver.plugins.serializers.MessagingStreamingSerializerPlugin":
			return SPluginType.SERIALIZER;
		case "org.bimserver.plugins.serializers.MessagingSerializerPlugin":
			return SPluginType.SERIALIZER;
		case "org.bimserver.plugins.modelchecker.ModelCheckerPlugin":
			return SPluginType.MODEL_CHECKER;
		case "org.bimserver.plugins.modelmerger.ModelMergerPlugin":
			return SPluginType.MODEL_MERGER;
		case "org.bimserver.plugins.modelcompare.ModelComparePlugin":
			return SPluginType.MODEL_COMPARE;
		case "org.bimserver.plugins.objectidms.ObjectIDMPlugin":
			return SPluginType.OBJECT_IDM;
		case "org.bimserver.plugins.queryengine.QueryEnginePlugin":
			return SPluginType.QUERY_ENGINE;
		case "org.bimserver.plugins.services.ServicePlugin":
			return SPluginType.SERVICE;
		case "org.bimserver.plugins.renderengine.RenderEnginePlugin":
			return SPluginType.RENDER_ENGINE;
		case "org.bimserver.plugins.stillimagerenderer.StillImageRenderPlugin":
			return SPluginType.STILL_IMAGE_RENDER;
		case "org.bimserver.plugins.web.WebModulePlugin":
			return SPluginType.WEB_MODULE;
		}
		return null;
	}

	public PluginBundle loadFromPluginDir(PluginBundleVersionIdentifier pluginBundleVersionIdentifier, SPluginBundleVersion pluginBundleVersion, List plugins, boolean strictDependencyChecking) throws Exception {
		Path target = pluginsDir.resolve(pluginBundleVersionIdentifier.getFileName());
		if (!Files.exists(target)) {
			throw new PluginException(target.toString() + " not found");
		}

		SPluginBundle sPluginBundle = new SPluginBundle();

		MavenXpp3Reader mavenreader = new MavenXpp3Reader();

		try (JarFile jarFile = new JarFile(target.toFile())) {
			ZipEntry entry = jarFile.getEntry("META-INF/maven/" + pluginBundleVersion.getGroupId() + "/" + pluginBundleVersion.getArtifactId() + "/pom.xml");
			Model model = mavenreader.read(jarFile.getInputStream(entry));

			sPluginBundle.setOrganization(model.getOrganization().getName());
			sPluginBundle.setName(model.getName());

			DelegatingClassLoader delegatingClassLoader = new DelegatingClassLoader(getClass().getClassLoader());

			for (org.apache.maven.model.Dependency dependency : model.getDependencies()) {
				if (dependency.getGroupId().equals("org.opensourcebim") && (dependency.getArtifactId().equals("shared") || dependency.getArtifactId().equals("pluginbase"))) {
					// TODO Skip, we should also check the version though
				} else {
					PluginBundleIdentifier pluginBundleIdentifier = new PluginBundleIdentifier(dependency.getGroupId(), dependency.getArtifactId());
					if (pluginBundleIdentifierToPluginBundle.containsKey(pluginBundleIdentifier)) {
						if (strictDependencyChecking) {
							VersionRange versionRange = VersionRange.createFromVersion(dependency.getVersion());
							String version = pluginBundleIdentifierToPluginBundle.get(pluginBundleIdentifier).getPluginBundleVersion().getVersion();
							ArtifactVersion artifactVersion = new DefaultArtifactVersion(version);
							if (versionRange.containsVersion(artifactVersion)) {
								// OK
							} else {
								throw new Exception("Required dependency " + pluginBundleIdentifier + " is installed, but it's version (" + version + ") does not comply to the required version (" + dependency.getVersion() + ")");
							}
						} else {
							LOGGER.info("Skipping strict dependency checking for dependency " + dependency.getArtifactId());
						}
					} else {
						if (dependency.getGroupId().equals("org.opensourcebim") && (dependency.getArtifactId().equals("shared") || dependency.getArtifactId().equals("pluginbase"))) {
						} else {
							MavenPluginLocation mavenPluginLocation = mavenPluginRepository.getPluginLocation(dependency.getGroupId(), dependency.getArtifactId());

							try {
								Path depJarFile = mavenPluginLocation.getVersionJar(dependency.getVersion());

								FileJarClassLoader jarClassLoader = new FileJarClassLoader(this, delegatingClassLoader, depJarFile);
								jarClassLoaders.add(jarClassLoader);
								delegatingClassLoader.add(jarClassLoader);
							} catch (Exception e) {

							}
						}
					}
				}
			}

			return loadPlugin(pluginBundleVersionIdentifier, target, sPluginBundle, pluginBundleVersion, plugins, delegatingClassLoader);
		}
	}

	public PluginBundle loadPlugin(PluginBundleVersionIdentifier pluginBundleVersionIdentifier, Path target, SPluginBundle sPluginBundle, SPluginBundleVersion pluginBundleVersion, List plugins,
			ClassLoader parentClassLoader) throws Exception {
		PluginBundle pluginBundle = null;
		// Stage 1, load all plugins from the JAR file and initialize them
		try {
			pluginBundle = loadPluginsFromJar(pluginBundleVersionIdentifier, target, sPluginBundle, pluginBundleVersion, parentClassLoader);
			if (plugins.isEmpty()) {
				LOGGER.warn("No plugins given to install for bundle " + sPluginBundle.getName());
			}
			for (SPluginInformation sPluginInformation : plugins) {
				if (sPluginInformation.isEnabled()) {
					PluginContext pluginContext = pluginBundle.getPluginContext(sPluginInformation.getIdentifier());
					if (pluginContext == null) {
						LOGGER.info("No plugin context found for " + sPluginInformation.getIdentifier());
					} else {
						pluginContext.getPlugin().init(pluginContext);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			if (pluginBundle != null) {
				pluginBundle.close();
			}
			pluginBundleVersionIdentifierToPluginBundle.remove(pluginBundleVersionIdentifier);
			pluginBundleIdentifierToPluginBundle.remove(pluginBundleVersionIdentifier.getPluginBundleIdentifier());
			Files.delete(target);
			LOGGER.error("", e);
			throw e;
		}
		// Stage 2, if all went well, notify the listeners of this plugin, if
		// anything goes wrong in the notifications, the plugin bundle will be
		// uninstalled
		try {
			long pluginBundleVersionId = pluginChangeListener.pluginBundleInstalled(pluginBundle);
			for (SPluginInformation sPluginInformation : plugins) {
				if (sPluginInformation.isEnabled()) {
					PluginContext pluginContext = pluginBundle.getPluginContext(sPluginInformation.getIdentifier());
					pluginChangeListener.pluginInstalled(pluginBundleVersionId, pluginContext, sPluginInformation);
				}
			}
			return pluginBundle;
		} catch (Exception e) {
			uninstall(pluginBundleVersionIdentifier);
			LOGGER.error("", e);
			throw e;
		}
	}

	public PluginBundle install(MavenPluginBundle mavenPluginBundle, boolean strictDependencyChecking) throws Exception {
		return install(mavenPluginBundle, null, strictDependencyChecking);
	}

	public PluginBundle install(MavenPluginBundle mavenPluginBundle, List plugins, boolean strictDependencyChecking) throws Exception {
		PluginBundleVersionIdentifier pluginBundleVersionIdentifier = mavenPluginBundle.getPluginVersionIdentifier();
		MavenXpp3Reader mavenreader = new MavenXpp3Reader();
		Model model = null;
		try (InputStream pomInputStream = mavenPluginBundle.getPomInputStream()) {
			model = mavenreader.read(pomInputStream);
		}

		if (plugins == null) {
			try (InputStream inputStream = mavenPluginBundle.getJarInputStream()) {
				try (JarInputStream jarInputStream = new JarInputStream(inputStream)) {
					JarEntry nextJarEntry = jarInputStream.getNextJarEntry();
					while (nextJarEntry != null) {
						if (nextJarEntry.getName().equals("plugin/plugin.xml")) {
							// Install all plugins
							PluginDescriptor pluginDescriptor = getPluginDescriptor(new FakeClosingInputStream(jarInputStream));
							plugins = new ArrayList<>();
							processPluginDescriptor(pluginDescriptor, plugins);
							for (SPluginInformation info : plugins) {
								info.setInstallForAllUsers(true);
								info.setInstallForNewUsers(true);
							}
							break;
						}
						nextJarEntry = jarInputStream.getNextJarEntry();
					}
				}
			}
		}

		DelegatingClassLoader delegatingClassLoader = new DelegatingClassLoader(getClass().getClassLoader());

		for (org.apache.maven.model.Dependency dependency : model.getDependencies()) {
			if (dependency.getGroupId().equals("org.opensourcebim") && (dependency.getArtifactId().equals("shared") || dependency.getArtifactId().equals("pluginbase"))) {
				// TODO Skip, we should also check the version though
			} else {
				PluginBundleIdentifier pluginBundleIdentifier = new PluginBundleIdentifier(dependency.getGroupId(), dependency.getArtifactId());
				if (pluginBundleIdentifierToPluginBundle.containsKey(pluginBundleIdentifier)) {
					if (strictDependencyChecking) {
						String version = dependency.getVersion();
						if (!version.contains("[") && !version.contains("(")) {
							version = "[" + version + "]";
						}
						VersionRange versionRange = VersionRange.createFromVersionSpec(version);
						// String version =
						// pluginBundleIdentifierToPluginBundle.get(pluginBundleIdentifier).getPluginBundleVersion().getVersion();
						ArtifactVersion artifactVersion = new DefaultArtifactVersion(mavenPluginBundle.getVersion());
						if (versionRange.containsVersion(artifactVersion)) {
							// OK
						} else {
							throw new Exception(
									"Required dependency " + pluginBundleIdentifier + " is installed, but it's version (" + mavenPluginBundle.getVersion() + ") does not comply to the required version (" + dependency.getVersion() + ")");
						}
					} else {
						LOGGER.info("Skipping strict dependency checking for dependency " + dependency.getArtifactId());
					}
				} else {
					try {
						MavenPluginLocation mavenPluginLocation = mavenPluginRepository.getPluginLocation(dependency.getGroupId(), dependency.getArtifactId());
						Path depJarFile = mavenPluginLocation.getVersionJar(dependency.getVersion());

						FileJarClassLoader jarClassLoader = new FileJarClassLoader(this, delegatingClassLoader, depJarFile);
						jarClassLoaders.add(jarClassLoader);
						delegatingClassLoader.add(jarClassLoader);
					} catch (Exception e) {
						throw new Exception("Required dependency " + pluginBundleIdentifier + " is not installed");
					}
				}
			}
		}

		Path target = pluginsDir.resolve(pluginBundleVersionIdentifier.getFileName());
		if (Files.exists(target)) {
			throw new PluginException("This plugin has already been installed " + target.getFileName().toString());
		}
		InputStream jarInputStream = mavenPluginBundle.getJarInputStream();
		try {
			Files.copy(jarInputStream, target);
		} finally {
			jarInputStream.close();
		}

		return loadPlugin(pluginBundleVersionIdentifier, target, mavenPluginBundle.getPluginBundle(), mavenPluginBundle.getPluginBundleVersion(), plugins, delegatingClassLoader);
	}

	// public PluginBundle install(PluginBundleVersionIdentifier
	// pluginBundleVersionIdentifier, SPluginBundle sPluginBundle,
	// SPluginBundleVersion pluginBundleVersion, List
	// plugins, boolean strictDependencyChecking) throws Exception {
	// MavenXpp3Reader mavenreader = new MavenXpp3Reader();
	// Model model = mavenreader.read(new FileReader(pomFile.toFile()));
	//
	// DelegatingClassLoader delegatingClassLoader = new
	// DelegatingClassLoader(getClass().getClassLoader());
	//
	// for (org.apache.maven.model.Dependency dependency :
	// model.getDependencies()) {
	// if (dependency.getGroupId().equals("org.opensourcebim") &&
	// (dependency.getArtifactId().equals("shared") ||
	// dependency.getArtifactId().equals("pluginbase"))) {
	// // TODO Skip, we should also check the version though
	// } else {
	// PluginBundleIdentifier pluginBundleIdentifier = new
	// PluginBundleIdentifier(dependency.getGroupId(),
	// dependency.getArtifactId());
	// if
	// (pluginBundleIdentifierToPluginBundle.containsKey(pluginBundleIdentifier))
	// {
	// if (strictDependencyChecking) {
	// VersionRange versionRange =
	// VersionRange.createFromVersion(dependency.getVersion());
	// String version =
	// pluginBundleIdentifierToPluginBundle.get(pluginBundleIdentifier).getPluginBundleVersion().getVersion();
	// ArtifactVersion artifactVersion = new DefaultArtifactVersion(version);
	// if (versionRange.containsVersion(artifactVersion)) {
	// // OK
	// } else {
	// throw new Exception("Required dependency " + pluginBundleIdentifier + "
	// is installed, but it's version (" + version + ") does not comply to the
	// required version (" + dependency.getVersion() + ")");
	// }
	// } else {
	// LOGGER.info("Skipping strict dependency checking for dependency " +
	// dependency.getArtifactId());
	// }
	// } else {
	// try {
	// MavenPluginLocation mavenPluginLocation =
	// mavenPluginRepository.getPluginLocation(dependency.getGroupId(),
	// dependency.getArtifactId());
	// Path depJarFile =
	// mavenPluginLocation.getVersionJar(dependency.getVersion());
	//
	// FileJarClassLoader jarClassLoader = new FileJarClassLoader(this,
	// delegatingClassLoader, depJarFile);
	// delegatingClassLoader.add(jarClassLoader);
	// } catch (Exception e) {
	// throw new Exception("Required dependency " + pluginBundleIdentifier + "
	// is not installed");
	// }
	// }
	// }
	// }
	//
	// Path target =
	// pluginsDir.resolve(pluginBundleVersionIdentifier.getFileName());
	// if (Files.exists(target)) {
	// throw new PluginException("This plugin has already been installed " +
	// target.getFileName().toString());
	// }
	// Files.copy(jarFile, target);
	//
	// return loadPlugin(pluginBundleVersionIdentifier, target, sPluginBundle,
	// pluginBundleVersion, plugins, delegatingClassLoader);
	// }

	public void uninstall(PluginBundleVersionIdentifier pluginBundleVersionIdentifier) {
		PluginBundle pluginBundle = pluginBundleVersionIdentifierToPluginBundle.get(pluginBundleVersionIdentifier);
		if (pluginBundle == null) {
			return;
		}
		try {
			pluginBundle.close();
			pluginBundleVersionIdentifierToPluginBundle.remove(pluginBundleVersionIdentifier);
			pluginBundleIdentifierToPluginBundle.remove(pluginBundleVersionIdentifier.getPluginBundleIdentifier());
			pluginBundleIdentifierToCurrentPluginBundleVersionIdentifier.remove(pluginBundleVersionIdentifier.getPluginBundleIdentifier());

			for (PluginContext pluginContext : pluginBundle) {
				Set set = implementations.get(pluginContext.getPluginInterface());
				set.remove(pluginContext);
			}

			Path target = pluginsDir.resolve(pluginBundleVersionIdentifier.getFileName());
			Files.delete(target);

			for (PluginContext pluginContext : pluginBundle) {
				pluginChangeListener.pluginUninstalled(pluginContext);
			}
			pluginChangeListener.pluginBundleUninstalled(pluginBundle);

		} catch (IOException e) {
			LOGGER.error("", e);
		}
	}

	public PluginBundle getPluginBundle(PluginBundleIdentifier pluginIdentifier) {
		return pluginBundleIdentifierToPluginBundle.get(pluginIdentifier);
	}

	public Collection getPluginBundles() {
		return pluginBundleVersionIdentifierToPluginBundle.values();
	}

	@Override
	public ObjectIDM getDefaultObjectIDM() throws ObjectIDMException {
		// TODO add a mechanism that can be used to ask a database what the
		// default plugin is

		return null;
	}

	@Override
	public void notifyPluginStateChange(PluginContext pluginContext, boolean enabled) {
		if (pluginChangeListener != null) {
			pluginChangeListener.pluginStateChanged(pluginContext, enabled);
		}
	}

	public PluginBundle update(PluginBundleVersionIdentifier pluginBundleVersionIdentifier, SPluginBundle sPluginBundle, SPluginBundleVersion pluginBundleVersion, Path jarFile, Path pomFile, List plugins)
			throws Exception {
		PluginBundle existingPluginBundle = pluginBundleIdentifierToPluginBundle.get(pluginBundleVersionIdentifier.getPluginBundleIdentifier());
		if (existingPluginBundle == null) {
			throw new UserException("No previous version of plugin bundle " + pluginBundleVersionIdentifier.getPluginBundleIdentifier() + " found");
		}
		try {
			existingPluginBundle.close();

			if (pluginBundleIdentifierToPluginBundle.remove(pluginBundleVersionIdentifier.getPluginBundleIdentifier()) == null) {
				LOGGER.warn("Previous version of " + pluginBundleVersionIdentifier.getPluginBundleIdentifier() + " not found");
			}
			PluginBundleVersionIdentifier currentVersion = pluginBundleIdentifierToCurrentPluginBundleVersionIdentifier.get(pluginBundleVersionIdentifier.getPluginBundleIdentifier());
			if (pluginBundleIdentifierToCurrentPluginBundleVersionIdentifier.remove(pluginBundleVersionIdentifier.getPluginBundleIdentifier()) == null) {
				LOGGER.warn("Previous version of " + pluginBundleVersionIdentifier.getPluginBundleIdentifier() + " not found");
			}
			if (pluginBundleVersionIdentifierToPluginBundle.remove(currentVersion) == null) {
				LOGGER.warn("Previous version (" + currentVersion + ") of " + pluginBundleVersionIdentifier.getPluginBundleIdentifier() + " not found");
			}

			for (PluginContext pluginContext : existingPluginBundle) {
				Set set = implementations.get(pluginContext.getPluginInterface());
				set.remove(pluginContext);
			}

			if (existingPluginBundle.getPluginBundle().getInstalledVersion().getType() == SPluginBundleType.MAVEN) {
				Path target = pluginsDir.resolve(currentVersion.getFileName());
				Files.delete(target);
			}

			// for (PluginContext pluginContext : existingPluginBundle) {
			// pluginChangeListener.pluginUninstalled(pluginContext);
			// }
		} catch (IOException e) {
			LOGGER.error("", e);
		}

		Path target = pluginsDir.resolve(pluginBundleVersionIdentifier.getFileName());
		if (Files.exists(target)) {
			throw new PluginException("This plugin has already been installed " + target.getFileName().toString());
		}
		Files.copy(jarFile, target);

		MavenXpp3Reader mavenreader = new MavenXpp3Reader();

		Model model = null;
		try (FileReader fileReader = new FileReader(pomFile.toFile())) {
			model = mavenreader.read(fileReader);
		}

		DelegatingClassLoader delegatingClassLoader = new DelegatingClassLoader(getClass().getClassLoader());

		for (org.apache.maven.model.Dependency dependency : model.getDependencies()) {
			if (dependency.getGroupId().equals("org.opensourcebim") && (dependency.getArtifactId().equals("shared") || dependency.getArtifactId().equals("pluginbase"))) {
				// TODO Skip, we should also check the version though
			} else {
				PluginBundleIdentifier pluginBundleIdentifier = new PluginBundleIdentifier(dependency.getGroupId(), dependency.getArtifactId());
				if (pluginBundleIdentifierToPluginBundle.containsKey(pluginBundleIdentifier)) {
					// if (false) {
					// VersionRange versionRange =
					// VersionRange.createFromVersion(dependency.getVersion());
					// String version =
					// pluginBundleIdentifierToPluginBundle.get(pluginBundleIdentifier).getPluginBundleVersion().getVersion();
					// ArtifactVersion artifactVersion = new
					// DefaultArtifactVersion(version);
					// if (versionRange.containsVersion(artifactVersion)) {
					// // OK
					// } else {
					// throw new Exception("Required dependency " +
					// pluginBundleIdentifier + " is installed, but it's version
					// (" + version + ") does not comply to the required version
					// (" + dependency.getVersion() + ")");
					// }
					// } else {
					LOGGER.info("Skipping strict dependency checking for dependency " + dependency.getArtifactId());
					// }
				} else {
					if (dependency.getGroupId().equals("org.opensourcebim") && (dependency.getArtifactId().equals("shared") || dependency.getArtifactId().equals("pluginbase"))) {
						throw new Exception("Required dependency " + pluginBundleIdentifier + " is not installed");
					} else {
						MavenPluginLocation mavenPluginLocation = mavenPluginRepository.getPluginLocation(model.getRepositories().get(0).getUrl(), dependency.getGroupId(), dependency.getArtifactId());

						try {
							Path depJarFile = mavenPluginLocation.getVersionJar(dependency.getVersion());

							FileJarClassLoader jarClassLoader = new FileJarClassLoader(this, delegatingClassLoader, depJarFile);
							jarClassLoaders.add(jarClassLoader);
							delegatingClassLoader.add(jarClassLoader);
						} catch (Exception e) {

						}
					}
				}
			}
		}

		PluginBundle pluginBundle = null;
		// Stage 1, load all plugins from the JAR file and initialize them
		try {
			pluginBundle = loadPluginsFromJar(pluginBundleVersionIdentifier, target, sPluginBundle, pluginBundleVersion, delegatingClassLoader);

			for (SPluginInformation sPluginInformation : plugins) {
				if (sPluginInformation.isEnabled()) {
					PluginContext pluginContext = pluginBundle.getPluginContext(sPluginInformation.getIdentifier());
					pluginContext.getPlugin().init(pluginContext);
				}
			}
		} catch (Exception e) {
			Files.delete(target);
			LOGGER.error("", e);
			throw e;
		}
		// Stage 2, if all went well, notify the listeners of this plugin, if
		// anything goes wrong in the notifications, the plugin bundle will be
		// uninstalled
		try {
			long pluginBundleVersionId = pluginChangeListener.pluginBundleUpdated(pluginBundle);

			for (SPluginInformation sPluginInformation : plugins) {
				if (sPluginInformation.isEnabled()) {
					PluginContext pluginContext = pluginBundle.getPluginContext(sPluginInformation.getIdentifier());
					pluginChangeListener.pluginUpdated(pluginBundleVersionId, pluginContext, sPluginInformation);
				}
			}
			return pluginBundle;
		} catch (Exception e) {
			uninstall(pluginBundleVersionIdentifier);
			LOGGER.error("", e);
			throw e;
		}
	}

	@Override
	public SerializerPlugin getSerializerPlugin(String pluginClassName) {
		return getPluginByClassName(SerializerPlugin.class, pluginClassName, true);
	}

	public void close() {
		for (FileJarClassLoader fileJarClassLoader : jarClassLoaders) {
			try {
				fileJarClassLoader.close();
			} catch (IOException e) {
				LOGGER.error("", e);
			}
		}
	}
	
	@Override
	public BasicServerInfo getBasicServerInfo() {
		return basicServerInfoProvider.getBasicServerInfo();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy