| /* |
| * 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); |
| } |
| |
| } |