| /* |
| * 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.awt.Component; |
| import java.awt.Desktop; |
| import java.awt.Dimension; |
| import java.awt.EventQueue; |
| import java.awt.GraphicsEnvironment; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import javax.swing.JButton; |
| import javax.swing.JComponent; |
| import javax.swing.JDialog; |
| import javax.swing.JOptionPane; |
| import javax.swing.JScrollPane; |
| import javax.swing.JTextPane; |
| import javax.swing.UIManager; |
| import javax.swing.UnsupportedLookAndFeelException; |
| import javax.swing.event.HyperlinkEvent; |
| import javax.swing.event.HyperlinkEvent.EventType; |
| import javax.swing.event.HyperlinkListener; |
| import org.netbeans.Events; |
| import org.netbeans.Module; |
| import org.netbeans.Util; |
| import static org.netbeans.core.startup.Bundle.*; |
| import org.openide.modules.SpecificationVersion; |
| import org.openide.util.NbBundle.Messages; |
| import org.openide.util.NbCollections; |
| import org.openide.util.RequestProcessor; |
| |
| /** Report events to the performance logger, status text/splash screen, |
| * console, and so on. |
| * @author Jesse Glick |
| */ |
| final class NbEvents extends Events { |
| private static final Logger logger = Logger.getLogger(NbEvents.class.getName()); |
| |
| private int moduleCount; |
| |
| private int counter; |
| |
| /** Handle a logged event. |
| * CAREFUL that this is called synchronously, usually within a write |
| * mutex or other sensitive environment. So do not call anything |
| * blocking (like TM.notify) directly. TM.setStatusText and printing |
| * to console are fine, as well as performance logging. |
| */ |
| @Messages({ |
| "MSG_start_load_boot_modules=Loading core...", |
| "MSG_finish_load_boot_modules=Loading core...done.", |
| "MSG_start_auto_restore=Loading modules...", |
| "MSG_finish_auto_restore=Done loading modules.", |
| "MSG_start_enable_modules=Turning on modules...", |
| "TEXT_finish_enable_modules=Turning on modules:", |
| "# {0} = startlevel", |
| "MSG_startlevelinfo=, startlevel={0}", |
| "MSG_finish_enable_modules=Turning on modules...done.", |
| "MSG_start_disable_modules=Turning off modules...", |
| "TEXT_finish_disable_modules=Turning off modules:", |
| "MSG_finish_disable_modules=Turning off modules...done.", |
| "# {0} - file name ", "TEXT_start_deploy_test_module=Deploying test module in {0} ...", |
| "TEXT_finish_deploy_test_module=Finished deploying test module.", |
| "MSG_failed_install_new=Warning - could not install some modules:", |
| "# {0} - module display name", "MSG_failed_install_new_unexpected=Warning - could not install module {0}", |
| "MSG_start_read=Reading module storage...", |
| "MSG_finish_read=Reading module storage...done.", |
| "MSG_restore=Starting modules...", |
| "# #13806: might have been installed before and uninstalled.", "# {0} - module display name", "MSG_install=Installing {0}", |
| "# {0} - module display name", "TEXT_install=Installing new module: {0}", |
| "# {0} - module display name", "MSG_update=Starting a new version of {0}", |
| "# {0} - module display name", "TEXT_update=Updating module: {0}", |
| "# {0} - module display name", "MSG_uninstall=Stopping {0}", |
| "MSG_load_section=Loading modules...", |
| "MSG_load_layers=Loading module services...", |
| "# {0} - path to expected module JAR", "TEXT_missing_jar_file=Warning: the module {0} could not be found, ignoring...", |
| "# {0} - module display name", "TEXT_cant_delete_enabled_autoload=The module {0} could not be deleted as it was an autoload or eager module and still in use.", |
| "# {0} - module display name", "# {1} - property name in XML", "# {2} - value on disk in XML", "# {3} - actual value", "TEXT_misc_prop_mismatch=An attempt was made to change the property {1} of the module {0} in the system/Modules folder.\nThe actual value is \"{3}\" but it was changed on disk to read \"{2}\".\nThis property cannot be changed while the IDE is running, so this attempt had no effect.", |
| "# {0} - JAR file name", "TEXT_patch=Module patch or custom extension: {0}" |
| }) |
| @Override protected void logged(final String message, Object[] args) { |
| if (message.equals(PERF_TICK)) { |
| StartLog.logProgress( (String)args[0]); |
| } else if (message.equals(PERF_START)) { |
| StartLog.logStart( (String)args[0]); |
| } else if (message.equals(PERF_END)) { |
| StartLog.logEnd( (String)args[0]); |
| } else if (message.equals(START_CREATE_BOOT_MODULE)) { |
| org.netbeans.core.startup.Splash.getInstance().increment(1); |
| } else if (message.equals(START_LOAD_BOOT_MODULES)) { |
| setStatusText( |
| MSG_start_load_boot_modules()); |
| StartLog.logStart("ModuleSystem.loadBootModules"); // NOI18N |
| } else if (message.equals(START_LOAD)) { |
| StartLog.logStart("NbInstaller.load"); // NOI18N |
| } else if (message.equals(FINISH_LOAD_BOOT_MODULES)) { |
| setStatusText( |
| MSG_finish_load_boot_modules()); |
| StartLog.logEnd( "ModuleSystem.loadBootModules" ); // NOI18N |
| } else if (message.equals(FINISH_LOAD)) { |
| StartLog.logEnd("NbInstaller.load"); // NOI18N |
| } else if (message.equals(START_AUTO_RESTORE)) { |
| Set<?> modules = (Set) args[0]; |
| if (! modules.isEmpty()) { |
| setStatusText( |
| MSG_start_auto_restore()); |
| } |
| } else if (message.equals(FINISH_AUTO_RESTORE)) { |
| setStatusText( |
| MSG_finish_auto_restore()); |
| } else if (message.equals(START_ENABLE_MODULES)) { |
| setStatusText( |
| MSG_start_enable_modules()); |
| } else if (message.equals(FINISH_ENABLE_MODULES)) { |
| List<Module> modules = NbCollections.checkedListByCopy((List) args[0], Module.class, true); |
| if (! modules.isEmpty()) { |
| logger.log(Level.INFO, TEXT_finish_enable_modules()); |
| dumpModulesList(modules); |
| } |
| setStatusText( |
| MSG_finish_enable_modules()); |
| StartLog.logEnd("ModuleManager.enable"); // NOI18N |
| } else if (message.equals(START_DISABLE_MODULES)) { |
| setStatusText( |
| MSG_start_disable_modules()); |
| } else if (message.equals(FINISH_DISABLE_MODULES)) { |
| List<Module> modules = NbCollections.checkedListByCopy((List<?>) args[0], Module.class, true); |
| if (! modules.isEmpty()) { |
| logger.log(Level.INFO, TEXT_finish_disable_modules()); |
| dumpModulesList(modules); |
| } |
| setStatusText( |
| MSG_finish_disable_modules()); |
| } else if (message.equals(START_DEPLOY_TEST_MODULE)) { |
| // No need to print anything. ModuleSystem.deployTestModule prints |
| // its own stuff (it needs to be printed synchronously to console |
| // in order to appear in the output window). But status text is OK. |
| // Fix for IZ#81566 - I18N: need to localize status messages for module dev |
| String msg = TEXT_start_deploy_test_module((File) args[0]); |
| setStatusText( msg ); |
| } else if (message.equals(FINISH_DEPLOY_TEST_MODULE)) { |
| // Fix for IZ#81566 - I18N: need to localize status messages for module dev |
| setStatusText( |
| TEXT_finish_deploy_test_module()); |
| } else if (message.equals(FAILED_INSTALL_NEW)) { |
| Set<Module> modules = NbCollections.checkedSetByCopy((Set) args[0], Module.class, true); |
| { |
| StringBuilder buf = new StringBuilder(MSG_failed_install_new()); |
| NbProblemDisplayer.problemMessagesForModules(buf, modules, false); |
| buf.append('\n'); // #123669 |
| logger.log(Level.INFO, buf.toString()); |
| } |
| { |
| StringBuilder buf = new StringBuilder(MSG_failed_install_new()); |
| NbProblemDisplayer.problemMessagesForModules(buf, modules, true); |
| String msg = buf.toString(); |
| notify(msg, true); |
| } |
| setStatusText(""); |
| } else if (message.equals(FAILED_INSTALL_NEW_UNEXPECTED)) { |
| Module m = (Module)args[0]; |
| List<Module> modules = new ArrayList<Module> (); |
| modules.add (m); |
| modules.addAll (NbCollections.checkedSetByCopy((Set) args[1], Module.class, true)); |
| // ignore args[2]: InvalidException |
| { |
| StringBuilder buf = new StringBuilder(MSG_failed_install_new_unexpected(m.getDisplayName())); |
| NbProblemDisplayer.problemMessagesForModules(buf, modules, false); |
| buf.append('\n'); |
| logger.log(Level.INFO, buf.toString()); |
| } |
| |
| { |
| notify(NbProblemDisplayer.messageForProblem (m, m.getProblems ().iterator ().next (), true), true); |
| } |
| setStatusText(""); |
| } else if (message.equals(START_READ)) { |
| setStatusText( |
| MSG_start_read()); |
| StartLog.logStart("ModuleList.readInitial"); // NOI18N |
| } else if (message.equals(MODULES_FILE_SCANNED)) { |
| moduleCount = (Integer)args[0]; |
| Splash.getInstance().addToMaxSteps(Math.max(moduleCount + moduleCount/2 - 100, 0)); |
| } else if (message.equals(MODULES_FILE_PROCESSED)) { |
| Splash.getInstance().increment(1); |
| if (StartLog.willLog()) { |
| StartLog.logProgress("module " + args[0] + " processed"); // NOI18N |
| } |
| } else if (message.equals(FINISH_READ)) { |
| if (moduleCount < 100) { |
| Splash.getInstance().increment(moduleCount - 100); |
| } |
| setStatusText( |
| MSG_finish_read()); |
| StartLog.logEnd("ModuleList.readInitial"); // NOI18N |
| } else if (message.equals(RESTORE)) { |
| // Don't look for display name. Just takes too long. |
| setStatusText( |
| MSG_restore(/*, ((Module)args[0]).getDisplayName()*/)); |
| if (++counter < moduleCount / 2) { |
| Splash.getInstance().increment(1); |
| } |
| } else if (message.equals(INSTALL)) { |
| // Nice to see the real title; not that common, after all. |
| setStatusText( |
| MSG_install(((Module)args[0]).getDisplayName())); |
| logger.log(Level.INFO, TEXT_install(((Module)args[0]).getDisplayName())); |
| } else if (message.equals(UPDATE)) { |
| setStatusText( |
| MSG_update(((Module)args[0]).getDisplayName())); |
| logger.log(Level.INFO, TEXT_update(((Module)args[0]).getDisplayName())); |
| } else if (message.equals(UNINSTALL)) { |
| setStatusText( |
| MSG_uninstall(((Module)args[0]).getDisplayName())); |
| } else if (message.equals(LOAD_SECTION)) { |
| // Again avoid finding display name now. |
| setStatusText( |
| MSG_load_section(/*, ((Module)args[0]).getDisplayName()*/)); |
| if (++counter < moduleCount / 4) { |
| Splash.getInstance().increment(1); |
| } |
| } else if (message.equals(LOAD_LAYERS)) { |
| setStatusText( |
| MSG_load_layers()); |
| } else if (message.equals(WRONG_CLASS_LOADER)) { |
| if (! Boolean.getBoolean("netbeans.moduleitem.dontverifyclassloader") && Util.err.isLoggable(Level.WARNING)) { // NOI18N |
| Class<?> clazz = (Class) args[1]; |
| // Message for developers, no need for I18N. |
| StringBuilder b = new StringBuilder(); |
| b.append("The module ").append(((Module) args[0]).getDisplayName()).append(" loaded the class ").append(clazz.getName()).append("\n"); // NOI18N |
| b.append("from the wrong classloader. The expected classloader was ").append(args[2]).append("\n"); // NOI18N |
| b.append("whereas it was actually loaded from ").append(clazz.getClassLoader()).append("\n"); // NOI18N |
| b.append("Usually this means that some classes were in the startup classpath.\n"); // NOI18N |
| b.append("To suppress this message, run with: -J-Dnetbeans.moduleitem.dontverifyclassloader=true"); // NOI18N |
| Util.err.warning(b.toString()); |
| } |
| } else if (message.equals(EXTENSION_MULTIPLY_LOADED)) { |
| // Developer-oriented message, no need for I18N. |
| logger.log(Level.WARNING, "The extension {0} may be multiply loaded by modules: {1}; see: http://www.netbeans.org/download/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/classpath.html#class-path", new Object[] {(File) args[0], (Set<?/*File*/>) args[1]}); |
| } else if (message.equals(MISSING_JAR_FILE)) { |
| File jar = (File)args[0]; |
| Level level = Boolean.FALSE.equals(args[1]) ? Level.FINE : Level.INFO; |
| logger.log(level, TEXT_missing_jar_file(jar.getAbsolutePath())); |
| } else if (message.equals(CANT_DELETE_ENABLED_AUTOLOAD)) { |
| Module m = (Module)args[0]; |
| logger.log(Level.INFO, TEXT_cant_delete_enabled_autoload(m.getDisplayName())); |
| } else if (message.equals(MISC_PROP_MISMATCH)) { |
| // XXX does this really need to be logged to the user? |
| // Or should it just be sent quietly to the log file? |
| Module m = (Module)args[0]; |
| String prop = (String)args[1]; |
| Object onDisk = args[2]; |
| Object inMem = args[3]; |
| logger.log(Level.INFO, TEXT_misc_prop_mismatch(m.getDisplayName(), prop, onDisk, inMem)); |
| } else if (message.equals(PATCH)) { |
| File f = (File)args[0]; |
| logger.log(Level.INFO, TEXT_patch(f.getAbsolutePath())); |
| } |
| // XXX other messages? |
| } |
| |
| /** Print a nonempty list of modules to console (= log file). |
| * @param modules the modules |
| */ |
| private void dumpModulesList(Collection<Module> modules) { |
| if (modules.isEmpty()) { |
| throw new IllegalArgumentException(); |
| } |
| StringBuilder buf = new StringBuilder(modules.size() * 100 + 1); |
| String lineSep = System.getProperty("line.separator"); |
| for (Module m : modules) { |
| buf.append('\t'); // NOI18N |
| buf.append(m.getCodeName()); |
| buf.append(" ["); // NOI18N |
| SpecificationVersion sv = m.getSpecificationVersion(); |
| if (sv != null) { |
| buf.append(sv); |
| } |
| String iv = m.getImplementationVersion(); |
| if (iv != null) { |
| buf.append(' '); // NOI18N |
| buf.append(iv); |
| } |
| String bv = m.getBuildVersion(); |
| if (bv != null && !bv.equals (iv)) { |
| buf.append(' '); // NOI18N |
| buf.append(bv); |
| } |
| buf.append(']'); // NOI18N |
| int startlevel = m.getStartLevel(); |
| if (startlevel != -1) { |
| buf.append(MSG_startlevelinfo(startlevel)); |
| } |
| // #32331: use platform-specific newlines |
| buf.append(lineSep); |
| } |
| logger.log(Level.INFO, buf.toString()); |
| } |
| |
| private void notify(String text, boolean warn) { |
| if (GraphicsEnvironment.isHeadless() || Boolean.getBoolean("netbeans.full.hack")) { // NOI18N |
| // #21773: interferes with automated GUI testing. |
| logger.log(Level.WARNING, "{0}\n", text); |
| } else { |
| // Normal - display dialog. |
| new Notifier(text, warn).show(); |
| } |
| } |
| private static final class Notifier implements Runnable { |
| private static boolean showDialog = true; |
| |
| private boolean warn; |
| private String text; |
| private static RequestProcessor RP = new RequestProcessor("Notify About Module System"); // NOI18N |
| |
| public Notifier(String text, boolean type) { |
| this.warn = type; |
| this.text = text; |
| } |
| void show() { |
| if (showDialog) { |
| showDialog = false; |
| if (EventQueue.isDispatchThread()) { |
| run(); |
| } else { |
| RP.post(this, 0, Thread.MIN_PRIORITY).waitFinished (); |
| } |
| } |
| } |
| |
| @Messages({ |
| "MSG_warning=Warning", |
| "MSG_info=Information", |
| "MSG_continue=Disable Modules and Continue", |
| "MSG_exit=Exit" |
| }) |
| @Override public void run() { |
| int type = warn ? JOptionPane.WARNING_MESSAGE : JOptionPane.INFORMATION_MESSAGE; |
| String msg = warn ? MSG_warning() : MSG_info(); |
| |
| Splash out = Splash.getInstance(); |
| final Component c = out.getComponent() == null ? null : out.getComponent(); |
| try { |
| UIManager.setLookAndFeel (UIManager.getSystemLookAndFeelClassName ()); |
| } catch (ClassNotFoundException ex) { |
| logger.log(Level.INFO, null, ex); |
| } catch (InstantiationException ex) { |
| logger.log(Level.INFO, null, ex); |
| } catch (IllegalAccessException ex) { |
| logger.log(Level.INFO, null, ex); |
| } catch (UnsupportedLookAndFeelException ex) { |
| logger.log(Level.INFO, null, ex); |
| } |
| JTextPane tp = new JTextPane (); |
| tp.setContentType("text/html"); // NOI18N |
| text = text.replace ("\n", "<br>"); // NOI18N |
| |
| tp.setEditable(false); |
| tp.setOpaque (false); |
| tp.setEnabled(true); |
| tp.addHyperlinkListener(new HyperlinkListener() { |
| @Override public void hyperlinkUpdate(HyperlinkEvent hlevt) { |
| if (EventType.ACTIVATED == hlevt.getEventType()) { |
| assert hlevt.getURL() != null; |
| try { |
| Desktop.getDesktop().browse(hlevt.getURL().toURI()); |
| } catch (Exception ex) { |
| logger.log(Level.INFO, null, ex); |
| } |
| } |
| } |
| }); |
| |
| tp.setText (text); |
| |
| JComponent sp; |
| if (tp.getPreferredSize ().width > 600 || tp.getPreferredSize ().height > 400) { |
| tp.setPreferredSize (new Dimension (600, 400)); |
| sp = new JScrollPane (tp); |
| } else { |
| sp = tp; |
| } |
| final JOptionPane op = new JOptionPane (sp, type, JOptionPane.YES_NO_OPTION, null); |
| |
| JButton continueButton = new JButton(MSG_continue()); |
| continueButton.setDisplayedMnemonicIndex (0); |
| continueButton.addActionListener (new ActionListener () { |
| @Override public void actionPerformed (ActionEvent e) { |
| op.setValue (0); |
| } |
| }); |
| JButton exitButton = new JButton(MSG_exit()); |
| exitButton.addActionListener (new ActionListener () { |
| @Override public void actionPerformed (ActionEvent e) { |
| op.setValue (1); |
| } |
| }); |
| |
| Object [] options = new JButton [] {continueButton, exitButton}; |
| op.setOptions (options); |
| op.setInitialValue (options [1]); |
| JDialog d = op.createDialog (c, msg); |
| d.setResizable (true); |
| d.pack(); |
| d.setVisible (true); |
| |
| Object res = op.getValue (); |
| if (res instanceof Integer) { |
| int ret = (Integer) res; |
| if (ret == 1 || ret == -1) { // exit or close |
| TopLogging.exit(1); |
| } |
| } |
| } |
| } |
| |
| private static void setStatusText (String msg) { |
| Main.setStatusText (msg); |
| } |
| } |