blob: cbdfbef31de70b6b5f4d160d9dd79c4a2a40a0c8 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.core.startup;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
import javax.swing.SwingUtilities;
import org.netbeans.Events;
import org.netbeans.InvalidException;
import org.netbeans.JaveleonModule;
import org.netbeans.Module;
import org.netbeans.ModuleManager;
import org.netbeans.Util;
import org.openide.modules.Dependency;
import org.openide.util.Exceptions;
/**
*
* @author Allan Gregersen
*/
class JaveleonModuleReloader {
private static JaveleonModuleReloader reloader = new JaveleonModuleReloader();
static JaveleonModuleReloader getDefault() {
return reloader;
}
/** This map ensures that the layer handling done by
* NBInstaller.loadLayer()is consistent with the modules
* registered with the currently installed layers.
*/
private HashMap<String, Module> registeredModules = new HashMap<String, Module>();
// Use JaveleonModuleReloader.getDefault() to get the singleton instance
private JaveleonModuleReloader() {
}
boolean reloadJaveleonModule(File jar, ModuleManager mgr, NbInstaller installer, Events ev) throws IOException {
if (!JaveleonModule.incrementGlobalId()) {
// oops, we shouldn't end up in here, since Javeleon was
// supposed to be present given the above test succeeeded!
// Oh well, just fall back to normal reload operation then
return false;
}
System.err.println("Start Javeleon module update...");
// the existing module if any
Module m = null;
// the new updated module
JaveleonModule tm = null;
// Anything that needs to have class loaders refreshed
List<Module> dependents;
// First see if this refers to an existing module.
for (Module module : mgr.getModules()) {
if (module.getJarFile() != null) {
if (jar.equals(module.getJarFile())) {
// Hah, found it.
m = module;
tm = createJaveleonModule(mgr, jar, new ModuleHistory(jar.getAbsolutePath()));
break;
}
}
}
if(m == null) {
return false;
}
// now find dependent modules which need to be class loader migrated
dependents = mgr.simulateJaveleonReload(m);
// setup the class loader for the new Javeleon module
// That's all we need to do to update the module with Javeleon!
setupClassLoaderForJaveleonModule(mgr, tm);
refreshLayer(m, tm, installer, mgr);
// OK so far, then create new Javeleon modules for the
// dependent modules and create new classloaders for
// them as well
for (Module m3 : dependents) {
File moduleJar = m3.getJarFile();
JaveleonModule toRefresh = createJaveleonModule(mgr, moduleJar, new ModuleHistory(moduleJar.getAbsolutePath()));
setupClassLoaderForJaveleonModule(mgr, toRefresh);
refreshLayer(m3, toRefresh, installer, mgr);
}
// done...
System.err.println("Javeleon finished module update...");
MainLookup.systemClassLoaderChanged(mgr.getClassLoader());
ev.log(Events.FINISH_DEPLOY_TEST_MODULE, jar);
return true;
}
private JaveleonModule createJaveleonModule(ModuleManager mgr, File jar, Object history) throws IOException {
try {
return new JaveleonModule(mgr, jar.getAbsoluteFile(), history, mgr.getEvents());
} catch (IOException ex) {
System.err.println("EXCEPTION IN MGR.createJav...");
throw ex;
}
}
private void setupClassLoaderForJaveleonModule(ModuleManager mgr, JaveleonModule javeleonModule) throws InvalidException {
try {
// Calculate the parents to initialize the classloader with.
Dependency[] dependencies = javeleonModule.getDependenciesArray();
Set<Module> parents = new HashSet<Module>(dependencies.length * 4 / 3 + 1);
for (Dependency dep : dependencies) {
if (dep.getType() != Dependency.TYPE_MODULE) {
// Token providers do *not* go into the parent classloader
// list. The providing module must have been turned on first.
// But you cannot automatically access classes from it.
continue;
}
String name = (String) Util.parseCodeName(dep.getName())[0];
Module parent = mgr.get(name);
// Should not happen:
if (parent == null) {
throw new IOException("Parent " + name + " not found!"); // NOI18N
}
parents.add(parent);
}
javeleonModule.classLoaderUp(parents);
// classLoader.append(new ClassLoader[]{javeleonModule.getClassLoader()});
} catch (IOException ioe) {
InvalidException ie = new InvalidException(javeleonModule, ioe.toString());
ie.initCause(ioe);
throw ie;
}
}
private Set</*TopComponent*/?> getOpenTopComponents(ClassLoader loader) {
try {
Class<?> classWindowManager = loader.loadClass("org.openide.windows.WindowManager");
Object manager = classWindowManager.getMethod("getDefault").invoke(null);
Object registry = classWindowManager.getMethod("getRegistry").invoke(manager);
Class<?> classRegistry = loader.loadClass("org.openide.windows.TopComponent$Registry");
return (Set) classRegistry.getMethod("getOpened").invoke(registry);
} catch (Exception ex) {
//Exceptions.printStackTrace(ex);
return Collections.emptySet();
}
}
private void restoreOpenTopComponents(final ClassLoader loader, final Set</*TopComponent*/?> openTCs) {
if (openTCs == null || openTCs.isEmpty()) {
return;
}
// TopComponent.open must be called from the AWT thread
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
Class<?> classTopComponent = loader.loadClass("org.openide.windows.TopComponent");
for (Object topComponent : openTCs) {
classTopComponent.getMethod("open").invoke(topComponent);
}
} catch (Exception ex) {
Exceptions.printStackTrace(ex);
}
}
});
}
private void refreshLayer(Module original, Module newModule, NbInstaller installer, ModuleManager mgr) {
try {
boolean changed = layersChanged(original, newModule);
Set</*TopComponent*/?> openTCs = null;
// Always refresh the layer. Exsitng instances created from the
// layer will be retained and their identity preserved in the updated
// module.
if (changed) {
openTCs = getOpenTopComponents(mgr.getClassLoader());
Module registeredModule = getAndClearRegisteredModule(original);
installer.loadLayers(Collections.singletonList(registeredModule), false);
//installer.unload(Collections.singletonList(registeredModule));
installer.dispose(registeredModule);
}
mgr.replaceJaveleonModule(original, newModule);
if (changed) {
installer.prepare(newModule);
installer.loadLayers(Collections.singletonList(newModule), true);
//installer.load(Collections.singletonList(newModule));
registerModule(newModule);
restoreOpenTopComponents(mgr.getClassLoader(), openTCs);
}
if (!changed && !(original instanceof JaveleonModule)) {
// make sure to register the original module for later unloading
// of the original module that installed the layer.
registerModule(original);
}
}
catch (InvalidException ex) {
// shouldn't happen ever
}
catch (Throwable ex) {
ex.printStackTrace(System.err);
}
}
private boolean layersChanged(Module m1, Module m2) {
return ((CRC32Layer(m1) != CRC32Layer(m2)) || (CRC32GeneratedLayer(m1) != CRC32GeneratedLayer(m2)));
}
private long calculateChecksum(URL layer) {
if (layer == null) {
return -1;
}
try {
InputStream is = layer.openStream();
try {
CheckedInputStream cis = new CheckedInputStream(is, new CRC32());
// Compute the CRC32 checksum
byte[] buf = new byte[1024];
while (cis.read(buf) >= 0) {
}
cis.close();
return cis.getChecksum().getValue();
} finally {
is.close();
}
} catch (IOException e) {
return -1;
}
}
private long CRC32Layer(Module m) {
String layerResource = m.getManifest().getMainAttributes().getValue("OpenIDE-Module-Layer"); // NOI18N
String osgi = m.getManifest().getMainAttributes().getValue("Bundle-SymbolicName"); // NOI18N
if (layerResource != null && osgi == null) {
URL layer = m.getClassLoader().getResource(layerResource);
return calculateChecksum(layer);
}
return -1;
}
private long CRC32GeneratedLayer(Module m) {
String layerRessource = "META-INF/generated-layer.xml"; // NOI18N
URL layer = m.getClassLoader().getResource(layerRessource);
return calculateChecksum(layer);
}
private Module getAndClearRegisteredModule(Module original) {
return (registeredModules.containsKey(original.getCodeNameBase())) ?
registeredModules.remove(original.getCodeNameBase()) :
original;
}
private void registerModule(Module newModule) {
registeredModules.put(newModule.getCodeNameBase(), newModule);
}
}