From 82f96368e5e289f37ce71fd49264cba4354c70cb Mon Sep 17 00:00:00 2001 From: malenkov Date: Thu, 19 Jun 2008 18:03:43 +0400 Subject: [PATCH 001/139] 4114658: DOC: Unspecified behaviour for java.beans.PropertyEditorSupport Reviewed-by: peterz, loneid --- .../classes/java/beans/PropertyEditor.java | 17 ++++++------ .../java/beans/PropertyEditorSupport.java | 26 ++++++++++++++----- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/share/classes/java/beans/PropertyEditor.java b/src/share/classes/java/beans/PropertyEditor.java index b6ddf4153..965775898 100644 --- a/src/share/classes/java/beans/PropertyEditor.java +++ b/src/share/classes/java/beans/PropertyEditor.java @@ -204,20 +204,21 @@ public interface PropertyEditor { //---------------------------------------------------------------------- /** - * Register a listener for the PropertyChange event. When a - * PropertyEditor changes its value it should fire a PropertyChange - * event on all registered PropertyChangeListeners, specifying the - * null value for the property name and itself as the source. + * Adds a listener for the value change. + * When the property editor changes its value + * it should fire a {@link PropertyChangeEvent} + * on all registered {@link PropertyChangeListener}s, + * specifying the {@code null} value for the property name + * and itself as the source. * - * @param listener An object to be invoked when a PropertyChange - * event is fired. + * @param listener the {@link PropertyChangeListener} to add */ void addPropertyChangeListener(PropertyChangeListener listener); /** - * Remove a listener for the PropertyChange event. + * Removes a listener for the value change. * - * @param listener The PropertyChange listener to be removed. + * @param listener the {@link PropertyChangeListener} to remove */ void removePropertyChangeListener(PropertyChangeListener listener); diff --git a/src/share/classes/java/beans/PropertyEditorSupport.java b/src/share/classes/java/beans/PropertyEditorSupport.java index 12f07c06b..57fddf043 100644 --- a/src/share/classes/java/beans/PropertyEditorSupport.java +++ b/src/share/classes/java/beans/PropertyEditorSupport.java @@ -233,11 +233,20 @@ public class PropertyEditorSupport implements PropertyEditor { //---------------------------------------------------------------------- /** - * Register a listener for the PropertyChange event. The class will - * fire a PropertyChange value whenever the value is updated. + * Adds a listener for the value change. + * When the property editor changes its value + * it should fire a {@link PropertyChangeEvent} + * on all registered {@link PropertyChangeListener}s, + * specifying the {@code null} value for the property name. + * If the source property is set, + * it should be used as the source of the event. + *

+ * The same listener object may be added more than once, + * and will be called as many times as it is added. + * If {@code listener} is {@code null}, + * no exception is thrown and no action is taken. * - * @param listener An object to be invoked when a PropertyChange - * event is fired. + * @param listener the {@link PropertyChangeListener} to add */ public synchronized void addPropertyChangeListener( PropertyChangeListener listener) { @@ -248,9 +257,14 @@ public class PropertyEditorSupport implements PropertyEditor { } /** - * Remove a listener for the PropertyChange event. + * Removes a listener for the value change. + *

+ * If the same listener was added more than once, + * it will be notified one less time after being removed. + * If {@code listener} is {@code null}, or was never added, + * no exception is thrown and no action is taken. * - * @param listener The PropertyChange listener to be removed. + * @param listener the {@link PropertyChangeListener} to remove */ public synchronized void removePropertyChangeListener( PropertyChangeListener listener) { -- GitLab From 5e86409850743d34c49f16fbd1c6c6a70a4bbab2 Mon Sep 17 00:00:00 2001 From: idk Date: Mon, 23 Jun 2008 15:21:37 -0400 Subject: [PATCH 002/139] 6623943: javax.swing.TimerQueue's thread occasionally fails to start Reviewed-by: alexp --- src/share/classes/javax/swing/JApplet.java | 5 +- src/share/classes/javax/swing/TimerQueue.java | 67 ++++++++++--------- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/src/share/classes/javax/swing/JApplet.java b/src/share/classes/javax/swing/JApplet.java index b9b5b2506..47f792a94 100644 --- a/src/share/classes/javax/swing/JApplet.java +++ b/src/share/classes/javax/swing/JApplet.java @@ -131,10 +131,7 @@ public class JApplet extends Applet implements Accessible, // Check the timerQ and restart if necessary. TimerQueue q = TimerQueue.sharedInstance(); if(q != null) { - synchronized(q) { - if(!q.running) - q.start(); - } + q.startIfNeeded(); } /* Workaround for bug 4155072. The shared double buffer image diff --git a/src/share/classes/javax/swing/TimerQueue.java b/src/share/classes/javax/swing/TimerQueue.java index 212193901..1f694d1c8 100644 --- a/src/share/classes/javax/swing/TimerQueue.java +++ b/src/share/classes/javax/swing/TimerQueue.java @@ -31,6 +31,7 @@ package javax.swing; import java.util.*; import java.util.concurrent.*; +import java.util.concurrent.locks.*; import java.util.concurrent.atomic.AtomicLong; import sun.awt.AppContext; @@ -52,7 +53,8 @@ class TimerQueue implements Runnable new StringBuffer("TimerQueue.expiredTimersKey"); private final DelayQueue queue; - volatile boolean running; + private volatile boolean running; + private final Lock runningLock; /* Lock object used in place of class object for synchronization. * (4187686) @@ -69,7 +71,8 @@ class TimerQueue implements Runnable super(); queue = new DelayQueue(); // Now start the TimerQueue thread. - start(); + runningLock = new ReentrantLock(); + startIfNeeded(); } @@ -87,33 +90,30 @@ class TimerQueue implements Runnable } - synchronized void start() { - if (running) { - throw new RuntimeException("Can't start a TimerQueue " + - "that is already running"); - } - else { - final ThreadGroup threadGroup = - AppContext.getAppContext().getThreadGroup(); - java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public Object run() { - Thread timerThread = new Thread(threadGroup, TimerQueue.this, - "TimerQueue"); - timerThread.setDaemon(true); - timerThread.setPriority(Thread.NORM_PRIORITY); - timerThread.start(); - return null; - } - }); - running = true; + void startIfNeeded() { + if (! running) { + runningLock.lock(); + try { + final ThreadGroup threadGroup = + AppContext.getAppContext().getThreadGroup(); + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Object run() { + Thread timerThread = new Thread(threadGroup, TimerQueue.this, + "TimerQueue"); + timerThread.setDaemon(true); + timerThread.setPriority(Thread.NORM_PRIORITY); + timerThread.start(); + return null; + } + }); + running = true; + } finally { + runningLock.unlock(); + } } } - synchronized void stop() { - running = false; - } - void addTimer(Timer timer, long delayMillis) { timer.getLock().lock(); try { @@ -164,6 +164,7 @@ class TimerQueue implements Runnable public void run() { + runningLock.lock(); try { while (running) { try { @@ -195,14 +196,14 @@ class TimerQueue implements Runnable } } catch (ThreadDeath td) { - synchronized (this) { - running = false; - // Mark all the timers we contain as not being queued. - for (DelayedTimer delayedTimer : queue) { - delayedTimer.getTimer().cancelEvent(); - } - throw td; + // Mark all the timers we contain as not being queued. + for (DelayedTimer delayedTimer : queue) { + delayedTimer.getTimer().cancelEvent(); } + throw td; + } finally { + running = false; + runningLock.unlock(); } } -- GitLab From 1c3d2eabb5b95ceda776ea48331070b44766bbcd Mon Sep 17 00:00:00 2001 From: malenkov Date: Thu, 26 Jun 2008 15:11:04 +0400 Subject: [PATCH 003/139] 6718964: Swing border tests should be open source Reviewed-by: peterz --- test/javax/swing/border/Test4120351.java | 43 ++++++++++++++ test/javax/swing/border/Test4124729.java | 38 ++++++++++++ test/javax/swing/border/Test4243289.html | 9 +++ test/javax/swing/border/Test4243289.java | 52 +++++++++++++++++ test/javax/swing/border/Test4247606.html | 10 ++++ test/javax/swing/border/Test4247606.java | 62 ++++++++++++++++++++ test/javax/swing/border/Test4252164.html | 10 ++++ test/javax/swing/border/Test4252164.java | 73 ++++++++++++++++++++++++ test/javax/swing/border/Test6461042.java | 57 ++++++++++++++++++ 9 files changed, 354 insertions(+) create mode 100644 test/javax/swing/border/Test4120351.java create mode 100644 test/javax/swing/border/Test4124729.java create mode 100644 test/javax/swing/border/Test4243289.html create mode 100644 test/javax/swing/border/Test4243289.java create mode 100644 test/javax/swing/border/Test4247606.html create mode 100644 test/javax/swing/border/Test4247606.java create mode 100644 test/javax/swing/border/Test4252164.html create mode 100644 test/javax/swing/border/Test4252164.java create mode 100644 test/javax/swing/border/Test6461042.java diff --git a/test/javax/swing/border/Test4120351.java b/test/javax/swing/border/Test4120351.java new file mode 100644 index 000000000..1253e638a --- /dev/null +++ b/test/javax/swing/border/Test4120351.java @@ -0,0 +1,43 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4120351 + * @summary Tests that the methods createEtchedBorder(int type) and + * createEtchedBorder(int type, Color highlight, Color shadows) are added + * @author Andrey Pikalev + */ + +import java.awt.Color; +import javax.swing.BorderFactory; +import javax.swing.border.EtchedBorder; + +public class Test4120351 { + public static void main(String[] args) { + BorderFactory.createEtchedBorder(EtchedBorder.RAISED); + BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); + BorderFactory.createEtchedBorder(EtchedBorder.RAISED, Color.BLACK, Color.WHITE); + BorderFactory.createEtchedBorder(EtchedBorder.LOWERED, Color.WHITE, Color.BLACK); + } +} diff --git a/test/javax/swing/border/Test4124729.java b/test/javax/swing/border/Test4124729.java new file mode 100644 index 000000000..d48ce9d1d --- /dev/null +++ b/test/javax/swing/border/Test4124729.java @@ -0,0 +1,38 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4124729 + * @summary Test that constrtructor LineBorder(?,?,?) is public + * @author Andrey Pikalev + */ + +import java.awt.Color; +import javax.swing.border.LineBorder; + +public class Test4124729 { + public static void main(String[] args) { + new LineBorder(Color.BLUE, 3, true); + } +} diff --git a/test/javax/swing/border/Test4243289.html b/test/javax/swing/border/Test4243289.html new file mode 100644 index 000000000..9133d99c5 --- /dev/null +++ b/test/javax/swing/border/Test4243289.html @@ -0,0 +1,9 @@ + + +When applet starts, you'll see a panel with a TitledBorder with title "Panel Title". +If this title is overstriken with the border line, test fails, otherwise it passes. + + + + + diff --git a/test/javax/swing/border/Test4243289.java b/test/javax/swing/border/Test4243289.java new file mode 100644 index 000000000..1c462a859 --- /dev/null +++ b/test/javax/swing/border/Test4243289.java @@ -0,0 +1,52 @@ +/* + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4243289 + * @summary Tests that TitledBorder do not draw line through its caption + * @author Peter Zhelezniakov + * @run applet/manual=yesno Test4243289.html + */ + +import java.awt.Font; +import javax.swing.BorderFactory; +import javax.swing.JApplet; +import javax.swing.JPanel; +import javax.swing.border.TitledBorder; + +public class Test4243289 extends JApplet { + public void init() { + Font font = new Font("Dialog", Font.PLAIN, 12); // NON-NLS: the font name + TitledBorder border = BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), + "Panel Title", // NON-NLS: the title of the border + TitledBorder.DEFAULT_JUSTIFICATION, + TitledBorder.DEFAULT_POSITION, + font); + + JPanel panel = new JPanel(); + panel.setBorder(border); + getContentPane().add(panel); + } +} diff --git a/test/javax/swing/border/Test4247606.html b/test/javax/swing/border/Test4247606.html new file mode 100644 index 000000000..d501ab221 --- /dev/null +++ b/test/javax/swing/border/Test4247606.html @@ -0,0 +1,10 @@ + + +If the button do not fit into the titled border bounds +and cover the bottom border's line then test fails. +Otherwise test passes. + + + + + diff --git a/test/javax/swing/border/Test4247606.java b/test/javax/swing/border/Test4247606.java new file mode 100644 index 000000000..b598d4a1b --- /dev/null +++ b/test/javax/swing/border/Test4247606.java @@ -0,0 +1,62 @@ +/* + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4247606 + * @summary BorderedPane appears wrong with Title Position Below Bottom + * @author Andrey Pikalev + * @run applet/manual=yesno Test4247606.html + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import javax.swing.BorderFactory; +import javax.swing.JApplet; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.TitledBorder; + +public class Test4247606 extends JApplet { + public void init() { + JButton button = new JButton("Button"); // NON-NLS: the button text + button.setBorder(BorderFactory.createLineBorder(Color.red, 1)); + + TitledBorder border = new TitledBorder("Bordered Pane"); // NON-NLS: the panel title + border.setTitlePosition(TitledBorder.BELOW_BOTTOM); + + JPanel panel = create(button, border); + panel.setBackground(Color.green); + + getContentPane().add(create(panel, BorderFactory.createEmptyBorder(10, 10, 10, 10))); + } + + private static JPanel create(JComponent component, Border border) { + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(border); + panel.add(component); + return panel; + } +} diff --git a/test/javax/swing/border/Test4252164.html b/test/javax/swing/border/Test4252164.html new file mode 100644 index 000000000..eb436c53f --- /dev/null +++ b/test/javax/swing/border/Test4252164.html @@ -0,0 +1,10 @@ + + +Please, ensure that rounded border is filled completely. +It should not contain white points inside. +Use Mouse Wheel to change thickness of the border. + + + + + diff --git a/test/javax/swing/border/Test4252164.java b/test/javax/swing/border/Test4252164.java new file mode 100644 index 000000000..a39193fc6 --- /dev/null +++ b/test/javax/swing/border/Test4252164.java @@ -0,0 +1,73 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4252164 + * @summary Tests rounded LineBorder for components + * @author Sergey Malenkov + * @run applet/manual=yesno Test4252164.html + */ + +import java.awt.Color; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import javax.swing.JApplet; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.LineBorder; + +public class Test4252164 extends JApplet implements MouseWheelListener { + private int thickness; + private JLabel rounded; + private JLabel straight; + + public void mouseWheelMoved(MouseWheelEvent event) { + update(event.getWheelRotation()); + } + + public void init() { + add(createUI()); + addMouseWheelListener(this); + } + + private JPanel createUI() { + this.rounded = new JLabel("ROUNDED"); // NON-NLS: the label for rounded border + this.straight = new JLabel("STRAIGHT"); // NON-NLS: the label for straight border + + JPanel panel = new JPanel(); + panel.add(this.rounded); + panel.add(this.straight); + + update(10); + + return panel; + } + + private void update(int thickness) { + this.thickness += thickness; + + this.rounded.setBorder(new LineBorder(Color.RED, this.thickness, true)); + this.straight.setBorder(new LineBorder(Color.RED, this.thickness, false)); + } +} diff --git a/test/javax/swing/border/Test6461042.java b/test/javax/swing/border/Test6461042.java new file mode 100644 index 000000000..3224b1db8 --- /dev/null +++ b/test/javax/swing/border/Test6461042.java @@ -0,0 +1,57 @@ +/* + * Copyright 2006-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6461042 + * @summary Tests that toString() doesn't cause StackOverflowException + * when a JComponent is its own border + * @author Shannon Hickey + */ + +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Insets; +import javax.swing.JComponent; +import javax.swing.border.Border; + +public class Test6461042 extends JComponent implements Border { + public static void main(String[] args) { + new Test6461042().toString(); + } + + public Test6461042() { + setBorder(this); + } + + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + } + + public Insets getBorderInsets(Component c) { + return null; + } + + public boolean isBorderOpaque() { + return false; + } +} -- GitLab From ec9212ec0a4c0c550a0266cf0ec3c201d0fc8f9c Mon Sep 17 00:00:00 2001 From: malenkov Date: Thu, 26 Jun 2008 15:39:12 +0400 Subject: [PATCH 004/139] 6718965: Swing color chooser tests should be open source Reviewed-by: peterz --- .../swing/JColorChooser/Test4165217.java | 78 +++++++++++++++ .../swing/JColorChooser/Test4177735.java | 95 +++++++++++++++++++ .../swing/JColorChooser/Test4193384.java | 70 ++++++++++++++ .../swing/JColorChooser/Test4234761.java | 60 ++++++++++++ .../swing/JColorChooser/Test4380468.html | 17 ++++ .../swing/JColorChooser/Test4380468.java | 40 ++++++++ .../swing/JColorChooser/Test4461329.java | 46 +++++++++ .../swing/JColorChooser/Test4711996.java | 41 ++++++++ .../swing/JColorChooser/Test4759306.html | 8 ++ .../swing/JColorChooser/Test4759306.java | 42 ++++++++ .../swing/JColorChooser/Test4759934.html | 14 +++ .../swing/JColorChooser/Test4759934.java | 80 ++++++++++++++++ .../swing/JColorChooser/Test4887836.html | 9 ++ .../swing/JColorChooser/Test4887836.java | 43 +++++++++ 14 files changed, 643 insertions(+) create mode 100644 test/javax/swing/JColorChooser/Test4165217.java create mode 100644 test/javax/swing/JColorChooser/Test4177735.java create mode 100644 test/javax/swing/JColorChooser/Test4193384.java create mode 100644 test/javax/swing/JColorChooser/Test4234761.java create mode 100644 test/javax/swing/JColorChooser/Test4380468.html create mode 100644 test/javax/swing/JColorChooser/Test4380468.java create mode 100644 test/javax/swing/JColorChooser/Test4461329.java create mode 100644 test/javax/swing/JColorChooser/Test4711996.java create mode 100644 test/javax/swing/JColorChooser/Test4759306.html create mode 100644 test/javax/swing/JColorChooser/Test4759306.java create mode 100644 test/javax/swing/JColorChooser/Test4759934.html create mode 100644 test/javax/swing/JColorChooser/Test4759934.java create mode 100644 test/javax/swing/JColorChooser/Test4887836.html create mode 100644 test/javax/swing/JColorChooser/Test4887836.java diff --git a/test/javax/swing/JColorChooser/Test4165217.java b/test/javax/swing/JColorChooser/Test4165217.java new file mode 100644 index 000000000..59a80e898 --- /dev/null +++ b/test/javax/swing/JColorChooser/Test4165217.java @@ -0,0 +1,78 @@ +/* + * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4165217 + * @summary Tests JColorChooser serialization + * @author Ilya Boyandin + */ + +import java.awt.Color; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Random; +import javax.swing.JColorChooser; + +public class Test4165217 { + public static void main(String[] args) { + JColorChooser chooser = new JColorChooser(); + chooser.setColor(new Color(new Random().nextInt())); + + Color before = chooser.getColor(); + Color after = copy(chooser).getColor(); + + if (!after.equals(before)) { + throw new Error("color is changed after serialization"); + } + } + + private static JColorChooser copy(JColorChooser chooser) { + try { + return (JColorChooser) deserialize(serialize(chooser)); + } + catch (ClassNotFoundException exception) { + throw new Error("unexpected exception during class creation", exception); + } + catch (IOException exception) { + throw new Error("unexpected exception during serialization", exception); + } + } + + private static byte[] serialize(Object object) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(object); + oos.flush(); + return baos.toByteArray(); + } + + private static Object deserialize(byte[] array) throws IOException, ClassNotFoundException { + ByteArrayInputStream bais = new ByteArrayInputStream(array); + ObjectInputStream ois = new ObjectInputStream(bais); + return ois.readObject(); + } +} diff --git a/test/javax/swing/JColorChooser/Test4177735.java b/test/javax/swing/JColorChooser/Test4177735.java new file mode 100644 index 000000000..b8b2d93f4 --- /dev/null +++ b/test/javax/swing/JColorChooser/Test4177735.java @@ -0,0 +1,95 @@ +/* + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4177735 + * @summary Tests that JColorChooser leaves no threads when disposed + * @author Shannon Hickey + */ + +import java.awt.Point; +import javax.swing.JColorChooser; +import javax.swing.JDialog; +import javax.swing.SwingUtilities; +import javax.swing.colorchooser.AbstractColorChooserPanel; + +public class Test4177735 implements Runnable { + private static final long DELAY = 1000L; + + public static void main(String[] args) throws Exception { + JColorChooser chooser = new JColorChooser(); + AbstractColorChooserPanel[] panels = chooser.getChooserPanels(); + chooser.setChooserPanels(new AbstractColorChooserPanel[] { panels[1] }); + + JDialog dialog = show(chooser); + pause(DELAY); + + dialog.dispose(); + pause(DELAY); + + Test4177735 test = new Test4177735(); + SwingUtilities.invokeAndWait(test); + if (test.count != 0) { + throw new Error("JColorChooser leaves " + test.count + " threads running"); + } + } + + static JDialog show(JColorChooser chooser) { + JDialog dialog = JColorChooser.createDialog(null, null, false, chooser, null, null); + dialog.setVisible(true); + // block till displayed + Point point = null; + while (point == null) { + try { + point = dialog.getLocationOnScreen(); + } + catch (IllegalStateException exception) { + pause(DELAY); + } + } + return dialog; + } + + private static void pause(long delay) { + try { + Thread.sleep(delay); + } + catch (InterruptedException exception) { + } + } + + private int count; + + public void run() { + ThreadGroup group = Thread.currentThread().getThreadGroup(); + Thread[] threads = new Thread[group.activeCount()]; + int count = group.enumerate(threads, false); + for (int i = 0; i < count; i++) { + String name = threads[i].getName(); + if ("SyntheticImageGenerator".equals(name)) { // NON-NLS: thread name + this.count++; + } + } + } +} diff --git a/test/javax/swing/JColorChooser/Test4193384.java b/test/javax/swing/JColorChooser/Test4193384.java new file mode 100644 index 000000000..e9ed74728 --- /dev/null +++ b/test/javax/swing/JColorChooser/Test4193384.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4193384 4200976 + * @summary Tests the color conversions and the preview panel foreground color + * @author Mark Davidson + */ + +import java.awt.Color; +import javax.swing.JColorChooser; +import javax.swing.JLabel; + +public class Test4193384 { + public static void main(String[] args) { + test(new Color[] { + new Color(11, 12, 13), + new Color(204, 0, 204), + new Color(0, 51, 51) + }); + } + + private static void test(Color[] colors) { + JLabel label = new JLabel("Preview Panel"); // NON-NLS: simple label + + JColorChooser chooser = new JColorChooser(); + chooser.setPreviewPanel(label); + + float[] hsb = new float[3]; + for (int i = 0; i < colors.length; i++) { + Color color = colors[i]; + // Make sure sure that there wasn't a regression + // in java.awt.Color and the conversion methods + Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), hsb); + if (!color.equals(Color.getHSBColor(hsb[0], hsb[1], hsb[2]))) { + throw new Error("color conversion is failed"); + } + // 4193384 regression test + if (!color.equals(new JColorChooser(color).getColor())) { + throw new Error("constructor sets incorrect initial color"); + } + // 4200976 regression test + chooser.setColor(color); + if (!color.equals(label.getForeground())) { + throw new Error("a custom preview panel doesn't handle colors"); + } + } + } +} diff --git a/test/javax/swing/JColorChooser/Test4234761.java b/test/javax/swing/JColorChooser/Test4234761.java new file mode 100644 index 000000000..aa3d972a6 --- /dev/null +++ b/test/javax/swing/JColorChooser/Test4234761.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4234761 + * @summary RGB values sholdn't be changed in transition to HSB tab + * @author Oleg Mokhovikov + */ + +import java.awt.Color; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.JColorChooser; +import javax.swing.JDialog; +import javax.swing.JTabbedPane; + +public class Test4234761 implements PropertyChangeListener { + private static final Color COLOR = new Color(51, 51, 51); + + public static void main(String[] args) { + JColorChooser chooser = new JColorChooser(COLOR); + JDialog dialog = Test4177735.show(chooser); + + PropertyChangeListener listener = new Test4234761(); + chooser.addPropertyChangeListener("color", listener); // NON-NLS: property name + + JTabbedPane tabbedPane = (JTabbedPane) chooser.getComponent(0); + tabbedPane.setSelectedIndex(1); // HSB tab index + + if (!chooser.getColor().equals(COLOR)) { + listener.propertyChange(null); + } + dialog.dispose(); + } + + public void propertyChange(PropertyChangeEvent event) { + throw new Error("RGB value is changed after transition to HSB tab"); + } +} diff --git a/test/javax/swing/JColorChooser/Test4380468.html b/test/javax/swing/JColorChooser/Test4380468.html new file mode 100644 index 000000000..fbbba0d2e --- /dev/null +++ b/test/javax/swing/JColorChooser/Test4380468.html @@ -0,0 +1,17 @@ + + +1. Click the HSB tab at the ColorChooser. +2. Click in the lower left corner of the gradient palette + in order to select a color such that all three RGB values + are single digit colors (such as 0, 0, 0 or 5, 3, 1). +3. Click another tab, then click back to the HSB tab. +4. Now click the lighter colors that should have + 2 and 3 digit RGB values (in the upper right corner). + +If all digits of each RGB value are shown then test passes. +If only the last digit of their values are shown then test fails. + + + + + diff --git a/test/javax/swing/JColorChooser/Test4380468.java b/test/javax/swing/JColorChooser/Test4380468.java new file mode 100644 index 000000000..7e9033171 --- /dev/null +++ b/test/javax/swing/JColorChooser/Test4380468.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4380468 + * @summary JColorChooser's HSB panel should display all RGB digits + * @author Andrey Pikalev + * @run applet/manual=yesno Test4380468.html + */ + +import java.awt.Color; +import javax.swing.JApplet; +import javax.swing.JColorChooser; + +public class Test4380468 extends JApplet { + public void init() { + add(new JColorChooser(Color.GREEN)); + } +} diff --git a/test/javax/swing/JColorChooser/Test4461329.java b/test/javax/swing/JColorChooser/Test4461329.java new file mode 100644 index 000000000..2f23bc1ec --- /dev/null +++ b/test/javax/swing/JColorChooser/Test4461329.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4461329 + * @summary Tests getPreviewPanel() and setPreviewPanel() methods + * @author Leif Samuelsson + */ + +import javax.swing.JButton; +import javax.swing.JColorChooser; + +public class Test4461329 { + public static void main(String[] args) { + JColorChooser chooser = new JColorChooser(); + if (null == chooser.getPreviewPanel()) { + throw new Error("Failed: getPreviewPanel() returned null"); + } + JButton button = new JButton("Color"); // NON-NLS: simple label + chooser.setPreviewPanel(button); + if (button != chooser.getPreviewPanel()) { + throw new Error("Failed in setPreviewPanel()"); + } + } +} diff --git a/test/javax/swing/JColorChooser/Test4711996.java b/test/javax/swing/JColorChooser/Test4711996.java new file mode 100644 index 000000000..fb6007712 --- /dev/null +++ b/test/javax/swing/JColorChooser/Test4711996.java @@ -0,0 +1,41 @@ +/* + * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4711996 + * @summary Checks if IllegalArgumentException is thrown when updating JColorChooserUI + * @author Konstantin Eremin + */ + +import javax.swing.JColorChooser; +import javax.swing.colorchooser.AbstractColorChooserPanel; + +public class Test4711996 { + public static void main(String[] args) { + JColorChooser chooser = new JColorChooser(); + AbstractColorChooserPanel[] panels = chooser.getChooserPanels(); + chooser.removeChooserPanel(panels[0]); + chooser.updateUI(); + } +} diff --git a/test/javax/swing/JColorChooser/Test4759306.html b/test/javax/swing/JColorChooser/Test4759306.html new file mode 100644 index 000000000..8a4d53f00 --- /dev/null +++ b/test/javax/swing/JColorChooser/Test4759306.html @@ -0,0 +1,8 @@ + + +If you see the preview panel, then test failed, otherwise it passed. + + + + + diff --git a/test/javax/swing/JColorChooser/Test4759306.java b/test/javax/swing/JColorChooser/Test4759306.java new file mode 100644 index 000000000..726ed798f --- /dev/null +++ b/test/javax/swing/JColorChooser/Test4759306.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4759306 + * @summary Checks if JColorChooser.setPreviewPanel removes the old one + * @author Konstantin Eremin + @run applet/manual=yesno Test4759306.html + */ + +import javax.swing.JApplet; +import javax.swing.JColorChooser; +import javax.swing.JPanel; + +public class Test4759306 extends JApplet { + public void init() { + JColorChooser chooser = new JColorChooser(); + chooser.setPreviewPanel(new JPanel()); + getContentPane().add(chooser); + } +} diff --git a/test/javax/swing/JColorChooser/Test4759934.html b/test/javax/swing/JColorChooser/Test4759934.html new file mode 100644 index 000000000..0441b67ac --- /dev/null +++ b/test/javax/swing/JColorChooser/Test4759934.html @@ -0,0 +1,14 @@ + + +1. Press button "Show Dialog" at the frame "Test" and + the dialog with button "Show ColorChooser" should appears. +2. Press button "Show ColorChooser" at the dialog "Dialog" and + the colorchooser should appears. +3. Press the button "Cancel" of colorchooser. + If the focus will come to the frame "Test" then test fails. + If the focus will come to the dialog "Dialog" then test passes. + + + + + diff --git a/test/javax/swing/JColorChooser/Test4759934.java b/test/javax/swing/JColorChooser/Test4759934.java new file mode 100644 index 000000000..27137726e --- /dev/null +++ b/test/javax/swing/JColorChooser/Test4759934.java @@ -0,0 +1,80 @@ +/* + * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4759934 + * @summary Tests windows activation problem + * @author Andrey Pikalev + * @run applet/manual=yesno Test4759934.html + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JApplet; +import javax.swing.JButton; +import javax.swing.JColorChooser; +import javax.swing.JDialog; +import javax.swing.JFrame; + +public class Test4759934 extends JApplet implements ActionListener { + private static final String CMD_DIALOG = "Show Dialog"; // NON-NLS: first button + private static final String CMD_CHOOSER = "Show ColorChooser"; // NON-NLS: second button + + private final JFrame frame = new JFrame("Test"); // NON-NLS: frame title + + public void init() { + show(this.frame, CMD_DIALOG); + } + + public void actionPerformed(ActionEvent event) { + String command = event.getActionCommand(); + if (CMD_DIALOG.equals(command)) { + JDialog dialog = new JDialog(this.frame, "Dialog"); // NON-NLS: dialog title + dialog.setLocation(200, 0); + show(dialog, CMD_CHOOSER); + } + else if (CMD_CHOOSER.equals(command)) { + Object source = event.getSource(); + Component component = (source instanceof Component) + ? (Component) source + : null; + + JColorChooser.showDialog(component, "ColorChooser", Color.BLUE); // NON-NLS: title + } + } + + private void show(Window window, String command) { + JButton button = new JButton(command); + button.setActionCommand(command); + button.addActionListener(this); + button.setFont(button.getFont().deriveFont(64.0f)); + + window.add(button); + window.pack(); + window.setVisible(true); + } +} diff --git a/test/javax/swing/JColorChooser/Test4887836.html b/test/javax/swing/JColorChooser/Test4887836.html new file mode 100644 index 000000000..324d5afe3 --- /dev/null +++ b/test/javax/swing/JColorChooser/Test4887836.html @@ -0,0 +1,9 @@ + + +If you do not see white area under swatches, +then test passed, otherwise it failed. + + + + + diff --git a/test/javax/swing/JColorChooser/Test4887836.java b/test/javax/swing/JColorChooser/Test4887836.java new file mode 100644 index 000000000..3c6bb3832 --- /dev/null +++ b/test/javax/swing/JColorChooser/Test4887836.java @@ -0,0 +1,43 @@ +/* + * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4887836 + * @summary Checks if no tooltip modification when no KeyStroke modifier + * @author Konstantin Eremin + * @run applet/manual=yesno Test4887836.html + */ + +import java.awt.Color; +import java.awt.Font; +import javax.swing.JApplet; +import javax.swing.JColorChooser; +import javax.swing.UIManager; + +public class Test4887836 extends JApplet { + public void init() { + UIManager.put("Label.font", new Font("Perpetua", 0, 36)); // NON-NLS: property and font names + add(new JColorChooser(Color.LIGHT_GRAY)); + } +} -- GitLab From 54e9ecfc5fafc63c01491552518043ba15892a56 Mon Sep 17 00:00:00 2001 From: mlapshin Date: Wed, 2 Jul 2008 18:17:56 +0400 Subject: [PATCH 005/139] 6618401: Input method cannot be selected from System menu Summary: lock.wait() added in sun.awt.im.InputMethodManager.showInputMethodMenuOnRequesterEDT() Reviewed-by: alexp --- src/share/classes/sun/awt/im/InputMethodManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/share/classes/sun/awt/im/InputMethodManager.java b/src/share/classes/sun/awt/im/InputMethodManager.java index f95b13c63..caba8d6a1 100644 --- a/src/share/classes/sun/awt/im/InputMethodManager.java +++ b/src/share/classes/sun/awt/im/InputMethodManager.java @@ -358,6 +358,7 @@ class ExecutableInputMethodManager extends InputMethodManager AppContext requesterAppContext = SunToolkit.targetToAppContext(requester); synchronized (lock) { SunToolkit.postEvent(requesterAppContext, event); + lock.wait(); } Throwable eventThrowable = event.getThrowable(); -- GitLab From 72e10a12f2f6e2be62b5fbb197fa4a54b92cf146 Mon Sep 17 00:00:00 2001 From: mlapshin Date: Mon, 7 Jul 2008 16:56:23 +0400 Subject: [PATCH 006/139] 6647340: Minimized JInternalFrame icons appear in incorrect positions if the main frame is resized Summary: Now BasicInternalFrameUI and BasicDesktopIconUI both recalculate frame icon position Reviewed-by: peterz --- .../swing/plaf/basic/BasicDesktopIconUI.java | 12 +- .../plaf/basic/BasicInternalFrameUI.java | 45 ++--- .../swing/plaf/basic/DesktopIconMover.java | 168 ++++++++++++++++++ .../JInternalFrame/6647340/bug6647340.java | 146 +++++++++++++++ 4 files changed, 335 insertions(+), 36 deletions(-) create mode 100644 src/share/classes/javax/swing/plaf/basic/DesktopIconMover.java create mode 100644 test/javax/swing/JInternalFrame/6647340/bug6647340.java diff --git a/src/share/classes/javax/swing/plaf/basic/BasicDesktopIconUI.java b/src/share/classes/javax/swing/plaf/basic/BasicDesktopIconUI.java index 9aab4dbe1..5ebb62894 100644 --- a/src/share/classes/javax/swing/plaf/basic/BasicDesktopIconUI.java +++ b/src/share/classes/javax/swing/plaf/basic/BasicDesktopIconUI.java @@ -1,5 +1,5 @@ /* - * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,6 +47,7 @@ public class BasicDesktopIconUI extends DesktopIconUI { protected JInternalFrame.JDesktopIcon desktopIcon; protected JInternalFrame frame; + private DesktopIconMover desktopIconMover; /** * The title pane component used in the desktop icon. @@ -127,12 +128,21 @@ public class BasicDesktopIconUI extends DesktopIconUI { mouseInputListener = createMouseInputListener(); desktopIcon.addMouseMotionListener(mouseInputListener); desktopIcon.addMouseListener(mouseInputListener); + getDesktopIconMover().installListeners(); } protected void uninstallListeners() { desktopIcon.removeMouseMotionListener(mouseInputListener); desktopIcon.removeMouseListener(mouseInputListener); mouseInputListener = null; + getDesktopIconMover().uninstallListeners(); + } + + private DesktopIconMover getDesktopIconMover() { + if (desktopIconMover == null) { + desktopIconMover = new DesktopIconMover(desktopIcon); + } + return desktopIconMover; } protected void installDefaults() { diff --git a/src/share/classes/javax/swing/plaf/basic/BasicInternalFrameUI.java b/src/share/classes/javax/swing/plaf/basic/BasicInternalFrameUI.java index 83b0fd73b..86a8f15dd 100644 --- a/src/share/classes/javax/swing/plaf/basic/BasicInternalFrameUI.java +++ b/src/share/classes/javax/swing/plaf/basic/BasicInternalFrameUI.java @@ -1,5 +1,5 @@ /* - * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,7 +55,6 @@ public class BasicInternalFrameUI extends InternalFrameUI protected MouseInputAdapter borderListener; protected PropertyChangeListener propertyChangeListener; protected LayoutManager internalFrameLayout; - protected ComponentListener componentListener; protected MouseInputListener glassPaneDispatcher; private InternalFrameListener internalFrameListener; @@ -67,9 +66,9 @@ public class BasicInternalFrameUI extends InternalFrameUI protected BasicInternalFrameTitlePane titlePane; // access needs this private static DesktopManager sharedDesktopManager; - private boolean componentListenerAdded = false; private Rectangle parentBounds; + private DesktopIconMover desktopIconMover; private boolean dragging = false; private boolean resizing = false; @@ -210,14 +209,17 @@ public class BasicInternalFrameUI extends InternalFrameUI frame.getGlassPane().addMouseListener(glassPaneDispatcher); frame.getGlassPane().addMouseMotionListener(glassPaneDispatcher); } - componentListener = createComponentListener(); if (frame.getParent() != null) { parentBounds = frame.getParent().getBounds(); } - if ((frame.getParent() != null) && !componentListenerAdded) { - frame.getParent().addComponentListener(componentListener); - componentListenerAdded = true; + getDesktopIconMover().installListeners(); + } + + private DesktopIconMover getDesktopIconMover() { + if (desktopIconMover == null) { + desktopIconMover = new DesktopIconMover(frame); } + return desktopIconMover; } // Provide a FocusListener to listen for a WINDOW_LOST_FOCUS event, @@ -288,11 +290,7 @@ public class BasicInternalFrameUI extends InternalFrameUI * @since 1.3 */ protected void uninstallListeners() { - if ((frame.getParent() != null) && componentListenerAdded) { - frame.getParent().removeComponentListener(componentListener); - componentListenerAdded = false; - } - componentListener = null; + getDesktopIconMover().uninstallListeners(); if (glassPaneDispatcher != null) { frame.getGlassPane().removeMouseListener(glassPaneDispatcher); frame.getGlassPane().removeMouseMotionListener(glassPaneDispatcher); @@ -1230,15 +1228,6 @@ public class BasicInternalFrameUI extends InternalFrameUI } } - // Relocate the icon base on the new parent bounds. - if (icon != null) { - Rectangle iconBounds = icon.getBounds(); - int y = iconBounds.y + - (parentNewBounds.height - parentBounds.height); - icon.setBounds(iconBounds.x, y, - iconBounds.width, iconBounds.height); - } - // Update the new parent bounds for next resize. if (!parentBounds.equals(parentNewBounds)) { parentBounds = parentNewBounds; @@ -1413,10 +1402,6 @@ public class BasicInternalFrameUI extends InternalFrameUI // Cancel a resize in progress if the internal frame // gets a setClosed(true) or dispose(). cancelResize(); - if ((frame.getParent() != null) && componentListenerAdded) { - frame.getParent().removeComponentListener( - componentListener); - } closeFrame(f); } } else if (JInternalFrame.IS_MAXIMUM_PROPERTY == prop) { @@ -1449,16 +1434,6 @@ public class BasicInternalFrameUI extends InternalFrameUI } else { parentBounds = null; } - if ((frame.getParent() != null) && !componentListenerAdded) { - f.getParent().addComponentListener(componentListener); - componentListenerAdded = true; - } else if ((newValue == null) && componentListenerAdded) { - if (f.getParent() != null) { - f.getParent().removeComponentListener( - componentListener); - } - componentListenerAdded = false; - } } else if (JInternalFrame.TITLE_PROPERTY == prop || prop == "closable" || prop == "iconable" || prop == "maximizable") { diff --git a/src/share/classes/javax/swing/plaf/basic/DesktopIconMover.java b/src/share/classes/javax/swing/plaf/basic/DesktopIconMover.java new file mode 100644 index 000000000..deff4f27a --- /dev/null +++ b/src/share/classes/javax/swing/plaf/basic/DesktopIconMover.java @@ -0,0 +1,168 @@ +/* + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.swing.plaf.basic; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.beans.*; + +/** + * DesktopIconMover is intended to move desktop icon + * when parent window is resized. + */ +class DesktopIconMover implements ComponentListener, PropertyChangeListener { + private Component parent; + private JInternalFrame frame; // if not null, DesktopIconMover(frame) + // constructor was used + private JInternalFrame.JDesktopIcon icon; + private Rectangle parentBounds; + private boolean componentListenerAdded = false; + + public DesktopIconMover(JInternalFrame frame) { + if (frame == null) { + throw new NullPointerException("Frame cannot be null"); + } + this.frame = frame; + this.icon = frame.getDesktopIcon(); + if (icon == null) { + throw new NullPointerException( + "frame.getDesktopIcon() cannot be null"); + } + this.parent = frame.getParent(); + if (this.parent != null) { + parentBounds = this.parent.getBounds(); + } + } + + public DesktopIconMover(JInternalFrame.JDesktopIcon icon) { + if (icon == null) { + throw new NullPointerException("Icon cannot be null"); + } + this.icon = icon; + this.parent = icon.getParent(); + if (this.parent != null) { + parentBounds = this.parent.getBounds(); + } + } + + public void installListeners() { + if (frame != null) { + frame.addPropertyChangeListener(this); + } else { + icon.addPropertyChangeListener(this); + } + addComponentListener(); + } + + public void uninstallListeners() { + if (frame != null) { + frame.removePropertyChangeListener(this); + } else { + icon.removePropertyChangeListener(this); + } + removeComponentListener(); + } + + public void propertyChange(PropertyChangeEvent evt) { + String propName = evt.getPropertyName(); + if ("ancestor".equals(propName)) { + Component newAncestor = (Component) evt.getNewValue(); + + // Remove component listener if parent is changing + Component probablyNewParent = getCurrentParent(); + if ((probablyNewParent != null) && + (!probablyNewParent.equals(parent))) { + removeComponentListener(); + parent = probablyNewParent; + } + + if (newAncestor == null) { + removeComponentListener(); + } else { + addComponentListener(); + } + + // Update parentBounds + if (parent != null) { + parentBounds = parent.getBounds(); + } else { + parentBounds = null; + } + } else if (JInternalFrame.IS_CLOSED_PROPERTY.equals(propName)) { + removeComponentListener(); + } + } + + private void addComponentListener() { + if (!componentListenerAdded && (parent != null)) { + parent.addComponentListener(this); + componentListenerAdded = true; + } + } + + private void removeComponentListener() { + if ((parent != null) && componentListenerAdded) { + parent.removeComponentListener(this); + componentListenerAdded = false; + } + } + + private Component getCurrentParent() { + if (frame != null) { + return frame.getParent(); + } else { + return icon.getParent(); + } + } + + public void componentResized(ComponentEvent e) { + if ((parent == null) || (parentBounds == null)) { + return; + } + + Rectangle parentNewBounds = parent.getBounds(); + if ((parentNewBounds == null) || parentNewBounds.equals(parentBounds)) { + return; + } + + // Move desktop icon only in up-down direction + int newIconY = icon.getLocation().y + + (parentNewBounds.height - parentBounds.height); + icon.setLocation(icon.getLocation().x, newIconY); + + parentBounds = parentNewBounds; + } + + public void componentMoved(ComponentEvent e) { + } + + public void componentShown(ComponentEvent e) { + } + + public void componentHidden(ComponentEvent e) { + } +} diff --git a/test/javax/swing/JInternalFrame/6647340/bug6647340.java b/test/javax/swing/JInternalFrame/6647340/bug6647340.java new file mode 100644 index 000000000..b4c180140 --- /dev/null +++ b/test/javax/swing/JInternalFrame/6647340/bug6647340.java @@ -0,0 +1,146 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 6647340 + * @summary Checks that iconified internal frame follows + * the main frame borders properly. + * @author Mikhail Lapshin + */ + +import sun.awt.SunToolkit; + +import javax.swing.*; +import java.awt.*; +import java.beans.PropertyVetoException; + +public class bug6647340 { + private JFrame frame; + private Point location; + private JInternalFrame jif; + + public static void main(String[] args) throws Exception { + final bug6647340 test = new bug6647340(); + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + test.setupUI(); + } + }); + test.test(); + } finally { + if (test.frame != null) { + test.frame.dispose(); + } + } + } + + private void setupUI() { + frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + JDesktopPane desktop = new JDesktopPane(); + frame.add(desktop); + + jif = new JInternalFrame("Internal Frame", true, true, true, true); + jif.setBounds(20, 20, 200, 100); + desktop.add(jif); + jif.setVisible(true); + + Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + frame.setBounds((screen.width - 400) / 2, (screen.height - 400) / 2, 400, 400); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private void test() throws Exception { + realSync(); + test1(); + realSync(); + check1(); + realSync(); + test2(); + realSync(); + check2(); + } + + private void test1() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + setIcon(true); + location = jif.getDesktopIcon().getLocation(); + Dimension size = frame.getSize(); + frame.setSize(size.width + 100, size.height + 100); + } + }); + } + + private void test2() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + setIcon(false); + } + }); + realSync(); + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + Dimension size = frame.getSize(); + frame.setSize(size.width - 100, size.height - 100); + } + }); + realSync(); + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + setIcon(true); + } + }); + } + + private void check1() { + if (!jif.getDesktopIcon().getLocation().equals(location)) { + System.out.println("First test passed"); + } else { + throw new RuntimeException("Icon isn't shifted with the frame bounds"); + } + } + + private void check2() { + if (jif.getDesktopIcon().getLocation().equals(location)) { + System.out.println("Second test passed"); + } else { + throw new RuntimeException("Icon isn't located near the frame bottom"); + } + } + + private static void realSync() { + ((SunToolkit) (Toolkit.getDefaultToolkit())).realSync(); + } + + private void setIcon(boolean b) { + try { + jif.setIcon(b); + } catch (PropertyVetoException e) { + e.printStackTrace(); + } + } +} -- GitLab From c684d359315aa5ea0525fe0da170d82b30c6478b Mon Sep 17 00:00:00 2001 From: peterz Date: Tue, 8 Jul 2008 11:36:19 +0400 Subject: [PATCH 007/139] 6635663: make/tools/AutoMulti/{AutoMulti,TestALFGenerator}.java still generate files with wrong legal notices Summary: Removed unused files Reviewed-by: ohair --- make/tools/Makefile | 1 - make/tools/auto_multi/Makefile | 43 -- .../src/build/tools/automulti/AutoMulti.java | 458 ------------------ .../src/build/tools/automulti/README.txt | 36 -- .../tools/automulti/TestALFGenerator.java | 401 --------------- .../tools/automulti/TestALFLookAndFeel.java | 182 ------- 6 files changed, 1121 deletions(-) delete mode 100644 make/tools/auto_multi/Makefile delete mode 100644 make/tools/src/build/tools/automulti/AutoMulti.java delete mode 100644 make/tools/src/build/tools/automulti/README.txt delete mode 100644 make/tools/src/build/tools/automulti/TestALFGenerator.java delete mode 100644 make/tools/src/build/tools/automulti/TestALFLookAndFeel.java diff --git a/make/tools/Makefile b/make/tools/Makefile index be1c59a7c..c396097c0 100644 --- a/make/tools/Makefile +++ b/make/tools/Makefile @@ -32,7 +32,6 @@ include $(BUILDDIR)/common/Defs.gmk SUBDIRS = \ addjsum \ - auto_multi \ buildmetaindex \ commentchecker \ compile_font_config \ diff --git a/make/tools/auto_multi/Makefile b/make/tools/auto_multi/Makefile deleted file mode 100644 index f5db35099..000000000 --- a/make/tools/auto_multi/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -# -# Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Sun designates this -# particular file as subject to the "Classpath" exception as provided -# by Sun in the LICENSE file that accompanied this code. -# -# This code 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 General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, -# CA 95054 USA or visit www.sun.com if you need additional information or -# have any questions. -# - -# -# Makefile for building the automulti tool -# - -BUILDDIR = ../.. -PACKAGE = build.tools.automulti -PRODUCT = tools -PROGRAM = automulti -include $(BUILDDIR)/common/Defs.gmk - -BUILDTOOL_SOURCE_ROOT = $(BUILDDIR)/tools/src -BUILDTOOL_MAIN = $(PKGDIR)/AutoMulti.java - -# -# Build tool jar rules. -# -include $(BUILDDIR)/common/BuildToolJar.gmk - diff --git a/make/tools/src/build/tools/automulti/AutoMulti.java b/make/tools/src/build/tools/automulti/AutoMulti.java deleted file mode 100644 index a59edc856..000000000 --- a/make/tools/src/build/tools/automulti/AutoMulti.java +++ /dev/null @@ -1,458 +0,0 @@ -/* - * Copyright 1998-2001 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -package build.tools.automulti; - -import java.lang.reflect.*; -import java.util.*; -import java.io.*; - -/** - * Automatically generates the Multiplexing UI classes - * for Swing. - *

- * To use, type 'java AutoMulti ' where - * is the directory containing the source for Swing's UI classes and - * is the package prefix to use before ".swing.plaf.multi". - * For example: - * - *

- * cd TEST
- * ../../../../build/solaris-sparc/bin/java AutoMulti ../../../../src/share/classes/javax/swing/plaf javax
- * 
- * - * AutoMulti will scour the plaf directory for *UI.java files and - * generate Multi*UI.java files that do the multiplexing thing. - *

- * NOTE: This tool depends upon the existence of and on the - * compiled classes from being somewhere in the class path. - * - * @author Willie Walker - */ -public class AutoMulti { - static String importLines; - - /** - * A silly list of parameter names to use. Skips "i" because we use - * it as a 'for' loop counter. If you want to get fancy, please feel - * to change how parameter names are obtained. This will break if - * someone decides to create a UI method that takes more than 8 - * parameters. Which one is a bug (this breaking or having a method - * with more than eight parameters) is a subjective thing. - */ - public static String[] paramNames = {"a","b","c","d","e","f","g","h"}; - - /** - * Removes the package names (e.g., javax.swing) from the name. - */ - public static String unqualifyName(String name) { - StringTokenizer parser = new StringTokenizer(name,"."); - String unqualifiedName = null; - while (parser.hasMoreTokens()) { - unqualifiedName = parser.nextToken(); - } - return removeDollars(unqualifiedName); - } - - /** - * Strips the extension from the filename. - */ - public static String stripExtension(String name) { - StringTokenizer parser = new StringTokenizer(name,"."); - return parser.nextToken(); - } - - /** - * Adds some spaces. - */ - public static void indent(StringBuffer s, int i) { - while (i > 0) { - s.append(" "); - i--; - } - } - - /** - * Spits out all the beginning stuff. - */ - public static StringBuffer createPreamble(String prefixName) { - StringBuffer s = new StringBuffer(); - s.append("/*\n"); - s.append(" *\n"); - s.append(" * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.\n"); - s.append(" * \n"); - s.append(" * This software is the proprietary information of Sun Microsystems, Inc. \n"); - s.append(" * Use is subject to license terms.\n"); - s.append(" * \n"); - s.append(" */\n"); - s.append("package " + prefixName + ".swing.plaf.multi;\n"); - s.append("\n"); - return s; - } - - /** - * Replaces 'Xxx$Yyy' with "Xxx'. Used by addImport because you - * can't import nested classes directly. - */ - public static String removeNestedClassName(String s) { - int dollarPosition = s.indexOf('$'); - - if (dollarPosition >= 0) { // s contains '$' - StringBuffer sb = new StringBuffer(s); - sb.setLength(dollarPosition); - return sb.toString(); - } else { // no '$' - return s; - } - } - - /** - * Replaces '$' with ".'. Needed for printing inner class names - * for argument and return types. - */ - public static String removeDollars(String s) { - int dollarPosition = s.indexOf('$'); - - if (dollarPosition >= 0) { // s contains '$' - StringBuffer sb = new StringBuffer(s); - while (dollarPosition >= 0) { - //XXX: will there ever be more than one '$'? - sb.replace(dollarPosition, dollarPosition+1, "."); - dollarPosition = sb.indexOf("$", dollarPosition); - } - return sb.toString(); - } else { // no $ - return s; - } - } - - /** - * Adds an import line to the String. - */ - public static void addImport(String s, Class theClass) { - if (!theClass.isPrimitive() && (theClass != Object.class)) { - String className = removeNestedClassName(theClass.getName()); - String importLine = new String("import " + className + ";\n"); - if (importLines.indexOf(importLine) == -1) { - importLines += importLine; - } - } - } - - /** - * Spits out the class header information. - */ - public static void addHeader(StringBuffer s, String className) { - s.append("/**\n"); - s.append(" * A multiplexing UI used to combine " + className + "s.\n"); - s.append(" * \n"); - s.append(" *

This file was automatically generated by AutoMulti.\n"); - s.append(" *\n"); - s.append(" * @author Otto Multey\n"); // Get it? I crack myself up. - s.append(" */\n"); - s.append("public class Multi" + className + " extends " + className + " {\n"); - s.append("\n"); - s.append(" /**\n"); - s.append(" * The vector containing the real UIs. This is populated \n"); - s.append(" * in the call to createUI, and can be obtained by calling\n"); - s.append(" * the getUIs method. The first element is guaranteed to be the real UI \n"); - s.append(" * obtained from the default look and feel.\n"); - s.append(" */\n"); - s.append(" protected Vector uis = new Vector();\n"); - s.append("\n"); - s.append("////////////////////\n"); - s.append("// Common UI methods\n"); - s.append("////////////////////\n"); - s.append("\n"); - s.append(" /**\n"); - s.append(" * Returns the list of UIs associated with this multiplexing UI. This \n"); - s.append(" * allows processing of the UIs by an application aware of multiplexing \n"); - s.append(" * UIs on components.\n"); - s.append(" */\n"); - s.append(" public ComponentUI[] getUIs() {\n"); - s.append(" return MultiLookAndFeel.uisToArray(uis);\n"); - s.append(" }\n"); - } - - /** - * Prints out the code for a method. This is pretty specific to the - * Multiplexing UI code, so don't get any fancy ideas. - */ - public static void addMethod(StringBuffer s, Method m, String origName, String className) { - - // Get the method name and the return type. Be a little careful about arrays. - // - String methodName = unqualifyName(m.getName()); - String returnType; - if (!m.getReturnType().isArray()) { - returnType = unqualifyName(m.getReturnType().toString()); - addImport(importLines,m.getReturnType()); - } else { - returnType = unqualifyName(m.getReturnType().getComponentType().toString()) - + "[]"; - addImport(importLines,m.getReturnType().getComponentType()); - } - - // Print the javadoc - // - s.append("\n"); - if (methodName.equals("createUI")) { - s.append(" /**\n"); - s.append(" * Returns a multiplexing UI instance if any of the auxiliary\n"); - s.append(" * LookAndFeels supports this UI. Otherwise, just returns the \n"); - s.append(" * UI object obtained from the default LookAndFeel.\n"); - s.append(" */\n"); - } else if (!returnType.equals("void")) { - s.append(" /**\n"); - s.append(" * Invokes the " + methodName + " method on each UI handled by this object.\n"); - s.append(" * \n"); - s.append(" * @return the value obtained from the first UI, which is\n"); - s.append(" * the UI obtained from the default LookAndFeel\n"); - s.append(" */\n"); - } else { - s.append(" /**\n"); - s.append(" * Invokes the " + methodName - + " method on each UI handled by this object.\n"); - s.append(" */\n"); - } - - // Print the method signature - // - s.append(" public"); - if (Modifier.isStatic(m.getModifiers())) { - s.append(" static"); - } - s.append(" " + returnType); - s.append(" " + methodName); - s.append("("); - - Class[] params = m.getParameterTypes(); - Class temp; - String braces; - for (int i = 0; i < params.length; i++) { - if (i > 0) { - s.append(", "); - } - temp = params[i]; - braces = new String(""); - while (temp.isArray()) { - braces += "[]"; - temp = temp.getComponentType(); - } - s.append(unqualifyName(temp.getName()) + braces + " " + paramNames[i]); - addImport(importLines,temp); - } - s.append(")"); - - // Don't forget about exceptions - // - Class exceptions[] = m.getExceptionTypes(); - String throwsString = new String(""); - - if (exceptions.length > 0) { - s.append("\n"); - indent(s,12); - s.append("throws "); - for (int i = 0; i < exceptions.length; i++) { - if (i > 0) { - s.append(", "); - } - s.append(unqualifyName(exceptions[i].getName())); - addImport(importLines,exceptions[i]); - } - } - s.append(throwsString + " {\n"); - - // Now print out the contents of the method. We do a special thing - // for the createUI method, another thing if the method returns 'void' - // and a third thing if we don't do either of the first two. If - // you want to squash this down, feel free. - // - if (methodName.equals("createUI")) { - indent(s,8); - s.append("ComponentUI mui = new Multi" + origName + "();\n"); - indent(s,8); - s.append("return MultiLookAndFeel.createUIs(mui,\n"); - indent(s,42); - s.append("((Multi" + origName +") mui).uis,\n"); - indent(s,42); - for (int i = 0; i < params.length; i++) { - if (i > 0) { - s.append(","); - } - s.append(paramNames[i]); - } - s.append(");\n"); - } else if (!returnType.equals("void")) { - indent(s,8); - s.append(returnType + " returnValue = \n"); - indent(s,12); - s.append("((" + className + ") (uis.elementAt(0)))." - + methodName + "("); - for (int i = 0; i < params.length; i++) { - if (i > 0) { - s.append(","); - } - s.append(paramNames[i]); - } - s.append(");\n"); - indent(s,8); - s.append("for (int i = 1; i < uis.size(); i++) {\n"); - indent(s,12); - s.append("((" + className + ") (uis.elementAt(i)))." - + methodName + "("); - for (int i = 0; i < params.length; i++) { - if (i > 0) { - s.append(","); - } - s.append(paramNames[i]); - } - s.append(");\n"); - indent(s,8); - s.append("}\n"); - indent(s,8); - s.append("return returnValue;\n"); - } else { - indent(s,8); - s.append("for (int i = 0; i < uis.size(); i++) {\n"); - indent(s,12); - s.append("((" + className + ") (uis.elementAt(i)))." - + methodName + "("); - for (int i = 0; i < params.length; i++) { - if (i > 0) { - s.append(","); - } - s.append(paramNames[i]); - } - s.append(");\n"); - indent(s,8); - s.append("}\n"); - } - indent(s,4); - s.append("}\n"); - } - - /** - * Takes a plaf class name (e.g., "MenuUI") and generates the corresponding - * Multiplexing UI Java source code (e.g., "MultiMenuUI.java"). - */ - public static void generateFile(String prefixName, String className) { - try { - FileOutputStream fos; - PrintWriter outFile; - - importLines = new String(); - importLines += new String("import java.util.Vector;\n"); - - StringBuffer body = new StringBuffer(); - Class wee = Class.forName(prefixName + ".swing.plaf." + className); - String weeName = unqualifyName(wee.getName()); - addImport(importLines,wee); - while (!weeName.equals("Object")) { - body.append("\n"); - body.append("////////////////////\n"); - body.append("// " + weeName + " methods\n"); - body.append("////////////////////\n"); - Method[] methods = wee.getDeclaredMethods(); - for (int i=0; i < methods.length; i++) { - if (Modifier.isPublic(methods[i].getModifiers())) { - addMethod(body,methods[i],className,weeName); - } - } - wee = wee.getSuperclass(); - weeName = unqualifyName(wee.getName()); - addImport(importLines,wee); - } - - fos = new FileOutputStream("Multi" + className + ".java"); - outFile = new PrintWriter(fos); - StringBuffer outText = createPreamble(prefixName); - outText.append(importLines.toString() + "\n"); - addHeader(outText,className); - outText.append(body.toString()); - outText.append("}\n"); - outFile.write(outText.toString()); - outFile.flush(); - outFile.close(); - } catch (Exception e) { - System.err.println(e); - } - } - - /** - * D'Oh! Something bad happened. - */ - public static void usage(String s) throws IOException { - System.err.println("Usage: AutoMulti [com.sun]"); - throw new IllegalArgumentException(s); - } - - /** - * Takes the plaf directory name and generates the multiplexing UI - * source code. - */ - public static void main(String[] args) throws IOException { - - if (args.length < 1) { - usage(""); - } - - String dirName = args[0]; - File dir = new File(dirName); - if (!dir.isDirectory()) { - System.err.println("No such directory: " + dirName); - usage(""); - } - - String prefixName; - if (args.length > 1) { - prefixName = args[1]; - } else { - prefixName = "com.sun.java"; - } - - String plafUIs[] = dir.list(new UIJavaFilter()); - for (int i = 0; i < plafUIs.length; i++) { - generateFile(prefixName,stripExtension(plafUIs[i])); - } - } -} - -/** - * Only accepts file names of the form *UI.java. The one exception - * is not accepting ComponentUI.java because we don't need to generate - * a multiplexing class for it. - */ -class UIJavaFilter implements FilenameFilter { - public boolean accept(File dir, String name) { - if (name.equals("ComponentUI.java")) { - return false; - } else if (name.endsWith("UI.java")) { - return true; - } else { - return false; - } - } -} diff --git a/make/tools/src/build/tools/automulti/README.txt b/make/tools/src/build/tools/automulti/README.txt deleted file mode 100644 index b5de5afe9..000000000 --- a/make/tools/src/build/tools/automulti/README.txt +++ /dev/null @@ -1,36 +0,0 @@ -AutoMulti is the tool that automatically generates the -Multi*UI classes for the Multiplexing look and feel. -Instructions for using it are in AutoMulti.java. - -TestALFGenerator is a tool (a variation of AutoMulti) -that automatically generates an auxiliary look and -feel that you can use to test the Multiplexing look -and feel. The TestALF look and feel implements every -method by printing the message "In the xxx method of -the TextALFYyyUI class." and, except in the case of -createUI, returning something meaningless (since, -except in the case of createUI, the return value is -ignored). - -TestALFLookAndFeel.java is the only non-auto-generated -file for the TestALF L&F. If you specify a package -argument to TestALFGenerator, you'll have to change -the code in TestALFLookAndFeel.java to reflect the -package name. - -To test any application with the TestALF, make sure the -compiled TestALF classes are in the class path. Then add -this to the /lib/swing.properties file (which -you'll probably have to create): - -swing.auxiliarylaf=TestALFLookAndFeel - -E.g., if you're running SwingSet2 against your solaris -build, then you'd create/edit the swing.properties file -in /build/solaris-sparc/lib. - -Then run any app. You'll see lots of thrilling "In the -Xxxx method of the Yyy class" messages. If you get anything -else (especially an exception), then you've found a bug. -Probably in the default look and feel. - diff --git a/make/tools/src/build/tools/automulti/TestALFGenerator.java b/make/tools/src/build/tools/automulti/TestALFGenerator.java deleted file mode 100644 index 9b07dbc40..000000000 --- a/make/tools/src/build/tools/automulti/TestALFGenerator.java +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -package build.tools.automulti; - -import java.lang.reflect.*; -import java.util.*; -import java.io.*; - -/** - * Automatically generates an auxiliary look and feel to be - * used for testing the Multiplexing look and feel. - *

- * To use, type 'java TestALFGenerator []' where - * is the directory containing the source for Swing's UI classes. - * is an optional argument that specifies the package - * of the TestALF classes. If it's omitted, the classes are in - * the default package. - * For example: - * - *

- * ../../../../build/solaris-sparc/bin/java TestALFGenerator ../../../../src/share/classes/javax/swing/plaf com.myco.myalaf
- * 
- * - * TestALFGenerator will scour the plaf directory for *UI.java files and - * generate TestALF*UI.java files. - *

- * NOTE: This tool depends upon the existence of and on the - * compiled classes from being somewhere in the class path. - * - * @author Willie Walker - */ -public class TestALFGenerator { - static String importLines; - static String packageName; - static String classPrefix = "TestALF"; - - /** - * A silly list of parameter names to use. Skips "i" because we use - * it as a 'for' loop counter. If you want to get fancy, please feel - * to change how parameter names are obtained. This will break if - * someone decides to create a UI method that takes more than 8 - * parameters. Which one is a bug (this breaking or having a method - * with more than eight parameters) is a subjective thing. - */ - public static String[] paramNames = {"a","b","c","d","e","f","g","h"}; - - /** - * Removes the package names (e.g., javax.swing) from the name. - */ - public static String unqualifyName(String name) { - StringTokenizer parser = new StringTokenizer(name,"."); - String unqualifiedName = null; - while (parser.hasMoreTokens()) { - unqualifiedName = parser.nextToken(); - } - return removeDollars(unqualifiedName); - } - - /** - * Strips the extension from the filename. - */ - public static String stripExtension(String name) { - StringTokenizer parser = new StringTokenizer(name,"."); - return parser.nextToken(); - } - - /** - * Adds some spaces. - */ - public static void indent(StringBuffer s, int i) { - while (i > 0) { - s.append(" "); - i--; - } - } - - /** - * Spits out all the beginning stuff. - */ - public static StringBuffer createPreamble(String prefixName) { - StringBuffer s = new StringBuffer(); - s.append("/*\n"); - s.append(" *\n"); - s.append(" * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.\n"); - s.append(" * \n"); - s.append(" * This software is the proprietary information of Sun Microsystems, Inc. \n"); - s.append(" * Use is subject to license terms.\n"); - s.append(" * \n"); - s.append(" */\n"); - if (packageName != null) { - s.append("package " + packageName + ";\n"); - s.append("\n"); - } - return s; - } - - /** - * Replaces 'Xxx$Yyy' with "Xxx'. Used by addImport because you - * can't import nested classes directly. - */ - public static String removeNestedClassName(String s) { - int dollarPosition = s.indexOf('$'); - - if (dollarPosition >= 0) { // s contains '$' - StringBuffer sb = new StringBuffer(s); - sb.setLength(dollarPosition); - return sb.toString(); - } else { // no '$' - return s; - } - } - - /** - * Replaces '$' with ".'. Needed for printing inner class names - * for argument and return types. - */ - public static String removeDollars(String s) { - int dollarPosition = s.indexOf('$'); - - if (dollarPosition >= 0) { // s contains '$' - StringBuffer sb = new StringBuffer(s); - while (dollarPosition >= 0) { - //XXX: will there ever be more than one '$'? - sb.replace(dollarPosition, dollarPosition+1, "."); - dollarPosition = sb.indexOf("$", dollarPosition); - } - return sb.toString(); - } else { // no $ - return s; - } - } - - /** - * Adds an import line to the String. - */ - public static void addImport(String s, Class theClass) { - if (!theClass.isPrimitive() && (theClass != Object.class)) { - String className = removeNestedClassName(theClass.getName()); - String importLine = new String("import " + className + ";\n"); - if (importLines.indexOf(importLine) == -1) { - importLines += importLine; - } - } - } - - /** - * Spits out the class header information. - */ - public static void addHeader(StringBuffer s, String className) { - s.append("/**\n"); - s.append(" * An auxiliary UI for " + className + "s.\n"); - s.append(" * \n"); - s.append(" *

This file was automatically generated by TestALFGenerator.\n"); - s.append(" *\n"); - s.append(" * @author Otto Multey\n"); // Get it? I crack myself up. - s.append(" */\n"); - s.append("public class " + classPrefix + className + " extends " + className + " {\n"); - s.append("\n"); - } - - /** - * Prints out the code for a method. - */ - public static void addMethod(StringBuffer s, Method m, String origName, String className) { - - // Get the method name and the return type. Be a little careful about arrays. - // - String methodName = unqualifyName(m.getName()); - String returnType; - - if (!m.getReturnType().isArray()) { - returnType = unqualifyName(m.getReturnType().toString()); - addImport(importLines,m.getReturnType()); - } else { - returnType = unqualifyName(m.getReturnType().getComponentType().toString()) - + "[]"; - addImport(importLines,m.getReturnType().getComponentType()); - } - - // Print the javadoc - // - s.append("\n"); - - if (methodName.equals("createUI")) { - s.append(" /**\n"); - s.append(" * Returns a UI object for this component.\n"); - s.append(" */\n"); - } else { - s.append(" /**\n"); - s.append(" * Prints a message saying this method has been invoked.\n"); - s.append(" */\n"); - } - - // Print the method signature - // - s.append(" public"); - if (Modifier.isStatic(m.getModifiers())) { - s.append(" static"); - } - s.append(" " + returnType); - s.append(" " + methodName); - s.append("("); - - Class[] params = m.getParameterTypes(); - Class temp; - String braces; - for (int i = 0; i < params.length; i++) { - if (i > 0) { - s.append(", "); - } - temp = params[i]; - braces = new String(""); - while (temp.isArray()) { - braces += "[]"; - temp = temp.getComponentType(); - } - s.append(unqualifyName(temp.getName()) + braces + " " + paramNames[i]); - addImport(importLines,temp); - } - s.append(")"); - - // Don't forget about exceptions - // - Class exceptions[] = m.getExceptionTypes(); - String throwsString = new String(""); - - if (exceptions.length > 0) { - s.append("\n"); - indent(s,12); - s.append("throws "); - for (int i = 0; i < exceptions.length; i++) { - if (i > 0) { - s.append(", "); - } - s.append(unqualifyName(exceptions[i].getName())); - addImport(importLines,exceptions[i]); - } - } - s.append(throwsString + " {\n"); - - // Now print out the contents of the method. - indent(s,8); - s.append("System.out.println(\"In the " + methodName - + " method of the " - + classPrefix + origName + " class.\");\n"); - if (methodName.equals("createUI")) { - indent(s,8); - s.append("return ui;\n"); - } else { - // If we have to return something, do so. - if (!returnType.equals("void")) { - Class rType = m.getReturnType(); - indent(s,8); - if (!rType.isPrimitive()) { - s.append("return null;\n"); - } else if (rType == Boolean.TYPE) { - s.append("return false;\n"); - } else if (rType == Character.TYPE) { - s.append("return '0';\n"); - } else { // byte, short, int, long, float, or double - s.append("return 0;\n"); - } - } - } - - indent(s,4); - s.append("}\n"); - } - - /** - * Takes a plaf class name (e.g., "MenuUI") and generates the corresponding - * TestALF UI Java source code (e.g., "TestALFMenuUI.java"). - */ - public static void generateFile(String prefixName, String className) { - try { - FileOutputStream fos; - PrintWriter outFile; - - importLines = new String(); - importLines += new String("import java.util.Vector;\n"); - - StringBuffer body = new StringBuffer(); - Class wee = Class.forName(prefixName + ".swing.plaf." + className); - String weeName = unqualifyName(wee.getName()); - String thisClassName = classPrefix + className; - addImport(importLines,wee); - - // Declare and initialize the shared UI object. - body.append("\n"); - body.append("////////////////////\n"); - body.append("// Shared UI object\n"); - body.append("////////////////////\n"); - body.append("private final static " + thisClassName - + " ui = new " + thisClassName + "();\n"); - - while (!weeName.equals("Object")) { - body.append("\n"); - body.append("////////////////////\n"); - body.append("// " + weeName + " methods\n"); - body.append("////////////////////\n"); - Method[] methods = wee.getDeclaredMethods(); - for (int i=0; i < methods.length; i++) { - if (Modifier.isPublic(methods[i].getModifiers())) { - addMethod(body,methods[i],className,weeName); - } - } - wee = wee.getSuperclass(); - weeName = unqualifyName(wee.getName()); - addImport(importLines,wee); - } - - fos = new FileOutputStream(classPrefix + className + ".java"); - outFile = new PrintWriter(fos); - StringBuffer outText = createPreamble(prefixName); - outText.append(importLines.toString() + "\n"); - addHeader(outText,className); - outText.append(body.toString()); - outText.append("}\n"); - outFile.write(outText.toString()); - outFile.flush(); - outFile.close(); - } catch (Exception e) { - System.err.println(e); - } - } - - /** - * D'Oh! Something bad happened. - */ - public static void usage(String s) throws IOException { - System.err.println("Usage: java TestALFGenerator []"); - throw new IllegalArgumentException(s); - } - - /** - * Takes the plaf directory name and generates the TestALF UI - * source code. - */ - public static void main(String[] args) throws IOException { - - if (args.length < 1) { - usage(""); - } - - String dirName = args[0]; - File dir = new File(dirName); - if (!dir.isDirectory()) { - System.err.println("No such directory: " + dirName); - usage(""); - } - - if (args.length > 1) { - packageName = args[1]; - } - - String plafUIs[] = dir.list(new UIJavaFilter()); - for (int i = 0; i < plafUIs.length; i++) { - generateFile("javax",stripExtension(plafUIs[i])); - } - } -} - -/** - * Only accepts file names of the form *UI.java. The one exception - * is not accepting ComponentUI.java because we don't need to generate - * a TestALF class for it. - */ -class UIJavaFilter implements FilenameFilter { - public boolean accept(File dir, String name) { - if (name.equals("ComponentUI.java")) { - return false; - } else if (name.endsWith("UI.java")) { - return true; - } else { - return false; - } - } -} diff --git a/make/tools/src/build/tools/automulti/TestALFLookAndFeel.java b/make/tools/src/build/tools/automulti/TestALFLookAndFeel.java deleted file mode 100644 index aadab48ca..000000000 --- a/make/tools/src/build/tools/automulti/TestALFLookAndFeel.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ -//package com.myco.myalaf; //search for myalaf for other refs to package name - - -package build.tools.automulti; - -import java.util.Vector; -import java.lang.reflect.Method; -import javax.swing.*; -import javax.swing.plaf.*; - -/** - *

An auxiliary look and feel used for testing the Multiplexing - * look and feel. - *

- * - * @see UIManager#addAuxiliaryLookAndFeel - * @see javax.swing.plaf.multi - * - * @author Kathy Walrath - * @author Will Walker - */ -public class TestALFLookAndFeel extends LookAndFeel { - -////////////////////////////// -// LookAndFeel methods -////////////////////////////// - - /** - * Returns a string, suitable for use in menus, - * that identifies this look and feel. - * - * @return a string such as "Test Auxiliary Look and Feel" - */ - public String getName() { - return "Test Auxiliary Look and Feel"; - } - - /** - * Returns a string, suitable for use by applications/services, - * that identifies this look and feel. - * - * @return "TestALF" - */ - public String getID() { - return "TestALF"; - } - - /** - * Returns a one-line description of this look and feel. - * - * @return a descriptive string such as "Allows multiple UI instances per component instance" - */ - public String getDescription() { - return "Allows multiple UI instances per component instance"; - } - - /** - * Returns false; - * this look and feel is not native to any platform. - * - * @return false - */ - public boolean isNativeLookAndFeel() { - return false; - } - - /** - * Returns true; - * every platform permits this look and feel. - * - * @return true - */ - public boolean isSupportedLookAndFeel() { - return true; - } - - /** - * Creates, initializes, and returns - * the look and feel specific defaults. - * For this look and feel, - * the defaults consist solely of - * mappings of UI class IDs - * (such as "ButtonUI") - * to ComponentUI class names - * (such as "com.myco.myalaf.MultiButtonUI"). - * - * @return an initialized UIDefaults object - * @see javax.swing.JComponent#getUIClassID - */ - public UIDefaults getDefaults() { - System.out.println("In the TestALFLookAndFeel getDefaults method."); - UIDefaults table = new TestALFUIDefaults(); - //String prefix = "com.myco.myalaf.TestALF"; - String prefix = "TestALF"; - Object[] uiDefaults = { - "ButtonUI", prefix + "ButtonUI", - "CheckBoxMenuItemUI", prefix + "MenuItemUI", - "CheckBoxUI", prefix + "ButtonUI", - "ColorChooserUI", prefix + "ColorChooserUI", - "ComboBoxUI", prefix + "ComboBoxUI", - "DesktopIconUI", prefix + "DesktopIconUI", - "DesktopPaneUI", prefix + "DesktopPaneUI", - "EditorPaneUI", prefix + "TextUI", - "FileChooserUI", prefix + "FileChooserUI", - "FormattedTextFieldUI", prefix + "TextUI", - "InternalFrameUI", prefix + "InternalFrameUI", - "LabelUI", prefix + "LabelUI", - "ListUI", prefix + "ListUI", - "MenuBarUI", prefix + "MenuBarUI", - "MenuItemUI", prefix + "MenuItemUI", - "MenuUI", prefix + "MenuItemUI", - "OptionPaneUI", prefix + "OptionPaneUI", - "PanelUI", prefix + "PanelUI", - "PasswordFieldUI", prefix + "TextUI", - "PopupMenuSeparatorUI", prefix + "SeparatorUI", - "PopupMenuUI", prefix + "PopupMenuUI", - "ProgressBarUI", prefix + "ProgressBarUI", - "RadioButtonMenuItemUI", prefix + "MenuItemUI", - "RadioButtonUI", prefix + "ButtonUI", - "RootPaneUI", prefix + "RootPaneUI", - "ScrollBarUI", prefix + "ScrollBarUI", - "ScrollPaneUI", prefix + "ScrollPaneUI", - "SeparatorUI", prefix + "SeparatorUI", - "SliderUI", prefix + "SliderUI", - "SpinnerUI", prefix + "SpinnerUI", - "SplitPaneUI", prefix + "SplitPaneUI", - "TabbedPaneUI", prefix + "TabbedPaneUI", - "TableHeaderUI", prefix + "TableHeaderUI", - "TableUI", prefix + "TableUI", - "TextAreaUI", prefix + "TextUI", - "TextFieldUI", prefix + "TextUI", - "TextPaneUI", prefix + "TextUI", - "ToggleButtonUI", prefix + "ButtonUI", - "ToolBarSeparatorUI", prefix + "SeparatorUI", - "ToolBarUI", prefix + "ToolBarUI", - "ToolTipUI", prefix + "ToolTipUI", - "TreeUI", prefix + "TreeUI", - "ViewportUI", prefix + "ViewportUI", - }; - - table.putDefaults(uiDefaults); - return table; - } - -} - -/** - * We want the Test auxiliary look and feel to be quiet and fallback - * gracefully if it cannot find a UI. This class overrides the - * getUIError method of UIDefaults, which is the method that - * emits error messages when it cannot find a UI class in the - * LAF. - */ -class TestALFUIDefaults extends UIDefaults { - protected void getUIError(String msg) { - System.err.println("Test auxiliary L&F: " + msg); - } -} -- GitLab From 961b7f160764421bdb959f4d5f0790cf93dc62d8 Mon Sep 17 00:00:00 2001 From: malenkov Date: Tue, 8 Jul 2008 16:40:38 +0400 Subject: [PATCH 008/139] 4916852: RFE: LTP: BorderLayout Persistence Delegate should use 1.5 API Reviewed-by: peterz, loneid --- src/share/classes/java/beans/MetaData.java | 54 +++++++----- .../XMLEncoder/java_awt_BorderLayout.java | 82 +++++++++++++++++++ .../beans/XMLEncoder/java_awt_Component.java | 58 +++++++++++++ 3 files changed, 172 insertions(+), 22 deletions(-) create mode 100644 test/java/beans/XMLEncoder/java_awt_BorderLayout.java create mode 100644 test/java/beans/XMLEncoder/java_awt_Component.java diff --git a/src/share/classes/java/beans/MetaData.java b/src/share/classes/java/beans/MetaData.java index 9bcd505c2..e2cb0326c 100644 --- a/src/share/classes/java/beans/MetaData.java +++ b/src/share/classes/java/beans/MetaData.java @@ -986,14 +986,20 @@ class java_awt_Component_PersistenceDelegate extends DefaultPersistenceDelegate // null to defined values after the Windows are made visible - // special case them for now. if (!(oldInstance instanceof java.awt.Window)) { - String[] fieldNames = new String[]{"background", "foreground", "font"}; - for(int i = 0; i < fieldNames.length; i++) { - String name = fieldNames[i]; - Object oldValue = ReflectionUtils.getPrivateField(oldInstance, java.awt.Component.class, name, out.getExceptionListener()); - Object newValue = (newInstance == null) ? null : ReflectionUtils.getPrivateField(newInstance, java.awt.Component.class, name, out.getExceptionListener()); - if (oldValue != null && !oldValue.equals(newValue)) { - invokeStatement(oldInstance, "set" + NameGenerator.capitalize(name), new Object[]{oldValue}, out); - } + Object oldBackground = c.isBackgroundSet() ? c.getBackground() : null; + Object newBackground = c2.isBackgroundSet() ? c2.getBackground() : null; + if (!MetaData.equals(oldBackground, newBackground)) { + invokeStatement(oldInstance, "setBackground", new Object[] { oldBackground }, out); + } + Object oldForeground = c.isForegroundSet() ? c.getForeground() : null; + Object newForeground = c2.isForegroundSet() ? c2.getForeground() : null; + if (!MetaData.equals(oldForeground, newForeground)) { + invokeStatement(oldInstance, "setForeground", new Object[] { oldForeground }, out); + } + Object oldFont = c.isFontSet() ? c.getFont() : null; + Object newFont = c2.isFontSet() ? c2.getFont() : null; + if (!MetaData.equals(oldFont, newFont)) { + invokeStatement(oldInstance, "setFont", new Object[] { oldFont }, out); } } @@ -1104,26 +1110,30 @@ class java_awt_List_PersistenceDelegate extends DefaultPersistenceDelegate { // BorderLayout class java_awt_BorderLayout_PersistenceDelegate extends DefaultPersistenceDelegate { + private static final String[] CONSTRAINTS = { + BorderLayout.NORTH, + BorderLayout.SOUTH, + BorderLayout.EAST, + BorderLayout.WEST, + BorderLayout.CENTER, + BorderLayout.PAGE_START, + BorderLayout.PAGE_END, + BorderLayout.LINE_START, + BorderLayout.LINE_END, + }; + @Override protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) { super.initialize(type, oldInstance, newInstance, out); - String[] locations = {"north", "south", "east", "west", "center"}; - String[] names = {java.awt.BorderLayout.NORTH, java.awt.BorderLayout.SOUTH, - java.awt.BorderLayout.EAST, java.awt.BorderLayout.WEST, - java.awt.BorderLayout.CENTER}; - for(int i = 0; i < locations.length; i++) { - Object oldC = ReflectionUtils.getPrivateField(oldInstance, - java.awt.BorderLayout.class, - locations[i], - out.getExceptionListener()); - Object newC = ReflectionUtils.getPrivateField(newInstance, - java.awt.BorderLayout.class, - locations[i], - out.getExceptionListener()); + BorderLayout oldLayout = (BorderLayout) oldInstance; + BorderLayout newLayout = (BorderLayout) newInstance; + for (String constraints : CONSTRAINTS) { + Object oldC = oldLayout.getLayoutComponent(constraints); + Object newC = newLayout.getLayoutComponent(constraints); // Pending, assume any existing elements are OK. if (oldC != null && newC == null) { invokeStatement(oldInstance, "addLayoutComponent", - new Object[]{oldC, names[i]}, out); + new Object[] { oldC, constraints }, out); } } } diff --git a/test/java/beans/XMLEncoder/java_awt_BorderLayout.java b/test/java/beans/XMLEncoder/java_awt_BorderLayout.java new file mode 100644 index 000000000..1eba0c21b --- /dev/null +++ b/test/java/beans/XMLEncoder/java_awt_BorderLayout.java @@ -0,0 +1,82 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4916852 + * @summary Tests BorderLayout encoding + * @author Sergey Malenkov + */ + +import java.awt.BorderLayout; +import javax.swing.JLabel; + +public final class java_awt_BorderLayout extends AbstractTest { + private static final String[] CONSTRAINTS = { + BorderLayout.NORTH, + BorderLayout.SOUTH, + BorderLayout.EAST, + BorderLayout.WEST, + BorderLayout.CENTER, + BorderLayout.PAGE_START, + BorderLayout.PAGE_END, + BorderLayout.LINE_START, + BorderLayout.LINE_END, + }; + + public static void main(String[] args) { + new java_awt_BorderLayout().test(true); + } + + @Override + protected BorderLayout getObject() { + BorderLayout layout = new BorderLayout(); + update(layout, BorderLayout.EAST); + update(layout, BorderLayout.WEST); + update(layout, BorderLayout.NORTH); + update(layout, BorderLayout.SOUTH); + return layout; + } + + @Override + protected BorderLayout getAnotherObject() { + BorderLayout layout = getObject(); + update(layout, BorderLayout.CENTER); + return layout; + } + + @Override + protected void validate(BorderLayout before, BorderLayout after) { + super.validate(before, after); + + BeanValidator validator = new BeanValidator(); + for (String constraint : CONSTRAINTS) { + validator.validate(before.getLayoutComponent(constraint), + after.getLayoutComponent(constraint)); + } + } + + private static void update(BorderLayout layout, String constraints) { + layout.addLayoutComponent(new JLabel(constraints), constraints); + } +} diff --git a/test/java/beans/XMLEncoder/java_awt_Component.java b/test/java/beans/XMLEncoder/java_awt_Component.java new file mode 100644 index 000000000..a98424a3b --- /dev/null +++ b/test/java/beans/XMLEncoder/java_awt_Component.java @@ -0,0 +1,58 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4916852 + * @summary Tests Component encoding (background, foreground and font) + * @author Sergey Malenkov + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; + +public final class java_awt_Component extends AbstractTest { + public static void main(String[] args) { + new java_awt_Component().test(true); + } + + @Override + protected Component getObject() { + Component component = new MyComponent(); + component.setBackground(Color.WHITE); + component.setFont(new Font(null, Font.BOLD, 5)); + return component; + } + + @Override + protected Component getAnotherObject() { + Component component = new MyComponent(); + component.setForeground(Color.BLACK); + component.setFont(new Font(null, Font.ITALIC, 6)); + return component; + } + + public static final class MyComponent extends Component { + } +} \ No newline at end of file -- GitLab From 4665043f9b2c024564461c649bf78837126b7138 Mon Sep 17 00:00:00 2001 From: malenkov Date: Wed, 9 Jul 2008 15:25:38 +0400 Subject: [PATCH 009/139] 6351692: catch(Throwable) in java.beans.MetaData preventing thread shutdown Reviewed-by: peterz, loneid --- src/share/classes/java/beans/DefaultPersistenceDelegate.java | 2 +- src/share/classes/java/beans/EventHandler.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/share/classes/java/beans/DefaultPersistenceDelegate.java b/src/share/classes/java/beans/DefaultPersistenceDelegate.java index 6553a5172..5339aba83 100644 --- a/src/share/classes/java/beans/DefaultPersistenceDelegate.java +++ b/src/share/classes/java/beans/DefaultPersistenceDelegate.java @@ -298,7 +298,7 @@ public class DefaultPersistenceDelegate extends PersistenceDelegate { oldL = (EventListener[])MethodUtil.invoke(m, oldInstance, new Object[]{}); newL = (EventListener[])MethodUtil.invoke(m, newInstance, new Object[]{}); } - catch (Throwable e2) { + catch (Exception e2) { try { Method m = type.getMethod("getListeners", new Class[]{Class.class}); oldL = (EventListener[])MethodUtil.invoke(m, oldInstance, new Object[]{listenerType}); diff --git a/src/share/classes/java/beans/EventHandler.java b/src/share/classes/java/beans/EventHandler.java index 9d50dea91..43cd76ff3 100644 --- a/src/share/classes/java/beans/EventHandler.java +++ b/src/share/classes/java/beans/EventHandler.java @@ -404,7 +404,7 @@ public class EventHandler implements InvocationHandler { Object newTarget = MethodUtil.invoke(getter, target, new Object[]{}); return applyGetters(newTarget, rest); } - catch (Throwable e) { + catch (Exception e) { throw new RuntimeException("Failed to call method: " + first + " on " + target, e); } -- GitLab From b8445e0c9336b615ca28eafd3086eb5deb83f29d Mon Sep 17 00:00:00 2001 From: malenkov Date: Wed, 9 Jul 2008 19:29:07 +0400 Subject: [PATCH 010/139] 4994637: LTP: java.beans.java_util_Map_PersistenceDelegate: ConcurrentModificationException Reviewed-by: peterz, loneid --- src/share/classes/java/beans/MetaData.java | 2 +- test/java/beans/XMLEncoder/Test4994637.java | 58 +++++++++++++++++++ .../beans/XMLEncoder/java_util_HashMap.java | 24 ++++++-- 3 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 test/java/beans/XMLEncoder/Test4994637.java diff --git a/src/share/classes/java/beans/MetaData.java b/src/share/classes/java/beans/MetaData.java index e2cb0326c..5d8fd6979 100644 --- a/src/share/classes/java/beans/MetaData.java +++ b/src/share/classes/java/beans/MetaData.java @@ -650,7 +650,7 @@ class java_util_Map_PersistenceDelegate extends DefaultPersistenceDelegate { // Remove the new elements. // Do this first otherwise we undo the adding work. if (newMap != null) { - for ( Object newKey : newMap.keySet() ) { + for (Object newKey : newMap.keySet().toArray()) { // PENDING: This "key" is not in the right environment. if (!oldMap.containsKey(newKey)) { invokeStatement(oldInstance, "remove", new Object[]{newKey}, out); diff --git a/test/java/beans/XMLEncoder/Test4994637.java b/test/java/beans/XMLEncoder/Test4994637.java new file mode 100644 index 000000000..5476a8100 --- /dev/null +++ b/test/java/beans/XMLEncoder/Test4994637.java @@ -0,0 +1,58 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4994637 + * @summary Tests custom map encoding + * @author Sergey Malenkov + */ + +import java.util.HashMap; + +public final class Test4994637 extends AbstractTest { + public static void main(String[] args) { + new Test4994637().test(true); + } + + @Override + protected CustomMap getObject() { + return new CustomMap(); + } + + @Override + protected CustomMap getAnotherObject() { + CustomMap map = new CustomMap(); + map.clear(); + map.put(null, "zero"); + return map; + } + + public static final class CustomMap extends HashMap { + public CustomMap() { + put("1", "one"); + put("2", "two"); + put("3", "three"); + } + } +} diff --git a/test/java/beans/XMLEncoder/java_util_HashMap.java b/test/java/beans/XMLEncoder/java_util_HashMap.java index 9a9771f80..d9058003e 100644 --- a/test/java/beans/XMLEncoder/java_util_HashMap.java +++ b/test/java/beans/XMLEncoder/java_util_HashMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2006-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 4631471 4921212 + * @bug 4631471 4921212 4994637 * @summary Tests HashMap encoding * @author Sergey Malenkov */ @@ -36,10 +36,17 @@ public final class java_util_HashMap extends AbstractTest> { new java_util_HashMap().test(true); } + @Override protected Map getObject() { - return new HashMap(); + Map map = new HashMap(); + map.put(null, null); + map.put("key", "value"); + map.put("key-null", "null-value"); + map.put("way", "remove"); + return map; } + @Override protected Map getAnotherObject() { Map map = new HashMap(); map.put(null, "null-value"); @@ -48,6 +55,7 @@ public final class java_util_HashMap extends AbstractTest> { return map; } + @Override protected void validate(Map before, Map after) { super.validate(before, after); validate(before); @@ -55,10 +63,18 @@ public final class java_util_HashMap extends AbstractTest> { } private static void validate(Map map) { - if (!map.isEmpty()) { + switch (map.size()) { + case 3: validate(map, null, "null-value"); validate(map, "key", "value"); validate(map, "key-null", null); + break; + case 4: + validate(map, null, null); + validate(map, "key", "value"); + validate(map, "key-null", "null-value"); + validate(map, "way", "remove"); + break; } } -- GitLab From 5065850de1ca5bbe661e2e455bf8fef93b744ed1 Mon Sep 17 00:00:00 2001 From: malenkov Date: Fri, 18 Jul 2008 18:26:22 +0400 Subject: [PATCH 011/139] 6552812: Add HSL tab to JColorChooser Reviewed-by: peterz, avu --- .../plaf/basic/resources/basic.properties | 33 +- .../plaf/basic/resources/basic_de.properties | 33 +- .../plaf/basic/resources/basic_es.properties | 33 +- .../plaf/basic/resources/basic_fr.properties | 33 +- .../plaf/basic/resources/basic_it.properties | 33 +- .../plaf/basic/resources/basic_ja.properties | 33 +- .../plaf/basic/resources/basic_ko.properties | 33 +- .../plaf/basic/resources/basic_sv.properties | 33 +- .../basic/resources/basic_zh_CN.properties | 33 +- .../basic/resources/basic_zh_TW.properties | 33 +- .../ColorChooserComponentFactory.java | 19 +- .../swing/colorchooser/ColorChooserPanel.java | 182 ++++ .../javax/swing/colorchooser/ColorModel.java | 102 +++ .../swing/colorchooser/ColorModelCMYK.java | 94 ++ .../swing/colorchooser/ColorModelHSL.java | 188 ++++ .../swing/colorchooser/ColorModelHSV.java | 138 +++ .../javax/swing/colorchooser/ColorPanel.java | 210 +++++ .../colorchooser/DefaultHSBChooserPanel.java | 801 ------------------ .../colorchooser/DefaultRGBChooserPanel.java | 294 ------- .../swing/colorchooser/DiagramComponent.java | 160 ++++ .../swing/colorchooser/SlidingSpinner.java | 118 +++ .../swing/colorchooser/SyntheticImage.java | 166 ---- .../swing/colorchooser/ValueFormatter.java | 151 ++++ .../swing/plaf/basic/BasicColorChooserUI.java | 6 +- .../swing/JColorChooser/Test6524757.java | 168 ++-- .../swing/JColorChooser/Test6559154.java | 75 ++ 26 files changed, 1727 insertions(+), 1475 deletions(-) create mode 100644 src/share/classes/javax/swing/colorchooser/ColorChooserPanel.java create mode 100644 src/share/classes/javax/swing/colorchooser/ColorModel.java create mode 100644 src/share/classes/javax/swing/colorchooser/ColorModelCMYK.java create mode 100644 src/share/classes/javax/swing/colorchooser/ColorModelHSL.java create mode 100644 src/share/classes/javax/swing/colorchooser/ColorModelHSV.java create mode 100644 src/share/classes/javax/swing/colorchooser/ColorPanel.java delete mode 100644 src/share/classes/javax/swing/colorchooser/DefaultHSBChooserPanel.java delete mode 100644 src/share/classes/javax/swing/colorchooser/DefaultRGBChooserPanel.java create mode 100644 src/share/classes/javax/swing/colorchooser/DiagramComponent.java create mode 100644 src/share/classes/javax/swing/colorchooser/SlidingSpinner.java delete mode 100644 src/share/classes/javax/swing/colorchooser/SyntheticImage.java create mode 100644 src/share/classes/javax/swing/colorchooser/ValueFormatter.java create mode 100644 test/javax/swing/JColorChooser/Test6559154.java diff --git a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic.properties b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic.properties index 6087def36..4dac249bf 100644 --- a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic.properties +++ b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic.properties @@ -101,30 +101,41 @@ ColorChooser.resetMnemonic=82 ColorChooser.sampleText=Sample Text Sample Text ColorChooser.swatchesNameText=Swatches ColorChooser.swatchesMnemonic=83 -ColorChooser.swatchesDisplayedMnemonicIndex=0 ColorChooser.swatchesRecentText=Recent: -ColorChooser.hsbNameText=HSB # Each of the ColorChooser types can define a mnemonic, as a KeyEvent.VK_XXX # constant, and an index into the text to render the mnemonic as. The # mnemonic is xxxMnemonic and the index of the character to underline is # xxxDisplayedMnemonicIndex. -ColorChooser.hsbMnemonic=72 -ColorChooser.hsbDisplayedMnemonicIndex=0 -ColorChooser.hsbHueText=H -ColorChooser.hsbSaturationText=S -ColorChooser.hsbBrightnessText=B -ColorChooser.hsbRedText=R -ColorChooser.hsbGreenText=G -ColorChooser.hsbBlueText=B +ColorChooser.hsvNameText=HSV +ColorChooser.hsvMnemonic=72 +ColorChooser.hsvHueText=Hue +ColorChooser.hsvSaturationText=Saturation +ColorChooser.hsvValueText=Value +ColorChooser.hsvTransparencyText=Transparency +ColorChooser.hslNameText=HSL +ColorChooser.hslMnemonic=76 +ColorChooser.hslHueText=Hue +ColorChooser.hslSaturationText=Saturation +ColorChooser.hslLightnessText=Lightness +ColorChooser.hslTransparencyText=Transparency ColorChooser.rgbNameText=RGB ColorChooser.rgbMnemonic=71 -ColorChooser.rgbDisplayedMnemonicIndex=1 ColorChooser.rgbRedText=Red ColorChooser.rgbRedMnemonic=68 ColorChooser.rgbGreenText=Green ColorChooser.rgbGreenMnemonic=78 ColorChooser.rgbBlueText=Blue ColorChooser.rgbBlueMnemonic=66 +ColorChooser.rgbAlphaText=Alpha +ColorChooser.rgbHexCodeText=Color Code +ColorChooser.rgbHexCodeMnemonic=67 +ColorChooser.cmykNameText=CMYK +ColorChooser.cmykMnemonic=77 +ColorChooser.cmykCyanText=Cyan +ColorChooser.cmykMagentaText=Magenta +ColorChooser.cmykYellowText=Yellow +ColorChooser.cmykBlackText=Black +ColorChooser.cmykAlphaText=Alpha ############ OPTION PANE STRINGS ############# # Mnemonic keys correspond to KeyEvent.VK_XXX constant diff --git a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_de.properties b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_de.properties index 936605480..b34559777 100644 --- a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_de.properties +++ b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_de.properties @@ -101,30 +101,41 @@ ColorChooser.resetMnemonic=90 ColorChooser.sampleText=Beispieltext Beispieltext ColorChooser.swatchesNameText=Muster ColorChooser.swatchesMnemonic=77 -ColorChooser.swatchesDisplayedMnemonicIndex=0 ColorChooser.swatchesRecentText=Aktuell: -ColorChooser.hsbNameText=HSB # Each of the ColorChooser types can define a mnemonic, as a KeyEvent.VK_XXX # constant, and an index into the text to render the mnemonic as. The # mnemonic is xxxMnemonic and the index of the character to underline is # xxxDisplayedMnemonicIndex. -ColorChooser.hsbMnemonic=72 -ColorChooser.hsbDisplayedMnemonicIndex=0 -ColorChooser.hsbHueText=H -ColorChooser.hsbSaturationText=S -ColorChooser.hsbBrightnessText=B -ColorChooser.hsbRedText=R -ColorChooser.hsbGreenText=G -ColorChooser.hsbBlueText=B +ColorChooser.hsvNameText=HSV +ColorChooser.hsvMnemonic=72 +ColorChooser.hsvHueText=Hue +ColorChooser.hsvSaturationText=Saturation +ColorChooser.hsvValueText=Value +ColorChooser.hsvTransparencyText=Transparency +ColorChooser.hslNameText=HSL +ColorChooser.hslMnemonic=76 +ColorChooser.hslHueText=Hue +ColorChooser.hslSaturationText=Saturation +ColorChooser.hslLightnessText=Lightness +ColorChooser.hslTransparencyText=Transparency ColorChooser.rgbNameText=RGB ColorChooser.rgbMnemonic=71 -ColorChooser.rgbDisplayedMnemonicIndex=1 ColorChooser.rgbRedText=Rot ColorChooser.rgbRedMnemonic=82 ColorChooser.rgbGreenText=Gr\u00fcn ColorChooser.rgbGreenMnemonic=78 ColorChooser.rgbBlueText=Blau ColorChooser.rgbBlueMnemonic=66 +ColorChooser.rgbAlphaText=Alpha +ColorChooser.rgbHexCodeText=Color Code +ColorChooser.rgbHexCodeMnemonic=67 +ColorChooser.cmykNameText=CMYK +ColorChooser.cmykMnemonic=77 +ColorChooser.cmykCyanText=Cyan +ColorChooser.cmykMagentaText=Magenta +ColorChooser.cmykYellowText=Yellow +ColorChooser.cmykBlackText=Black +ColorChooser.cmykAlphaText=Alpha ############ OPTION PANE STRINGS ############# # Mnemonic keys correspond to KeyEvent.VK_XXX constant diff --git a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_es.properties b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_es.properties index b553b26f0..75877dd65 100644 --- a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_es.properties +++ b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_es.properties @@ -101,30 +101,41 @@ ColorChooser.resetMnemonic=82 ColorChooser.sampleText=Texto de ejemplo Texto de ejemplo ColorChooser.swatchesNameText=Muestras ColorChooser.swatchesMnemonic=77 -ColorChooser.swatchesDisplayedMnemonicIndex=0 ColorChooser.swatchesRecentText=Reciente: -ColorChooser.hsbNameText=HSB # Each of the ColorChooser types can define a mnemonic, as a KeyEvent.VK_XXX # constant, and an index into the text to render the mnemonic as. The # mnemonic is xxxMnemonic and the index of the character to underline is # xxxDisplayedMnemonicIndex. -ColorChooser.hsbMnemonic=72 -ColorChooser.hsbDisplayedMnemonicIndex=0 -ColorChooser.hsbHueText=H -ColorChooser.hsbSaturationText=S -ColorChooser.hsbBrightnessText=B -ColorChooser.hsbRedText=R -ColorChooser.hsbGreenText=V -ColorChooser.hsbBlueText=A +ColorChooser.hsvNameText=HSV +ColorChooser.hsvMnemonic=72 +ColorChooser.hsvHueText=Hue +ColorChooser.hsvSaturationText=Saturation +ColorChooser.hsvValueText=Value +ColorChooser.hsvTransparencyText=Transparency +ColorChooser.hslNameText=HSL +ColorChooser.hslMnemonic=76 +ColorChooser.hslHueText=Hue +ColorChooser.hslSaturationText=Saturation +ColorChooser.hslLightnessText=Lightness +ColorChooser.hslTransparencyText=Transparency ColorChooser.rgbNameText=RGB ColorChooser.rgbMnemonic=71 -ColorChooser.rgbDisplayedMnemonicIndex=1 ColorChooser.rgbRedText=Rojo ColorChooser.rgbRedMnemonic=74 ColorChooser.rgbGreenText=Verde ColorChooser.rgbGreenMnemonic=86 ColorChooser.rgbBlueText=Azul ColorChooser.rgbBlueMnemonic=76 +ColorChooser.rgbAlphaText=Alpha +ColorChooser.rgbHexCodeText=Color Code +ColorChooser.rgbHexCodeMnemonic=67 +ColorChooser.cmykNameText=CMYK +ColorChooser.cmykMnemonic=77 +ColorChooser.cmykCyanText=Cyan +ColorChooser.cmykMagentaText=Magenta +ColorChooser.cmykYellowText=Yellow +ColorChooser.cmykBlackText=Black +ColorChooser.cmykAlphaText=Alpha ############ OPTION PANE STRINGS ############# # Mnemonic keys correspond to KeyEvent.VK_XXX constant diff --git a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_fr.properties b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_fr.properties index b18de0274..333a1a82f 100644 --- a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_fr.properties +++ b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_fr.properties @@ -101,30 +101,41 @@ ColorChooser.resetMnemonic=82 ColorChooser.sampleText=Echantillon de texte Echantillon de texte ColorChooser.swatchesNameText=Echantillons ColorChooser.swatchesMnemonic=69 -ColorChooser.swatchesDisplayedMnemonicIndex=0 ColorChooser.swatchesRecentText=Dernier : -ColorChooser.hsbNameText=HSB # Each of the ColorChooser types can define a mnemonic, as a KeyEvent.VK_XXX # constant, and an index into the text to render the mnemonic as. The # mnemonic is xxxMnemonic and the index of the character to underline is # xxxDisplayedMnemonicIndex. -ColorChooser.hsbMnemonic=72 -ColorChooser.hsbDisplayedMnemonicIndex=0 -ColorChooser.hsbHueText=H -ColorChooser.hsbSaturationText=S -ColorChooser.hsbBrightnessText=B -ColorChooser.hsbRedText=R -ColorChooser.hsbGreenText=V -ColorChooser.hsbBlueText=B +ColorChooser.hsvNameText=HSV +ColorChooser.hsvMnemonic=72 +ColorChooser.hsvHueText=Hue +ColorChooser.hsvSaturationText=Saturation +ColorChooser.hsvValueText=Value +ColorChooser.hsvTransparencyText=Transparency +ColorChooser.hslNameText=HSL +ColorChooser.hslMnemonic=76 +ColorChooser.hslHueText=Hue +ColorChooser.hslSaturationText=Saturation +ColorChooser.hslLightnessText=Lightness +ColorChooser.hslTransparencyText=Transparency ColorChooser.rgbNameText=RVB ColorChooser.rgbMnemonic=86 -ColorChooser.rgbDisplayedMnemonicIndex=1 ColorChooser.rgbRedText=Rouge ColorChooser.rgbRedMnemonic=71 ColorChooser.rgbGreenText=Vert ColorChooser.rgbGreenMnemonic=84 ColorChooser.rgbBlueText=Bleu ColorChooser.rgbBlueMnemonic=66 +ColorChooser.rgbAlphaText=Alpha +ColorChooser.rgbHexCodeText=Color Code +ColorChooser.rgbHexCodeMnemonic=67 +ColorChooser.cmykNameText=CMYK +ColorChooser.cmykMnemonic=77 +ColorChooser.cmykCyanText=Cyan +ColorChooser.cmykMagentaText=Magenta +ColorChooser.cmykYellowText=Yellow +ColorChooser.cmykBlackText=Black +ColorChooser.cmykAlphaText=Alpha ############ OPTION PANE STRINGS ############# # Mnemonic keys correspond to KeyEvent.VK_XXX constant diff --git a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_it.properties b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_it.properties index 1c9f3790e..7bc69dbcf 100644 --- a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_it.properties +++ b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_it.properties @@ -101,30 +101,41 @@ ColorChooser.resetMnemonic=82 ColorChooser.sampleText=Testo di prova Testo di prova ColorChooser.swatchesNameText=Colori campione ColorChooser.swatchesMnemonic=67 -ColorChooser.swatchesDisplayedMnemonicIndex=0 ColorChooser.swatchesRecentText=Recenti: -ColorChooser.hsbNameText=HSB # Each of the ColorChooser types can define a mnemonic, as a KeyEvent.VK_XXX # constant, and an index into the text to render the mnemonic as. The # mnemonic is xxxMnemonic and the index of the character to underline is # xxxDisplayedMnemonicIndex. -ColorChooser.hsbMnemonic=72 -ColorChooser.hsbDisplayedMnemonicIndex=0 -ColorChooser.hsbHueText=H -ColorChooser.hsbSaturationText=S -ColorChooser.hsbBrightnessText=B -ColorChooser.hsbRedText=R -ColorChooser.hsbGreenText=G -ColorChooser.hsbBlueText=B +ColorChooser.hsvNameText=HSV +ColorChooser.hsvMnemonic=72 +ColorChooser.hsvHueText=Hue +ColorChooser.hsvSaturationText=Saturation +ColorChooser.hsvValueText=Value +ColorChooser.hsvTransparencyText=Transparency +ColorChooser.hslNameText=HSL +ColorChooser.hslMnemonic=76 +ColorChooser.hslHueText=Hue +ColorChooser.hslSaturationText=Saturation +ColorChooser.hslLightnessText=Lightness +ColorChooser.hslTransparencyText=Transparency ColorChooser.rgbNameText=RGB ColorChooser.rgbMnemonic=71 -ColorChooser.rgbDisplayedMnemonicIndex=1 ColorChooser.rgbRedText=Rosso ColorChooser.rgbRedMnemonic=79 ColorChooser.rgbGreenText=Verde ColorChooser.rgbGreenMnemonic=69 ColorChooser.rgbBlueText=Blu ColorChooser.rgbBlueMnemonic=66 +ColorChooser.rgbAlphaText=Alpha +ColorChooser.rgbHexCodeText=Color Code +ColorChooser.rgbHexCodeMnemonic=67 +ColorChooser.cmykNameText=CMYK +ColorChooser.cmykMnemonic=77 +ColorChooser.cmykCyanText=Cyan +ColorChooser.cmykMagentaText=Magenta +ColorChooser.cmykYellowText=Yellow +ColorChooser.cmykBlackText=Black +ColorChooser.cmykAlphaText=Alpha ############ OPTION PANE STRINGS ############# # Mnemonic keys correspond to KeyEvent.VK_XXX constant diff --git a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_ja.properties b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_ja.properties index 1b5a131ca..5603e9aa1 100644 --- a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_ja.properties +++ b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_ja.properties @@ -101,30 +101,41 @@ ColorChooser.resetMnemonic=82 ColorChooser.sampleText=\u30b5\u30f3\u30d7\u30eb\u30c6\u30ad\u30b9\u30c8 \u30b5\u30f3\u30d7\u30eb\u30c6\u30ad\u30b9\u30c8 ColorChooser.swatchesNameText=\u30b5\u30f3\u30d7\u30eb(S) ColorChooser.swatchesMnemonic=83 -ColorChooser.swatchesDisplayedMnemonicIndex=5 ColorChooser.swatchesRecentText=\u6700\u65b0: -ColorChooser.hsbNameText=HSB # Each of the ColorChooser types can define a mnemonic, as a KeyEvent.VK_XXX # constant, and an index into the text to render the mnemonic as. The # mnemonic is xxxMnemonic and the index of the character to underline is # xxxDisplayedMnemonicIndex. -ColorChooser.hsbMnemonic=72 -ColorChooser.hsbDisplayedMnemonicIndex=0 -ColorChooser.hsbHueText=H -ColorChooser.hsbSaturationText=S -ColorChooser.hsbBrightnessText=B -ColorChooser.hsbRedText=R -ColorChooser.hsbGreenText=G -ColorChooser.hsbBlueText=B +ColorChooser.hsvNameText=HSV +ColorChooser.hsvMnemonic=72 +ColorChooser.hsvHueText=Hue +ColorChooser.hsvSaturationText=Saturation +ColorChooser.hsvValueText=Value +ColorChooser.hsvTransparencyText=Transparency +ColorChooser.hslNameText=HSL +ColorChooser.hslMnemonic=76 +ColorChooser.hslHueText=Hue +ColorChooser.hslSaturationText=Saturation +ColorChooser.hslLightnessText=Lightness +ColorChooser.hslTransparencyText=Transparency ColorChooser.rgbNameText=RGB ColorChooser.rgbMnemonic=71 -ColorChooser.rgbDisplayedMnemonicIndex=1 ColorChooser.rgbRedText=\u8d64(D) ColorChooser.rgbRedMnemonic=68 ColorChooser.rgbGreenText=\u7dd1(N) ColorChooser.rgbGreenMnemonic=78 ColorChooser.rgbBlueText=\u9752(B) ColorChooser.rgbBlueMnemonic=66 +ColorChooser.rgbAlphaText=Alpha +ColorChooser.rgbHexCodeText=Color Code +ColorChooser.rgbHexCodeMnemonic=67 +ColorChooser.cmykNameText=CMYK +ColorChooser.cmykMnemonic=77 +ColorChooser.cmykCyanText=Cyan +ColorChooser.cmykMagentaText=Magenta +ColorChooser.cmykYellowText=Yellow +ColorChooser.cmykBlackText=Black +ColorChooser.cmykAlphaText=Alpha ############ OPTION PANE STRINGS ############# # Mnemonic keys correspond to KeyEvent.VK_XXX constant diff --git a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_ko.properties b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_ko.properties index 9b873a43a..f3866d01f 100644 --- a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_ko.properties +++ b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_ko.properties @@ -101,30 +101,41 @@ ColorChooser.resetMnemonic=82 ColorChooser.sampleText=\uc0d8\ud50c \ud14d\uc2a4\ud2b8 \uc0d8\ud50c \ud14d\uc2a4\ud2b8 ColorChooser.swatchesNameText=\uacac\ubcf8(S) ColorChooser.swatchesMnemonic=83 -ColorChooser.swatchesDisplayedMnemonicIndex=3 ColorChooser.swatchesRecentText=\ucd5c\uadfc \ubaa9\ub85d: -ColorChooser.hsbNameText=HSB # Each of the ColorChooser types can define a mnemonic, as a KeyEvent.VK_XXX # constant, and an index into the text to render the mnemonic as. The # mnemonic is xxxMnemonic and the index of the character to underline is # xxxDisplayedMnemonicIndex. -ColorChooser.hsbMnemonic=72 -ColorChooser.hsbDisplayedMnemonicIndex=0 -ColorChooser.hsbHueText=H -ColorChooser.hsbSaturationText=S -ColorChooser.hsbBrightnessText=B -ColorChooser.hsbRedText=R -ColorChooser.hsbGreenText=G -ColorChooser.hsbBlueText=B +ColorChooser.hsvNameText=HSV +ColorChooser.hsvMnemonic=72 +ColorChooser.hsvHueText=Hue +ColorChooser.hsvSaturationText=Saturation +ColorChooser.hsvValueText=Value +ColorChooser.hsvTransparencyText=Transparency +ColorChooser.hslNameText=HSL +ColorChooser.hslMnemonic=76 +ColorChooser.hslHueText=Hue +ColorChooser.hslSaturationText=Saturation +ColorChooser.hslLightnessText=Lightness +ColorChooser.hslTransparencyText=Transparency ColorChooser.rgbNameText=RGB ColorChooser.rgbMnemonic=71 -ColorChooser.rgbDisplayedMnemonicIndex=1 ColorChooser.rgbRedText=\ube68\uac04\uc0c9(D) ColorChooser.rgbRedMnemonic=68 ColorChooser.rgbGreenText=\ub179\uc0c9(N) ColorChooser.rgbGreenMnemonic=78 ColorChooser.rgbBlueText=\ud30c\ub780\uc0c9(B) ColorChooser.rgbBlueMnemonic=66 +ColorChooser.rgbAlphaText=Alpha +ColorChooser.rgbHexCodeText=Color Code +ColorChooser.rgbHexCodeMnemonic=67 +ColorChooser.cmykNameText=CMYK +ColorChooser.cmykMnemonic=77 +ColorChooser.cmykCyanText=Cyan +ColorChooser.cmykMagentaText=Magenta +ColorChooser.cmykYellowText=Yellow +ColorChooser.cmykBlackText=Black +ColorChooser.cmykAlphaText=Alpha ############ OPTION PANE STRINGS ############# # Mnemonic keys correspond to KeyEvent.VK_XXX constant diff --git a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_sv.properties b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_sv.properties index 2b8561b81..17ef3b2be 100644 --- a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_sv.properties +++ b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_sv.properties @@ -101,30 +101,41 @@ ColorChooser.resetMnemonic=84 ColorChooser.sampleText=Exempeltext Exempeltext ColorChooser.swatchesNameText=Prov ColorChooser.swatchesMnemonic=80 -ColorChooser.swatchesDisplayedMnemonicIndex=0 ColorChooser.swatchesRecentText=Tidigare: -ColorChooser.hsbNameText=HSB # Each of the ColorChooser types can define a mnemonic, as a KeyEvent.VK_XXX # constant, and an index into the text to render the mnemonic as. The # mnemonic is xxxMnemonic and the index of the character to underline is # xxxDisplayedMnemonicIndex. -ColorChooser.hsbMnemonic=72 -ColorChooser.hsbDisplayedMnemonicIndex=0 -ColorChooser.hsbHueText=H -ColorChooser.hsbSaturationText=S -ColorChooser.hsbBrightnessText=B -ColorChooser.hsbRedText=R -ColorChooser.hsbGreenText=G -ColorChooser.hsbBlueText=B +ColorChooser.hsvNameText=HSV +ColorChooser.hsvMnemonic=72 +ColorChooser.hsvHueText=Hue +ColorChooser.hsvSaturationText=Saturation +ColorChooser.hsvValueText=Value +ColorChooser.hsvTransparencyText=Transparency +ColorChooser.hslNameText=HSL +ColorChooser.hslMnemonic=76 +ColorChooser.hslHueText=Hue +ColorChooser.hslSaturationText=Saturation +ColorChooser.hslLightnessText=Lightness +ColorChooser.hslTransparencyText=Transparency ColorChooser.rgbNameText=RGB ColorChooser.rgbMnemonic=71 -ColorChooser.rgbDisplayedMnemonicIndex=1 ColorChooser.rgbRedText=R\u00f6d ColorChooser.rgbRedMnemonic=82 ColorChooser.rgbGreenText=Gr\u00f6n ColorChooser.rgbGreenMnemonic=71 ColorChooser.rgbBlueText=Bl\u00e5 ColorChooser.rgbBlueMnemonic=66 +ColorChooser.rgbAlphaText=Alpha +ColorChooser.rgbHexCodeText=Color Code +ColorChooser.rgbHexCodeMnemonic=67 +ColorChooser.cmykNameText=CMYK +ColorChooser.cmykMnemonic=77 +ColorChooser.cmykCyanText=Cyan +ColorChooser.cmykMagentaText=Magenta +ColorChooser.cmykYellowText=Yellow +ColorChooser.cmykBlackText=Black +ColorChooser.cmykAlphaText=Alpha ############ OPTION PANE STRINGS ############# # Mnemonic keys correspond to KeyEvent.VK_XXX constant diff --git a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_zh_CN.properties b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_zh_CN.properties index 7ef35330d..b030c20a9 100644 --- a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_zh_CN.properties +++ b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_zh_CN.properties @@ -101,30 +101,41 @@ ColorChooser.resetMnemonic=82 ColorChooser.sampleText=\u6837\u54c1\u6587\u672c \u6837\u54c1\u6587\u672c ColorChooser.swatchesNameText=\u6837\u54c1(S) ColorChooser.swatchesMnemonic=83 -ColorChooser.swatchesDisplayedMnemonicIndex=3 ColorChooser.swatchesRecentText=\u6700\u8fd1: -ColorChooser.hsbNameText=HSB # Each of the ColorChooser types can define a mnemonic, as a KeyEvent.VK_XXX # constant, and an index into the text to render the mnemonic as. The # mnemonic is xxxMnemonic and the index of the character to underline is # xxxDisplayedMnemonicIndex. -ColorChooser.hsbMnemonic=72 -ColorChooser.hsbDisplayedMnemonicIndex=0 -ColorChooser.hsbHueText=H -ColorChooser.hsbSaturationText=S -ColorChooser.hsbBrightnessText=B -ColorChooser.hsbRedText=R -ColorChooser.hsbGreenText=G -ColorChooser.hsbBlueText=B +ColorChooser.hsvNameText=HSV +ColorChooser.hsvMnemonic=72 +ColorChooser.hsvHueText=Hue +ColorChooser.hsvSaturationText=Saturation +ColorChooser.hsvValueText=Value +ColorChooser.hsvTransparencyText=Transparency +ColorChooser.hslNameText=HSL +ColorChooser.hslMnemonic=76 +ColorChooser.hslHueText=Hue +ColorChooser.hslSaturationText=Saturation +ColorChooser.hslLightnessText=Lightness +ColorChooser.hslTransparencyText=Transparency ColorChooser.rgbNameText=RGB ColorChooser.rgbMnemonic=71 -ColorChooser.rgbDisplayedMnemonicIndex=1 ColorChooser.rgbRedText=\u7ea2 ColorChooser.rgbRedMnemonic=68 ColorChooser.rgbGreenText=\u7eff ColorChooser.rgbGreenMnemonic=78 ColorChooser.rgbBlueText=\u84dd ColorChooser.rgbBlueMnemonic=66 +ColorChooser.rgbAlphaText=Alpha +ColorChooser.rgbHexCodeText=Color Code +ColorChooser.rgbHexCodeMnemonic=67 +ColorChooser.cmykNameText=CMYK +ColorChooser.cmykMnemonic=77 +ColorChooser.cmykCyanText=Cyan +ColorChooser.cmykMagentaText=Magenta +ColorChooser.cmykYellowText=Yellow +ColorChooser.cmykBlackText=Black +ColorChooser.cmykAlphaText=Alpha ############ OPTION PANE STRINGS ############# # Mnemonic keys correspond to KeyEvent.VK_XXX constant diff --git a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_zh_TW.properties b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_zh_TW.properties index dba0d76de..47f99f0dc 100644 --- a/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_zh_TW.properties +++ b/src/share/classes/com/sun/swing/internal/plaf/basic/resources/basic_zh_TW.properties @@ -101,30 +101,41 @@ ColorChooser.resetMnemonic=82 ColorChooser.sampleText=\u7bc4\u4f8b\u6587\u5b57 \u7bc4\u4f8b\u6587\u5b57 ColorChooser.swatchesNameText=\u8abf\u8272\u677f(S) ColorChooser.swatchesMnemonic=83 -ColorChooser.swatchesDisplayedMnemonicIndex=4 ColorChooser.swatchesRecentText=\u6700\u65b0\u9078\u64c7: -ColorChooser.hsbNameText=HSB # Each of the ColorChooser types can define a mnemonic, as a KeyEvent.VK_XXX # constant, and an index into the text to render the mnemonic as. The # mnemonic is xxxMnemonic and the index of the character to underline is # xxxDisplayedMnemonicIndex. -ColorChooser.hsbMnemonic=72 -ColorChooser.hsbDisplayedMnemonicIndex=0 -ColorChooser.hsbHueText=H -ColorChooser.hsbSaturationText=S -ColorChooser.hsbBrightnessText=B -ColorChooser.hsbRedText=R -ColorChooser.hsbGreenText=G -ColorChooser.hsbBlueText=B +ColorChooser.hsvNameText=HSV +ColorChooser.hsvMnemonic=72 +ColorChooser.hsvHueText=Hue +ColorChooser.hsvSaturationText=Saturation +ColorChooser.hsvValueText=Value +ColorChooser.hsvTransparencyText=Transparency +ColorChooser.hslNameText=HSL +ColorChooser.hslMnemonic=76 +ColorChooser.hslHueText=Hue +ColorChooser.hslSaturationText=Saturation +ColorChooser.hslLightnessText=Lightness +ColorChooser.hslTransparencyText=Transparency ColorChooser.rgbNameText=RGB ColorChooser.rgbMnemonic=71 -ColorChooser.rgbDisplayedMnemonicIndex=1 ColorChooser.rgbRedText=\u7d05\u8272(D) ColorChooser.rgbRedMnemonic=68 ColorChooser.rgbGreenText=\u7da0\u8272(N) ColorChooser.rgbGreenMnemonic=78 ColorChooser.rgbBlueText=\u85cd\u8272(B) ColorChooser.rgbBlueMnemonic=66 +ColorChooser.rgbAlphaText=Alpha +ColorChooser.rgbHexCodeText=Color Code +ColorChooser.rgbHexCodeMnemonic=67 +ColorChooser.cmykNameText=CMYK +ColorChooser.cmykMnemonic=77 +ColorChooser.cmykCyanText=Cyan +ColorChooser.cmykMagentaText=Magenta +ColorChooser.cmykYellowText=Yellow +ColorChooser.cmykBlackText=Black +ColorChooser.cmykAlphaText=Alpha ############ OPTION PANE STRINGS ############# # Mnemonic keys correspond to KeyEvent.VK_XXX constant diff --git a/src/share/classes/javax/swing/colorchooser/ColorChooserComponentFactory.java b/src/share/classes/javax/swing/colorchooser/ColorChooserComponentFactory.java index b36c6524d..0ee01e6cb 100644 --- a/src/share/classes/javax/swing/colorchooser/ColorChooserComponentFactory.java +++ b/src/share/classes/javax/swing/colorchooser/ColorChooserComponentFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 1998-2001 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,9 +25,7 @@ package javax.swing.colorchooser; -import javax.swing.*; - - +import javax.swing.JComponent; /** * A class designed to produce preconfigured "accessory" objects to @@ -49,16 +47,17 @@ public class ColorChooserComponentFactory { private ColorChooserComponentFactory() { } // can't instantiate - public static AbstractColorChooserPanel[] getDefaultChooserPanels() { - AbstractColorChooserPanel[] choosers = { new DefaultSwatchChooserPanel(), - new DefaultHSBChooserPanel(), - new DefaultRGBChooserPanel() }; - return choosers; + return new AbstractColorChooserPanel[] { + new DefaultSwatchChooserPanel(), + new ColorChooserPanel(new ColorModelHSV()), + new ColorChooserPanel(new ColorModelHSL()), + new ColorChooserPanel(new ColorModel()), + new ColorChooserPanel(new ColorModelCMYK()), + }; } public static JComponent getPreviewPanel() { return new DefaultPreviewPanel(); } - } diff --git a/src/share/classes/javax/swing/colorchooser/ColorChooserPanel.java b/src/share/classes/javax/swing/colorchooser/ColorChooserPanel.java new file mode 100644 index 000000000..46b5351c6 --- /dev/null +++ b/src/share/classes/javax/swing/colorchooser/ColorChooserPanel.java @@ -0,0 +1,182 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.swing.colorchooser; + +import java.awt.Color; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.SwingConstants; + +final class ColorChooserPanel extends AbstractColorChooserPanel implements PropertyChangeListener { + + private static final int MASK = 0xFF000000; + private final ColorModel model; + private final ColorPanel panel; + private final DiagramComponent slider; + private final DiagramComponent diagram; + private final JFormattedTextField text; + private final JLabel label; + + ColorChooserPanel(ColorModel model) { + this.model = model; + this.panel = new ColorPanel(this.model); + this.slider = new DiagramComponent(this.panel, false); + this.diagram = new DiagramComponent(this.panel, true); + this.text = new JFormattedTextField(); + this.label = new JLabel(null, null, SwingConstants.RIGHT); + ValueFormatter.init(6, true, this.text); + } + + @Override + public void updateChooser() { + Color color = getColorFromModel(); + this.panel.setColor(color); + this.text.setValue(Integer.valueOf(color.getRGB())); + this.slider.repaint(); + this.diagram.repaint(); + } + + @Override + protected void buildChooser() { + if (0 == getComponentCount()) { + setLayout(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + + gbc.gridx = 3; + gbc.gridwidth = 2; + gbc.weighty = 1.0; + gbc.anchor = GridBagConstraints.NORTH; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets.top = 10; + gbc.insets.right = 10; + add(this.panel, gbc); + + gbc.gridwidth = 1; + gbc.weightx = 1.0; + gbc.weighty = 0.0; + gbc.anchor = GridBagConstraints.CENTER; + gbc.insets.right = 5; + gbc.insets.bottom = 10; + add(this.label, gbc); + + gbc.gridx = 4; + gbc.weightx = 0.0; + gbc.insets.right = 10; + add(this.text, gbc); + + gbc.gridx = 2; + gbc.gridheight = 2; + gbc.anchor = GridBagConstraints.NORTH; + gbc.ipadx = this.text.getPreferredSize().height; + gbc.ipady = getPreferredSize().height; + add(this.slider, gbc); + + gbc.gridx = 1; + gbc.insets.left = 10; + gbc.ipadx = gbc.ipady; + add(this.diagram, gbc); + + this.label.setLabelFor(this.text); + this.text.addPropertyChangeListener("value", this); // NON-NLS: the property name + this.slider.setBorder(this.text.getBorder()); + this.diagram.setBorder(this.text.getBorder()); + + setInheritsPopupMenu(this, true); // CR:4966112 + } + String label = this.model.getText(this, "HexCode"); // NON-NLS: suffix + boolean visible = label != null; + this.text.setVisible(visible); + this.label.setVisible(visible); + if (visible) { + this.label.setText(label); + int mnemonic = this.model.getInteger(this, "HexCodeMnemonic"); // NON-NLS: suffix + if (mnemonic > 0) { + this.label.setDisplayedMnemonic(mnemonic); + mnemonic = this.model.getInteger(this, "HexCodeMnemonicIndex"); // NON-NLS: suffix + if (mnemonic >= 0) { + this.label.setDisplayedMnemonicIndex(mnemonic); + } + } + } + this.panel.buildPanel(); + } + + @Override + public String getDisplayName() { + return this.model.getText(this, "Name"); // NON-NLS: suffix + } + + @Override + public int getMnemonic() { + return this.model.getInteger(this, "Mnemonic"); // NON-NLS: suffix + } + + @Override + public int getDisplayedMnemonicIndex() { + return this.model.getInteger(this, "DisplayedMnemonicIndex"); // NON-NLS: suffix + } + + @Override + public Icon getSmallDisplayIcon() { + return null; + } + + @Override + public Icon getLargeDisplayIcon() { + return null; + } + + public void propertyChange(PropertyChangeEvent event) { + Object object = event.getNewValue(); + if (object instanceof Integer) { + int value = MASK & getColorFromModel().getRGB() | (Integer) object; + getColorSelectionModel().setSelectedColor(new Color(value, true)); + } + this.text.selectAll(); + } + + /** + * Allows to show context popup for all components recursively. + * + * @param component the root component of the tree + * @param value whether or not the popup menu is inherited + */ + private static void setInheritsPopupMenu(JComponent component, boolean value) { + component.setInheritsPopupMenu(value); + for (Object object : component.getComponents()) { + if (object instanceof JComponent) { + setInheritsPopupMenu((JComponent) object, value); + } + } + } +} diff --git a/src/share/classes/javax/swing/colorchooser/ColorModel.java b/src/share/classes/javax/swing/colorchooser/ColorModel.java new file mode 100644 index 000000000..955ed1626 --- /dev/null +++ b/src/share/classes/javax/swing/colorchooser/ColorModel.java @@ -0,0 +1,102 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.swing.colorchooser; + +import java.awt.Component; +import javax.swing.UIManager; + +class ColorModel { + + private final String prefix; + private final String[] labels; + + ColorModel(String name, String... labels) { + this.prefix = "ColorChooser." + name; // NON-NLS: default prefix + this.labels = labels; + } + + ColorModel() { + this("rgb", "Red", "Green", "Blue", "Alpha"); // NON-NLS: components + } + + void setColor(int color, float[] model) { + model[0] = normalize(color >> 16); + model[1] = normalize(color >> 8); + model[2] = normalize(color); + model[3] = normalize(color >> 24); + } + + int getColor(float[] model) { + return to8bit(model[2]) | (to8bit(model[1]) << 8) | (to8bit(model[0]) << 16) | (to8bit(model[3]) << 24); + } + + int getCount() { + return this.labels.length; + } + + int getMinimum(int index) { + return 0; + } + + int getMaximum(int index) { + return 255; + } + + float getDefault(int index) { + return 0.0f; + } + + final String getLabel(Component component, int index) { + return getText(component, this.labels[index]); + } + + private static float normalize(int value) { + return (float) (value & 0xFF) / 255.0f; + } + + private static int to8bit(float value) { + return (int) (255.0f * value); + } + + final String getText(Component component, String suffix) { + return UIManager.getString(this.prefix + suffix + "Text", component.getLocale()); // NON-NLS: default postfix + } + + final int getInteger(Component component, String suffix) { + Object value = UIManager.get(this.prefix + suffix, component.getLocale()); + if (value instanceof Integer) { + return (Integer) value; + } + if (value instanceof String) { + try { + return Integer.parseInt((String) value); + } + catch (NumberFormatException exception) { + } + } + return -1; + } +} diff --git a/src/share/classes/javax/swing/colorchooser/ColorModelCMYK.java b/src/share/classes/javax/swing/colorchooser/ColorModelCMYK.java new file mode 100644 index 000000000..77a7ca555 --- /dev/null +++ b/src/share/classes/javax/swing/colorchooser/ColorModelCMYK.java @@ -0,0 +1,94 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.swing.colorchooser; + +final class ColorModelCMYK extends ColorModel { + + ColorModelCMYK() { + super("cmyk", "Cyan", "Magenta", "Yellow", "Black", "Alpha"); // NON-NLS: components + } + + @Override + void setColor(int color, float[] space) { + super.setColor(color, space); + space[4] = space[3]; + RGBtoCMYK(space, space); + } + + @Override + int getColor(float[] space) { + CMYKtoRGB(space, space); + space[3] = space[4]; + return super.getColor(space); + } + + /** + * Converts CMYK components of a color to a set of RGB components. + * + * @param cmyk a float array with length equal to + * the number of CMYK components + * @param rgb a float array with length of at least 3 + * that contains RGB components of a color + * @return a float array that contains RGB components + */ + private static float[] CMYKtoRGB(float[] cmyk, float[] rgb) { + if (rgb == null) { + rgb = new float[3]; + } + rgb[0] = 1.0f + cmyk[0] * cmyk[3] - cmyk[3] - cmyk[0]; + rgb[1] = 1.0f + cmyk[1] * cmyk[3] - cmyk[3] - cmyk[1]; + rgb[2] = 1.0f + cmyk[2] * cmyk[3] - cmyk[3] - cmyk[2]; + return rgb; + } + + /** + * Converts RGB components of a color to a set of CMYK components. + * + * @param rgb a float array with length of at least 3 + * that contains RGB components of a color + * @param cmyk a float array with length equal to + * the number of CMYK components + * @return a float array that contains CMYK components + */ + private static float[] RGBtoCMYK(float[] rgb, float[] cmyk) { + if (cmyk == null) { + cmyk = new float[4]; + } + float max = ColorModelHSL.max(rgb[0], rgb[1], rgb[2]); + if (max > 0.0f) { + cmyk[0] = 1.0f - rgb[0] / max; + cmyk[1] = 1.0f - rgb[1] / max; + cmyk[2] = 1.0f - rgb[2] / max; + } + else { + cmyk[0] = 0.0f; + cmyk[1] = 0.0f; + cmyk[2] = 0.0f; + } + cmyk[3] = 1.0f - max; + return cmyk; + } +} diff --git a/src/share/classes/javax/swing/colorchooser/ColorModelHSL.java b/src/share/classes/javax/swing/colorchooser/ColorModelHSL.java new file mode 100644 index 000000000..85b0c8e5d --- /dev/null +++ b/src/share/classes/javax/swing/colorchooser/ColorModelHSL.java @@ -0,0 +1,188 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.swing.colorchooser; + +final class ColorModelHSL extends ColorModel { + + ColorModelHSL() { + super("hsl", "Hue", "Saturation", "Lightness", "Transparency"); // NON-NLS: components + } + + @Override + void setColor(int color, float[] space) { + super.setColor(color, space); + RGBtoHSL(space, space); + space[3] = 1.0f - space[3]; + } + + @Override + int getColor(float[] space) { + space[3] = 1.0f - space[3]; + HSLtoRGB(space, space); + return super.getColor(space); + } + + @Override + int getMaximum(int index) { + return (index == 0) ? 360 : 100; + } + + @Override + float getDefault(int index) { + return (index == 0) ? -1.0f : (index == 2) ? 0.5f : 1.0f; + } + + /** + * Converts HSL components of a color to a set of RGB components. + * + * @param hsl a float array with length equal to + * the number of HSL components + * @param rgb a float array with length of at least 3 + * that contains RGB components of a color + * @return a float array that contains RGB components + */ + private static float[] HSLtoRGB(float[] hsl, float[] rgb) { + if (rgb == null) { + rgb = new float[3]; + } + float hue = hsl[0]; + float saturation = hsl[1]; + float lightness = hsl[2]; + + if (saturation > 0.0f) { + hue = (hue < 1.0f) ? hue * 6.0f : 0.0f; + float q = lightness + saturation * ((lightness > 0.5f) ? 1.0f - lightness : lightness); + float p = 2.0f * lightness - q; + rgb[0]= normalize(q, p, (hue < 4.0f) ? (hue + 2.0f) : (hue - 4.0f)); + rgb[1]= normalize(q, p, hue); + rgb[2]= normalize(q, p, (hue < 2.0f) ? (hue + 4.0f) : (hue - 2.0f)); + } + else { + rgb[0] = lightness; + rgb[1] = lightness; + rgb[2] = lightness; + } + return rgb; + } + + /** + * Converts RGB components of a color to a set of HSL components. + * + * @param rgb a float array with length of at least 3 + * that contains RGB components of a color + * @param hsl a float array with length equal to + * the number of HSL components + * @return a float array that contains HSL components + */ + private static float[] RGBtoHSL(float[] rgb, float[] hsl) { + if (hsl == null) { + hsl = new float[3]; + } + float max = max(rgb[0], rgb[1], rgb[2]); + float min = min(rgb[0], rgb[1], rgb[2]); + + float summa = max + min; + float saturation = max - min; + if (saturation > 0.0f) { + saturation /= (summa > 1.0f) + ? 2.0f - summa + : summa; + } + hsl[0] = getHue(rgb[0], rgb[1], rgb[2], max, min); + hsl[1] = saturation; + hsl[2] = summa / 2.0f; + return hsl; + } + + /** + * Returns the smaller of three color components. + * + * @param red the red component of the color + * @param green the green component of the color + * @param blue the blue component of the color + * @return the smaller of {@code red}, {@code green} and {@code blue} + */ + static float min(float red, float green, float blue) { + float min = (red < green) ? red : green; + return (min < blue) ? min : blue; + } + + /** + * Returns the larger of three color components. + * + * @param red the red component of the color + * @param green the green component of the color + * @param blue the blue component of the color + * @return the larger of {@code red}, {@code green} and {@code blue} + */ + static float max(float red, float green, float blue) { + float max = (red > green) ? red : green; + return (max > blue) ? max : blue; + } + + /** + * Calculates the hue component for HSL and HSV color spaces. + * + * @param red the red component of the color + * @param green the green component of the color + * @param blue the blue component of the color + * @param max the larger of {@code red}, {@code green} and {@code blue} + * @param min the smaller of {@code red}, {@code green} and {@code blue} + * @return the hue component + */ + static float getHue(float red, float green, float blue, float max, float min) { + float hue = max - min; + if (hue > 0.0f) { + if (max == red) { + hue = (green - blue) / hue; + if (hue < 0.0f) { + hue += 6.0f; + } + } + else if (max == green) { + hue = 2.0f + (blue - red) / hue; + } + else /*max == blue*/ { + hue = 4.0f + (red - green) / hue; + } + hue /= 6.0f; + } + return hue; + } + + private static float normalize(float q, float p, float color) { + if (color < 1.0f) { + return p + (q - p) * color; + } + if (color < 3.0f) { + return q; + } + if (color < 4.0f) { + return p + (q - p) * (4.0f - color); + } + return p; + } +} diff --git a/src/share/classes/javax/swing/colorchooser/ColorModelHSV.java b/src/share/classes/javax/swing/colorchooser/ColorModelHSV.java new file mode 100644 index 000000000..e33eef77d --- /dev/null +++ b/src/share/classes/javax/swing/colorchooser/ColorModelHSV.java @@ -0,0 +1,138 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.swing.colorchooser; + +final class ColorModelHSV extends ColorModel { + + ColorModelHSV() { + super("hsv", "Hue", "Saturation", "Value", "Transparency"); // NON-NLS: components + } + + @Override + void setColor(int color, float[] space) { + super.setColor(color, space); + RGBtoHSV(space, space); + space[3] = 1.0f - space[3]; + } + + @Override + int getColor(float[] space) { + space[3] = 1.0f - space[3]; + HSVtoRGB(space, space); + return super.getColor(space); + } + + @Override + int getMaximum(int index) { + return (index == 0) ? 360 : 100; + } + + @Override + float getDefault(int index) { + return (index == 0) ? -1.0f : 1.0f; + } + + /** + * Converts HSV components of a color to a set of RGB components. + * + * @param hsv a float array with length equal to + * the number of HSV components + * @param rgb a float array with length of at least 3 + * that contains RGB components of a color + * @return a float array that contains RGB components + */ + private static float[] HSVtoRGB(float[] hsv, float[] rgb) { + if (rgb == null) { + rgb = new float[3]; + } + float hue = hsv[0]; + float saturation = hsv[1]; + float value = hsv[2]; + + rgb[0] = value; + rgb[1] = value; + rgb[2] = value; + + if (saturation > 0.0f) { + hue = (hue < 1.0f) ? hue * 6.0f : 0.0f; + int integer = (int) hue; + float f = hue - (float) integer; + switch (integer) { + case 0: + rgb[1] *= 1.0f - saturation * (1.0f - f); + rgb[2] *= 1.0f - saturation; + break; + case 1: + rgb[0] *= 1.0f - saturation * f; + rgb[2] *= 1.0f - saturation; + break; + case 2: + rgb[0] *= 1.0f - saturation; + rgb[2] *= 1.0f - saturation * (1.0f - f); + break; + case 3: + rgb[0] *= 1.0f - saturation; + rgb[1] *= 1.0f - saturation * f; + break; + case 4: + rgb[0] *= 1.0f - saturation * (1.0f - f); + rgb[1] *= 1.0f - saturation; + break; + case 5: + rgb[1] *= 1.0f - saturation; + rgb[2] *= 1.0f - saturation * f; + break; + } + } + return rgb; + } + + /** + * Converts RGB components of a color to a set of HSV components. + * + * @param rgb a float array with length of at least 3 + * that contains RGB components of a color + * @param hsv a float array with length equal to + * the number of HSV components + * @return a float array that contains HSV components + */ + private static float[] RGBtoHSV(float[] rgb, float[] hsv) { + if (hsv == null) { + hsv = new float[3]; + } + float max = ColorModelHSL.max(rgb[0], rgb[1], rgb[2]); + float min = ColorModelHSL.min(rgb[0], rgb[1], rgb[2]); + + float saturation = max - min; + if (saturation > 0.0f) { + saturation /= max; + } + hsv[0] = ColorModelHSL.getHue(rgb[0], rgb[1], rgb[2], max, min); + hsv[1] = saturation; + hsv[2] = max; + return hsv; + } +} diff --git a/src/share/classes/javax/swing/colorchooser/ColorPanel.java b/src/share/classes/javax/swing/colorchooser/ColorPanel.java new file mode 100644 index 000000000..6e9329dbe --- /dev/null +++ b/src/share/classes/javax/swing/colorchooser/ColorPanel.java @@ -0,0 +1,210 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.swing.colorchooser; + +import java.awt.Color; +import java.awt.ContainerOrderFocusTraversalPolicy; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.ButtonGroup; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.border.EmptyBorder; + +final class ColorPanel extends JPanel implements ActionListener { + + private final SlidingSpinner[] spinners = new SlidingSpinner[5]; + private final float[] values = new float[this.spinners.length]; + + private final ColorModel model; + private Color color; + private int x = 1; + private int y = 2; + private int z; + + ColorPanel(ColorModel model) { + super(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + + gbc.gridx = 1; + ButtonGroup group = new ButtonGroup(); + EmptyBorder border = null; + for (int i = 0; i < this.spinners.length; i++) { + if (i < 3) { + JRadioButton button = new JRadioButton(); + if (i == 0) { + Insets insets = button.getInsets(); + insets.left = button.getPreferredSize().width; + border = new EmptyBorder(insets); + button.setSelected(true); + gbc.insets.top = 5; + } + add(button, gbc); + group.add(button); + button.setActionCommand(Integer.toString(i)); + button.addActionListener(this); + this.spinners[i] = new SlidingSpinner(this, button); + } + else { + JLabel label = new JLabel(); + add(label, gbc); + label.setBorder(border); + label.setFocusable(false); + this.spinners[i] = new SlidingSpinner(this, label); + } + } + gbc.gridx = 2; + gbc.weightx = 1.0; + gbc.insets.top = 0; + gbc.insets.left = 5; + for (SlidingSpinner spinner : this.spinners) { + add(spinner.getSlider(), gbc); + gbc.insets.top = 5; + } + gbc.gridx = 3; + gbc.weightx = 0.0; + gbc.insets.top = 0; + for (SlidingSpinner spinner : this.spinners) { + add(spinner.getSpinner(), gbc); + gbc.insets.top = 5; + } + setFocusTraversalPolicy(new ContainerOrderFocusTraversalPolicy()); + setFocusTraversalPolicyProvider(true); + setFocusable(false); + + this.model = model; + } + + public void actionPerformed(ActionEvent event) { + try { + this.z = Integer.parseInt(event.getActionCommand()); + this.y = (this.z != 2) ? 2 : 1; + this.x = (this.z != 0) ? 0 : 1; + getParent().repaint(); + } + catch (NumberFormatException exception) { + } + } + + void buildPanel() { + int count = this.model.getCount(); + this.spinners[4].setVisible(count > 4); + for (int i = 0; i < count; i++) { + Object object = this.spinners[i].getLabel(); + if (object instanceof JRadioButton) { + JRadioButton button = (JRadioButton) object; + button.setText(this.model.getLabel(this, i)); + } + else if (object instanceof JLabel) { + JLabel label = (JLabel) object; + label.setText(this.model.getLabel(this, i)); + } + this.spinners[i].setRange(this.model.getMinimum(i), this.model.getMaximum(i)); + this.spinners[i].setValue(this.values[i]); + } + } + + void colorChanged() { + this.color = new Color(getColor(0), true); + Object parent = getParent(); + if (parent instanceof ColorChooserPanel) { + ColorChooserPanel chooser = (ColorChooserPanel) parent; + chooser.getColorSelectionModel().setSelectedColor(this.color); + chooser.repaint(); + } + } + + float getValueX() { + return this.spinners[this.x].getValue(); + } + + float getValueY() { + return 1.0f - this.spinners[this.y].getValue(); + } + + float getValueZ() { + return 1.0f - this.spinners[this.z].getValue(); + } + + void setValue(float z) { + this.spinners[this.z].setValue(1.0f - z); + colorChanged(); + } + + void setValue(float x, float y) { + this.spinners[this.x].setValue(x); + this.spinners[this.y].setValue(1.0f - y); + colorChanged(); + } + + int getColor(float z) { + setDefaultValue(this.x); + setDefaultValue(this.y); + this.values[this.z] = 1.0f - z; + return getColor(3); + } + + int getColor(float x, float y) { + this.values[this.x] = x; + this.values[this.y] = 1.0f - y; + setValue(this.z); + return getColor(3); + } + + void setColor(Color color) { + if (!color.equals(this.color)) { + this.color = color; + this.model.setColor(color.getRGB(), this.values); + for (int i = 0; i < this.model.getCount(); i++) { + this.spinners[i].setValue(this.values[i]); + } + } + } + + private int getColor(int index) { + while (index < this.model.getCount()) { + setValue(index++); + } + return this.model.getColor(this.values); + } + + private void setValue(int index) { + this.values[index] = this.spinners[index].getValue(); + } + + private void setDefaultValue(int index) { + float value = this.model.getDefault(index); + this.values[index] = (value < 0.0f) + ? this.spinners[index].getValue() + : value; + } +} diff --git a/src/share/classes/javax/swing/colorchooser/DefaultHSBChooserPanel.java b/src/share/classes/javax/swing/colorchooser/DefaultHSBChooserPanel.java deleted file mode 100644 index 89759e247..000000000 --- a/src/share/classes/javax/swing/colorchooser/DefaultHSBChooserPanel.java +++ /dev/null @@ -1,801 +0,0 @@ -/* - * Copyright 1998-2004 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -package javax.swing.colorchooser; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; -import javax.swing.event.*; -import javax.swing.border.*; -import java.awt.image.*; -import java.util.Locale; - -/** - * Implements the default HSB Color chooser - * - * @author Tom Santos - * @author Steve Wilson - * @author Mark Davidson - * @author Shannon Hickey - */ -class DefaultHSBChooserPanel extends AbstractColorChooserPanel implements ChangeListener, HierarchyListener { - - private transient HSBImage palette; - private transient HSBImage sliderPalette; - - private transient Image paletteImage; - private transient Image sliderPaletteImage; - - private JSlider slider; - private JSpinner hField; - private JSpinner sField; - private JSpinner bField; - - private JTextField redField; - private JTextField greenField; - private JTextField blueField; - - private boolean isAdjusting = false; // Flag which indicates that values are set internally - private Point paletteSelection = new Point(); - private JLabel paletteLabel; - private JLabel sliderPaletteLabel; - - private JRadioButton hRadio; - private JRadioButton sRadio; - private JRadioButton bRadio; - - private static final int PALETTE_DIMENSION = 200; - private static final int MAX_HUE_VALUE = 359; - private static final int MAX_SATURATION_VALUE = 100; - private static final int MAX_BRIGHTNESS_VALUE = 100; - - private int currentMode = HUE_MODE; - - private static final int HUE_MODE = 0; - private static final int SATURATION_MODE = 1; - private static final int BRIGHTNESS_MODE = 2; - - public DefaultHSBChooserPanel() { - } - - private void addPaletteListeners() { - paletteLabel.addMouseListener(new MouseAdapter() { - public void mousePressed(MouseEvent e ) { - float[] hsb = new float[3]; - palette.getHSBForLocation( e.getX(), e.getY(), hsb ); - updateHSB( hsb[0], hsb[1], hsb[2] ); - } - }); - - paletteLabel.addMouseMotionListener(new MouseMotionAdapter() { - public void mouseDragged( MouseEvent e ){ - int labelWidth = paletteLabel.getWidth(); - - int labelHeight = paletteLabel.getHeight(); - int x = e.getX(); - int y = e.getY(); - - if ( x >= labelWidth ) { - x = labelWidth - 1; - } - - if ( y >= labelHeight ) { - y = labelHeight - 1; - } - - if ( x < 0 ) { - x = 0; - } - - if ( y < 0 ) { - y = 0; - } - - float[] hsb = new float[3]; - palette.getHSBForLocation( x, y, hsb ); - updateHSB( hsb[0], hsb[1], hsb[2] ); - } - }); - } - - private void updatePalette( float h, float s, float b ) { - int x = 0; - int y = 0; - - switch ( currentMode ) { - case HUE_MODE: - if ( h != palette.getHue() ) { - palette.setHue( h ); - palette.nextFrame(); - } - x = PALETTE_DIMENSION - (int)(s * PALETTE_DIMENSION); - y = PALETTE_DIMENSION - (int)(b * PALETTE_DIMENSION); - break; - case SATURATION_MODE: - if ( s != palette.getSaturation() ) { - palette.setSaturation( s ); - palette.nextFrame(); - } - x = (int)(h * PALETTE_DIMENSION); - y = PALETTE_DIMENSION - (int)(b * PALETTE_DIMENSION); - break; - case BRIGHTNESS_MODE: - if ( b != palette.getBrightness() ) { - palette.setBrightness( b ); - palette.nextFrame(); - } - x = (int)(h * PALETTE_DIMENSION); - y = PALETTE_DIMENSION - (int)(s * PALETTE_DIMENSION); - break; - } - - paletteSelection.setLocation( x, y ); - paletteLabel.repaint(); - } - - private void updateSlider( float h, float s, float b ) { - // Update the slider palette if necessary. - // When the slider is the hue slider or the hue hasn't changed, - // the hue of the palette will not need to be updated. - if (currentMode != HUE_MODE && h != sliderPalette.getHue() ) { - sliderPalette.setHue( h ); - sliderPalette.nextFrame(); - } - - float value = 0f; - - switch ( currentMode ) { - case HUE_MODE: - value = h; - break; - case SATURATION_MODE: - value = s; - break; - case BRIGHTNESS_MODE: - value = b; - break; - } - - slider.setValue( Math.round(value * (slider.getMaximum())) ); - } - - private void updateHSBTextFields( float hue, float saturation, float brightness ) { - int h = Math.round(hue * 359); - int s = Math.round(saturation * 100); - int b = Math.round(brightness * 100); - - if (((Integer)hField.getValue()).intValue() != h) { - hField.setValue(new Integer(h)); - } - if (((Integer)sField.getValue()).intValue() != s) { - sField.setValue(new Integer(s)); - } - if (((Integer)bField.getValue()).intValue() != b) { - bField.setValue(new Integer(b)); - } - } - - /** - * Updates the values of the RGB fields to reflect the new color change - */ - private void updateRGBTextFields( Color color ) { - redField.setText(String.valueOf(color.getRed())); - greenField.setText(String.valueOf(color.getGreen())); - blueField.setText(String.valueOf(color.getBlue())); - } - - /** - * Main internal method of updating the ui controls and the color model. - */ - private void updateHSB( float h, float s, float b ) { - if ( !isAdjusting ) { - isAdjusting = true; - - updatePalette( h, s, b ); - updateSlider( h, s, b ); - updateHSBTextFields( h, s, b ); - - Color color = Color.getHSBColor(h, s, b); - updateRGBTextFields( color ); - - getColorSelectionModel().setSelectedColor( color ); - - isAdjusting = false; - } - } - - /** - * Invoked automatically when the model's state changes. - * It is also called by installChooserPanel to allow - * you to set up the initial state of your chooser. - * Override this method to update your ChooserPanel. - */ - public void updateChooser() { - if ( !isAdjusting ) { - float[] hsb = getHSBColorFromModel(); - updateHSB( hsb[0], hsb[1], hsb[2] ); - } - } - - public void installChooserPanel(JColorChooser enclosingChooser) { - super.installChooserPanel(enclosingChooser); - setInheritsPopupMenu(true); - addHierarchyListener(this); - } - - /** - * Invoked when the panel is removed from the chooser. - */ - public void uninstallChooserPanel(JColorChooser enclosingChooser) { - super.uninstallChooserPanel(enclosingChooser); - cleanupPalettesIfNecessary(); - removeAll(); - removeHierarchyListener(this); - } - - /** - * Returns an float array containing the HSB values of the selected color from - * the ColorSelectionModel - */ - private float[] getHSBColorFromModel() { - Color color = getColorFromModel(); - float[] hsb = new float[3]; - Color.RGBtoHSB( color.getRed(), color.getGreen(), color.getBlue(), hsb ); - - return hsb; - } - - /** - * Builds a new chooser panel. - */ - protected void buildChooser() { - setLayout(new BorderLayout()); - JComponent spp = buildSliderPalettePanel(); - spp.setInheritsPopupMenu(true); - add(spp, BorderLayout.BEFORE_LINE_BEGINS); - - JPanel controlHolder = new JPanel(new SmartGridLayout(1,3)); - JComponent hsbControls = buildHSBControls(); - hsbControls.setInheritsPopupMenu(true); - controlHolder.add(hsbControls); - - controlHolder.add(new JLabel(" ")); // spacer - - JComponent rgbControls = buildRGBControls(); - rgbControls.setInheritsPopupMenu(true); - controlHolder.add(rgbControls); - controlHolder.setInheritsPopupMenu(true); - - controlHolder.setBorder(new EmptyBorder( 10, 5, 10, 5)); - add( controlHolder, BorderLayout.CENTER); - } - - /** - * Creates the panel with the uneditable RGB field - */ - private JComponent buildRGBControls() { - JPanel panel = new JPanel(new SmartGridLayout(2,3)); - panel.setInheritsPopupMenu(true); - - Color color = getColorFromModel(); - redField = new JTextField( String.valueOf(color.getRed()), 3 ); - redField.setEditable(false); - redField.setHorizontalAlignment( JTextField.RIGHT ); - redField.setInheritsPopupMenu(true); - - greenField = new JTextField(String.valueOf(color.getGreen()), 3 ); - greenField.setEditable(false); - greenField.setHorizontalAlignment( JTextField.RIGHT ); - greenField.setInheritsPopupMenu(true); - - blueField = new JTextField( String.valueOf(color.getBlue()), 3 ); - blueField.setEditable(false); - blueField.setHorizontalAlignment( JTextField.RIGHT ); - blueField.setInheritsPopupMenu(true); - - Locale locale = getLocale(); - String redString = UIManager.getString("ColorChooser.hsbRedText", locale); - String greenString = UIManager.getString("ColorChooser.hsbGreenText", locale); - String blueString = UIManager.getString("ColorChooser.hsbBlueText", locale); - - panel.add( new JLabel(redString) ); - panel.add( redField ); - panel.add( new JLabel(greenString) ); - panel.add( greenField ); - panel.add( new JLabel(blueString) ); - panel.add( blueField ); - - return panel; - } - - /** - * Creates the panel with the editable HSB fields and the radio buttons. - */ - private JComponent buildHSBControls() { - - Locale locale = getLocale(); - String hueString = UIManager.getString("ColorChooser.hsbHueText", locale); - String saturationString = UIManager.getString("ColorChooser.hsbSaturationText", locale); - String brightnessString = UIManager.getString("ColorChooser.hsbBrightnessText", locale); - - RadioButtonHandler handler = new RadioButtonHandler(); - - hRadio = new JRadioButton(hueString); - hRadio.addActionListener(handler); - hRadio.setSelected(true); - hRadio.setInheritsPopupMenu(true); - - sRadio = new JRadioButton(saturationString); - sRadio.addActionListener(handler); - sRadio.setInheritsPopupMenu(true); - - bRadio = new JRadioButton(brightnessString); - bRadio.addActionListener(handler); - bRadio.setInheritsPopupMenu(true); - - ButtonGroup group = new ButtonGroup(); - group.add(hRadio); - group.add(sRadio); - group.add(bRadio); - - float[] hsb = getHSBColorFromModel(); - - hField = new JSpinner(new SpinnerNumberModel((int)(hsb[0] * 359), 0, 359, 1)); - sField = new JSpinner(new SpinnerNumberModel((int)(hsb[1] * 100), 0, 100, 1)); - bField = new JSpinner(new SpinnerNumberModel((int)(hsb[2] * 100), 0, 100, 1)); - - hField.addChangeListener(this); - sField.addChangeListener(this); - bField.addChangeListener(this); - - hField.setInheritsPopupMenu(true); - sField.setInheritsPopupMenu(true); - bField.setInheritsPopupMenu(true); - - JPanel panel = new JPanel( new SmartGridLayout(2, 3) ); - - panel.add(hRadio); - panel.add(hField); - panel.add(sRadio); - panel.add(sField); - panel.add(bRadio); - panel.add(bField); - panel.setInheritsPopupMenu(true); - - return panel; - } - - /** - * Handler for the radio button classes. - */ - private class RadioButtonHandler implements ActionListener { - public void actionPerformed(ActionEvent evt) { - Object obj = evt.getSource(); - - if (obj instanceof JRadioButton) { - JRadioButton button = (JRadioButton)obj; - if (button == hRadio) { - setMode(HUE_MODE); - } else if (button == sRadio) { - setMode(SATURATION_MODE); - } else if (button == bRadio) { - setMode(BRIGHTNESS_MODE); - } - } - } - } - - private void setMode(int mode) { - if (currentMode == mode) { - return; - } - - isAdjusting = true; // Ensure no events propagate from changing slider value. - currentMode = mode; - - float[] hsb = getHSBColorFromModel(); - - switch (currentMode) { - case HUE_MODE: - slider.setInverted(true); - slider.setMaximum(MAX_HUE_VALUE); - palette.setValues(HSBImage.HSQUARE, hsb[0], 1.0f, 1.0f); - sliderPalette.setValues(HSBImage.HSLIDER, 0f, 1.0f, 1.0f); - break; - case SATURATION_MODE: - slider.setInverted(false); - slider.setMaximum(MAX_SATURATION_VALUE); - palette.setValues(HSBImage.SSQUARE, hsb[0], hsb[1], 1.0f); - sliderPalette.setValues(HSBImage.SSLIDER, hsb[0], 1.0f, 1.0f); - break; - case BRIGHTNESS_MODE: - slider.setInverted(false); - slider.setMaximum(MAX_BRIGHTNESS_VALUE); - palette.setValues(HSBImage.BSQUARE, hsb[0], 1.0f, hsb[2]); - sliderPalette.setValues(HSBImage.BSLIDER, hsb[0], 1.0f, 1.0f); - break; - } - - isAdjusting = false; - - palette.nextFrame(); - sliderPalette.nextFrame(); - - updateChooser(); - } - - protected JComponent buildSliderPalettePanel() { - - // This slider has to have a minimum of 0. A lot of math in this file is simplified due to this. - slider = new JSlider(JSlider.VERTICAL, 0, MAX_HUE_VALUE, 0); - slider.setInverted(true); - slider.setPaintTrack(false); - slider.setPreferredSize(new Dimension(slider.getPreferredSize().width, PALETTE_DIMENSION + 15)); - slider.addChangeListener(this); - slider.setInheritsPopupMenu(true); - // We're not painting ticks, but need to ask UI classes to - // paint arrow shape anyway, if possible. - slider.putClientProperty("Slider.paintThumbArrowShape", Boolean.TRUE); - paletteLabel = createPaletteLabel(); - addPaletteListeners(); - sliderPaletteLabel = new JLabel(); - - JPanel panel = new JPanel(); - panel.add( paletteLabel ); - panel.add( slider ); - panel.add( sliderPaletteLabel ); - - initializePalettesIfNecessary(); - - return panel; - } - - private void initializePalettesIfNecessary() { - if (palette != null) { - return; - } - - float[] hsb = getHSBColorFromModel(); - - switch(currentMode){ - case HUE_MODE: - palette = new HSBImage(HSBImage.HSQUARE, PALETTE_DIMENSION, PALETTE_DIMENSION, hsb[0], 1.0f, 1.0f); - sliderPalette = new HSBImage(HSBImage.HSLIDER, 16, PALETTE_DIMENSION, 0f, 1.0f, 1.0f); - break; - case SATURATION_MODE: - palette = new HSBImage(HSBImage.SSQUARE, PALETTE_DIMENSION, PALETTE_DIMENSION, 1.0f, hsb[1], 1.0f); - sliderPalette = new HSBImage(HSBImage.SSLIDER, 16, PALETTE_DIMENSION, 1.0f, 0f, 1.0f); - break; - case BRIGHTNESS_MODE: - palette = new HSBImage(HSBImage.BSQUARE, PALETTE_DIMENSION, PALETTE_DIMENSION, 1.0f, 1.0f, hsb[2]); - sliderPalette = new HSBImage(HSBImage.BSLIDER, 16, PALETTE_DIMENSION, 1.0f, 1.0f, 0f); - break; - } - paletteImage = Toolkit.getDefaultToolkit().createImage(palette); - sliderPaletteImage = Toolkit.getDefaultToolkit().createImage(sliderPalette); - - paletteLabel.setIcon(new ImageIcon(paletteImage)); - sliderPaletteLabel.setIcon(new ImageIcon(sliderPaletteImage)); - } - - private void cleanupPalettesIfNecessary() { - if (palette == null) { - return; - } - - palette.aborted = true; - sliderPalette.aborted = true; - - palette.nextFrame(); - sliderPalette.nextFrame(); - - palette = null; - sliderPalette = null; - - paletteImage = null; - sliderPaletteImage = null; - - paletteLabel.setIcon(null); - sliderPaletteLabel.setIcon(null); - } - - protected JLabel createPaletteLabel() { - return new JLabel() { - protected void paintComponent( Graphics g ) { - super.paintComponent( g ); - g.setColor( Color.white ); - g.drawOval( paletteSelection.x - 4, paletteSelection.y - 4, 8, 8 ); - } - }; - } - - public String getDisplayName() { - return UIManager.getString("ColorChooser.hsbNameText", getLocale()); - } - - /** - * Provides a hint to the look and feel as to the - * KeyEvent.VK constant that can be used as a mnemonic to - * access the panel. A return value <= 0 indicates there is no mnemonic. - *

- * The return value here is a hint, it is ultimately up to the look - * and feel to honor the return value in some meaningful way. - *

- * This implementation looks up the value from the default - * ColorChooser.hsbMnemonic, or if it - * isn't available (or not an Integer) returns -1. - * The lookup for the default is done through the UIManager: - * UIManager.get("ColorChooser.rgbMnemonic");. - * - * @return KeyEvent.VK constant identifying the mnemonic; <= 0 for no - * mnemonic - * @see #getDisplayedMnemonicIndex - * @since 1.4 - */ - public int getMnemonic() { - return getInt("ColorChooser.hsbMnemonic", -1); - } - - /** - * Provides a hint to the look and feel as to the index of the character in - * getDisplayName that should be visually identified as the - * mnemonic. The look and feel should only use this if - * getMnemonic returns a value > 0. - *

- * The return value here is a hint, it is ultimately up to the look - * and feel to honor the return value in some meaningful way. For example, - * a look and feel may wish to render each - * AbstractColorChooserPanel in a JTabbedPane, - * and further use this return value to underline a character in - * the getDisplayName. - *

- * This implementation looks up the value from the default - * ColorChooser.rgbDisplayedMnemonicIndex, or if it - * isn't available (or not an Integer) returns -1. - * The lookup for the default is done through the UIManager: - * UIManager.get("ColorChooser.hsbDisplayedMnemonicIndex");. - * - * @return Character index to render mnemonic for; -1 to provide no - * visual identifier for this panel. - * @see #getMnemonic - * @since 1.4 - */ - public int getDisplayedMnemonicIndex() { - return getInt("ColorChooser.hsbDisplayedMnemonicIndex", -1); - } - - public Icon getSmallDisplayIcon() { - return null; - } - - public Icon getLargeDisplayIcon() { - return null; - } - - /** - * Class for the slider and palette images. - */ - class HSBImage extends SyntheticImage { - protected float h = .0f; - protected float s = .0f; - protected float b = .0f; - protected float[] hsb = new float[3]; - - protected boolean isDirty = true; - protected int cachedY; - protected int cachedColor; - protected int type; - - private static final int HSQUARE = 0; - private static final int SSQUARE = 1; - private static final int BSQUARE = 2; - private static final int HSLIDER = 3; - private static final int SSLIDER = 4; - private static final int BSLIDER = 5; - - protected HSBImage(int type, int width, int height, float h, float s, float b) { - super(width, height); - setValues(type, h, s, b); - } - - public void setValues(int type, float h, float s, float b) { - this.type = type; - cachedY = -1; - cachedColor = 0; - setHue( h ); - setSaturation( s ); - setBrightness( b ); - } - - public final void setHue( float hue ) { - h = hue; - } - - public final void setSaturation( float saturation ) { - s = saturation; - } - - public final void setBrightness( float brightness ) { - b = brightness; - } - - public final float getHue() { - return h; - } - - public final float getSaturation() { - return s; - } - - public final float getBrightness() { - return b; - } - - protected boolean isStatic() { - return false; - } - - public synchronized void nextFrame() { - isDirty = true; - notifyAll(); - } - - public synchronized void addConsumer(ImageConsumer ic) { - isDirty = true; - super.addConsumer(ic); - } - - private int getRGBForLocation( int x, int y ) { - if (type >= HSLIDER && y == cachedY) { - return cachedColor; - } - - getHSBForLocation( x, y, hsb ); - cachedY = y; - cachedColor = Color.HSBtoRGB( hsb[0], hsb[1], hsb[2] ); - - return cachedColor; - } - - public void getHSBForLocation( int x, int y, float[] hsbArray ) { - switch (type) { - case HSQUARE: { - float saturationStep = ((float)x) / width; - float brightnessStep = ((float)y) / height; - hsbArray[0] = h; - hsbArray[1] = s - saturationStep; - hsbArray[2] = b - brightnessStep; - break; - } - case SSQUARE: { - float brightnessStep = ((float)y) / height; - float step = 1.0f / ((float)width); - hsbArray[0] = x * step; - hsbArray[1] = s; - hsbArray[2] = 1.0f - brightnessStep; - break; - } - case BSQUARE: { - float saturationStep = ((float)y) / height; - float step = 1.0f / ((float)width); - hsbArray[0] = x * step; - hsbArray[1] = 1.0f - saturationStep; - hsbArray[2] = b; - break; - } - case HSLIDER: { - float step = 1.0f / ((float)height); - hsbArray[0] = y * step; - hsbArray[1] = s; - hsbArray[2] = b; - break; - } - case SSLIDER: { - float saturationStep = ((float)y) / height; - hsbArray[0] = h; - hsbArray[1] = s - saturationStep; - hsbArray[2] = b; - break; - } - case BSLIDER: { - float brightnessStep = ((float)y) / height; - hsbArray[0] = h; - hsbArray[1] = s; - hsbArray[2] = b - brightnessStep; - break; - } - } - } - - /** - * Overriden method from SyntheticImage - */ - protected void computeRow( int y, int[] row ) { - if ( y == 0 ) { - synchronized ( this ) { - try { - while ( !isDirty ) { - wait(); - } - } catch (InterruptedException ie) { - } - isDirty = false; - } - } - - if (aborted) { - return; - } - - for ( int i = 0; i < row.length; ++i ) { - row[i] = getRGBForLocation( i, y ); - } - } - } - - public void stateChanged(ChangeEvent e) { - if (e.getSource() == slider) { - boolean modelIsAdjusting = slider.getModel().getValueIsAdjusting(); - - if (!modelIsAdjusting && !isAdjusting) { - int sliderValue = slider.getValue(); - int sliderRange = slider.getMaximum(); - float value = (float)sliderValue / (float)sliderRange; - - float[] hsb = getHSBColorFromModel(); - - switch ( currentMode ){ - case HUE_MODE: - updateHSB(value, hsb[1], hsb[2]); - break; - case SATURATION_MODE: - updateHSB(hsb[0], value, hsb[2]); - break; - case BRIGHTNESS_MODE: - updateHSB(hsb[0], hsb[1], value); - break; - } - } - } else if (e.getSource() instanceof JSpinner) { - float hue = ((Integer)hField.getValue()).floatValue() / 359f; - float saturation = ((Integer)sField.getValue()).floatValue() / 100f; - float brightness = ((Integer)bField.getValue()).floatValue() / 100f; - - updateHSB(hue, saturation, brightness); - } - } - - public void hierarchyChanged(HierarchyEvent he) { - if ((he.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) { - if (isDisplayable()) { - initializePalettesIfNecessary(); - } else { - cleanupPalettesIfNecessary(); - } - } - } - -} diff --git a/src/share/classes/javax/swing/colorchooser/DefaultRGBChooserPanel.java b/src/share/classes/javax/swing/colorchooser/DefaultRGBChooserPanel.java deleted file mode 100644 index 28850dffa..000000000 --- a/src/share/classes/javax/swing/colorchooser/DefaultRGBChooserPanel.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright 1998-2004 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -package javax.swing.colorchooser; - -import javax.swing.*; -import javax.swing.event.*; -import java.awt.*; -import java.util.Locale; - -/** - * The standard RGB chooser. - *

- * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is - * appropriate for short term storage or RMI between applications running - * the same version of Swing. As of 1.4, support for long term storage - * of all JavaBeansTM - * has been added to the java.beans package. - * Please see {@link java.beans.XMLEncoder}. - * - * @author Steve Wilson - * @author Mark Davidson - * @see JColorChooser - * @see AbstractColorChooserPanel - */ -class DefaultRGBChooserPanel extends AbstractColorChooserPanel implements ChangeListener { - - protected JSlider redSlider; - protected JSlider greenSlider; - protected JSlider blueSlider; - protected JSpinner redField; - protected JSpinner blueField; - protected JSpinner greenField; - - private final int minValue = 0; - private final int maxValue = 255; - - private boolean isAdjusting = false; // indicates the fields are being set internally - - public DefaultRGBChooserPanel() { - super(); - setInheritsPopupMenu(true); - } - - /** - * Sets the values of the controls to reflect the color - */ - private void setColor( Color newColor ) { - int red = newColor.getRed(); - int blue = newColor.getBlue(); - int green = newColor.getGreen(); - - if (redSlider.getValue() != red) { - redSlider.setValue(red); - } - if (greenSlider.getValue() != green) { - greenSlider.setValue(green); - } - if (blueSlider.getValue() != blue) { - blueSlider.setValue(blue); - } - - if (((Integer)redField.getValue()).intValue() != red) - redField.setValue(new Integer(red)); - if (((Integer)greenField.getValue()).intValue() != green) - greenField.setValue(new Integer(green)); - if (((Integer)blueField.getValue()).intValue() != blue ) - blueField.setValue(new Integer(blue)); - } - - public String getDisplayName() { - return UIManager.getString("ColorChooser.rgbNameText", getLocale()); - } - - /** - * Provides a hint to the look and feel as to the - * KeyEvent.VK constant that can be used as a mnemonic to - * access the panel. A return value <= 0 indicates there is no mnemonic. - *

- * The return value here is a hint, it is ultimately up to the look - * and feel to honor the return value in some meaningful way. - *

- * This implementation looks up the value from the default - * ColorChooser.rgbMnemonic, or if it - * isn't available (or not an Integer) returns -1. - * The lookup for the default is done through the UIManager: - * UIManager.get("ColorChooser.rgbMnemonic");. - * - * @return KeyEvent.VK constant identifying the mnemonic; <= 0 for no - * mnemonic - * @see #getDisplayedMnemonicIndex - * @since 1.4 - */ - public int getMnemonic() { - return getInt("ColorChooser.rgbMnemonic", -1); - } - - /** - * Provides a hint to the look and feel as to the index of the character in - * getDisplayName that should be visually identified as the - * mnemonic. The look and feel should only use this if - * getMnemonic returns a value > 0. - *

- * The return value here is a hint, it is ultimately up to the look - * and feel to honor the return value in some meaningful way. For example, - * a look and feel may wish to render each - * AbstractColorChooserPanel in a JTabbedPane, - * and further use this return value to underline a character in - * the getDisplayName. - *

- * This implementation looks up the value from the default - * ColorChooser.rgbDisplayedMnemonicIndex, or if it - * isn't available (or not an Integer) returns -1. - * The lookup for the default is done through the UIManager: - * UIManager.get("ColorChooser.rgbDisplayedMnemonicIndex");. - * - * @return Character index to render mnemonic for; -1 to provide no - * visual identifier for this panel. - * @see #getMnemonic - * @since 1.4 - */ - public int getDisplayedMnemonicIndex() { - return getInt("ColorChooser.rgbDisplayedMnemonicIndex", -1); - } - - public Icon getSmallDisplayIcon() { - return null; - } - - public Icon getLargeDisplayIcon() { - return null; - } - - /** - * The background color, foreground color, and font are already set to the - * defaults from the defaults table before this method is called. - */ - public void installChooserPanel(JColorChooser enclosingChooser) { - super.installChooserPanel(enclosingChooser); - } - - protected void buildChooser() { - - Locale locale = getLocale(); - String redString = UIManager.getString("ColorChooser.rgbRedText", locale); - String greenString = UIManager.getString("ColorChooser.rgbGreenText", locale); - String blueString = UIManager.getString("ColorChooser.rgbBlueText", locale); - - setLayout( new BorderLayout() ); - Color color = getColorFromModel(); - - - JPanel enclosure = new JPanel(); - enclosure.setLayout( new SmartGridLayout( 3, 3 ) ); - enclosure.setInheritsPopupMenu(true); - - // The panel that holds the sliders - - add( enclosure, BorderLayout.CENTER ); - // sliderPanel.setBorder(new LineBorder(Color.black)); - - // The row for the red value - JLabel l = new JLabel(redString); - l.setDisplayedMnemonic(getInt("ColorChooser.rgbRedMnemonic", -1)); - enclosure.add(l); - redSlider = new JSlider(JSlider.HORIZONTAL, 0, 255, color.getRed()); - redSlider.setMajorTickSpacing( 85 ); - redSlider.setMinorTickSpacing( 17 ); - redSlider.setPaintTicks( true ); - redSlider.setPaintLabels( true ); - redSlider.setInheritsPopupMenu(true); - enclosure.add( redSlider ); - redField = new JSpinner( - new SpinnerNumberModel(color.getRed(), minValue, maxValue, 1)); - l.setLabelFor(redSlider); - redField.setInheritsPopupMenu(true); - JPanel redFieldHolder = new JPanel(new CenterLayout()); - redFieldHolder.setInheritsPopupMenu(true); - redField.addChangeListener(this); - redFieldHolder.add(redField); - enclosure.add(redFieldHolder); - - - // The row for the green value - l = new JLabel(greenString); - l.setDisplayedMnemonic(getInt("ColorChooser.rgbGreenMnemonic", -1)); - enclosure.add(l); - greenSlider = new JSlider(JSlider.HORIZONTAL, 0, 255, color.getGreen()); - greenSlider.setMajorTickSpacing( 85 ); - greenSlider.setMinorTickSpacing( 17 ); - greenSlider.setPaintTicks( true ); - greenSlider.setPaintLabels( true ); - greenSlider.setInheritsPopupMenu(true); - enclosure.add(greenSlider); - greenField = new JSpinner( - new SpinnerNumberModel(color.getGreen(), minValue, maxValue, 1)); - l.setLabelFor(greenSlider); - greenField.setInheritsPopupMenu(true); - JPanel greenFieldHolder = new JPanel(new CenterLayout()); - greenFieldHolder.add(greenField); - greenFieldHolder.setInheritsPopupMenu(true); - greenField.addChangeListener(this); - enclosure.add(greenFieldHolder); - - // The slider for the blue value - l = new JLabel(blueString); - l.setDisplayedMnemonic(getInt("ColorChooser.rgbBlueMnemonic", -1)); - enclosure.add(l); - blueSlider = new JSlider(JSlider.HORIZONTAL, 0, 255, color.getBlue()); - blueSlider.setMajorTickSpacing( 85 ); - blueSlider.setMinorTickSpacing( 17 ); - blueSlider.setPaintTicks( true ); - blueSlider.setPaintLabels( true ); - blueSlider.setInheritsPopupMenu(true); - enclosure.add(blueSlider); - blueField = new JSpinner( - new SpinnerNumberModel(color.getBlue(), minValue, maxValue, 1)); - l.setLabelFor(blueSlider); - blueField.setInheritsPopupMenu(true); - JPanel blueFieldHolder = new JPanel(new CenterLayout()); - blueFieldHolder.add(blueField); - blueField.addChangeListener(this); - blueFieldHolder.setInheritsPopupMenu(true); - enclosure.add(blueFieldHolder); - - redSlider.addChangeListener( this ); - greenSlider.addChangeListener( this ); - blueSlider.addChangeListener( this ); - - redSlider.putClientProperty("JSlider.isFilled", Boolean.TRUE); - greenSlider.putClientProperty("JSlider.isFilled", Boolean.TRUE); - blueSlider.putClientProperty("JSlider.isFilled", Boolean.TRUE); - } - - public void uninstallChooserPanel(JColorChooser enclosingChooser) { - super.uninstallChooserPanel(enclosingChooser); - removeAll(); - } - - public void updateChooser() { - if (!isAdjusting) { - isAdjusting = true; - - setColor(getColorFromModel()); - - isAdjusting = false; - } - } - - public void stateChanged( ChangeEvent e ) { - if ( e.getSource() instanceof JSlider && !isAdjusting) { - - int red = redSlider.getValue(); - int green = greenSlider.getValue(); - int blue = blueSlider.getValue() ; - Color color = new Color (red, green, blue); - - getColorSelectionModel().setSelectedColor(color); - } else if (e.getSource() instanceof JSpinner && !isAdjusting) { - - int red = ((Integer)redField.getValue()).intValue(); - int green = ((Integer)greenField.getValue()).intValue(); - int blue = ((Integer)blueField.getValue()).intValue(); - Color color = new Color (red, green, blue); - - getColorSelectionModel().setSelectedColor(color); - } - } - -} diff --git a/src/share/classes/javax/swing/colorchooser/DiagramComponent.java b/src/share/classes/javax/swing/colorchooser/DiagramComponent.java new file mode 100644 index 000000000..04905589e --- /dev/null +++ b/src/share/classes/javax/swing/colorchooser/DiagramComponent.java @@ -0,0 +1,160 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.swing.colorchooser; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.image.BufferedImage; +import javax.swing.JComponent; + +final class DiagramComponent extends JComponent implements MouseListener, MouseMotionListener { + + private final ColorPanel panel; + private final boolean diagram; + + private final Insets insets = new Insets(0, 0, 0, 0); + + private int width; + private int height; + + private int[] array; + private BufferedImage image; + + DiagramComponent(ColorPanel panel, boolean diagram) { + this.panel = panel; + this.diagram = diagram; + addMouseListener(this); + addMouseMotionListener(this); + } + + @Override + protected void paintComponent(Graphics g) { + getInsets(this.insets); + this.width = getWidth() - this.insets.left - this.insets.right; + this.height = getHeight() - this.insets.top - this.insets.bottom; + + boolean update = (this.image == null) + || (this.width != this.image.getWidth()) + || (this.height != this.image.getHeight()); + if (update) { + int size = this.width * this.height; + if ((this.array == null) || (this.array.length < size)) { + this.array = new int[size]; + } + this.image = new BufferedImage(this.width, this.height, BufferedImage.TYPE_INT_RGB); + } + { + float dx = 1.0f / (float) (this.width - 1); + float dy = 1.0f / (float) (this.height - 1); + + int offset = 0; + float y = 0.0f; + for (int h = 0; h < this.height; h++, y += dy) { + if (this.diagram) { + float x = 0.0f; + for (int w = 0; w < this.width; w++, x += dx, offset++) { + this.array[offset] = this.panel.getColor(x, y); + } + } + else { + int color = this.panel.getColor(y); + for (int w = 0; w < this.width; w++, offset++) { + this.array[offset] = color; + } + } + } + } + this.image.setRGB(0, 0, this.width, this.height, this.array, 0, this.width); + g.drawImage(this.image, this.insets.left, this.insets.top, this.width, this.height, this); + if (isEnabled()) { + this.width--; + this.height--; + g.setXORMode(Color.WHITE); + g.setColor(Color.BLACK); + if (this.diagram) { + int x = getValue(this.panel.getValueX(), this.insets.left, this.width); + int y = getValue(this.panel.getValueY(), this.insets.top, this.height); + g.drawLine(x - 8, y, x + 8, y); + g.drawLine(x, y - 8, x, y + 8); + } + else { + int z = getValue(this.panel.getValueZ(), this.insets.top, this.height); + g.drawLine(this.insets.left, z, this.insets.left + this.width, z); + } + g.setPaintMode(); + } + } + + public void mousePressed(MouseEvent event) { + mouseDragged(event); + } + + public void mouseReleased(MouseEvent event) { + } + + public void mouseClicked(MouseEvent event) { + } + + public void mouseEntered(MouseEvent event) { + } + + public void mouseExited(MouseEvent event) { + } + + public void mouseMoved(MouseEvent event) { + } + + public void mouseDragged(MouseEvent event) { + if (isEnabled()) { + float y = getValue(event.getY(), this.insets.top, this.height); + if (this.diagram) { + float x = getValue(event.getX(), this.insets.left, this.width); + this.panel.setValue(x, y); + } + else { + this.panel.setValue(y); + } + } + } + + private static int getValue(float value, int min, int max) { + return min + (int) (value * (float) (max)); + } + + private static float getValue(int value, int min, int max) { + if (min < value) { + value -= min; + return (value < max) + ? (float) value / (float) max + : 1.0f; + } + return 0.0f; + } +} diff --git a/src/share/classes/javax/swing/colorchooser/SlidingSpinner.java b/src/share/classes/javax/swing/colorchooser/SlidingSpinner.java new file mode 100644 index 000000000..99e9bf298 --- /dev/null +++ b/src/share/classes/javax/swing/colorchooser/SlidingSpinner.java @@ -0,0 +1,118 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.swing.colorchooser; + +import javax.swing.JComponent; +import javax.swing.JSlider; +import javax.swing.JSpinner; +import javax.swing.JSpinner.DefaultEditor; +import javax.swing.SpinnerNumberModel; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +final class SlidingSpinner implements ChangeListener { + + private final ColorPanel panel; + private final JComponent label; + private final SpinnerNumberModel model = new SpinnerNumberModel(); + private final JSlider slider = new JSlider(); + private final JSpinner spinner = new JSpinner(this.model); + private float value; + private boolean internal; + + SlidingSpinner(ColorPanel panel, JComponent label) { + this.panel = panel; + this.label = label; + this.slider.addChangeListener(this); + this.spinner.addChangeListener(this); + DefaultEditor editor = (DefaultEditor) this.spinner.getEditor(); + ValueFormatter.init(3, false, editor.getTextField()); + editor.setFocusable(false); + this.spinner.setFocusable(false); + } + + JComponent getLabel() { + return this.label; + } + + JSlider getSlider() { + return this.slider; + } + + JSpinner getSpinner() { + return this.spinner; + } + + float getValue() { + return this.value; + } + + void setValue(float value) { + int min = this.slider.getMinimum(); + int max = this.slider.getMaximum(); + this.internal = true; + this.slider.setValue(min + (int) (value * (float) (max - min))); + this.spinner.setValue(Integer.valueOf(this.slider.getValue())); + this.internal = false; + this.value = value; + } + + void setRange(int min, int max) { + this.internal = true; + this.slider.setMinimum(min); + this.slider.setMaximum(max); + this.model.setMinimum(Integer.valueOf(min)); + this.model.setMaximum(Integer.valueOf(max)); + this.internal = false; + } + + void setVisible(boolean visible) { + this.label.setVisible(visible); + this.slider.setVisible(visible); + this.spinner.setVisible(visible); + } + + public void stateChanged(ChangeEvent event) { + if (!this.internal) { + if (this.spinner == event.getSource()) { + Object value = this.spinner.getValue(); + if (value instanceof Integer) { + this.internal = true; + this.slider.setValue((Integer) value); + this.internal = false; + } + } + int value = this.slider.getValue(); + this.internal = true; + this.spinner.setValue(Integer.valueOf(value)); + this.internal = false; + int min = this.slider.getMinimum(); + int max = this.slider.getMaximum(); + this.value = (float) (value - min) / (float) (max - min); + this.panel.colorChanged(); + } + } +} diff --git a/src/share/classes/javax/swing/colorchooser/SyntheticImage.java b/src/share/classes/javax/swing/colorchooser/SyntheticImage.java deleted file mode 100644 index cec69e9c3..000000000 --- a/src/share/classes/javax/swing/colorchooser/SyntheticImage.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 1997-2003 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -package javax.swing.colorchooser; - -import java.awt.*; -import java.awt.image.*; - -/** A helper class to make computing synthetic images a little easier. - * All you need to do is define a subclass that overrides computeRow - * to compute a row of the image. It is passed the y coordinate of the - * row and an array into which to put the pixels in - * - * standard ARGB format. - *

Normal usage looks something like this: - *

 Image i = createImage(new SyntheticImage(200, 100) {
- *       protected void computeRow(int y, int[] row) {
- *         for(int i = width; --i>=0; ) {
- *             int grey = i*255/(width-1);
- *             row[i] = (255<<24)|(grey<<16)|(grey<<8)|grey;
- *         }
- *       }
- *   }
- *  
This creates a image 200 pixels wide and 100 pixels high - * that is a horizontal grey ramp, going from black on the left to - * white on the right. - *

- * If the image is to be a movie, override isStatic to return false, - * y cycling back to 0 is computeRow's signal that the next - * frame has started. It is acceptable (expected?) for computeRow(0,r) - * to pause until the appropriate time to start the next frame. - * - * @author James Gosling - */ -abstract class SyntheticImage implements ImageProducer { - private SyntheticImageGenerator root; - protected int width=10, height=100; - static final ColorModel cm = ColorModel.getRGBdefault(); - public static final int pixMask = 0xFF; - private Thread runner; - protected SyntheticImage() { } - protected SyntheticImage(int w, int h) { width = w; height = h; } - protected void computeRow(int y, int[] row) { - int p = 255-255*y/(height-1); - p = (pixMask<<24)|(p<<16)|(p<<8)|p; - for (int i = row.length; --i>=0; ) row[i] = p; - } - public synchronized void addConsumer(ImageConsumer ic){ - for (SyntheticImageGenerator ics = root; ics != null; ics = ics.next) - if (ics.ic == ic) return; - root = new SyntheticImageGenerator(ic, root, this); - } - public synchronized boolean isConsumer(ImageConsumer ic){ - for (SyntheticImageGenerator ics = root; ics != null; ics = ics.next) - if (ics.ic == ic) return true; - return false; - } - public synchronized void removeConsumer(ImageConsumer ic) { - SyntheticImageGenerator prev = null; - for (SyntheticImageGenerator ics = root; ics != null; ics = ics.next) { - if (ics.ic == ic) { - ics.useful = false; - if (prev!=null) prev.next = ics.next; - else root = ics.next; - return; - } - prev = ics; - } - } - public synchronized void startProduction(ImageConsumer ic) { - addConsumer(ic); - for (SyntheticImageGenerator ics = root; ics != null; ics = ics.next) - if (ics.useful && !ics.isAlive()) - ics.start(); - } - protected boolean isStatic() { return true; } - public void nextFrame(int param) {}//Override if !isStatic - public void requestTopDownLeftRightResend(ImageConsumer ic){} - - protected volatile boolean aborted = false; -} - -class SyntheticImageGenerator extends Thread { - ImageConsumer ic; - boolean useful; - SyntheticImageGenerator next; - SyntheticImage parent; - SyntheticImageGenerator(ImageConsumer ic, SyntheticImageGenerator next, - SyntheticImage parent) { - super("SyntheticImageGenerator"); - this.ic = ic; - this.next = next; - this.parent = parent; - useful = true; - setDaemon(true); - } - public void run() { - ImageConsumer ic = this.ic; - int w = parent.width; - int h = parent.height; - int hints = ic.SINGLEPASS|ic.COMPLETESCANLINES|ic.TOPDOWNLEFTRIGHT; - if (parent.isStatic()) - hints |= ic.SINGLEFRAME; - ic.setHints(hints); - ic.setDimensions(w, h); - ic.setProperties(null); - ic.setColorModel(parent.cm); - - if (useful) { - int[] row=new int[w]; - doPrivileged( new Runnable() { - public void run() { - Thread.currentThread().setPriority(Thread.MIN_PRIORITY); - } - }); - - do { - for (int y = 0; y>= 4; + } + return new String(array).toUpperCase(ENGLISH); + } + throw new ParseException("illegal object", 0); + } + + @Override + protected DocumentFilter getDocumentFilter() { + return this.filter; + } + + public void focusGained(FocusEvent event) { + Object source = event.getSource(); + if (source instanceof JFormattedTextField) { + this.text = (JFormattedTextField) source; + SwingUtilities.invokeLater(this); + } + } + + public void focusLost(FocusEvent event) { + } + + public void run() { + if (this.text != null) { + this.text.selectAll(); + } + } + + private boolean isValid(int length) { + return (0 <= length) && (length <= this.length); + } + + private boolean isValid(String text) { + int length = text.length(); + for (int i = 0; i < length; i++) { + char ch = text.charAt(i); + if (Character.digit(ch, this.radix) < 0) { + return false; + } + } + return true; + } +} diff --git a/src/share/classes/javax/swing/plaf/basic/BasicColorChooserUI.java b/src/share/classes/javax/swing/plaf/basic/BasicColorChooserUI.java index fd63e7fcb..09f9372b9 100644 --- a/src/share/classes/javax/swing/plaf/basic/BasicColorChooserUI.java +++ b/src/share/classes/javax/swing/plaf/basic/BasicColorChooserUI.java @@ -299,8 +299,10 @@ public class BasicColorChooserUI extends ColorChooserUI tabbedPane.addTab(name, centerWrapper); if (mnemonic > 0) { tabbedPane.setMnemonicAt(i, mnemonic); - tabbedPane.setDisplayedMnemonicIndexAt( - i, newPanels[i].getDisplayedMnemonicIndex()); + int index = newPanels[i].getDisplayedMnemonicIndex(); + if (index >= 0) { + tabbedPane.setDisplayedMnemonicIndexAt(i, index); + } } } } diff --git a/test/javax/swing/JColorChooser/Test6524757.java b/test/javax/swing/JColorChooser/Test6524757.java index 0389efc70..dc7fa06ce 100644 --- a/test/javax/swing/JColorChooser/Test6524757.java +++ b/test/javax/swing/JColorChooser/Test6524757.java @@ -1,5 +1,5 @@ /* - * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,14 +34,13 @@ import java.awt.Dimension; import java.util.ArrayList; import java.util.List; import java.util.Locale; -import javax.swing.JButton; +import javax.swing.AbstractButton; import javax.swing.JColorChooser; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; -import javax.swing.JRadioButton; import javax.swing.UIManager; import javax.swing.WindowConstants; import javax.swing.colorchooser.AbstractColorChooserPanel; @@ -59,31 +58,41 @@ public class Test6524757 { "ColorChooser.swatchesNameText", // NON-NLS: string key from DefaultSwatchChooserPanel "ColorChooser.swatchesMnemonic", // NON-NLS: string key from DefaultSwatchChooserPanel:int - "ColorChooser.swatchesDisplayedMnemonicIndex", // NON-NLS: int key from DefaultSwatchChooserPanel "ColorChooser.swatchesSwatchSize", // NON-NLS: dimension key from DefaultSwatchChooserPanel "ColorChooser.swatchesRecentText", // NON-NLS: string key from DefaultSwatchChooserPanel "ColorChooser.swatchesRecentSwatchSize", // NON-NLS: dimension key from DefaultSwatchChooserPanel //NotAvail: "ColorChooser.swatchesDefaultRecentColor", // NON-NLS: color key from DefaultSwatchChooserPanel - "ColorChooser.hsbNameText", // NON-NLS: string key from DefaultHSBChooserPanel - "ColorChooser.hsbMnemonic", // NON-NLS: int key from DefaultHSBChooserPanel - "ColorChooser.hsbDisplayedMnemonicIndex", // NON-NLS: int key from DefaultHSBChooserPanel - "ColorChooser.hsbHueText", // NON-NLS: string key from DefaultHSBChooserPanel - "ColorChooser.hsbSaturationText", // NON-NLS: string key from DefaultHSBChooserPanel - "ColorChooser.hsbBrightnessText", // NON-NLS: string key from DefaultHSBChooserPanel - "ColorChooser.hsbRedText", // NON-NLS: string key from DefaultHSBChooserPanel - "ColorChooser.hsbGreenText", // NON-NLS: string key from DefaultHSBChooserPanel - "ColorChooser.hsbBlueText", // NON-NLS: string key from DefaultHSBChooserPanel - - "ColorChooser.rgbNameText", // NON-NLS: string key from DefaultRGBChooserPanel - "ColorChooser.rgbMnemonic", // NON-NLS: int key from DefaultRGBChooserPanel - "ColorChooser.rgbDisplayedMnemonicIndex", // NON-NLS: int key from DefaultRGBChooserPanel - "ColorChooser.rgbRedText", // NON-NLS: string key from DefaultRGBChooserPanel - "ColorChooser.rgbGreenText", // NON-NLS: string key from DefaultRGBChooserPanel - "ColorChooser.rgbBlueText", // NON-NLS: string key from DefaultRGBChooserPanel - "ColorChooser.rgbRedMnemonic", // NON-NLS: int key from DefaultRGBChooserPanel - "ColorChooser.rgbGreenMnemonic", // NON-NLS: int key from DefaultRGBChooserPanel - "ColorChooser.rgbBlueMnemonic", // NON-NLS: int key from DefaultRGBChooserPanel + "ColorChooser.hsvNameText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.hsvMnemonic", // NON-NLS: int key from HSV ColorChooserPanel + "ColorChooser.hsvHueText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.hsvSaturationText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.hsvValueText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.hsvTransparencyText", // NON-NLS: string key from HSV ColorChooserPanel + + "ColorChooser.hslNameText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.hslMnemonic", // NON-NLS: int key from HSV ColorChooserPanel + "ColorChooser.hslHueText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.hslSaturationText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.hslLightnessText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.hslTransparencyText", // NON-NLS: string key from HSV ColorChooserPanel + + "ColorChooser.rgbNameText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.rgbMnemonic", // NON-NLS: int key from HSV ColorChooserPanel + "ColorChooser.rgbRedText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.rgbGreenText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.rgbBlueText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.rgbAlphaText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.rgbHexCodeText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.rgbHexCodeMnemonic", // NON-NLS: int key from HSV ColorChooserPanel + + "ColorChooser.cmykNameText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.cmykMnemonic", // NON-NLS: int key from HSV ColorChooserPanel + "ColorChooser.cmykCyanText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.cmykMagentaText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.cmykYellowText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.cmykBlackText", // NON-NLS: string key from HSV ColorChooserPanel + "ColorChooser.cmykAlphaText", // NON-NLS: string key from HSV ColorChooserPanel }; private static final Object[] KOREAN = convert(Locale.KOREAN, KEYS); private static final Object[] FRENCH = convert(Locale.FRENCH, KEYS); @@ -91,19 +100,15 @@ public class Test6524757 { public static void main(String[] args) { // it affects Swing because it is not initialized Locale.setDefault(Locale.KOREAN); - Object[] korean = create(); + validate(KOREAN, create()); // it does not affect Swing because it is initialized Locale.setDefault(Locale.CANADA); - Object[] canada = create(); + validate(KOREAN, create()); // it definitely should affect Swing JComponent.setDefaultLocale(Locale.FRENCH); - Object[] french = create(); - - validate(KOREAN, korean); - validate(KOREAN, canada); - validate(FRENCH, french); + validate(FRENCH, create()); } private static void validate(Object[] expected, Object[] actual) { @@ -153,10 +158,47 @@ public class Test6524757 { // process all values List list = new ArrayList(KEYS.length); - addMain(list, dialog); - addSwatch(list, chooser); - addHSB(list, chooser); - addRGB(list, chooser); + + Component component = getC(getC(dialog.getLayeredPane(), 0), 1); + AbstractButton ok = (AbstractButton) getC(component, 0); + AbstractButton cancel = (AbstractButton) getC(component, 1); + AbstractButton reset = (AbstractButton) getC(component, 2); + list.add(ok.getText()); + list.add(cancel.getText()); + list.add(reset.getText()); + list.add(Integer.valueOf(reset.getMnemonic())); + + for (int i = 0; i < 5; i++) { + AbstractColorChooserPanel panel = (AbstractColorChooserPanel) getC(getC(getC(chooser, 0), i), 0); + list.add(panel.getDisplayName()); + list.add(Integer.valueOf(panel.getMnemonic())); + if (i == 0) { + JLabel label = (JLabel) getC(getC(panel, 0), 1); + JPanel upper = (JPanel) getC(getC(getC(panel, 0), 0), 0); + JPanel lower = (JPanel) getC(getC(getC(panel, 0), 2), 0); + addSize(list, upper, 1, 1, 31, 9); + list.add(label.getText()); + addSize(list, lower, 1, 1, 5, 7); + } + else { + Component container = getC(panel, 0); + for (int j = 0; j < 3; j++) { + AbstractButton button = (AbstractButton) getC(container, j); + list.add(button.getText()); + } + JLabel label = (JLabel) getC(container, 3); + list.add(label.getText()); + if (i == 4) { + label = (JLabel) getC(container, 4); + list.add(label.getText()); + } + if (i == 3) { + label = (JLabel) getC(panel, 1); + list.add(label.getText()); + list.add(Integer.valueOf(label.getDisplayedMnemonic())); + } + } + } // close dialog dialog.setVisible(false); @@ -169,56 +211,6 @@ public class Test6524757 { return list.toArray(); } - private static void addMain(List list, JDialog dialog) { - Component component = getC(getC(dialog.getLayeredPane(), 0), 1); - JButton ok = (JButton) getC(component, 0); - JButton cancel = (JButton) getC(component, 1); - JButton reset = (JButton) getC(component, 2); - list.add(ok.getText()); - list.add(cancel.getText()); - list.add(reset.getText()); - list.add(Integer.valueOf(reset.getMnemonic())); - } - - private static void addSwatch(List list, JColorChooser chooser) { - Component component = addPanel(list, chooser, 0); - JLabel label = (JLabel) getC(getC(component, 0), 1); - JPanel upper = (JPanel) getC(getC(getC(component, 0), 0), 0); - JPanel lower = (JPanel) getC(getC(getC(component, 0), 2), 0); - addSize(list, upper, 1, 1, 31, 9); - list.add(label.getText()); - addSize(list, lower, 1, 1, 5, 7); - } - - private static void addHSB(List list, JColorChooser chooser) { - Component component = addPanel(list, chooser, 1); - JRadioButton h = (JRadioButton) getC(getC(getC(component, 1), 0), 0); - JRadioButton s = (JRadioButton) getC(getC(getC(component, 1), 0), 2); - JRadioButton b = (JRadioButton) getC(getC(getC(component, 1), 0), 4); - list.add(h.getText()); - list.add(s.getText()); - list.add(b.getText()); - JLabel red = (JLabel) getC(getC(getC(component, 1), 2), 0); - JLabel green = (JLabel) getC(getC(getC(component, 1), 2), 2); - JLabel blue = (JLabel) getC(getC(getC(component, 1), 2), 4); - list.add(red.getText()); - list.add(green.getText()); - list.add(blue.getText()); - } - - private static void addRGB(List list, JColorChooser chooser) { - Component component = addPanel(list, chooser, 2); - JLabel red = (JLabel) getC(getC(component, 0), 0); - JLabel green = (JLabel) getC(getC(component, 0), 3); - JLabel blue = (JLabel) getC(getC(component, 0), 6); - list.add(red.getText()); - list.add(green.getText()); - list.add(blue.getText()); - list.add(Integer.valueOf(red.getDisplayedMnemonic())); - list.add(Integer.valueOf(green.getDisplayedMnemonic())); - list.add(Integer.valueOf(blue.getDisplayedMnemonic())); - } - private static void addSize(List list, Component component, int x, int y, int w, int h) { Dimension size = component.getPreferredSize(); int width = (size.width + 1) / w - x; @@ -226,14 +218,6 @@ public class Test6524757 { list.add(new Dimension(width, height)); } - private static Component addPanel(List list, JColorChooser chooser, int index) { - AbstractColorChooserPanel panel = (AbstractColorChooserPanel) getC(getC(getC(chooser, 0), index), 0); - list.add(panel.getDisplayName()); - list.add(Integer.valueOf(panel.getMnemonic())); - list.add(Integer.valueOf(panel.getDisplayedMnemonicIndex())); - return panel; - } - private static Component getC(Component component, int index) { Container container = (Container) component; return container.getComponent(index); diff --git a/test/javax/swing/JColorChooser/Test6559154.java b/test/javax/swing/JColorChooser/Test6559154.java new file mode 100644 index 000000000..e84d90539 --- /dev/null +++ b/test/javax/swing/JColorChooser/Test6559154.java @@ -0,0 +1,75 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6559154 + * @summary Tests EDT hanging + * @author Sergey Malenkov + */ + +import java.awt.Component; +import java.awt.Container; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JColorChooser; +import javax.swing.JDialog; +import javax.swing.SwingUtilities; +import javax.swing.Timer; + +public class Test6559154 implements ActionListener, Runnable { + + private JDialog dialog; + + public void actionPerformed(ActionEvent event) { + if (this.dialog != null) { + this.dialog.dispose(); + } + } + + public void run() { + Timer timer = new Timer(1000, this); + timer.setRepeats(false); + timer.start(); + + JColorChooser chooser = new JColorChooser(); + setEnabledRecursive(chooser, false); + + this.dialog = new JDialog(); + this.dialog.add(chooser); + this.dialog.setVisible(true); + } + + private static void setEnabledRecursive(Container container, boolean enabled) { + for (Component component : container.getComponents()) { + component.setEnabled(enabled); + if (component instanceof Container) { + setEnabledRecursive((Container) component, enabled); + } + } + } + + public static void main(String[] args) throws Exception { + SwingUtilities.invokeAndWait(new Test6559154()); + } +} -- GitLab From 906d7e74ee98d9c447b095e34a67ff5b5932566b Mon Sep 17 00:00:00 2001 From: idk Date: Mon, 21 Jul 2008 10:21:42 -0400 Subject: [PATCH 012/139] 6668281: NullPointerException in DefaultTableCellHeaderRenderer.getColumnSortOrder() Reviewed-by: alexp --- .../com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/share/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java b/src/share/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java index e2217e0f3..0f2b39d89 100644 --- a/src/share/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java +++ b/src/share/classes/com/sun/java/swing/plaf/windows/WindowsTableHeaderUI.java @@ -124,7 +124,7 @@ public class WindowsTableHeaderUI extends BasicTableHeaderUI { setIcon(null); sortIcon = null; SortOrder sortOrder = - getColumnSortOrder(header.getTable(), column); + getColumnSortOrder(table, column); if (sortOrder != null) { switch (sortOrder) { case ASCENDING: -- GitLab From 286f83f41decfaef5df81b5e20d1be1be1926b73 Mon Sep 17 00:00:00 2001 From: mlapshin Date: Mon, 21 Jul 2008 19:58:43 +0400 Subject: [PATCH 013/139] 6607130: REGRESSION: JComboBox cell editor isn't hidden if the same value is selected with keyboard Summary: JComboBox cell editor now hides if the same value is selected with keyboard Reviewed-by: peterz, alexp --- .../swing/plaf/basic/BasicComboBoxUI.java | 12 +- .../swing/JComboBox/6607130/bug6607130.java | 149 ++++++++++++++++++ 2 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 test/javax/swing/JComboBox/6607130/bug6607130.java diff --git a/src/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java b/src/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java index f073eeda8..c49387fd9 100644 --- a/src/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java +++ b/src/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java @@ -1509,15 +1509,21 @@ public class BasicComboBoxUI extends ComboBoxUI { || ui.isTableCellEditor) { Object listItem = ui.popup.getList().getSelectedValue(); if (listItem != null) { - comboBox.getModel().setSelectedItem(listItem); - // Ensure that JComboBox.actionPerformed() - // doesn't set editor value as selected item + // Use the selected value from popup + // to set the selected item in combo box, + // but ensure before that JComboBox.actionPerformed() + // won't use editor's value to set the selected item comboBox.getEditor().setItem(listItem); + comboBox.setSelectedItem(listItem); } } comboBox.setPopupVisible(false); } else { + // Hide combo box if it is a table cell editor + if (ui.isTableCellEditor && !comboBox.isEditable()) { + comboBox.setSelectedItem(comboBox.getSelectedItem()); + } // Call the default button binding. // This is a pretty messy way of passing an event through // to the root pane. diff --git a/test/javax/swing/JComboBox/6607130/bug6607130.java b/test/javax/swing/JComboBox/6607130/bug6607130.java new file mode 100644 index 000000000..5d465c9d7 --- /dev/null +++ b/test/javax/swing/JComboBox/6607130/bug6607130.java @@ -0,0 +1,149 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 6607130 + * @summary Checks that JComboBox cell editor is hidden if the same + * item is selected with keyboard. + * Also checks that JComboBox cell editor is hidden if F2 and then + * ENTER were pressed. + * @author Mikhail Lapshin + */ + +import sun.awt.SunToolkit; + +import javax.swing.*; +import javax.swing.table.DefaultTableModel; +import java.awt.*; +import java.awt.event.KeyEvent; + +public class bug6607130 { + private JFrame frame; + private JComboBox cb; + private Robot robot; + + public static void main(String[] args) throws Exception { + final bug6607130 test = new bug6607130(); + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + test.setupUI(); + } + }); + test.test(); + } finally { + if (test.frame != null) { + test.frame.dispose(); + } + } + } + + public bug6607130() throws AWTException { + robot = new Robot(); + } + + private void setupUI() { + frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + DefaultTableModel model = new DefaultTableModel(1, 1); + JTable table = new JTable(model); + + cb = new JComboBox(new String[]{"one", "two", "three"}); + table.getColumnModel().getColumn(0).setCellEditor(new DefaultCellEditor(cb)); + frame.add(table); + + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private void test() throws Exception { + realSync(); + test1(); + realSync(); + checkResult("First test"); + test2(); + realSync(); + checkResult("Second test"); + } + + private void test1() throws Exception { + // Select 'one' + hitKey(KeyEvent.VK_TAB); + realSync(); + hitKey(KeyEvent.VK_F2); + realSync(); + hitKey(KeyEvent.VK_DOWN); + realSync(); + hitKey(KeyEvent.VK_DOWN); + realSync(); + hitKey(KeyEvent.VK_ENTER); + realSync(); + + // Select 'one' again + hitKey(KeyEvent.VK_F2); + realSync(); + hitKey(KeyEvent.VK_DOWN); + realSync(); + hitKey(KeyEvent.VK_ENTER); + realSync(); + } + + private void test2() throws Exception { + // Press F2 and then press ENTER + // Editor should be shown and then closed + hitKey(KeyEvent.VK_F2); + realSync(); + hitKey(KeyEvent.VK_ENTER); + realSync(); + } + + private void checkResult(String testName) { + if (!cb.isShowing()) { + System.out.println(testName + " passed"); + } else { + System.out.println(testName + " failed"); + throw new RuntimeException("JComboBox is showing " + + "after item selection."); + } + } + + private static void realSync() { + ((SunToolkit) (Toolkit.getDefaultToolkit())).realSync(); + } + + public void hitKey(int keycode) { + robot.keyPress(keycode); + robot.keyRelease(keycode); + delay(); + } + + private void delay() { + try { + Thread.sleep(1000); + } catch(InterruptedException ie) { + ie.printStackTrace(); + } + } +} -- GitLab From 905b3ccd73922a84eb05fdab9e86fe38f9b01ce3 Mon Sep 17 00:00:00 2001 From: mlapshin Date: Thu, 24 Jul 2008 14:34:02 +0400 Subject: [PATCH 014/139] 6725409: Unable to localize JInternalFrame system menu during run-time Summary: Use of the static final constants replaced by direct calls of UIManager.getString(). Reviewed-by: alexp --- .../basic/BasicInternalFrameTitlePane.java | 19 +- .../JInternalFrame/6725409/bug6725409.java | 162 ++++++++++++++++++ 2 files changed, 175 insertions(+), 6 deletions(-) create mode 100644 test/javax/swing/JInternalFrame/6725409/bug6725409.java diff --git a/src/share/classes/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java b/src/share/classes/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java index 344a4b2b5..d8f257312 100644 --- a/src/share/classes/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java +++ b/src/share/classes/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java @@ -86,6 +86,7 @@ public class BasicInternalFrameTitlePane extends JComponent protected Action moveAction; protected Action sizeAction; + // These constants are not used in JDK code protected static final String CLOSE_CMD = UIManager.getString("InternalFrameTitlePane.closeButtonText"); protected static final String ICONIFY_CMD = @@ -600,7 +601,8 @@ public class BasicInternalFrameTitlePane extends JComponent */ public class CloseAction extends AbstractAction { public CloseAction() { - super(CLOSE_CMD); + super(UIManager.getString( + "InternalFrameTitlePane.closeButtonText")); } public void actionPerformed(ActionEvent e) { @@ -616,7 +618,8 @@ public class BasicInternalFrameTitlePane extends JComponent */ public class MaximizeAction extends AbstractAction { public MaximizeAction() { - super(MAXIMIZE_CMD); + super(UIManager.getString( + "InternalFrameTitlePane.maximizeButtonText")); } public void actionPerformed(ActionEvent evt) { @@ -644,7 +647,8 @@ public class BasicInternalFrameTitlePane extends JComponent */ public class IconifyAction extends AbstractAction { public IconifyAction() { - super(ICONIFY_CMD); + super(UIManager.getString( + "InternalFrameTitlePane.minimizeButtonText")); } public void actionPerformed(ActionEvent e) { @@ -664,7 +668,8 @@ public class BasicInternalFrameTitlePane extends JComponent */ public class RestoreAction extends AbstractAction { public RestoreAction() { - super(RESTORE_CMD); + super(UIManager.getString( + "InternalFrameTitlePane.restoreButtonText")); } public void actionPerformed(ActionEvent evt) { @@ -690,7 +695,8 @@ public class BasicInternalFrameTitlePane extends JComponent */ public class MoveAction extends AbstractAction { public MoveAction() { - super(MOVE_CMD); + super(UIManager.getString( + "InternalFrameTitlePane.moveButtonText")); } public void actionPerformed(ActionEvent e) { @@ -723,7 +729,8 @@ public class BasicInternalFrameTitlePane extends JComponent */ public class SizeAction extends AbstractAction { public SizeAction() { - super(SIZE_CMD); + super(UIManager.getString( + "InternalFrameTitlePane.sizeButtonText")); } public void actionPerformed(ActionEvent e) { diff --git a/test/javax/swing/JInternalFrame/6725409/bug6725409.java b/test/javax/swing/JInternalFrame/6725409/bug6725409.java new file mode 100644 index 000000000..b118fed88 --- /dev/null +++ b/test/javax/swing/JInternalFrame/6725409/bug6725409.java @@ -0,0 +1,162 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 6725409 + * @summary Checks that JInternalFrame's system menu + * can be localized during run-time + * @author Mikhail Lapshin + */ + +import javax.swing.*; +import java.awt.*; + +public class bug6725409 { + private JFrame frame; + private JInternalFrame iFrame; + private TestTitlePane testTitlePane; + private boolean passed; + + public static void main(String[] args) throws Exception { + try { + UIManager.setLookAndFeel( + new com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel()); + } catch(UnsupportedLookAndFeelException e) { + System.out.println("The test is for Windows LaF only"); + System.exit(0); + } + + final bug6725409 bug6725409 = new bug6725409(); + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + bug6725409.setupUIStep1(); + } + }); + realSync(); + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + bug6725409.setupUIStep2(); + } + }); + realSync(); + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + bug6725409.test(); + } + }); + realSync(); + bug6725409.checkResult(); + } finally { + if (bug6725409.frame != null) { + bug6725409.frame.dispose(); + } + } + } + + private void setupUIStep1() { + frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + JDesktopPane desktop = new JDesktopPane(); + iFrame = new JInternalFrame("Internal Frame", true, true, true, true); + iFrame.setSize(200, 100); + desktop.add(iFrame); + frame.add(desktop); + iFrame.setVisible(true); + + frame.setSize(500, 300); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private void setupUIStep2() { + UIManager.put("InternalFrameTitlePane.restoreButtonText", + "CUSTOM.restoreButtonText"); + UIManager.put("InternalFrameTitlePane.moveButtonText", + "CUSTOM.moveButtonText"); + UIManager.put("InternalFrameTitlePane.sizeButtonText", + "CUSTOM.sizeButtonText"); + UIManager.put("InternalFrameTitlePane.minimizeButtonText", + "CUSTOM.minimizeButtonText"); + UIManager.put("InternalFrameTitlePane.maximizeButtonText", + "CUSTOM.maximizeButtonText"); + UIManager.put("InternalFrameTitlePane.closeButtonText", + "CUSTOM.closeButtonText"); + SwingUtilities.updateComponentTreeUI(frame); + } + + // The test depends on the order of the menu items in + // WindowsInternalFrameTitlePane.systemPopupMenu + private void test() { + testTitlePane = new TestTitlePane(iFrame); + passed = true; + checkMenuItemText(0, "CUSTOM.restoreButtonText"); + checkMenuItemText(1, "CUSTOM.moveButtonText"); + checkMenuItemText(2, "CUSTOM.sizeButtonText"); + checkMenuItemText(3, "CUSTOM.minimizeButtonText"); + checkMenuItemText(4, "CUSTOM.maximizeButtonText"); + // Skip separator + checkMenuItemText(6, "CUSTOM.closeButtonText"); + } + + private void checkMenuItemText(int index, String text) { + JMenuItem menuItem = (JMenuItem) + testTitlePane.getSystemPopupMenu().getComponent(index); + if (!text.equals(menuItem.getText())) { + passed = false; + } + } + + private void checkResult() { + if (passed) { + System.out.println("Test passed"); + } else { + throw new RuntimeException("Unable to localize " + + "JInternalFrame's system menu during run-time"); + } + } + + private static void realSync() { + ((sun.awt.SunToolkit) (Toolkit.getDefaultToolkit())).realSync(); + } + + // Extend WindowsInternalFrameTitlePane to get access to systemPopupMenu + private class TestTitlePane extends + com.sun.java.swing.plaf.windows.WindowsInternalFrameTitlePane { + private JPopupMenu systemPopupMenu; + + public TestTitlePane(JInternalFrame f) { + super(f); + } + + public JPopupMenu getSystemPopupMenu() { + return systemPopupMenu; + } + + protected void addSystemMenuItems(JPopupMenu menu) { + super.addSystemMenuItems(menu); + systemPopupMenu = menu; + } + } +} -- GitLab From c9da1be8d7e0579900ea9f5825e786bcb51bfda2 Mon Sep 17 00:00:00 2001 From: malenkov Date: Thu, 24 Jul 2008 14:51:13 +0400 Subject: [PATCH 015/139] 4778988: CompoundBorder.isBorderOpaque() has incorrect documentation. Reviewed-by: peterz, rupashka --- src/share/classes/javax/swing/border/CompoundBorder.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/share/classes/javax/swing/border/CompoundBorder.java b/src/share/classes/javax/swing/border/CompoundBorder.java index f736cf902..46ac44e50 100644 --- a/src/share/classes/javax/swing/border/CompoundBorder.java +++ b/src/share/classes/javax/swing/border/CompoundBorder.java @@ -79,10 +79,13 @@ public class CompoundBorder extends AbstractBorder { } /** - * Returns whether or not this compound border is opaque. - * Returns true if both the inside and outside borders are - * non-null and opaque; returns false otherwise. + * Returns whether or not the compound border is opaque. + * + * @return {@code true} if the inside and outside borders + * are each either {@code null} or opaque; + * or {@code false} otherwise */ + @Override public boolean isBorderOpaque() { return (outsideBorder == null || outsideBorder.isBorderOpaque()) && (insideBorder == null || insideBorder.isBorderOpaque()); -- GitLab From 24f54a1531f1822f367e79da02f72309e70f408f Mon Sep 17 00:00:00 2001 From: rupashka Date: Thu, 24 Jul 2008 16:43:36 +0400 Subject: [PATCH 016/139] 6722802: Code improvement and warnings removing from the javax.swing.text package Summary: Removed unnecessary castings and other warnings Reviewed-by: peterz Contributed-by: Florian Brunner --- .../javax/swing/text/AbstractDocument.java | 29 +++-- .../javax/swing/text/AsyncBoxView.java | 12 +-- .../javax/swing/text/ComponentView.java | 5 +- .../javax/swing/text/DefaultCaret.java | 5 +- .../javax/swing/text/DefaultFormatter.java | 4 +- .../javax/swing/text/DefaultHighlighter.java | 20 ++-- .../swing/text/DefaultStyledDocument.java | 97 ++++++++--------- .../javax/swing/text/ElementIterator.java | 20 ++-- .../classes/javax/swing/text/FlowView.java | 6 +- .../classes/javax/swing/text/GapContent.java | 14 +-- .../swing/text/InternationalFormatter.java | 9 +- .../javax/swing/text/JTextComponent.java | 73 ++++++------- .../classes/javax/swing/text/LayoutQueue.java | 6 +- .../javax/swing/text/MaskFormatter.java | 6 +- .../javax/swing/text/NumberFormatter.java | 18 +--- .../javax/swing/text/PlainDocument.java | 4 +- .../javax/swing/text/SegmentCache.java | 6 +- .../javax/swing/text/SimpleAttributeSet.java | 6 +- .../javax/swing/text/StringContent.java | 10 +- .../javax/swing/text/StyleContext.java | 38 +++---- .../classes/javax/swing/text/TableView.java | 11 +- .../classes/javax/swing/text/TextAction.java | 8 +- .../javax/swing/text/TextLayoutStrategy.java | 12 +-- .../classes/javax/swing/text/ZoneView.java | 10 +- .../javax/swing/text/html/AccessibleHTML.java | 42 ++++---- .../classes/javax/swing/text/html/CSS.java | 71 ++++++------ .../classes/javax/swing/text/html/HTML.java | 18 ++-- .../javax/swing/text/html/HTMLDocument.java | 69 ++++++------ .../javax/swing/text/html/HTMLEditorKit.java | 25 +++-- .../javax/swing/text/html/HTMLWriter.java | 33 +++--- .../classes/javax/swing/text/html/Map.java | 18 ++-- .../swing/text/html/MinimalHTMLWriter.java | 8 +- .../swing/text/html/OptionListModel.java | 9 +- .../javax/swing/text/html/StyleSheet.java | 102 +++++++++--------- .../javax/swing/text/html/TableView.java | 10 +- .../swing/text/html/parser/AttributeList.java | 2 +- .../javax/swing/text/html/parser/DTD.java | 28 +++-- .../javax/swing/text/html/parser/Element.java | 4 +- .../javax/swing/text/html/parser/Entity.java | 4 +- .../javax/swing/text/html/parser/Parser.java | 14 ++- .../swing/text/html/parser/TagStack.java | 16 --- .../swing/text/rtf/MockAttributeSet.java | 2 +- .../javax/swing/text/rtf/RTFAttributes.java | 13 ++- .../javax/swing/text/rtf/RTFGenerator.java | 42 ++++---- .../javax/swing/text/rtf/RTFParser.java | 2 +- .../javax/swing/text/rtf/RTFReader.java | 102 +++++++++--------- 46 files changed, 499 insertions(+), 564 deletions(-) diff --git a/src/share/classes/javax/swing/text/AbstractDocument.java b/src/share/classes/javax/swing/text/AbstractDocument.java index 406350a9b..a5cbdc239 100644 --- a/src/share/classes/javax/swing/text/AbstractDocument.java +++ b/src/share/classes/javax/swing/text/AbstractDocument.java @@ -123,15 +123,15 @@ public abstract class AbstractDocument implements Document, Serializable { if (defaultI18NProperty == null) { // determine default setting for i18n support - Object o = java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public Object run() { + String o = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public String run() { return System.getProperty(I18NProperty); } } ); if (o != null) { - defaultI18NProperty = Boolean.valueOf((String)o); + defaultI18NProperty = Boolean.valueOf(o); } else { defaultI18NProperty = Boolean.FALSE; } @@ -163,7 +163,7 @@ public abstract class AbstractDocument implements Document, Serializable { */ public Dictionary getDocumentProperties() { if (documentProperties == null) { - documentProperties = new Hashtable(2); + documentProperties = new Hashtable(2); } return documentProperties; } @@ -467,8 +467,7 @@ public abstract class AbstractDocument implements Document, Serializable { * @since 1.4 */ public DocumentListener[] getDocumentListeners() { - return (DocumentListener[])listenerList.getListeners( - DocumentListener.class); + return listenerList.getListeners(DocumentListener.class); } /** @@ -508,8 +507,7 @@ public abstract class AbstractDocument implements Document, Serializable { * @since 1.4 */ public UndoableEditListener[] getUndoableEditListeners() { - return (UndoableEditListener[])listenerList.getListeners( - UndoableEditListener.class); + return listenerList.getListeners(UndoableEditListener.class); } /** @@ -610,7 +608,7 @@ public abstract class AbstractDocument implements Document, Serializable { DefaultDocumentEvent chng = new DefaultDocumentEvent(offs, len, DocumentEvent.EventType.REMOVE); - boolean isComposedTextElement = false; + boolean isComposedTextElement; // Check whether the position of interest is the composed text isComposedTextElement = Utilities.isComposedTextElement(this, offs); @@ -1051,7 +1049,7 @@ public abstract class AbstractDocument implements Document, Serializable { byte levels[] = calculateBidiLevels( firstPStart, lastPEnd ); - Vector newElements = new Vector(); + Vector newElements = new Vector(); // Calculate the first span of characters in the affected range with // the same bidi level. If this level is the same as the level of the @@ -1831,7 +1829,6 @@ public abstract class AbstractDocument implements Document, Serializable { } out.println("["+contentStr+"]"); } catch (BadLocationException e) { - ; } } else { @@ -2460,7 +2457,7 @@ public abstract class AbstractDocument implements Document, Serializable { if(nchildren == 0) return null; - Vector tempVector = new Vector(nchildren); + Vector tempVector = new Vector(nchildren); for(int counter = 0; counter < nchildren; counter++) tempVector.addElement(children[counter]); @@ -2749,7 +2746,7 @@ public abstract class AbstractDocument implements Document, Serializable { // if the number of changes gets too great, start using // a hashtable for to locate the change for a given element. if ((changeLookup == null) && (edits.size() > 10)) { - changeLookup = new Hashtable(); + changeLookup = new Hashtable(); int n = edits.size(); for (int i = 0; i < n; i++) { Object o = edits.elementAt(i); @@ -2918,7 +2915,7 @@ public abstract class AbstractDocument implements Document, Serializable { */ public DocumentEvent.ElementChange getChange(Element elem) { if (changeLookup != null) { - return (DocumentEvent.ElementChange) changeLookup.get(elem); + return changeLookup.get(elem); } int n = edits.size(); for (int i = 0; i < n; i++) { @@ -2937,7 +2934,7 @@ public abstract class AbstractDocument implements Document, Serializable { private int offset; private int length; - private Hashtable changeLookup; + private Hashtable changeLookup; private DocumentEvent.EventType type; } diff --git a/src/share/classes/javax/swing/text/AsyncBoxView.java b/src/share/classes/javax/swing/text/AsyncBoxView.java index 54a0e9735..ae11bde81 100644 --- a/src/share/classes/javax/swing/text/AsyncBoxView.java +++ b/src/share/classes/javax/swing/text/AsyncBoxView.java @@ -25,6 +25,7 @@ package javax.swing.text; import java.util.*; +import java.util.List; import java.awt.*; import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; @@ -58,7 +59,7 @@ public class AsyncBoxView extends View { */ public AsyncBoxView(Element elem, int axis) { super(elem); - stats = new ArrayList(); + stats = new ArrayList(); this.axis = axis; locator = new ChildLocator(); flushTask = new FlushTask(); @@ -197,7 +198,7 @@ public class AsyncBoxView extends View { protected ChildState getChildState(int index) { synchronized(stats) { if ((index >= 0) && (index < stats.size())) { - return (ChildState) stats.get(index); + return stats.get(index); } return null; } @@ -357,7 +358,7 @@ public class AsyncBoxView extends View { synchronized(stats) { // remove the replaced state records for (int i = 0; i < length; i++) { - ChildState cs = (ChildState)stats.remove(offset); + ChildState cs = stats.remove(offset); float csSpan = cs.getMajorSpan(); cs.getChildView().setParent(null); @@ -863,7 +864,7 @@ public class AsyncBoxView extends View { /** * The children and their layout statistics. */ - java.util.List stats; + List stats; /** * Current span along the major axis. This @@ -1110,7 +1111,7 @@ public class AsyncBoxView extends View { */ int updateChildOffsets(float targetOffset) { int n = getViewCount(); - int targetIndex = n - 1;; + int targetIndex = n - 1; int pos = lastValidOffset.getChildView().getStartOffset(); int startIndex = getViewIndex(pos, Position.Bias.Forward); float start = lastValidOffset.getMajorOffset(); @@ -1394,7 +1395,6 @@ public class AsyncBoxView extends View { private float min; private float pref; private float max; - private float align; private boolean minorValid; // major axis diff --git a/src/share/classes/javax/swing/text/ComponentView.java b/src/share/classes/javax/swing/text/ComponentView.java index 62e4bd02a..085d99967 100644 --- a/src/share/classes/javax/swing/text/ComponentView.java +++ b/src/share/classes/javax/swing/text/ComponentView.java @@ -27,6 +27,7 @@ package javax.swing.text; import java.awt.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.Set; import javax.swing.SwingUtilities; import javax.swing.event.*; @@ -434,7 +435,7 @@ public class ComponentView extends View { /** * Shows or hides this component depending on the value of parameter * b. - * @param b If true, shows this component; + * @param b If true, shows this component; * otherwise, hides this component. * @see #isVisible * @since JDK1.1 @@ -480,7 +481,7 @@ public class ComponentView extends View { return yalign; } - public java.util.Set getFocusTraversalKeys(int id) { + public Set getFocusTraversalKeys(int id) { return KeyboardFocusManager.getCurrentKeyboardFocusManager(). getDefaultFocusTraversalKeys(id); } diff --git a/src/share/classes/javax/swing/text/DefaultCaret.java b/src/share/classes/javax/swing/text/DefaultCaret.java index 22ec280ba..e0e156811 100644 --- a/src/share/classes/javax/swing/text/DefaultCaret.java +++ b/src/share/classes/javax/swing/text/DefaultCaret.java @@ -774,8 +774,7 @@ public class DefaultCaret extends Rectangle implements Caret, FocusListener, Mou * @since 1.4 */ public ChangeListener[] getChangeListeners() { - return (ChangeListener[])listenerList.getListeners( - ChangeListener.class); + return listenerList.getListeners(ChangeListener.class); } /** @@ -1330,7 +1329,7 @@ public class DefaultCaret extends Rectangle implements Caret, FocusListener, Mou if (this.dot != this.mark && component != null) { Clipboard clip = getSystemSelection(); if (clip != null) { - String selectedText = null; + String selectedText; if (component instanceof JPasswordField && component.getClientProperty("JPasswordField.cutCopyAllowed") != Boolean.TRUE) { diff --git a/src/share/classes/javax/swing/text/DefaultFormatter.java b/src/share/classes/javax/swing/text/DefaultFormatter.java index 794190472..51dab601c 100644 --- a/src/share/classes/javax/swing/text/DefaultFormatter.java +++ b/src/share/classes/javax/swing/text/DefaultFormatter.java @@ -68,7 +68,7 @@ public class DefaultFormatter extends JFormattedTextField.AbstractFormatter private boolean commitOnEdit; /** Class used to create new instances. */ - private Class valueClass; + private Class valueClass; /** NavigationFilter that forwards calls back to DefaultFormatter. */ private NavigationFilter navigationFilter; @@ -231,7 +231,7 @@ public class DefaultFormatter extends JFormattedTextField.AbstractFormatter * @return Object representation of text */ public Object stringToValue(String string) throws ParseException { - Class vc = getValueClass(); + Class vc = getValueClass(); JFormattedTextField ftf = getFormattedTextField(); if (vc == null && ftf != null) { diff --git a/src/share/classes/javax/swing/text/DefaultHighlighter.java b/src/share/classes/javax/swing/text/DefaultHighlighter.java index 9e3202130..028b24c0e 100644 --- a/src/share/classes/javax/swing/text/DefaultHighlighter.java +++ b/src/share/classes/javax/swing/text/DefaultHighlighter.java @@ -56,7 +56,7 @@ public class DefaultHighlighter extends LayeredHighlighter { // PENDING(prinz) - should cull ranges not visible int len = highlights.size(); for (int i = 0; i < len; i++) { - HighlightInfo info = (HighlightInfo) highlights.elementAt(i); + HighlightInfo info = highlights.elementAt(i); if (!(info instanceof LayeredHighlightInfo)) { // Avoid allocing unless we need it. Rectangle a = component.getBounds(); @@ -66,7 +66,7 @@ public class DefaultHighlighter extends LayeredHighlighter { a.width -= insets.left + insets.right; a.height -= insets.top + insets.bottom; for (; i < len; i++) { - info = (HighlightInfo)highlights.elementAt(i); + info = highlights.elementAt(i); if (!(info instanceof LayeredHighlightInfo)) { Highlighter.HighlightPainter p = info.getPainter(); p.paint(g, info.getStartOffset(), info.getEndOffset(), @@ -159,7 +159,7 @@ public class DefaultHighlighter extends LayeredHighlighter { int p0 = -1; int p1 = -1; for (int i = 0; i < len; i++) { - HighlightInfo hi = (HighlightInfo)highlights.elementAt(i); + HighlightInfo hi = highlights.elementAt(i); if (hi instanceof LayeredHighlightInfo) { LayeredHighlightInfo info = (LayeredHighlightInfo)hi; minX = Math.min(minX, info.x); @@ -195,7 +195,7 @@ public class DefaultHighlighter extends LayeredHighlighter { int p0 = Integer.MAX_VALUE; int p1 = 0; for (int i = 0; i < len; i++) { - HighlightInfo info = (HighlightInfo) highlights.elementAt(i); + HighlightInfo info = highlights.elementAt(i); p0 = Math.min(p0, info.p0.getOffset()); p1 = Math.max(p1, info.p1.getOffset()); } @@ -282,7 +282,7 @@ public class DefaultHighlighter extends LayeredHighlighter { Shape viewBounds, JTextComponent editor, View view) { for (int counter = highlights.size() - 1; counter >= 0; counter--) { - Object tag = highlights.elementAt(counter); + HighlightInfo tag = highlights.elementAt(counter); if (tag instanceof LayeredHighlightInfo) { LayeredHighlightInfo lhi = (LayeredHighlightInfo)tag; int start = lhi.getStartOffset(); @@ -333,7 +333,7 @@ public class DefaultHighlighter extends LayeredHighlighter { private final static Highlighter.Highlight[] noHighlights = new Highlighter.Highlight[0]; - private Vector highlights = new Vector(); // Vector + private Vector highlights = new Vector(); private JTextComponent component; private boolean drawsLayeredHighlights; private SafeDamager safeDamager = new SafeDamager(); @@ -573,8 +573,8 @@ public class DefaultHighlighter extends LayeredHighlighter { * call. */ class SafeDamager implements Runnable { - private Vector p0 = new Vector(10); - private Vector p1 = new Vector(10); + private Vector p0 = new Vector(10); + private Vector p1 = new Vector(10); private Document lastDoc = null; /** @@ -589,8 +589,8 @@ public class DefaultHighlighter extends LayeredHighlighter { int len = p0.size(); for (int i = 0; i < len; i++){ mapper.damageRange(component, - ((Position)p0.get(i)).getOffset(), - ((Position)p1.get(i)).getOffset()); + p0.get(i).getOffset(), + p1.get(i).getOffset()); } } } diff --git a/src/share/classes/javax/swing/text/DefaultStyledDocument.java b/src/share/classes/javax/swing/text/DefaultStyledDocument.java index 4939a7615..d3ba5d98d 100644 --- a/src/share/classes/javax/swing/text/DefaultStyledDocument.java +++ b/src/share/classes/javax/swing/text/DefaultStyledDocument.java @@ -84,7 +84,7 @@ public class DefaultStyledDocument extends AbstractDocument implements StyledDoc */ public DefaultStyledDocument(Content c, StyleContext styles) { super(c, styles); - listeningStyles = new Vector(); + listeningStyles = new Vector is received. */ - private java.util.List _stateInfos; + private List _stateInfos; /** * Current style. @@ -151,7 +152,7 @@ class SynthParser extends HandlerBase { /** * Bindings for the current InputMap */ - private java.util.List _inputMapBindings; + private List _inputMapBindings; /** * ID for the input map. This is cached as @@ -177,30 +178,30 @@ class SynthParser extends HandlerBase { /** * List of ColorTypes. This is populated in startColorType. */ - private java.util.List _colorTypes; + private List _colorTypes; /** * defaultsPropertys are placed here. */ - private Map _defaultsMap; + private Map _defaultsMap; /** * List of SynthStyle.Painters that will be applied to the current style. */ - private java.util.List _stylePainters; + private List _stylePainters; /** * List of SynthStyle.Painters that will be applied to the current state. */ - private java.util.List _statePainters; + private List _statePainters; SynthParser() { _mapping = new HashMap(); - _stateInfos = new ArrayList(); - _colorTypes = new ArrayList(); - _inputMapBindings = new ArrayList(); - _stylePainters = new ArrayList(); - _statePainters = new ArrayList(); + _stateInfos = new ArrayList(); + _colorTypes = new ArrayList(); + _inputMapBindings = new ArrayList(); + _stylePainters = new ArrayList(); + _statePainters = new ArrayList(); } /** @@ -219,7 +220,7 @@ class SynthParser extends HandlerBase { public void parse(InputStream inputStream, DefaultSynthStyleFactory factory, URL urlResourceBase, Class classResourceBase, - Map defaultsMap) + Map defaultsMap) throws ParseException, IllegalArgumentException { if (inputStream == null || factory == null || (urlResourceBase == null && classResourceBase == null)) { @@ -333,7 +334,7 @@ class SynthParser extends HandlerBase { * type type, this will throw an exception. */ private Object lookup(String key, Class type) throws SAXException { - Object value = null; + Object value; if (_handler != null) { if ((value = _handler.lookup(key)) != null) { return checkCast(value, type); @@ -423,15 +424,12 @@ class SynthParser extends HandlerBase { private void endStyle() throws SAXException { int size = _stylePainters.size(); if (size > 0) { - _style.setPainters((ParsedSynthStyle.PainterInfo[]) - _stylePainters.toArray(new ParsedSynthStyle. - PainterInfo[size])); + _style.setPainters(_stylePainters.toArray(new ParsedSynthStyle.PainterInfo[size])); _stylePainters.clear(); } size = _stateInfos.size(); if (size > 0) { - _style.setStateInfo((ParsedSynthStyle.StateInfo[])_stateInfos. - toArray(new ParsedSynthStyle.StateInfo[size])); + _style.setStateInfo(_stateInfos.toArray(new ParsedSynthStyle.StateInfo[size])); _stateInfos.clear(); } _style = null; @@ -501,9 +499,7 @@ class SynthParser extends HandlerBase { private void endState() throws SAXException { int size = _statePainters.size(); if (size > 0) { - _stateInfo.setPainters((ParsedSynthStyle.PainterInfo[]) - _statePainters.toArray(new ParsedSynthStyle. - PainterInfo[size])); + _stateInfo.setPainters(_statePainters.toArray(new ParsedSynthStyle.PainterInfo[size])); _statePainters.clear(); } _stateInfo = null; @@ -684,8 +680,7 @@ class SynthParser extends HandlerBase { int max = 0; for (int counter = _colorTypes.size() - 1; counter >= 0; counter--) { - max = Math.max(max, ((ColorType)_colorTypes.get(counter)). - getID()); + max = Math.max(max, _colorTypes.get(counter).getID()); } if (colors == null || colors.length <= max) { Color[] newColors = new Color[max + 1]; @@ -696,7 +691,7 @@ class SynthParser extends HandlerBase { } for (int counter = _colorTypes.size() - 1; counter >= 0; counter--) { - colors[((ColorType)_colorTypes.get(counter)).getID()] = color; + colors[_colorTypes.get(counter).getID()] = color; } _stateInfo.setColors(colors); } @@ -705,7 +700,7 @@ class SynthParser extends HandlerBase { private void startProperty(AttributeList attributes, Object property) throws SAXException { Object value = null; - Object key = null; + String key = null; // Type of the value: 0=idref, 1=boolean, 2=dimension, 3=insets, // 4=integer,5=string int iType = 0; @@ -1027,7 +1022,7 @@ class SynthParser extends HandlerBase { } } - private void addPainterOrMerge(java.util.List painters, String method, + private void addPainterOrMerge(List painters, String method, SynthPainter painter, int direction) { ParsedSynthStyle.PainterInfo painterInfo; painterInfo = new ParsedSynthStyle.PainterInfo(method, diff --git a/src/share/classes/javax/swing/plaf/synth/SynthSplitPaneUI.java b/src/share/classes/javax/swing/plaf/synth/SynthSplitPaneUI.java index ddbc7ae7e..21466d960 100644 --- a/src/share/classes/javax/swing/plaf/synth/SynthSplitPaneUI.java +++ b/src/share/classes/javax/swing/plaf/synth/SynthSplitPaneUI.java @@ -48,13 +48,13 @@ class SynthSplitPaneUI extends BasicSplitPaneUI implements * Keys to use for forward focus traversal when the JComponent is * managing focus. */ - private static Set managingFocusForwardTraversalKeys; + private static Set managingFocusForwardTraversalKeys; /** * Keys to use for backward focus traversal when the JComponent is * managing focus. */ - private static Set managingFocusBackwardTraversalKeys; + private static Set managingFocusBackwardTraversalKeys; /** * Style for the JSplitPane. @@ -96,7 +96,7 @@ class SynthSplitPaneUI extends BasicSplitPaneUI implements // focus forward traversal key if (managingFocusForwardTraversalKeys==null) { - managingFocusForwardTraversalKeys = new HashSet(); + managingFocusForwardTraversalKeys = new HashSet(); managingFocusForwardTraversalKeys.add( KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0)); } @@ -104,7 +104,7 @@ class SynthSplitPaneUI extends BasicSplitPaneUI implements managingFocusForwardTraversalKeys); // focus backward traversal key if (managingFocusBackwardTraversalKeys==null) { - managingFocusBackwardTraversalKeys = new HashSet(); + managingFocusBackwardTraversalKeys = new HashSet(); managingFocusBackwardTraversalKeys.add( KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK)); } diff --git a/src/share/classes/javax/swing/plaf/synth/SynthStyle.java b/src/share/classes/javax/swing/plaf/synth/SynthStyle.java index c9475f91b..ab9e42b41 100644 --- a/src/share/classes/javax/swing/plaf/synth/SynthStyle.java +++ b/src/share/classes/javax/swing/plaf/synth/SynthStyle.java @@ -53,7 +53,7 @@ public abstract class SynthStyle { /** * Contains the default values for certain properties. */ - private static Map DEFAULT_VALUES; + private static Map DEFAULT_VALUES; /** * Shared SynthGraphics. @@ -715,7 +715,7 @@ public abstract class SynthStyle { private static Object getDefaultValue(Object key) { synchronized(SynthStyle.class) { if (DEFAULT_VALUES == null) { - DEFAULT_VALUES = new HashMap(); + DEFAULT_VALUES = new HashMap(); populateDefaultValues(); } Object value = DEFAULT_VALUES.get(key); diff --git a/src/share/classes/javax/swing/plaf/synth/SynthTextAreaUI.java b/src/share/classes/javax/swing/plaf/synth/SynthTextAreaUI.java index f52d3a68b..d07c4d5ad 100644 --- a/src/share/classes/javax/swing/plaf/synth/SynthTextAreaUI.java +++ b/src/share/classes/javax/swing/plaf/synth/SynthTextAreaUI.java @@ -66,7 +66,7 @@ class SynthTextAreaUI extends BasicTextAreaUI implements SynthUI { protected void installDefaults() { // Installs the text cursor on the component super.installDefaults(); - updateStyle((JTextComponent)getComponent()); + updateStyle(getComponent()); } protected void uninstallDefaults() { diff --git a/src/share/classes/javax/swing/plaf/synth/SynthTextFieldUI.java b/src/share/classes/javax/swing/plaf/synth/SynthTextFieldUI.java index 5b7cbd0a5..28bbdf843 100644 --- a/src/share/classes/javax/swing/plaf/synth/SynthTextFieldUI.java +++ b/src/share/classes/javax/swing/plaf/synth/SynthTextFieldUI.java @@ -232,7 +232,7 @@ class SynthTextFieldUI protected void installDefaults() { // Installs the text cursor on the component super.installDefaults(); - updateStyle((JTextComponent)getComponent()); + updateStyle(getComponent()); getComponent().addFocusListener(this); } diff --git a/src/share/classes/javax/swing/plaf/synth/SynthTreeUI.java b/src/share/classes/javax/swing/plaf/synth/SynthTreeUI.java index 1e12a820e..464b947b0 100644 --- a/src/share/classes/javax/swing/plaf/synth/SynthTreeUI.java +++ b/src/share/classes/javax/swing/plaf/synth/SynthTreeUI.java @@ -390,7 +390,7 @@ class SynthTreeUI extends BasicTreeUI implements PropertyChangeListener, } private Rectangle getDropLineRect(JTree.DropLocation loc) { - Rectangle rect = null; + Rectangle rect; TreePath path = loc.getPath(); int index = loc.getChildIndex(); boolean ltr = tree.getComponentOrientation().isLeftToRight(); @@ -523,7 +523,7 @@ class SynthTreeUI extends BasicTreeUI implements PropertyChangeListener, // Don't paint the renderer if editing this row. boolean selected = tree.isRowSelected(row); - JTree.DropLocation dropLocation = (JTree.DropLocation)tree.getDropLocation(); + JTree.DropLocation dropLocation = tree.getDropLocation(); boolean isDrop = dropLocation != null && dropLocation.getChildIndex() == -1 && path == dropLocation.getPath(); diff --git a/src/share/classes/sun/swing/plaf/synth/DefaultSynthStyle.java b/src/share/classes/sun/swing/plaf/synth/DefaultSynthStyle.java index de209df68..f66692e57 100644 --- a/src/share/classes/sun/swing/plaf/synth/DefaultSynthStyle.java +++ b/src/share/classes/sun/swing/plaf/synth/DefaultSynthStyle.java @@ -44,7 +44,7 @@ import javax.swing.plaf.*; * @author Scott Violet */ public class DefaultSynthStyle extends SynthStyle implements Cloneable { - private static final Object PENDING = new String("Pending"); + private static final String PENDING = "Pending"; /** * Should the component be opaque? @@ -690,8 +690,8 @@ public class DefaultSynthStyle extends SynthStyle implements Cloneable { StateInfo[] states = getStateInfo(); if (states != null) { buf.append("states["); - for (int i = 0; i < states.length; i++) { - buf.append(states[i].toString()).append(','); + for (StateInfo state : states) { + buf.append(state.toString()).append(','); } buf.append(']').append(','); } @@ -888,7 +888,7 @@ public class DefaultSynthStyle extends SynthStyle implements Cloneable { * Returns the number of states that are similar between the * ComponentState this StateInfo represents and val. */ - private final int getMatchCount(int val) { + private int getMatchCount(int val) { // This comes from BigInteger.bitCnt val &= state; val -= (0xaaaaaaaa & val) >>> 1; diff --git a/src/share/classes/sun/swing/plaf/synth/SynthFileChooserUIImpl.java b/src/share/classes/sun/swing/plaf/synth/SynthFileChooserUIImpl.java index abd99439e..2721764d1 100644 --- a/src/share/classes/sun/swing/plaf/synth/SynthFileChooserUIImpl.java +++ b/src/share/classes/sun/swing/plaf/synth/SynthFileChooserUIImpl.java @@ -735,7 +735,7 @@ public class SynthFileChooserUIImpl extends SynthFileChooserUI { * Data model for a type-face selection combo-box. */ protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel { - Vector directories = new Vector(); + Vector directories = new Vector(); int[] depths = null; File selectedDirectory = null; JFileChooser chooser = getFileChooser(); @@ -778,7 +778,7 @@ public class SynthFileChooserUIImpl extends SynthFileChooserUI { // Get the canonical (full) path. This has the side // benefit of removing extraneous chars from the path, // for example /foo/bar/ becomes /foo/bar - File canonical = null; + File canonical; try { canonical = directory.getCanonicalFile(); } catch (IOException e) { @@ -791,7 +791,7 @@ public class SynthFileChooserUIImpl extends SynthFileChooserUI { File sf = useShellFolder ? ShellFolder.getShellFolder(canonical) : canonical; File f = sf; - Vector path = new Vector(10); + Vector path = new Vector(10); do { path.addElement(f); } while ((f = f.getParentFile()) != null); @@ -799,7 +799,7 @@ public class SynthFileChooserUIImpl extends SynthFileChooserUI { int pathCount = path.size(); // Insert chain at appropriate place in vector for (int i = 0; i < pathCount; i++) { - f = (File)path.get(i); + f = path.get(i); if (directories.contains(f)) { int topIndex = directories.indexOf(f); for (int j = i-1; j >= 0; j--) { @@ -818,12 +818,12 @@ public class SynthFileChooserUIImpl extends SynthFileChooserUI { private void calculateDepths() { depths = new int[directories.size()]; for (int i = 0; i < depths.length; i++) { - File dir = (File)directories.get(i); + File dir = directories.get(i); File parent = dir.getParentFile(); depths[i] = 0; if (parent != null) { for (int j = i-1; j >= 0; j--) { - if (parent.equals((File)directories.get(j))) { + if (parent.equals(directories.get(j))) { depths[i] = depths[j] + 1; break; } @@ -940,8 +940,8 @@ public class SynthFileChooserUIImpl extends SynthFileChooserUI { FileFilter currentFilter = getFileChooser().getFileFilter(); boolean found = false; if(currentFilter != null) { - for(int i=0; i < filters.length; i++) { - if(filters[i] == currentFilter) { + for (FileFilter filter : filters) { + if (filter == currentFilter) { found = true; } } -- GitLab From 91debf039d373601f144583fa38e2a67716dbf64 Mon Sep 17 00:00:00 2001 From: idk Date: Fri, 25 Jul 2008 11:32:12 -0400 Subject: [PATCH 020/139] 6608456: need API to define RepaintManager per components hierarchy Reviewed-by: alexp --- make/javax/swing/Makefile | 2 +- .../com/sun/java/swing/SwingUtilities3.java | 91 ++++++++++ .../classes/javax/swing/RepaintManager.java | 51 ++++++ .../RepaintManager/6608456/bug6608456.java | 162 ++++++++++++++++++ 4 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 src/share/classes/com/sun/java/swing/SwingUtilities3.java create mode 100644 test/javax/swing/RepaintManager/6608456/bug6608456.java diff --git a/make/javax/swing/Makefile b/make/javax/swing/Makefile index c2056a45c..e112e609c 100644 --- a/make/javax/swing/Makefile +++ b/make/javax/swing/Makefile @@ -33,7 +33,7 @@ include $(BUILDDIR)/common/Defs.gmk # Files # include FILES.gmk -AUTO_FILES_JAVA_DIRS = javax/swing sun/swing +AUTO_FILES_JAVA_DIRS = javax/swing sun/swing com/sun/java/swing AUTO_JAVA_PRUNE = plaf SUBDIRS = html32dtd plaf diff --git a/src/share/classes/com/sun/java/swing/SwingUtilities3.java b/src/share/classes/com/sun/java/swing/SwingUtilities3.java new file mode 100644 index 000000000..2ace1e4ce --- /dev/null +++ b/src/share/classes/com/sun/java/swing/SwingUtilities3.java @@ -0,0 +1,91 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.java.swing; + +import sun.awt.AppContext; +import java.awt.Component; +import javax.swing.JComponent; +import javax.swing.RepaintManager; + +/** + * A collection of utility methods for Swing. + *

+ * WARNING: While this class is public, it should not be treated as + * public API and its API may change in incompatable ways between dot dot + * releases and even patch releases. You should not rely on this class even + * existing. + * + * This is a second part of sun.swing.SwingUtilities2. It is required + * to provide services for JavaFX applets. + * + */ +public class SwingUtilities3 { + /** + * The {@code clientProperty} key for delegate {@code RepaintManager} + */ + private static final Object DELEGATE_REPAINT_MANAGER_KEY = + new StringBuilder("DelegateRepaintManagerKey"); + + /** + * Registers delegate RepaintManager for {@code JComponent}. + */ + public static void setDelegateRepaintManager(JComponent component, + RepaintManager repaintManager) { + /* setting up flag in AppContext to speed up lookups in case + * there are no delegate RepaintManagers used. + */ + AppContext.getAppContext().put(DELEGATE_REPAINT_MANAGER_KEY, + Boolean.TRUE); + + component.putClientProperty(DELEGATE_REPAINT_MANAGER_KEY, + repaintManager); + } + + /** + * Returns delegate {@code RepaintManager} for {@code component} hierarchy. + */ + public static RepaintManager getDelegateRepaintManager(Component + component) { + RepaintManager delegate = null; + if (Boolean.TRUE == AppContext.getAppContext().get( + DELEGATE_REPAINT_MANAGER_KEY)) { + while (delegate == null && component != null) { + while (component != null + && ! (component instanceof JComponent)) { + component = component.getParent(); + } + if (component != null) { + delegate = (RepaintManager) + ((JComponent) component) + .getClientProperty(DELEGATE_REPAINT_MANAGER_KEY); + component = component.getParent(); + } + + } + } + return delegate; + } +} diff --git a/src/share/classes/javax/swing/RepaintManager.java b/src/share/classes/javax/swing/RepaintManager.java index 91034dbf8..81c816022 100644 --- a/src/share/classes/javax/swing/RepaintManager.java +++ b/src/share/classes/javax/swing/RepaintManager.java @@ -40,6 +40,8 @@ import sun.awt.SunToolkit; import sun.java2d.SunGraphicsEnvironment; import sun.security.action.GetPropertyAction; +import com.sun.java.swing.SwingUtilities3; + /** * This class manages repaint requests, allowing the number @@ -303,6 +305,11 @@ public class RepaintManager */ public synchronized void addInvalidComponent(JComponent invalidComponent) { + RepaintManager delegate = getDelegate(invalidComponent); + if (delegate != null) { + delegate.addInvalidComponent(invalidComponent); + return; + } Component validateRoot = null; /* Find the first JComponent ancestor of this component whose @@ -373,6 +380,11 @@ public class RepaintManager * @see #addInvalidComponent */ public synchronized void removeInvalidComponent(JComponent component) { + RepaintManager delegate = getDelegate(component); + if (delegate != null) { + delegate.removeInvalidComponent(component); + return; + } if(invalidComponents != null) { int index = invalidComponents.indexOf(component); if(index != -1) { @@ -464,6 +476,11 @@ public class RepaintManager */ public void addDirtyRegion(JComponent c, int x, int y, int w, int h) { + RepaintManager delegate = getDelegate(c); + if (delegate != null) { + delegate.addDirtyRegion(c, x, y, w, h); + return; + } addDirtyRegion0(c, x, y, w, h); } @@ -588,6 +605,10 @@ public class RepaintManager * dirty. */ public Rectangle getDirtyRegion(JComponent aComponent) { + RepaintManager delegate = getDelegate(aComponent); + if (delegate != null) { + return delegate.getDirtyRegion(aComponent); + } Rectangle r = null; synchronized(this) { r = (Rectangle)dirtyComponents.get(aComponent); @@ -603,6 +624,11 @@ public class RepaintManager * completely painted during the next paintDirtyRegions() call. */ public void markCompletelyDirty(JComponent aComponent) { + RepaintManager delegate = getDelegate(aComponent); + if (delegate != null) { + delegate.markCompletelyDirty(aComponent); + return; + } addDirtyRegion(aComponent,0,0,Integer.MAX_VALUE,Integer.MAX_VALUE); } @@ -611,6 +637,11 @@ public class RepaintManager * get painted during the next paintDirtyRegions() call. */ public void markCompletelyClean(JComponent aComponent) { + RepaintManager delegate = getDelegate(aComponent); + if (delegate != null) { + delegate.markCompletelyClean(aComponent); + return; + } synchronized(this) { dirtyComponents.remove(aComponent); } @@ -623,6 +654,10 @@ public class RepaintManager * if it return true. */ public boolean isCompletelyDirty(JComponent aComponent) { + RepaintManager delegate = getDelegate(aComponent); + if (delegate != null) { + return delegate.isCompletelyDirty(aComponent); + } Rectangle r; r = getDirtyRegion(aComponent); @@ -900,6 +935,10 @@ public class RepaintManager * repaint manager. */ public Image getOffscreenBuffer(Component c,int proposedWidth,int proposedHeight) { + RepaintManager delegate = getDelegate(c); + if (delegate != null) { + return delegate.getOffscreenBuffer(c, proposedWidth, proposedHeight); + } return _getOffscreenBuffer(c, proposedWidth, proposedHeight); } @@ -917,6 +956,11 @@ public class RepaintManager */ public Image getVolatileOffscreenBuffer(Component c, int proposedWidth,int proposedHeight) { + RepaintManager delegate = getDelegate(c); + if (delegate != null) { + return delegate.getVolatileOffscreenBuffer(c, proposedWidth, + proposedHeight); + } GraphicsConfiguration config = c.getGraphicsConfiguration(); if (config == null) { config = GraphicsEnvironment.getLocalGraphicsEnvironment(). @@ -1550,4 +1594,11 @@ public class RepaintManager prePaintDirtyRegions(); } } + private RepaintManager getDelegate(Component c) { + RepaintManager delegate = SwingUtilities3.getDelegateRepaintManager(c); + if (this == delegate) { + delegate = null; + } + return delegate; + } } diff --git a/test/javax/swing/RepaintManager/6608456/bug6608456.java b/test/javax/swing/RepaintManager/6608456/bug6608456.java new file mode 100644 index 000000000..1d8a14fca --- /dev/null +++ b/test/javax/swing/RepaintManager/6608456/bug6608456.java @@ -0,0 +1,162 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * + * @bug 6608456 + * @author Igor Kushnirskiy + * @summary tests if delegate RepaintManager gets invoked. + */ + +import java.awt.*; +import java.lang.reflect.Method; +import java.util.concurrent.Callable; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; + +import javax.swing.JComponent; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.RepaintManager; +import javax.swing.SwingUtilities; + + + +public class bug6608456 { + private static final TestFuture testFuture = new TestFuture(); + public static void main(String[] args) throws Exception { + final JComponent component = invokeAndWait( + new Callable() { + public JComponent call() throws Exception { + RepaintManager.setCurrentManager(new TestRepaintManager()); + JFrame frame = new JFrame("test"); + frame.setLayout(new FlowLayout()); + JButton button = new JButton("default"); + + frame.add(button); + button = new JButton("delegate"); + if ( ! registerDelegate( + button, new TestRepaintManager())) { + return null; + } + frame.add(button); + frame.pack(); + frame.setVisible(true); + return button; + } + }); + if (component == null) { + throw new RuntimeException("failed. can not register delegate"); + } + blockTillDisplayed(component); + // trigger repaint for delegate RepaintManager + invokeAndWait( + new Callable() { + public Void call() { + component.repaint(); + return null; + } + }); + try { + if (testFuture.get(10, TimeUnit.SECONDS)) { + // passed + } + } catch (Exception e) { + throw new RuntimeException("failed", e); + } finally { + JFrame frame = (JFrame) SwingUtilities + .getAncestorOfClass(JFrame.class, component); + if (frame != null) { + frame.dispose(); + } + } + } + static class TestRepaintManager extends RepaintManager { + @Override + public void addDirtyRegion(JComponent c, int x, int y, int w, int h) { + if (RepaintManager.currentManager(c) == this) { + testFuture.defaultCalled(); + } else { + testFuture.delegateCalled(); + } + super.addDirtyRegion(c, x, y, w, h); + } + } + static class TestFuture extends FutureTask { + private volatile boolean defaultCalled = false; + private volatile boolean delegateCalled = false; + public TestFuture() { + super(new Callable() { + public Boolean call() { + return null; + } + }); + } + public void defaultCalled() { + defaultCalled = true; + updateState(); + } + public void delegateCalled() { + delegateCalled = true; + updateState(); + } + private void updateState() { + if (defaultCalled && delegateCalled) { + set(Boolean.TRUE); + } + } + } + + private static boolean registerDelegate(JComponent c, + RepaintManager repaintManager) { + boolean rv = false; + try { + Class clazz = Class.forName("com.sun.java.swing.SwingUtilities3"); + Method method = clazz.getMethod("setDelegateRepaintManager", + JComponent.class, RepaintManager.class); + method.invoke(clazz, c, repaintManager); + rv = true; + } catch (Exception ignore) { + } + return rv; + } + static T invokeAndWait(Callable callable) throws Exception { + FutureTask future = new FutureTask(callable); + SwingUtilities.invokeLater(future); + return future.get(); + } + + public static void blockTillDisplayed(Component comp) { + Point p = null; + while (p == null) { + try { + p = comp.getLocationOnScreen(); + } catch (IllegalStateException e) { + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + } + } + } + } +} -- GitLab From acc7457fdcdfe0629104d91a3973092851b541b6 Mon Sep 17 00:00:00 2001 From: malenkov Date: Fri, 25 Jul 2008 21:00:05 +0400 Subject: [PATCH 021/139] 6630275: The spec on VetoableChangeSupport.fireVetoableChange should be updated Reviewed-by: peterz, rupashka --- .../java/beans/PropertyChangeSupport.java | 195 ++++++++--------- .../java/beans/VetoableChangeSupport.java | 203 ++++++++++-------- .../VetoableChangeSupport/Test6630275.java | 81 +++++++ 3 files changed, 287 insertions(+), 192 deletions(-) create mode 100644 test/java/beans/VetoableChangeSupport/Test6630275.java diff --git a/src/share/classes/java/beans/PropertyChangeSupport.java b/src/share/classes/java/beans/PropertyChangeSupport.java index 2d4ed88fd..2a3f79ff3 100644 --- a/src/share/classes/java/beans/PropertyChangeSupport.java +++ b/src/share/classes/java/beans/PropertyChangeSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -208,91 +208,91 @@ public class PropertyChangeSupport implements Serializable { } /** - * Report a bound property update to any registered listeners. - * No event is fired if old and new are equal and non-null. - * + * Reports a bound property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * No event is fired if old and new values are equal and non-null. *

* This is merely a convenience wrapper around the more general - * firePropertyChange method that takes {@code - * PropertyChangeEvent} value. + * {@link #firePropertyChange(PropertyChangeEvent)} method. * - * @param propertyName The programmatic name of the property - * that was changed. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. + * @param propertyName the programmatic name of the property that was changed + * @param oldValue the old value of the property + * @param newValue the new value of the property */ - public void firePropertyChange(String propertyName, - Object oldValue, Object newValue) { - if (oldValue != null && newValue != null && oldValue.equals(newValue)) { - return; + public void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { + firePropertyChange(new PropertyChangeEvent(this.source, propertyName, oldValue, newValue)); } - firePropertyChange(new PropertyChangeEvent(source, propertyName, - oldValue, newValue)); } /** - * Report an int bound property update to any registered listeners. - * No event is fired if old and new are equal. + * Reports an integer bound property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * No event is fired if old and new values are equal. *

* This is merely a convenience wrapper around the more general - * firePropertyChange method that takes Object values. + * {@link #firePropertyChange(String, Object, Object)} method. * - * @param propertyName The programmatic name of the property - * that was changed. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. + * @param propertyName the programmatic name of the property that was changed + * @param oldValue the old value of the property + * @param newValue the new value of the property */ - public void firePropertyChange(String propertyName, - int oldValue, int newValue) { - if (oldValue == newValue) { - return; + public void firePropertyChange(String propertyName, int oldValue, int newValue) { + if (oldValue != newValue) { + firePropertyChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue)); } - firePropertyChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue)); } /** - * Report a boolean bound property update to any registered listeners. - * No event is fired if old and new are equal. + * Reports a boolean bound property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * No event is fired if old and new values are equal. *

* This is merely a convenience wrapper around the more general - * firePropertyChange method that takes Object values. + * {@link #firePropertyChange(String, Object, Object)} method. * - * @param propertyName The programmatic name of the property - * that was changed. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. + * @param propertyName the programmatic name of the property that was changed + * @param oldValue the old value of the property + * @param newValue the new value of the property */ - public void firePropertyChange(String propertyName, - boolean oldValue, boolean newValue) { - if (oldValue == newValue) { - return; + public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { + if (oldValue != newValue) { + firePropertyChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); } - firePropertyChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); } /** - * Fire an existing PropertyChangeEvent to any registered listeners. - * No event is fired if the given event's old and new values are - * equal and non-null. - * @param evt The PropertyChangeEvent object. + * Fires a property change event to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * No event is fired if the given event's old and new values are equal and non-null. + * + * @param event the {@code PropertyChangeEvent} to be fired */ - public void firePropertyChange(PropertyChangeEvent evt) { - Object oldValue = evt.getOldValue(); - Object newValue = evt.getNewValue(); - String propertyName = evt.getPropertyName(); - if (oldValue != null && newValue != null && oldValue.equals(newValue)) { - return; - } - PropertyChangeListener[] common = this.map.get(null); - PropertyChangeListener[] named = (propertyName != null) - ? this.map.get(propertyName) - : null; + public void firePropertyChange(PropertyChangeEvent event) { + Object oldValue = event.getOldValue(); + Object newValue = event.getNewValue(); + if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { + String name = event.getPropertyName(); - fire(common, evt); - fire(named, evt); + PropertyChangeListener[] common = this.map.get(null); + PropertyChangeListener[] named = (name != null) + ? this.map.get(name) + : null; + + fire(common, event); + fire(named, event); + } } - private void fire(PropertyChangeListener[] listeners, PropertyChangeEvent event) { + private static void fire(PropertyChangeListener[] listeners, PropertyChangeEvent event) { if (listeners != null) { for (PropertyChangeListener listener : listeners) { listener.propertyChange(event); @@ -301,78 +301,69 @@ public class PropertyChangeSupport implements Serializable { } /** - * Report a bound indexed property update to any registered - * listeners. + * Reports a bound indexed property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. *

- * No event is fired if old and new values are equal - * and non-null. - * + * No event is fired if old and new values are equal and non-null. *

* This is merely a convenience wrapper around the more general - * firePropertyChange method that takes {@code PropertyChangeEvent} value. + * {@link #firePropertyChange(PropertyChangeEvent)} method. * - * @param propertyName The programmatic name of the property that - * was changed. - * @param index index of the property element that was changed. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. + * @param propertyName the programmatic name of the property that was changed + * @param index the index of the property element that was changed + * @param oldValue the old value of the property + * @param newValue the new value of the property * @since 1.5 */ - public void fireIndexedPropertyChange(String propertyName, int index, - Object oldValue, Object newValue) { - firePropertyChange(new IndexedPropertyChangeEvent - (source, propertyName, oldValue, newValue, index)); + public void fireIndexedPropertyChange(String propertyName, int index, Object oldValue, Object newValue) { + if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { + firePropertyChange(new IndexedPropertyChangeEvent(source, propertyName, oldValue, newValue, index)); + } } /** - * Report an int bound indexed property update to any registered - * listeners. + * Reports an integer bound indexed property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. *

* No event is fired if old and new values are equal. *

* This is merely a convenience wrapper around the more general - * fireIndexedPropertyChange method which takes Object values. + * {@link #fireIndexedPropertyChange(String, int, Object, Object)} method. * - * @param propertyName The programmatic name of the property that - * was changed. - * @param index index of the property element that was changed. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. + * @param propertyName the programmatic name of the property that was changed + * @param index the index of the property element that was changed + * @param oldValue the old value of the property + * @param newValue the new value of the property * @since 1.5 */ - public void fireIndexedPropertyChange(String propertyName, int index, - int oldValue, int newValue) { - if (oldValue == newValue) { - return; + public void fireIndexedPropertyChange(String propertyName, int index, int oldValue, int newValue) { + if (oldValue != newValue) { + fireIndexedPropertyChange(propertyName, index, Integer.valueOf(oldValue), Integer.valueOf(newValue)); } - fireIndexedPropertyChange(propertyName, index, - Integer.valueOf(oldValue), - Integer.valueOf(newValue)); } /** - * Report a boolean bound indexed property update to any - * registered listeners. + * Reports a boolean bound indexed property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. *

* No event is fired if old and new values are equal. *

* This is merely a convenience wrapper around the more general - * fireIndexedPropertyChange method which takes Object values. + * {@link #fireIndexedPropertyChange(String, int, Object, Object)} method. * - * @param propertyName The programmatic name of the property that - * was changed. - * @param index index of the property element that was changed. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. + * @param propertyName the programmatic name of the property that was changed + * @param index the index of the property element that was changed + * @param oldValue the old value of the property + * @param newValue the new value of the property * @since 1.5 */ - public void fireIndexedPropertyChange(String propertyName, int index, - boolean oldValue, boolean newValue) { - if (oldValue == newValue) { - return; + public void fireIndexedPropertyChange(String propertyName, int index, boolean oldValue, boolean newValue) { + if (oldValue != newValue) { + fireIndexedPropertyChange(propertyName, index, Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); } - fireIndexedPropertyChange(propertyName, index, Boolean.valueOf(oldValue), - Boolean.valueOf(newValue)); } /** diff --git a/src/share/classes/java/beans/VetoableChangeSupport.java b/src/share/classes/java/beans/VetoableChangeSupport.java index 7d1418e17..f6707b49f 100644 --- a/src/share/classes/java/beans/VetoableChangeSupport.java +++ b/src/share/classes/java/beans/VetoableChangeSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -208,126 +208,149 @@ public class VetoableChangeSupport implements Serializable { } /** - * Report a vetoable property update to any registered listeners. If - * anyone vetos the change, then fire a new event reverting everyone to - * the old value and then rethrow the PropertyVetoException. + * Reports a constrained property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. *

- * No event is fired if old and new are equal and non-null. + * Any listener can throw a {@code PropertyVetoException} to veto the update. + * If one of the listeners vetoes the update, this method passes + * a new "undo" {@code PropertyChangeEvent} that reverts to the old value + * to all listeners that already confirmed this update + * and throws the {@code PropertyVetoException} again. + *

+ * No event is fired if old and new values are equal and non-null. + *

+ * This is merely a convenience wrapper around the more general + * {@link #fireVetoableChange(PropertyChangeEvent)} method. * - * @param propertyName The programmatic name of the property - * that is about to change.. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. - * @exception PropertyVetoException if the recipient wishes the property - * change to be rolled back. + * @param propertyName the programmatic name of the property that is about to change + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @throws PropertyVetoException if one of listeners vetoes the property update */ - public void fireVetoableChange(String propertyName, - Object oldValue, Object newValue) - throws PropertyVetoException { - if (oldValue != null && newValue != null && oldValue.equals(newValue)) { - return; + public void fireVetoableChange(String propertyName, Object oldValue, Object newValue) + throws PropertyVetoException { + if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { + fireVetoableChange(new PropertyChangeEvent(this.source, propertyName, oldValue, newValue)); } - PropertyChangeEvent evt = new PropertyChangeEvent(source, propertyName, - oldValue, newValue); - fireVetoableChange(evt); } /** - * Report a int vetoable property update to any registered listeners. - * No event is fired if old and new are equal. + * Reports an integer constrained property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * Any listener can throw a {@code PropertyVetoException} to veto the update. + * If one of the listeners vetoes the update, this method passes + * a new "undo" {@code PropertyChangeEvent} that reverts to the old value + * to all listeners that already confirmed this update + * and throws the {@code PropertyVetoException} again. + *

+ * No event is fired if old and new values are equal. *

* This is merely a convenience wrapper around the more general - * fireVetoableChange method that takes Object values. + * {@link #fireVetoableChange(String, Object, Object)} method. * - * @param propertyName The programmatic name of the property - * that is about to change. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. + * @param propertyName the programmatic name of the property that is about to change + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @throws PropertyVetoException if one of listeners vetoes the property update */ - public void fireVetoableChange(String propertyName, - int oldValue, int newValue) - throws PropertyVetoException { - if (oldValue == newValue) { - return; + public void fireVetoableChange(String propertyName, int oldValue, int newValue) + throws PropertyVetoException { + if (oldValue != newValue) { + fireVetoableChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue)); } - fireVetoableChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue)); } /** - * Report a boolean vetoable property update to any registered listeners. - * No event is fired if old and new are equal. + * Reports a boolean constrained property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * Any listener can throw a {@code PropertyVetoException} to veto the update. + * If one of the listeners vetoes the update, this method passes + * a new "undo" {@code PropertyChangeEvent} that reverts to the old value + * to all listeners that already confirmed this update + * and throws the {@code PropertyVetoException} again. + *

+ * No event is fired if old and new values are equal. *

* This is merely a convenience wrapper around the more general - * fireVetoableChange method that takes Object values. + * {@link #fireVetoableChange(String, Object, Object)} method. * - * @param propertyName The programmatic name of the property - * that is about to change. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. + * @param propertyName the programmatic name of the property that is about to change + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @throws PropertyVetoException if one of listeners vetoes the property update */ - public void fireVetoableChange(String propertyName, - boolean oldValue, boolean newValue) - throws PropertyVetoException { - if (oldValue == newValue) { - return; + public void fireVetoableChange(String propertyName, boolean oldValue, boolean newValue) + throws PropertyVetoException { + if (oldValue != newValue) { + fireVetoableChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); } - fireVetoableChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); } /** - * Fire a vetoable property update to any registered listeners. If - * anyone vetos the change, then fire a new event reverting everyone to - * the old value and then rethrow the PropertyVetoException. + * Fires a property change event to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. *

- * No event is fired if old and new are equal and non-null. + * Any listener can throw a {@code PropertyVetoException} to veto the update. + * If one of the listeners vetoes the update, this method passes + * a new "undo" {@code PropertyChangeEvent} that reverts to the old value + * to all listeners that already confirmed this update + * and throws the {@code PropertyVetoException} again. + *

+ * No event is fired if the given event's old and new values are equal and non-null. * - * @param evt The PropertyChangeEvent to be fired. - * @exception PropertyVetoException if the recipient wishes the property - * change to be rolled back. + * @param event the {@code PropertyChangeEvent} to be fired + * @throws PropertyVetoException if one of listeners vetoes the property update */ - public void fireVetoableChange(PropertyChangeEvent evt) - throws PropertyVetoException { + public void fireVetoableChange(PropertyChangeEvent event) + throws PropertyVetoException { + Object oldValue = event.getOldValue(); + Object newValue = event.getNewValue(); + if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { + String name = event.getPropertyName(); - Object oldValue = evt.getOldValue(); - Object newValue = evt.getNewValue(); - String propertyName = evt.getPropertyName(); - if (oldValue != null && newValue != null && oldValue.equals(newValue)) { - return; - } - VetoableChangeListener[] common = this.map.get(null); - VetoableChangeListener[] named = (propertyName != null) - ? this.map.get(propertyName) - : null; - fire(common, evt); - fire(named, evt); - } + VetoableChangeListener[] common = this.map.get(null); + VetoableChangeListener[] named = (name != null) + ? this.map.get(name) + : null; - private void fire(VetoableChangeListener[] listeners, PropertyChangeEvent event) throws PropertyVetoException { - if (listeners != null) { - VetoableChangeListener current = null; - try { - for (VetoableChangeListener listener : listeners) { - current = listener; - listener.vetoableChange(event); - } - } catch (PropertyVetoException veto) { - // Create an event to revert everyone to the old value. - event = new PropertyChangeEvent( this.source, - event.getPropertyName(), - event.getNewValue(), - event.getOldValue() ); - for (VetoableChangeListener listener : listeners) { - if (current == listener) { - break; + VetoableChangeListener[] listeners; + if (common == null) { + listeners = named; + } + else if (named == null) { + listeners = common; + } + else { + listeners = new VetoableChangeListener[common.length + named.length]; + System.arraycopy(common, 0, listeners, 0, common.length); + System.arraycopy(named, 0, listeners, common.length, named.length); + } + if (listeners != null) { + int current = 0; + try { + while (current < listeners.length) { + listeners[current].vetoableChange(event); + current++; } - try { - listener.vetoableChange(event); - } catch (PropertyVetoException ex) { - // We just ignore exceptions that occur during reversions. + } + catch (PropertyVetoException veto) { + event = new PropertyChangeEvent(this.source, name, newValue, oldValue); + for (int i = 0; i < current; i++) { + try { + listeners[i].vetoableChange(event); + } + catch (PropertyVetoException exception) { + // ignore exceptions that occur during rolling back + } } + throw veto; // rethrow the veto exception } - // And now rethrow the PropertyVetoException. - throw veto; } } } diff --git a/test/java/beans/VetoableChangeSupport/Test6630275.java b/test/java/beans/VetoableChangeSupport/Test6630275.java new file mode 100644 index 000000000..ce68720d8 --- /dev/null +++ b/test/java/beans/VetoableChangeSupport/Test6630275.java @@ -0,0 +1,81 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6630275 + * @summary Tests VetoableChangeSupport specification + * @author Sergey Malenkov + */ + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; +import java.beans.VetoableChangeSupport; + +public class Test6630275 { + private static final String PROPERTY = "property"; // NON-NLS: predefined property name + + public static void main(String[] args) { + CheckListener first = new CheckListener(false); + CheckListener second = new CheckListener(true); + CheckListener third = new CheckListener(false); + + VetoableChangeSupport vcs = new VetoableChangeSupport(Test6630275.class); + vcs.addVetoableChangeListener(first); + vcs.addVetoableChangeListener(PROPERTY, first); + vcs.addVetoableChangeListener(PROPERTY, second); + vcs.addVetoableChangeListener(PROPERTY, third); + try { + vcs.fireVetoableChange(PROPERTY, true, false); + } catch (PropertyVetoException exception) { + first.validate(); + second.validate(); + third.validate(); + return; // expected exception + } + throw new Error("exception should be thrown"); + } + + private static class CheckListener implements VetoableChangeListener { + private final boolean veto; + private boolean odd; // even/odd check for notification + + private CheckListener(boolean veto) { + this.veto = veto; + } + + private void validate() { + if (this.veto != this.odd) + throw new Error(this.odd + ? "undo event expected" + : "unexpected undo event"); + } + + public void vetoableChange(PropertyChangeEvent event) throws PropertyVetoException { + this.odd = !this.odd; + if (this.veto) + throw new PropertyVetoException("disable all changes", event); + } + } +} -- GitLab From 7acda73287dac0254d7b30e8c015177c9fc88e2a Mon Sep 17 00:00:00 2001 From: idk Date: Fri, 25 Jul 2008 14:13:59 -0400 Subject: [PATCH 022/139] 6638195: need API for EventQueueDelegate Reviewed-by: bchristi --- .../com/sun/java/swing/SwingUtilities3.java | 83 +++++++++++ .../classes/java/awt/EventDispatchThread.java | 20 ++- .../classes/sun/awt/EventQueueDelegate.java | 71 +++++++++ .../awt/EventQueue/6638195/bug6638195.java | 138 ++++++++++++++++++ 4 files changed, 309 insertions(+), 3 deletions(-) create mode 100644 src/share/classes/sun/awt/EventQueueDelegate.java create mode 100644 test/java/awt/EventQueue/6638195/bug6638195.java diff --git a/src/share/classes/com/sun/java/swing/SwingUtilities3.java b/src/share/classes/com/sun/java/swing/SwingUtilities3.java index 2ace1e4ce..9268d7370 100644 --- a/src/share/classes/com/sun/java/swing/SwingUtilities3.java +++ b/src/share/classes/com/sun/java/swing/SwingUtilities3.java @@ -25,7 +25,12 @@ package com.sun.java.swing; +import sun.awt.EventQueueDelegate; import sun.awt.AppContext; +import java.util.Map; +import java.util.concurrent.Callable; +import java.awt.AWTEvent; +import java.awt.EventQueue; import java.awt.Component; import javax.swing.JComponent; import javax.swing.RepaintManager; @@ -88,4 +93,82 @@ public class SwingUtilities3 { } return delegate; } + + /* + * We use maps to avoid reflection. Hopefully it should perform better + * this way. + */ + public static void setEventQueueDelegate( + Map> map) { + EventQueueDelegate.setDelegate(new EventQueueDelegateFromMap(map)); + } + + private static class EventQueueDelegateFromMap + implements EventQueueDelegate.Delegate { + private final AWTEvent[] afterDispatchEventArgument; + private final Object[] afterDispatchHandleArgument; + private final Callable afterDispatchCallable; + + private final AWTEvent[] beforeDispatchEventArgument; + private final Callable beforeDispatchCallable; + + private final EventQueue[] getNextEventEventQueueArgument; + private final Callable getNextEventCallable; + + @SuppressWarnings("unchecked") + public EventQueueDelegateFromMap(Map> objectMap) { + Map methodMap = objectMap.get("afterDispatch"); + afterDispatchEventArgument = (AWTEvent[]) methodMap.get("event"); + afterDispatchHandleArgument = (Object[]) methodMap.get("handle"); + afterDispatchCallable = (Callable) methodMap.get("method"); + + methodMap = objectMap.get("beforeDispatch"); + beforeDispatchEventArgument = (AWTEvent[]) methodMap.get("event"); + beforeDispatchCallable = (Callable) methodMap.get("method"); + + methodMap = objectMap.get("getNextEvent"); + getNextEventEventQueueArgument = + (EventQueue[]) methodMap.get("eventQueue"); + getNextEventCallable = (Callable) methodMap.get("method"); + } + + @Override + public void afterDispatch(AWTEvent event, Object handle) { + afterDispatchEventArgument[0] = event; + afterDispatchHandleArgument[0] = handle; + try { + afterDispatchCallable.call(); + } catch (Exception e) { + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + } + } + + @Override + public Object beforeDispatch(AWTEvent event) { + beforeDispatchEventArgument[0] = event; + try { + return beforeDispatchCallable.call(); + } catch (Exception e) { + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + } + return null; + } + + @Override + public AWTEvent getNextEvent(EventQueue eventQueue) { + getNextEventEventQueueArgument[0] = eventQueue; + try { + return getNextEventCallable.call(); + } catch (Exception e) { + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + } + return null; + } + } } diff --git a/src/share/classes/java/awt/EventDispatchThread.java b/src/share/classes/java/awt/EventDispatchThread.java index f49bacb67..23d08a0d4 100644 --- a/src/share/classes/java/awt/EventDispatchThread.java +++ b/src/share/classes/java/awt/EventDispatchThread.java @@ -39,6 +39,7 @@ import java.util.Vector; import java.util.logging.*; import sun.awt.dnd.SunDragSourceContextPeer; +import sun.awt.EventQueueDelegate; /** * EventDispatchThread is a package-private AWT class which takes @@ -243,10 +244,16 @@ class EventDispatchThread extends Thread { try { AWTEvent event; boolean eventOK; + EventQueueDelegate.Delegate delegate = + EventQueueDelegate.getDelegate(); do { - event = (id == ANY_EVENT) - ? theQueue.getNextEvent() - : theQueue.getNextEvent(id); + if (delegate != null && id == ANY_EVENT) { + event = delegate.getNextEvent(theQueue); + } else { + event = (id == ANY_EVENT) + ? theQueue.getNextEvent() + : theQueue.getNextEvent(id); + } eventOK = true; synchronized (eventFilters) { @@ -272,7 +279,14 @@ class EventDispatchThread extends Thread { eventLog.log(Level.FINEST, "Dispatching: " + event); } + Object handle = null; + if (delegate != null) { + handle = delegate.beforeDispatch(event); + } theQueue.dispatchEvent(event); + if (delegate != null) { + delegate.afterDispatch(event, handle); + } return true; } catch (ThreadDeath death) { diff --git a/src/share/classes/sun/awt/EventQueueDelegate.java b/src/share/classes/sun/awt/EventQueueDelegate.java new file mode 100644 index 000000000..8f8a6f3fe --- /dev/null +++ b/src/share/classes/sun/awt/EventQueueDelegate.java @@ -0,0 +1,71 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.awt; + +import java.awt.AWTEvent; +import java.awt.EventQueue; + + +public class EventQueueDelegate { + private static final Object EVENT_QUEUE_DELEGATE_KEY = + new StringBuilder("EventQueueDelegate.Delegate"); + + public static void setDelegate(Delegate delegate) { + AppContext.getAppContext().put(EVENT_QUEUE_DELEGATE_KEY, delegate); + } + public static Delegate getDelegate() { + return + (Delegate) AppContext.getAppContext().get(EVENT_QUEUE_DELEGATE_KEY); + } + public interface Delegate { + /** + * This method allows for changing {@code EventQueue} events order. + * + * @param eventQueue current {@code EventQueue} + * @return next {@code event} for the {@code EventDispatchThread} + */ + + public AWTEvent getNextEvent(EventQueue eventQueue) throws InterruptedException; + + /** + * Notifies delegate before EventQueue.dispatch method. + * + * Note: this method may mutate the event + * + * @param event to be dispatched by {@code dispatch} method + * @return handle to be passed to {@code afterDispatch} method + */ + public Object beforeDispatch(AWTEvent event); + + /** + * Notifies delegate after EventQueue.dispatch method. + * + * @param event {@code event} dispatched by the {@code dispatch} method + * @param handle object which came from {@code beforeDispatch} method + */ + public void afterDispatch(AWTEvent event, Object handle); + } +} diff --git a/test/java/awt/EventQueue/6638195/bug6638195.java b/test/java/awt/EventQueue/6638195/bug6638195.java new file mode 100644 index 000000000..b11099670 --- /dev/null +++ b/test/java/awt/EventQueue/6638195/bug6638195.java @@ -0,0 +1,138 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * + * @bug 6638195 + * @author Igor Kushnirskiy + * @summary tests if EventQueueDelegate.Delegate is invoked. + */ + +import sun.awt.EventQueueDelegate; +import com.sun.java.swing.SwingUtilities3; + +import java.util.*; +import java.util.concurrent.*; +import java.awt.*; + +public class bug6638195 { + public static void main(String[] args) throws Exception { + MyEventQueueDelegate delegate = new MyEventQueueDelegate(); + EventQueueDelegate.setDelegate(delegate); + runTest(delegate); + + delegate = new MyEventQueueDelegate(); + SwingUtilities3.setEventQueueDelegate(getObjectMap(delegate)); + runTest(delegate); + } + + private static void runTest(MyEventQueueDelegate delegate) throws Exception { + EventQueue.invokeLater( + new Runnable() { + public void run() { + } + }); + final CountDownLatch latch = new CountDownLatch(1); + EventQueue.invokeLater( + new Runnable() { + public void run() { + latch.countDown(); + } + }); + latch.await(); + if (! delegate.allInvoked()) { + throw new RuntimeException("failed"); + } + } + + static Map> getObjectMap( + final EventQueueDelegate.Delegate delegate) { + Map> objectMap = + new HashMap>(); + Map methodMap; + + final AWTEvent[] afterDispatchEventArgument = new AWTEvent[1]; + final Object[] afterDispatchHandleArgument = new Object[1]; + Callable afterDispatchCallable = + new Callable() { + public Void call() { + delegate.afterDispatch(afterDispatchEventArgument[0], + afterDispatchHandleArgument[0]); + return null; + } + }; + methodMap = new HashMap(); + methodMap.put("event", afterDispatchEventArgument); + methodMap.put("handle", afterDispatchHandleArgument); + methodMap.put("method", afterDispatchCallable); + objectMap.put("afterDispatch", methodMap); + + final AWTEvent[] beforeDispatchEventArgument = new AWTEvent[1]; + Callable beforeDispatchCallable = + new Callable() { + public Object call() { + return delegate.beforeDispatch( + beforeDispatchEventArgument[0]); + } + }; + methodMap = new HashMap(); + methodMap.put("event", beforeDispatchEventArgument); + methodMap.put("method", beforeDispatchCallable); + objectMap.put("beforeDispatch", methodMap); + + final EventQueue[] getNextEventEventQueueArgument = new EventQueue[1]; + Callable getNextEventCallable = + new Callable() { + public AWTEvent call() throws Exception { + return delegate.getNextEvent( + getNextEventEventQueueArgument[0]); + } + }; + methodMap = new HashMap(); + methodMap.put("eventQueue", getNextEventEventQueueArgument); + methodMap.put("method", getNextEventCallable); + objectMap.put("getNextEvent", methodMap); + + return objectMap; + } + static class MyEventQueueDelegate implements EventQueueDelegate.Delegate { + private volatile boolean getNextEventInvoked = false; + private volatile boolean beforeDispatchInvoked = false; + private volatile boolean afterDispatchInvoked = false; + public AWTEvent getNextEvent(EventQueue eventQueue) + throws InterruptedException { + getNextEventInvoked = true; + return eventQueue.getNextEvent(); + } + public Object beforeDispatch(AWTEvent event) { + beforeDispatchInvoked = true; + return null; + } + public void afterDispatch(AWTEvent event, Object handle) { + afterDispatchInvoked = true; + } + private boolean allInvoked() { + return getNextEventInvoked && beforeDispatchInvoked && afterDispatchInvoked; + } + } +} -- GitLab From 12b796c2569feb6599a7b91414eeb21a6f93ca20 Mon Sep 17 00:00:00 2001 From: idk Date: Fri, 25 Jul 2008 14:26:27 -0400 Subject: [PATCH 023/139] 6699328: NullPointerException in EventQueue.dispatchEvent when applet is closed, only reprise/scenario applet Reviewed-by: bchristi --- .../com/sun/java/swing/SwingUtilities3.java | 32 +++++++++++-------- .../classes/sun/awt/EventQueueDelegate.java | 4 +-- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/share/classes/com/sun/java/swing/SwingUtilities3.java b/src/share/classes/com/sun/java/swing/SwingUtilities3.java index 9268d7370..28d9e8af6 100644 --- a/src/share/classes/com/sun/java/swing/SwingUtilities3.java +++ b/src/share/classes/com/sun/java/swing/SwingUtilities3.java @@ -133,42 +133,46 @@ public class SwingUtilities3 { } @Override - public void afterDispatch(AWTEvent event, Object handle) { + public void afterDispatch(AWTEvent event, Object handle) throws InterruptedException { afterDispatchEventArgument[0] = event; afterDispatchHandleArgument[0] = handle; try { afterDispatchCallable.call(); + } catch (InterruptedException e) { + throw e; + } catch (RuntimeException e) { + throw e; } catch (Exception e) { - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } + throw new RuntimeException(e); } } @Override - public Object beforeDispatch(AWTEvent event) { + public Object beforeDispatch(AWTEvent event) throws InterruptedException { beforeDispatchEventArgument[0] = event; try { return beforeDispatchCallable.call(); + } catch (InterruptedException e) { + throw e; + } catch (RuntimeException e) { + throw e; } catch (Exception e) { - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } + throw new RuntimeException(e); } - return null; } @Override - public AWTEvent getNextEvent(EventQueue eventQueue) { + public AWTEvent getNextEvent(EventQueue eventQueue) throws InterruptedException { getNextEventEventQueueArgument[0] = eventQueue; try { return getNextEventCallable.call(); + } catch (InterruptedException e) { + throw e; + } catch (RuntimeException e) { + throw e; } catch (Exception e) { - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } + throw new RuntimeException(e); } - return null; } } } diff --git a/src/share/classes/sun/awt/EventQueueDelegate.java b/src/share/classes/sun/awt/EventQueueDelegate.java index 8f8a6f3fe..c9d0a90d2 100644 --- a/src/share/classes/sun/awt/EventQueueDelegate.java +++ b/src/share/classes/sun/awt/EventQueueDelegate.java @@ -58,7 +58,7 @@ public class EventQueueDelegate { * @param event to be dispatched by {@code dispatch} method * @return handle to be passed to {@code afterDispatch} method */ - public Object beforeDispatch(AWTEvent event); + public Object beforeDispatch(AWTEvent event) throws InterruptedException; /** * Notifies delegate after EventQueue.dispatch method. @@ -66,6 +66,6 @@ public class EventQueueDelegate { * @param event {@code event} dispatched by the {@code dispatch} method * @param handle object which came from {@code beforeDispatch} method */ - public void afterDispatch(AWTEvent event, Object handle); + public void afterDispatch(AWTEvent event, Object handle) throws InterruptedException; } } -- GitLab From e4dd400f889540a30ecb851a8ca9434990fd76c5 Mon Sep 17 00:00:00 2001 From: ohair Date: Wed, 30 Jul 2008 19:40:57 -0700 Subject: [PATCH 024/139] 6729772: 64-bit build with SS12 compiler: SIGSEGV (0xb) at pc=0x0000000000000048, pid=14826, tid=2 Reviewed-by: tbell --- make/common/Defs-linux.gmk | 45 +++--- make/common/Defs-solaris.gmk | 242 +++++++++++++++++------------ make/common/Defs-windows.gmk | 82 +++++----- make/common/Defs.gmk | 5 - make/common/Library.gmk | 2 +- make/common/shared/Defs.gmk | 3 - make/java/fdlibm/Makefile | 23 +-- make/java/java_hprof_demo/Makefile | 6 +- make/sun/awt/Makefile | 10 +- make/sun/font/Makefile | 8 +- make/sun/font/t2k/Makefile | 8 +- make/sun/image/generic/Makefile | 7 +- make/sun/image/vis/Makefile | 10 +- make/sun/jpeg/Makefile | 7 +- 14 files changed, 240 insertions(+), 218 deletions(-) diff --git a/make/common/Defs-linux.gmk b/make/common/Defs-linux.gmk index c4f1856f5..c682e3ac1 100644 --- a/make/common/Defs-linux.gmk +++ b/make/common/Defs-linux.gmk @@ -86,18 +86,22 @@ HPIS = native # # Default optimization # -CC_HIGHEST_OPT = -O3 -CC_HIGHER_OPT = -O3 -CC_LOWER_OPT = -O2 -CC_NO_OPT = -ifeq ($(PRODUCT), java) - _OPT = $(CC_HIGHER_OPT) -else - _OPT = $(CC_LOWER_OPT) - CPPFLAGS_DBG += -DLOGGING +ifndef OPTIMIZATION_LEVEL + ifeq ($(PRODUCT), java) + OPTIMIZATION_LEVEL = HIGHER + else + OPTIMIZATION_LEVEL = LOWER + endif endif +CC_OPT/NONE = +CC_OPT/LOWER = -O2 +CC_OPT/HIGHER = -O3 +CC_OPT/HIGHEST = -O3 + +CC_OPT = $(CC_OPT/$(OPTIMIZATION_LEVEL)) + # For all platforms, do not omit the frame pointer register usage. # We need this frame pointer to make it easy to walk the stacks. # This should be the default on X86, but ia64 and amd64 may not have this @@ -112,18 +116,6 @@ LDFLAGS_COMMON_sparc += -m32 -mcpu=v9 CFLAGS_REQUIRED = $(CFLAGS_REQUIRED_$(ARCH)) LDFLAGS_COMMON += $(LDFLAGS_COMMON_$(ARCH)) -# Add in platform specific optimizations for all opt levels -CC_HIGHEST_OPT += $(_OPT_$(ARCH)) -CC_HIGHER_OPT += $(_OPT_$(ARCH)) -CC_LOWER_OPT += $(_OPT_$(ARCH)) - -# If NO_OPTIMIZATIONS is defined in the environment, turn all optimzations off -ifdef NO_OPTIMIZATIONS - CC_HIGHEST_OPT = $(CC_NO_OPT) - CC_HIGHER_OPT = $(CC_NO_OPT) - CC_LOWER_OPT = $(CC_NO_OPT) -endif - # # Selection of warning messages # @@ -163,19 +155,19 @@ ifeq ($(FASTDEBUG), true) endif endif -CFLAGS_OPT = $(POPT) +CFLAGS_OPT = $(CC_OPT) CFLAGS_DBG = $(DEBUG_FLAG) CFLAGS_COMMON += $(CFLAGS_REQUIRED) CXXFLAGS_COMMON = $(GLOBAL_KPIC) -DCC_NOEX $(GCC_WARNINGS) -CXXFLAGS_OPT = $(POPT) +CXXFLAGS_OPT = $(CC_OPT) CXXFLAGS_DBG = $(DEBUG_FLAG) CXXFLAGS_COMMON += $(CFLAGS_REQUIRED) # FASTDEBUG: Optimize the code in the -g versions, gives us a faster debug java ifeq ($(FASTDEBUG), true) - CFLAGS_DBG += $(CC_LOWER_OPT) - CXXFLAGS_DBG += $(CC_LOWER_OPT) + CFLAGS_DBG += $(CC_OPT/LOWER) + CXXFLAGS_DBG += $(CC_OPT/LOWER) endif CPPFLAGS_COMMON = -D$(ARCH) -DARCH='"$(ARCH)"' -DLINUX $(VERSION_DEFINES) \ @@ -187,6 +179,9 @@ endif CPPFLAGS_OPT = CPPFLAGS_DBG = -DDEBUG +ifneq ($(PRODUCT), java) + CPPFLAGS_DBG += -DLOGGING +endif ifdef LIBRARY # Libraries need to locate other libraries at runtime, and you can tell diff --git a/make/common/Defs-solaris.gmk b/make/common/Defs-solaris.gmk index 01ea5bd27..71e56d4c0 100644 --- a/make/common/Defs-solaris.gmk +++ b/make/common/Defs-solaris.gmk @@ -83,15 +83,16 @@ HPIS = native # # Java default optimization (-x04/-O2) etc. Applies to the VM. # -ifeq ($(PRODUCT), java) - _OPT = $(CC_HIGHER_OPT) -else - _OPT = $(CC_LOWER_OPT) - CPPFLAGS_DBG += -DLOGGING -DDBINFO +ifndef OPTIMIZATION_LEVEL + ifeq ($(PRODUCT), java) + OPTIMIZATION_LEVEL = HIGHER + else + OPTIMIZATION_LEVEL = LOWER + endif endif # -# If -Xa is in CFLAGS_COMMON it will end up ahead of $(POPT) for the +# If -Xa is in CFLAGS_COMMON it will end up ahead of $(CC_OPT) for the # optimized build, and that ordering of the flags completely freaks # out cc. Hence, -Xa is instead in each CFLAGS variant. # @@ -116,8 +117,8 @@ endif # # Debug flag for C and C++ compiler # -CFLAGS_DEBUG_OPTION=-g -CXXFLAGS_DEBUG_OPTION=-g +CFLAGS_DEBUG_OPTION = -g $(CC_OPT/NONE) +CXXFLAGS_DEBUG_OPTION = -g $(CXX_OPT/NONE) # Turn off -g if we are doing tcov build ifdef TCOV_BUILD @@ -135,15 +136,14 @@ endif # Performance/size of files should be about the same, maybe smaller. # ifeq ($(FASTDEBUG), true) - CC_FASTDEBUG_OPT = $(CC_LOWER_OPT) - CFLAGS_DEBUG_OPTION = -g $(CC_FASTDEBUG_OPT) - CXXFLAGS_DEBUG_OPTION = -g0 $(CC_FASTDEBUG_OPT) + CFLAGS_DEBUG_OPTION = -g $(CC_OPT/LOWER) + CXXFLAGS_DEBUG_OPTION = -g0 $(CXX_OPT/LOWER) endif CFLAGS_COMMON = -v -mt -L$(OBJDIR) -xc99=%none CFLAGS_COMMON += -xCC CFLAGS_COMMON += -errshort=tags -CFLAGS_OPT = $(POPT) +CFLAGS_OPT = $(CC_OPT) CFLAGS_DBG = $(CFLAGS_DEBUG_OPTION) CFLAGS_COMMON += -Xa $(CFLAGS_REQUIRED) @@ -171,7 +171,7 @@ ifeq ($(COMPILER_WARNINGS_FATAL),true) CXXFLAGS_COMMON += -errwarn=%all endif -CXXFLAGS_OPT = $(POPT) +CXXFLAGS_OPT = $(CXX_OPT) CXXFLAGS_DBG = $(CXXFLAGS_DEBUG_OPTION) CXXFLAGS_COMMON += $(CFLAGS_REQUIRED) @@ -241,6 +241,10 @@ CPPFLAGS_COMMON = -D$(ARCH_FAMILY) -D__solaris__ -D_REENTRANT CPPFLAGS_OPT = CPPFLAGS_DBG = -DDEBUG +ifneq ($(PRODUCT), java) + CPPFLAGS_DBG += -DLOGGING -DDBINFO +endif + ifeq ($(ARCH_FAMILY), i586) # The macro _LITTLE_ENDIAN needs to be defined the same to avoid the # Sun C compiler warning message: warning: macro redefined: _LITTLE_ENDIAN @@ -384,63 +388,151 @@ endif # Different "levels" of optimization. # ifeq ($(CC_VERSION),gcc) - CC_HIGHEST_OPT = -O3 - CC_HIGHER_OPT = -O3 - CC_LOWER_OPT = -O2 + + CC_OPT/NONE = + CC_OPT/LOWER = -O2 + CC_OPT/HIGHER = -O3 + CC_OPT/HIGHEST = -O3 + + CXX_OPT/NONE = + CXX_OPT/LOWER = -O2 + CXX_OPT/HIGHER = -O3 + CXX_OPT/HIGHEST = -O3 + CFLAGS_REQUIRED_i586 += -fno-omit-frame-pointer CFLAGS_REQUIRED_amd64 += -fno-omit-frame-pointer + # Automatic precompiled header option to use (if COMPILE_APPROACH=batch) # (See Rules.gmk) May need to wait for gcc 5? AUTOMATIC_PCH_OPTION = + else + # Highest could be -xO5, but indications are that -xO5 should be reserved # for a per-file use, on sources with known performance impacts. - CC_HIGHEST_OPT = -xO4 - CC_HIGHER_OPT = -xO4 - CC_LOWER_OPT = -xO2 + OPT_LEVEL/LOWER = 2 + OPT_LEVEL/HIGHER = 4 + OPT_LEVEL/HIGHEST = 4 + + CC_OPT/NONE = + CC_OPT/LOWER = $(OPT_LEVEL/LOWER:%=-xO%) + CC_OPT/HIGHER = $(OPT_LEVEL/HIGHER:%=-xO%) + CC_OPT/HIGHEST = $(OPT_LEVEL/HIGHEST:%=-xO%) + + CXX_OPT/NONE = + CXX_OPT/LOWER = $(OPT_LEVEL/LOWER:%=-xO%) + CXX_OPT/HIGHER = $(OPT_LEVEL/HIGHER:%=-xO%) + CXX_OPT/HIGHEST = $(OPT_LEVEL/HIGHEST:%=-xO%) + + # We need stack frames at all times + USE_XKEEPFRAME_OPTION = false + ifeq ($(USE_XKEEPFRAME_OPTION),true) + + # Unknown spelling on this option at this time (Maybe in SS13?) + CC_XKEEPFRAME_OPTIONS = -xkeepframe + CXX_XKEEPFRAME_OPTIONS = -xkeepframe + + else + + # On X86, make sure tail call optimization is off + # The z and y are the tail call optimizations. + ifeq ($(ARCH_FAMILY), i586) + ifeq ($(shell $(EXPR) $(CC_VER) \> 5.8), 1) + # Somehow, tail call optimization is creeping in. + # Make sure it is off. + # WARNING: These may cause compiler warnings about duplicate -O options + CC_XKEEPFRAME_OPTIONS += -Wu,-O$(OPT_LEVEL/$(OPTIMIZATION_LEVEL))~yz + CXX_XKEEPFRAME_OPTIONS += -Qoption ube -O$(OPT_LEVEL/$(OPTIMIZATION_LEVEL))~yz + endif + endif + + # On i586 we need to tell the code generator to ALWAYS use a + # frame pointer. + ifeq ($(ARCH_FAMILY), i586) + # Note that in 5.7, this is done with -xregs=no%frameptr + ifeq ($(CC_VER), 5.5) + # It's not exactly clear when this optimization kicks in, the + # current assumption is -xO4 or greater and for C++ with + # the -features=no%except option and -xO4 and greater. + # Bottom line is, we ALWAYS want a frame pointer! + CC_XKEEPFRAME_OPTIONS += -Wu,-Z~B + CXX_XKEEPFRAME_OPTIONS += -Qoption ube -Z~B + endif + ifeq ($(shell $(EXPR) $(CC_VER) \> 5.6), 1) + # Do NOT use frame pointer register as a general purpose opt register + CC_OPT/NONE += -xregs=no%frameptr + CXX_OPT/NONE += -xregs=no%frameptr + CC_XKEEPFRAME_OPTIONS += -xregs=no%frameptr + CXX_XKEEPFRAME_OPTIONS += -xregs=no%frameptr + endif + endif + + # Optimizer for sparc needs to be told not to do certain things + # related to frames or save instructions. + ifeq ($(ARCH_FAMILY), sparc) + # Do not use save instructions instead of add instructions + # This was an optimization starting in SC5.0 that made it hard for us to + # find the "save" instruction (which got turned into an "add") + CC_XKEEPFRAME_OPTIONS += -Wc,-Qrm-s + CXX_XKEEPFRAME_OPTIONS += -Qoption cg -Qrm-s + # Don't allow tail call code optimization. Started in SC5.0. + # We don't like code of this form: + # save + # + # call foo + # restore + # because we can't tell if the method will have a stack frame + # and register windows or not. + CC_XKEEPFRAME_OPTIONS += -Wc,-Qiselect-T0 + CXX_XKEEPFRAME_OPTIONS += -Qoption cg -Qiselect-T0 + endif + + endif + + # Extra options used with HIGHEST # - # WARNING: Use of _OPT=$(CC_HIGHEST_OPT) in your Makefile needs to be + # WARNING: Use of OPTIMIZATION_LEVEL=HIGHEST in your Makefile needs to be # done with care, there are some assumptions below that need to # be understood about the use of pointers, and IEEE behavior. # # Use non-standard floating point mode (not IEEE 754) - CC_HIGHEST_OPT += -fns + CC_HIGHEST_EXTRAS += -fns # Do some simplification of floating point arithmetic (not IEEE 754) - CC_HIGHEST_OPT += -fsimple + CC_HIGHEST_EXTRAS += -fsimple # Use single precision floating point with 'float' - CC_HIGHEST_OPT += -fsingle + CC_HIGHEST_EXTRAS += -fsingle # Assume memory references via basic pointer types do not alias # (Source with excessing pointer casting and data access with mixed # pointer types are not recommended) - CC_HIGHEST_OPT += -xalias_level=basic + CC_HIGHEST_EXTRAS += -xalias_level=basic # Use intrinsic or inline versions for math/std functions # (If you expect perfect errno behavior, do not use this) - CC_HIGHEST_OPT += -xbuiltin=%all + CC_HIGHEST_EXTRAS += -xbuiltin=%all # Loop data dependency optimizations (need -xO3 or higher) - CC_HIGHEST_OPT += -xdepend + CC_HIGHEST_EXTRAS += -xdepend # Pointer parameters to functions do not overlap # (Similar to -xalias_level=basic usage, but less obvious sometimes. # If you pass in multiple pointers to the same data, do not use this) - CC_HIGHEST_OPT += -xrestrict + CC_HIGHEST_EXTRAS += -xrestrict # Inline some library routines # (If you expect perfect errno behavior, do not use this) - CC_HIGHEST_OPT += -xlibmil + CC_HIGHEST_EXTRAS += -xlibmil # Use optimized math routines # (If you expect perfect errno behavior, do not use this) # Can cause undefined external on Solaris 8 X86 on __sincos, removing for now - # CC_HIGHEST_OPT += -xlibmopt + # CC_HIGHEST_EXTRAS += -xlibmopt ifeq ($(ARCH_FAMILY), sparc) # Assume at most 8byte alignment, raise SIGBUS on error ### Presents an ABI issue with customer JNI libs? - ####CC_HIGHEST_OPT += -xmemalign=8s + ####CC_HIGHEST_EXTRAS += -xmemalign=8s # Automatic prefetch instructions, explicit prefetch macros - CC_HIGHEST_OPT += -xprefetch=auto,explicit + CC_HIGHEST_EXTRAS += -xprefetch=auto,explicit # Pick ultra as the chip to optimize to - CC_HIGHEST_OPT += -xchip=ultra + CC_HIGHEST_EXTRAS += -xchip=ultra endif ifeq ($(ARCH), i586) # Pick pentium as the chip to optimize to - CC_HIGHEST_OPT += -xchip=pentium + CC_HIGHEST_EXTRAS += -xchip=pentium endif ifdef LIBRARY # The Solaris CBE (Common Build Environment) requires that the use @@ -450,9 +542,6 @@ else CFLAGS_REQUIRED_sparcv9 += -xregs=no%appl endif ifeq ($(shell $(EXPR) $(CC_VER) \> 5.6), 1) - # Do NOT use the frame pointer register as a general purpose opt register - CFLAGS_REQUIRED_i586 += -xregs=no%frameptr - CFLAGS_REQUIRED_amd64 += -xregs=no%frameptr # We MUST allow data alignment of 4 for sparc V8 (32bit) # Presents an ABI issue with customer JNI libs? We must be able to # to handle 4byte aligned objects? (rare occurance, but possible?) @@ -466,77 +555,28 @@ else # Automatic precompiled header option to use (if COMPILE_APPROACH=batch) # (See Rules.gmk) The SS11 -xpch=auto* options appear to be broken. AUTOMATIC_PCH_OPTION = + + # Add in keep frame options + CC_OPT/LOWER += $(CC_XKEEPFRAME_OPTIONS) + CC_OPT/HIGHER += $(CC_XKEEPFRAME_OPTIONS) + CC_OPT/HIGHEST += $(CC_XKEEPFRAME_OPTIONS) + CXX_OPT/LOWER += $(CXX_XKEEPFRAME_OPTIONS) + CXX_OPT/HIGHER += $(CXX_XKEEPFRAME_OPTIONS) + CXX_OPT/HIGHEST += $(CXX_XKEEPFRAME_OPTIONS) + + # Add in highest optimization settings + CC_OPT/HIGHEST += $(CC_HIGHEST_EXTRAS) + CXX_OPT/HIGHEST += $(CC_HIGHEST_EXTRAS) + endif -CC_NO_OPT = -# If NO_OPTIMIZATIONS is defined in the environment, turn all optimzations off -ifdef NO_OPTIMIZATIONS - CC_HIGHEST_OPT = $(CC_NO_OPT) - CC_HIGHER_OPT = $(CC_NO_OPT) - CC_LOWER_OPT = $(CC_NO_OPT) -endif +# Default optimization settings based on level. +CC_OPT = $(CC_OPT/$(OPTIMIZATION_LEVEL)) +CXX_OPT = $(CXX_OPT/$(OPTIMIZATION_LEVEL)) # Flags required all the time CFLAGS_REQUIRED = $(CFLAGS_REQUIRED_$(ARCH)) -# Add processor specific options for optimizations -CC_HIGHEST_OPT += $(_OPT_$(ARCH)) -CC_HIGHER_OPT += $(_OPT_$(ARCH)) -CC_LOWER_OPT += $(_OPT_$(ARCH)) - -# Secret compiler optimization options that should be in the above macros -# but since they differ in format from C to C++, are added into the C or -# C++ specific macros for compiler flags. -# -# On i586 we need to tell the code generator to ALWAYS use a -# frame pointer. -ifeq ($(ARCH_FAMILY), i586) - # Note that in 5.7, this is done with -xregs=no%frameptr - ifeq ($(CC_VER), 5.5) - # It's not exactly clear when this optimization kicks in, the - # current assumption is -xO4 or greater and for C++ with - # the -features=no%except option and -xO4 and greater. - # Bottom line is, we ALWAYS want a frame pointer! - CXXFLAGS_OPT += -Qoption ube -Z~B - CFLAGS_OPT += -Wu,-Z~B - ifeq ($(FASTDEBUG), true) - CXXFLAGS_DBG += -Qoption ube -Z~B - CFLAGS_DBG += -Wu,-Z~B - endif - endif -endif -# -# Optimizer for sparc needs to be told not to do certain things -# related to frames or save instructions. -ifeq ($(ARCH_FAMILY), sparc) - # NOTE: Someday the compilers will provide a high-level option for this. - # Use save instructions instead of add instructions - # This was an optimization starting in SC5.0 that made it hard for us to - # find the "save" instruction (which got turned into an "add") - CXXFLAGS_OPT += -Qoption cg -Qrm-s - CFLAGS_OPT += -Wc,-Qrm-s - ifeq ($(FASTDEBUG), true) - CXXFLAGS_DBG += -Qoption cg -Qrm-s - CFLAGS_DBG += -Wc,-Qrm-s - endif - # - # NOTE: Someday the compilers will provide a high-level option for this. - # Don't allow tail call code optimization. Started in SC5.0. - # We don't like code of this form: - # save - # - # call foo - # restore - # because we can't tell if the method will have a stack frame - # and register windows or not. - CXXFLAGS_OPT += -Qoption cg -Qiselect-T0 - CFLAGS_OPT += -Wc,-Qiselect-T0 - ifeq ($(FASTDEBUG), true) - CXXFLAGS_DBG += -Qoption cg -Qiselect-T0 - CFLAGS_DBG += -Wc,-Qiselect-T0 - endif -endif - # # Path and option to link against the VM, if you have to. Note that # there are libraries that link against only -ljava, but they do get diff --git a/make/common/Defs-windows.gmk b/make/common/Defs-windows.gmk index 5bb50249c..cecf88677 100644 --- a/make/common/Defs-windows.gmk +++ b/make/common/Defs-windows.gmk @@ -84,6 +84,15 @@ EXTRA_LFLAGS += /LIBPATH:$(DXSDK_LIB_PATH) # # Default optimization # + +ifndef OPTIMIZATION_LEVEL + ifeq ($(PRODUCT), java) + OPTIMIZATION_LEVEL = HIGHER + else + OPTIMIZATION_LEVEL = LOWER + endif +endif + ifeq ($(CC_VERSION),msvc) # Visual Studio .NET 2003 or VS2003 compiler option definitions: # -O1 Favors reduced size over speed (-Og -Os -Oy -Ob2 -Gs -GF -Gy) @@ -113,21 +122,28 @@ ifeq ($(CC_VERSION),msvc) # NOTE: With VC6, -Ox, -O1, and -O2 used -Ob1, not -Ob2. # NOTE: With VC6, -O1 and -O2 used -Gf, not -GF. # + + CC_OPT/NONE = -Od + CC_OPT/LOWER = -O2 + CC_OPT/HIGHER = -O3 + CC_OPT/HIGHEST = -O3 + ifeq ($(COMPILER_VERSION), VC6) # VC6 (6.2) msvc compiler (the way Tiger and early Mustang were built) # Automatic precompiled header option to use (if COMPILE_APPROACH=batch) AUTOMATIC_PCH_OPTION = GX_OPTION = -GX ifeq ($(ARCH_DATA_MODEL), 32) - CC_HIGHEST_OPT = -Ox -Gy -Os -GB - CC_HIGHER_OPT = -Ox -Gy -Os -GB - CC_LOWER_OPT = -Ox -Gy -Os -GB + CC_OPT/HIGHEST = -Ox -Gy -Os -GB + CC_OPT/HIGHER = -Ox -Gy -Os -GB + CC_OPT/LOWER = -Ox -Gy -Os -GB else - CC_HIGHEST_OPT = -Ox -Gy -Op - CC_HIGHER_OPT = -Ox -Gy -Op - CC_LOWER_OPT = -Ox -Gy -Op + CC_OPT/HIGHEST = -Ox -Gy -Op + CC_OPT/HIGHER = -Ox -Gy -Op + CC_OPT/LOWER = -Ox -Gy -Op endif endif + ifeq ($(COMPILER_VERSION), VS2003) # Automatic precompiled header option to use (if COMPILE_APPROACH=batch) AUTOMATIC_PCH_OPTION = -YX @@ -135,53 +151,45 @@ ifeq ($(CC_VERSION),msvc) GX_OPTION = -GX ifeq ($(ARCH_DATA_MODEL), 32) # Lowered opt level to try and reduce footprint, dll size especially. - # Was: CC_HIGHEST_OPT = -O2 -G6 - # Was: CC_HIGHER_OPT = -O2 - CC_HIGHEST_OPT = -O2 - CC_HIGHER_OPT = -O1 - CC_LOWER_OPT = -O1 + # Was: CC_OPT/HIGHEST = -O2 -G6 + # Was: CC_OPT/HIGHER = -O2 + CC_OPT/HIGHEST = -O2 + CC_OPT/HIGHER = -O1 + CC_OPT/LOWER = -O1 else - CC_HIGHEST_OPT = -O2 -Op - CC_HIGHER_OPT = -O2 -Op - CC_LOWER_OPT = -O1 -Op + CC_OPT/HIGHEST = -O2 -Op + CC_OPT/HIGHER = -O2 -Op + CC_OPT/LOWER = -O1 -Op endif endif + ifeq ($(COMPILER_VERSION), VS2005) # Automatic precompiled header option to use (if COMPILE_APPROACH=batch) AUTOMATIC_PCH_OPTION = # VS2005 compiler, only with Platform SDK right now? GX_OPTION = -EHsc ifeq ($(ARCH_DATA_MODEL), 32) - CC_HIGHEST_OPT = -O2 - CC_HIGHER_OPT = -O1 - CC_LOWER_OPT = -O1 + CC_OPT/HIGHEST = -O2 + CC_OPT/HIGHER = -O1 + CC_OPT/LOWER = -O1 else - CC_HIGHEST_OPT = -O2 - CC_HIGHER_OPT = -O1 - CC_LOWER_OPT = -O1 + CC_OPT/HIGHEST = -O2 + CC_OPT/HIGHER = -O1 + CC_OPT/LOWER = -O1 endif endif - CC_NO_OPT = -Od + else # CC_VERSION + # GCC not supported, but left for historical reference... - CC_HIGHEST_OPT = -O3 - CC_HIGHER_OPT = -O2 - CC_LOWER_OPT = -O2 - CC_NO_OPT = -endif + CC_OPT/NONE = + CC_OPT/LOWER = -O2 + CC_OPT/HIGHER = -O2 + CC_OPT/HIGHEST = -O3 -# If NO_OPTIMIZATIONS is defined in the environment, turn all optimzations off -ifdef NO_OPTIMIZATIONS - CC_HIGHEST_OPT = $(CC_NO_OPT) - CC_HIGHER_OPT = $(CC_NO_OPT) - CC_LOWER_OPT = $(CC_NO_OPT) endif -ifeq ($(PRODUCT), java) - _OPT = $(CC_HIGHER_OPT) -else - _OPT = $(CC_LOWER_OPT) -endif +CC_OPT = $(CC_OPT/$(OPTIMIZATION_LEVEL)) # Select the runtime support library carefully, need to be consistent # @@ -233,7 +241,7 @@ ifeq ($(CC_VERSION),msvc) # Use static link for the C++ runtime (so msvcp71.dll not needed) # CFLAGS_COMMON += -Zi -nologo - CFLAGS_OPT = $(POPT) + CFLAGS_OPT = $(CC_OPT) CFLAGS_DBG = -Od $(MS_RUNTIME_DEBUG_OPTION) # Starting from VS2005 the wchar_t is handled as a built-in C/C++ data type diff --git a/make/common/Defs.gmk b/make/common/Defs.gmk index 1c72ff99d..289e5f5a8 100644 --- a/make/common/Defs.gmk +++ b/make/common/Defs.gmk @@ -482,11 +482,6 @@ PKGDIR = $(subst .,/,$(PACKAGE)) # include $(JDK_MAKE_SHARED_DIR)/Defs-java.gmk -# -# Set opt level to ALT_OPT if set otherwise _OPT -# -POPT = $(_OPT$(ALT_OPT))$(ALT_OPT) - # # Convenient macros # diff --git a/make/common/Library.gmk b/make/common/Library.gmk index 2b9fe5d46..2aa247f81 100644 --- a/make/common/Library.gmk +++ b/make/common/Library.gmk @@ -238,7 +238,7 @@ else # PLATFORM # $(ACTUAL_LIBRARY):: $(COMPILE_FILES_o) $(FILES_m) $(FILES_reorder) @$(prep-target) - @$(ECHO) "STATS: LIBRARY=$(LIBRARY), PRODUCT=$(PRODUCT), _OPT=$(_OPT)" + @$(ECHO) "STATS: LIBRARY=$(LIBRARY), PRODUCT=$(PRODUCT), OPTIMIZATION_LEVEL=$(OPTIMIZATION_LEVEL)" @$(ECHO) "Rebuilding $@ because of $?" ifeq ($(LIBRARY), fdlibm) $(AR) -r $@ $(FILES_o) diff --git a/make/common/shared/Defs.gmk b/make/common/shared/Defs.gmk index 8fc28e239..05f33b098 100644 --- a/make/common/shared/Defs.gmk +++ b/make/common/shared/Defs.gmk @@ -277,9 +277,6 @@ PROMOTED_BUILD_LATEST = latest PROMOTED_BUILD_BASEDIR = $(PROMOTED_RE_AREA)/$(PROMOTED_BUILD_LATEST) PROMOTED_BUILD_BINARIES = $(PROMOTED_BUILD_BASEDIR)/binaries -# OPT: Changes what the optimizations settings (in _OPT) -POPT = $(_OPT$(ALT_OPT))$(ALT_OPT) - # PARALLEL_COMPILE_JOBS: is the number of compiles done in parallel. # If the user sets ALT_PARALLEL_COMPILE_JOBS, then COMPILE_APPROACH is set # to parallel. diff --git a/make/java/fdlibm/Makefile b/make/java/fdlibm/Makefile index fd2442b03..b0259ee35 100644 --- a/make/java/fdlibm/Makefile +++ b/make/java/fdlibm/Makefile @@ -33,6 +33,7 @@ BUILDDIR = ../.. LIBRARY = fdlibm PRODUCT = java + include $(BUILDDIR)/common/Defs.gmk # @@ -40,16 +41,25 @@ include $(BUILDDIR)/common/Defs.gmk # FDLIBM_SRC = $(SHARE_SRC)/native/java/lang/fdlibm -# windows compiler flags +# Windows: compiler flags ifeq ($(PLATFORM),windows) # Turn all optimizations off - _OPT = $(CC_NO_OPT) + OPTIMIZATION_LEVEL = NONE OTHER_CFLAGS = CPPFLAGS_DBG += -DLOGGING # Files built here do not compile with warning level 3 if warnings are fatal COMPILER_WARNINGS_FATAL=false endif +# +# Linux: Disable optimization to get correctly reproducible +# floating-point results. +# +ifeq ($(PLATFORM),linux) + # Turn all optimizations off + OPTIMIZATION_LEVEL = NONE +endif + # # Include path. # @@ -70,15 +80,6 @@ include FILES_c.gmk # include $(BUILDDIR)/common/Library.gmk -# -# Disable optimization to get correctly reproducible -# floating-point results. -# -ifeq ($(PLATFORM),linux) - # Turn all optimizations off - _OPT = $(CC_NO_OPT) -endif - # # Find fdlibm source files. # diff --git a/make/java/java_hprof_demo/Makefile b/make/java/java_hprof_demo/Makefile index 71529433b..ec55c4708 100644 --- a/make/java/java_hprof_demo/Makefile +++ b/make/java/java_hprof_demo/Makefile @@ -28,14 +28,14 @@ LIBRARY = hprof PRODUCT = sun LIBRARY_OUTPUT = hprof_jvmti -# Configure the CFLAGS for this library. +# Use highest optimization +OPTIMIZATION_LEVEL = HIGHEST +# Configure the CFLAGS for this library. FILES_m = mapfile-vers include $(BUILDDIR)/common/Defs.gmk -_OPT=$(CC_HIGHEST_OPT) - SRCDIR=$(SHARE_SRC)/demo/jvmti/hprof PSRCDIR=$(PLATFORM_SRC)/demo/jvmti/hprof diff --git a/make/sun/awt/Makefile b/make/sun/awt/Makefile index 53db7b5b6..57c965ae2 100644 --- a/make/sun/awt/Makefile +++ b/make/sun/awt/Makefile @@ -28,17 +28,13 @@ PACKAGE = sun.awt LIBRARY = awt PRODUCT = sun -# # Tell Defs.gmk that VIS is needed -# VIS_NEEDED=true -include $(BUILDDIR)/common/Defs.gmk - -# # Use highest optimization level -# -_OPT = $(CC_HIGHEST_OPT) +OPTMIZATION_LEVEL = HIGHEST + +include $(BUILDDIR)/common/Defs.gmk OTHER_CFLAGS += -D__MEDIALIB_OLD_NAMES -D__USE_J2D_NAMES diff --git a/make/sun/font/Makefile b/make/sun/font/Makefile index def2eba80..5e2ab8c84 100644 --- a/make/sun/font/Makefile +++ b/make/sun/font/Makefile @@ -35,6 +35,9 @@ PRODUCT = sun # Indicate we want the C++ compiler to do the linking. CPLUSPLUSLIBRARY=true +# Use higher optimization level +OPTIMIZATION_LEVEL = HIGHER + include $(BUILDDIR)/common/Defs.gmk # @@ -48,11 +51,6 @@ endif # Files # -# -# Use higher optimization level -# -_OPT = $(CC_HIGHER_OPT) - include FILES_c.gmk AUTO_FILES_JAVA_DIRS = sun/font diff --git a/make/sun/font/t2k/Makefile b/make/sun/font/t2k/Makefile index 93d2e59d2..68ec5ec50 100644 --- a/make/sun/font/t2k/Makefile +++ b/make/sun/font/t2k/Makefile @@ -41,12 +41,10 @@ CPLUSPLUSLIBRARY=true # for a few ones with native methods) so shouldn't clobber them. DONT_CLOBBER_CLASSES=true -include $(BUILDDIR)/common/Defs.gmk - -# # Use higher optimization level -# -_OPT = $(CC_HIGHER_OPT) +OPTIMIZATION_LEVEL = HIGHER + +include $(BUILDDIR)/common/Defs.gmk # # Files diff --git a/make/sun/image/generic/Makefile b/make/sun/image/generic/Makefile index 6157c79f5..665755337 100644 --- a/make/sun/image/generic/Makefile +++ b/make/sun/image/generic/Makefile @@ -31,12 +31,11 @@ BUILDDIR = ../../.. PACKAGE = sun.awt.medialib LIBRARY = mlib_image PRODUCT = sun -include $(BUILDDIR)/common/Defs.gmk -# # Use highest level of optimization on this library -# -_OPT = $(CC_HIGHEST_OPT) +OPTIMIZATION_LEVEL = HIGHEST + +include $(BUILDDIR)/common/Defs.gmk # # Use mapfile diff --git a/make/sun/image/vis/Makefile b/make/sun/image/vis/Makefile index 00c11c361..bb23d6a32 100644 --- a/make/sun/image/vis/Makefile +++ b/make/sun/image/vis/Makefile @@ -31,17 +31,13 @@ PACKAGE = sun.awt.medialib LIBRARY = mlib_image_v PRODUCT = sun -# # Tell Defs.gmk we need VIS instructions -# VIS_NEEDED=true -include $(BUILDDIR)/common/Defs.gmk - -# # Select highest level of optimization for this library -# -_OPT = $(CC_HIGHEST_OPT) +OPTIMIZATION_LEVEL = HIGHEST + +include $(BUILDDIR)/common/Defs.gmk # # Use generic mapfile diff --git a/make/sun/jpeg/Makefile b/make/sun/jpeg/Makefile index 45bc70855..e06f9c99f 100644 --- a/make/sun/jpeg/Makefile +++ b/make/sun/jpeg/Makefile @@ -27,12 +27,11 @@ BUILDDIR = ../.. PACKAGE = sun.awt LIBRARY = jpeg PRODUCT = sun -include $(BUILDDIR)/common/Defs.gmk -# # Use highest optimization level -# -_OPT = $(CC_HIGHEST_OPT) +OPTIMIZATION_LEVEL = HIGHEST + +include $(BUILDDIR)/common/Defs.gmk # # Files -- GitLab From 8b5d3f940b8533045a4a58290bac1b2610d30d5d Mon Sep 17 00:00:00 2001 From: dfuchs Date: Thu, 31 Jul 2008 12:41:35 +0200 Subject: [PATCH 025/139] 6730926: Document that create/registerMBean can throw RuntimeMBeanException from postRegister Reviewed-by: emcmanus --- .../DefaultMBeanServerInterceptor.java | 53 +- .../javax/management/MBeanRegistration.java | 25 +- .../classes/javax/management/MBeanServer.java | 19 + .../management/MBeanServerConnection.java | 99 +++- .../MBeanServer/PostExceptionTest.java | 516 ++++++++++++++++++ 5 files changed, 693 insertions(+), 19 deletions(-) create mode 100644 test/javax/management/MBeanServer/PostExceptionTest.java diff --git a/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java index 99b10ad1e..8bd6cba27 100644 --- a/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java +++ b/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java @@ -453,11 +453,12 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { final ResourceContext context = unregisterFromRepository(resource, instance, name); - - if (instance instanceof MBeanRegistration) - postDeregisterInvoke((MBeanRegistration) instance); - - context.done(); + try { + if (instance instanceof MBeanRegistration) + postDeregisterInvoke(name,(MBeanRegistration) instance); + } finally { + context.done(); + } } public ObjectInstance getObjectInstance(ObjectName name) @@ -989,10 +990,12 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { registerFailed = false; registered = true; } finally { - postRegister(mbean, registered, registerFailed); + try { + postRegister(logicalName, mbean, registered, registerFailed); + } finally { + if (registered) context.done(); + } } - - context.done(); return new ObjectInstance(logicalName, classname); } @@ -1051,7 +1054,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } private static void postRegister( - DynamicMBean mbean, boolean registrationDone, boolean registerFailed) { + ObjectName logicalName, DynamicMBean mbean, + boolean registrationDone, boolean registerFailed) { if (registerFailed && mbean instanceof DynamicMBean2) ((DynamicMBean2) mbean).registerFailed(); @@ -1059,11 +1063,19 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { if (mbean instanceof MBeanRegistration) ((MBeanRegistration) mbean).postRegister(registrationDone); } catch (RuntimeException e) { + MBEANSERVER_LOGGER.fine("While registering MBean ["+logicalName+ + "]: " + "Exception thrown by postRegister: " + + "rethrowing <"+e+">, but keeping the MBean registered"); throw new RuntimeMBeanException(e, - "RuntimeException thrown in postRegister method"); + "RuntimeException thrown in postRegister method: "+ + "rethrowing <"+e+">, but keeping the MBean registered"); } catch (Error er) { + MBEANSERVER_LOGGER.fine("While registering MBean ["+logicalName+ + "]: " + "Error thrown by postRegister: " + + "rethrowing <"+er+">, but keeping the MBean registered"); throw new RuntimeErrorException(er, - "Error thrown in postRegister method"); + "Error thrown in postRegister method: "+ + "rethrowing <"+er+">, but keeping the MBean registered"); } } @@ -1076,15 +1088,28 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } } - private static void postDeregisterInvoke(MBeanRegistration moi) { + private static void postDeregisterInvoke(ObjectName mbean, + MBeanRegistration moi) { try { moi.postDeregister(); } catch (RuntimeException e) { + MBEANSERVER_LOGGER.fine("While unregistering MBean ["+mbean+ + "]: " + "Exception thrown by postDeregister: " + + "rethrowing <"+e+">, although the MBean is succesfully " + + "unregistered"); throw new RuntimeMBeanException(e, - "RuntimeException thrown in postDeregister method"); + "RuntimeException thrown in postDeregister method: "+ + "rethrowing <"+e+ + ">, although the MBean is sucessfully unregistered"); } catch (Error er) { + MBEANSERVER_LOGGER.fine("While unregistering MBean ["+mbean+ + "]: " + "Error thrown by postDeregister: " + + "rethrowing <"+er+">, although the MBean is succesfully " + + "unregistered"); throw new RuntimeErrorException(er, - "Error thrown in postDeregister method"); + "Error thrown in postDeregister method: "+ + "rethrowing <"+er+ + ">, although the MBean is sucessfully unregistered"); } } diff --git a/src/share/classes/javax/management/MBeanRegistration.java b/src/share/classes/javax/management/MBeanRegistration.java index 1ba1c0d82..be51f4a89 100644 --- a/src/share/classes/javax/management/MBeanRegistration.java +++ b/src/share/classes/javax/management/MBeanRegistration.java @@ -158,7 +158,19 @@ public interface MBeanRegistration { /** * Allows the MBean to perform any operations needed after having been * registered in the MBean server or after the registration has failed. - * + *

If the implementation of this method throws a {@link RuntimeException} + * or an {@link Error}, the MBean Server will rethrow those inside + * a {@link RuntimeMBeanException} or {@link RuntimeErrorException}, + * respectively. However, throwing an exception in {@code postRegister} + * will not change the state of the MBean: + * if the MBean was already registered ({@code registrationDone} is + * {@code true}), the MBean will remain registered.

+ *

This might be confusing for the code calling {@code createMBean()} + * or {@code registerMBean()}, as such code might assume that MBean + * registration has failed when such an exception is raised. + * Therefore it is recommended that implementations of + * {@code postRegister} do not throw Runtime Exceptions or Errors if it + * can be avoided.

* @param registrationDone Indicates whether or not the MBean has * been successfully registered in the MBean server. The value * false means that the registration phase has failed. @@ -178,6 +190,17 @@ public interface MBeanRegistration { /** * Allows the MBean to perform any operations needed after having been * unregistered in the MBean server. + *

If the implementation of this method throws a {@link RuntimeException} + * or an {@link Error}, the MBean Server will rethrow those inside + * a {@link RuntimeMBeanException} or {@link RuntimeErrorException}, + * respectively. However, throwing an excepption in {@code postDeregister} + * will not change the state of the MBean: + * the MBean was already successfully deregistered and will remain so.

+ *

This might be confusing for the code calling + * {@code unregisterMBean()}, as it might assume that MBean deregistration + * has failed. Therefore it is recommended that implementations of + * {@code postDeregister} do not throw Runtime Exceptions or Errors if it + * can be avoided.

*/ public void postDeregister(); diff --git a/src/share/classes/javax/management/MBeanServer.java b/src/share/classes/javax/management/MBeanServer.java index f0d4d16c4..55a096aef 100644 --- a/src/share/classes/javax/management/MBeanServer.java +++ b/src/share/classes/javax/management/MBeanServer.java @@ -328,11 +328,30 @@ public interface MBeanServer extends MBeanServerConnection { * preRegister (MBeanRegistration * interface) method of the MBean has thrown an exception. The * MBean will not be registered. + * @exception RuntimeMBeanException If the postRegister + * (MBeanRegistration interface) method of the MBean throws a + * RuntimeException, the registerMBean method will + * throw a RuntimeMBeanException, although the MBean + * registration succeeded. In such a case, the MBean will be actually + * registered even though the registerMBean method + * threw an exception. Note that RuntimeMBeanException can + * also be thrown by preRegister, in which case the MBean + * will not be registered. + * @exception RuntimeErrorException If the postRegister + * (MBeanRegistration interface) method of the MBean throws an + * Error, the registerMBean method will + * throw a RuntimeErrorException, although the MBean + * registration succeeded. In such a case, the MBean will be actually + * registered even though the registerMBean method + * threw an exception. Note that RuntimeErrorException can + * also be thrown by preRegister, in which case the MBean + * will not be registered. * @exception NotCompliantMBeanException This object is not a JMX * compliant MBean * @exception RuntimeOperationsException Wraps a * java.lang.IllegalArgumentException: The object * passed in parameter is null or no object name is specified. + * @see javax.management.MBeanRegistration */ public ObjectInstance registerMBean(Object object, ObjectName name) throws InstanceAlreadyExistsException, MBeanRegistrationException, diff --git a/src/share/classes/javax/management/MBeanServerConnection.java b/src/share/classes/javax/management/MBeanServerConnection.java index 4047373c2..3f3bc4442 100644 --- a/src/share/classes/javax/management/MBeanServerConnection.java +++ b/src/share/classes/javax/management/MBeanServerConnection.java @@ -75,6 +75,24 @@ public interface MBeanServerConnection { * preRegister (MBeanRegistration * interface) method of the MBean has thrown an exception. The * MBean will not be registered. + * @exception RuntimeMBeanException If the postRegister + * (MBeanRegistration interface) method of the MBean throws a + * RuntimeException, the createMBean method will + * throw a RuntimeMBeanException, although the MBean creation + * and registration succeeded. In such a case, the MBean will be actually + * registered even though the createMBean method + * threw an exception. Note that RuntimeMBeanException can + * also be thrown by preRegister, in which case the MBean + * will not be registered. + * @exception RuntimeErrorException If the postRegister + * (MBeanRegistration interface) method of the MBean throws an + * Error, the createMBean method will + * throw a RuntimeErrorException, although the MBean creation + * and registration succeeded. In such a case, the MBean will be actually + * registered even though the createMBean method + * threw an exception. Note that RuntimeErrorException can + * also be thrown by preRegister, in which case the MBean + * will not be registered. * @exception MBeanException The constructor of the MBean has * thrown an exception * @exception NotCompliantMBeanException This class is not a JMX @@ -86,7 +104,7 @@ public interface MBeanServerConnection { * is specified for the MBean. * @exception IOException A communication problem occurred when * talking to the MBean server. - * + * @see javax.management.MBeanRegistration */ public ObjectInstance createMBean(String className, ObjectName name) throws ReflectionException, InstanceAlreadyExistsException, @@ -129,6 +147,24 @@ public interface MBeanServerConnection { * preRegister (MBeanRegistration * interface) method of the MBean has thrown an exception. The * MBean will not be registered. + * @exception RuntimeMBeanException If the postRegister + * (MBeanRegistration interface) method of the MBean throws a + * RuntimeException, the createMBean method will + * throw a RuntimeMBeanException, although the MBean creation + * and registration succeeded. In such a case, the MBean will be actually + * registered even though the createMBean method + * threw an exception. Note that RuntimeMBeanException can + * also be thrown by preRegister, in which case the MBean + * will not be registered. + * @exception RuntimeErrorException If the postRegister + * (MBeanRegistration interface) method of the MBean throws an + * Error, the createMBean method will + * throw a RuntimeErrorException, although the MBean creation + * and registration succeeded. In such a case, the MBean will be actually + * registered even though the createMBean method + * threw an exception. Note that RuntimeErrorException can + * also be thrown by preRegister, in which case the MBean + * will not be registered. * @exception MBeanException The constructor of the MBean has * thrown an exception * @exception NotCompliantMBeanException This class is not a JMX @@ -142,6 +178,7 @@ public interface MBeanServerConnection { * is specified for the MBean. * @exception IOException A communication problem occurred when * talking to the MBean server. + * @see javax.management.MBeanRegistration */ public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName) @@ -185,6 +222,24 @@ public interface MBeanServerConnection { * preRegister (MBeanRegistration * interface) method of the MBean has thrown an exception. The * MBean will not be registered. + * @exception RuntimeMBeanException If the postRegister + * (MBeanRegistration interface) method of the MBean throws a + * RuntimeException, the createMBean method will + * throw a RuntimeMBeanException, although the MBean creation + * and registration succeeded. In such a case, the MBean will be actually + * registered even though the createMBean method + * threw an exception. Note that RuntimeMBeanException can + * also be thrown by preRegister, in which case the MBean + * will not be registered. + * @exception RuntimeErrorException If the postRegister + * (MBeanRegistration interface) method of the MBean throws an + * Error, the createMBean method will + * throw a RuntimeErrorException, although the MBean creation + * and registration succeeded. In such a case, the MBean will be actually + * registered even though the createMBean method + * threw an exception. Note that RuntimeErrorException can + * also be thrown by preRegister, in which case the MBean + * will not be registered. * @exception MBeanException The constructor of the MBean has * thrown an exception * @exception NotCompliantMBeanException This class is not a JMX @@ -196,7 +251,7 @@ public interface MBeanServerConnection { * is specified for the MBean. * @exception IOException A communication problem occurred when * talking to the MBean server. - * + * @see javax.management.MBeanRegistration */ public ObjectInstance createMBean(String className, ObjectName name, Object params[], String signature[]) @@ -239,6 +294,24 @@ public interface MBeanServerConnection { * preRegister (MBeanRegistration * interface) method of the MBean has thrown an exception. The * MBean will not be registered. + * @exception RuntimeMBeanException If the postRegister + * (MBeanRegistration interface) method of the MBean throws a + * RuntimeException, the createMBean method will + * throw a RuntimeMBeanException, although the MBean creation + * and registration succeeded. In such a case, the MBean will be actually + * registered even though the createMBean method + * threw an exception. Note that RuntimeMBeanException can + * also be thrown by preRegister, in which case the MBean + * will not be registered. + * @exception RuntimeErrorException If the postRegister method + * (MBeanRegistration interface) method of the MBean throws an + * Error, the createMBean method will + * throw a RuntimeErrorException, although the MBean creation + * and registration succeeded. In such a case, the MBean will be actually + * registered even though the createMBean method + * threw an exception. Note that RuntimeErrorException can + * also be thrown by preRegister, in which case the MBean + * will not be registered. * @exception MBeanException The constructor of the MBean has * thrown an exception * @exception NotCompliantMBeanException This class is not a JMX @@ -252,7 +325,7 @@ public interface MBeanServerConnection { * is specified for the MBean. * @exception IOException A communication problem occurred when * talking to the MBean server. - * + * @see javax.management.MBeanRegistration */ public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, Object params[], @@ -275,6 +348,24 @@ public interface MBeanServerConnection { * @exception MBeanRegistrationException The preDeregister * ((MBeanRegistration interface) method of the MBean * has thrown an exception. + * @exception RuntimeMBeanException If the postDeregister + * (MBeanRegistration interface) method of the MBean throws a + * RuntimeException, the unregisterMBean method + * will throw a RuntimeMBeanException, although the MBean + * unregistration succeeded. In such a case, the MBean will be actually + * unregistered even though the unregisterMBean method + * threw an exception. Note that RuntimeMBeanException can + * also be thrown by preDeregister, in which case the MBean + * will remain registered. + * @exception RuntimeErrorException If the postDeregister + * (MBeanRegistration interface) method of the MBean throws an + * Error, the unregisterMBean method will + * throw a RuntimeErrorException, although the MBean + * unregistration succeeded. In such a case, the MBean will be actually + * unregistered even though the unregisterMBean method + * threw an exception. Note that RuntimeMBeanException can + * also be thrown by preDeregister, in which case the MBean + * will remain registered. * @exception RuntimeOperationsException Wraps a * java.lang.IllegalArgumentException: The object * name in parameter is null or the MBean you are when trying to @@ -282,7 +373,7 @@ public interface MBeanServerConnection { * MBeanServerDelegate} MBean. * @exception IOException A communication problem occurred when * talking to the MBean server. - * + * @see javax.management.MBeanRegistration */ public void unregisterMBean(ObjectName name) throws InstanceNotFoundException, MBeanRegistrationException, diff --git a/test/javax/management/MBeanServer/PostExceptionTest.java b/test/javax/management/MBeanServer/PostExceptionTest.java new file mode 100644 index 000000000..d0e0aa3b8 --- /dev/null +++ b/test/javax/management/MBeanServer/PostExceptionTest.java @@ -0,0 +1,516 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6730926 + * @summary Check behaviour of MBeanServer when postRegister and postDeregister + * throw exceptions. + * @author Daniel Fuchs + * @compile PostExceptionTest.java + * @run main PostExceptionTest + */ + +import javax.management.*; +import java.io.Serializable; +import java.net.URL; +import java.util.EnumSet; +import javax.management.loading.MLet; + +public class PostExceptionTest { + + /** + * A test case where we instantiate an ExceptionalWombatMBean (or a + * subclass of it) which will throw the exception {@code t} from within + * the methods indicated by {@code where} + */ + public static class Case { + public final Throwable t; + public final EnumSet where; + public Case(Throwable t,EnumSet where) { + this.t=t; this.where=where; + } + } + + // Various methods to create an instance of Case in a single line + // -------------------------------------------------------------- + + public static Case caze(Throwable t, WHERE w) { + return new Case(t,EnumSet.of(w)); + } + public static Case caze(Throwable t, EnumSet where) { + return new Case(t,where); + } + public static Case caze(Throwable t, WHERE w, WHERE... rest) { + return new Case(t,EnumSet.of(w,rest)); + } + + /** + * Here is the list of our test cases: + */ + public static Case[] cases ={ + caze(new RuntimeException(),WHERE.PREREGISTER), + caze(new RuntimeException(),WHERE.POSTREGISTER), + caze(new RuntimeException(),WHERE.POSTREGISTER, WHERE.PREDEREGISTER), + caze(new RuntimeException(),WHERE.POSTREGISTER, WHERE.POSTDEREGISTER), + caze(new Exception(),WHERE.PREREGISTER), + caze(new Exception(),WHERE.POSTREGISTER), + caze(new Exception(),WHERE.POSTREGISTER, WHERE.PREDEREGISTER), + caze(new Exception(),WHERE.POSTREGISTER, WHERE.POSTDEREGISTER), + caze(new Error(),WHERE.PREREGISTER), + caze(new Error(),WHERE.POSTREGISTER), + caze(new Error(),WHERE.POSTREGISTER, WHERE.PREDEREGISTER), + caze(new Error(),WHERE.POSTREGISTER, WHERE.POSTDEREGISTER), + caze(new RuntimeException(),EnumSet.allOf(WHERE.class)), + caze(new Exception(),EnumSet.allOf(WHERE.class)), + caze(new Error(),EnumSet.allOf(WHERE.class)), + }; + + public static void main(String[] args) throws Exception { + System.out.println("Test behaviour of MBeanServer when postRegister " + + "or postDeregister throw exceptions"); + MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + int failures = 0; + final ObjectName n = new ObjectName("test:type=Wombat"); + + // We're going to test each cases, using each of the 4 createMBean + // forms + registerMBean in turn to create the MBean. + // Wich method is used to create the MBean is indicated by "how" + // + for (Case caze:cases) { + for (CREATE how : CREATE.values()) { + failures+=test(mbs,n,how,caze.t,caze.where); + } + } + if (failures == 0) + System.out.println("Test passed"); + else { + System.out.println("TEST FAILED: " + failures + " failure(s)"); + System.exit(1); + } + } + + // Execute a test case composed of: + // mbs: The MBeanServer where the MBean will be registered, + // name: The name of that MBean + // how: How will the MBean be created/registered (which MBeanServer + // method) + // t: The exception/error that the MBean will throw + // where: In which pre/post register/deregister method the exception/error + // will be thrown + // + private static int test(MBeanServer mbs, ObjectName name, CREATE how, + Throwable t, EnumSet where) + throws Exception { + System.out.println("-------<"+how+"> / <"+t+"> / "+ where + "-------"); + + int failures = 0; + ObjectInstance oi = null; + Exception reg = null; // exception thrown by create/register + Exception unreg = null; // exception thrown by unregister + try { + // Create the MBean + oi = how.create(t, where, mbs, name); + } catch (Exception xx) { + reg=xx; + } + final ObjectName n = (oi==null)?name:oi.getObjectName(); + final boolean isRegistered = mbs.isRegistered(n); + try { + // If the MBean is registered, unregister it + if (isRegistered) mbs.unregisterMBean(n); + } catch (Exception xxx) { + unreg=xxx; + } + final boolean isUnregistered = !mbs.isRegistered(n); + if (!isUnregistered) { + // if the MBean is still registered (preDeregister threw an + // exception) signify to the MBean that it now should stop + // throwing anaything and unregister it. + JMX.newMBeanProxy(mbs, n, ExceptionalWombatMBean.class).end(); + mbs.unregisterMBean(n); + } + + // Now analyze the result. If we didn't ask the MBean to throw any + // exception then reg should be null. + if (where.isEmpty() && reg!=null) { + System.out.println("Unexpected registration exception: "+ + reg); + throw new RuntimeException("Unexpected registration exception: "+ + reg,reg); + } + + // If we didn't ask the MBean to throw any exception then unreg should + // also be null. + if (where.isEmpty() && unreg!=null) { + System.out.println("Unexpected unregistration exception: "+ + unreg); + throw new RuntimeException("Unexpected unregistration exception: "+ + unreg,unreg); + } + + // If we asked the MBean to throw an exception in either of preRegister + // or postRegister, then reg should not be null. + if ((where.contains(WHERE.PREREGISTER) + || where.contains(WHERE.POSTREGISTER))&& reg==null) { + System.out.println("Expected registration exception not " + + "thrown by "+where); + throw new RuntimeException("Expected registration exception not " + + "thrown by "+where); + } + + // If we asked the MBean not to throw any exception in preRegister + // then the MBean should have been registered, unregisterMBean should + // have been called. + // If we asked the MBean to throw an exception in either of preDeregister + // or postDeregister, then unreg should not be null. + if ((where.contains(WHERE.PREDEREGISTER) + || where.contains(WHERE.POSTDEREGISTER))&& unreg==null + && !where.contains(WHERE.PREREGISTER)) { + System.out.println("Expected unregistration exception not " + + "thrown by "+where); + throw new RuntimeException("Expected unregistration exception not " + + "thrown by "+where); + } + + // If we asked the MBean to throw an exception in preRegister + // then the MBean should not have been registered. + if (where.contains(WHERE.PREREGISTER)) { + if (isRegistered) { + System.out.println("MBean is still registered [" + + where+ + "]: "+name+" / "+reg); + throw new RuntimeException("MBean is still registered [" + + where+ + "]: "+name+" / "+reg,reg); + } + } + + // If we asked the MBean not to throw an exception in preRegister, + // but to throw an exception in postRegister, then the MBean should + // have been registered. + if (where.contains(WHERE.POSTREGISTER) && + !where.contains(WHERE.PREREGISTER)) { + if (!isRegistered) { + System.out.println("MBean is already unregistered [" + + where+ + "]: "+name+" / "+reg); + throw new RuntimeException("MBean is already unregistered [" + + where+ + "]: "+name+" / "+reg,reg); + } + } + + // If we asked the MBean to throw an exception in preRegister, + // check that the exception we caught was as expected. + // + if (where.contains(WHERE.PREREGISTER)) { + WHERE.PREREGISTER.check(reg, t); + } else if (where.contains(WHERE.POSTREGISTER)) { + // If we asked the MBean to throw an exception in postRegister, + // check that the exception we caught was as expected. + // We don't do this check if we asked the MBean to also throw an + // exception in pre register, because postRegister will not have + // been called. + WHERE.POSTREGISTER.check(reg, t); + } + + if (!isRegistered) return failures; + + // The MBean was registered, so unregisterMBean was called. Check + // unregisterMBean exceptions... + // + + // If we asked the MBean to throw an exception in preDeregister + // then the MBean should not have been deregistered. + if (where.contains(WHERE.PREDEREGISTER)) { + if (isUnregistered) { + System.out.println("MBean is already unregistered [" + + where+ + "]: "+name+" / "+unreg); + throw new RuntimeException("MBean is already unregistered [" + + where+ + "]: "+name+" / "+unreg,unreg); + } + } + + // If we asked the MBean not to throw an exception in preDeregister, + // but to throw an exception in postDeregister, then the MBean should + // have been deregistered. + if (where.contains(WHERE.POSTDEREGISTER) && + !where.contains(WHERE.PREDEREGISTER)) { + if (!isUnregistered) { + System.out.println("MBean is not unregistered [" + + where+ + "]: "+name+" / "+unreg); + throw new RuntimeException("MBean is not unregistered [" + + where+ + "]: "+name+" / "+unreg,unreg); + } + } + + // If we asked the MBean to throw an exception in preDeregister, + // check that the exception we caught was as expected. + // + if (where.contains(WHERE.PREDEREGISTER)) { + WHERE.PREDEREGISTER.check(unreg, t); + } else if (where.contains(WHERE.POSTDEREGISTER)) { + // If we asked the MBean to throw an exception in postDeregister, + // check that the exception we caught was as expected. + // We don't do this check if we asked the MBean to also throw an + // exception in pre register, because postRegister will not have + // been called. + WHERE.POSTDEREGISTER.check(unreg, t); + } + return failures; + } + + /** + * This enum lists the 4 methods in MBeanRegistration. + */ + public static enum WHERE { + + PREREGISTER, POSTREGISTER, PREDEREGISTER, POSTDEREGISTER; + + // Checks that an exception thrown by the MBeanServer correspond to + // what is expected when an MBean throws an exception in this + // MBeanRegistration method ("this" is one of the 4 enum values above) + // + public void check(Exception thrown, Throwable t) + throws Exception { + if (t instanceof RuntimeException) { + if (!(thrown instanceof RuntimeMBeanException)) { + System.out.println("Expected RuntimeMBeanException, got "+ + thrown); + throw new Exception("Expected RuntimeMBeanException, got "+ + thrown); + } + } else if (t instanceof Error) { + if (!(thrown instanceof RuntimeErrorException)) { + System.out.println("Expected RuntimeErrorException, got "+ + thrown); + throw new Exception("Expected RuntimeErrorException, got "+ + thrown); + } + } else if (t instanceof Exception) { + if (EnumSet.of(POSTDEREGISTER,POSTREGISTER).contains(this)) { + if (!(thrown instanceof RuntimeMBeanException)) { + System.out.println("Expected RuntimeMBeanException, got "+ + thrown); + throw new Exception("Expected RuntimeMBeanException, got "+ + thrown); + } + if (! (thrown.getCause() instanceof RuntimeException)) { + System.out.println("Bad cause: " + + "expected RuntimeException, " + + "got <"+thrown.getCause()+">"); + throw new Exception("Bad cause: " + + "expected RuntimeException, " + + "got <"+thrown.getCause()+">"); + } + } + if (EnumSet.of(PREDEREGISTER,PREREGISTER).contains(this)) { + if (!(thrown instanceof MBeanRegistrationException)) { + System.out.println("Expected " + + "MBeanRegistrationException, got "+ + thrown); + throw new Exception("Expected " + + "MBeanRegistrationException, got "+ + thrown); + } + if (! (thrown.getCause() instanceof Exception)) { + System.out.println("Bad cause: " + + "expected Exception, " + + "got <"+thrown.getCause()+">"); + throw new Exception("Bad cause: " + + "expected Exception, " + + "got <"+thrown.getCause()+">"); + } + } + } + + } + } + + /** + * This enum lists the 5 methods to create and register an + * ExceptionalWombat MBean + */ + public static enum CREATE { + + CREATE1() { + // Creates an ExceptionalWombat MBean using createMBean form #1 + public ObjectInstance create(Throwable t, EnumSet where, + MBeanServer server, ObjectName name) throws Exception { + ExceptionallyHackyWombat.t = t; + ExceptionallyHackyWombat.w = where; + return server.createMBean( + ExceptionallyHackyWombat.class.getName(), + name); + } + }, + CREATE2() { + // Creates an ExceptionalWombat MBean using createMBean form #2 + public ObjectInstance create(Throwable t, EnumSet where, + MBeanServer server, ObjectName name) throws Exception { + ExceptionallyHackyWombat.t = t; + ExceptionallyHackyWombat.w = where; + final ObjectName loaderName = registerMLet(server); + return server.createMBean( + ExceptionallyHackyWombat.class.getName(), + name, loaderName); + } + }, + CREATE3() { + // Creates an ExceptionalWombat MBean using createMBean form #3 + public ObjectInstance create(Throwable t, EnumSet where, + MBeanServer server, ObjectName name) throws Exception { + final Object[] params = {t, where}; + final String[] signature = {Throwable.class.getName(), + EnumSet.class.getName() + }; + return server.createMBean( + ExceptionalWombat.class.getName(), name, + params, signature); + } + }, + CREATE4() { + // Creates an ExceptionalWombat MBean using createMBean form #4 + public ObjectInstance create(Throwable t, EnumSet where, + MBeanServer server, ObjectName name) throws Exception { + final Object[] params = {t, where}; + final String[] signature = {Throwable.class.getName(), + EnumSet.class.getName() + }; + return server.createMBean( + ExceptionalWombat.class.getName(), name, + registerMLet(server), params, signature); + } + }, + REGISTER() { + // Creates an ExceptionalWombat MBean using registerMBean + public ObjectInstance create(Throwable t, EnumSet where, + MBeanServer server, ObjectName name) throws Exception { + final ExceptionalWombat wombat = + new ExceptionalWombat(t, where); + return server.registerMBean(wombat, name); + } + }; + + // Creates an ExceptionalWombat MBean using the method denoted by this + // Enum value - one of CREATE1, CREATE2, CREATE3, CREATE4, or REGISTER. + public abstract ObjectInstance create(Throwable t, EnumSet where, + MBeanServer server, ObjectName name) throws Exception; + + // This is a bit of a hack - we use an MLet that delegates to the + // System ClassLoader so that we can use createMBean form #2 and #3 + // while still using the same class loader (system). + // This is necessary to make the ExceptionallyHackyWombatMBean work ;-) + // + public ObjectName registerMLet(MBeanServer server) throws Exception { + final ObjectName name = new ObjectName("test:type=MLet"); + if (server.isRegistered(name)) { + return name; + } + final MLet mlet = new MLet(new URL[0], + ClassLoader.getSystemClassLoader()); + return server.registerMBean(mlet, name).getObjectName(); + } + } + + /** + *A Wombat MBean that can throw exceptions or errors in any of the + * MBeanRegistration methods. + */ + public static interface ExceptionalWombatMBean { + // Tells the MBean to stop throwing exceptions - we sometime + // need to call this at the end of the test so that we can + // actually unregister the MBean. + public void end(); + } + + /** + *A Wombat MBean that can throw exceptions or errors in any of the + * MBeanRegistration methods. + */ + public static class ExceptionalWombat + implements ExceptionalWombatMBean, MBeanRegistration { + + private final Throwable throwable; + private final EnumSet where; + private volatile boolean end=false; + + public ExceptionalWombat(Throwable t, EnumSet where) { + this.throwable=t; this.where=where; + } + private Exception doThrow() { + if (throwable instanceof Error) + throw (Error)throwable; + if (throwable instanceof RuntimeException) + throw (RuntimeException)throwable; + return (Exception)throwable; + } + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + if (!end && where.contains(WHERE.PREREGISTER)) + throw doThrow(); + return name; + } + + public void postRegister(Boolean registrationDone) { + if (!end && where.contains(WHERE.POSTREGISTER)) + throw new RuntimeException(doThrow()); + } + + public void preDeregister() throws Exception { + if (!end && where.contains(WHERE.PREDEREGISTER)) + throw doThrow(); + } + + public void postDeregister() { + if (!end && where.contains(WHERE.POSTREGISTER)) + throw new RuntimeException(doThrow()); + } + + public void end() { + this.end=true; + } + } + + /** + * This is a big ugly hack to call createMBean form #1 and #2 - where + * the empty constructor is used. Since we still want to supply parameters + * to the ExceptionalWombat super class, we temporarily store these + * parameter value in a static volatile before calling create MBean. + * Of course this only works because our test is sequential and single + * threaded, and nobody but our test uses this ExceptionallyHackyWombat. + */ + public static class ExceptionallyHackyWombat extends ExceptionalWombat { + public static volatile Throwable t; + public static volatile EnumSet w; + public ExceptionallyHackyWombat() { + super(t,w); + } + } + +} -- GitLab From 152f3ab7df60da07975a46f887c1e775ce6de772 Mon Sep 17 00:00:00 2001 From: dfuchs Date: Thu, 31 Jul 2008 14:20:11 +0200 Subject: [PATCH 026/139] 6689505: Improve MBeanServerNotification.toString Reviewed-by: emcmanus --- .../management/MBeanServerNotification.java | 98 +++++++------ .../MBeanServerNotificationTest.java | 132 ++++++++++++++++++ 2 files changed, 185 insertions(+), 45 deletions(-) create mode 100644 test/javax/management/MBeanServer/MBeanServerNotificationTest.java diff --git a/src/share/classes/javax/management/MBeanServerNotification.java b/src/share/classes/javax/management/MBeanServerNotification.java index 25f125df0..91362f8e0 100644 --- a/src/share/classes/javax/management/MBeanServerNotification.java +++ b/src/share/classes/javax/management/MBeanServerNotification.java @@ -38,56 +38,64 @@ package javax.management; * * @since 1.5 */ - public class MBeanServerNotification extends Notification { +public class MBeanServerNotification extends Notification { - /* Serial version */ - private static final long serialVersionUID = 2876477500475969677L; + /* Serial version */ + private static final long serialVersionUID = 2876477500475969677L; + /** + * Notification type denoting that an MBean has been registered. + * Value is "JMX.mbean.registered". + */ + public static final String REGISTRATION_NOTIFICATION = + "JMX.mbean.registered"; + /** + * Notification type denoting that an MBean has been unregistered. + * Value is "JMX.mbean.unregistered". + */ + public static final String UNREGISTRATION_NOTIFICATION = + "JMX.mbean.unregistered"; + /** + * @serial The object names of the MBeans concerned by this notification + */ + private final ObjectName objectName; - /** - * Notification type denoting that an MBean has been registered. Value is "JMX.mbean.registered". - */ - public static final String REGISTRATION_NOTIFICATION = "JMX.mbean.registered" ; + /** + * Creates an MBeanServerNotification object specifying object names of + * the MBeans that caused the notification and the specified notification + * type. + * + * @param type A string denoting the type of the + * notification. Set it to one these values: {@link + * #REGISTRATION_NOTIFICATION}, {@link + * #UNREGISTRATION_NOTIFICATION}. + * @param source The MBeanServerNotification object responsible + * for forwarding MBean server notification. + * @param sequenceNumber A sequence number that can be used to order + * received notifications. + * @param objectName The object name of the MBean that caused the + * notification. + * + */ + public MBeanServerNotification(String type, Object source, + long sequenceNumber, ObjectName objectName) { + super(type, source, sequenceNumber); + this.objectName = objectName; + } - /** - * Notification type denoting that an MBean has been unregistered. Value is "JMX.mbean.unregistered". - */ - public static final String UNREGISTRATION_NOTIFICATION = "JMX.mbean.unregistered" ; + /** + * Returns the object name of the MBean that caused the notification. + * + * @return the object name of the MBean that caused the notification. + */ + public ObjectName getMBeanName() { + return objectName; + } + @Override + public String toString() { + return super.toString() + "[mbeanName=" + objectName + "]"; - /** - * @serial The object names of the MBeans concerned by this notification - */ - private final ObjectName objectName; - - - /** - * Creates an MBeanServerNotification object specifying object names of - * the MBeans that caused the notification and the specified notification type. - * - * @param type A string denoting the type of the - * notification. Set it to one these values: {@link - * #REGISTRATION_NOTIFICATION}, {@link - * #UNREGISTRATION_NOTIFICATION}. - * @param source The MBeanServerNotification object responsible - * for forwarding MBean server notification. - * @param sequenceNumber A sequence number that can be used to order - * received notifications. - * @param objectName The object name of the MBean that caused the notification. - * - */ - public MBeanServerNotification(String type, Object source, long sequenceNumber, ObjectName objectName ) { - super (type,source,sequenceNumber) ; - this.objectName = objectName ; - } - - /** - * Returns the object name of the MBean that caused the notification. - * - * @return the object name of the MBean that caused the notification. - */ - public ObjectName getMBeanName() { - return objectName ; - } + } } diff --git a/test/javax/management/MBeanServer/MBeanServerNotificationTest.java b/test/javax/management/MBeanServer/MBeanServerNotificationTest.java new file mode 100644 index 000000000..54fd6ba2d --- /dev/null +++ b/test/javax/management/MBeanServer/MBeanServerNotificationTest.java @@ -0,0 +1,132 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6689505 + * @summary Checks that MBeanServerNotification.toString contains the + * MBean name. + * @author Daniel Fuchs + * @compile MBeanServerNotificationTest.java + * @run main MBeanServerNotificationTest + */ + +import com.sun.jmx.mbeanserver.Util; +import javax.management.*; +import java.util.concurrent.*; + +public class MBeanServerNotificationTest { + final static String[] names = { + ":type=Wombat", "wombat:type=Wombat",null, + }; + public static void main(String[] args) throws Exception { + System.out.println("Test that MBeanServerNotification.toString " + + "contains the name of the MBean being registered " + + "or unregistered."); + int failures = 0; + final MBeanServer mbs = MBeanServerFactory.createMBeanServer(); + for (String str:names) { + try { + final ObjectName name = (str==null)?null:new ObjectName(str); + failures+=test(mbs, name, name!=null); + } catch(Exception x) { + x.printStackTrace(System.out); + System.out.println("Test failed for: "+str); + failures++; + } + } + if (failures == 0) + System.out.println("Test passed"); + else { + System.out.println("TEST FAILED: " + failures + " failure(s)"); + System.exit(1); + } + } + + private static enum Registration { + REGISTER(MBeanServerNotification.REGISTRATION_NOTIFICATION), + UNREGISTER(MBeanServerNotification.UNREGISTRATION_NOTIFICATION); + final String type; + private Registration(String type) {this.type = type;} + public int test(MBeanServerNotification n, ObjectName name) { + int failures = 0; + System.out.println("Testing: "+n); + if (!n.toString().endsWith("[type="+type+ + "][message="+n.getMessage()+ + "][mbeanName="+name+"]")) { + System.err.println("Test failed for "+ type+ + " ["+name+"]: "+n); + failures++; + } + return failures; + } + public MBeanServerNotification create(ObjectName name) { + return new MBeanServerNotification(type, + MBeanServerDelegate.DELEGATE_NAME, next(), name); + } + private static long next = 0; + private static synchronized long next() {return next++;} + + } + + private static int test(MBeanServer mbs, ObjectName name, + boolean register) + throws Exception { + System.out.println("--------" + name + "--------"); + + int failures = 0; + for (Registration reg : Registration.values()) { + failures = reg.test(reg.create(name), name); + } + if (!register) return failures; + + final ArrayBlockingQueue queue = + new ArrayBlockingQueue(10); + final NotificationListener listener = new NotificationListener() { + public void handleNotification(Notification notification, + Object handback) { + try { + queue.put(notification); + } catch(Exception x) { + x.printStackTrace(System.out); + } + } + }; + mbs.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, + listener, null, name); + final ObjectInstance oi = mbs.registerMBean(new Wombat(), name); + try { + failures+=Registration.REGISTER.test((MBeanServerNotification) + queue.poll(2, TimeUnit.SECONDS), oi.getObjectName()); + } finally { + mbs.unregisterMBean(oi.getObjectName()); + failures+=Registration.UNREGISTER.test((MBeanServerNotification) + queue.poll(2, TimeUnit.SECONDS), oi.getObjectName()); + } + return failures; + } + + public static interface WombatMBean {} + public static class Wombat implements WombatMBean {} + +} -- GitLab From c6b1acc764aec8474a286f5903c4d95e3e1a6120 Mon Sep 17 00:00:00 2001 From: sjiang Date: Thu, 31 Jul 2008 15:31:13 +0200 Subject: [PATCH 027/139] 5108776: Add reliable event handling to the JMX API 6218920: API bug - impossible to delete last MBeanServerForwarder on a connector Reviewed-by: emcmanus --- .../sun/jmx/event/DaemonThreadFactory.java | 77 + .../com/sun/jmx/event/EventBuffer.java | 252 +++ .../com/sun/jmx/event/EventClientFactory.java | 46 + .../com/sun/jmx/event/EventConnection.java | 81 + .../com/sun/jmx/event/EventParams.java | 65 + .../com/sun/jmx/event/LeaseManager.java | 146 ++ .../com/sun/jmx/event/LeaseRenewer.java | 143 ++ .../com/sun/jmx/event/ReceiverBuffer.java | 97 ++ .../sun/jmx/event/RepeatedSingletonJob.java | 117 ++ .../jmx/interceptor/MBeanServerSupport.java | 1341 ++++++++++++++++ .../jmx/interceptor/SingleMBeanForwarder.java | 414 +++++ .../com/sun/jmx/interceptor/package.html | 3 + .../sun/jmx/mbeanserver/MBeanInjector.java | 2 +- .../com/sun/jmx/mbeanserver/MBeanSupport.java | 1 - .../jmx/mbeanserver/PerThreadGroupPool.java | 71 + .../classes/com/sun/jmx/mbeanserver/Util.java | 96 ++ .../remote/internal/ClientNotifForwarder.java | 14 + .../jmx/remote/internal/ProxyInputStream.java | 21 +- .../com/sun/jmx/remote/internal/ProxyRef.java | 21 +- .../remote/internal/ServerNotifForwarder.java | 40 +- .../jmx/remote/security/FileLoginModule.java | 16 +- .../com/sun/jmx/remote/util/EnvHelp.java | 175 +- .../remote/util/EventClientConnection.java | 471 ++++++ .../com/sun/jmx/snmp/tasks/ThreadService.java | 10 - .../javax/management/ImmutableDescriptor.java | 4 +- .../classes/javax/management/MBeanServer.java | 8 +- .../management/MBeanServerConnection.java | 86 +- .../classes/javax/management/MXBean.java | 4 +- .../classes/javax/management/QueryParser.java | 2 +- .../javax/management/StringValueExp.java | 1 + .../javax/management/event/EventClient.java | 1068 +++++++++++++ .../management/event/EventClientDelegate.java | 766 +++++++++ .../event/EventClientDelegateMBean.java | 318 ++++ .../event/EventClientNotFoundException.java | 79 + .../javax/management/event/EventConsumer.java | 98 ++ .../management/event/EventForwarder.java | 63 + .../javax/management/event/EventReceiver.java | 77 + .../javax/management/event/EventRelay.java | 80 + .../management/event/EventSubscriber.java | 381 +++++ .../event/FetchingEventForwarder.java | 151 ++ .../management/event/FetchingEventRelay.java | 389 +++++ .../javax/management/event/ListenerInfo.java | 169 ++ .../management/event/NotificationManager.java | 136 ++ .../event/RMIPushEventForwarder.java | 198 +++ .../management/event/RMIPushEventRelay.java | 161 ++ .../javax/management/event/RMIPushServer.java | 48 + .../javax/management/event/package-info.java | 312 ++++ .../javax/management/loading/MLet.java | 36 +- .../modelmbean/ModelMBeanInfoSupport.java | 4 +- .../modelmbean/RequiredModelMBean.java | 2 +- .../management/relation/RelationService.java | 4 +- .../remote/IdentityMBeanServerForwarder.java | 303 ++++ .../javax/management/remote/JMXConnector.java | 20 + .../management/remote/JMXConnectorServer.java | 355 ++++- .../remote/JMXConnectorServerFactory.java | 5 +- .../remote/JMXConnectorServerMBean.java | 19 +- .../remote/rmi/RMIConnectionImpl.java | 368 ++++- .../management/remote/rmi/RMIConnector.java | 115 +- .../remote/rmi/RMIConnectorServer.java | 44 +- .../MBeanServer/DynamicWrapperMBeanTest.java | 164 ++ .../MBeanServer/OldMBeanServerTest.java | 1410 +++++++++++++++++ .../eventService/AddRemoveListenerTest.java | 371 +++++ .../eventService/CustomForwarderTest.java | 348 ++++ .../eventService/EventClientExecutorTest.java | 191 +++ .../EventDelegateSecurityTest.java | 289 ++++ .../eventService/EventManagerTest.java | 221 +++ .../management/eventService/FetchingTest.java | 276 ++++ .../LeaseManagerDeadlockTest.java | 96 ++ .../management/eventService/LeaseTest.java | 361 +++++ .../management/eventService/ListenerTest.java | 224 +++ .../MyFetchingEventForwarder.java | 53 + .../NotSerializableNotifTest.java | 227 +++ .../management/eventService/PublishTest.java | 184 +++ .../ReconnectableConnectorTest.java | 488 ++++++ .../eventService/SharingThreadTest.java | 364 +++++ .../eventService/SubscribeTest.java | 127 ++ .../eventService/UsingEventService.java | 84 + .../mxbean/GenericArrayTypeTest.java | 56 +- test/javax/management/mxbean/LeakTest.java | 2 +- .../mxbean/MBeanOperationInfoTest.java | 3 +- test/javax/management/mxbean/MXBeanTest.java | 2 +- .../management/mxbean/ThreadMXBeanTest.java | 3 +- test/javax/management/mxbean/TigerMXBean.java | 16 +- .../query/QueryNotifFilterTest.java | 2 +- .../mandatory/connection/CloseServerTest.java | 73 +- .../mandatory/connection/DeadLockTest.java | 4 + .../mandatory/connection/IdleTimeoutTest.java | 10 +- .../mandatory/connection/RMIExitTest.java | 16 +- .../mandatory/connection/ReconnectTest.java | 3 +- .../connectorServer/ForwarderChainTest.java | 274 ++++ .../StandardForwardersTest.java | 177 +++ .../mandatory/loading/MissingClassTest.java | 62 +- .../remote/mandatory/notif/AddRemoveTest.java | 20 +- .../remote/mandatory/notif/DiffHBTest.java | 252 +-- .../notif/EmptyDomainNotificationTest.java | 24 +- .../mandatory/notif/ListenerScaleTest.java | 41 +- .../NotifBufferSizePropertyNameTest.java | 5 +- .../notif/NotifReconnectDeadlockTest.java | 23 +- .../NotificationAccessControllerTest.java | 3 +- .../notif/NotificationBufferCreationTest.java | 5 + .../notif/NotificationBufferDeadlockTest.java | 16 +- .../notif/NotificationEmissionTest.java | 75 +- .../remote/mandatory/notif/RMINotifTest.java | 52 +- .../mandatory/notif/UnexpectedNotifTest.java | 124 +- 104 files changed, 15853 insertions(+), 562 deletions(-) create mode 100644 src/share/classes/com/sun/jmx/event/DaemonThreadFactory.java create mode 100644 src/share/classes/com/sun/jmx/event/EventBuffer.java create mode 100644 src/share/classes/com/sun/jmx/event/EventClientFactory.java create mode 100644 src/share/classes/com/sun/jmx/event/EventConnection.java create mode 100644 src/share/classes/com/sun/jmx/event/EventParams.java create mode 100644 src/share/classes/com/sun/jmx/event/LeaseManager.java create mode 100644 src/share/classes/com/sun/jmx/event/LeaseRenewer.java create mode 100644 src/share/classes/com/sun/jmx/event/ReceiverBuffer.java create mode 100644 src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java create mode 100644 src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java create mode 100644 src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java create mode 100644 src/share/classes/com/sun/jmx/mbeanserver/PerThreadGroupPool.java create mode 100644 src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java create mode 100644 src/share/classes/javax/management/event/EventClient.java create mode 100644 src/share/classes/javax/management/event/EventClientDelegate.java create mode 100644 src/share/classes/javax/management/event/EventClientDelegateMBean.java create mode 100644 src/share/classes/javax/management/event/EventClientNotFoundException.java create mode 100644 src/share/classes/javax/management/event/EventConsumer.java create mode 100644 src/share/classes/javax/management/event/EventForwarder.java create mode 100644 src/share/classes/javax/management/event/EventReceiver.java create mode 100644 src/share/classes/javax/management/event/EventRelay.java create mode 100644 src/share/classes/javax/management/event/EventSubscriber.java create mode 100644 src/share/classes/javax/management/event/FetchingEventForwarder.java create mode 100644 src/share/classes/javax/management/event/FetchingEventRelay.java create mode 100644 src/share/classes/javax/management/event/ListenerInfo.java create mode 100644 src/share/classes/javax/management/event/NotificationManager.java create mode 100644 src/share/classes/javax/management/event/RMIPushEventForwarder.java create mode 100644 src/share/classes/javax/management/event/RMIPushEventRelay.java create mode 100644 src/share/classes/javax/management/event/RMIPushServer.java create mode 100644 src/share/classes/javax/management/event/package-info.java create mode 100644 src/share/classes/javax/management/remote/IdentityMBeanServerForwarder.java create mode 100644 test/javax/management/MBeanServer/DynamicWrapperMBeanTest.java create mode 100644 test/javax/management/MBeanServer/OldMBeanServerTest.java create mode 100644 test/javax/management/eventService/AddRemoveListenerTest.java create mode 100644 test/javax/management/eventService/CustomForwarderTest.java create mode 100644 test/javax/management/eventService/EventClientExecutorTest.java create mode 100644 test/javax/management/eventService/EventDelegateSecurityTest.java create mode 100644 test/javax/management/eventService/EventManagerTest.java create mode 100644 test/javax/management/eventService/FetchingTest.java create mode 100644 test/javax/management/eventService/LeaseManagerDeadlockTest.java create mode 100644 test/javax/management/eventService/LeaseTest.java create mode 100644 test/javax/management/eventService/ListenerTest.java create mode 100644 test/javax/management/eventService/MyFetchingEventForwarder.java create mode 100644 test/javax/management/eventService/NotSerializableNotifTest.java create mode 100644 test/javax/management/eventService/PublishTest.java create mode 100644 test/javax/management/eventService/ReconnectableConnectorTest.java create mode 100644 test/javax/management/eventService/SharingThreadTest.java create mode 100644 test/javax/management/eventService/SubscribeTest.java create mode 100644 test/javax/management/eventService/UsingEventService.java create mode 100644 test/javax/management/remote/mandatory/connectorServer/ForwarderChainTest.java create mode 100644 test/javax/management/remote/mandatory/connectorServer/StandardForwardersTest.java diff --git a/src/share/classes/com/sun/jmx/event/DaemonThreadFactory.java b/src/share/classes/com/sun/jmx/event/DaemonThreadFactory.java new file mode 100644 index 000000000..85377d4d3 --- /dev/null +++ b/src/share/classes/com/sun/jmx/event/DaemonThreadFactory.java @@ -0,0 +1,77 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.event; + +import com.sun.jmx.remote.util.ClassLogger; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +public class DaemonThreadFactory implements ThreadFactory { + public DaemonThreadFactory(String nameTemplate) { + this(nameTemplate, null); + } + + // nameTemplate should be a format with %d in it, which will be replaced + // by a sequence number of threads created by this factory. + public DaemonThreadFactory(String nameTemplate, ThreadGroup threadGroup) { + if (logger.debugOn()) { + logger.debug("DaemonThreadFactory", + "Construct a new daemon factory: "+nameTemplate); + } + + if (threadGroup == null) { + SecurityManager s = System.getSecurityManager(); + threadGroup = (s != null) ? s.getThreadGroup() : + Thread.currentThread().getThreadGroup(); + } + + this.nameTemplate = nameTemplate; + this.threadGroup = threadGroup; + } + + public Thread newThread(Runnable r) { + final String name = + String.format(nameTemplate, threadNumber.getAndIncrement()); + Thread t = new Thread(threadGroup, r, name, 0); + t.setDaemon(true); + if (t.getPriority() != Thread.NORM_PRIORITY) + t.setPriority(Thread.NORM_PRIORITY); + + if (logger.debugOn()) { + logger.debug("newThread", + "Create a new daemon thread with the name "+t.getName()); + } + + return t; + } + + private final String nameTemplate; + private final ThreadGroup threadGroup; + private final AtomicInteger threadNumber = new AtomicInteger(1); + + private static final ClassLogger logger = + new ClassLogger("com.sun.jmx.event", "DaemonThreadFactory"); +} diff --git a/src/share/classes/com/sun/jmx/event/EventBuffer.java b/src/share/classes/com/sun/jmx/event/EventBuffer.java new file mode 100644 index 000000000..bfdabdbb4 --- /dev/null +++ b/src/share/classes/com/sun/jmx/event/EventBuffer.java @@ -0,0 +1,252 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.event; + +import com.sun.jmx.remote.util.ClassLogger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.management.remote.NotificationResult; +import javax.management.remote.TargetedNotification; + +public class EventBuffer { + + public EventBuffer() { + this(Integer.MAX_VALUE, null); + } + + public EventBuffer(int capacity) { + this(capacity, new ArrayList()); + } + + public EventBuffer(int capacity, final List list) { + if (logger.traceOn()) { + logger.trace("EventBuffer", "New buffer with the capacity: " + +capacity); + } + if (capacity < 1) { + throw new IllegalArgumentException( + "The capacity must be bigger than 0"); + } + + if (list == null) { + throw new NullPointerException("Null list."); + } + + this.capacity = capacity; + this.list = list; + } + + public void add(TargetedNotification tn) { + if (logger.traceOn()) { + logger.trace("add", "Add one notif."); + } + + synchronized(lock) { + if (list.size() == capacity) { // have to throw one + passed++; + list.remove(0); + + if (logger.traceOn()) { + logger.trace("add", "Over, remove the oldest one."); + } + } + + list.add(tn); + lock.notify(); + } + } + + public void add(TargetedNotification[] tns) { + if (tns == null || tns.length == 0) { + return; + } + + if (logger.traceOn()) { + logger.trace("add", "Add notifs: "+tns.length); + } + + synchronized(lock) { + final int d = list.size() - capacity + tns.length; + if (d > 0) { // have to throw + passed += d; + if (logger.traceOn()) { + logger.trace("add", + "Over, remove the oldest: "+d); + } + if (tns.length <= capacity){ + list.subList(0, d).clear(); + } else { + list.clear(); + TargetedNotification[] tmp = + new TargetedNotification[capacity]; + System.arraycopy(tns, tns.length-capacity, tmp, 0, capacity); + tns = tmp; + } + } + + Collections.addAll(list,tns); + lock.notify(); + } + } + + public NotificationResult fetchNotifications(long startSequenceNumber, + long timeout, + int maxNotifications) { + if (logger.traceOn()) { + logger.trace("fetchNotifications", + "Being called: " + +startSequenceNumber+" " + +timeout+" "+maxNotifications); + } + if (startSequenceNumber < 0 || + timeout < 0 || + maxNotifications < 0) { + throw new IllegalArgumentException("Negative value."); + } + + TargetedNotification[] tns = new TargetedNotification[0]; + long earliest = startSequenceNumber < passed ? + passed : startSequenceNumber; + long next = earliest; + + final long startTime = System.currentTimeMillis(); + long toWait = timeout; + synchronized(lock) { + int toSkip = (int)(startSequenceNumber - passed); + + // skip those before startSequenceNumber. + while (!closed && toSkip > 0) { + toWait = timeout - (System.currentTimeMillis() - startTime); + if (list.size() == 0) { + if (toWait <= 0) { + // the notification of startSequenceNumber + // does not arrive yet. + return new NotificationResult(startSequenceNumber, + startSequenceNumber, + new TargetedNotification[0]); + } + + waiting(toWait); + continue; + } + + if (toSkip <= list.size()) { + list.subList(0, toSkip).clear(); + passed += toSkip; + + break; + } else { + passed += list.size(); + toSkip -= list.size(); + + list.clear(); + } + } + + earliest = passed; + + if (list.size() == 0) { + toWait = timeout - (System.currentTimeMillis() - startTime); + + waiting(toWait); + } + + if (list.size() == 0) { + tns = new TargetedNotification[0]; + } else if (list.size() <= maxNotifications) { + tns = list.toArray(new TargetedNotification[0]); + } else { + tns = new TargetedNotification[maxNotifications]; + for (int i=0; i 0) { + try { + lock.wait(toWait); + + toWait = timeout - (System.currentTimeMillis() - startTime); + } catch (InterruptedException ire) { + logger.trace("waiting", ire); + break; + } + } + } + } + + private final int capacity; + private final List list; + private boolean closed; + + private long passed = 0; + private final int[] lock = new int[0]; + + private static final ClassLogger logger = + new ClassLogger("javax.management.event", "EventBuffer"); +} diff --git a/src/share/classes/com/sun/jmx/event/EventClientFactory.java b/src/share/classes/com/sun/jmx/event/EventClientFactory.java new file mode 100644 index 000000000..f08b0990d --- /dev/null +++ b/src/share/classes/com/sun/jmx/event/EventClientFactory.java @@ -0,0 +1,46 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.event; + +import javax.management.event.*; + +/** + * Implemented by objects which are using an {@link EventClient} to + * subscribe for Notifications. + * + */ +public interface EventClientFactory { + /** + * Returns the {@code EventClient} that the object implementing this + * interface uses to subscribe for Notifications. This method returns + * {@code null} if no {@code EventClient} can be used - e.g. because + * the underlying server does not have any {@link EventDelegate}. + * + * @return an {@code EventClient} or {@code null}. + **/ + public EventClient getEventClient(); + +} diff --git a/src/share/classes/com/sun/jmx/event/EventConnection.java b/src/share/classes/com/sun/jmx/event/EventConnection.java new file mode 100644 index 000000000..d52d9d3de --- /dev/null +++ b/src/share/classes/com/sun/jmx/event/EventConnection.java @@ -0,0 +1,81 @@ +/* + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.event; + +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import javax.management.MBeanServerConnection; +import javax.management.event.EventClient; +import javax.management.event.EventClientDelegate; +import javax.management.event.EventConsumer; +import javax.management.event.NotificationManager; + +/** + * Override the methods related to the notification to use the + * Event service. + */ +public interface EventConnection extends MBeanServerConnection, EventConsumer { + public EventClient getEventClient(); + + public static class Factory { + public static EventConnection make( + final MBeanServerConnection mbsc, + final EventClient eventClient) + throws IOException { + if (!mbsc.isRegistered(EventClientDelegate.OBJECT_NAME)) { + throw new IOException( + "The server does not support the event service."); + } + InvocationHandler ih = new InvocationHandler() { + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + Class intf = method.getDeclaringClass(); + try { + if (intf.isInstance(eventClient)) + return method.invoke(eventClient, args); + else + return method.invoke(mbsc, args); + } catch (InvocationTargetException e) { + throw e.getCause(); + } + } + }; + // It is important to declare NotificationManager.class first + // in the array below, so that the relevant addNL and removeNL + // methods will show up with method.getDeclaringClass() as + // being from that interface and not MBeanServerConnection. + return (EventConnection) Proxy.newProxyInstance( + NotificationManager.class.getClassLoader(), + new Class[] { + NotificationManager.class, EventConnection.class, + }, + ih); + } + } +} diff --git a/src/share/classes/com/sun/jmx/event/EventParams.java b/src/share/classes/com/sun/jmx/event/EventParams.java new file mode 100644 index 000000000..f941fbe15 --- /dev/null +++ b/src/share/classes/com/sun/jmx/event/EventParams.java @@ -0,0 +1,65 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.event; + +import com.sun.jmx.mbeanserver.GetPropertyAction; +import com.sun.jmx.remote.util.ClassLogger; +import java.security.AccessController; +import javax.management.event.EventClient; + +/** + * + * @author sjiang + */ +public class EventParams { + public static final String DEFAULT_LEASE_TIMEOUT = + "com.sun.event.lease.time"; + + + @SuppressWarnings("cast") // cast for jdk 1.5 + public static long getLeaseTimeout() { + long timeout = EventClient.DEFAULT_LEASE_TIMEOUT; + try { + final GetPropertyAction act = + new GetPropertyAction(DEFAULT_LEASE_TIMEOUT); + final String s = (String)AccessController.doPrivileged(act); + if (s != null) { + timeout = Long.parseLong(s); + } + } catch (RuntimeException e) { + logger.fine("getLeaseTimeout", "exception getting property", e); + } + + return timeout; + } + + /** Creates a new instance of EventParams */ + private EventParams() { + } + + private static final ClassLogger logger = + new ClassLogger("javax.management.event", "EventParams"); +} diff --git a/src/share/classes/com/sun/jmx/event/LeaseManager.java b/src/share/classes/com/sun/jmx/event/LeaseManager.java new file mode 100644 index 000000000..cb1b88bf5 --- /dev/null +++ b/src/share/classes/com/sun/jmx/event/LeaseManager.java @@ -0,0 +1,146 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.event; + +import com.sun.jmx.remote.util.ClassLogger; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +/** + *

Manage a renewable lease. The lease can be renewed indefinitely + * but if the lease runs to its current expiry date without being renewed + * then the expiry callback is invoked. If the lease has already expired + * when renewal is attempted then the lease method returns zero.

+ * @author sjiang + * @author emcmanus + */ +// The synchronization logic of this class is tricky to deal correctly with the +// case where the lease expires at the same time as the |lease| or |stop| method +// is called. If the lease is active then the field |scheduled| represents +// the expiry task; otherwise |scheduled| is null. Renewing or stopping the +// lease involves canceling this task and setting |scheduled| either to a new +// task (to renew) or to null (to stop). +// +// Suppose the expiry task runs at the same time as the |lease| method is called. +// If the task enters its synchronized block before the method starts, then +// it will set |scheduled| to null and the method will return 0. If the method +// starts before the task enters its synchronized block, then the method will +// cancel the task which will see that when it later enters the block. +// Similar reasoning applies to the |stop| method. It is not expected that +// different threads will call |lease| or |stop| simultaneously, although the +// logic should be correct then too. +public class LeaseManager { + public LeaseManager(Runnable callback) { + this(callback, EventParams.getLeaseTimeout()); + } + + public LeaseManager(Runnable callback, long timeout) { + if (logger.traceOn()) { + logger.trace("LeaseManager", "new manager with lease: "+timeout); + } + if (callback == null) { + throw new NullPointerException("Null callback."); + } + if (timeout <= 0) + throw new IllegalArgumentException("Timeout must be positive: " + timeout); + + this.callback = callback; + schedule(timeout); + } + + /** + *

Renew the lease for the given time. The new time can be shorter + * than the previous one, in which case the lease will expire earlier + * than it would have.

+ * + *

Calling this method after the lease has expired will return zero + * immediately and have no other effect.

+ * + * @param timeout the new lifetime. If zero, the lease + * will expire immediately. + */ + public synchronized long lease(long timeout) { + if (logger.traceOn()) { + logger.trace("lease", "new lease to: "+timeout); + } + + if (timeout < 0) + throw new IllegalArgumentException("Negative lease: " + timeout); + + if (scheduled == null) + return 0L; + + scheduled.cancel(false); + + if (logger.traceOn()) + logger.trace("lease", "start lease: "+timeout); + schedule(timeout); + + return timeout; + } + + private class Expire implements Runnable { + ScheduledFuture task; + + public void run() { + synchronized (LeaseManager.this) { + if (task.isCancelled()) + return; + scheduled = null; + } + callback.run(); + } + } + + private synchronized void schedule(long timeout) { + Expire expire = new Expire(); + scheduled = executor.schedule(expire, timeout, TimeUnit.MILLISECONDS); + expire.task = scheduled; + } + + /** + *

Cancel the lease without calling the expiry callback.

+ */ + public synchronized void stop() { + logger.trace("stop", "canceling lease"); + scheduled.cancel(false); + scheduled = null; + } + + private final Runnable callback; + private ScheduledFuture scheduled; // If null, the lease has expired. + + private final ScheduledExecutorService executor + = Executors.newScheduledThreadPool(1, + new DaemonThreadFactory("LeaseManager")); + + private static final ClassLogger logger = + new ClassLogger("javax.management.event", "LeaseManager"); + +} diff --git a/src/share/classes/com/sun/jmx/event/LeaseRenewer.java b/src/share/classes/com/sun/jmx/event/LeaseRenewer.java new file mode 100644 index 000000000..6f2986e5c --- /dev/null +++ b/src/share/classes/com/sun/jmx/event/LeaseRenewer.java @@ -0,0 +1,143 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.event; + +import com.sun.jmx.remote.util.ClassLogger; +import java.util.concurrent.Callable; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +/** + * + * @author sjiang + */ +public class LeaseRenewer { + public LeaseRenewer(ScheduledExecutorService scheduler, Callable doRenew) { + if (logger.traceOn()) { + logger.trace("LeaseRenewer", "New LeaseRenewer."); + } + + if (doRenew == null) { + throw new NullPointerException("Null job to call server."); + } + + this.doRenew = doRenew; + nextRenewTime = System.currentTimeMillis(); + + this.scheduler = scheduler; + future = this.scheduler.schedule(myRenew, 0, TimeUnit.MILLISECONDS); + } + + public void close() { + if (logger.traceOn()) { + logger.trace("close", "Close the lease."); + } + + synchronized(lock) { + if (closed) { + return; + } else { + closed = true; + } + } + + try { + future.cancel(false); // not interrupt if running + } catch (Exception e) { + // OK + if (logger.debugOn()) { + logger.debug("close", "Failed to cancel the leasing job.", e); + } + } + } + + public boolean closed() { + synchronized(lock) { + return closed; + } + } + + // ------------------------------ + // private + // ------------------------------ + private final Runnable myRenew = new Runnable() { + public void run() { + synchronized(lock) { + if (closed()) { + return; + } + } + + long next = nextRenewTime - System.currentTimeMillis(); + if (next < MIN_MILLIS) { + try { + if (logger.traceOn()) { + logger.trace("myRenew-run", ""); + } + next = doRenew.call().longValue(); + + } catch (Exception e) { + logger.fine("myRenew-run", "Failed to renew lease", e); + close(); + } + + if (next > 0 && next < Long.MAX_VALUE) { + next = next/2; + next = (next < MIN_MILLIS) ? MIN_MILLIS : next; + } else { + close(); + } + } + + nextRenewTime = System.currentTimeMillis() + next; + + if (logger.traceOn()) { + logger.trace("myRenew-run", "Next leasing: "+next); + } + + synchronized(lock) { + if (!closed) { + future = scheduler.schedule(this, next, TimeUnit.MILLISECONDS); + } + } + } + }; + + private final Callable doRenew; + private ScheduledFuture future; + private boolean closed = false; + private long nextRenewTime; + + private final int[] lock = new int[0]; + + private final ScheduledExecutorService scheduler; + + private static final long MIN_MILLIS = 50; + + private static final ClassLogger logger = + new ClassLogger("javax.management.event", "LeaseRenewer"); +} diff --git a/src/share/classes/com/sun/jmx/event/ReceiverBuffer.java b/src/share/classes/com/sun/jmx/event/ReceiverBuffer.java new file mode 100644 index 000000000..7c2a737a2 --- /dev/null +++ b/src/share/classes/com/sun/jmx/event/ReceiverBuffer.java @@ -0,0 +1,97 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.event; + +import com.sun.jmx.remote.util.ClassLogger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.management.remote.NotificationResult; +import javax.management.remote.TargetedNotification; + + +public class ReceiverBuffer { + public void addNotifs(NotificationResult nr) { + if (nr == null) { + return; + } + + TargetedNotification[] tns = nr.getTargetedNotifications(); + + if (logger.traceOn()) { + logger.trace("addNotifs", "" + tns.length); + } + + long impliedStart = nr.getEarliestSequenceNumber(); + final long missed = impliedStart - start; + start = nr.getNextSequenceNumber(); + + if (missed > 0) { + if (logger.traceOn()) { + logger.trace("addNotifs", + "lost: "+missed); + } + + lost += missed; + } + + Collections.addAll(notifList, nr.getTargetedNotifications()); + } + + public TargetedNotification[] removeNotifs() { + if (logger.traceOn()) { + logger.trace("removeNotifs", String.valueOf(notifList.size())); + } + + if (notifList.size() == 0) { + return null; + } + + TargetedNotification[] ret = notifList.toArray( + new TargetedNotification[]{}); + notifList.clear(); + + return ret; + } + + public int size() { + return notifList.size(); + } + + public int removeLost() { + int ret = lost; + lost = 0; + return ret; + } + + private List notifList + = new ArrayList(); + private long start = 0; + private int lost = 0; + + private static final ClassLogger logger = + new ClassLogger("javax.management.event", "ReceiverBuffer"); +} diff --git a/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java b/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java new file mode 100644 index 000000000..7de1b40e9 --- /dev/null +++ b/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.event; + +import com.sun.jmx.remote.util.ClassLogger; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; + +/** + *

A task that is repeatedly run by an Executor. The task will be + * repeated as long as the {@link #isSuspended()} method returns true. Once + * that method returns false, the task is no longer executed until someone + * calls {@link #resume()}.

+ * @author sjiang + */ +public abstract class RepeatedSingletonJob implements Runnable { + public RepeatedSingletonJob(Executor executor) { + if (executor == null) { + throw new NullPointerException("Null executor!"); + } + + this.executor = executor; + } + + public boolean isWorking() { + return working; + } + + public void resume() { + + synchronized(this) { + if (!working) { + if (logger.traceOn()) { + logger.trace("resume", ""); + } + working = true; + execute(); + } + } + } + + public abstract void task(); + public abstract boolean isSuspended(); + + public void run() { + if (logger.traceOn()) { + logger.trace("run", "execute the task"); + } + try { + task(); + } catch (Exception e) { + // A correct task() implementation should not throw exceptions. + // It may cause isSuspended() to start returning true, though. + logger.trace("run", "failed to execute the task", e); + } + + synchronized(this) { + if (!isSuspended()) { + execute(); + } else { + if (logger.traceOn()) { + logger.trace("run", "suspend the task"); + } + working = false; + } + } + + } + + private void execute() { + try { + executor.execute(this); + } catch (RejectedExecutionException e) { + logger.warning( + "setEventReceiver", "Executor threw exception", e); + throw new RejectedExecutionException( + "Executor.execute threw exception -" + + "should not be possible", e); + // User-supplied Executor should not be configured in a way that + // might cause this exception, for example if it is shared between + // several client objects and doesn't have capacity for one job + // from each one. CR 6732037 will add text to the spec explaining + // the problem. The rethrown exception will propagate either out + // of resume() to user code, or out of run() to the Executor + // (which will probably ignore it). + } + } + + private boolean working = false; + private final Executor executor; + + private static final ClassLogger logger = + new ClassLogger("javax.management.event", "RepeatedSingletonJob"); +} diff --git a/src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java b/src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java new file mode 100644 index 000000000..e7144cf36 --- /dev/null +++ b/src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java @@ -0,0 +1,1341 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.jmx.interceptor; + +import com.sun.jmx.mbeanserver.Util; +import java.io.ObjectInputStream; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.DynamicMBean; +import javax.management.DynamicWrapperMBean; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.JMRuntimeException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; +import javax.management.NotificationBroadcaster; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.OperationsException; +import javax.management.QueryEval; +import javax.management.QueryExp; +import javax.management.ReflectionException; +import javax.management.RuntimeOperationsException; +import javax.management.loading.ClassLoaderRepository; + +/** + *

Base class for custom implementations of the {@link MBeanServer} + * interface. The commonest use of this class is as the {@linkplain + * JMXNamespace#getSourceServer() source server} for a {@link + * JMXNamespace}, although this class can be used anywhere an {@code + * MBeanServer} instance is required. Note that the usual ways to + * obtain an {@code MBeanServer} instance are either to use {@link + * java.lang.management.ManagementFactory#getPlatformMBeanServer() + * ManagementFactory.getPlatformMBeanServer()} or to use the {@code + * newMBeanServer} or {@code createMBeanServer} methods from {@link + * javax.management.MBeanServerFactory MBeanServerFactory}. {@code + * MBeanServerSupport} is for certain cases where those are not + * appropriate.

+ * + *

There are two main use cases for this class: special-purpose MBeanServer implementations, + * and namespaces containing Virtual MBeans. The next + * sections explain these use cases.

+ * + *

In the simplest case, a subclass needs to implement only two methods:

+ * + *
    + *
  • + * {@link #getNames getNames} which returns the name of + * all MBeans handled by this {@code MBeanServer}. + *
  • + *
  • + * {@link #getDynamicMBeanFor getDynamicMBeanFor} which returns a + * {@link DynamicMBean} that can be used to invoke operations and + * obtain meta data (MBeanInfo) on a given MBean. + *
  • + *
+ * + *

Subclasses can create such {@link DynamicMBean} MBeans on the fly - for + * instance, using the class {@link javax.management.StandardMBean}, just for + * the duration of an MBeanServer method call.

+ * + *

Special-purpose MBeanServer implementations

+ * + *

In some cases + * the general-purpose {@code MBeanServer} that you get from + * {@link javax.management.MBeanServerFactory MBeanServerFactory} is not + * appropriate. You might need different security checks, or you might + * want a mock {@code MBeanServer} suitable for use in tests, or you might + * want a simplified and optimized {@code MBeanServer} for a special purpose.

+ * + *

As an example of a special-purpose {@code MBeanServer}, the class {@link + * javax.management.QueryNotificationFilter QueryNotificationFilter} constructs + * an {@code MBeanServer} instance every time it filters a notification, + * with just one MBean that represents the notification. Although it could + * use {@code MBeanServerFactory.newMBeanServer}, a special-purpose {@code + * MBeanServer} will be quicker to create, use less memory, and have simpler + * methods that execute faster.

+ * + *

Here is an example of a special-purpose {@code MBeanServer} + * implementation that contains exactly one MBean, which is specified at the + * time of creation.

+ * + *
+ * public class SingletonMBeanServer extends MBeanServerSupport {
+ *     private final ObjectName objectName;
+ *     private final DynamicMBean mbean;
+ *
+ *     public SingletonMBeanServer(ObjectName objectName, DynamicMBean mbean) {
+ *         this.objectName = objectName;
+ *         this.mbean = mbean;
+ *     }
+ *
+ *     @Override
+ *     protected {@code Set} {@link #getNames getNames}() {
+ *         return Collections.singleton(objectName);
+ *     }
+ *
+ *     @Override
+ *     public DynamicMBean {@link #getDynamicMBeanFor
+ *                                getDynamicMBeanFor}(ObjectName name)
+ *             throws InstanceNotFoundException {
+ *         if (objectName.equals(name))
+ *             return mbean;
+ *         else
+ *             throw new InstanceNotFoundException(name);
+ *     }
+ * }
+ * 
+ * + *

Using this class, you could make an {@code MBeanServer} that contains + * a {@link javax.management.timer.Timer Timer} MBean like this:

+ * + *
+ *     Timer timer = new Timer();
+ *     DynamicMBean mbean = new {@link javax.management.StandardMBean
+ *                                     StandardMBean}(timer, TimerMBean.class);
+ *     ObjectName name = new ObjectName("com.example:type=Timer");
+ *     MBeanServer timerMBS = new SingletonMBeanServer(name, mbean);
+ * 
+ * + *

When {@code getDynamicMBeanFor} always returns the same object for the + * same name, as here, notifications work in the expected way: if the object + * is a {@link NotificationEmitter} then listeners can be added using + * {@link MBeanServer#addNotificationListener(ObjectName, NotificationListener, + * NotificationFilter, Object) MBeanServer.addNotificationListener}. If + * {@code getDynamicMBeanFor} does not always return the same object for the + * same name, more work is needed to make notifications work, as described + * below.

+ * + *

Namespaces containing Virtual MBeans

+ * + *

Virtual MBeans are MBeans that do not exist as Java objects, + * except transiently while they are being accessed. This is useful when + * there might be very many of them, or when keeping track of their creation + * and deletion might be expensive or hard. For example, you might have one + * MBean per system process. With an ordinary {@code MBeanServer}, you would + * have to list the system processes in order to create an MBean object for + * each one, and you would have to track the arrival and departure of system + * processes in order to create or delete the corresponding MBeans. With + * Virtual MBeans, you only need the MBean for a given process at the exact + * point where it is referenced with a call such as + * {@link MBeanServer#getAttribute MBeanServer.getAttribute}.

+ * + *

Here is an example of an {@code MBeanServer} implementation that has + * one MBean for every system property. The system property {@code "java.home"} + * is represented by the MBean called {@code + * com.example:type=Property,name="java.home"}, with an attribute called + * {@code Value} that is the value of the property.

+ * + *
+ * public interface PropertyMBean {
+ *     public String getValue();
+ * }
+ *
+ * public class PropsMBS extends MBeanServerSupport {
+ *     private static ObjectName newObjectName(String name) {
+ *         try {
+ *             return new ObjectName(name);
+ *         } catch (MalformedObjectNameException e) {
+ *             throw new AssertionError(e);
+ *         }
+ *     }
+ *
+ *     public static class PropertyImpl implements PropertyMBean {
+ *         private final String name;
+ *
+ *         public PropertyImpl(String name) {
+ *             this.name = name;
+ *         }
+ *
+ *         public String getValue() {
+ *             return System.getProperty(name);
+ *         }
+ *     }
+ *
+ *     @Override
+ *     public DynamicMBean {@link #getDynamicMBeanFor
+ *                                getDynamicMBeanFor}(ObjectName name)
+ *             throws InstanceNotFoundException {
+ *
+ *         // Check that the name is a legal one for a Property MBean
+ *         ObjectName namePattern = newObjectName(
+ *                     "com.example:type=Property,name=\"*\"");
+ *         if (!namePattern.apply(name))
+ *             throw new InstanceNotFoundException(name);
+ *
+ *         // Extract the name of the property that the MBean corresponds to
+ *         String propName = ObjectName.unquote(name.getKeyProperty("name"));
+ *         if (System.getProperty(propName) == null)
+ *             throw new InstanceNotFoundException(name);
+ *
+ *         // Construct and return a transient MBean object
+ *         PropertyMBean propMBean = new PropertyImpl(propName);
+ *         return new StandardMBean(propMBean, PropertyMBean.class, false);
+ *     }
+ *
+ *     @Override
+ *     protected {@code Set} {@link #getNames getNames}() {
+ *         {@code Set names = new TreeSet();}
+ *         Properties props = System.getProperties();
+ *         for (String propName : props.stringPropertyNames()) {
+ *             ObjectName objectName = newObjectName(
+ *                     "com.example:type=Property,name=" +
+ *                     ObjectName.quote(propName));
+ *             names.add(objectName);
+ *         }
+ *         return names;
+ *     }
+ * }
+ * 
+ * + *

Because the {@code getDynamicMBeanFor} method + * returns a different object every time it is called, the default handling + * of notifications will not work, as explained below. + * In this case it does not matter, because the object returned by {@code + * getDynamicMBeanFor} is not a {@code NotificationEmitter}, so {@link + * MBeanServer#addNotificationListener(ObjectName, NotificationListener, + * NotificationFilter, Object) MBeanServer.addNotificationListener} will + * always fail. But if we wanted to extend {@code PropsMBS} so that the MBean + * for property {@code "foo"} emitted a notification every time that property + * changed, we would need to do it as shown below. (Because there is no API to + * be informed when a property changes, this code assumes that some other code + * calls the {@code propertyChanged} method every time a property changes.)

+ * + *
+ * public class PropsMBS {
+ *     ...as above...
+ *
+ *     private final {@link VirtualEventManager} vem = new VirtualEventManager();
+ *
+ *     @Override
+ *     public NotificationEmitter {@link #getNotificationEmitterFor
+ *                                       getNotificationEmitterFor}(
+ *             ObjectName name) throws InstanceNotFoundException {
+ *         getDynamicMBeanFor(name);  // check that the name is valid
+ *         return vem.{@link VirtualEventManager#getNotificationEmitterFor
+ *                           getNotificationEmitterFor}(name);
+ *     }
+ *
+ *     public void propertyChanged(String name, String newValue) {
+ *         ObjectName objectName = newObjectName(
+ *                 "com.example:type=Property,name=" + ObjectName.quote(name));
+ *         Notification n = new Notification(
+ *                 "com.example.property.changed", objectName, 0L,
+ *                 "Property " + name + " changed");
+ *         n.setUserData(newValue);
+ *         vem.{@link VirtualEventManager#publish publish}(objectName, n);
+ *     }
+ * }
+ * 
+ * + *

MBean creation and deletion

+ * + *

MBean creation through {@code MBeanServer.createMBean} is disabled + * by default. Subclasses which need to support MBean creation + * through {@code createMBean} need to implement a single method {@link + * #createMBean(String, ObjectName, ObjectName, Object[], String[], + * boolean)}.

+ * + *

Similarly MBean registration and unregistration through {@code + * registerMBean} and {@code unregisterMBean} are disabled by default. + * Subclasses which need to support MBean registration and + * unregistration will need to implement {@link #registerMBean registerMBean} + * and {@link #unregisterMBean unregisterMBean}.

+ * + *

Notifications

+ * + *

By default {@link MBeanServer#addNotificationListener(ObjectName, + * NotificationListener, NotificationFilter, Object) addNotificationListener} + * is accepted for an MBean {@code name} if {@link #getDynamicMBeanFor + * getDynamicMBeanFor}(name) returns an object that is a + * {@link NotificationEmitter}. That is appropriate if + * {@code getDynamicMBeanFor}(name) always returns the + * same object for the same {@code name}. But with + * Virtual MBeans, every call to {@code getDynamicMBeanFor} returns a new object, + * which is discarded as soon as the MBean request has finished. + * So a listener added to that object would be immediately forgotten.

+ * + *

The simplest way for a subclass that defines Virtual MBeans + * to support notifications is to create a private {@link VirtualEventManager} + * and override the method {@link + * #getNotificationEmitterFor getNotificationEmitterFor} as follows:

+ * + *
+ *     private final VirtualEventManager vem = new VirtualEventManager();
+ *
+ *     @Override
+ *     public NotificationEmitter getNotificationEmitterFor(
+ *             ObjectName name) throws InstanceNotFoundException {
+ *         // Check that the name is a valid Virtual MBean.
+ *         // This is the easiest way to do that, but not always the
+ *         // most efficient:
+ *         getDynamicMBeanFor(name);
+ *
+ *         // Return an object that supports add/removeNotificationListener
+ *         // through the VirtualEventManager.
+ *         return vem.getNotificationEmitterFor(name);
+ *     }
+ * 
+ * + *

A notification {@code n} can then be sent from the Virtual MBean + * called {@code name} by calling {@link VirtualEventManager#publish + * vem.publish}(name, n). See the example + * above.

+ * + * @since Java SE 7 + */ +public abstract class MBeanServerSupport implements MBeanServer { + + /** + * A logger for this class. + */ + private static final Logger LOG = + Logger.getLogger(MBeanServerSupport.class.getName()); + + /** + *

Make a new {@code MBeanServerSupport} instance.

+ */ + protected MBeanServerSupport() { + } + + /** + *

Returns a dynamically created handle that makes it possible to + * access the named MBean for the duration of a method call.

+ * + *

An easy way to create such a {@link DynamicMBean} handle is, for + * instance, to create a temporary MXBean instance and to wrap it in + * an instance of + * {@link javax.management.StandardMBean}. + * This handle should remain valid for the duration of the call + * but can then be discarded.

+ * @param name the name of the MBean for which a request was received. + * @return a {@link DynamicMBean} handle that can be used to invoke + * operations on the named MBean. + * @throws InstanceNotFoundException if no such MBean is supposed + * to exist. + */ + public abstract DynamicMBean getDynamicMBeanFor(ObjectName name) + throws InstanceNotFoundException; + + /** + *

Subclasses should implement this method to return + * the names of all MBeans handled by this object instance.

+ * + *

The object returned by getNames() should be safely {@linkplain + * Set#iterator iterable} even in the presence of other threads that may + * cause the set of names to change. Typically this means one of the + * following:

+ * + *
    + *
  • the returned set of names is always the same; or + *
  • the returned set of names is an object such as a {@link + * java.util.concurrent.CopyOnWriteArraySet CopyOnWriteArraySet} that is + * safely iterable even if the set is changed by other threads; or + *
  • a new Set is constructed every time this method is called. + *
+ * + * @return the names of all MBeans handled by this object. + */ + protected abstract Set getNames(); + + /** + *

List names matching the given pattern. + * The default implementation of this method calls {@link #getNames()} + * and returns the subset of those names matching {@code pattern}.

+ * + * @param pattern an ObjectName pattern + * @return the list of MBean names that match the given pattern. + */ + protected Set getMatchingNames(ObjectName pattern) { + return Util.filterMatchingNames(pattern, getNames()); + } + + /** + *

Returns a {@link NotificationEmitter} which can be used to + * subscribe or unsubscribe for notifications with the named + * mbean.

+ * + *

The default implementation of this method calls {@link + * #getDynamicMBeanFor getDynamicMBeanFor(name)} and returns that object + * if it is a {@code NotificationEmitter}, otherwise null. See above for further discussion of notification + * handling.

+ * + * @param name The name of the MBean whose notifications are being + * subscribed, or unsuscribed. + * + * @return A {@link NotificationEmitter} that can be used to subscribe or + * unsubscribe for notifications emitted by the named MBean, or {@code + * null} if the MBean does not emit notifications and should not be + * considered as a {@code NotificationEmitter}. + * + * @throws InstanceNotFoundException if {@code name} is not the name of + * an MBean in this {@code MBeanServer}. + */ + public NotificationEmitter getNotificationEmitterFor(ObjectName name) + throws InstanceNotFoundException { + DynamicMBean mbean = getDynamicMBeanFor(name); + if (mbean instanceof NotificationEmitter) + return (NotificationEmitter) mbean; + else + return null; + } + + private NotificationEmitter getNonNullNotificationEmitterFor( + ObjectName name) + throws InstanceNotFoundException { + NotificationEmitter emitter = getNotificationEmitterFor(name); + if (emitter == null) { + IllegalArgumentException iae = new IllegalArgumentException( + "Not a NotificationEmitter: " + name); + throw new RuntimeOperationsException(iae); + } + return emitter; + } + + /** + *

Creates a new MBean in the MBean name space. + * This operation is not supported in this base class implementation.

+ * The default implementation of this method always throws an {@link + * UnsupportedOperationException} + * wrapped in a {@link RuntimeOperationsException}.

+ * + *

Subclasses may redefine this method to provide an implementation. + * All the various flavors of {@code MBeanServer.createMBean} methods + * will eventually call this method. A subclass that wishes to + * support MBean creation through {@code createMBean} thus only + * needs to provide an implementation for this one method. + * + * @param className The class name of the MBean to be instantiated. + * @param name The object name of the MBean. May be null. + * @param params An array containing the parameters of the + * constructor to be invoked. + * @param signature An array containing the signature of the + * constructor to be invoked. + * @param loaderName The object name of the class loader to be used. + * @param useCLR This parameter is {@code true} when this method + * is called from one of the {@code MBeanServer.createMBean} methods + * whose signature does not include the {@code ObjectName} of an + * MBean class loader to use for loading the MBean class. + * + * @return An ObjectInstance, containing the + * ObjectName and the Java class name of the newly + * instantiated MBean. If the contained ObjectName + * is n, the contained Java class name is + * {@link javax.management.MBeanServer#getMBeanInfo + * getMBeanInfo(n)}.getClassName(). + * + * @exception ReflectionException Wraps a + * java.lang.ClassNotFoundException or a + * java.lang.Exception that occurred when trying to + * invoke the MBean's constructor. + * @exception InstanceAlreadyExistsException The MBean is already + * under the control of the MBean server. + * @exception MBeanRegistrationException The + * preRegister (MBeanRegistration + * interface) method of the MBean has thrown an exception. The + * MBean will not be registered. + * @exception MBeanException The constructor of the MBean has + * thrown an exception + * @exception NotCompliantMBeanException This class is not a JMX + * compliant MBean + * @exception InstanceNotFoundException The specified class loader + * is not registered in the MBean server. + * @exception RuntimeOperationsException Wraps either: + *

    + *
  • a java.lang.IllegalArgumentException: The className + * passed in parameter is null, the ObjectName passed in + * parameter contains a pattern or no ObjectName is specified + * for the MBean; or
  • + *
  • an {@code UnsupportedOperationException} if creating MBeans is not + * supported by this {@code MBeanServer} implementation. + *
+ */ + public ObjectInstance createMBean(String className, + ObjectName name, ObjectName loaderName, Object[] params, + String[] signature, boolean useCLR) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException { + throw newUnsupportedException("createMBean"); + } + + + /** + *

Attempts to determine whether the named MBean should be + * considered as an instance of a given class. The default implementation + * of this method calls {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} + * to get an MBean object. Then its behaviour is the same as the standard + * {@link MBeanServer#isInstanceOf MBeanServer.isInstanceOf} method.

+ * + * {@inheritDoc} + */ + public boolean isInstanceOf(ObjectName name, String className) + throws InstanceNotFoundException { + + final DynamicMBean instance = nonNullMBeanFor(name); + + try { + final String mbeanClassName = instance.getMBeanInfo().getClassName(); + + if (mbeanClassName.equals(className)) + return true; + + final Object resource; + final ClassLoader cl; + if (instance instanceof DynamicWrapperMBean) { + DynamicWrapperMBean d = (DynamicWrapperMBean) instance; + resource = d.getWrappedObject(); + cl = d.getWrappedClassLoader(); + } else { + resource = instance; + cl = instance.getClass().getClassLoader(); + } + + final Class classNameClass = Class.forName(className, false, cl); + + if (classNameClass.isInstance(resource)) + return true; + + if (classNameClass == NotificationBroadcaster.class || + classNameClass == NotificationEmitter.class) { + try { + getNotificationEmitterFor(name); + return true; + } catch (Exception x) { + LOG.finest("MBean " + name + + " is not a notification emitter. Ignoring: "+x); + return false; + } + } + + final Class resourceClass = Class.forName(mbeanClassName, false, cl); + return classNameClass.isAssignableFrom(resourceClass); + } catch (Exception x) { + /* Could be SecurityException or ClassNotFoundException */ + LOG.logp(Level.FINEST, + MBeanServerSupport.class.getName(), + "isInstanceOf", "Exception calling isInstanceOf", x); + return false; + } + } + + /** + * {@inheritDoc} + * + *

The default implementation of this method returns the string + * "DefaultDomain".

+ */ + public String getDefaultDomain() { + return "DefaultDomain"; + } + + /** + * {@inheritDoc} + * + *

The default implementation of this method returns + * {@link #getNames()}.size().

+ */ + public Integer getMBeanCount() { + return getNames().size(); + } + + /** + * {@inheritDoc} + * + *

The default implementation of this method first calls {@link #getNames + * getNames()} to get a list of all MBean names, + * and from this set of names, derives the set of domains which contain + * MBeans.

+ */ + public String[] getDomains() { + final Set names = getNames(); + final Set res = new TreeSet(); + for (ObjectName n : names) { + if (n == null) continue; // not allowed but you never know. + res.add(n.getDomain()); + } + return res.toArray(new String[res.size()]); + } + + + /** + * {@inheritDoc} + * + *

The default implementation of this method will first + * call {@link + * #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a handle + * to the named MBean, + * and then call {@link DynamicMBean#getAttribute getAttribute} + * on that {@link DynamicMBean} handle.

+ * + * @throws RuntimeOperationsException {@inheritDoc} + */ + public Object getAttribute(ObjectName name, String attribute) + throws MBeanException, AttributeNotFoundException, + InstanceNotFoundException, ReflectionException { + final DynamicMBean mbean = nonNullMBeanFor(name); + return mbean.getAttribute(attribute); + } + + /** + * {@inheritDoc} + * + *

The default implementation of this method will first + * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} + * to obtain a handle to the named MBean, + * and then call {@link DynamicMBean#setAttribute setAttribute} + * on that {@link DynamicMBean} handle.

+ * + * @throws RuntimeOperationsException {@inheritDoc} + */ + public void setAttribute(ObjectName name, Attribute attribute) + throws InstanceNotFoundException, AttributeNotFoundException, + InvalidAttributeValueException, MBeanException, + ReflectionException { + final DynamicMBean mbean = nonNullMBeanFor(name); + mbean.setAttribute(attribute); + } + + /** + * {@inheritDoc} + * + *

The default implementation of this method will first + * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a + * handle to the named MBean, + * and then call {@link DynamicMBean#getAttributes getAttributes} + * on that {@link DynamicMBean} handle.

+ * + * @throws RuntimeOperationsException {@inheritDoc} + */ + public AttributeList getAttributes(ObjectName name, + String[] attributes) throws InstanceNotFoundException, + ReflectionException { + final DynamicMBean mbean = nonNullMBeanFor(name); + return mbean.getAttributes(attributes); + } + + /** + * {@inheritDoc} + * + *

The default implementation of this method will first + * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a + * handle to the named MBean, + * and then call {@link DynamicMBean#setAttributes setAttributes} + * on that {@link DynamicMBean} handle.

+ * + * @throws RuntimeOperationsException {@inheritDoc} + */ + public AttributeList setAttributes(ObjectName name, AttributeList attributes) + throws InstanceNotFoundException, ReflectionException { + final DynamicMBean mbean = nonNullMBeanFor(name); + return mbean.setAttributes(attributes); + } + + /** + * {@inheritDoc} + * + *

The default implementation of this method will first + * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a + * handle to the named MBean, + * and then call {@link DynamicMBean#invoke invoke} + * on that {@link DynamicMBean} handle.

+ */ + public Object invoke(ObjectName name, String operationName, + Object[] params, String[] signature) + throws InstanceNotFoundException, MBeanException, + ReflectionException { + final DynamicMBean mbean = nonNullMBeanFor(name); + return mbean.invoke(operationName, params, signature); + } + + /** + * {@inheritDoc} + * + *

The default implementation of this method will first + * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a + * handle to the named MBean, + * and then call {@link DynamicMBean#getMBeanInfo getMBeanInfo} + * on that {@link DynamicMBean} handle.

+ */ + public MBeanInfo getMBeanInfo(ObjectName name) + throws InstanceNotFoundException, IntrospectionException, + ReflectionException { + final DynamicMBean mbean = nonNullMBeanFor(name); + return mbean.getMBeanInfo(); + } + + /** + * {@inheritDoc} + * + *

The default implementation of this method will call + * {@link #getDynamicMBeanFor getDynamicMBeanFor(name)}.{@link DynamicMBean#getMBeanInfo getMBeanInfo()}.{@link MBeanInfo#getClassName getClassName()} to get the + * class name to combine with {@code name} to produce a new + * {@code ObjectInstance}.

+ */ + public ObjectInstance getObjectInstance(ObjectName name) + throws InstanceNotFoundException { + final DynamicMBean mbean = nonNullMBeanFor(name); + final String className = mbean.getMBeanInfo().getClassName(); + return new ObjectInstance(name, className); + } + + /** + * {@inheritDoc} + * + *

The default implementation of this method will first call {@link + * #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a handle to the + * named MBean. If {@code getDynamicMBeanFor} returns an object, {@code + * isRegistered} will return true. If {@code getDynamicMBeanFor} returns + * null or throws {@link InstanceNotFoundException}, {@code isRegistered} + * will return false.

+ * + * @throws RuntimeOperationsException {@inheritDoc} + */ + public boolean isRegistered(ObjectName name) { + try { + final DynamicMBean mbean = getDynamicMBeanFor(name); + return mbean!=null; + } catch (InstanceNotFoundException x) { + if (LOG.isLoggable(Level.FINEST)) + LOG.finest("MBean "+name+" is not registered: "+x); + return false; + } + } + + + /** + * {@inheritDoc} + * + *

The default implementation of this method will first + * call {@link #queryNames queryNames} + * to get a list of all matching MBeans, and then, for each returned name, + * call {@link #getObjectInstance getObjectInstance(name)}.

+ */ + public Set queryMBeans(ObjectName pattern, QueryExp query) { + final Set names = queryNames(pattern, query); + if (names.isEmpty()) return Collections.emptySet(); + final Set mbeans = new HashSet(); + for (ObjectName name : names) { + try { + mbeans.add(getObjectInstance(name)); + } catch (SecurityException x) { // DLS: OK + continue; + } catch (InstanceNotFoundException x) { // DLS: OK + continue; + } + } + return mbeans; + } + + /** + * {@inheritDoc} + * + *

The default implementation of this method calls {@link #getMatchingNames + * getMatchingNames(pattern)} to obtain a list of MBeans matching + * the given name pattern. If the {@code query} parameter is null, + * this will be the result. Otherwise, it will evaluate the + * {@code query} parameter for each of the returned names, exactly + * as an {@code MBeanServer} would. This might result in + * {@link #getDynamicMBeanFor getDynamicMBeanFor} being called + * several times for each returned name.

+ */ + public Set queryNames(ObjectName pattern, QueryExp query) { + try { + final Set res = getMatchingNames(pattern); + return filterListOfObjectNames(res, query); + } catch (Exception x) { + LOG.fine("Unexpected exception raised in queryNames: "+x); + LOG.log(Level.FINEST, "Unexpected exception raised in queryNames", x); + } + // We reach here only when an exception was raised. + // + return Collections.emptySet(); + } + + private final static boolean apply(final QueryExp query, + final ObjectName on, + final MBeanServer srv) { + boolean res = false; + MBeanServer oldServer = QueryEval.getMBeanServer(); + query.setMBeanServer(srv); + try { + res = query.apply(on); + } catch (Exception e) { + LOG.finest("QueryExp.apply threw exception, returning false." + + " Cause: "+e); + res = false; + } finally { + /* + * query.setMBeanServer is probably + * QueryEval.setMBeanServer so put back the old + * value. Since that method uses a ThreadLocal + * variable, this code is only needed for the + * unusual case where the user creates a custom + * QueryExp that calls a nested query on another + * MBeanServer. + */ + query.setMBeanServer(oldServer); + } + return res; + } + + /** + * Filters a {@code Set} according to a pattern and a query. + * This might be quite inefficient for virtual name spaces. + */ + Set + filterListOfObjectNames(Set list, + QueryExp query) { + if (list.isEmpty() || query == null) + return list; + + // create a new result set + final Set result = new HashSet(); + + for (ObjectName on : list) { + // if on doesn't match query exclude it. + if (apply(query, on, this)) + result.add(on); + } + return result; + } + + + // Don't use {@inheritDoc}, because we don't want to say that the + // MBeanServer replaces a reference to the MBean by its ObjectName. + /** + *

Adds a listener to a registered MBean. A notification emitted by + * the MBean will be forwarded to the listener.

+ * + *

This implementation calls + * {@link #getNotificationEmitterFor getNotificationEmitterFor} + * and invokes {@code addNotificationListener} on the + * {@link NotificationEmitter} it returns. + * + * @see #getDynamicMBeanFor getDynamicMBeanFor + * @see #getNotificationEmitterFor getNotificationEmitterFor + */ + public void addNotificationListener(ObjectName name, + NotificationListener listener, NotificationFilter filter, + Object handback) throws InstanceNotFoundException { + final NotificationEmitter emitter = + getNonNullNotificationEmitterFor(name); + emitter.addNotificationListener(listener, filter, handback); + } + + /** + * {@inheritDoc} + * + *

This implementation calls + * {@link #getNotificationEmitterFor getNotificationEmitterFor} + * and invokes {@code removeNotificationListener} on the + * {@link NotificationEmitter} it returns. + * @see #getDynamicMBeanFor getDynamicMBeanFor + * @see #getNotificationEmitterFor getNotificationEmitterFor + */ + public void removeNotificationListener(ObjectName name, + NotificationListener listener) + throws InstanceNotFoundException, ListenerNotFoundException { + final NotificationEmitter emitter = + getNonNullNotificationEmitterFor(name); + emitter.removeNotificationListener(listener); + } + + /** + * {@inheritDoc} + * + *

This implementation calls + * {@link #getNotificationEmitterFor getNotificationEmitterFor} + * and invokes {@code removeNotificationListener} on the + * {@link NotificationEmitter} it returns. + * @see #getDynamicMBeanFor getDynamicMBeanFor + * @see #getNotificationEmitterFor getNotificationEmitterFor + */ + public void removeNotificationListener(ObjectName name, + NotificationListener listener, NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, ListenerNotFoundException { + NotificationEmitter emitter = + getNonNullNotificationEmitterFor(name); + emitter.removeNotificationListener(listener); + } + + + /** + *

Adds a listener to a registered MBean.

+ * + *

The default implementation of this method first calls + * {@link #getDynamicMBeanFor getDynamicMBeanFor(listenerName)}. + * If that successfully returns an object, call it {@code + * mbean}, then (a) if {@code mbean} is an instance of {@link + * NotificationListener} then this method calls {@link + * #addNotificationListener(ObjectName, NotificationListener, + * NotificationFilter, Object) addNotificationListener(name, mbean, filter, + * handback)}, otherwise (b) this method throws an exception as specified + * for this case.

+ * + *

This default implementation is not appropriate for Virtual MBeans, + * although that only matters if the object returned by {@code + * getDynamicMBeanFor} can be an instance of + * {@code NotificationListener}.

+ * + * @throws RuntimeOperationsException {@inheritDoc} + */ + public void addNotificationListener(ObjectName name, ObjectName listenerName, + NotificationFilter filter, Object handback) + throws InstanceNotFoundException { + NotificationListener listener = getListenerMBean(listenerName); + addNotificationListener(name, listener, filter, handback); + } + + /** + * {@inheritDoc} + * + *

This operation is not supported in this base class implementation. + * The default implementation of this method always throws + * {@link RuntimeOperationsException} wrapping + * {@link UnsupportedOperationException}.

+ * + * @throws javax.management.RuntimeOperationsException wrapping + * {@link UnsupportedOperationException} + */ + public void removeNotificationListener(ObjectName name, + ObjectName listenerName) + throws InstanceNotFoundException, ListenerNotFoundException { + NotificationListener listener = getListenerMBean(listenerName); + removeNotificationListener(name, listener); + } + + /** + * {@inheritDoc} + * + *

This operation is not supported in this base class implementation. + * The default implementation of this method always throws + * {@link RuntimeOperationsException} wrapping + * {@link UnsupportedOperationException}.

+ * + * @throws javax.management.RuntimeOperationsException wrapping + * {@link UnsupportedOperationException} + */ + public void removeNotificationListener(ObjectName name, + ObjectName listenerName, NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, ListenerNotFoundException { + NotificationListener listener = getListenerMBean(listenerName); + removeNotificationListener(name, listener, filter, handback); + } + + private NotificationListener getListenerMBean(ObjectName listenerName) + throws InstanceNotFoundException { + Object mbean = getDynamicMBeanFor(listenerName); + if (mbean instanceof NotificationListener) + return (NotificationListener) mbean; + else { + throw newIllegalArgumentException( + "MBean is not a NotificationListener: " + listenerName); + } + } + + + /** + * {@inheritDoc} + * + *

This operation is not supported in this base class implementation. + * The default implementation of this method always throws + * {@link InstanceNotFoundException} wrapping + * {@link UnsupportedOperationException}.

+ * + * @return the default implementation of this method never returns. + * @throws javax.management.RuntimeOperationsException wrapping + * {@link UnsupportedOperationException} + */ + public ClassLoader getClassLoader(ObjectName loaderName) + throws InstanceNotFoundException { + final UnsupportedOperationException failed = + new UnsupportedOperationException("getClassLoader"); + final InstanceNotFoundException x = + new InstanceNotFoundException(String.valueOf(loaderName)); + x.initCause(failed); + throw x; + } + + /** + * {@inheritDoc} + * + *

The default implementation of this method calls + * {@link #getDynamicMBeanFor getDynamicMBeanFor(mbeanName)} and applies + * the logic just described to the result.

+ */ + public ClassLoader getClassLoaderFor(ObjectName mbeanName) + throws InstanceNotFoundException { + final DynamicMBean mbean = nonNullMBeanFor(mbeanName); + if (mbean instanceof DynamicWrapperMBean) + return ((DynamicWrapperMBean) mbean).getWrappedClassLoader(); + else + return mbean.getClass().getClassLoader(); + } + + /** + * {@inheritDoc} + * + *

The default implementation of this method returns a + * {@link ClassLoaderRepository} containing exactly one loader, + * the {@linkplain Thread#getContextClassLoader() context class loader} + * for the current thread. + * Subclasses can override this method to return a different + * {@code ClassLoaderRepository}.

+ */ + public ClassLoaderRepository getClassLoaderRepository() { + // We return a new ClassLoaderRepository each time this + // method is called. This is by design, because the + // SingletonClassLoaderRepository is a very small object and + // getClassLoaderRepository() will not be called very often + // (the connector server calls it once) - in the context of + // MBeanServerSupport there's a very good chance that this method will + // *never* be called. + ClassLoader ccl = Thread.currentThread().getContextClassLoader(); + return Util.getSingleClassLoaderRepository(ccl); + } + + + /** + * {@inheritDoc} + * + *

This operation is not supported in this base class implementation. + * The default implementation of this method always throws + * {@link RuntimeOperationsException} wrapping + * {@link UnsupportedOperationException}.

+ * @throws javax.management.RuntimeOperationsException wrapping + * {@link UnsupportedOperationException} + */ + public ObjectInstance registerMBean(Object object, ObjectName name) + throws InstanceAlreadyExistsException, MBeanRegistrationException, + NotCompliantMBeanException { + throw newUnsupportedException("registerMBean"); + } + + /** + * {@inheritDoc} + * + *

This operation is not supported in this base class implementation. + * The default implementation of this method always throws + * {@link RuntimeOperationsException} wrapping + * {@link UnsupportedOperationException}. + * @throws javax.management.RuntimeOperationsException wrapping + * {@link UnsupportedOperationException} + */ + public void unregisterMBean(ObjectName name) + throws InstanceNotFoundException, MBeanRegistrationException { + throw newUnsupportedException("unregisterMBean"); + } + + /** + * Calls {@link #createMBean(String, ObjectName, + * ObjectName, Object[], String[], boolean) + * createMBean(className, name, null, params, signature, true)}; + */ + public final ObjectInstance createMBean(String className, ObjectName name, + Object[] params, String[] signature) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException { + try { + return safeCreateMBean(className, name, null, params, signature, true); + } catch (InstanceNotFoundException ex) { + // should not happen! + throw new MBeanException(ex, "Unexpected exception: " + ex); + } + } + + /** + * Calls {@link #createMBean(String, ObjectName, + * ObjectName, Object[], String[], boolean) + * createMBean(className,name, loaderName, params, signature, false)}; + */ + public final ObjectInstance createMBean(String className, ObjectName name, + ObjectName loaderName, Object[] params, String[] signature) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException { + return safeCreateMBean(className, name, loaderName, params, signature, false); + } + + /** + * Calls {@link #createMBean(String, ObjectName, + * ObjectName, Object[], String[], boolean) + * createMBean(className, name, null, null, null, true)}; + */ + public final ObjectInstance createMBean(String className, ObjectName name) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException { + try { + return safeCreateMBean(className, name, null, null, null, true); + } catch (InstanceNotFoundException ex) { + // should not happen! + throw new MBeanException(ex, "Unexpected exception: " + ex); + } + } + + /** + * Calls {@link #createMBean(String, ObjectName, + * ObjectName, Object[], String[], boolean) + * createMBean(className, name, loaderName, null, null, false)}; + */ + public final ObjectInstance createMBean(String className, ObjectName name, + ObjectName loaderName) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException { + return safeCreateMBean(className, name, loaderName, null, null, false); + } + + // make sure all exceptions are correctly wrapped in a JMXException + private ObjectInstance safeCreateMBean(String className, + ObjectName name, ObjectName loaderName, Object[] params, + String[] signature, boolean useRepository) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException { + try { + return createMBean(className, name, loaderName, params, + signature, useRepository); + } catch (ReflectionException x) { throw x; + } catch (InstanceAlreadyExistsException x) { throw x; + } catch (MBeanRegistrationException x) { throw x; + } catch (MBeanException x) { throw x; + } catch (NotCompliantMBeanException x) { throw x; + } catch (InstanceNotFoundException x) { throw x; + } catch (SecurityException x) { throw x; + } catch (JMRuntimeException x) { throw x; + } catch (RuntimeException x) { + throw new RuntimeOperationsException(x, x.toString()); + } catch (Exception x) { + throw new MBeanException(x, x.toString()); + } + } + + + /** + * {@inheritDoc} + * + *

This operation is not supported in this base class implementation. + * The default implementation of this method always throws + * {@link RuntimeOperationsException} wrapping + * {@link UnsupportedOperationException}.

+ * + * @throws javax.management.RuntimeOperationsException wrapping + * {@link UnsupportedOperationException} + */ + public Object instantiate(String className) + throws ReflectionException, MBeanException { + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * {@inheritDoc} + * + *

This operation is not supported in this base class implementation. + * The default implementation of this method always throws + * {@link RuntimeOperationsException} wrapping + * {@link UnsupportedOperationException}.

+ * + * @throws javax.management.RuntimeOperationsException wrapping + * {@link UnsupportedOperationException} + */ + public Object instantiate(String className, ObjectName loaderName) + throws ReflectionException, MBeanException, + InstanceNotFoundException { + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * {@inheritDoc} + * + *

This operation is not supported in this base class implementation. + * The default implementation of this method always throws + * {@link RuntimeOperationsException} wrapping + * {@link UnsupportedOperationException}.

+ * + * @throws javax.management.RuntimeOperationsException wrapping + * {@link UnsupportedOperationException} + */ + public Object instantiate(String className, Object[] params, + String[] signature) throws ReflectionException, MBeanException { + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * {@inheritDoc} + * + *

This operation is not supported in this base class implementation. + * The default implementation of this method always throws + * {@link RuntimeOperationsException} wrapping + * {@link UnsupportedOperationException}.

+ * + * @throws javax.management.RuntimeOperationsException wrapping + * {@link UnsupportedOperationException} + */ + public Object instantiate(String className, ObjectName loaderName, + Object[] params, String[] signature) + throws ReflectionException, MBeanException, + InstanceNotFoundException { + throw new UnsupportedOperationException("Not applicable."); + } + + + /** + * {@inheritDoc} + * + *

This operation is not supported in this base class implementation. + * The default implementation of this method always throws + * {@link RuntimeOperationsException} wrapping + * {@link UnsupportedOperationException}.

+ * + * @throws javax.management.RuntimeOperationsException wrapping + * {@link UnsupportedOperationException} + */ + @Deprecated + public ObjectInputStream deserialize(ObjectName name, byte[] data) + throws InstanceNotFoundException, OperationsException { + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * {@inheritDoc} + * + *

This operation is not supported in this base class implementation. + * The default implementation of this method always throws + * {@link RuntimeOperationsException} wrapping + * {@link UnsupportedOperationException}.

+ * + * @throws javax.management.RuntimeOperationsException wrapping + * {@link UnsupportedOperationException} + */ + @Deprecated + public ObjectInputStream deserialize(String className, byte[] data) + throws OperationsException, ReflectionException { + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * {@inheritDoc} + * + *

This operation is not supported in this base class implementation. + * The default implementation of this method always throws + * {@link RuntimeOperationsException} wrapping + * {@link UnsupportedOperationException}.

+ * + * @throws javax.management.RuntimeOperationsException wrapping + * {@link UnsupportedOperationException} + */ + @Deprecated + public ObjectInputStream deserialize(String className, + ObjectName loaderName, byte[] data) + throws InstanceNotFoundException, OperationsException, + ReflectionException { + throw new UnsupportedOperationException("Not applicable."); + } + + + // Calls getDynamicMBeanFor, and throws an InstanceNotFoundException + // if the returned mbean is null. + // The DynamicMBean returned by this method is thus guaranteed to be + // non null. + // + private DynamicMBean nonNullMBeanFor(ObjectName name) + throws InstanceNotFoundException { + if (name == null) + throw newIllegalArgumentException("Null ObjectName"); + if (name.getDomain().equals("")) { + String defaultDomain = getDefaultDomain(); + try { + // XXX change to ObjectName.switchDomain + // current code DOES NOT PRESERVE the order of keys + name = new ObjectName(defaultDomain, name.getKeyPropertyList()); + } catch (Exception e) { + throw newIllegalArgumentException( + "Illegal default domain: " + defaultDomain); + } + } + final DynamicMBean mbean = getDynamicMBeanFor(name); + if (mbean!=null) return mbean; + throw new InstanceNotFoundException(String.valueOf(name)); + } + + static RuntimeException newUnsupportedException(String operation) { + return new RuntimeOperationsException( + new UnsupportedOperationException( + operation+": Not supported in this namespace")); + } + + static RuntimeException newIllegalArgumentException(String msg) { + return new RuntimeOperationsException( + new IllegalArgumentException(msg)); + } + +} diff --git a/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java b/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java new file mode 100644 index 000000000..ec53998f5 --- /dev/null +++ b/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java @@ -0,0 +1,414 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.interceptor; + +import com.sun.jmx.mbeanserver.Util; +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import java.util.TreeSet; +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.DynamicMBean; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.NotCompliantMBeanException; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.QueryExp; +import javax.management.ReflectionException; +import javax.management.remote.IdentityMBeanServerForwarder; + +public class SingleMBeanForwarder extends IdentityMBeanServerForwarder { + + private final ObjectName mbeanName; + private DynamicMBean mbean; + + private MBeanServer mbeanMBS = new MBeanServerSupport() { + + @Override + public DynamicMBean getDynamicMBeanFor(ObjectName name) + throws InstanceNotFoundException { + if (mbeanName.equals(name)) { + return mbean; + } else { + throw new InstanceNotFoundException(name.toString()); + } + } + + @Override + protected Set getNames() { + return Collections.singleton(mbeanName); + } + + @Override + public NotificationEmitter getNotificationEmitterFor( + ObjectName name) { + if (mbean instanceof NotificationEmitter) + return (NotificationEmitter) mbean; + return null; + } + + }; + + public SingleMBeanForwarder(ObjectName mbeanName, DynamicMBean mbean) { + this.mbeanName = mbeanName; + setSingleMBean(mbean); + } + + protected void setSingleMBean(DynamicMBean mbean) { + this.mbean = mbean; + } + + @Override + public void addNotificationListener(ObjectName name, ObjectName listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException { + if (mbeanName.equals(name)) + mbeanMBS.addNotificationListener(name, listener, filter, handback); + else + super.addNotificationListener(name, listener, filter, handback); + } + + @Override + public void addNotificationListener(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException { + if (mbeanName.equals(name)) + mbeanMBS.addNotificationListener(name, listener, filter, handback); + else + super.addNotificationListener(name, listener, filter, handback); + } + + @Override + public ObjectInstance createMBean(String className, ObjectName name, + ObjectName loaderName, Object[] params, + String[] signature) + throws ReflectionException, + InstanceAlreadyExistsException, + MBeanRegistrationException, + MBeanException, + NotCompliantMBeanException, + InstanceNotFoundException { + if (mbeanName.equals(name)) + throw new InstanceAlreadyExistsException(mbeanName.toString()); + else + return super.createMBean(className, name, loaderName, params, signature); + } + + @Override + public ObjectInstance createMBean(String className, ObjectName name, + Object[] params, String[] signature) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException { + if (mbeanName.equals(name)) + throw new InstanceAlreadyExistsException(mbeanName.toString()); + return super.createMBean(className, name, params, signature); + } + + @Override + public ObjectInstance createMBean(String className, ObjectName name, + ObjectName loaderName) + throws ReflectionException, + InstanceAlreadyExistsException, + MBeanRegistrationException, + MBeanException, + NotCompliantMBeanException, + InstanceNotFoundException { + if (mbeanName.equals(name)) + throw new InstanceAlreadyExistsException(mbeanName.toString()); + return super.createMBean(className, name, loaderName); + } + + @Override + public ObjectInstance createMBean(String className, ObjectName name) + throws ReflectionException, + InstanceAlreadyExistsException, + MBeanRegistrationException, + MBeanException, + NotCompliantMBeanException { + if (mbeanName.equals(name)) + throw new InstanceAlreadyExistsException(mbeanName.toString()); + return super.createMBean(className, name); + } + + @Override + public Object getAttribute(ObjectName name, String attribute) + throws MBeanException, + AttributeNotFoundException, + InstanceNotFoundException, + ReflectionException { + if (mbeanName.equals(name)) + return mbeanMBS.getAttribute(name, attribute); + else + return super.getAttribute(name, attribute); + } + + @Override + public AttributeList getAttributes(ObjectName name, String[] attributes) + throws InstanceNotFoundException, ReflectionException { + if (mbeanName.equals(name)) + return mbeanMBS.getAttributes(name, attributes); + else + return super.getAttributes(name, attributes); + } + + @Override + public ClassLoader getClassLoader(ObjectName loaderName) + throws InstanceNotFoundException { + if (mbeanName.equals(loaderName)) + return mbeanMBS.getClassLoader(loaderName); + else + return super.getClassLoader(loaderName); + } + + @Override + public ClassLoader getClassLoaderFor(ObjectName name) + throws InstanceNotFoundException { + if (mbeanName.equals(name)) + return mbeanMBS.getClassLoaderFor(name); + else + return super.getClassLoaderFor(name); + } + + @Override + public String[] getDomains() { + TreeSet domainSet = + new TreeSet(Arrays.asList(super.getDomains())); + domainSet.add(mbeanName.getDomain()); + return domainSet.toArray(new String[domainSet.size()]); + } + + @Override + public Integer getMBeanCount() { + Integer count = super.getMBeanCount(); + if (!super.isRegistered(mbeanName)) + count++; + return count; + } + + @Override + public MBeanInfo getMBeanInfo(ObjectName name) + throws InstanceNotFoundException, + IntrospectionException, + ReflectionException { + if (mbeanName.equals(name)) + return mbeanMBS.getMBeanInfo(name); + else + return super.getMBeanInfo(name); + } + + @Override + public ObjectInstance getObjectInstance(ObjectName name) + throws InstanceNotFoundException { + if (mbeanName.equals(name)) + return mbeanMBS.getObjectInstance(name); + else + return super.getObjectInstance(name); + } + + @Override + public Object invoke(ObjectName name, String operationName, Object[] params, + String[] signature) + throws InstanceNotFoundException, + MBeanException, + ReflectionException { + if (mbeanName.equals(name)) + return mbeanMBS.invoke(name, operationName, params, signature); + else + return super.invoke(name, operationName, params, signature); + } + + @Override + public boolean isInstanceOf(ObjectName name, String className) + throws InstanceNotFoundException { + if (mbeanName.equals(name)) + return mbeanMBS.isInstanceOf(name, className); + else + return super.isInstanceOf(name, className); + } + + @Override + public boolean isRegistered(ObjectName name) { + if (mbeanName.equals(name)) + return true; + else + return super.isRegistered(name); + } + + /** + * This is a ugly hack. Although jmx.context//*:* matches jmx.context//:* + * queryNames(jmx.context//*:*,null) must not return jmx.context//:* + * @param pattern the pattern to match against. must not be null. + * @return true if mbeanName can be included, false if it must not. + */ + private boolean applies(ObjectName pattern) { + // we know pattern is not null. + if (!pattern.apply(mbeanName)) + return false; + +// final String dompat = pattern.getDomain(); +// if (!dompat.contains(JMXNamespaces.NAMESPACE_SEPARATOR)) +// return true; // We already checked that patterns apply. +// +// if (mbeanName.getDomain().endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) { +// // only matches if pattern ends with // +// return dompat.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR); +// } + + // should not come here, unless mbeanName contains a // in the + // middle of its domain, which would be weird. + // let query on mbeanMBS proceed and take care of that. + // + return true; + } + + @Override + public Set queryMBeans(ObjectName name, QueryExp query) { + Set names = super.queryMBeans(name, query); + if (name == null || applies(name) ) { + // Don't assume mbs.queryNames returns a writable set. + names = Util.cloneSet(names); + names.addAll(mbeanMBS.queryMBeans(name, query)); + } + return names; + } + + @Override + public Set queryNames(ObjectName name, QueryExp query) { + Set names = super.queryNames(name, query); + if (name == null || applies(name)) { + // Don't assume mbs.queryNames returns a writable set. + names = Util.cloneSet(names); + names.addAll(mbeanMBS.queryNames(name, query)); + } + return names; + } + + + @Override + public ObjectInstance registerMBean(Object object, ObjectName name) + throws InstanceAlreadyExistsException, + MBeanRegistrationException, + NotCompliantMBeanException { + if (mbeanName.equals(name)) + throw new InstanceAlreadyExistsException(mbeanName.toString()); + else + return super.registerMBean(object, name); + } + + @Override + public void removeNotificationListener(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, + ListenerNotFoundException { + if (mbeanName.equals(name)) + mbeanMBS.removeNotificationListener(name, listener, filter, handback); + else + super.removeNotificationListener(name, listener, filter, handback); + } + + @Override + public void removeNotificationListener(ObjectName name, + NotificationListener listener) + throws InstanceNotFoundException, ListenerNotFoundException { + if (mbeanName.equals(name)) + mbeanMBS.removeNotificationListener(name, listener); + else + super.removeNotificationListener(name, listener); + } + + @Override + public void removeNotificationListener(ObjectName name, ObjectName listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, + ListenerNotFoundException { + if (mbeanName.equals(name)) + mbeanMBS.removeNotificationListener(name, listener, filter, handback); + else + super.removeNotificationListener(name, listener, filter, handback); + } + + @Override + public void removeNotificationListener(ObjectName name, ObjectName listener) + throws InstanceNotFoundException, ListenerNotFoundException { + if (mbeanName.equals(name)) + mbeanMBS.removeNotificationListener(name, listener); + else + super.removeNotificationListener(name, listener); + } + + @Override + public void setAttribute(ObjectName name, Attribute attribute) + throws InstanceNotFoundException, + AttributeNotFoundException, + InvalidAttributeValueException, + MBeanException, + ReflectionException { + if (mbeanName.equals(name)) + mbeanMBS.setAttribute(name, attribute); + else + super.setAttribute(name, attribute); + } + + @Override + public AttributeList setAttributes(ObjectName name, + AttributeList attributes) + throws InstanceNotFoundException, ReflectionException { + if (mbeanName.equals(name)) + return mbeanMBS.setAttributes(name, attributes); + else + return super.setAttributes(name, attributes); + } + + @Override + public void unregisterMBean(ObjectName name) + throws InstanceNotFoundException, + MBeanRegistrationException { + if (mbeanName.equals(name)) + mbeanMBS.unregisterMBean(name); + else + super.unregisterMBean(name); + } +} diff --git a/src/share/classes/com/sun/jmx/interceptor/package.html b/src/share/classes/com/sun/jmx/interceptor/package.html index 854436a94..ad2163aa0 100644 --- a/src/share/classes/com/sun/jmx/interceptor/package.html +++ b/src/share/classes/com/sun/jmx/interceptor/package.html @@ -29,5 +29,8 @@ have any questions. Provides specific classes to Sun JMX Reference Implementation. +

+ This API is a Sun internal API and is subject to changes without notice. +

diff --git a/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java b/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java index 4831134f6..e84e043d3 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java @@ -172,7 +172,7 @@ public class MBeanInjector { * reference. * * So we accept a Field if it has a @Resource annotation and either - * (a) its type is ObjectName or a subclass and its @Resource type is + * (a) its type is exactly ObjectName and its @Resource type is * compatible with ObjectName (e.g. it is Object); or * (b) its type is compatible with ObjectName and its @Resource type * is exactly ObjectName. Fields that meet these criteria will not diff --git a/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java b/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java index d4f3123fb..20a776534 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java @@ -25,7 +25,6 @@ package com.sun.jmx.mbeanserver; -import static com.sun.jmx.mbeanserver.Util.*; import javax.management.Attribute; import javax.management.AttributeList; diff --git a/src/share/classes/com/sun/jmx/mbeanserver/PerThreadGroupPool.java b/src/share/classes/com/sun/jmx/mbeanserver/PerThreadGroupPool.java new file mode 100644 index 000000000..6fce0b871 --- /dev/null +++ b/src/share/classes/com/sun/jmx/mbeanserver/PerThreadGroupPool.java @@ -0,0 +1,71 @@ +/* + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.mbeanserver; + +import java.lang.ref.WeakReference; +import java.util.concurrent.ThreadPoolExecutor; + +/** + *

A factory for ThreadPoolExecutor objects that allows the same object to + * be shared by all users of the factory that are in the same ThreadGroup.

+ */ +// We return a ThreadPoolExecutor rather than the more general ExecutorService +// because we need to be able to call allowCoreThreadTimeout so that threads in +// the pool will eventually be destroyed when the pool is no longer in use. +// Otherwise these threads would keep the ThreadGroup alive forever. +public class PerThreadGroupPool { + private final WeakIdentityHashMap> map = + WeakIdentityHashMap.make(); + + public static interface Create { + public T createThreadPool(ThreadGroup group); + } + + private PerThreadGroupPool() {} + + public static PerThreadGroupPool make() { + return new PerThreadGroupPool(); + } + + public synchronized T getThreadPoolExecutor(Create create) { + // Find out if there's already an existing executor for the calling + // thread and reuse it. Otherwise, create a new one and store it in + // the executors map. If there is a SecurityManager, the group of + // System.getSecurityManager() is used, else the group of the calling + // thread. + SecurityManager s = System.getSecurityManager(); + ThreadGroup group = (s != null) ? s.getThreadGroup() : + Thread.currentThread().getThreadGroup(); + WeakReference wr = map.get(group); + T executor = (wr == null) ? null : wr.get(); + if (executor == null) { + executor = create.createThreadPool(group); + executor.allowCoreThreadTimeOut(true); + map.put(group, new WeakReference(executor)); + } + return executor; + } +} diff --git a/src/share/classes/com/sun/jmx/mbeanserver/Util.java b/src/share/classes/com/sun/jmx/mbeanserver/Util.java index af427da28..b0f4f1dbf 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/Util.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/Util.java @@ -38,10 +38,13 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; +import java.util.SortedSet; import java.util.TreeMap; +import java.util.TreeSet; import java.util.WeakHashMap; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; +import javax.management.loading.ClassLoaderRepository; public class Util { static Map newMap() { @@ -142,4 +145,97 @@ public class Util { return hash; } + /** + * Filters a set of ObjectName according to a given pattern. + * + * @param pattern the pattern that the returned names must match. + * @param all the set of names to filter. + * @return a set of ObjectName from which non matching names + * have been removed. + */ + public static Set filterMatchingNames(ObjectName pattern, + Set all) { + // If no pattern, just return all names + if (pattern == null + || all.isEmpty() + || ObjectName.WILDCARD.equals(pattern)) + return all; + + // If there's a pattern, do the matching. + final Set res = equivalentEmptySet(all); + for (ObjectName n : all) if (pattern.apply(n)) res.add(n); + return res; + } + + /** + * An abstract ClassLoaderRepository that contains a single class loader. + **/ + private final static class SingleClassLoaderRepository + implements ClassLoaderRepository { + private final ClassLoader singleLoader; + + SingleClassLoaderRepository(ClassLoader loader) { + this.singleLoader = loader; + } + + ClassLoader getSingleClassLoader() { + return singleLoader; + } + + private Class loadClass(String className, ClassLoader loader) + throws ClassNotFoundException { + return Class.forName(className, false, loader); + } + + public Class loadClass(String className) + throws ClassNotFoundException { + return loadClass(className, getSingleClassLoader()); + } + + public Class loadClassWithout(ClassLoader exclude, + String className) throws ClassNotFoundException { + final ClassLoader loader = getSingleClassLoader(); + if (exclude != null && exclude.equals(loader)) + throw new ClassNotFoundException(className); + return loadClass(className, loader); + } + + public Class loadClassBefore(ClassLoader stop, String className) + throws ClassNotFoundException { + return loadClassWithout(stop, className); + } + } + + /** + * Returns a ClassLoaderRepository that contains a single class loader. + * @param loader the class loader contained in the returned repository. + * @return a ClassLoaderRepository that contains the single loader. + */ + public static ClassLoaderRepository getSingleClassLoaderRepository( + final ClassLoader loader) { + return new SingleClassLoaderRepository(loader); + } + + public static Set cloneSet(Set set) { + if (set instanceof SortedSet) { + @SuppressWarnings("unchecked") + SortedSet sset = (SortedSet) set; + set = new TreeSet(sset.comparator()); + set.addAll(sset); + } else + set = new HashSet(set); + return set; + } + + public static Set equivalentEmptySet(Set set) { + if (set instanceof SortedSet) { + @SuppressWarnings("unchecked") + SortedSet sset = (SortedSet) set; + set = new TreeSet(sset.comparator()); + } else if (set != null) { + set = new HashSet(set.size()); + } else + set = new HashSet(); + return set; + } } diff --git a/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java b/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java index a37b75395..b2ceb2fc1 100644 --- a/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java +++ b/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java @@ -576,6 +576,7 @@ public abstract class ClientNotifForwarder { int notFoundCount = 0; NotificationResult result = null; + long firstEarliest = -1; while (result == null && !shouldStop()) { NotificationResult nr; @@ -598,6 +599,8 @@ public abstract class ClientNotifForwarder { return null; startSequenceNumber = nr.getNextSequenceNumber(); + if (firstEarliest < 0) + firstEarliest = nr.getEarliestSequenceNumber(); try { // 1 notif to skip possible missing class @@ -628,6 +631,17 @@ public abstract class ClientNotifForwarder { (notFoundCount == 1 ? "" : "s") + " because classes were missing locally"; lostNotifs(msg, notFoundCount); + // Even if result.getEarliestSequenceNumber() is now greater than + // it was initially, meaning some notifs have been dropped + // from the buffer, we don't want the caller to see that + // because it is then likely to renotify about the lost notifs. + // So we put back the first value of earliestSequenceNumber + // that we saw. + if (result != null) { + result = new NotificationResult( + firstEarliest, result.getNextSequenceNumber(), + result.getTargetedNotifications()); + } } return result; diff --git a/src/share/classes/com/sun/jmx/remote/internal/ProxyInputStream.java b/src/share/classes/com/sun/jmx/remote/internal/ProxyInputStream.java index cdcdd6159..ceb6cef7d 100644 --- a/src/share/classes/com/sun/jmx/remote/internal/ProxyInputStream.java +++ b/src/share/classes/com/sun/jmx/remote/internal/ProxyInputStream.java @@ -33,10 +33,8 @@ import org.omg.CORBA.Any; import org.omg.CORBA.Context; import org.omg.CORBA.NO_IMPLEMENT; import org.omg.CORBA.ORB; -import org.omg.CORBA.Principal; import org.omg.CORBA.TypeCode; import org.omg.CORBA.portable.BoxedValueHelper; -import org.omg.CORBA_2_3.portable.InputStream; @SuppressWarnings("deprecation") public class ProxyInputStream extends org.omg.CORBA_2_3.portable.InputStream { @@ -160,54 +158,71 @@ public class ProxyInputStream extends org.omg.CORBA_2_3.portable.InputStream { return in.read_any(); } - public Principal read_Principal() { + /** + * @deprecated + */ + @Override + @Deprecated + public org.omg.CORBA.Principal read_Principal() { return in.read_Principal(); } + @Override public int read() throws IOException { return in.read(); } + @Override public BigDecimal read_fixed() { return in.read_fixed(); } + @Override public Context read_Context() { return in.read_Context(); } + @Override public org.omg.CORBA.Object read_Object(java.lang.Class clz) { return in.read_Object(clz); } + @Override public ORB orb() { return in.orb(); } + @Override public Serializable read_value() { return narrow().read_value(); } + @Override public Serializable read_value(Class clz) { return narrow().read_value(clz); } + @Override public Serializable read_value(BoxedValueHelper factory) { return narrow().read_value(factory); } + @Override public Serializable read_value(String rep_id) { return narrow().read_value(rep_id); } + @Override public Serializable read_value(Serializable value) { return narrow().read_value(value); } + @Override public Object read_abstract_interface() { return narrow().read_abstract_interface(); } + @Override public Object read_abstract_interface(Class clz) { return narrow().read_abstract_interface(clz); } diff --git a/src/share/classes/com/sun/jmx/remote/internal/ProxyRef.java b/src/share/classes/com/sun/jmx/remote/internal/ProxyRef.java index 3c16e9e7f..1a4478a60 100644 --- a/src/share/classes/com/sun/jmx/remote/internal/ProxyRef.java +++ b/src/share/classes/com/sun/jmx/remote/internal/ProxyRef.java @@ -31,8 +31,6 @@ import java.io.ObjectOutput; import java.lang.reflect.Method; import java.rmi.Remote; import java.rmi.RemoteException; -import java.rmi.server.Operation; -import java.rmi.server.RemoteCall; import java.rmi.server.RemoteObject; import java.rmi.server.RemoteRef; @@ -54,7 +52,11 @@ public class ProxyRef implements RemoteRef { ref.writeExternal(out); } - public void invoke(RemoteCall call) throws Exception { + /** + * @deprecated + */ + @Deprecated + public void invoke(java.rmi.server.RemoteCall call) throws Exception { ref.invoke(call); } @@ -63,7 +65,11 @@ public class ProxyRef implements RemoteRef { return ref.invoke(obj, method, params, opnum); } - public void done(RemoteCall call) throws RemoteException { + /** + * @deprecated + */ + @Deprecated + public void done(java.rmi.server.RemoteCall call) throws RemoteException { ref.done(call); } @@ -71,7 +77,12 @@ public class ProxyRef implements RemoteRef { return ref.getRefClass(out); } - public RemoteCall newCall(RemoteObject obj, Operation[] op, int opnum, + /** + * @deprecated + */ + @Deprecated + public java.rmi.server.RemoteCall newCall(RemoteObject obj, + java.rmi.server.Operation[] op, int opnum, long hash) throws RemoteException { return ref.newCall(obj, op, opnum, hash); } diff --git a/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java b/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java index ae84f4b9d..2a140c2fe 100644 --- a/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java +++ b/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java @@ -25,16 +25,16 @@ package com.sun.jmx.remote.internal; +import com.sun.jmx.mbeanserver.Util; import com.sun.jmx.remote.security.NotificationAccessController; import com.sun.jmx.remote.util.ClassLogger; import com.sun.jmx.remote.util.EnvHelp; import java.io.IOException; import java.security.AccessControlContext; import java.security.AccessController; +import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -67,9 +67,9 @@ public class ServerNotifForwarder { connectionTimeout = EnvHelp.getServerConnectionTimeout(env); checkNotificationEmission = EnvHelp.computeBooleanFromString( env, - "jmx.remote.x.check.notification.emission"); - notificationAccessController = (NotificationAccessController) - env.get("com.sun.jmx.remote.notification.access.controller"); + "jmx.remote.x.check.notification.emission",false); + notificationAccessController = + EnvHelp.getNotificationAccessController(env); } public Integer addNotificationListener(final ObjectName name, @@ -88,9 +88,7 @@ public class ServerNotifForwarder { checkMBeanPermission(name, "addNotificationListener"); if (notificationAccessController != null) { notificationAccessController.addNotificationListener( - connectionId, - name, - Subject.getSubject(AccessController.getContext())); + connectionId, name, getSubject()); } try { boolean instanceOf = @@ -160,9 +158,7 @@ public class ServerNotifForwarder { checkMBeanPermission(name, "removeNotificationListener"); if (notificationAccessController != null) { notificationAccessController.removeNotificationListener( - connectionId, - name, - Subject.getSubject(AccessController.getContext())); + connectionId, name, getSubject()); } Exception re = null; @@ -312,6 +308,10 @@ public class ServerNotifForwarder { // PRIVATE METHODS //---------------- + private Subject getSubject() { + return Subject.getSubject(AccessController.getContext()); + } + private void checkState() throws IOException { synchronized(terminationLock) { if (terminated) { @@ -332,7 +332,13 @@ public class ServerNotifForwarder { */ private void checkMBeanPermission(final ObjectName name, final String actions) - throws InstanceNotFoundException, SecurityException { + throws InstanceNotFoundException, SecurityException { + checkMBeanPermission(mbeanServer, name, actions); + } + + public static void checkMBeanPermission( + final MBeanServer mbs, final ObjectName name, final String actions) + throws InstanceNotFoundException, SecurityException { SecurityManager sm = System.getSecurityManager(); if (sm != null) { AccessControlContext acc = AccessController.getContext(); @@ -342,7 +348,7 @@ public class ServerNotifForwarder { new PrivilegedExceptionAction() { public ObjectInstance run() throws InstanceNotFoundException { - return mbeanServer.getObjectInstance(name); + return mbs.getObjectInstance(name); } }); } catch (PrivilegedActionException e) { @@ -364,14 +370,12 @@ public class ServerNotifForwarder { TargetedNotification tn) { try { if (checkNotificationEmission) { - checkMBeanPermission(name, "addNotificationListener"); + checkMBeanPermission( + name, "addNotificationListener"); } if (notificationAccessController != null) { notificationAccessController.fetchNotification( - connectionId, - name, - tn.getNotification(), - Subject.getSubject(AccessController.getContext())); + connectionId, name, tn.getNotification(), getSubject()); } return true; } catch (SecurityException e) { diff --git a/src/share/classes/com/sun/jmx/remote/security/FileLoginModule.java b/src/share/classes/com/sun/jmx/remote/security/FileLoginModule.java index 47f3e07ab..a901a19c8 100644 --- a/src/share/classes/com/sun/jmx/remote/security/FileLoginModule.java +++ b/src/share/classes/com/sun/jmx/remote/security/FileLoginModule.java @@ -25,6 +25,7 @@ package com.sun.jmx.remote.security; +import com.sun.jmx.mbeanserver.GetPropertyAction; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; @@ -47,8 +48,6 @@ import com.sun.jmx.remote.util.ClassLogger; import com.sun.jmx.remote.util.EnvHelp; import sun.management.jmxremote.ConnectorBootstrap; -import sun.security.action.GetPropertyAction; - /** * This {@link LoginModule} performs file-based authentication. * @@ -479,7 +478,7 @@ public class FileLoginModule implements LoginModule { if (userSuppliedPasswordFile || hasJavaHomePermission) { throw e; } else { - FilePermission fp = + final FilePermission fp = new FilePermission(passwordFileDisplayName, "read"); AccessControlException ace = new AccessControlException( "access denied " + fp.toString()); @@ -488,10 +487,13 @@ public class FileLoginModule implements LoginModule { } } try { - BufferedInputStream bis = new BufferedInputStream(fis); - userCredentials = new Properties(); - userCredentials.load(bis); - bis.close(); + final BufferedInputStream bis = new BufferedInputStream(fis); + try { + userCredentials = new Properties(); + userCredentials.load(bis); + } finally { + bis.close(); + } } finally { fis.close(); } diff --git a/src/share/classes/com/sun/jmx/remote/util/EnvHelp.java b/src/share/classes/com/sun/jmx/remote/util/EnvHelp.java index 219559ea5..8b99cf198 100644 --- a/src/share/classes/com/sun/jmx/remote/util/EnvHelp.java +++ b/src/share/classes/com/sun/jmx/remote/util/EnvHelp.java @@ -40,9 +40,6 @@ import java.util.TreeMap; import java.util.TreeSet; import java.security.AccessController; -import java.security.PrivilegedAction; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import javax.management.ObjectName; import javax.management.MBeanServer; @@ -50,6 +47,9 @@ import javax.management.InstanceNotFoundException; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXConnectorServerFactory; import com.sun.jmx.mbeanserver.GetPropertyAction; +import com.sun.jmx.remote.security.NotificationAccessController; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorServer; public class EnvHelp { @@ -346,7 +346,24 @@ public class EnvHelp { */ public static long getFetchTimeout(Map env) { return getIntegerAttribute(env, FETCH_TIMEOUT, 60000L, 0, - Long.MAX_VALUE); + Long.MAX_VALUE); + } + + /** + *

Name of the attribute that specifies an object that will check + * accesses to add/removeNotificationListener and also attempts to + * receive notifications. The value associated with this attribute + * should be a NotificationAccessController object. + * The default value is null.

+ * This field is not public because of its com.sun dependency. + */ + public static final String NOTIF_ACCESS_CONTROLLER = + "com.sun.jmx.remote.notification.access.controller"; + + public static NotificationAccessController getNotificationAccessController( + Map env) { + return (env == null) ? null : + (NotificationAccessController) env.get(NOTIF_ACCESS_CONTROLLER); } /** @@ -470,24 +487,24 @@ public class EnvHelp { } /** - The value of this attribute, if present, is a string specifying - what other attributes should not appear in - JMXConnectorServer.getAttributes(). It is a space-separated - list of attribute patterns, where each pattern is either an - attribute name, or an attribute prefix followed by a "*" - character. The "*" has no special significance anywhere except - at the end of a pattern. By default, this list is added to the - list defined by {@link #DEFAULT_HIDDEN_ATTRIBUTES} (which - uses the same format). If the value of this attribute begins - with an "=", then the remainder of the string defines the - complete list of attribute patterns. + * The value of this attribute, if present, is a string specifying + * what other attributes should not appear in + * JMXConnectorServer.getAttributes(). It is a space-separated + * list of attribute patterns, where each pattern is either an + * attribute name, or an attribute prefix followed by a "*" + * character. The "*" has no special significance anywhere except + * at the end of a pattern. By default, this list is added to the + * list defined by {@link #DEFAULT_HIDDEN_ATTRIBUTES} (which + * uses the same format). If the value of this attribute begins + * with an "=", then the remainder of the string defines the + * complete list of attribute patterns. */ public static final String HIDDEN_ATTRIBUTES = "jmx.remote.x.hidden.attributes"; /** - Default list of attributes not to show. - @see #HIDDEN_ATTRIBUTES + * Default list of attributes not to show. + * @see #HIDDEN_ATTRIBUTES */ /* This list is copied directly from the spec, plus java.naming.security.*. Most of the attributes here would have @@ -651,6 +668,8 @@ public class EnvHelp { * @param env the environment map. * @param prop the name of the property in the environment map whose * returned string value must be converted into a boolean value. + * @param systemProperty if true, consult a system property of the + * same name if there is no entry in the environment map. * * @return *
    @@ -671,16 +690,73 @@ public class EnvHelp { * @throws ClassCastException if {@code env.get(prop)} cannot be cast * to {@code String}. */ - public static boolean computeBooleanFromString(Map env, String prop) - throws IllegalArgumentException, ClassCastException { + public static boolean computeBooleanFromString( + Map env, String prop, boolean systemProperty) { + + if (env == null) + throw new IllegalArgumentException("env map cannot be null"); + + // returns a default value of 'false' if no property is found... + return computeBooleanFromString(env,prop,systemProperty,false); + } + + /** + * Computes a boolean value from a string value retrieved from a + * property in the given map. + * + * @param env the environment map. + * @param prop the name of the property in the environment map whose + * returned string value must be converted into a boolean value. + * @param systemProperty if true, consult a system property of the + * same name if there is no entry in the environment map. + * @param defaultValue a default value to return in case no property + * was defined. + * + * @return + *
      + *
    • {@code defaultValue} if {@code env.get(prop)} is {@code null} + * and {@code systemProperty} is {@code false}
    • + *
    • {@code defaultValue} if {@code env.get(prop)} is {@code null} + * and {@code systemProperty} is {@code true} and + * {@code System.getProperty(prop)} is {@code null}
    • + *
    • {@code false} if {@code env.get(prop)} is {@code null} + * and {@code systemProperty} is {@code true} and + * {@code System.getProperty(prop).equalsIgnoreCase("false")} + * is {@code true}
    • + *
    • {@code true} if {@code env.get(prop)} is {@code null} + * and {@code systemProperty} is {@code true} and + * {@code System.getProperty(prop).equalsIgnoreCase("true")} + * is {@code true}
    • + *
    • {@code false} if + * {@code ((String)env.get(prop)).equalsIgnoreCase("false")} + * is {@code true}
    • + *
    • {@code true} if + * {@code ((String)env.get(prop)).equalsIgnoreCase("true")} + * is {@code true}
    • + *
    + * + * @throws IllegalArgumentException if {@code env} is {@code null} or + * {@code env.get(prop)} is not {@code null} and + * {@code ((String)env.get(prop)).equalsIgnoreCase("false")} and + * {@code ((String)env.get(prop)).equalsIgnoreCase("true")} are + * {@code false}. + * @throws ClassCastException if {@code env.get(prop)} cannot be cast + * to {@code String}. + */ + public static boolean computeBooleanFromString( + Map env, String prop, boolean systemProperty, boolean defaultValue) { if (env == null) throw new IllegalArgumentException("env map cannot be null"); String stringBoolean = (String) env.get(prop); + if (stringBoolean == null && systemProperty) { + stringBoolean = + AccessController.doPrivileged(new GetPropertyAction(prop)); + } if (stringBoolean == null) - return false; + return defaultValue; else if (stringBoolean.equalsIgnoreCase("true")) return true; else if (stringBoolean.equalsIgnoreCase("false")) @@ -703,6 +779,65 @@ public class EnvHelp { return new Hashtable(m); } + /** + * Returns true if the parameter JMXConnector.USE_EVENT_SERVICE is set to a + * String equals "true" by ignoring case in the map or in the System. + */ + public static boolean eventServiceEnabled(Map env) { + return computeBooleanFromString(env, JMXConnector.USE_EVENT_SERVICE, true); + } + + /** + * Returns true if the parameter JMXConnectorServer.DELEGATE_TO_EVENT_SERVICE + * is set to a String equals "true" (ignores case). + * If the property DELEGATE_TO_EVENT_SERVICE is not set, returns + * a default value of "true". + */ + public static boolean delegateToEventService(Map env) { + return computeBooleanFromString(env, + JMXConnectorServer.DELEGATE_TO_EVENT_SERVICE, true, true); + } + +// /** +// *

    Name of the attribute that specifies an EventRelay object to use. +// */ +// public static final String EVENT_RELAY = +// "jmx.remote.x.event.relay"; +// +// +// /** +// * Returns an EventRelay object. The default one is FetchingEventRelay. +// * If {@code EVENT_RELAY} is specified in {@code env} as a key, +// * its value will be returned as an EventRelay object, if the value is +// * not of type {@code EventRelay}, the default {@code FetchingEventRelay} +// * will be returned. +// * If {@code EVENT_RELAY} is not specified but {@code ENABLE_EVENT_RELAY} +// * is specified as a key and its value is , the default {@code FetchingEventRelay} +// * will be returned. +// */ +// public static EventRelay getEventRelay(Map env) { +// Map info = env == null ? +// Collections.EMPTY_MAP : env; +// +// Object o = env.get(EVENT_RELAY); +// if (o instanceof EventRelay) { +// return (EventRelay)o; +// } else if (o != null) { +// logger.warning("getEventRelay", +// "The user specified object is not an EventRelay object, " + +// "using the default class FetchingEventRelay."); +// +// return new FetchingEventRelay(); +// } +// +// if (enableEventRelay(env)) { +// return new FetchingEventRelay(); +// } +// +// return null; +// } + + private static final class SinkOutputStream extends OutputStream { public void write(byte[] b, int off, int len) {} public void write(int b) {} diff --git a/src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java b/src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java new file mode 100644 index 000000000..b3520a408 --- /dev/null +++ b/src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java @@ -0,0 +1,471 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.remote.util; + +import com.sun.jmx.event.EventClientFactory; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.management.MBeanServerConnection; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventClient; +import javax.management.event.EventClientDelegate; + +/** + * Class EventClientConnection - a {@link Proxy} that wraps an + * {@link MBeanServerConnection} and an {@link EventClient}. + * All methods are routed to the underlying {@code MBeanServerConnection}, + * except add/remove notification listeners which are routed to the + * {@code EventClient}. + * The caller only sees an {@code MBeanServerConnection} which uses an + * {@code EventClient} behind the scenes. + * + * @author Sun Microsystems, Inc. + */ +public class EventClientConnection implements InvocationHandler, + EventClientFactory { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(EventClientConnection.class.getName()); + + private static final String NAMESPACE_SEPARATOR = "//"; + private static final int NAMESPACE_SEPARATOR_LENGTH = + NAMESPACE_SEPARATOR.length(); + + /** + * Creates a new {@code EventClientConnection}. + * @param connection The underlying MBeanServerConnection. + */ + public EventClientConnection(MBeanServerConnection connection) { + this(connection,null); + } + + /** + * Creates a new {@code EventClientConnection}. + * @param connection The underlying MBeanServerConnection. + * @param eventClientFactory a factory object that will be invoked + * to create an {@link EventClient} when needed. + * The {@code EventClient} is created lazily, when it is needed + * for the first time. If null, a default factory will be used + * (see {@link #createEventClient}). + */ + public EventClientConnection(MBeanServerConnection connection, + Callable eventClientFactory) { + + if (connection == null) { + throw new IllegalArgumentException("Null connection"); + } + this.connection = connection; + if (eventClientFactory == null) { + eventClientFactory = new Callable() { + public final EventClient call() throws Exception { + return createEventClient(EventClientConnection.this.connection); + } + }; + } + this.eventClientFactory = eventClientFactory; + this.lock = new ReentrantLock(); + } + + /** + *

    The MBean server connection through which the methods of + * a proxy using this handler are forwarded.

    + * + * @return the MBean server connection. + * + * @since 1.6 + */ + public MBeanServerConnection getMBeanServerConnection() { + return connection; + } + + + + + /** + * Creates a new EventClientConnection proxy instance. + * + * @param The underlying {@code MBeanServerConnection} - which should + * not be using the Event Service itself. + * @param interfaceClass {@code MBeanServerConnection.class}, or a subclass. + * @param eventClientFactory a factory used to create the EventClient. + * If null, a default factory is used (see {@link + * #createEventClient}). + * @return the new proxy instance, which will route add/remove notification + * listener calls through an {@code EventClient}. + * + */ + private static T + newProxyInstance(T connection, + Class interfaceClass, Callable eventClientFactory) { + final InvocationHandler handler = + new EventClientConnection(connection,eventClientFactory); + final Class[] interfaces = + new Class[] {interfaceClass, EventClientFactory.class}; + + Object proxy = + Proxy.newProxyInstance(interfaceClass.getClassLoader(), + interfaces, + handler); + return interfaceClass.cast(proxy); + } + + + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + final String methodName = method.getName(); + + // add/remove notification listener are routed to the EventClient + if (methodName.equals("addNotificationListener") + || methodName.equals("removeNotificationListener")) { + final Class[] sig = method.getParameterTypes(); + if (sig.length>1 && + NotificationListener.class.isAssignableFrom(sig[1])) { + return invokeBroadcasterMethod(proxy,method,args); + } + } + + // subscribe/unsubscribe are also routed to the EventClient. + final Class clazz = method.getDeclaringClass(); + if (clazz.equals(EventClientFactory.class)) { + return invokeEventClientSubscriberMethod(proxy,method,args); + } + + // local or not: equals, toString, hashCode + if (shouldDoLocally(proxy, method)) + return doLocally(proxy, method, args); + + return call(connection,method,args); + } + + // The purpose of this method is to unwrap InvocationTargetException, + // in order to avoid throwing UndeclaredThrowableException for + // declared exceptions. + // + // When calling method.invoke(), any exception thrown by the invoked + // method will be wrapped in InvocationTargetException. If we don't + // unwrap this exception, the proxy will always throw + // UndeclaredThrowableException, even for runtime exceptions. + // + private Object call(final Object obj, final Method m, + final Object[] args) + throws Throwable { + try { + return m.invoke(obj,args); + } catch (InvocationTargetException x) { + final Throwable xx = x.getTargetException(); + if (xx == null) throw x; + else throw xx; + } + } + + /** + * Route add/remove notification listener to the event client. + **/ + private Object invokeBroadcasterMethod(Object proxy, Method method, + Object[] args) throws Exception { + final String methodName = method.getName(); + final int nargs = (args == null) ? 0 : args.length; + + if (nargs < 1) { + final String msg = + "Bad arg count: " + nargs; + throw new IllegalArgumentException(msg); + } + + final ObjectName mbean = (ObjectName) args[0]; + final EventClient client = getEventClient(); + + // Fails if client is null AND the MBean we try to listen to is + // in a subnamespace. We fail here because we know this will not + // work. + // + // Note that if the wrapped MBeanServerConnection points to a an + // earlier agent (JDK 1.6 or earlier), then the EventClient will + // be null (we can't use the event service with earlier JDKs). + // + // In principle a null client indicates that the remote VM is of + // an earlier version, in which case it shouldn't contain any namespace. + // + // So having a null client AND an MBean contained in a namespace is + // clearly an error case. + // + if (client == null) { + final String domain = mbean.getDomain(); + final int index = domain.indexOf(NAMESPACE_SEPARATOR); + if (index > -1 && index < + (domain.length()-NAMESPACE_SEPARATOR_LENGTH)) { + throw new UnsupportedOperationException(method.getName()+ + " on namespace "+domain.substring(0,index+ + NAMESPACE_SEPARATOR_LENGTH)); + } + } + + if (methodName.equals("addNotificationListener")) { + /* The various throws of IllegalArgumentException here + should not happen, since we know what the methods in + NotificationBroadcaster and NotificationEmitter + are. */ + if (nargs != 4) { + final String msg = + "Bad arg count to addNotificationListener: " + nargs; + throw new IllegalArgumentException(msg); + } + /* Other inconsistencies will produce ClassCastException + below. */ + + final NotificationListener listener = (NotificationListener) args[1]; + final NotificationFilter filter = (NotificationFilter) args[2]; + final Object handback = args[3]; + + if (client != null) { + // general case + client.addNotificationListener(mbean,listener,filter,handback); + } else { + // deprecated case. Only works for mbean in local namespace. + connection.addNotificationListener(mbean,listener,filter, + handback); + } + return null; + + } else if (methodName.equals("removeNotificationListener")) { + + /* NullPointerException if method with no args, but that + shouldn't happen because removeNL does have args. */ + NotificationListener listener = (NotificationListener) args[1]; + + switch (nargs) { + case 2: + if (client != null) { + // general case + client.removeNotificationListener(mbean,listener); + } else { + // deprecated case. Only works for mbean in local namespace. + connection.removeNotificationListener(mbean, listener); + } + return null; + + case 4: + NotificationFilter filter = (NotificationFilter) args[2]; + Object handback = args[3]; + if (client != null) { + client.removeNotificationListener(mbean, + listener, + filter, + handback); + } else { + connection.removeNotificationListener(mbean, + listener, + filter, + handback); + } + return null; + + default: + final String msg = + "Bad arg count to removeNotificationListener: " + nargs; + throw new IllegalArgumentException(msg); + } + + } else { + throw new IllegalArgumentException("Bad method name: " + + methodName); + } + } + + private boolean shouldDoLocally(Object proxy, Method method) { + final String methodName = method.getName(); + if ((methodName.equals("hashCode") || methodName.equals("toString")) + && method.getParameterTypes().length == 0 + && isLocal(proxy, method)) + return true; + if (methodName.equals("equals") + && Arrays.equals(method.getParameterTypes(), + new Class[] {Object.class}) + && isLocal(proxy, method)) + return true; + return false; + } + + private Object doLocally(Object proxy, Method method, Object[] args) { + final String methodName = method.getName(); + + if (methodName.equals("equals")) { + + if (this == args[0]) { + return true; + } + + if (!(args[0] instanceof Proxy)) { + return false; + } + + final InvocationHandler ihandler = + Proxy.getInvocationHandler(args[0]); + + if (ihandler == null || + !(ihandler instanceof EventClientConnection)) { + return false; + } + + final EventClientConnection handler = + (EventClientConnection)ihandler; + + return connection.equals(handler.connection) && + proxy.getClass().equals(args[0].getClass()); + } else if (methodName.equals("hashCode")) { + return connection.hashCode(); + } + + throw new RuntimeException("Unexpected method name: " + methodName); + } + + private static boolean isLocal(Object proxy, Method method) { + final Class[] interfaces = proxy.getClass().getInterfaces(); + if(interfaces == null) { + return true; + } + + final String methodName = method.getName(); + final Class[] params = method.getParameterTypes(); + for (Class intf : interfaces) { + try { + intf.getMethod(methodName, params); + return false; // found method in one of our interfaces + } catch (NoSuchMethodException nsme) { + // OK. + } + } + + return true; // did not find in any interface + } + + /** + * Return the EventClient used by this object. Can be null if the + * remote VM is of an earlier JDK version which doesn't have the + * event service.
    + * This method will invoke the event client factory the first time + * it is called. + **/ + public final EventClient getEventClient() { + if (initialized) return client; + try { + if (!lock.tryLock(TRYLOCK_TIMEOUT,TimeUnit.SECONDS)) + throw new IllegalStateException("can't acquire lock"); + try { + client = eventClientFactory.call(); + initialized = true; + } finally { + lock.unlock(); + } + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new IllegalStateException("Can't create EventClient: "+x,x); + } + return client; + } + + /** + * Returns an event client for the wrapped {@code MBeanServerConnection}. + * This is the method invoked by the default event client factory. + * @param connection the wrapped {@code MBeanServerConnection}. + **/ + protected EventClient createEventClient(MBeanServerConnection connection) + throws Exception { + final ObjectName name = + EventClientDelegate.OBJECT_NAME; + if (connection.isRegistered(name)) { + return new EventClient(connection); + } + return null; + } + + /** + * Creates a new {@link MBeanServerConnection} that goes through an + * {@link EventClient} to receive/subscribe to notifications. + * @param connection the underlying {@link MBeanServerConnection}. + * The given connection shouldn't be already + * using an {@code EventClient}. + * @param eventClientFactory a factory object that will be invoked + * to create an {@link EventClient} when needed. + * The {@code EventClient} is created lazily, when it is needed + * for the first time. If null, a default factory will be used + * (see {@link #createEventClient}). + * @return the + **/ + public static MBeanServerConnection getEventConnectionFor( + MBeanServerConnection connection, + Callable eventClientFactory) { + // if c already uses an EventClient no need to create a new one. + // + if (connection instanceof EventClientFactory + && eventClientFactory != null) + throw new IllegalArgumentException("connection already uses EventClient"); + + if (connection instanceof EventClientFactory) + return connection; + + // create a new proxy using an event client. + // + if (LOG.isLoggable(Level.FINE)) + LOG.fine("Creating EventClient for: "+connection); + return newProxyInstance(connection, + MBeanServerConnection.class, + eventClientFactory); + } + + private Object invokeEventClientSubscriberMethod(Object proxy, + Method method, Object[] args) throws Throwable { + return call(this,method,args); + } + + // Maximum lock timeout in seconds. Obviously arbitrary. + // + private final static short TRYLOCK_TIMEOUT = 3; + + private final MBeanServerConnection connection; + private final Callable eventClientFactory; + private final Lock lock; + private volatile EventClient client = null; + private volatile boolean initialized = false; + +} diff --git a/src/share/classes/com/sun/jmx/snmp/tasks/ThreadService.java b/src/share/classes/com/sun/jmx/snmp/tasks/ThreadService.java index 95090412a..544364afb 100644 --- a/src/share/classes/com/sun/jmx/snmp/tasks/ThreadService.java +++ b/src/share/classes/com/sun/jmx/snmp/tasks/ThreadService.java @@ -45,15 +45,9 @@ public class ThreadService implements TaskServer { minThreads = threadNumber; threadList = new ExecutorThread[threadNumber]; -// for (int i=0; i compare = String.CASE_INSENSITIVE_ORDER; String lastName = ""; // also catches illegal null name for (int i = 0; i < names.length; i++) { diff --git a/src/share/classes/javax/management/MBeanServer.java b/src/share/classes/javax/management/MBeanServer.java index 55a096aef..c5a1836b8 100644 --- a/src/share/classes/javax/management/MBeanServer.java +++ b/src/share/classes/javax/management/MBeanServer.java @@ -420,7 +420,13 @@ public interface MBeanServer extends MBeanServerConnection { // doc comment inherited from MBeanServerConnection public String[] getDomains(); - // doc comment inherited from MBeanServerConnection + // doc comment inherited from MBeanServerConnection, plus: + /** + * {@inheritDoc} + * If the source of the notification + * is a reference to an MBean object, the MBean server will replace it + * by that MBean's ObjectName. Otherwise the source is unchanged. + */ public void addNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, diff --git a/src/share/classes/javax/management/MBeanServerConnection.java b/src/share/classes/javax/management/MBeanServerConnection.java index 3f3bc4442..779301491 100644 --- a/src/share/classes/javax/management/MBeanServerConnection.java +++ b/src/share/classes/javax/management/MBeanServerConnection.java @@ -29,6 +29,7 @@ package javax.management; // java import import java.io.IOException; import java.util.Set; +import javax.management.event.NotificationManager; /** @@ -39,7 +40,7 @@ import java.util.Set; * * @since 1.5 */ -public interface MBeanServerConnection { +public interface MBeanServerConnection extends NotificationManager { /** *

    Instantiates and registers an MBean in the MBean server. The * MBean server will use its {@link @@ -676,32 +677,7 @@ public interface MBeanServerConnection { public String[] getDomains() throws IOException; - /** - *

    Adds a listener to a registered MBean.

    - * - *

    A notification emitted by an MBean will be forwarded by the - * MBeanServer to the listener. If the source of the notification - * is a reference to an MBean object, the MBean server will replace it - * by that MBean's ObjectName. Otherwise the source is unchanged. - * - * @param name The name of the MBean on which the listener should - * be added. - * @param listener The listener object which will handle the - * notifications emitted by the registered MBean. - * @param filter The filter object. If filter is null, no - * filtering will be performed before handling notifications. - * @param handback The context to be sent to the listener when a - * notification is emitted. - * - * @exception InstanceNotFoundException The MBean name provided - * does not match any of the registered MBeans. - * @exception IOException A communication problem occurred when - * talking to the MBean server. - * - * @see #removeNotificationListener(ObjectName, NotificationListener) - * @see #removeNotificationListener(ObjectName, NotificationListener, - * NotificationFilter, Object) - */ + // doc inherited from NotificationManager public void addNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, @@ -818,65 +794,13 @@ public interface MBeanServerConnection { throws InstanceNotFoundException, ListenerNotFoundException, IOException; - - /** - *

    Removes a listener from a registered MBean.

    - * - *

    If the listener is registered more than once, perhaps with - * different filters or callbacks, this method will remove all - * those registrations. - * - * @param name The name of the MBean on which the listener should - * be removed. - * @param listener The listener to be removed. - * - * @exception InstanceNotFoundException The MBean name provided - * does not match any of the registered MBeans. - * @exception ListenerNotFoundException The listener is not - * registered in the MBean. - * @exception IOException A communication problem occurred when - * talking to the MBean server. - * - * @see #addNotificationListener(ObjectName, NotificationListener, - * NotificationFilter, Object) - */ + // doc inherited from NotificationManager public void removeNotificationListener(ObjectName name, NotificationListener listener) throws InstanceNotFoundException, ListenerNotFoundException, IOException; - /** - *

    Removes a listener from a registered MBean.

    - * - *

    The MBean must have a listener that exactly matches the - * given listener, filter, and - * handback parameters. If there is more than one - * such listener, only one is removed.

    - * - *

    The filter and handback parameters - * may be null if and only if they are null in a listener to be - * removed.

    - * - * @param name The name of the MBean on which the listener should - * be removed. - * @param listener The listener to be removed. - * @param filter The filter that was specified when the listener - * was added. - * @param handback The handback that was specified when the - * listener was added. - * - * @exception InstanceNotFoundException The MBean name provided - * does not match any of the registered MBeans. - * @exception ListenerNotFoundException The listener is not - * registered in the MBean, or it is not registered with the given - * filter and handback. - * @exception IOException A communication problem occurred when - * talking to the MBean server. - * - * @see #addNotificationListener(ObjectName, NotificationListener, - * NotificationFilter, Object) - * - */ + // doc inherited from NotificationManager public void removeNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, diff --git a/src/share/classes/javax/management/MXBean.java b/src/share/classes/javax/management/MXBean.java index a577fd94f..cf0f70f93 100644 --- a/src/share/classes/javax/management/MXBean.java +++ b/src/share/classes/javax/management/MXBean.java @@ -33,7 +33,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; // remaining imports are for Javadoc -import java.beans.ConstructorProperties; import java.io.InvalidObjectException; import java.lang.management.MemoryUsage; import java.lang.reflect.UndeclaredThrowableException; @@ -865,7 +864,8 @@ public interface ModuleMXBean { J.

  • Otherwise, if J has at least one public - constructor with a {@link ConstructorProperties} annotation, then one + constructor with a {@link java.beans.ConstructorProperties + ConstructorProperties} annotation, then one of those constructors (not necessarily always the same one) will be called to reconstruct an instance of J. Every such annotation must list as many strings as the diff --git a/src/share/classes/javax/management/QueryParser.java b/src/share/classes/javax/management/QueryParser.java index babf05c7a..715f42070 100644 --- a/src/share/classes/javax/management/QueryParser.java +++ b/src/share/classes/javax/management/QueryParser.java @@ -312,7 +312,7 @@ class QueryParser { if (e > 0) ss = s.substring(0, e); ss = ss.replace("0", "").replace(".", ""); - if (!ss.isEmpty()) + if (!ss.equals("")) throw new NumberFormatException("Underflow: " + s); } return d; diff --git a/src/share/classes/javax/management/StringValueExp.java b/src/share/classes/javax/management/StringValueExp.java index 9f2ed4a3b..cc092db38 100644 --- a/src/share/classes/javax/management/StringValueExp.java +++ b/src/share/classes/javax/management/StringValueExp.java @@ -85,6 +85,7 @@ public class StringValueExp implements ValueExp { /* There is no need for this method, because if a query is being evaluated a StringValueExp can only appear inside a QueryExp, and that QueryExp will itself have done setMBeanServer. */ + @Deprecated public void setMBeanServer(MBeanServer s) { } /** diff --git a/src/share/classes/javax/management/event/EventClient.java b/src/share/classes/javax/management/event/EventClient.java new file mode 100644 index 000000000..923ef90cd --- /dev/null +++ b/src/share/classes/javax/management/event/EventClient.java @@ -0,0 +1,1068 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.event; + +import com.sun.jmx.event.DaemonThreadFactory; +import com.sun.jmx.event.LeaseRenewer; +import com.sun.jmx.event.ReceiverBuffer; +import com.sun.jmx.event.RepeatedSingletonJob; +import com.sun.jmx.mbeanserver.PerThreadGroupPool; +import com.sun.jmx.remote.util.ClassLogger; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import javax.management.InstanceNotFoundException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanServerConnection; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; +import javax.management.remote.NotificationResult; +import javax.management.remote.TargetedNotification; + +/** + *

    This class is used to manage its notification listeners on the client + * side in the same way as on the MBean server side. This class needs to work + * with an {@link EventClientDelegateMBean} on the server side.

    + * + *

    A user can specify an {@link EventRelay} object to specify how to receive + * notifications forwarded by the {@link EventClientDelegateMBean}. By default, + * the class {@link FetchingEventRelay} is used.

    + * + *

    A user can specify an {@link java.util.concurrent.Executor Executor} + * to distribute notifications to local listeners. If no executor is + * specified, the thread in the {@link EventRelay} which calls {@link + * EventReceiver#receive EventReceiver.receive} will be reused to distribute + * the notifications (in other words, to call the {@link + * NotificationListener#handleNotification handleNotification} method of the + * appropriate listeners). It is useful to make a separate thread do this + * distribution in some cases. For example, if network communication is slow, + * the forwarding thread can concentrate on communication while, locally, + * the distributing thread distributes the received notifications. Another + * usage is to share a thread pool between many clients, for scalability. + * Note, though, that if the {@code Executor} can create more than one thread + * then it is possible that listeners will see notifications in a different + * order from the order in which they were sent.

    + * + *

    An object of this class sends notifications to listeners added with + * {@link #addEventClientListener}. The {@linkplain Notification#getType() + * type} of each such notification is one of {@link #FAILED}, {@link #NONFATAL}, + * or {@link #NOTIFS_LOST}.

    + * + * @since JMX 2.0 + */ +public class EventClient implements EventConsumer, NotificationManager { + + /** + *

    A notification string type used by an {@code EventClient} object + * to inform a listener added by {@link #addEventClientListener} that + * it failed to get notifications from a remote server, and that it is + * possible that no more notifications will be delivered.

    + * + * @see #addEventClientListener + * @see EventReceiver#failed + */ + public static final String FAILED = "jmx.event.service.failed"; + + /** + *

    Reports that an unexpected exception has been received by the {@link + * EventRelay} object but that it is non-fatal. For example, a notification + * received is not serializable or its class is not found.

    + * + * @see #addEventClientListener + * @see EventReceiver#nonFatal + */ + public static final String NONFATAL = "jmx.event.service.nonfatal"; + + /** + *

    A notification string type used by an {@code EventClient} object to + * inform a listener added by {@code #addEventClientListener} that it + * has detected that notifications have been lost. The {@link + * Notification#getUserData() userData} of the notification is a Long which + * is an upper bound on the number of lost notifications that have just + * been detected.

    + * + * @see #addEventClientListener + */ + public static final String NOTIFS_LOST = "jmx.event.service.notifs.lost"; + + /** + * The default lease time, {@value}, in milliseconds. + * + * @see EventClientDelegateMBean#lease + */ + public static final long DEFAULT_LEASE_TIMEOUT = 300000; + + /** + *

    Constructs a default {@code EventClient} object.

    + * + *

    This object creates a {@link FetchingEventRelay} object to + * receive notifications forwarded by the {@link EventClientDelegateMBean}. + * The {@link EventClientDelegateMBean} that it works with is the + * one registered with the {@linkplain EventClientDelegate#OBJECT_NAME + * default ObjectName}. The thread from the {@link FetchingEventRelay} + * object that fetches the notifications is also used to distribute them. + * + * @param conn An {@link MBeanServerConnection} object used to communicate + * with an {@link EventClientDelegateMBean} MBean. + * + * @throws IllegalArgumentException If {@code conn} is null. + * @throws IOException If an I/O error occurs when communicating with the + * {@code EventClientDelegateMBean}. + */ + public EventClient(MBeanServerConnection conn) throws IOException { + this(EventClientDelegate.getProxy(conn)); + } + + /** + * Constructs an {@code EventClient} object with a specified + * {@link EventClientDelegateMBean}. + * + *

    This object creates a {@link FetchingEventRelay} object to receive + * notifications forwarded by the {@link EventClientDelegateMBean}. The + * thread from the {@link FetchingEventRelay} object that fetches the + * notifications is also used to distribute them. + * + * @param delegate An {@link EventClientDelegateMBean} object to work with. + * + * @throws IllegalArgumentException If {@code delegate} is null. + * @throws IOException If an I/O error occurs when communicating with the + * the {@link EventClientDelegateMBean}. + */ + public EventClient(EventClientDelegateMBean delegate) + throws IOException { + this(delegate, null, null, null, DEFAULT_LEASE_TIMEOUT); + } + + /** + * Constructs an {@code EventClient} object with the specified + * {@link EventClientDelegateMBean}, {@link EventRelay} + * object, and distributing thread. + * + * @param delegate An {@link EventClientDelegateMBean} object to work with. + * Usually, this will be a proxy constructed using + * {@link EventClientDelegate#getProxy}. + * @param eventRelay An object used to receive notifications + * forwarded by the {@link EventClientDelegateMBean}. If {@code null}, a + * {@link FetchingEventRelay} object will be used. + * @param distributingExecutor Used to distribute notifications to local + * listeners. If {@code null}, the thread that calls {@link + * EventReceiver#receive EventReceiver.receive} from the {@link EventRelay} + * object is used. + * @param leaseScheduler An object that will be used to schedule the + * periodic {@linkplain EventClientDelegateMBean#lease lease updates}. + * If {@code null}, a default scheduler will be used. + * @param requestedLeaseTime The lease time used to keep this client alive + * in the {@link EventClientDelegateMBean}. A value of zero is equivalent + * to the {@linkplain #DEFAULT_LEASE_TIMEOUT default value}. + * + * @throws IllegalArgumentException If {@code delegate} is null. + * @throws IOException If an I/O error occurs when communicating with the + * {@link EventClientDelegateMBean}. + */ + public EventClient(EventClientDelegateMBean delegate, + EventRelay eventRelay, + Executor distributingExecutor, + ScheduledExecutorService leaseScheduler, + long requestedLeaseTime) + throws IOException { + if (delegate == null) { + throw new IllegalArgumentException("Null EventClientDelegateMBean"); + } + + if (requestedLeaseTime == 0) + requestedLeaseTime = DEFAULT_LEASE_TIMEOUT; + else if (requestedLeaseTime < 0) { + throw new IllegalArgumentException( + "Negative lease time: " + requestedLeaseTime); + } + + eventClientDelegate = delegate; + + if (eventRelay != null) { + this.eventRelay = eventRelay; + } else { + try { + this.eventRelay = new FetchingEventRelay(delegate); + } catch (IOException ioe) { + throw ioe; + } catch (Exception e) { + // impossible? + final IOException ioee = new IOException(e.toString()); + ioee.initCause(e); + throw ioee; + } + } + + if (distributingExecutor == null) + distributingExecutor = callerExecutor; + this.distributingExecutor = distributingExecutor; + this.dispatchingJob = new DispatchingJob(); + + clientId = this.eventRelay.getClientId(); + + this.requestedLeaseTime = requestedLeaseTime; + if (leaseScheduler == null) + leaseScheduler = defaultLeaseScheduler(); + leaseRenewer = new LeaseRenewer(leaseScheduler, renewLease); + + if (logger.traceOn()) { + logger.trace("init", "New EventClient: "+clientId); + } + } + + private static ScheduledExecutorService defaultLeaseScheduler() { + // The default lease scheduler uses a ScheduledThreadPoolExecutor + // with a maximum of 20 threads. This means that if you have many + // EventClient instances and some of them get blocked (because of an + // unresponsive network, for example), then even the instances that + // are connected to responsive servers may have their leases expire. + // XXX check if the above is true and possibly fix. + PerThreadGroupPool.Create create = + new PerThreadGroupPool.Create() { + public ScheduledThreadPoolExecutor createThreadPool(ThreadGroup group) { + ThreadFactory daemonThreadFactory = new DaemonThreadFactory( + "EventClient lease renewer %d"); + ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor( + 20, daemonThreadFactory); + exec.setKeepAliveTime(3, TimeUnit.SECONDS); + exec.allowCoreThreadTimeOut(true); + return exec; + } + }; + return leaseRenewerThreadPool.getThreadPoolExecutor(create); + + } + + /** + *

    Closes this EventClient, removes all listeners and stops receiving + * notifications.

    + * + *

    This method calls {@link + * EventClientDelegateMBean#removeClient(String)} and {@link + * EventRelay#stop}. Both operations occur even if one of them + * throws an {@code IOException}. + * + * @throws IOException if an I/O error occurs when communicating with + * {@link EventClientDelegateMBean}, or if {@link EventRelay#stop} + * throws an {@code IOException}. + */ + public void close() throws IOException { + if (logger.traceOn()) { + logger.trace("close", clientId); + } + + synchronized(listenerInfoMap) { + if (closed) { + return; + } + + closed = true; + listenerInfoMap.clear(); + } + + if (leaseRenewer != null) + leaseRenewer.close(); + + IOException ioe = null; + try { + eventRelay.stop(); + } catch (IOException e) { + ioe = e; + logger.debug("close", "EventRelay.stop", e); + } + + try { + eventClientDelegate.removeClient(clientId); + } catch (Exception e) { + if (e instanceof IOException) + ioe = (IOException) e; + else + ioe = new IOException(e); + logger.debug("close", + "Got exception when removing "+clientId, e); + } + + if (ioe != null) + throw ioe; + } + + /** + *

    Determine if this {@code EventClient} is closed.

    + * + * @return True if the {@code EventClient} is closed. + */ + public boolean closed() { + return closed; + } + + /** + *

    Return the {@link EventRelay} associated with this + * {@code EventClient}.

    + * + * @return The {@link EventRelay} object used. + */ + public EventRelay getEventRelay() { + return eventRelay; + } + + /** + *

    Return the lease time that this {@code EventClient} requests + * on every lease renewal.

    + * + * @return The requested lease time. + * + * @see EventClientDelegateMBean#lease + */ + public long getRequestedLeaseTime() { + return requestedLeaseTime; + } + + /** + * @see javax.management.MBeanServerConnection#addNotificationListener( + * ObjectName, NotificationListener, NotificationFilter, Object). + */ + public void addNotificationListener(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, IOException { + if (logger.traceOn()) { + logger.trace("addNotificationListener", ""); + } + + checkState(); + + Integer listenerId; + try { + listenerId = + eventClientDelegate.addListener(clientId, name, filter); + } catch (EventClientNotFoundException ecnfe) { + final IOException ioe = new IOException(); + ioe.initCause(ecnfe); + throw ioe; + } + + synchronized(listenerInfoMap) { + listenerInfoMap.put(listenerId, new ListenerInfo( + name, + listener, + filter, + handback, + false)); + } + + startListening(); + } + + /** + * @see javax.management.MBeanServerConnection#removeNotificationListener( + * ObjectName, NotificationListener). + */ + public void removeNotificationListener(ObjectName name, + NotificationListener listener) + throws InstanceNotFoundException, + ListenerNotFoundException, + IOException { + if (logger.traceOn()) { + logger.trace("removeNotificationListener", ""); + } + checkState(); + + for (Integer id : getListenerInfo(name, listener, false)) { + removeListener(id); + } + } + + /** + * @see javax.management.MBeanServerConnection#removeNotificationListener( + * ObjectName, NotificationListener, NotificationFilter, Object). + */ + public void removeNotificationListener(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, + ListenerNotFoundException, + IOException { + if (logger.traceOn()) { + logger.trace("removeNotificationListener", "with all arguments."); + } + checkState(); + final Integer listenerId = + getListenerInfo(name, listener, filter, handback, false); + + removeListener(listenerId); + } + + /** + * @see javax.management.event.EventConsumer#unsubscribe( + * ObjectName, NotificationListener). + */ + public void unsubscribe(ObjectName name, + NotificationListener listener) + throws ListenerNotFoundException, IOException { + if (logger.traceOn()) { + logger.trace("unsubscribe", ""); + } + checkState(); + final Integer listenerId = + getMatchedListenerInfo(name, listener, true); + + synchronized(listenerInfoMap) { + if (listenerInfoMap.remove(listenerId) == null) { + throw new ListenerNotFoundException(); + } + } + + stopListening(); + + try { + eventClientDelegate.removeListenerOrSubscriber(clientId, listenerId); + } catch (InstanceNotFoundException e) { + logger.trace("unsubscribe", "removeSubscriber", e); + } catch (EventClientNotFoundException cnfe) { + logger.trace("unsubscribe", "removeSubscriber", cnfe); + } + } + + /** + * @see javax.management.event.EventConsumer#subscribe( + * ObjectName, NotificationListener, NotificationFilter, Object). + */ + public void subscribe(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) throws IOException { + if (logger.traceOn()) { + logger.trace("subscribe", ""); + } + + checkState(); + + Integer listenerId; + try { + listenerId = + eventClientDelegate.addSubscriber(clientId, name, filter); + } catch (EventClientNotFoundException ecnfe) { + final IOException ioe = new IOException(); + ioe.initCause(ecnfe); + throw ioe; + } + + synchronized(listenerInfoMap) { + listenerInfoMap.put(listenerId, new ListenerInfo( + name, + listener, + filter, + handback, + true)); + } + + startListening(); + } + + /** + *

    Adds a set of listeners to the remote MBeanServer. This method can + * be used to copy the listeners from one {@code EventClient} to another.

    + * + *

    A listener is represented by a {@link ListenerInfo} object. The listener + * is added by calling {@link #subscribe(ObjectName, + * NotificationListener, NotificationFilter, Object)} if the method + * {@link ListenerInfo#isSubscription() isSubscription} + * returns {@code true}; otherwise it is added by calling + * {@link #addNotificationListener(ObjectName, NotificationListener, + * NotificationFilter, Object)}.

    + * + *

    The method returns the listeners which were added successfully. The + * elements in the returned collection are a subset of the elements in + * {@code infoList}. If all listeners were added successfully, the two + * collections are the same. If no listener was added successfully, the + * returned collection is empty.

    + * + * @param listeners the listeners to add. + * + * @return The listeners that were added successfully. + * + * @throws IOException If an I/O error occurs. + * + * @see #getListeners() + */ + public Collection addListeners(Collection listeners) + throws IOException { + if (logger.traceOn()) { + logger.trace("addListeners", ""); + } + + checkState(); + + if (listeners == null || listeners.isEmpty()) + return Collections.emptySet(); + + final List list = new ArrayList(); + for (ListenerInfo l : listeners) { + try { + if (l.isSubscription()) { + subscribe(l.getObjectName(), + l.getListener(), + l.getFilter(), + l.getHandback()); + } else { + addNotificationListener(l.getObjectName(), + l.getListener(), + l.getFilter(), + l.getHandback()); + } + + list.add(l); + } catch (Exception e) { + if (logger.traceOn()) { + logger.trace("addListeners", "failed to add: "+l, e); + } + } + } + + return list; + } + + /** + * Returns the set of listeners that have been added through + * this {@code EventClient} and not subsequently removed. + * + * @return A collection of listener information. Empty if there are no + * current listeners or if this {@code EventClient} has been {@linkplain + * #close closed}. + * + * @see #addListeners + */ + public Collection getListeners() { + if (logger.traceOn()) { + logger.trace("getListeners", ""); + } + + synchronized(listenerInfoMap) { + return Collections.unmodifiableCollection(listenerInfoMap.values()); + } + } + + /** + * Adds a listener to receive the {@code EventClient} notifications specified in + * {@link #getEventClientNotificationInfo}. + * + * @param listener A listener to receive {@code EventClient} notifications. + * @param filter A filter to select which notifications are to be delivered + * to the listener, or {@code null} if all notifications are to be delivered. + * @param handback An object to be given to the listener along with each + * notification. Can be null. + * @throws NullPointerException If listener is null. + * @see #removeEventClientListener + */ + public void addEventClientListener(NotificationListener listener, + NotificationFilter filter, + Object handback) { + if (logger.traceOn()) { + logger.trace("addEventClientListener", ""); + } + broadcaster.addNotificationListener(listener, filter, handback); + } + + /** + * Removes a listener added to receive {@code EventClient} notifications specified in + * {@link #getEventClientNotificationInfo}. + * + * @param listener A listener to receive {@code EventClient} notifications. + * @throws NullPointerException If listener is null. + * @throws ListenerNotFoundException If the listener is not added to + * this {@code EventClient}. + */ + public void removeEventClientListener(NotificationListener listener) + throws ListenerNotFoundException { + if (logger.traceOn()) { + logger.trace("removeEventClientListener", ""); + } + broadcaster.removeNotificationListener(listener); + } + + /** + *

    Get the types of notification that an {@code EventClient} can send + * to listeners added with {@link #addEventClientListener + * addEventClientListener}.

    + * + * @return Types of notification emitted by this {@code EventClient}. + * + * @see #FAILED + * @see #NONFATAL + * @see #NOTIFS_LOST + */ + public MBeanNotificationInfo[] getEventClientNotificationInfo() { + return myInfo.clone(); + } + + private static boolean match(ListenerInfo li, + ObjectName name, + NotificationListener listener, + boolean subscribed) { + return li.getObjectName().equals(name) && + li.getListener() == listener && + li.isSubscription() == subscribed; + } + + private static boolean match(ListenerInfo li, + ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback, + boolean subscribed) { + return li.getObjectName().equals(name) && + li.getFilter() == filter && + li.getListener() == listener && + li.getHandback() == handback && + li.isSubscription() == subscribed; + } + +// --------------------------------------------------- +// private classes +// --------------------------------------------------- + private class DispatchingJob extends RepeatedSingletonJob { + public DispatchingJob() { + super(distributingExecutor); + } + + public boolean isSuspended() { + return closed || buffer.size() == 0; + } + + public void task() { + TargetedNotification[] tns ; + int lost = 0; + + synchronized(buffer) { + tns = buffer.removeNotifs(); + lost = buffer.removeLost(); + } + + if ((tns == null || tns.length == 0) + && lost == 0) { + return; + } + + // forwarding + if (tns != null && tns.length > 0) { + if (logger.traceOn()) { + logger.trace("DispatchingJob-task", + "Forwarding: "+tns.length); + } + for (TargetedNotification tn : tns) { + final ListenerInfo li = listenerInfoMap.get(tn.getListenerID()); + try { + li.getListener().handleNotification(tn.getNotification(), + li.getHandback()); + } catch (Exception e) { + logger.fine( + "DispatchingJob.task", "listener got exception", e); + } + } + } + + if (lost > 0) { + if (logger.traceOn()) { + logger.trace("DispatchingJob-task", + "lost: "+lost); + } + final Notification n = new Notification(NOTIFS_LOST, + EventClient.this, + myNotifCounter.getAndIncrement(), + System.currentTimeMillis(), + "Lost notifications."); + n.setUserData(new Long(lost)); + broadcaster.sendNotification(n); + } + } + } + + + private class EventReceiverImpl implements EventReceiver { + public void receive(NotificationResult nr) { + if (logger.traceOn()) { + logger.trace("MyEventReceiver-receive", ""); + } + + synchronized(buffer) { + buffer.addNotifs(nr); + + dispatchingJob.resume(); + } + } + + public void failed(Throwable t) { + if (logger.traceOn()) { + logger.trace("MyEventReceiver-failed", "", t); + } + final Notification n = new Notification(FAILED, + this, + myNotifCounter.getAndIncrement(), + System.currentTimeMillis()); + n.setSource(t); + broadcaster.sendNotification(n); + } + + public void nonFatal(Exception e) { + if (logger.traceOn()) { + logger.trace("MyEventReceiver-nonFatal", "", e); + } + + final Notification n = new Notification(NONFATAL, + this, + myNotifCounter.getAndIncrement(), + System.currentTimeMillis()); + n.setSource(e); + broadcaster.sendNotification(n); + } + } + +// ---------------------------------------------------- +// private class +// ---------------------------------------------------- + + +// ---------------------------------------------------- +// private methods +// ---------------------------------------------------- + private Integer getListenerInfo(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback, + boolean subscribed) throws ListenerNotFoundException { + + synchronized(listenerInfoMap) { + for (Map.Entry entry : + listenerInfoMap.entrySet()) { + ListenerInfo li = entry.getValue(); + if (match(li, name, listener, filter, handback, subscribed)) { + return entry.getKey(); + } + } + } + + throw new ListenerNotFoundException(); + } + + private Integer getMatchedListenerInfo(ObjectName name, + NotificationListener listener, + boolean subscribed) throws ListenerNotFoundException { + + synchronized(listenerInfoMap) { + for (Map.Entry entry : + listenerInfoMap.entrySet()) { + ListenerInfo li = entry.getValue(); + if (li.getObjectName().equals(name) && + li.getListener() == listener && + li.isSubscription() == subscribed) { + return entry.getKey(); + } + } + } + + throw new ListenerNotFoundException(); + } + + private Collection getListenerInfo(ObjectName name, + NotificationListener listener, + boolean subscribed) throws ListenerNotFoundException { + + final ArrayList ids = new ArrayList(); + synchronized(listenerInfoMap) { + for (Map.Entry entry : + listenerInfoMap.entrySet()) { + ListenerInfo li = entry.getValue(); + if (match(li, name, listener, subscribed)) { + ids.add(entry.getKey()); + } + } + } + + if (ids.isEmpty()) { + throw new ListenerNotFoundException(); + } + + return ids; + } + + private void checkState() throws IOException { + synchronized(listenerInfoMap) { + if (closed) { + throw new IOException("Ended!"); + } + } + } + + private void startListening() throws IOException { + synchronized(listenerInfoMap) { + if (!startedListening && listenerInfoMap.size() > 0) { + eventRelay.setEventReceiver(myReceiver); + } + + startedListening = true; + + if (logger.traceOn()) { + logger.trace("startListening", "listening"); + } + } + } + + private void stopListening() throws IOException { + synchronized(listenerInfoMap) { + if (listenerInfoMap.size() == 0 && startedListening) { + eventRelay.setEventReceiver(null); + + startedListening = false; + + if (logger.traceOn()) { + logger.trace("stopListening", "non listening"); + } + } + } + } + + private void removeListener(Integer id) + throws InstanceNotFoundException, + ListenerNotFoundException, + IOException { + synchronized(listenerInfoMap) { + if (listenerInfoMap.remove(id) == null) { + throw new ListenerNotFoundException(); + } + + stopListening(); + } + + try { + eventClientDelegate.removeListenerOrSubscriber(clientId, id); + } catch (EventClientNotFoundException cnfe) { + logger.trace("removeListener", "ecd.removeListener", cnfe); + } + } + + +// ---------------------------------------------------- +// private variables +// ---------------------------------------------------- + private static final ClassLogger logger = + new ClassLogger("javax.management.event", "EventClient"); + + private final Executor distributingExecutor; + private final EventClientDelegateMBean eventClientDelegate; + private final EventRelay eventRelay; + private volatile String clientId = null; + private final long requestedLeaseTime; + + private final ReceiverBuffer buffer = new ReceiverBuffer(); + + private final EventReceiverImpl myReceiver = + new EventReceiverImpl(); + private final DispatchingJob dispatchingJob; + + private final HashMap listenerInfoMap = + new HashMap(); + + private volatile boolean closed = false; + + private volatile boolean startedListening = false; + + // Could change synchronization here. But at worst a race will mean + // sequence numbers are not contiguous, which may not matter much. + private final AtomicLong myNotifCounter = new AtomicLong(); + + private final static MBeanNotificationInfo[] myInfo = + new MBeanNotificationInfo[] { + new MBeanNotificationInfo( + new String[] {FAILED, NOTIFS_LOST}, + Notification.class.getName(), "")}; + + private final NotificationBroadcasterSupport broadcaster = + new NotificationBroadcasterSupport(); + + private final static Executor callerExecutor = new Executor() { + // DirectExecutor using caller thread + public void execute(Runnable r) { + r.run(); + } + }; + + private static void checkInit(final MBeanServerConnection conn, + final ObjectName delegateName) + throws IOException { + if (conn == null) { + throw new IllegalArgumentException("No connection specified"); + } + if (delegateName != null && + (!conn.isRegistered(delegateName))) { + throw new IllegalArgumentException( + delegateName + + ": not found"); + } + if (delegateName == null && + (!conn.isRegistered( + EventClientDelegate.OBJECT_NAME))) { + throw new IllegalArgumentException( + EventClientDelegate.OBJECT_NAME + + ": not found"); + } + } + +// ---------------------------------------------------- +// private event lease issues +// ---------------------------------------------------- + private Callable renewLease = new Callable() { + public Long call() throws IOException, EventClientNotFoundException { + return eventClientDelegate.lease(clientId, requestedLeaseTime); + } + }; + + private final LeaseRenewer leaseRenewer; + +// ------------------------------------------------------------------------ + /** + * Constructs an {@code MBeanServerConnection} that uses an {@code EventClient} object, + * if the underlying connection has an {@link EventClientDelegateMBean}. + *

    The {@code EventClient} object creates a default + * {@link FetchingEventRelay} object to + * receive notifications forwarded by the {@link EventClientDelegateMBean}. + * The {@link EventClientDelegateMBean} it works with is the + * default one registered with the ObjectName + * {@link EventClientDelegate#OBJECT_NAME + * OBJECT_NAME}. + * The thread from the {@link FetchingEventRelay} object that fetches the + * notifications is also used to distribute them. + * + * @param conn An {@link MBeanServerConnection} object used to communicate + * with an {@link EventClientDelegateMBean}. + * @throws IllegalArgumentException If the value of {@code conn} is null, + * or the default {@link EventClientDelegateMBean} is not registered. + * @throws IOException If an I/O error occurs. + */ + public static MBeanServerConnection getEventClientConnection( + final MBeanServerConnection conn) + throws IOException { + return getEventClientConnection(conn, null); + } + + /** + * Constructs an MBeanServerConnection that uses an {@code EventClient} + * object with a user-specific {@link EventRelay} + * object. + *

    + * The {@link EventClientDelegateMBean} which it works with is the + * default one registered with the ObjectName + * {@link EventClientDelegate#OBJECT_NAME + * OBJECT_NAME} + * The thread that calls {@link EventReceiver#receive + * EventReceiver.receive} from the {@link EventRelay} object is used + * to distribute notifications to their listeners. + * + * @param conn An {@link MBeanServerConnection} object used to communicate + * with an {@link EventClientDelegateMBean}. + * @param eventRelay A user-specific object used to receive notifications + * forwarded by the {@link EventClientDelegateMBean}. If null, the default + * {@link FetchingEventRelay} object is used. + * @throws IllegalArgumentException If the value of {@code conn} is null, + * or the default {@link EventClientDelegateMBean} is not registered. + * @throws IOException If an I/O error occurs. + */ + public static MBeanServerConnection getEventClientConnection( + final MBeanServerConnection conn, + final EventRelay eventRelay) + throws IOException { + + if (newEventConn == null) { + throw new IllegalArgumentException( + "Class not found: EventClientConnection"); + } + + checkInit(conn,null); + final Callable factory = new Callable() { + final public EventClient call() throws Exception { + EventClientDelegateMBean ecd = EventClientDelegate.getProxy(conn); + return new EventClient(ecd, eventRelay, null, null, + DEFAULT_LEASE_TIMEOUT); + } + }; + + try { + return (MBeanServerConnection)newEventConn.invoke(null, + conn, factory); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } + + private static Method newEventConn = null; + static { + try { + Class c = Class.forName( + "com.sun.jmx.remote.util.EventClientConnection", + false, Thread.currentThread().getContextClassLoader()); + newEventConn = c.getMethod("getEventConnectionFor", + MBeanServerConnection.class, Callable.class); + } catch (Exception e) { + // OK: we're running in a subset of our classes + } + } + + /** + *

    Get the client id of this {@code EventClient} in the + * {@link EventClientDelegateMBean}. + * + * @return the client id. + * + * @see EventClientDelegateMBean#addClient(String, Object[], String[]) + * EventClientDelegateMBean.addClient + */ + public String getClientId() { + return clientId; + } + + private static final PerThreadGroupPool + leaseRenewerThreadPool = PerThreadGroupPool.make(); +} diff --git a/src/share/classes/javax/management/event/EventClientDelegate.java b/src/share/classes/javax/management/event/EventClientDelegate.java new file mode 100644 index 000000000..e5ce1f7be --- /dev/null +++ b/src/share/classes/javax/management/event/EventClientDelegate.java @@ -0,0 +1,766 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + * @since JMX 2.0 + */ + +package javax.management.event; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Collection; +import java.util.Collections; +import java.util.UUID; + +import javax.management.InstanceNotFoundException; +import javax.management.ListenerNotFoundException; +import javax.management.Notification; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.remote.NotificationResult; +import com.sun.jmx.event.EventBuffer; +import com.sun.jmx.event.LeaseManager; +import com.sun.jmx.interceptor.SingleMBeanForwarder; +import com.sun.jmx.mbeanserver.Util; +import com.sun.jmx.remote.util.ClassLogger; +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import javax.management.DynamicMBean; +import javax.management.MBeanException; +import javax.management.MBeanPermission; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerDelegate; +import javax.management.MBeanServerInvocationHandler; +import javax.management.MBeanServerNotification; +import javax.management.ObjectInstance; +import javax.management.StandardMBean; +import javax.management.remote.MBeanServerForwarder; + +/** + * This is the default implementation of the MBean + * {@link EventClientDelegateMBean}. + */ +public class EventClientDelegate implements EventClientDelegateMBean { + + private EventClientDelegate(MBeanServer server) { + if (server == null) { + throw new NullPointerException("Null MBeanServer."); + } + + if (logger.traceOn()) { + logger.trace("EventClientDelegate", "new one"); + } + mbeanServer = server; + eventSubscriber = EventSubscriber.getEventSubscriber(mbeanServer); + } + + /** + * Returns an {@code EventClientDelegate} instance for the given + * {@code MBeanServer}. Calling this method more than once with the same + * {@code server} argument may return the same object or a different object + * each time. See {@link EventClientDelegateMBean} for an example use of + * this method. + * + * @param server An MBean server instance to work with. + * @return An {@code EventClientDelegate} instance. + * @throws NullPointerException If {@code server} is null. + */ + public static EventClientDelegate getEventClientDelegate(MBeanServer server) { + EventClientDelegate delegate = null; + synchronized(delegateMap) { + final WeakReference wrf = delegateMap.get(server); + delegate = (wrf == null) ? null : (EventClientDelegate)wrf.get(); + + if (delegate == null) { + delegate = new EventClientDelegate(server); + try { + // TODO: this may not work with federated MBean, because + // the delegate will *not* emit notifications for those MBeans. + delegate.mbeanServer.addNotificationListener( + MBeanServerDelegate.DELEGATE_NAME, + delegate.cleanListener, null, null); + } catch (InstanceNotFoundException e) { + logger.fine( + "getEventClientDelegate", + "Could not add MBeanServerDelegate listener", e); + } + delegateMap.put(server, + new WeakReference(delegate)); + } + } + + return delegate; + } + + // Logic for the MBeanServerForwarder that simulates the existence of the + // EventClientDelegate MBean. Things are complicated by the fact that + // there may not be anything in the chain after this forwarder when it is + // created - the connection to a real MBeanServer might only come later. + // Recall that there are two ways of creating a JMXConnectorServer - + // either you specify its MBeanServer when you create it, or you specify + // no MBeanServer and register it in an MBeanServer later. In the latter + // case, the forwarder chain points nowhere until this registration + // happens. Since EventClientDelegate wants to add a listener to the + // MBeanServerDelegate, we can't create an EventClientDelegate until + // there is an MBeanServer. So the forwarder initially has + // a dummy ECD where every method throws an exception, and + // the real ECD is created as soon as doing so does not produce an + // exception. + // TODO: rewrite so that the switch from the dummy to the real ECD happens + // just before we would otherwise have thrown UnsupportedOperationException. + // This is more correct, because it's not guaranteed that we will see the + // moment where the real MBeanServer is attached, if it happens by virtue + // of a setMBeanServer on some other forwarder later in the chain. + + private static class Forwarder extends SingleMBeanForwarder { + + private static class UnsupportedInvocationHandler + implements InvocationHandler { + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + throw new UnsupportedOperationException( + "EventClientDelegate unavailable: no MBeanServer, or " + + "MBeanServer inaccessible"); + } + } + + private static DynamicMBean makeUnsupportedECD() { + EventClientDelegateMBean unsupported = (EventClientDelegateMBean) + Proxy.newProxyInstance( + EventClientDelegateMBean.class.getClassLoader(), + new Class[] {EventClientDelegateMBean.class}, + new UnsupportedInvocationHandler()); + return new StandardMBean( + unsupported, EventClientDelegateMBean.class, false); + } + + private volatile boolean madeECD; + + Forwarder() { + super(OBJECT_NAME, makeUnsupportedECD()); + } + + @Override + public synchronized void setMBeanServer(final MBeanServer mbs) { + super.setMBeanServer(mbs); + + if (!madeECD) { + try { + EventClientDelegate ecd = + AccessController.doPrivileged( + new PrivilegedAction() { + public EventClientDelegate run() { + return getEventClientDelegate(Forwarder.this); + } + }); + DynamicMBean mbean = new StandardMBean( + ecd, EventClientDelegateMBean.class, false); + setSingleMBean(mbean); + madeECD = true; + } catch (Exception e) { + // OK: assume no MBeanServer + logger.fine("setMBeanServer", "isRegistered", e); + } + } + } + } + + /** + *

    Create a new {@link MBeanServerForwarder} that simulates the existence + * of an {@code EventClientDelegateMBean} with the {@linkplain + * #OBJECT_NAME default name}. This forwarder intercepts MBean requests + * that are targeted for that MBean and handles them itself. All other + * requests are forwarded to the next element in the forwarder chain.

    + * + * @return a new {@code MBeanServerForwarder} that simulates the existence + * of an {@code EventClientDelegateMBean}. + */ + public static MBeanServerForwarder newForwarder() { + return new Forwarder(); + } + + /** + * Returns a proxy of the default {@code EventClientDelegateMBean}. + * + * @param conn An {@link MBeanServerConnection} to work with. + */ + @SuppressWarnings("cast") // cast for jdk 1.5 + public static EventClientDelegateMBean getProxy(MBeanServerConnection conn) { + return (EventClientDelegateMBean)MBeanServerInvocationHandler. + newProxyInstance(conn, + OBJECT_NAME, + EventClientDelegateMBean.class, + false); + } + + public String addClient(String className, Object[] params, String[] sig) + throws MBeanException { + return addClient(className, null, params, sig, true); + } + + public String addClient(String className, + ObjectName classLoader, + Object[] params, + String[] sig) throws MBeanException { + return addClient(className, classLoader, params, sig, false); + } + + private String addClient(String className, + ObjectName classLoader, + Object[] params, + String[] sig, + boolean classLoaderRepository) throws MBeanException { + try { + return addClientX( + className, classLoader, params, sig, classLoaderRepository); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new MBeanException(e); + } + } + + private String addClientX(String className, + ObjectName classLoader, + Object[] params, + String[] sig, + boolean classLoaderRepository) throws Exception { + if (className == null) { + throw new IllegalArgumentException("Null class name."); + } + + final Object o; + + // The special treatment of standard EventForwarders is so that no + // special permissions are necessary to use them. Otherwise you + // couldn't use EventClient if you didn't have permission to call + // MBeanServer.instantiate. We do require that permission for + // non-standard forwarders, because otherwise you could instantiate + // any class with possibly adverse consequences. We also avoid using + // MBeanInstantiator because it looks up constructors by loading each + // class in the sig array, which means a remote user could cause any + // class to be loaded. That's probably not hugely risky but still. + if (className.startsWith("javax.management.event.")) { + Class c = Class.forName( + className, false, this.getClass().getClassLoader()); + Constructor foundCons = null; + if (sig == null) + sig = new String[0]; + for (Constructor cons : c.getConstructors()) { + Class[] types = cons.getParameterTypes(); + String[] consSig = new String[types.length]; + for (int i = 0; i < types.length; i++) + consSig[i] = types[i].getName(); + if (Arrays.equals(sig, consSig)) { + foundCons = cons; + break; + } + } + if (foundCons == null) { + throw new NoSuchMethodException( + "Constructor for " + className + " with argument types " + + Arrays.toString(sig)); + } + o = foundCons.newInstance(params); + } else if (classLoaderRepository) { + o = mbeanServer.instantiate(className, params, sig); + } else { + o = mbeanServer.instantiate(className, classLoader, params, sig); + } + + if (!(o instanceof EventForwarder)) { + throw new IllegalArgumentException( + className+" is not an EventForwarder class."); + } + + final EventForwarder forwarder = (EventForwarder)o; + final String clientId = UUID.randomUUID().toString(); + ClientInfo clientInfo = new ClientInfo(clientId, forwarder); + + clientInfoMap.put(clientId, clientInfo); + + forwarder.setClientId(clientId); + + if (logger.traceOn()) { + logger.trace("addClient", clientId); + } + + return clientId; + } + + public Integer[] getListenerIds(String clientId) + throws IOException, EventClientNotFoundException { + ClientInfo clientInfo = getClientInfo(clientId); + + if (clientInfo == null) { + throw new EventClientNotFoundException("The client is not found."); + } + + Map listenerInfoMap = clientInfo.listenerInfoMap; + synchronized (listenerInfoMap) { + Set ids = listenerInfoMap.keySet(); + return ids.toArray(new Integer[ids.size()]); + } + } + + /** + * {@inheritDoc} + * + *

    The execution of this method includes a call to + * {@link MBeanServer#addNotificationListener(ObjectName, + * NotificationListener, NotificationFilter, Object)}.

    + */ + public Integer addListener(String clientId, + final ObjectName name, + NotificationFilter filter) + throws EventClientNotFoundException, InstanceNotFoundException { + + if (logger.traceOn()) { + logger.trace("addListener", ""); + } + + return getClientInfo(clientId).addListenerInfo(name, filter); + } + + /** + * {@inheritDoc} + * + *

    The execution of this method can include call to + * {@link MBeanServer#removeNotificationListener(ObjectName, + * NotificationListener, NotificationFilter, Object)}.

    + */ + public void removeListenerOrSubscriber(String clientId, Integer listenerId) + throws InstanceNotFoundException, + ListenerNotFoundException, + EventClientNotFoundException, + IOException { + if (logger.traceOn()) { + logger.trace("removeListener", ""+listenerId); + } + getClientInfo(clientId).removeListenerInfo(listenerId); + } + + /** + * {@inheritDoc} + * + *

    The execution of this method includes a call to + * {@link MBeanServer#addNotificationListener(ObjectName, + * NotificationListener, NotificationFilter, Object)} for + * every MBean matching {@code name}. If {@code name} is + * an {@code ObjectName} pattern, then the execution of this + * method will include a call to {@link MBeanServer#queryNames}.

    + */ + public Integer addSubscriber(String clientId, ObjectName name, + NotificationFilter filter) + throws EventClientNotFoundException, IOException { + if (logger.traceOn()) { + logger.trace("addSubscriber", ""); + } + return getClientInfo(clientId).subscribeListenerInfo(name, filter); + } + + public NotificationResult fetchNotifications(String clientId, + long startSequenceNumber, + int maxNotifs, + long timeout) + throws EventClientNotFoundException { + if (logger.traceOn()) { + logger.trace("fetchNotifications", "for "+clientId); + } + return getClientInfo(clientId).fetchNotifications(startSequenceNumber, + maxNotifs, + timeout); + } + + public void removeClient(String clientId) + throws EventClientNotFoundException { + if (clientId == null) + throw new EventClientNotFoundException("Null clientId"); + if (logger.traceOn()) { + logger.trace("removeClient", clientId); + } + ClientInfo ci = null; + ci = clientInfoMap.remove(clientId); + + if (ci == null) { + throw new EventClientNotFoundException("clientId is "+clientId); + } else { + ci.clean(); + } + } + + public long lease(String clientId, long timeout) + throws IOException, EventClientNotFoundException { + if (logger.traceOn()) { + logger.trace("lease", "for "+clientId); + } + return getClientInfo(clientId).lease(timeout); + } + + // ------------------------------------ + // private classes + // ------------------------------------ + private class ClientInfo { + String clientId; + EventBuffer buffer; + NotificationListener clientListener; + Map listenerInfoMap = + new HashMap(); + + ClientInfo(String clientId, EventForwarder forwarder) { + this.clientId = clientId; + this.forwarder = forwarder; + clientListener = + new ForwardingClientListener(listenerInfoMap, forwarder); + } + + Integer addOrSubscribeListenerInfo( + ObjectName name, NotificationFilter filter, boolean subscribe) + throws InstanceNotFoundException, IOException { + + final Integer listenerId = nextListenerId(); + AddedListener listenerInfo = new AddedListener( + listenerId, filter, name, subscribe); + if (subscribe) { + eventSubscriber.subscribe(name, + clientListener, + filter, + listenerInfo); + } else { + mbeanServer.addNotificationListener(name, + clientListener, + filter, + listenerInfo); + } + + synchronized(listenerInfoMap) { + listenerInfoMap.put(listenerId, listenerInfo); + } + + return listenerId; + } + + Integer addListenerInfo(ObjectName name, + NotificationFilter filter) throws InstanceNotFoundException { + try { + return addOrSubscribeListenerInfo(name, filter, false); + } catch (IOException e) { // can't happen + logger.warning( + "EventClientDelegate.addListenerInfo", + "unexpected exception", e); + throw new RuntimeException(e); + } + } + + Integer subscribeListenerInfo(ObjectName name, + NotificationFilter filter) throws IOException { + try { + return addOrSubscribeListenerInfo(name, filter, true); + } catch (InstanceNotFoundException e) { // can't happen + logger.warning( + "EventClientDelegate.subscribeListenerInfo", + "unexpected exception", e); + throw new RuntimeException(e); + } + } + + private final AtomicInteger nextListenerId = new AtomicInteger(); + + private Integer nextListenerId() { + return nextListenerId.getAndIncrement(); + } + + NotificationResult fetchNotifications(long startSequenceNumber, + int maxNotifs, + long timeout) { + + if (!(forwarder instanceof FetchingEventForwarder)) { + throw new IllegalArgumentException( + "This client is using Event Postal Service!"); + } + + return ((FetchingEventForwarder)forwarder). + fetchNotifications(startSequenceNumber, + maxNotifs, timeout); + } + + void removeListenerInfo(Integer listenerId) + throws InstanceNotFoundException, ListenerNotFoundException, IOException { + AddedListener listenerInfo; + synchronized(listenerInfoMap) { + listenerInfo = listenerInfoMap.remove(listenerId); + } + + if (listenerInfo == null) { + throw new ListenerNotFoundException("The listener is not found."); + } + + if (listenerInfo.subscription) { + eventSubscriber.unsubscribe(listenerInfo.name, + clientListener); + } else { + mbeanServer.removeNotificationListener(listenerInfo.name, + clientListener, + listenerInfo.filter, + listenerInfo); + } + } + + void clean(ObjectName name) { + synchronized(listenerInfoMap) { + for (Map.Entry entry : + listenerInfoMap.entrySet()) { + AddedListener li = entry.getValue(); + if (name.equals(li.name)) { + listenerInfoMap.remove(entry.getKey()); + } + } + } + } + + void clean() { + synchronized(listenerInfoMap) { + for (AddedListener li : listenerInfoMap.values()) { + try { + mbeanServer.removeNotificationListener(li.name, + clientListener); + } catch (Exception e) { + logger.trace("ClientInfo.clean", "removeNL", e); + } + } + listenerInfoMap.clear(); + } + + try { + forwarder.close(); + } catch (Exception e) { + logger.trace( + "ClientInfo.clean", "forwarder.close", e); + } + + if (leaseManager != null) { + leaseManager.stop(); + } + } + + long lease(long timeout) { + return leaseManager.lease(timeout); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o instanceof ClientInfo && + clientId.equals(((ClientInfo)o).clientId)) { + return true; + } + + return false; + } + + @Override + public int hashCode() { + return clientId.hashCode(); + } + + private EventForwarder forwarder = null; + + private final Runnable leaseExpiryCallback = new Runnable() { + public void run() { + try { + removeClient(clientId); + } catch (Exception e) { + logger.trace( + "ClientInfo.leaseExpiryCallback", "removeClient", e); + } + } + }; + + private LeaseManager leaseManager = new LeaseManager(leaseExpiryCallback); + } + + private class ForwardingClientListener implements NotificationListener { + public ForwardingClientListener(Map listenerInfoMap, + EventForwarder forwarder) { + this.listenerInfoMap = listenerInfoMap; + this.forwarder = forwarder; + } + + public void handleNotification(Notification n, Object o) { + if (n == null || (!(o instanceof AddedListener))) { + if (logger.traceOn()) { + logger.trace("ForwardingClientListener-handleNotification", + "received a unknown notif"); + } + return; + } + + AddedListener li = (AddedListener) o; + + if (checkListenerPermission(li.name,li.acc)) { + try { + forwarder.forward(n, li.listenerId); + } catch (Exception e) { + if (logger.traceOn()) { + logger.trace( + "ForwardingClientListener-handleNotification", + "forwarding failed.", e); + } + } + } + } + + private final Map listenerInfoMap; + private final EventForwarder forwarder; + } + + private class AddedListener { + final int listenerId; + final NotificationFilter filter; + final ObjectName name; + final boolean subscription; + final AccessControlContext acc; + + public AddedListener( + int listenerId, + NotificationFilter filter, + ObjectName name, + boolean subscription) { + this.listenerId = listenerId; + this.filter = filter; + this.name = name; + this.subscription = subscription; + acc = AccessController.getContext(); + } + } + + private class CleanListener implements NotificationListener { + public void handleNotification(Notification notification, + Object handback) { + if (notification instanceof MBeanServerNotification) { + if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals( + notification.getType())) { + final ObjectName name = + ((MBeanServerNotification)notification).getMBeanName(); + + final Collection list = + Collections.unmodifiableCollection(clientInfoMap.values()); + + for (ClientInfo ci : list) { + ci.clean(name); + } + } + + } + } + } + + // ------------------------------------------------- + // private method + // ------------------------------------------------- + private ClientInfo getClientInfo(String clientId) + throws EventClientNotFoundException { + ClientInfo clientInfo = null; + clientInfo = clientInfoMap.get(clientId); + + if (clientInfo == null) { + throw new EventClientNotFoundException("The client is not found."); + } + + return clientInfo; + } + + /** + * Explicitly check the MBeanPermission for + * the current access control context. + */ + private boolean checkListenerPermission(final ObjectName name, + final AccessControlContext acc) { + if (logger.traceOn()) { + logger.trace("checkListenerPermission", ""); + } + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + ObjectInstance oi = (ObjectInstance) AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public Object run() + throws InstanceNotFoundException { + return mbeanServer.getObjectInstance(name); + } + }); + + String classname = oi.getClassName(); + MBeanPermission perm = new MBeanPermission( + classname, + null, + name, + "addNotificationListener"); + sm.checkPermission(perm, acc); + } catch (Exception e) { + if (logger.debugOn()) { + logger.debug("checkListenerPermission", "refused.", e); + } + return false; + } + } + return true; + } + + // ------------------------------------ + // private variables + // ------------------------------------ + private final MBeanServer mbeanServer; + private volatile String mbeanServerName = null; + private Map clientInfoMap = + new ConcurrentHashMap(); + + private final CleanListener cleanListener = new CleanListener(); + private final EventSubscriber eventSubscriber; + + private static final ClassLogger logger = + new ClassLogger("javax.management.event", "EventClientDelegate"); + + private static final + Map> delegateMap = + new WeakHashMap>(); +} diff --git a/src/share/classes/javax/management/event/EventClientDelegateMBean.java b/src/share/classes/javax/management/event/EventClientDelegateMBean.java new file mode 100644 index 000000000..ba57cce9c --- /dev/null +++ b/src/share/classes/javax/management/event/EventClientDelegateMBean.java @@ -0,0 +1,318 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.event; + +import com.sun.jmx.mbeanserver.Util; +import java.io.IOException; +import javax.management.InstanceNotFoundException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.NotificationFilter; +import javax.management.ObjectName; +import javax.management.remote.NotificationResult; + +/** + *

    This interface specifies necessary methods on the MBean server + * side for a JMX remote client to manage its notification listeners as + * if they are local. + * Users do not usually work directly with this MBean; instead, the {@link + * EventClient} class is designed to be used directly by the user.

    + * + *

    A default implementation of this interface can be added to an MBean + * Server in one of several ways.

    + * + *
      + *
    • The most usual is to insert an {@link + * javax.management.remote.MBeanServerForwarder MBeanServerForwarder} between + * the {@linkplain javax.management.remote.JMXConnectorServer Connector Server} + * and the MBean Server, that will intercept accesses to the Event Client + * Delegate MBean and treat them as the real MBean would. This forwarder is + * inserted by default with the standard RMI Connector Server, and can also + * be created explicitly using {@link EventClientDelegate#newForwarder()}. + * + *

    • A variant on the above is to replace the MBean Server that is + * used locally with a forwarder as described above. Since + * {@code MBeanServerForwarder} extends {@code MBeanServer}, you can use + * a forwarder anywhere you would have used the original MBean Server. The + * code to do this replacement typically looks something like this:

      + * + *
      + * MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();  // or whatever
      + * MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
      + * mbsf.setMBeanServer(mbs);
      + * mbs = mbsf;
      + * // now use mbs just as you did before, but it will have an EventClientDelegate
      + * 
      + * + *
    • The final way is to create an instance of {@link EventClientDelegate} + * and register it in the MBean Server under the standard {@linkplain + * #OBJECT_NAME name}:

      + * + *
      + * MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();  // or whatever
      + * EventClientDelegate ecd = EventClientDelegate.getEventClientDelegate(mbs);
      + * mbs.registerMBean(ecd, EventClientDelegateMBean.OBJECT_NAME);
      + * 
      + * 
    + * + * @since JMX 2.0 + */ +public interface EventClientDelegateMBean { + /** + * The string representation of {@link #OBJECT_NAME}. + */ + // This shouldn't really be necessary but an apparent javadoc bug + // meant that the {@value} tags didn't work if this was a + // field in EventClientDelegate, even a public field. + public static final String OBJECT_NAME_STRING = + "javax.management.event:type=EventClientDelegate"; + + /** + * The standard ObjectName used to register the default + * EventClientDelegateMBean. The name is + * {@value #OBJECT_NAME_STRING}. + */ + public final static ObjectName OBJECT_NAME = + Util.newObjectName(OBJECT_NAME_STRING); + + /** + * A unique listener identifier specified for an EventClient. + * Any notification associated with this id is intended for + * the EventClient which receives the notification, rather than + * a listener added using that EventClient. + */ + public static final int EVENT_CLIENT_LISTENER_ID = -100; + + /** + * Adds a new client to the EventClientDelegateMBean with + * a user-specified + * {@link EventForwarder} to forward notifications to the client. The + * EventForwarder is created by calling + * {@link javax.management.MBeanServer#instantiate(String, Object[], + * String[])}. + * + * @param className The class name used to create an + * {@code EventForwarder}. + * @param params An array containing the parameters of the constructor to + * be invoked. + * @param sig An array containing the signature of the constructor to be + * invoked + * @return A client identifier. + * @exception IOException Reserved for a remote call to throw on the client + * side. + * @exception MBeanException An exception thrown when creating the user + * specified EventForwarder. + */ + public String addClient(String className, Object[] params, String[] sig) + throws IOException, MBeanException; + + /** + * Adds a new client to the EventClientDelegateMBean with + * a user-specified + * {@link EventForwarder} to forward notifications to the client. The + * EventForwarder is created by calling + * {@link javax.management.MBeanServer#instantiate(String, ObjectName, + * Object[], String[])}. A user-specified class loader is used to create + * this EventForwarder. + * + * @param className The class name used to create an + * {@code EventForwarder}. + * @param classLoader An ObjectName registered as a + * ClassLoader MBean. + * @param params An array containing the parameters of the constructor to + * be invoked. + * @param sig An array containing the signature of the constructor to be + * invoked + * @return A client identifier. + * @exception IOException Reserved for a remote call to throw on the client + * side. + * @exception MBeanException An exception thrown when creating the user + * specified EventForwarder. + */ + public String addClient(String className, + ObjectName classLoader, + Object[] params, + String[] sig) throws IOException, MBeanException; + + /** + * Removes an added client. Calling this method will remove all listeners + * added with the client. + * + * @exception EventClientNotFoundException If the {@code clientId} is + * not found. + * @exception IOException Reserved for a remote call to throw on the client + * side. + */ + public void removeClient(String clientID) + throws EventClientNotFoundException, IOException; + + /** + * Returns the identifiers of listeners added or subscribed to with the + * specified client identifier. + *

    If no listener is currently registered with the client, an empty + * array is returned. + * @param clientID The client identifier with which the listeners are + * added or subscribed to. + * @return An array of listener identifiers. + * @exception EventClientNotFoundException If the {@code clientId} is + * not found. + * @exception IOException Reserved for a remote call to throw on the client + * side. + */ + public Integer[] getListenerIds(String clientID) + throws EventClientNotFoundException, IOException; + + /** + * Adds a listener to receive notifications from an MBean and returns + * a non-negative integer as the identifier of the listener. + *

    This method is called by an {@link EventClient} to implement the + * method {@link EventClient#addNotificationListener(ObjectName, + * NotificationListener, NotificationFilter, Object)}. + * + * @param name The name of the MBean onto which the listener should be added. + * @param filter The filter object. If {@code filter} is null, + * no filtering will be performed before handling notifications. + * @param clientId The client identifier with which the listener is added. + * @return A listener identifier. + * @throws EventClientNotFoundException Thrown if the {@code clientId} is + * not found. + * @throws InstanceNotFoundException Thrown if the MBean is not found. + * @throws IOException Reserved for a remote call to throw on the client + * side. + */ + public Integer addListener(String clientId, + ObjectName name, + NotificationFilter filter) + throws InstanceNotFoundException, EventClientNotFoundException, + IOException; + + + /** + *

    Subscribes a listener to receive notifications from an MBean or a + * set of MBeans represented by an {@code ObjectName} pattern. (It is + * not an error if no MBeans match the pattern at the time this method is + * called.)

    + * + *

    Returns a non-negative integer as the identifier of the listener.

    + * + *

    This method is called by an {@link EventClient} to execute its + * method {@link EventClient#subscribe(ObjectName, NotificationListener, + * NotificationFilter, Object)}.

    + * + * @param clientId The remote client's identifier. + * @param name The name of an MBean or an {@code ObjectName} pattern + * representing a set of MBeans to which the listener should listen. + * @param filter The filter object. If {@code filter} is null, no + * filtering will be performed before notifications are handled. + * + * @return A listener identifier. + * + * @throws IllegalArgumentException If the {@code name} or + * {@code listener} is null. + * @throws EventClientNotFoundException If the client ID is not found. + * @throws IOException Reserved for a remote client to throw if + * an I/O error occurs. + * + * @see EventConsumer#subscribe(ObjectName, NotificationListener, + * NotificationFilter,Object) + * @see #removeListenerOrSubscriber(String, Integer) + */ + public Integer addSubscriber(String clientId, ObjectName name, + NotificationFilter filter) + throws EventClientNotFoundException, IOException; + + /** + * Removes a listener, to stop receiving notifications. + *

    This method is called by an {@link EventClient} to execute its + * methods {@link EventClient#removeNotificationListener(ObjectName, + * NotificationListener, NotificationFilter, Object)}, + * {@link EventClient#removeNotificationListener(ObjectName, + * NotificationListener)}, and {@link EventClient#unsubscribe}. + * + * @param clientId The client identifier with which the listener was added. + * @param listenerId The listener identifier to be removed. This must be + * an identifier returned by a previous {@link #addListener addListener} + * or {@link #addSubscriber addSubscriber} call. + * + * @throws InstanceNotFoundException if the MBean on which the listener + * was added no longer exists. + * @throws ListenerNotFoundException if there is no listener with the + * given {@code listenerId}. + * @throws EventClientNotFoundException if the {@code clientId} is + * not found. + * @throws IOException Reserved for a remote call to throw on the client + * side. + */ + public void removeListenerOrSubscriber(String clientId, Integer listenerId) + throws InstanceNotFoundException, ListenerNotFoundException, + EventClientNotFoundException, IOException; + + /** + * Called by a client to fetch notifications that are to be sent to its + * listeners. + * + * @param clientId The client's identifier. + * @param startSequenceNumber The first sequence number to + * consider. + * @param timeout The maximum waiting time. + * @param maxNotifs The maximum number of notifications to return. + * + * @throws EventClientNotFoundException Thrown if the {@code clientId} is + * not found. + * @throws IllegalArgumentException if the client was {@linkplain + * #addClient(String, Object[], String[]) added} with an {@link + * EventForwarder} that is not a {@link FetchingEventForwarder}. + * @throws IOException Reserved for a remote call to throw on the client + * side. + */ + public NotificationResult fetchNotifications(String clientId, + long startSequenceNumber, + int maxNotifs, + long timeout) + throws EventClientNotFoundException, IOException; + + /** + * An {@code EventClient} calls this method to keep its {@code clientId} + * alive in this MBean. The client will be removed if the lease times out. + * + * @param clientId The client's identifier. + * @param timeout The time in milliseconds by which the lease is to be + * extended. The value zero has no special meaning, so it will cause the + * lease to time out immediately. + * + * @return The new lifetime of the lease in milliseconds. This may be + * different from the requested time. + * + * @throws EventClientNotFoundException if the {@code clientId} is + * not found. + * @throws IOException reserved for a remote call to throw on the client + * side. + * @throws IllegalArgumentException if {@code clientId} is null or + * {@code timeout} is negative. + */ + public long lease(String clientId, long timeout) + throws IOException, EventClientNotFoundException; +} diff --git a/src/share/classes/javax/management/event/EventClientNotFoundException.java b/src/share/classes/javax/management/event/EventClientNotFoundException.java new file mode 100644 index 000000000..cd691b8a4 --- /dev/null +++ b/src/share/classes/javax/management/event/EventClientNotFoundException.java @@ -0,0 +1,79 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.event; + +import javax.management.JMException; + +/** + * Thrown if an event client identifier is unknown. + */ +public class EventClientNotFoundException extends JMException { + + /* Serial version */ + private static final long serialVersionUID = -3910667345840643089L; + + /** + *Constructs a {@code ClientNotFoundException} without a detail message. + */ + public EventClientNotFoundException() { + super(); + } + + /** + * Constructs a {@code ClientNotFoundException} with the specified detail message. + * @param message The message. + */ + public EventClientNotFoundException(String message) { + super(message); + } + + /** + * Constructs a {@code ClientNotFoundException} with the specified detail message + * and cause. + * + * @param message The message. + * @param cause The cause (which is saved for later retrieval by the + * {@code Throwable.getCause()} method). A null value is permitted, and indicates + * that the cause is non-existent or unknown. + */ + public EventClientNotFoundException(String message, Throwable cause) { + super(message); + + initCause(cause); + } + + /** + * Constructs a new exception with the specified cause. + * @param cause The cause (which is saved for later retrieval by the + * {@code Throwable.getCause()} method). A null value is permitted, and indicates + * that the cause is non-existent or unknown. + */ + public EventClientNotFoundException(Throwable cause) { + super(); + + initCause(cause); + } +} diff --git a/src/share/classes/javax/management/event/EventConsumer.java b/src/share/classes/javax/management/event/EventConsumer.java new file mode 100644 index 000000000..51baf3807 --- /dev/null +++ b/src/share/classes/javax/management/event/EventConsumer.java @@ -0,0 +1,98 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.event; + +import java.io.IOException; +import javax.management.ListenerNotFoundException; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; + +/** + * This interface specifies methods to subscribe a listener to receive events + * from an MBean or a set of MBeans. The MBeans can already be registered in + * an MBean server, or they can be pending registration, or they can be MBeans + * that will never be registered, or they can be MBeans that will be registered + * then unregistered. + * @since JMX 2.0 + */ +public interface EventConsumer { + /** + *

    Subscribes a listener to receive events from an MBean or a set + * of MBeans represented by an {@code ObjectName} pattern.

    + * + *

    An event emitted by an MBean is forwarded to every listener that was + * subscribed with the name of that MBean, or with a pattern that matches + * that name.

    + * + * @param name The name of an MBean or an {@code ObjectName} pattern + * representing a set of MBeans to which the listener should listen. + * @param listener The listener object that will handle the + * notifications emitted by the MBeans. + * @param filter The filter object. If {@code filter} is null, no + * filtering will be performed before notification handling. + * @param handback The context to be sent to the listener when a + * notification is emitted. + * + * @throws IllegalArgumentException If the {@code name} or + * {@code listener} is null. + * @throws IOException for a remote client, thrown if + * an I/O error occurs. + * @see #unsubscribe(ObjectName, NotificationListener) + */ + public void subscribe(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws IOException; + + /** + *

    Unsubscribes a listener which is listening to an MBean or a set of + * MBeans represented by an {@code ObjectName} pattern.

    + * + *

    The listener to be removed must have been added by the {@link + * #subscribe subscribe} method with the given {@code name}. If the {@code + * name} is a pattern, then the {@code subscribe} must have used the same + * pattern. If the same listener has been subscribed more than once to the + * {@code name}, perhaps with different filters or handbacks, then all such + * listeners are removed.

    + * + * @param name The name of the MBean or an {@code ObjectName} pattern + * representing a set of MBeans to which the listener was subscribed. + * @param listener A listener that was previously subscribed to the + * MBean(s). + * + * @throws ListenerNotFoundException The given {@code listener} was not + * subscribed to the given {@code name}. + * @throws IOException for a remote client, thrown if + * an I/O error occurs. + * + * @see #subscribe + */ + public void unsubscribe(ObjectName name, + NotificationListener listener) + throws ListenerNotFoundException, IOException; +} diff --git a/src/share/classes/javax/management/event/EventForwarder.java b/src/share/classes/javax/management/event/EventForwarder.java new file mode 100644 index 000000000..471aefb62 --- /dev/null +++ b/src/share/classes/javax/management/event/EventForwarder.java @@ -0,0 +1,63 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.event; + +import java.io.IOException; +import javax.management.Notification; + +/** + * This interface can be used to specify a custom forwarding mechanism for + * {@code EventClientDelegateMBean} to forward events to the client. + * + * @see Custom notification + * transports + */ +public interface EventForwarder { + /** + * Forwards a notification. + * @param n The notification to be forwarded to a remote listener. + * @param listenerId The identifier of the listener to receive the notification. + * @throws IOException If it is closed or an I/O error occurs. + */ + public void forward(Notification n, Integer listenerId) + throws IOException; + + /** + * Informs the {@code EventForwarder} to shut down. + *

    After this method is called, any call to the method + * {@link #forward forward(Notification, Integer)} may get an {@code IOException}. + * @throws IOException If an I/O error occurs. + */ + public void close() throws IOException; + + /** + * Sets an event client identifier created by {@link EventClientDelegateMBean}. + *

    This method will be called just after this {@code EventForwarder} + * is constructed and before calling the {@code forward} method to forward any + * notifications. + */ + public void setClientId(String clientId) throws IOException; +} diff --git a/src/share/classes/javax/management/event/EventReceiver.java b/src/share/classes/javax/management/event/EventReceiver.java new file mode 100644 index 000000000..5be396d2b --- /dev/null +++ b/src/share/classes/javax/management/event/EventReceiver.java @@ -0,0 +1,77 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.event; + +import javax.management.remote.NotificationResult; + +/** + * An object implementing this interface is passed by an {@link EventClient} + * to its {@link EventRelay}, to allow the {@code EventRelay} to communicate + * received notifications to the {@code EventClient}. + * + * @see Custom notification + * transports + */ +public interface EventReceiver { + + /** + * This method is implemented by {@code EventClient} as a callback to + * receive notifications from {@code EventRelay}. + *

    The notifications are included in an object specified by the class + * {@link NotificationResult}. In + * addition to a set of notifications, the class object also contains two values: + * {@code earliestSequenceNumber} and {@code nextSequenceNumber}. + * These two values determine whether any notifications have been lost. + * The {@code nextSequenceNumber} value of the last time is compared + * to the received value {@code earliestSequenceNumber}. If the + * received {@code earliesSequenceNumber} is greater, than the difference + * signifies the number of lost notifications. A sender should + * ensure the sequence of notifications sent, meaning that the value + * {@code earliestSequenceNumber} of the next return should be always equal to + * or greater than the value {@code nextSequenceNumber} of the last return. + * + * @param nr the received notifications and sequence numbers. + */ + public void receive(NotificationResult nr); + + /** + * Allows the {@link EventRelay} to report when it receives an unexpected + * exception, which may be fatal and which may make it stop receiving + * notifications. + * + * @param t The unexpected exception received while {@link EventRelay} was running. + */ + public void failed(Throwable t); + + /** + * Allows the {@link EventRelay} to report when it receives an unexpected + * exception that is not fatal. For example, a notification received is not + * serializable or its class is not found. + * + * @param e The unexpected exception received while notifications are being received. + */ + public void nonFatal(Exception e); +} diff --git a/src/share/classes/javax/management/event/EventRelay.java b/src/share/classes/javax/management/event/EventRelay.java new file mode 100644 index 000000000..a8c486636 --- /dev/null +++ b/src/share/classes/javax/management/event/EventRelay.java @@ -0,0 +1,80 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.event; + +import java.io.IOException; +import java.util.concurrent.Executors; // for javadoc +import java.util.concurrent.ScheduledFuture; + +/** + * This interface is used to specify a way to receive + * notifications from a remote MBean server and then to forward the notifications + * to an {@link EventClient}. + * + * @see Custom notification + * transports + */ +public interface EventRelay { + /** + * Returns an identifier that is used by this {@code EventRelay} to identify + * the client when communicating with the {@link EventClientDelegateMBean}. + *

    This identifier is obtained by calling + * {@link EventClientDelegateMBean#addClient(String, Object[], String[]) + * EventClientDelegateMBean.addClient}. + *

    It is the {@code EventRelay} that calls {@code EventClientDelegateMBean} to obtain + * the client identifier because it is the {@code EventRelay} that decides + * how to get notifications from the {@code EventClientDelegateMBean}, + * by creating the appropriate {@link EventForwarder}. + * + * @return A client identifier. + * @throws IOException If an I/O error occurs when communicating with + * the {@code EventClientDelegateMBean}. + */ + public String getClientId() throws IOException; + + /** + * This method is called by {@link EventClient} to register a callback + * to receive notifications from an {@link EventClientDelegateMBean} object. + * A {@code null} value is allowed, which means that the {@code EventClient} suspends + * reception of notifications, so that the {@code EventRelay} can decide to stop receiving + * notifications from its {@code EventForwarder}. + * + * @param eventReceiver An {@link EventClient} callback to receive + * events. + */ + public void setEventReceiver(EventReceiver eventReceiver); + + /** + * Stops receiving and forwarding notifications and performs any necessary + * cleanup. After calling this method, the {@link EventClient} will never + * call any other methods of this object. + * + * @throws IOException If an I/O exception appears. + * + * @see EventClient#close + */ + public void stop() throws IOException; +} diff --git a/src/share/classes/javax/management/event/EventSubscriber.java b/src/share/classes/javax/management/event/EventSubscriber.java new file mode 100644 index 000000000..242634555 --- /dev/null +++ b/src/share/classes/javax/management/event/EventSubscriber.java @@ -0,0 +1,381 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.event; + +import com.sun.jmx.remote.util.ClassLogger; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import javax.management.InstanceNotFoundException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerDelegate; +import javax.management.MBeanServerNotification; +import javax.management.Notification; +import javax.management.NotificationBroadcaster; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.Query; +import javax.management.QueryEval; +import javax.management.QueryExp; + +/** + *

    An object that can be used to subscribe for notifications from all MBeans + * in an MBeanServer that match a pattern. For example, to listen for + * notifications from all MBeans in the MBeanServer {@code mbs} that match + * {@code com.example:type=Controller,name=*} you could write:

    + * + *
    + * EventSubscriber subscriber = EventSubscriber.getEventSubscriber(mbs);
    + * ObjectName pattern = new ObjectName("com.example:type=Controller,name=*");
    + * NotificationListener myListener = ...;
    + * NotificationFilter myFilter = null;  // or whatever
    + * Object handback = null;              // or whatever
    + * subscriber.subscribe(pattern, myListener, myFilter, myHandback);
    + * 
    + */ +public class EventSubscriber implements EventConsumer { + /** + * Returns an {@code EventSubscriber} object to subscribe for notifications + * from the given {@code MBeanServer}. Calling this method more + * than once with the same parameter may or may not return the same object. + * + * @param mbs the {@code MBeanServer} containing MBeans to be subscribed to. + * @return An {@code EventSubscriber} object. + * + * @throws NullPointerException if mbs is null. + */ + public static EventSubscriber getEventSubscriber(MBeanServer mbs) { + if (mbs == null) + throw new NullPointerException("Null MBeanServer"); + + EventSubscriber eventSubscriber = null; + synchronized (subscriberMap) { + final WeakReference wrf = subscriberMap.get(mbs); + eventSubscriber = (wrf == null) ? null : wrf.get(); + + if (eventSubscriber == null) { + eventSubscriber = new EventSubscriber(mbs); + + subscriberMap.put(mbs, + new WeakReference(eventSubscriber)); + } + } + + return eventSubscriber; + } + + private EventSubscriber(final MBeanServer mbs) { + logger.trace("EventSubscriber", "create a new one"); + this.mbeanServer = mbs; + + Exception x = null; + try { + AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public Void run() throws Exception { + mbs.addNotificationListener( + MBeanServerDelegate.DELEGATE_NAME, + myMBeanServerListener, null, null); + return null; + } + }); + } catch (PrivilegedActionException ex) { + x = ex.getException(); + } + + // handle possible exceptions. + // + // Fail unless x is null or x is instance of InstanceNotFoundException + // The logic here is that if the MBeanServerDelegate is not present, + // we will assume that the connection will not emit any + // MBeanServerNotifications. + // + if (x != null && !(x instanceof InstanceNotFoundException)) { + if (x instanceof RuntimeException) + throw (RuntimeException) x; + throw new RuntimeException( + "Can't add listener to MBean server delegate: " + x, x); + } + } + + public void subscribe(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws IOException { + + if (logger.traceOn()) + logger.trace("subscribe", "" + name); + + if (name == null) + throw new IllegalArgumentException("Null MBean name"); + + if (listener == null) + throw new IllegalArgumentException("Null listener"); + + final ListenerInfo li = new ListenerInfo(listener, filter, handback); + List list; + + Map> map; + Set names; + if (name.isPattern()) { + map = patternSubscriptionMap; + names = mbeanServer.queryNames(name, notificationBroadcasterExp); + } else { + map = exactSubscriptionMap; + names = Collections.singleton(name); + } + + synchronized (map) { + list = map.get(name); + if (list == null) { + list = new ArrayList(); + map.put(name, list); + } + list.add(li); + } + + for (ObjectName mbeanName : names) { + try { + mbeanServer.addNotificationListener(mbeanName, + listener, + filter, + handback); + } catch (Exception e) { + logger.fine("subscribe", "addNotificationListener", e); + } + } + } + + public void unsubscribe(ObjectName name, + NotificationListener listener) + throws ListenerNotFoundException, IOException { + + if (logger.traceOn()) + logger.trace("unsubscribe", "" + name); + + if (name == null) + throw new IllegalArgumentException("Null MBean name"); + + if (listener == null) + throw new ListenerNotFoundException(); + + Map> map; + Set names; + + if (name.isPattern()) { + map = patternSubscriptionMap; + names = mbeanServer.queryNames(name, notificationBroadcasterExp); + } else { + map = exactSubscriptionMap; + names = Collections.singleton(name); + } + + final ListenerInfo li = new ListenerInfo(listener, null, null); + List list; + synchronized (map) { + list = map.get(name); + if (list == null || !list.remove(li)) + throw new ListenerNotFoundException(); + + if (list.isEmpty()) + map.remove(name); + } + + for (ObjectName mbeanName : names) { + try { + mbeanServer.removeNotificationListener(mbeanName, li.listener); + } catch (Exception e) { + logger.fine("unsubscribe", "removeNotificationListener", e); + } + } + } + + // --------------------------------- + // private stuff + // --------------------------------- + // used to receive MBeanServerNotification + private NotificationListener myMBeanServerListener = + new NotificationListener() { + public void handleNotification(Notification n, Object hb) { + if (!(n instanceof MBeanServerNotification) || + !MBeanServerNotification. + REGISTRATION_NOTIFICATION.equals(n.getType())) { + return; + } + + final ObjectName name = + ((MBeanServerNotification)n).getMBeanName(); + try { + if (!mbeanServer.isInstanceOf(name, + NotificationBroadcaster.class.getName())) { + return; + } + } catch (Exception e) { + // The only documented exception is InstanceNotFoundException, + // which could conceivably happen if the MBean is unregistered + // immediately after being registered. + logger.fine("myMBeanServerListener.handleNotification", + "isInstanceOf", e); + return; + } + + final List listeners = new ArrayList(); + + // If there are subscribers for the exact name that has just arrived + // then add their listeners to the list. + synchronized (exactSubscriptionMap) { + List exactListeners = exactSubscriptionMap.get(name); + if (exactListeners != null) + listeners.addAll(exactListeners); + } + + // For every subscription pattern that matches the new name, + // add all the listeners for that pattern to "listeners". + synchronized (patternSubscriptionMap) { + for (ObjectName on : patternSubscriptionMap.keySet()) { + if (on.apply(name)) { + listeners.addAll(patternSubscriptionMap.get(on)); + } + } + } + + // Add all the listeners just found to the new MBean. + for (ListenerInfo li : listeners) { + try { + mbeanServer.addNotificationListener( + name, + li.listener, + li.filter, + li.handback); + } catch (Exception e) { + logger.fine("myMBeanServerListener.handleNotification", + "addNotificationListener", e); + } + } + } + }; + + private static class ListenerInfo { + public final NotificationListener listener; + public final NotificationFilter filter; + public final Object handback; + + public ListenerInfo(NotificationListener listener, + NotificationFilter filter, + Object handback) { + + if (listener == null) + throw new IllegalArgumentException("Null listener"); + + this.listener = listener; + this.filter = filter; + this.handback = handback; + } + + /* Two ListenerInfo instances are equal if they have the same + * NotificationListener. This means that we can use List.remove + * to implement the two-argument removeNotificationListener. + */ + @Override + public boolean equals(Object o) { + if (o == this) + return true; + + if (!(o instanceof ListenerInfo)) + return false; + + return listener.equals(((ListenerInfo)o).listener); + } + + @Override + public int hashCode() { + return listener.hashCode(); + } + } + + // --------------------------------- + // private methods + // --------------------------------- + // --------------------------------- + // private variables + // --------------------------------- + private final MBeanServer mbeanServer; + + private final Map> exactSubscriptionMap = + new HashMap>(); + private final Map> patternSubscriptionMap = + new HashMap>(); + + + + // trace issues + private static final ClassLogger logger = + new ClassLogger("javax.management.event", "EventSubscriber"); + + // Compatibility code, so we can run on Tiger: + private static final QueryExp notificationBroadcasterExp; + static { + QueryExp broadcasterExp; + try { + final Method m = Query.class.getMethod("isInstanceOf", + new Class[] {String.class}); + broadcasterExp = (QueryExp)m.invoke(Query.class, + new Object[] {NotificationBroadcaster.class.getName()}); + } catch (Exception e) { + broadcasterExp = new BroadcasterQueryExp(); + } + notificationBroadcasterExp = broadcasterExp; + } + private static class BroadcasterQueryExp extends QueryEval implements QueryExp { + private static final long serialVersionUID = 1234L; + public boolean apply(ObjectName name) { + try { + return getMBeanServer().isInstanceOf( + name, NotificationBroadcaster.class.getName()); + } catch (Exception e) { + return false; + } + } + } + + private static final + Map> subscriberMap = + new WeakHashMap>(); +} diff --git a/src/share/classes/javax/management/event/FetchingEventForwarder.java b/src/share/classes/javax/management/event/FetchingEventForwarder.java new file mode 100644 index 000000000..528775f15 --- /dev/null +++ b/src/share/classes/javax/management/event/FetchingEventForwarder.java @@ -0,0 +1,151 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.event; + +import com.sun.jmx.event.EventBuffer; +import com.sun.jmx.remote.util.ClassLogger; +import java.io.IOException; +import java.util.List; +import javax.management.Notification; +import javax.management.remote.NotificationResult; +import javax.management.remote.TargetedNotification; + +/** + * This class is used by {@link FetchingEventRelay}. When + * {@link FetchingEventRelay} calls {@link + * EventClientDelegateMBean#addClient(String, Object[], String[])} to get a new + * client identifier, it uses + * this class name as the first argument to ask {@code EventClientDelegateMBean} + * to create an object of this class. + * Then {@code EventClientDelegateMBean} forwards client notifications + * to this object. + * When {@link FetchingEventRelay} calls + * {@link EventClientDelegateMBean#fetchNotifications(String, long, int, long)} + * to fetch notifications, the {@code EventClientDelegateMBean} will forward + * the call to this object. + */ +public class FetchingEventForwarder implements EventForwarder { + + /** + * Construct a new {@code FetchingEventForwarder} with the given + * buffer size. + * @param bufferSize the size of the buffer that will store notifications + * until they have been fetched and acknowledged by the client. + */ + public FetchingEventForwarder(int bufferSize) { + if (logger.traceOn()) { + logger.trace("Constructor", "buffer size is "+bufferSize); + } + + buffer = new EventBuffer(bufferSize); + this.bufferSize = bufferSize; + } + + /** + * Called by an {@link EventClientDelegateMBean} to forward a user call + * {@link EventClientDelegateMBean#fetchNotifications(String, long, int, long)}. + * A call of this method is considered to acknowledge reception of all + * notifications whose sequence numbers are less the + * {@code startSequenceNumber}, so all these notifications can be deleted + * from this object. + * + * @param startSequenceNumber The first sequence number to + * consider. + * @param timeout The maximum waiting time in milliseconds. + * If no notifications have arrived after this period of time, the call + * will return with an empty list of notifications. + * @param maxNotifs The maximum number of notifications to return. + */ + public NotificationResult fetchNotifications(long startSequenceNumber, + int maxNotifs, long timeout) { + if (logger.traceOn()) { + logger.trace("fetchNotifications", + startSequenceNumber+" "+ + maxNotifs+" "+ + timeout); + } + + return buffer.fetchNotifications(startSequenceNumber, + timeout, + maxNotifs); + } + + /** + * {@inheritDoc} + * In this implementation, the notification is stored in the local buffer + * waiting for {@link #fetchNotifications fetchNotifications} to pick + * it up. + */ + public void forward(Notification n, Integer listenerId) throws IOException { + if (logger.traceOn()) { + logger.trace("forward", n+" "+listenerId); + } + + buffer.add(new TargetedNotification(n, listenerId)); + } + + public void close() throws IOException { + if (logger.traceOn()) { + logger.trace("close", ""); + } + + buffer.close(); + } + + public void setClientId(String clientId) throws IOException { + if (logger.traceOn()) { + logger.trace("setClientId", clientId); + } + this.clientId = clientId; + } + + /** + * Sets a user specific list to save notifications in server side + * before forwarding to an FetchingEventRelay in client side. + *

    This method should be called before any notification is + * forwarded to this forwader. + * + * @param list a user specific list to save notifications + */ + protected void setList(List list) { + if (logger.traceOn()) { + logger.trace("setList", ""); + } + + if (clientId == null) { + buffer = new EventBuffer(bufferSize, list); + } else { + throw new IllegalStateException(); + } + } + + private EventBuffer buffer; + private int bufferSize; + private String clientId; + + private static final ClassLogger logger = + new ClassLogger("javax.management.event", "FetchingEventForwarder"); +} diff --git a/src/share/classes/javax/management/event/FetchingEventRelay.java b/src/share/classes/javax/management/event/FetchingEventRelay.java new file mode 100644 index 000000000..2b65f9b12 --- /dev/null +++ b/src/share/classes/javax/management/event/FetchingEventRelay.java @@ -0,0 +1,389 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.event; + +import com.sun.jmx.event.DaemonThreadFactory; +import com.sun.jmx.event.RepeatedSingletonJob; +import com.sun.jmx.remote.util.ClassLogger; +import java.io.IOException; +import java.io.NotSerializableException; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import javax.management.MBeanException; +import javax.management.remote.NotificationResult; + +/** + * This class is an implementation of the {@link EventRelay} interface. It calls + * {@link EventClientDelegateMBean#fetchNotifications + * fetchNotifications(String, long, int, long)} to get + * notifications and then forwards them to an {@link EventReceiver} object. + * + * @since JMX 2.0 + */ +public class FetchingEventRelay implements EventRelay { + /** + * The default buffer size: {@value #DEFAULT_BUFFER_SIZE}. + */ + public final static int DEFAULT_BUFFER_SIZE = 1000; + + /** + * The default waiting timeout: {@value #DEFAULT_WAITING_TIMEOUT} + * in millseconds when fetching notifications from + * an {@code EventClientDelegateMBean}. + */ + public final static long DEFAULT_WAITING_TIMEOUT = 60000; + + /** + * The default maximum notifications to fetch every time: + * {@value #DEFAULT_MAX_NOTIFICATIONS}. + */ + public final static int DEFAULT_MAX_NOTIFICATIONS = DEFAULT_BUFFER_SIZE; + + /** + * Constructs a default {@code FetchingEventRelay} object by using the default + * configuration: {@code DEFAULT_BUFFER_SIZE}, {@code DEFAULT_WAITING_TIMEOUT} + * {@code DEFAULT_MAX_NOTIFICATIONS}. A single thread is created + * to do fetching. + * + * @param delegate The {@code EventClientDelegateMBean} to work with. + * @throws IOException If failed to work with the {@code delegate}. + * @throws MBeanException if unable to add a client to the remote + * {@code EventClientDelegateMBean} (see {@link + * EventClientDelegateMBean#addClient(String, Object[], String[]) + * EventClientDelegateMBean.addClient}). + * @throws IllegalArgumentException If {@code delegate} is {@code null}. + */ + public FetchingEventRelay(EventClientDelegateMBean delegate) + throws IOException, MBeanException { + this(delegate, null); + } + + /** + * Constructs a {@code FetchingEventRelay} object by using the default + * configuration: {@code DEFAULT_BUFFER_SIZE}, {@code DEFAULT_WAITING_TIMEOUT} + * {@code DEFAULT_MAX_NOTIFICATIONS}, with a user-specific executor to do + * the fetching. + * + * @param delegate The {@code EventClientDelegateMBean} to work with. + * @param executor Used to do the fetching. A new thread is created if + * {@code null}. + * @throws IOException If failed to work with the {@code delegate}. + * @throws MBeanException if unable to add a client to the remote + * {@code EventClientDelegateMBean} (see {@link + * EventClientDelegateMBean#addClient(String, Object[], String[]) + * EventClientDelegateMBean.addClient}). + * @throws IllegalArgumentException If {@code delegate} is {@code null}. + */ + public FetchingEventRelay(EventClientDelegateMBean delegate, + Executor executor) throws IOException, MBeanException { + this(delegate, + DEFAULT_BUFFER_SIZE, + DEFAULT_WAITING_TIMEOUT, + DEFAULT_MAX_NOTIFICATIONS, + executor); + } + + /** + * Constructs a {@code FetchingEventRelay} object with user-specific + * configuration and executor to fetch notifications via the + * {@link EventClientDelegateMBean}. + * + * @param delegate The {@code EventClientDelegateMBean} to work with. + * @param bufferSize The buffer size for saving notifications in + * {@link EventClientDelegateMBean} before they are fetched. + * @param timeout The waiting time in millseconds when fetching + * notifications from an {@code EventClientDelegateMBean}. + * @param maxNotifs The maximum notifications to fetch every time. + * @param executor Used to do the fetching. A new thread is created if + * {@code null}. + * @throws IOException if failed to communicate with the {@code delegate}. + * @throws MBeanException if unable to add a client to the remote + * {@code EventClientDelegateMBean} (see {@link + * EventClientDelegateMBean#addClient(String, Object[], String[]) + * EventClientDelegateMBean.addClient}). + * @throws IllegalArgumentException If {@code delegate} is {@code null}. + */ + public FetchingEventRelay(EventClientDelegateMBean delegate, + int bufferSize, + long timeout, + int maxNotifs, + Executor executor) throws IOException, MBeanException { + this(delegate, + bufferSize, + timeout, + maxNotifs, + executor, + FetchingEventForwarder.class.getName(), + new Object[] {bufferSize}, + new String[] {int.class.getName()}); + } + + /** + * Constructs a {@code FetchingEventRelay} object with user-specific + * configuration and executor to fetch notifications via the + * {@link EventClientDelegateMBean}. + * + * @param delegate The {@code EventClientDelegateMBean} to work with. + * @param bufferSize The buffer size for saving notifications in + * {@link EventClientDelegateMBean} before they are fetched. + * @param timeout The waiting time in millseconds when fetching + * notifications from an {@code EventClientDelegateMBean}. + * @param maxNotifs The maximum notifications to fetch every time. + * @param executor Used to do the fetching. + * @param forwarderName the class name of a user specific EventForwarder + * to create in server to forward notifications to this object. The class + * should be a subclass of the class {@link FetchingEventForwarder}. + * @param params the parameters passed to create {@code forwarderName} + * @param sig the signature of the {@code params} + * @throws IOException if failed to communicate with the {@code delegate}. + * @throws MBeanException if unable to add a client to the remote + * {@code EventClientDelegateMBean} (see {@link + * EventClientDelegateMBean#addClient(String, Object[], String[]) + * EventClientDelegateMBean.addClient}). + * @throws IllegalArgumentException if {@code bufferSize} or + * {@code maxNotifs} is less than {@code 1} + * @throws NullPointerException if {@code delegate} is {@code null}. + */ + public FetchingEventRelay(EventClientDelegateMBean delegate, + int bufferSize, + long timeout, + int maxNotifs, + Executor executor, + String forwarderName, + Object[] params, + String[] sig) throws IOException, MBeanException { + + if (logger.traceOn()) { + logger.trace("FetchingEventRelay", "delegateMBean "+ + bufferSize+" "+ + timeout+" "+ + maxNotifs+" "+ + executor+" "+ + forwarderName+" "); + } + + if(delegate == null) { + throw new NullPointerException("Null EventClientDelegateMBean!"); + } + + + if (bufferSize<=1) { + throw new IllegalArgumentException( + "The bufferSize cannot be less than 1, no meaning."); + } + + if (maxNotifs<=1) { + throw new IllegalArgumentException( + "The maxNotifs cannot be less than 1, no meaning."); + } + + clientId = delegate.addClient( + forwarderName, + params, + sig); + + this.delegate = delegate; + this.timeout = timeout; + this.maxNotifs = maxNotifs; + + if (executor == null) { + executor = Executors.newSingleThreadScheduledExecutor( + daemonThreadFactory); + } + this.executor = executor; + if (executor instanceof ScheduledExecutorService) + leaseScheduler = (ScheduledExecutorService) executor; + else { + leaseScheduler = Executors.newSingleThreadScheduledExecutor( + daemonThreadFactory); + } + + startSequenceNumber = 0; + fetchingJob = new MyJob(); + } + + public void setEventReceiver(EventReceiver eventReceiver) { + if (logger.traceOn()) { + logger.trace("setEventReceiver", ""+eventReceiver); + } + + EventReceiver old = this.eventReceiver; + synchronized(fetchingJob) { + this.eventReceiver = eventReceiver; + if (old == null && eventReceiver != null) + fetchingJob.resume(); + } + } + + public String getClientId() { + return clientId; + } + + public void stop() { + if (logger.traceOn()) { + logger.trace("stop", ""); + } + synchronized(fetchingJob) { + if (stopped) { + return; + } + + stopped = true; + clientId = null; + } + } + + private class MyJob extends RepeatedSingletonJob { + public MyJob() { + super(executor); + } + + public boolean isSuspended() { + boolean b; + synchronized(FetchingEventRelay.this) { + b = stopped || + (eventReceiver == null) || + (clientId == null); + } + + if (logger.traceOn()) { + logger.trace("-MyJob-isSuspended", ""+b); + } + return b; + } + + public void task() { + logger.trace("MyJob-task", ""); + long fetchTimeout = timeout; + NotificationResult nr = null; + Throwable failedExcep = null; + try { + nr = delegate.fetchNotifications( + clientId, + startSequenceNumber, + maxNotifs, + fetchTimeout); + } catch (Exception e) { + if (isSerialOrClassNotFound(e)) { + try { + nr = fetchOne(); + } catch (Exception ee) { + failedExcep = e; + } + } else { + failedExcep = e; + } + } + + if (failedExcep != null && + !isSuspended()) { + logger.fine("MyJob-task", + "Failed to fetch notification, stopping...", failedExcep); + try { + eventReceiver.failed(failedExcep); + } catch (Exception e) { + logger.trace( + "MyJob-task", "exception from eventReceiver.failed", e); + } + + stop(); + } else if (nr != null) { + try { + eventReceiver.receive(nr); + } catch (RuntimeException e) { + logger.trace( + "MyJob-task", + "exception delivering notifs to EventClient", e); + } finally { + startSequenceNumber = nr.getNextSequenceNumber(); + } + } + } + } + + private NotificationResult fetchOne() throws Exception { + logger.trace("fetchOne", ""); + + while (true) { + try { + // 1 notif to skip possible missing class + return delegate.fetchNotifications( + clientId, + startSequenceNumber, + 1, + timeout); + } catch (Exception e) { + if (isSerialOrClassNotFound(e)) { // skip and continue + if (logger.traceOn()) { + logger.trace("fetchOne", "Ignore", e); + } + eventReceiver.nonFatal(e); + startSequenceNumber++; + } else { + throw e; + } + } + } + } + + static boolean isSerialOrClassNotFound(Exception e) { + Throwable cause = e.getCause(); + + while (cause != null && + !(cause instanceof ClassNotFoundException) && + !(cause instanceof NotSerializableException)) { + cause = cause.getCause(); + } + + return (cause instanceof ClassNotFoundException || + cause instanceof NotSerializableException); + } + + private long startSequenceNumber = 0; + private EventReceiver eventReceiver = null; + private final EventClientDelegateMBean delegate; + private String clientId; + private boolean stopped = false; + private volatile ScheduledFuture leaseRenewalFuture; + + private final Executor executor; + private final ScheduledExecutorService leaseScheduler; + private final MyJob fetchingJob; + + private final long timeout; + private final int maxNotifs; + + private static final ClassLogger logger = + new ClassLogger("javax.management.event", + "FetchingEventRelay"); + private static final ThreadFactory daemonThreadFactory = + new DaemonThreadFactory("FetchingEventRelay-executor"); +} diff --git a/src/share/classes/javax/management/event/ListenerInfo.java b/src/share/classes/javax/management/event/ListenerInfo.java new file mode 100644 index 000000000..063946c90 --- /dev/null +++ b/src/share/classes/javax/management/event/ListenerInfo.java @@ -0,0 +1,169 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.event; + +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; + +/** + * This class specifies all the information required to register a user listener into + * a remote MBean server. This class is not serializable because a user listener + * is not serialized in order to be sent to the remote server. + * + * @since JMX 2.0 + */ +public class ListenerInfo { + + /** + * Constructs a {@code ListenerInfo} object. + * + * @param name The name of the MBean to which the listener should + * be added. + * @param listener The listener object which will handle the + * notifications emitted by the MBean. + * @param filter The filter object. If the filter is null, no + * filtering will be performed before notifications are handled. + * @param handback The context to be sent to the listener when a + * notification is emitted. + * @param isSubscription If true, the listener is subscribed via + * an {@code EventManager}. Otherwise it is added to a registered MBean. + */ + public ListenerInfo(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback, + boolean isSubscription) { + this.name = name; + this.listener = listener; + this.filter = filter; + this.handback = handback; + this.isSubscription = isSubscription; + } + + /** + * Returns an MBean or an MBean pattern that the listener listens to. + * + * @return An MBean or an MBean pattern. + */ + public ObjectName getObjectName() { + return name; + } + + /** + * Returns the listener. + * + * @return The listener. + */ + public NotificationListener getListener() { + return listener; + } + + /** + * Returns the listener filter. + * + * @return The filter. + */ + public NotificationFilter getFilter() { + return filter; + } + + /** + * Returns the listener handback. + * + * @return The handback. + */ + public Object getHandback() { + return handback; + } + + /** + * Returns true if this is a subscription listener. + * + * @return True if this is a subscription listener. + * + * @see EventClient#addListeners + */ + public boolean isSubscription() { + return isSubscription; + } + + /** + *

    Indicates whether some other object is "equal to" this one. + * The return value is true if and only if {@code o} is an instance of + * {@code ListenerInfo} and has equal values for all of its properties.

    + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof ListenerInfo)) { + return false; + } + + ListenerInfo li = (ListenerInfo)o; + + boolean ret = name.equals(li.name) && + (listener == li.listener) && + (isSubscription == li.isSubscription); + + if (filter != null) { + ret &= filter.equals(li.filter); + } else { + ret &= (li.filter == null); + } + + if (handback != null) { + ret &= handback.equals(li.handback); + } else { + ret &= (li.handback == null); + } + + return ret; + } + + @Override + public int hashCode() { + return name.hashCode() + listener.hashCode(); + } + + @Override + public String toString() { + return name.toString() + "_" + + listener + "_" + + filter + "_" + + handback + "_" + + isSubscription; + } + + private final ObjectName name; + private final NotificationListener listener; + private final NotificationFilter filter; + private final Object handback; + private final boolean isSubscription; +} diff --git a/src/share/classes/javax/management/event/NotificationManager.java b/src/share/classes/javax/management/event/NotificationManager.java new file mode 100644 index 000000000..90a522c0f --- /dev/null +++ b/src/share/classes/javax/management/event/NotificationManager.java @@ -0,0 +1,136 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.event; + +import java.io.IOException; +import javax.management.InstanceNotFoundException; +import javax.management.ListenerNotFoundException; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; + +/** + * This interface specifies methods to add and remove notification listeners + * on named MBeans. + */ +public interface NotificationManager { + /** + *

    Adds a listener to a registered MBean. + * Notifications emitted by the MBean will be forwarded + * to the listener. + * + * @param name The name of the MBean on which the listener should + * be added. + * @param listener The listener object which will handle the + * notifications emitted by the registered MBean. + * @param filter The filter object. If filter is null, no + * filtering will be performed before handling notifications. + * @param handback The context to be sent to the listener when a + * notification is emitted. + * + * @exception InstanceNotFoundException The MBean name provided + * does not match any of the registered MBeans. + * @exception IOException A communication problem occurred when + * talking to the MBean server. + * + * @see #removeNotificationListener(ObjectName, NotificationListener) + * @see #removeNotificationListener(ObjectName, NotificationListener, + * NotificationFilter, Object) + */ + public void addNotificationListener(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, + IOException; + + /** + *

    Removes a listener from a registered MBean.

    + * + *

    If the listener is registered more than once, perhaps with + * different filters or callbacks, this method will remove all + * those registrations. + * + * @param name The name of the MBean on which the listener should + * be removed. + * @param listener The listener to be removed. + * + * @exception InstanceNotFoundException The MBean name provided + * does not match any of the registered MBeans. + * @exception ListenerNotFoundException The listener is not + * registered in the MBean. + * @exception IOException A communication problem occurred when + * talking to the MBean server. + * + * @see #addNotificationListener(ObjectName, NotificationListener, + * NotificationFilter, Object) + */ + public void removeNotificationListener(ObjectName name, + NotificationListener listener) + throws InstanceNotFoundException, + ListenerNotFoundException, + IOException; + + /** + *

    Removes a listener from a registered MBean.

    + * + *

    The MBean must have a listener that exactly matches the + * given listener, filter, and + * handback parameters. If there is more than one + * such listener, only one is removed.

    + * + *

    The filter and handback parameters + * may be null if and only if they are null in a listener to be + * removed.

    + * + * @param name The name of the MBean on which the listener should + * be removed. + * @param listener The listener to be removed. + * @param filter The filter that was specified when the listener + * was added. + * @param handback The handback that was specified when the + * listener was added. + * + * @exception InstanceNotFoundException The MBean name provided + * does not match any of the registered MBeans. + * @exception ListenerNotFoundException The listener is not + * registered in the MBean, or it is not registered with the given + * filter and handback. + * @exception IOException A communication problem occurred when + * talking to the MBean server. + * + * @see #addNotificationListener(ObjectName, NotificationListener, + * NotificationFilter, Object) + * + */ + public void removeNotificationListener(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, + ListenerNotFoundException, + IOException; +} diff --git a/src/share/classes/javax/management/event/RMIPushEventForwarder.java b/src/share/classes/javax/management/event/RMIPushEventForwarder.java new file mode 100644 index 000000000..2018f98ad --- /dev/null +++ b/src/share/classes/javax/management/event/RMIPushEventForwarder.java @@ -0,0 +1,198 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.event; + +import com.sun.jmx.event.DaemonThreadFactory; +import com.sun.jmx.event.RepeatedSingletonJob; +import com.sun.jmx.remote.util.ClassLogger; +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import javax.management.Notification; +import javax.management.remote.NotificationResult; +import javax.management.remote.TargetedNotification; + + +/** + * This class is used by {@link RMIPushEventRelay}. When + * {@link RMIPushEventRelay} calls {@link + * EventClientDelegateMBean#addClient(String, Object[], String[])} to get a new + * client identifier, it uses this class name as the + * first argument to ask {@code EventClientDelegateMBean} to create an object of + * this class. + * Then {@code EventClientDelegateMBean} forwards client notifications + * to this object. This object then continues forwarding the notifications + * to the {@code RMIPushEventRelay}. + */ +public class RMIPushEventForwarder implements EventForwarder { + private static final int DEFAULT_BUFFER_SIZE = 6000; + + /** + * Creates a new instance of {@code RMIPushEventForwarder}. + * + * @param receiver An RMI stub exported to receive notifications + * from this object for its {@link RMIPushEventRelay}. + * + * @param bufferSize The maximum number of notifications to store + * while waiting for the last remote send to complete. + */ + public RMIPushEventForwarder(RMIPushServer receiver, int bufferSize) { + if (logger.traceOn()) { + logger.trace("RMIEventForwarder", "new one"); + } + + if (bufferSize < 0) { + throw new IllegalArgumentException( + "Negative buffer size: " + bufferSize); + } else if (bufferSize == 0) + bufferSize = DEFAULT_BUFFER_SIZE; + + if (receiver == null) { + throw new NullPointerException(); + } + + this.receiver = receiver; + this.buffer = new ArrayBlockingQueue(bufferSize); + } + + public void forward(Notification n, Integer listenerId) { + if (logger.traceOn()) { + logger.trace("forward", "to the listener: "+listenerId); + } + synchronized(sendingJob) { + TargetedNotification tn = new TargetedNotification(n, listenerId); + while (!buffer.offer(tn)) { + buffer.remove(); + passed++; + } + sendingJob.resume(); + } + } + + public void close() { + if (logger.traceOn()) { + logger.trace("close", "called"); + } + + synchronized(sendingJob) { + ended = true; + buffer.clear(); + } + } + + public void setClientId(String clientId) { + if (logger.traceOn()) { + logger.trace("setClientId", clientId); + } + } + + private class SendingJob extends RepeatedSingletonJob { + public SendingJob() { + super(executor); + } + + public boolean isSuspended() { + return ended || buffer.isEmpty(); + } + + public void task() { + final long earliest = passed; + + List tns = + new ArrayList(buffer.size()); + synchronized(sendingJob) { + buffer.drainTo(tns); + passed += tns.size(); + } + + if (logger.traceOn()) { + logger.trace("SendingJob-task", "sending: "+tns.size()); + } + + if (!tns.isEmpty()) { + try { + TargetedNotification[] tnArray = + new TargetedNotification[tns.size()]; + tns.toArray(tnArray); + receiver.receive(new NotificationResult(earliest, passed, tnArray)); + } catch (RemoteException e) { + if (logger.debugOn()) { + logger.debug("SendingJob-task", + "Got exception to forward notifs.", e); + } + + long currentLost = passed - earliest; + if (FetchingEventRelay.isSerialOrClassNotFound(e)) { + // send one by one + long tmpPassed = earliest; + for (TargetedNotification tn : tns) { + try { + receiver.receive(new NotificationResult(earliest, + ++tmpPassed, new TargetedNotification[]{tn})); + } catch (RemoteException ioee) { + logger.trace( + "SendingJob-task", "send to remote", ioee); + // sends nonFatal notifs? + } + } + + currentLost = passed - tmpPassed; + } + + if (currentLost > 0) { // inform of the lost. + try { + receiver.receive(new NotificationResult( + passed, passed, + new TargetedNotification[]{})); + } catch (RemoteException ee) { + logger.trace( + "SendingJob-task", "receiver.receive", ee); + } + } + } + } + } + } + + private long passed = 0; + + private static final ExecutorService executor = + Executors.newCachedThreadPool( + new DaemonThreadFactory("RMIEventForwarder Executor")); + private final SendingJob sendingJob = new SendingJob(); + + private final BlockingQueue buffer; + + private final RMIPushServer receiver; + private boolean ended = false; + + private static final ClassLogger logger = + new ClassLogger("javax.management.event", "RMIEventForwarder"); +} diff --git a/src/share/classes/javax/management/event/RMIPushEventRelay.java b/src/share/classes/javax/management/event/RMIPushEventRelay.java new file mode 100644 index 000000000..51af99597 --- /dev/null +++ b/src/share/classes/javax/management/event/RMIPushEventRelay.java @@ -0,0 +1,161 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.event; + +import com.sun.jmx.remote.util.ClassLogger; +import java.io.IOException; +import java.rmi.NoSuchObjectException; +import java.rmi.RemoteException; +import java.rmi.server.UnicastRemoteObject; +import java.rmi.server.RMIClientSocketFactory; +import java.rmi.server.RMIServerSocketFactory; +import javax.management.MBeanException; +import javax.management.remote.NotificationResult; + +/** + * This class is an implementation of the {@link EventRelay} interface, using + * push mode. It exports an RMI object that {@link RMIPushEventForwarder} uses + * to forward notifications. + * + * @since JMX 2.0 + */ +public class RMIPushEventRelay implements EventRelay { + /** + * Constructs a default {@code RMIPushEventRelay} object + * and exports its {@linkplain RMIPushServer notification + * receiver} on any free port. This constructor is equivalent + * to {@link #RMIPushEventRelay(EventClientDelegateMBean, + * int, RMIClientSocketFactory, RMIServerSocketFactory, int) + * RMIPushEventRelay(delegate, 0, null, null, <default buffer + * size>)}. + * + * @param delegate The {@link EventClientDelegateMBean} proxy to work with. + * @throws IOException if failed to communicate with + * {@link EventClientDelegateMBean}. + * @throws MBeanException if the {@link EventClientDelegateMBean} failed + * to create an {@code EventForwarder} for this object. + */ + public RMIPushEventRelay(EventClientDelegateMBean delegate) + throws IOException, MBeanException { + this(delegate, 0, null, null, 0); + } + + /** + * Constructs a {@code RMIPushEventRelay} object and exports its + * {@linkplain RMIPushServer notification receiver} on a specified port. + * + * @param delegate The {@link EventClientDelegateMBean} proxy to work with. + * @param port The port used to export an RMI object to receive notifications + * from a server. If the port is zero, an anonymous port is used. + * @param csf The client socket factory used to export the RMI object. + * Can be null. + * @param ssf The server socket factory used to export the RMI object. + * Can be null. + * @param bufferSize The number of notifications held on the server + * while waiting for the previous transmission to complete. A value of + * zero means the default buffer size. + * + * @throws IOException if failed to communicate with + * {@link EventClientDelegateMBean}. + * @throws MBeanException if the {@link EventClientDelegateMBean} failed + * to create an {@code EventForwarder} for this object. + * + * @see RMIPushEventForwarder#RMIPushEventForwarder(RMIPushServer, int) + */ + public RMIPushEventRelay(EventClientDelegateMBean delegate, + int port, + RMIClientSocketFactory csf, + RMIServerSocketFactory ssf, + int bufferSize) + throws IOException, MBeanException { + + UnicastRemoteObject.exportObject(exportedReceiver, port, csf, ssf); + + clientId = delegate.addClient( + RMIPushEventForwarder.class.getName(), + new Object[] {exportedReceiver, bufferSize}, + new String[] {RMIPushServer.class.getName(), + int.class.getName()}); + } + + public String getClientId() { + return clientId; + } + + public void setEventReceiver(EventReceiver receiver) { + if (logger.traceOn()) { + logger.trace("setEventReceiver", ""+receiver); + } + synchronized(lock) { + this.receiver = receiver; + } + } + + public void stop() { + if (logger.traceOn()) { + logger.trace("stop", ""); + } + synchronized(lock) { + if (stopped) { + return; + } else { + stopped = true; + } + + if (clientId == null) { + return; + } + + try { + UnicastRemoteObject.unexportObject(exportedReceiver, true); + } catch (NoSuchObjectException nsoe) { + logger.fine("RMIPushEventRelay.stop", "unexport", nsoe); + // OK: we wanted it unexported, and apparently it already is + } + } + } + + private volatile String clientId; + private volatile EventReceiver receiver; + + private RMIPushServer exportedReceiver = new RMIPushServer() { + public void receive(NotificationResult nr) throws RemoteException { + if (logger.traceOn()) { + logger.trace("EventPusherImpl-receive",""); + } + receiver.receive(nr); + // Any exception will be sent back to the client. + } + }; + + private boolean stopped = false; + + private final int[] lock = new int[0]; + + private static final ClassLogger logger = + new ClassLogger("javax.management.event", + "PushEventRelay"); +} diff --git a/src/share/classes/javax/management/event/RMIPushServer.java b/src/share/classes/javax/management/event/RMIPushServer.java new file mode 100644 index 000000000..53bd63cc1 --- /dev/null +++ b/src/share/classes/javax/management/event/RMIPushServer.java @@ -0,0 +1,48 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.event; + +import java.rmi.Remote; +import java.rmi.RemoteException; +import javax.management.remote.NotificationResult; + +/** + * The {@link RMIPushEventRelay} exports an RMI object of this class and + * sends a client stub for that object to the associated + * {@link RMIPushEventForwarder} in a remote MBean server. The + * {@code RMIPushEventForwarder} then sends notifications to the + * RMI object. + */ +public interface RMIPushServer extends Remote { + /** + *

    Dispatch the notifications in {@code nr} to the {@link RMIPushEventRelay} + * associated with this object.

    + * @param nr the notification result to dispatch. + * @throws java.rmi.RemoteException if the remote invocation of this method + * failed. + */ + public void receive(NotificationResult nr) throws RemoteException; +} diff --git a/src/share/classes/javax/management/event/package-info.java b/src/share/classes/javax/management/event/package-info.java new file mode 100644 index 000000000..c4c7dbed6 --- /dev/null +++ b/src/share/classes/javax/management/event/package-info.java @@ -0,0 +1,312 @@ +/** + *

    Defines the Event Service, which provides extended support + * for JMX notifications.

    + * + *

    The Event Service provides greater control over + * notification handling than the default technique using {@link + * javax.management.MBeanServer#addNotificationListener(ObjectName, + * NotificationListener, NotificationFilter, Object) + * MBeanServer.addNotificationListener} or {@link + * javax.management.MBeanServerConnection#addNotificationListener(ObjectName, + * NotificationListener, NotificationFilter, Object) + * MBeanServerConnection.addNotificationListener}.

    + * + *

    Here are some reasons you may want to use the Event Service:

    + * + *
      + *
    • To receive notifications from a set of MBeans defined by an + * ObjectName pattern, such as {@code com.example.config:type=Cache,*}. + * + *
    • When the notification-handling behavior of the connector you are + * using does not match your requirements. For example, with the standard + * RMI connector you can lose notifications if there are very many of them + * in the MBean Server you are connected to, even if you are only listening + * for a small proportion of them. + * + *
    • To change the threading behavior of notification dispatch. + * + *
    • To define a different transport for notifications, for example to + * arrange for them to be delivered through the Java Message Service (JMS). The Event Service comes with + * one alternative transport as standard, a "push-mode" RMI transport. + * + *
    • To handle notifications on behalf of MBeans (often virtual) in a + * namespace. + *
    + * + *

    The Event Service is new in version 2.0 of the JMX API, which is the + * version introduced in version 7 of the Java SE platform. It is not usually + * possible to use the Event Service when connecting remotely to an + * MBean Server that is running an earlier version.

    + * + * + *

    Handling remote notifications with the Event + * Service

    + * + *

    Prior to version 2.0 of the JMX API, every connector + * had to include logic to handle notifications. The standard {@linkplain + * javax.management.remote.rmi RMI} and JMXMP connectors defined by JSR 160 handle notifications + * in a way that is not always appropriate for applications. Specifically, + * the connector server adds one listener to every MBean that might emit + * notifications, and adds all received notifications to a fixed-size + * buffer. This means that if there are very many notifications, a + * remote client may miss some, even if it is only registered for a + * very small subset of notifications. Furthermore, since every {@link + * javax.management.NotificationBroadcaster NotificationBroadcaster} MBean + * gets a listener from the connector server, MBeans cannot usefully optimize + * by only sending notifications when there is a listener. Finally, since + * the connector server uses just one listener per MBean, MBeans cannot + * impose custom behavior per listener, such as security checks or localized + * notifications.

    + * + *

    The Event Service does not have these restrictions. The RMI connector + * that is included in this version of the JMX API uses the Event Service by + * default, although it can be configured to have the previous behavior if + * required.

    + * + *

    The Event Service can be used with any connector via the + * method {@link javax.management.event.EventClient#getEventClientConnection + * EventClient.getEventClientConnection}, like this:

    + * + *
    + * JMXConnector conn = ...;
    + * MBeanServerConnection mbsc = conn.getMBeanServerConnection();
    + * MBeanServerConnection eventMbsc = EventClient.getEventClientConnection(mbsc);
    + * 
    + * + *

    If you add listeners using {@code eventMbsc.addNotificationListener} + * instead of {@code mbsc.addNotificationListener}, then they will be handled + * by the Event Service rather than by the connector's notification system.

    + * + *

    For the Event Service to work, either the {@link + * javax.management.event.EventClientDelegateMBean EventClientDelegateMBean} + * must be registered in the MBean Server, or the connector server must + * be configured to simulate the existence of this MBean, for example + * using {@link javax.management.event.EventClientDelegate#newForwarder() + * EventClientDelegate.newForwarder()}. The standard RMI connector is so + * configured by default. The {@code EventClientDelegateMBean} documentation + * has further details.

    + * + * + *

    Receiving notifications from a set of MBeans

    + * + *

    The Event Server allows you to receive notifications from every MBean + * that matches an {@link javax.management.ObjectName ObjectName} pattern. + * For local clients (in the same JVM as the MBean Server), the {@link + * javax.management.event.EventSubscriber EventSubscriber} class can be used for + * this. For remote clients, or if the same code is to be used locally and + * remotely, use an + * {@link javax.management.event.EventClient EventClient}.

    + * + *

    EventSubscriber and EventClient correctly handle the case where a new + * MBean is registered under a name that matches the pattern. Notifications + * from the new MBean will also be received.

    + * + *

    Here is how to receive notifications from all MBeans in a local + * {@code MBeanServer} that match {@code com.example.config:type=Cache,*}:

    + * + *
    + * MBeanServer mbs = ...;
    + * NotificationListener listener = ...;
    + * ObjectName pattern = new ObjectName("com.example.config:type=Cache,*");
    + * EventSubscriber esub = EventSubscriber.getEventSubscriber(mbs);
    + * esub.{@link javax.management.event.EventSubscriber#subscribe
    + * subscribe}(pattern, listener, null, null);
    + * 
    + * + *

    Here is how to do the same thing remotely:

    + * + *
    + * MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection();
    + * EventClient events = new EventClient(mbsc);
    + * NotificationListener listener = ...;
    + * ObjectName pattern = new ObjectName("com.example.config:type=Cache,*");
    + * events.{@link javax.management.event.EventClient#subscribe
    + * subscribe}(pattern, listener, null, null);
    + * 
    + * + * + *

    Controlling threading behavior for notification + * dispatch

    + * + *

    The EventClient class can be used to control threading of listener + * dispatch. For example, to arrange for all listeners to be invoked + * in the same thread, you can create an {@code EventClient} like this:

    + * + *
    + * MBeanServerConnection mbsc = ...;
    + * Executor singleThreadExecutor = {@link
    + * java.util.concurrent.Executors#newSingleThreadExecutor()
    + * Executors.newSingleThreadExecutor}();
    + * EventClient events = new EventClient(
    + *         mbsc, null, singleThreadExecutor, EventClient.DEFAULT_LEASE_TIMEOUT);
    + * events.addNotificationListener(...);
    + * events.subscribe(...);
    + * 
    + * + * + *

    Leasing

    + * + *

    The {@code EventClient} uses a lease mechanism to ensure + * that resources are eventually released on the server even if the client + * does not explicitly clean up. (This can happen through network + * partitioning, for example.)

    + * + *

    When an {@code EventClient} registers with the {@code + * EventClientDelegateMBean} using one of the {@code addClient} methods, + * an initial lease is created with a default expiry time. The {@code + * EventClient} requests an explicit lease shortly after that, with a + * configurable expiry time. Then the {@code EventClient} periodically + * renews the lease before it expires, typically about half way + * through the lifetime of the lease. If at any point the lease reaches + * the expiry time of the last renewal then it expires, and {@code + * EventClient} is unregistered as if it had called the {@link + * javax.management.event.EventClientDelegateMBean#removeClient removeClient} + * method.

    + * + * + *

    Custom notification transports

    + * + *

    When you create an {@code EventClient}, you can define the transport + * that it uses to deliver notifications. The transport might use the + * Java Message Service (JMS) or + * any other communication system. Specifying a transport is useful for + * example when you want different network behavior from the default, or + * different reliability guarantees. The default transport calls {@link + * javax.management.event.EventClientDelegateMBean#fetchNotifications + * EventClientDelegateMBean.fetchNotifications} repeatedly, which usually means + * that there must be a network connection permanently open between the client + * and the server. If the same client is connected to many servers this can + * cause scalability problems. If notifications are relatively rare, then + * JMS or the {@linkplain javax.management.event.RMIPushEventRelay push-mode + * RMI transport} may be more suitable.

    + * + *

    A transport is implemented by an {@link javax.management.event.EventRelay + * EventRelay} on the client side and a corresponding {@link + * javax.management.event.EventForwarder EventForwarder} + * on the server side. An example is the {@link + * javax.management.event.RMIPushEventRelay RMIPushEventRelay} and its + * {@link javax.management.event.RMIPushEventForwarder RMIPushEventForwarder}.

    + * + *

    To use a given transport with an {@code EventClient}, you first create + * an instance of its {@code EventRelay}. Typically the {@code EventRelay}'s + * constructor will have a parameter of type {@code MBeanServerConnection} + * or {@code EventClientDelegateMBean}, so that it can communicate with the + * {@code EventClientDelegateMBean} in the server. For example, the {@link + * javax.management.event.RMIPushEventForwarder RMIPushEventForwarder}'s constructors + * all take an {@code EventClientDelegateMBean} parameter, which is expected to + * be a {@linkplain javax.management.JMX#newMBeanProxy(MBeanServerConnection, + * ObjectName, Class) proxy} for the {@code EventClientDelegateMBean} in the + * server.

    + * + *

    When it is created, the {@code EventRelay} will call + * {@link javax.management.event.EventClientDelegateMBean#addClient(String, + * Object[], String[]) EventClientDelegateMBean.addClient}. It passes the + * name of the {@code EventForwarder} class and its constructor parameters. + * The {@code EventClientDelegateMBean} will instantiate this class using + * {@link javax.management.MBeanServer#instantiate(String, Object[], String[]) + * MBeanServer.instantiate}, and it will return a unique client id.

    + * + *

    Then you pass the newly-created {@code EventRelay} to one of the {@code + * EventClient} constructors, and you have an {@code EventClient} that uses the + * chosen transport.

    + * + *

    For example, when you create an {@code RMIPushEventRelay}, it + * uses {@code MBeanServerDelegateMBean.addClient} to create an {@code + * RMIEventForwarder} in the server. Notifications will then be delivered + * through an RMI communication from the {@code RMIEventForwarder} to the + * {@code RMIPushEventRelay}.

    + * + * + *

    Writing a custom transport

    + * + *

    To write a custom transport, you need to understand the sequence + * of events when an {@code EventRelay} and its corresponding {@code + * EventForwarder} are created, and when a notification is sent from the {@code + * EventForwarder} to the {@code EventRelay}.

    + * + *

    When an {@code EventRelay} is created:

    + * + *
      + *
    • The {@code EventRelay} must call {@code + * EventClientDelegateMBean.addClient} with the name of the {@code + * EventForwarder} and the constructor parameters.

      + * + *
    • {@code EventClientDelegateMBean.addClient} will do the following + * steps:

      + * + *
        + *
      • create the {@code EventForwarder} using {@code MBeanServer.instantiate}; + *
      • allocate a unique client id; + *
      • call the new {@code EventForwarder}'s {@link + * javax.management.event.EventForwarder#setClientId setClientId} method with + * the new client id; + *
      • return the client id to the caller. + *
      + * + *
    + * + *

    When an {@code EventClient} is created with an {@code EventRelay} + * parameter, it calls {@link javax.management.event.EventRelay#setEventReceiver + * EventRelay.setEventReceiver} with an {@code EventReceiver} that the + * {@code EventRelay} will use to deliver notifications.

    + * + *

    When a listener is added using the {@code EventClient}, the + * {@code EventRelay} and {@code EventForwarder} are not involved.

    + * + *

    When an MBean emits a notification and a listener has been added + * to that MBean using the {@code EventClient}:

    + * + *
      + *
    • The {@code EventForwarder}'s + * {@link javax.management.event.EventForwarder#forward forward} method + * is called with the notification and a listener id.

      + * + *
    • The {@code EventForwarder} sends the notification and listener id + * to the {@code EventRelay} using the custom transport.

      + * + *
    • The {@code EventRelay} delivers the notification by calling + * {@link javax.management.event.EventReceiver#receive EventReceiver.receive}.

      + *
    + * + *

    When the {@code EventClient} is closed ({@link + * javax.management.event.EventClient#close EventClient.close}):

    + * + *
      + *
    • The {@code EventClient} calls {@link + * javax.management.event.EventRelay#stop EventRelay.stop}.

      + * + *
    • The {@code EventClient} calls {@link + * javax.management.event.EventClientDelegateMBean#removeClient + * EventClientDelegateMBean.removeClient}.

      + * + *
    • The {@code EventClientDelegateMBean} removes any listeners it + * had added on behalf of this {@code EventClient}.

      + * + *
    • The {@code EventClientDelegateMBean} calls + * {@link javax.management.event.EventForwarder#close EventForwarder.close}.

      + *
    + * + * + *

    Threading and buffering

    + * + *

    The {@link javax.management.event.EventForwarder#forward + * EventForwarder.forward} method may be called in the thread that the + * source MBean is using to send its notification. MBeans can expect + * that notification sending does not block. Therefore a {@code forward} + * method will typically add the notification to a queue, with a separate + * thread that takes notifications off the queue and sends them.

    + * + *

    An {@code EventRelay} does not usually need to buffer notifications + * before giving them to + * {@link javax.management.event.EventReceiver#receive EventReceiver.receive}. + * Although by default each such notification will be sent to potentially-slow + * listeners, if this is a problem then an {@code Executor} can be given to + * the {@code EventClient} constructor to cause the listeners to be called + * in a different thread.

    + * + * @since 1.7 + */ + +package javax.management.event; diff --git a/src/share/classes/javax/management/loading/MLet.java b/src/share/classes/javax/management/loading/MLet.java index ba27646df..d6540591e 100644 --- a/src/share/classes/javax/management/loading/MLet.java +++ b/src/share/classes/javax/management/loading/MLet.java @@ -1154,21 +1154,29 @@ public class MLet extends java.net.URLClassLoader */ private synchronized String loadLibraryAsResource(String libname) { try { - InputStream is = getResourceAsStream(libname.replace(File.separatorChar,'/')); + InputStream is = getResourceAsStream( + libname.replace(File.separatorChar,'/')); if (is != null) { - File directory = new File(libraryDirectory); - directory.mkdirs(); - File file = File.createTempFile(libname + ".", null, directory); - file.deleteOnExit(); - FileOutputStream fileOutput = new FileOutputStream(file); - int c; - while ((c = is.read()) != -1) { - fileOutput.write(c); - } - is.close(); - fileOutput.close(); - if (file.exists()) { - return file.getAbsolutePath(); + try { + File directory = new File(libraryDirectory); + directory.mkdirs(); + File file = File.createTempFile(libname + ".", null, + directory); + file.deleteOnExit(); + FileOutputStream fileOutput = new FileOutputStream(file); + try { + int c; + while ((c = is.read()) != -1) { + fileOutput.write(c); + } + } finally { + fileOutput.close(); + } + if (file.exists()) { + return file.getAbsolutePath(); + } + } finally { + is.close(); } } } catch (Exception e) { diff --git a/src/share/classes/javax/management/modelmbean/ModelMBeanInfoSupport.java b/src/share/classes/javax/management/modelmbean/ModelMBeanInfoSupport.java index 6e4a3ed63..3655daadd 100644 --- a/src/share/classes/javax/management/modelmbean/ModelMBeanInfoSupport.java +++ b/src/share/classes/javax/management/modelmbean/ModelMBeanInfoSupport.java @@ -373,7 +373,7 @@ public class ModelMBeanInfoSupport extends MBeanInfo implements ModelMBeanInfo { "getDescriptors(String)", "Entry"); } - if ((inDescriptorType == null) || (inDescriptorType.isEmpty())) { + if ((inDescriptorType == null) || (inDescriptorType.equals(""))) { inDescriptorType = "all"; } @@ -616,7 +616,7 @@ public class ModelMBeanInfoSupport extends MBeanInfo implements ModelMBeanInfo { inDescriptor = new DescriptorSupport(); } - if ((inDescriptorType == null) || (inDescriptorType.isEmpty())) { + if ((inDescriptorType == null) || (inDescriptorType.equals(""))) { inDescriptorType = (String) inDescriptor.getFieldValue("descriptorType"); diff --git a/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java b/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java index 3c09c3ff2..7d99fba00 100644 --- a/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java +++ b/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java @@ -1123,7 +1123,7 @@ public class RequiredModelMBean if (tracing) { MODELMBEAN_LOGGER.logp(Level.FINER, RequiredModelMBean.class.getName(),"resolveMethod", - "resolving " + targetClass + "." + opMethodName); + "resolving " + targetClass.getName() + "." + opMethodName); } final Class[] argClasses; diff --git a/src/share/classes/javax/management/relation/RelationService.java b/src/share/classes/javax/management/relation/RelationService.java index 5950aafa5..98a4809ea 100644 --- a/src/share/classes/javax/management/relation/RelationService.java +++ b/src/share/classes/javax/management/relation/RelationService.java @@ -108,7 +108,7 @@ public class RelationService extends NotificationBroadcasterSupport // the value HashMap mapping: // -> ArrayList of // to track where a given MBean is referenced. - private Map>> + private final Map>> myRefedMBeanObjName2RelIdsMap = new HashMap>>(); @@ -1492,7 +1492,7 @@ public class RelationService extends NotificationBroadcasterSupport // Clones the list of notifications to be able to still receive new // notifications while proceeding those ones List localUnregNtfList; - synchronized(myUnregNtfList) { + synchronized(myRefedMBeanObjName2RelIdsMap) { localUnregNtfList = new ArrayList(myUnregNtfList); // Resets list diff --git a/src/share/classes/javax/management/remote/IdentityMBeanServerForwarder.java b/src/share/classes/javax/management/remote/IdentityMBeanServerForwarder.java new file mode 100644 index 000000000..0b1e996d7 --- /dev/null +++ b/src/share/classes/javax/management/remote/IdentityMBeanServerForwarder.java @@ -0,0 +1,303 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package javax.management.remote; + +import java.io.ObjectInputStream; +import java.util.Set; +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.NotCompliantMBeanException; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.OperationsException; +import javax.management.QueryExp; +import javax.management.ReflectionException; +import javax.management.loading.ClassLoaderRepository; + +/** + * An {@link MBeanServerForwarder} that forwards all {@link MBeanServer} + * operations unchanged to the next {@code MBeanServer} in the chain. + * This class is typically subclassed to override some but not all methods. + */ +public class IdentityMBeanServerForwarder implements MBeanServerForwarder { + + private MBeanServer next; + + /** + *

    Construct a forwarder that has no next {@code MBeanServer}. + * The resulting object will be unusable until {@link #setMBeanServer + * setMBeanServer} is called to establish the next item in the chain.

    + */ + public IdentityMBeanServerForwarder() { + } + + /** + *

    Construct a forwarder that forwards to the given {@code MBeanServer}. + * It is not an error for {@code next} to be null, but the resulting object + * will be unusable until {@link #setMBeanServer setMBeanServer} is called + * to establish the next item in the chain.

    + */ + public IdentityMBeanServerForwarder(MBeanServer next) { + this.next = next; + } + + public synchronized MBeanServer getMBeanServer() { + return next; + } + + public synchronized void setMBeanServer(MBeanServer mbs) { + next = mbs; + } + + private synchronized MBeanServer next() { + return next; + } + + public void unregisterMBean(ObjectName name) + throws InstanceNotFoundException, MBeanRegistrationException { + next().unregisterMBean(name); + } + + public AttributeList setAttributes(ObjectName name, + AttributeList attributes) + throws InstanceNotFoundException, ReflectionException { + return next().setAttributes(name, attributes); + } + + public void setAttribute(ObjectName name, Attribute attribute) + throws InstanceNotFoundException, AttributeNotFoundException, + InvalidAttributeValueException, MBeanException, + ReflectionException { + next().setAttribute(name, attribute); + } + + public void removeNotificationListener(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, ListenerNotFoundException { + next().removeNotificationListener(name, listener, filter, handback); + } + + public void removeNotificationListener(ObjectName name, + NotificationListener listener) + throws InstanceNotFoundException, ListenerNotFoundException { + next().removeNotificationListener(name, listener); + } + + public void removeNotificationListener(ObjectName name, ObjectName listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, ListenerNotFoundException { + next().removeNotificationListener(name, listener, filter, handback); + } + + public void removeNotificationListener(ObjectName name, ObjectName listener) + throws InstanceNotFoundException, ListenerNotFoundException { + next().removeNotificationListener(name, listener); + } + + public ObjectInstance registerMBean(Object object, ObjectName name) + throws InstanceAlreadyExistsException, MBeanRegistrationException, + NotCompliantMBeanException { + return next().registerMBean(object, name); + } + + public Set queryNames(ObjectName name, QueryExp query) { + return next().queryNames(name, query); + } + + public Set queryMBeans(ObjectName name, QueryExp query) { + return next().queryMBeans(name, query); + } + + public boolean isRegistered(ObjectName name) { + return next().isRegistered(name); + } + + public boolean isInstanceOf(ObjectName name, String className) + throws InstanceNotFoundException { + return next().isInstanceOf(name, className); + } + + public Object invoke(ObjectName name, String operationName, Object[] params, + String[] signature) + throws InstanceNotFoundException, MBeanException, + ReflectionException { + return next().invoke(name, operationName, params, signature); + } + + public Object instantiate(String className, ObjectName loaderName, + Object[] params, String[] signature) + throws ReflectionException, MBeanException, + InstanceNotFoundException { + return next().instantiate(className, loaderName, params, signature); + } + + public Object instantiate(String className, Object[] params, + String[] signature) + throws ReflectionException, MBeanException { + return next().instantiate(className, params, signature); + } + + public Object instantiate(String className, ObjectName loaderName) + throws ReflectionException, MBeanException, + InstanceNotFoundException { + return next().instantiate(className, loaderName); + } + + public Object instantiate(String className) + throws ReflectionException, MBeanException { + return next().instantiate(className); + } + + public ObjectInstance getObjectInstance(ObjectName name) + throws InstanceNotFoundException { + return next().getObjectInstance(name); + } + + public MBeanInfo getMBeanInfo(ObjectName name) + throws InstanceNotFoundException, IntrospectionException, + ReflectionException { + return next().getMBeanInfo(name); + } + + public Integer getMBeanCount() { + return next().getMBeanCount(); + } + + public String[] getDomains() { + return next().getDomains(); + } + + public String getDefaultDomain() { + return next().getDefaultDomain(); + } + + public ClassLoaderRepository getClassLoaderRepository() { + return next().getClassLoaderRepository(); + } + + public ClassLoader getClassLoaderFor(ObjectName mbeanName) + throws InstanceNotFoundException { + return next().getClassLoaderFor(mbeanName); + } + + public ClassLoader getClassLoader(ObjectName loaderName) + throws InstanceNotFoundException { + return next().getClassLoader(loaderName); + } + + public AttributeList getAttributes(ObjectName name, String[] attributes) + throws InstanceNotFoundException, ReflectionException { + return next().getAttributes(name, attributes); + } + + public Object getAttribute(ObjectName name, String attribute) + throws MBeanException, AttributeNotFoundException, + InstanceNotFoundException, ReflectionException { + return next().getAttribute(name, attribute); + } + + @Deprecated + public ObjectInputStream deserialize(String className, + ObjectName loaderName, + byte[] data) + throws InstanceNotFoundException, OperationsException, + ReflectionException { + return next().deserialize(className, loaderName, data); + } + + @Deprecated + public ObjectInputStream deserialize(String className, byte[] data) + throws OperationsException, ReflectionException { + return next().deserialize(className, data); + } + + @Deprecated + public ObjectInputStream deserialize(ObjectName name, byte[] data) + throws InstanceNotFoundException, OperationsException { + return next().deserialize(name, data); + } + + public ObjectInstance createMBean(String className, ObjectName name, + ObjectName loaderName, Object[] params, + String[] signature) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException { + return next().createMBean(className, name, loaderName, params, signature); + } + + public ObjectInstance createMBean(String className, ObjectName name, + Object[] params, String[] signature) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException { + return next().createMBean(className, name, params, signature); + } + + public ObjectInstance createMBean(String className, ObjectName name, + ObjectName loaderName) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException { + return next().createMBean(className, name, loaderName); + } + + public ObjectInstance createMBean(String className, ObjectName name) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException { + return next().createMBean(className, name); + } + + public void addNotificationListener(ObjectName name, ObjectName listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException { + next().addNotificationListener(name, listener, filter, handback); + } + + public void addNotificationListener(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException { + next().addNotificationListener(name, listener, filter, handback); + } +} diff --git a/src/share/classes/javax/management/remote/JMXConnector.java b/src/share/classes/javax/management/remote/JMXConnector.java index 9ac3aa043..c268dff2b 100644 --- a/src/share/classes/javax/management/remote/JMXConnector.java +++ b/src/share/classes/javax/management/remote/JMXConnector.java @@ -57,6 +57,26 @@ public interface JMXConnector extends Closeable { public static final String CREDENTIALS = "jmx.remote.credentials"; + /** + *

    Name of the attribute that specifies whether to use the + * {@linkplain javax.management.event Event Service} to handle + * notifications for this connector. The value associated with + * this attribute, if any, is a String, which must be equal, + * ignoring case, to {@code "true"} or {@code "false"}.

    + * + *

    Not all connectors will understand this attribute, but the + * standard {@linkplain javax.management.remote.rmi.RMIConnector + * RMI Connector} does.

    + * + *

    If this attribute is not present, then the system property of the + * same name ({@value}) is consulted. If that is not set + * either, then the Event Service is not used.

    + * + * @since 1.7 + */ + public static final String USE_EVENT_SERVICE = + "jmx.remote.use.event.service"; + /** *

    Establishes the connection to the connector server. This * method is equivalent to {@link #connect(Map) diff --git a/src/share/classes/javax/management/remote/JMXConnectorServer.java b/src/share/classes/javax/management/remote/JMXConnectorServer.java index 240bc3bdc..3a83fae45 100644 --- a/src/share/classes/javax/management/remote/JMXConnectorServer.java +++ b/src/share/classes/javax/management/remote/JMXConnectorServer.java @@ -26,17 +26,21 @@ package javax.management.remote; +import com.sun.jmx.remote.util.EnvHelp; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; +import javax.management.MBeanInfo; // for javadoc import javax.management.MBeanNotificationInfo; import javax.management.MBeanRegistration; import javax.management.MBeanServer; import javax.management.Notification; import javax.management.NotificationBroadcasterSupport; import javax.management.ObjectName; +import javax.management.event.EventClientDelegate; /** *

    Superclass of every connector server. A connector server is @@ -75,6 +79,48 @@ public abstract class JMXConnectorServer public static final String AUTHENTICATOR = "jmx.remote.authenticator"; + /** + *

    Name of the attribute that specifies whether this connector + * server can delegate notification handling to the + * {@linkplain javax.management.event Event Service}. + * The value associated with + * this attribute, if any, is a String, which must be equal, + * ignoring case, to {@code "true"} or {@code "false"}.

    + * + *

    Not all connector servers will understand this attribute, but the + * standard {@linkplain javax.management.remote.rmi.RMIConnectorServer + * RMI Connector Server} does.

    + * + *

    If this attribute is not present, then the system property of the + * same name ({@value}) is consulted. If that is not set + * either, then the Event Service is used if the connector server + * supports it.

    + * + * @since 1.7 + */ + public static final String DELEGATE_TO_EVENT_SERVICE = + "jmx.remote.delegate.event.service"; + + /** + *

    Name of the attribute that specifies whether this connector + * server simulates the existence of the {@link EventClientDelegate} + * MBean. The value associated with this attribute, if any, must + * be a string that is equal to {@code "true"} or {@code "false"}, + * ignoring case. If it is {@code "true"}, then the connector server + * will simulate an EventClientDelegate MBean, as described in {@link + * EventClientDelegate#newForwarder}. This MBean is needed for {@link + * javax.management.event.EventClient EventClient} to function correctly.

    + * + *

    Not all connector servers will understand this attribute, but the + * standard {@linkplain javax.management.remote.rmi.RMIConnectorServer + * RMI Connector Server} does. For a connector server that understands + * this attribute, the default value is {@code "true"}.

    + * + * @since 1.7 + */ + public static final String EVENT_CLIENT_DELEGATE_FORWARDER = + "jmx.remote.event.client.delegate.forwarder"; + /** *

    Constructs a connector server that will be registered as an * MBean in the MBean server it is attached to. This constructor @@ -89,34 +135,274 @@ public abstract class JMXConnectorServer /** *

    Constructs a connector server that is attached to the given * MBean server. A connector server that is created in this way - * can be registered in a different MBean server.

    + * can be registered in a different MBean server, or not registered + * in any MBean server.

    * * @param mbeanServer the MBean server that this connector server * is attached to. Null if this connector server will be attached * to an MBean server by being registered in it. */ public JMXConnectorServer(MBeanServer mbeanServer) { - this.mbeanServer = mbeanServer; + insertUserMBeanServer(mbeanServer); } /** *

    Returns the MBean server that this connector server is - * attached to.

    + * attached to, or the first in a chain of user-added + * {@link MBeanServerForwarder}s, if any.

    * * @return the MBean server that this connector server is attached * to, or null if it is not yet attached to an MBean server. + * + * @see #setMBeanServerForwarder + * @see #getSystemMBeanServer */ public synchronized MBeanServer getMBeanServer() { - return mbeanServer; + return userMBeanServer; } - public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf) - { + public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf) { + if (mbsf == null) + throw new IllegalArgumentException("Invalid null argument: mbsf"); + + if (userMBeanServer != null) + mbsf.setMBeanServer(userMBeanServer); + insertUserMBeanServer(mbsf); + } + + /** + *

    Remove a forwarder from the chain of forwarders. The forwarder can + * be in the system chain or the user chain. On successful return from + * this method, the first occurrence in the chain of an object that is + * {@linkplain Object#equals equal} to {@code mbsf} will have been + * removed.

    + * @param mbsf the forwarder to remove + * @throws NoSuchElementException if there is no occurrence of {@code mbsf} + * in the chain. + * @throws IllegalArgumentException if {@code mbsf} is null. + */ + public synchronized void removeMBeanServerForwarder(MBeanServerForwarder mbsf) { if (mbsf == null) throw new IllegalArgumentException("Invalid null argument: mbsf"); - if (mbeanServer != null) mbsf.setMBeanServer(mbeanServer); - mbeanServer = mbsf; + MBeanServerForwarder prev = null; + MBeanServer curr = systemMBeanServer; + while (curr instanceof MBeanServerForwarder && !mbsf.equals(curr)) { + prev = (MBeanServerForwarder) curr; + curr = prev.getMBeanServer(); + } + if (!(curr instanceof MBeanServerForwarder)) + throw new NoSuchElementException("MBeanServerForwarder not in chain"); + MBeanServerForwarder deleted = (MBeanServerForwarder) curr; + MBeanServer next = deleted.getMBeanServer(); + if (prev != null) + prev.setMBeanServer(next); + if (systemMBeanServer == deleted) + systemMBeanServer = next; + if (userMBeanServer == deleted) + userMBeanServer = next; + } + + /* + * Set userMBeanServer to mbs and arrange for the end of the chain of + * system MBeanServerForwarders to point to it. See the comment before + * the systemMBeanServer and userMBeanServer field declarations. + */ + private void insertUserMBeanServer(MBeanServer mbs) { + MBeanServerForwarder lastSystemMBSF = null; + for (MBeanServer mbsi = systemMBeanServer; + mbsi != userMBeanServer; + mbsi = lastSystemMBSF.getMBeanServer()) { + lastSystemMBSF = (MBeanServerForwarder) mbsi; + } + userMBeanServer = mbs; + if (lastSystemMBSF == null) + systemMBeanServer = mbs; + else + lastSystemMBSF.setMBeanServer(mbs); + } + + /** + *

    Returns the first item in the chain of system and then user + * forwarders. In the simplest case, a {@code JMXConnectorServer} + * is connected directly to an {@code MBeanServer}. But there can + * also be a chain of {@link MBeanServerForwarder}s between the two. + * This chain consists of two sub-chains: first the system chain + * and then the user chain. Incoming requests are given to the + * first forwarder in the system chain. Each forwarder can handle + * a request itself, or more usually forward it to the next forwarder, + * perhaps with some extra behavior such as logging or security + * checking before or after the forwarding. The last forwarder in + * the system chain is followed by the first forwarder in the user + * chain.

    + * + *

    The system chain is usually + * defined by a connector server based on the environment Map; + * see {@link JMXConnectorServerFactory#newJMXConnectorServer}. Allowing the + * connector server to define its forwarders in this way ensures that + * they are in the correct order - some forwarders need to be inserted + * before others for correct behavior. It is possible to modify the + * system chain, for example using {@link #setSystemMBeanServerForwarder} or + * {@link #removeMBeanServerForwarder}, but in that case the system + * chain is no longer guaranteed to be correct.

    + * + *

    The user chain is defined by calling {@link + * #setMBeanServerForwarder} to insert forwarders at the head of the user + * chain.

    + * + *

    If there are no forwarders in either chain, then both + * {@link #getMBeanServer()} and {@code getSystemMBeanServer()} will + * return the {@code MBeanServer} for this connector server. If there + * are forwarders in the user chain but not the system chain, then + * both methods will return the first forwarder in the user chain. + * If there are forwarders in the system chain but not the user chain, + * then {@code getSystemMBeanServer()} will return the first forwarder + * in the system chain, and {@code getMBeanServer()} will return the + * {@code MBeanServer} for this connector server. Finally, if there + * are forwarders in each chain then {@code getSystemMBeanServer()} + * will return the first forwarder in the system chain, and {@code + * getMBeanServer()} will return the first forwarder in the user chain.

    + * + *

    This code illustrates how the chains can be traversed:

    + * + *
    +     * JMXConnectorServer cs;
    +     * System.out.println("system chain:");
    +     * MBeanServer mbs = cs.getSystemMBeanServer();
    +     * while (true) {
    +     *     if (mbs == cs.getMBeanServer())
    +     *         System.out.println("user chain:");
    +     *     if (!(mbs instanceof MBeanServerForwarder))
    +     *         break;
    +     *     MBeanServerForwarder mbsf = (MBeanServerForwarder) mbs;
    +     *     System.out.println("--forwarder: " + mbsf);
    +     *     mbs = mbsf.getMBeanServer();
    +     * }
    +     * System.out.println("--MBean Server");
    +     * 
    + * + * @return the first item in the system chain of forwarders. + * + * @see #setSystemMBeanServerForwarder + */ + public synchronized MBeanServer getSystemMBeanServer() { + return systemMBeanServer; + } + + /** + *

    Inserts an object that intercepts requests for the MBean server + * that arrive through this connector server. This object will be + * supplied as the MBeanServer for any new connection + * created by this connector server. Existing connections are + * unaffected.

    + * + *

    This method can be called more than once with different + * {@link MBeanServerForwarder} objects. The result is a chain + * of forwarders. The last forwarder added is the first in the chain.

    + * + *

    This method modifies the system chain of {@link MBeanServerForwarder}s. + * Usually user code should change the user chain instead, via + * {@link #setMBeanServerForwarder}.

    + * + *

    Not all connector servers support a system chain of forwarders. + * Calling this method on a connector server that does not will produce an + * {@link UnsupportedOperationException}.

    + * + *

    Suppose {@code mbs} is the result of {@link #getSystemMBeanServer()} + * before calling this method. If {@code mbs} is not null, then + * {@code mbsf.setMBeanServer(mbs)} will be called. If doing so + * produces an exception, this method throws the same exception without + * any other effect. If {@code mbs} is null, or if the call to + * {@code mbsf.setMBeanServer(mbs)} succeeds, then this method will + * return normally and {@code getSystemMBeanServer()} will then return + * {@code mbsf}.

    + * + *

    The result of {@link #getMBeanServer()} is unchanged by this method.

    + * + * @param mbsf the new MBeanServerForwarder. + * + * @throws IllegalArgumentException if the call to {@link + * MBeanServerForwarder#setMBeanServer mbsf.setMBeanServer} fails + * with IllegalArgumentException, or if + * mbsf is null. + * + * @throws UnsupportedOperationException if + * {@link #supportsSystemMBeanServerForwarder} returns false. + * + * @see #getSystemMBeanServer() + */ + public synchronized void setSystemMBeanServerForwarder( + MBeanServerForwarder mbsf) { + if (mbsf == null) + throw new IllegalArgumentException("Invalid null argument: mbsf"); + mustSupportSystemMBSF(); + + if (systemMBeanServer != null) + mbsf.setMBeanServer(systemMBeanServer); + systemMBeanServer = mbsf; + } + + /** + *

    Returns true if this connector server supports a system chain of + * {@link MBeanServerForwarder}s. The default implementation of this + * method returns false. Connector servers that do support the system + * chain must override this method to return true. + * + * @return true if this connector server supports the system chain of + * forwarders. + */ + public boolean supportsSystemMBeanServerForwarder() { + return false; + } + + private void mustSupportSystemMBSF() { + if (!supportsSystemMBeanServerForwarder()) { + throw new UnsupportedOperationException( + "System MBeanServerForwarder not supported by this " + + "connector server"); + } + } + + /** + *

    Install {@link MBeanServerForwarder}s in the system chain + * based on the attributes in the given {@code Map}. A connector + * server that {@linkplain #supportsSystemMBeanServerForwarder supports} + * a system chain of {@code MBeanServerForwarder}s can call this method + * to add forwarders to that chain based on the contents of {@code env}. + * In order:

    + * + *
      + * + *
    • If {@link #EVENT_CLIENT_DELEGATE_FORWARDER} is absent, or is + * present with the value {@code "true"}, then a forwarder with the + * functionality of {@link EventClientDelegate#newForwarder} is inserted + * at the start of the system chain.
    • + * + *
    + * + *

    For {@code EVENT_CLIENT_DELEGATE_FORWARDER}, if the + * attribute is absent from the {@code Map} and a system property + * of the same name is defined, then the value of the system + * property is used as if it were in the {@code Map}. + * + *

    Attributes in {@code env} that are not listed above are ignored + * by this method.

    + * + * @throws UnsupportedOperationException if {@link + * #supportsSystemMBeanServerForwarder} is false. + */ + protected void installStandardForwarders(Map env) { + mustSupportSystemMBSF(); + + // Remember that forwarders must be added in reverse order! + + boolean ecd = EnvHelp.computeBooleanFromString( + env, EVENT_CLIENT_DELEGATE_FORWARDER, false, true); + + if (ecd) { + MBeanServerForwarder mbsf = EventClientDelegate.newForwarder(); + setSystemMBeanServerForwarder(mbsf); + } } public String[] getConnectionIds() { @@ -359,8 +645,8 @@ public abstract class JMXConnectorServer ObjectName name) { if (mbs == null || name == null) throw new NullPointerException("Null MBeanServer or ObjectName"); - if (mbeanServer == null) { - mbeanServer = mbs; + if (userMBeanServer == null) { + insertUserMBeanServer(mbs); myName = name; } return name; @@ -394,10 +680,53 @@ public abstract class JMXConnectorServer myName = null; } - /** - * The MBeanServer used by this server to execute a client request. + /* + * Fields describing the chains of forwarders (MBeanServerForwarders). + * In the general case, the forwarders look something like this: + * + * systemMBeanServer userMBeanServer + * | | + * v v + * mbsf1 -> mbsf2 -> mbsf3 -> mbsf4 -> mbsf5 -> mbs + * + * Here, each mbsfi is an MBeanServerForwarder, and the arrows + * illustrate its getMBeanServer() method. The last MBeanServerForwarder + * can point to an MBeanServer that is not instanceof MBeanServerForwarder, + * here mbs. + * + * Initially, the chain can be empty if this JMXConnectorServer was + * constructed without an MBeanServer. In this case, both systemMBS + * and userMBS will be null. If there is initially an MBeanServer, + * then both systemMBS and userMBS will point to it. + * + * Whenever userMBS is changed, the system chain must be updated. If there + * are forwarders in the system chain (between systemMBS and userMBS in the + * picture above), then the last one must point to the old value of userMBS + * (possibly null). It must be updated to point to the new value. If there + * are no forwarders in the system chain, then systemMBS must be updated to + * the new value of userMBS. The invariant is that starting from systemMBS + * and repeatedly calling MBSF.getMBeanServer() you will end up at + * userMBS. The implication is that you will not see any MBeanServer + * object on the way that is not also an MBeanServerForwarder. + * + * The method insertUserMBeanServer contains the logic to change userMBS + * and adjust the system chain appropriately. + * + * If userMBS is null and this JMXConnectorServer is registered in an + * MBeanServer, then userMBS becomes that MBeanServer, and the system + * chain must be updated as just described. + * + * When systemMBS is updated, there is no effect on userMBS. The system + * chain may contain forwarders even though the user chain is empty + * (there is no MBeanServer). In that case an attempt to forward an + * incoming request through the chain will fall off the end and fail with a + * NullPointerException. Usually a connector server will refuse to start() + * if it is not attached to an MBS, so this situation should not arise. */ - private MBeanServer mbeanServer = null; + + private MBeanServer userMBeanServer; + + private MBeanServer systemMBeanServer; /** * The name used to registered this server in an MBeanServer. diff --git a/src/share/classes/javax/management/remote/JMXConnectorServerFactory.java b/src/share/classes/javax/management/remote/JMXConnectorServerFactory.java index a641f3c98..4de8cbf5e 100644 --- a/src/share/classes/javax/management/remote/JMXConnectorServerFactory.java +++ b/src/share/classes/javax/management/remote/JMXConnectorServerFactory.java @@ -35,10 +35,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import java.util.ServiceLoader; import javax.management.MBeanServer; -import javax.management.ObjectName; /** *

    Factory to create JMX API connector servers. There @@ -172,7 +170,8 @@ public class JMXConnectorServerFactory { * loader MBean name. This class loader is used to deserialize objects in * requests received from the client, possibly after consulting an * MBean-specific class loader. The value associated with this - * attribute is an instance of {@link ObjectName}.

    + * attribute is an instance of {@link javax.management.ObjectName + * ObjectName}.

    */ public static final String DEFAULT_CLASS_LOADER_NAME = "jmx.remote.default.class.loader.name"; diff --git a/src/share/classes/javax/management/remote/JMXConnectorServerMBean.java b/src/share/classes/javax/management/remote/JMXConnectorServerMBean.java index 048e5a921..dcc41c8da 100644 --- a/src/share/classes/javax/management/remote/JMXConnectorServerMBean.java +++ b/src/share/classes/javax/management/remote/JMXConnectorServerMBean.java @@ -105,23 +105,34 @@ public interface JMXConnectorServerMBean { public boolean isActive(); /** - *

    Adds an object that intercepts requests for the MBean server + *

    Inserts an object that intercepts requests for the MBean server * that arrive through this connector server. This object will be * supplied as the MBeanServer for any new connection * created by this connector server. Existing connections are * unaffected.

    * - *

    If this connector server is already associated with an + *

    This method can be called more than once with different + * {@link MBeanServerForwarder} objects. The result is a chain + * of forwarders. The last forwarder added is the first in the chain. + * In more detail:

    + * + *
      + *
    • If this connector server is already associated with an * MBeanServer object, then that object is given to * {@link MBeanServerForwarder#setMBeanServer * mbsf.setMBeanServer}. If doing so produces an exception, this * method throws the same exception without any other effect.

      * - *

      If this connector is not already associated with an + *

    • If this connector is not already associated with an * MBeanServer object, or if the * mbsf.setMBeanServer call just mentioned succeeds, * then mbsf becomes this connector server's * MBeanServer.

      + *
    + * + *

    A connector server may support two chains of forwarders, + * a system chain and a user chain. See {@link + * JMXConnectorServer#setSystemMBeanServerForwarder} for details.

    * * @param mbsf the new MBeanServerForwarder. * @@ -129,6 +140,8 @@ public interface JMXConnectorServerMBean { * MBeanServerForwarder#setMBeanServer mbsf.setMBeanServer} fails * with IllegalArgumentException. This includes the * case where mbsf is null. + * + * @see JMXConnectorServer#setSystemMBeanServerForwarder */ public void setMBeanServerForwarder(MBeanServerForwarder mbsf); diff --git a/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java b/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java index bff48451f..8f5119242 100644 --- a/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java +++ b/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java @@ -25,10 +25,12 @@ package javax.management.remote.rmi; +import com.sun.jmx.mbeanserver.Util; import static com.sun.jmx.mbeanserver.Util.cast; import com.sun.jmx.remote.internal.ServerCommunicatorAdmin; import com.sun.jmx.remote.internal.ServerNotifForwarder; import com.sun.jmx.remote.security.JMXSubjectDomainCombiner; +import com.sun.jmx.remote.security.NotificationAccessController; import com.sun.jmx.remote.security.SubjectDelegator; import com.sun.jmx.remote.util.ClassLoaderWithRepository; import com.sun.jmx.remote.util.ClassLogger; @@ -36,6 +38,7 @@ import com.sun.jmx.remote.util.EnvHelp; import com.sun.jmx.remote.util.OrderClassLoaders; import java.io.IOException; +import java.lang.reflect.UndeclaredThrowableException; import java.rmi.MarshalledObject; import java.rmi.UnmarshalException; import java.rmi.server.Unreferenced; @@ -56,19 +59,24 @@ import javax.management.InstanceAlreadyExistsException; import javax.management.InstanceNotFoundException; import javax.management.IntrospectionException; import javax.management.InvalidAttributeValueException; +import javax.management.JMX; import javax.management.ListenerNotFoundException; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanRegistrationException; import javax.management.MBeanServer; import javax.management.NotCompliantMBeanException; +import javax.management.Notification; import javax.management.NotificationFilter; import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.management.QueryExp; import javax.management.ReflectionException; import javax.management.RuntimeOperationsException; -import javax.management.loading.ClassLoaderRepository; +import javax.management.event.EventClientDelegate; +import javax.management.event.EventClientDelegateMBean; +import javax.management.event.EventClientNotFoundException; +import javax.management.event.FetchingEventForwarder; import javax.management.remote.JMXServerErrorException; import javax.management.remote.NotificationResult; import javax.management.remote.TargetedNotification; @@ -149,28 +157,16 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { new PrivilegedAction() { public ClassLoaderWithRepository run() { return new ClassLoaderWithRepository( - getClassLoaderRepository(), - dcl); + mbeanServer.getClassLoaderRepository(), + dcl); } }); - serverCommunicatorAdmin = new RMIServerCommunicatorAdmin(EnvHelp.getServerConnectionTimeout(env)); this.env = env; } - private synchronized ServerNotifForwarder getServerNotifFwd() { - // Lazily created when first use. Mainly when - // addNotificationListener is first called. - if (serverNotifForwarder == null) - serverNotifForwarder = - new ServerNotifForwarder(mbeanServer, - env, - rmiServer.getNotifBuffer(), - connectionId); - return serverNotifForwarder; - } public String getConnectionId() throws IOException { // We should call reqIncomming() here... shouldn't we? @@ -181,6 +177,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { final boolean debug = logger.debugOn(); final String idstr = (debug?"["+this.toString()+"]":null); + final SubscriptionManager mgr; synchronized (this) { if (terminated) { if (debug) logger.debug("close",idstr + " already terminated."); @@ -195,11 +192,12 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { serverCommunicatorAdmin.terminate(); } - if (serverNotifForwarder != null) { - serverNotifForwarder.terminate(); - } + mgr = subscriptionManager; + subscriptionManager = null; } + if (mgr != null) mgr.terminate(); + rmiServer.clientClosed(this); if (debug) logger.debug("close",idstr + " closed."); @@ -955,8 +953,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { int i=0; ClassLoader targetCl; NotificationFilter[] filterValues = - new NotificationFilter[names.length]; - Object params[]; + new NotificationFilter[names.length]; Integer[] ids = new Integer[names.length]; final boolean debug=logger.debugOn(); @@ -991,8 +988,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { // remove all registered listeners for (int j=0; j action = - new PrivilegedAction() { - public NotificationResult run() { - return getServerNotifFwd().fetchNotifs(csn, t, mn); + + final PrivilegedExceptionAction action = + new PrivilegedExceptionAction() { + public NotificationResult run() throws IOException { + return doFetchNotifs(csn, t, mn); } }; - if (acc == null) - return action.run(); - else - return AccessController.doPrivileged(action, acc); + try { + if (acc == null) + return action.run(); + else + return AccessController.doPrivileged(action, acc); + } catch (IOException x) { + throw x; + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + // should not happen + throw new UndeclaredThrowableException(x); + } + } finally { serverCommunicatorAdmin.rspOutgoing(); } } + /** + * This is an abstraction class that let us use the legacy + * ServerNotifForwarder and the new EventClientDelegateMBean + * indifferently. + **/ + private static interface SubscriptionManager { + public void removeNotificationListener(ObjectName name, Integer id) + throws InstanceNotFoundException, ListenerNotFoundException, IOException; + public void removeNotificationListener(ObjectName name, Integer[] ids) + throws Exception; + public NotificationResult fetchNotifications(long csn, long timeout, int maxcount) + throws IOException; + public Integer addNotificationListener(ObjectName name, NotificationFilter filter) + throws InstanceNotFoundException, IOException; + public void terminate() + throws IOException; + } + + /** + * A SubscriptionManager that uses a ServerNotifForwarder. + **/ + private static class LegacySubscriptionManager implements SubscriptionManager { + private final ServerNotifForwarder forwarder; + LegacySubscriptionManager(ServerNotifForwarder forwarder) { + this.forwarder = forwarder; + } + + public void removeNotificationListener(ObjectName name, Integer id) + throws InstanceNotFoundException, ListenerNotFoundException, + IOException { + forwarder.removeNotificationListener(name,id); + } + + public void removeNotificationListener(ObjectName name, Integer[] ids) + throws Exception { + forwarder.removeNotificationListener(name,ids); + } + + public NotificationResult fetchNotifications(long csn, long timeout, int maxcount) { + return forwarder.fetchNotifs(csn,timeout,maxcount); + } + + public Integer addNotificationListener(ObjectName name, + NotificationFilter filter) + throws InstanceNotFoundException, IOException { + return forwarder.addNotificationListener(name,filter); + } + + public void terminate() { + forwarder.terminate(); + } + } + + /** + * A SubscriptionManager that uses an EventClientDelegateMBean. + **/ + private static class EventSubscriptionManager + implements SubscriptionManager { + private final MBeanServer mbeanServer; + private final EventClientDelegateMBean delegate; + private final NotificationAccessController notifAC; + private final boolean checkNotificationEmission; + private final String clientId; + private final String connectionId; + + EventSubscriptionManager( + MBeanServer mbeanServer, + EventClientDelegateMBean delegate, + Map env, + String clientId, + String connectionId) { + this.mbeanServer = mbeanServer; + this.delegate = delegate; + this.notifAC = EnvHelp.getNotificationAccessController(env); + this.checkNotificationEmission = + EnvHelp.computeBooleanFromString( + env, "jmx.remote.x.check.notification.emission", false); + this.clientId = clientId; + this.connectionId = connectionId; + } + + @SuppressWarnings("serial") // no serialVersionUID + private class AccessControlFilter implements NotificationFilter { + private final NotificationFilter wrapped; + private final ObjectName name; + + AccessControlFilter(ObjectName name, NotificationFilter wrapped) { + this.name = name; + this.wrapped = wrapped; + } + + public boolean isNotificationEnabled(Notification notification) { + try { + if (checkNotificationEmission) { + ServerNotifForwarder.checkMBeanPermission( + mbeanServer, name, "addNotificationListener"); + } + notifAC.fetchNotification( + connectionId, name, notification, getSubject()); + return (wrapped == null) ? true : + wrapped.isNotificationEnabled(notification); + } catch (InstanceNotFoundException e) { + return false; + } catch (SecurityException e) { + return false; + } + } + + } + + public Integer addNotificationListener( + ObjectName name, NotificationFilter filter) + throws InstanceNotFoundException, IOException { + if (notifAC != null) { + notifAC.addNotificationListener(connectionId, name, getSubject()); + filter = new AccessControlFilter(name, filter); + } + try { + return delegate.addListener(clientId,name,filter); + } catch (EventClientNotFoundException x) { + throw new IOException("Unknown clientId: "+clientId,x); + } + } + + public void removeNotificationListener(ObjectName name, Integer id) + throws InstanceNotFoundException, ListenerNotFoundException, + IOException { + if (notifAC != null) + notifAC.removeNotificationListener(connectionId, name, getSubject()); + try { + delegate.removeListenerOrSubscriber(clientId,id); + } catch (EventClientNotFoundException x) { + throw new IOException("Unknown clientId: "+clientId,x); + } + } + + public void removeNotificationListener(ObjectName name, Integer[] ids) + throws InstanceNotFoundException, ListenerNotFoundException, + IOException { + if (notifAC != null) + notifAC.removeNotificationListener(connectionId, name, getSubject()); + try { + for (Integer id : ids) + delegate.removeListenerOrSubscriber(clientId,id); + } catch (EventClientNotFoundException x) { + throw new IOException("Unknown clientId: "+clientId,x); + } + } + + public NotificationResult fetchNotifications(long csn, long timeout, + int maxcount) + throws IOException { + try { + // For some reason the delegate doesn't accept a negative + // sequence number. However legacy clients will always call + // fetchNotifications with a negative sequence number, when + // they call it for the first time. + // In that case, we will use 0 instead. + // + return delegate.fetchNotifications( + clientId, Math.max(csn, 0), maxcount, timeout); + } catch (EventClientNotFoundException x) { + throw new IOException("Unknown clientId: "+clientId,x); + } + } + + public void terminate() + throws IOException { + try { + delegate.removeClient(clientId); + } catch (EventClientNotFoundException x) { + throw new IOException("Unknown clientId: "+clientId,x); + } + } + + private static Subject getSubject() { + return Subject.getSubject(AccessController.getContext()); + } + } + + /** + * Creates a SubscriptionManager that uses either the legacy notifications + * mechanism (ServerNotifForwarder) or the new event service + * (EventClientDelegateMBean) depending on which option was passed in + * the connector's map. + **/ + private SubscriptionManager createSubscriptionManager() + throws IOException { + if (EnvHelp.delegateToEventService(env) && + mbeanServer.isRegistered(EventClientDelegate.OBJECT_NAME)) { + final EventClientDelegateMBean mbean = + JMX.newMBeanProxy(mbeanServer, + EventClientDelegate.OBJECT_NAME, + EventClientDelegateMBean.class); + String clientId; + try { + clientId = + mbean.addClient( + FetchingEventForwarder.class.getName(), + new Object[] {EnvHelp.getNotifBufferSize(env)}, + new String[] {int.class.getName()}); + } catch (Exception e) { + if (e instanceof IOException) + throw (IOException) e; + else + throw new IOException(e); + } + + // we're going to call remove client... + try { + mbean.lease(clientId, Long.MAX_VALUE); + } catch (EventClientNotFoundException x) { + throw new IOException("Unknown clientId: "+clientId,x); + } + return new EventSubscriptionManager(mbeanServer, mbean, env, + clientId, connectionId); + } else { + final ServerNotifForwarder serverNotifForwarder = + new ServerNotifForwarder(mbeanServer, + env, + rmiServer.getNotifBuffer(), + connectionId); + return new LegacySubscriptionManager(serverNotifForwarder); + } + } + + /** + * Lazy creation of a SubscriptionManager. + **/ + private synchronized SubscriptionManager getSubscriptionManager() + throws IOException { + // Lazily created when first use. Mainly when + // addNotificationListener is first called. + + if (subscriptionManager == null) { + subscriptionManager = createSubscriptionManager(); + } + return subscriptionManager; + } + + // calls SubscriptionManager. + private void doRemoveListener(ObjectName name, Integer id) + throws InstanceNotFoundException, ListenerNotFoundException, + IOException { + getSubscriptionManager().removeNotificationListener(name,id); + } + + // calls SubscriptionManager. + private void doRemoveListener(ObjectName name, Integer[] ids) + throws Exception { + getSubscriptionManager().removeNotificationListener(name,ids); + } + + // calls SubscriptionManager. + private NotificationResult doFetchNotifs(long csn, long timeout, int maxcount) + throws IOException { + return getSubscriptionManager().fetchNotifications(csn, timeout, maxcount); + } + + // calls SubscriptionManager. + private Integer doAddListener(ObjectName name, NotificationFilter filter) + throws InstanceNotFoundException, IOException { + return getSubscriptionManager().addNotificationListener(name,filter); + } + + /** *

    Returns a string representation of this object. In general, * the toString method returns a string that @@ -1313,16 +1586,6 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { // private methods //------------------------------------------------------------------------ - private ClassLoaderRepository getClassLoaderRepository() { - return - AccessController.doPrivileged( - new PrivilegedAction() { - public ClassLoaderRepository run() { - return mbeanServer.getClassLoaderRepository(); - } - }); - } - private ClassLoader getClassLoader(final ObjectName name) throws InstanceNotFoundException { try { @@ -1482,9 +1745,8 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { return null; case ADD_NOTIFICATION_LISTENERS: - return getServerNotifFwd().addNotificationListener( - (ObjectName)params[0], - (NotificationFilter)params[1]); + return doAddListener((ObjectName)params[0], + (NotificationFilter)params[1]); case ADD_NOTIFICATION_LISTENER_OBJECTNAME: mbeanServer.addNotificationListener((ObjectName)params[0], @@ -1494,9 +1756,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { return null; case REMOVE_NOTIFICATION_LISTENER: - getServerNotifFwd().removeNotificationListener( - (ObjectName)params[0], - (Integer[])params[1]); + doRemoveListener((ObjectName)params[0],(Integer[])params[1]); return null; case REMOVE_NOTIFICATION_LISTENER_OBJECTNAME: @@ -1709,23 +1969,21 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { private final static int REMOVE_NOTIFICATION_LISTENER = 19; private final static int - REMOVE_NOTIFICATION_LISTENER_FILTER_HANDBACK = 20; - private final static int - REMOVE_NOTIFICATION_LISTENER_OBJECTNAME = 21; + REMOVE_NOTIFICATION_LISTENER_OBJECTNAME = 20; private final static int - REMOVE_NOTIFICATION_LISTENER_OBJECTNAME_FILTER_HANDBACK = 22; + REMOVE_NOTIFICATION_LISTENER_OBJECTNAME_FILTER_HANDBACK = 21; private final static int - SET_ATTRIBUTE = 23; + SET_ATTRIBUTE = 22; private final static int - SET_ATTRIBUTES = 24; + SET_ATTRIBUTES = 23; private final static int - UNREGISTER_MBEAN = 25; + UNREGISTER_MBEAN = 24; // SERVER NOTIFICATION //-------------------- - private ServerNotifForwarder serverNotifForwarder; - private Map env; + private SubscriptionManager subscriptionManager; + private Map env; // TRACES & DEBUG //--------------- diff --git a/src/share/classes/javax/management/remote/rmi/RMIConnector.java b/src/share/classes/javax/management/remote/rmi/RMIConnector.java index 2f5f234ea..bdcbb1568 100644 --- a/src/share/classes/javax/management/remote/rmi/RMIConnector.java +++ b/src/share/classes/javax/management/remote/rmi/RMIConnector.java @@ -25,6 +25,9 @@ package javax.management.remote.rmi; +import com.sun.jmx.event.DaemonThreadFactory; +import com.sun.jmx.event.EventConnection; +import com.sun.jmx.mbeanserver.PerThreadGroupPool; import com.sun.jmx.remote.internal.ClientCommunicatorAdmin; import com.sun.jmx.remote.internal.ClientListenerInfo; import com.sun.jmx.remote.internal.ClientNotifForwarder; @@ -68,6 +71,12 @@ import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.WeakHashMap; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.AttributeNotFoundException; @@ -75,6 +84,7 @@ import javax.management.InstanceAlreadyExistsException; import javax.management.InstanceNotFoundException; import javax.management.IntrospectionException; import javax.management.InvalidAttributeValueException; +import javax.management.JMX; import javax.management.ListenerNotFoundException; import javax.management.MBeanException; import javax.management.MBeanInfo; @@ -92,6 +102,8 @@ import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.management.QueryExp; import javax.management.ReflectionException; +import javax.management.event.EventClient; +import javax.management.event.EventClientDelegateMBean; import javax.management.remote.JMXConnectionNotification; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; @@ -280,8 +292,8 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable // client-side environment property is set to "true". // boolean checkStub = EnvHelp.computeBooleanFromString( - usemap, - "jmx.remote.x.check.stub"); + usemap, + "jmx.remote.x.check.stub",false); if (checkStub) checkStub(stub, rmiServerImplStubClass); // Connect IIOP Stub if needed. @@ -318,6 +330,8 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable // connectionId = getConnectionId(); + eventServiceEnabled = EnvHelp.eventServiceEnabled(env); + Notification connectedNotif = new JMXConnectionNotification(JMXConnectionNotification.OPENED, this, @@ -327,6 +341,8 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable null); sendNotification(connectedNotif); + // whether or not event service + if (tracing) logger.trace("connect",idstr + " done..."); } catch (IOException e) { if (tracing) @@ -378,13 +394,42 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable throw new IOException("Not connected"); } - MBeanServerConnection mbsc = rmbscMap.get(delegationSubject); - if (mbsc != null) - return mbsc; + MBeanServerConnection rmbsc = rmbscMap.get(delegationSubject); + if (rmbsc != null) { + return rmbsc; + } + + rmbsc = new RemoteMBeanServerConnection(delegationSubject); + if (eventServiceEnabled) { + EventClientDelegateMBean ecd = JMX.newMBeanProxy( + rmbsc, EventClientDelegateMBean.OBJECT_NAME, + EventClientDelegateMBean.class); + EventClient ec = new EventClient(ecd, null, defaultExecutor(), null, + EventClient.DEFAULT_LEASE_TIMEOUT); - mbsc = new RemoteMBeanServerConnection(delegationSubject); - rmbscMap.put(delegationSubject, mbsc); - return mbsc; + rmbsc = EventConnection.Factory.make(rmbsc, ec); + ec.addEventClientListener( + lostNotifListener, null, null); + } + rmbscMap.put(delegationSubject, rmbsc); + return rmbsc; + } + + private static Executor defaultExecutor() { + PerThreadGroupPool.Create create = + new PerThreadGroupPool.Create() { + public ThreadPoolExecutor createThreadPool(ThreadGroup group) { + ThreadFactory daemonThreadFactory = new DaemonThreadFactory( + "RMIConnector listener dispatch %d"); + ThreadPoolExecutor exec = new ThreadPoolExecutor( + 1, 10, 1, TimeUnit.SECONDS, + new LinkedBlockingDeque(), + daemonThreadFactory); + exec.allowCoreThreadTimeOut(true); + return exec; + } + }; + return listenerDispatchThreadPool.getThreadPoolExecutor(create); } public void @@ -466,6 +511,17 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable communicatorAdmin.terminate(); } + // close all EventClient + for (MBeanServerConnection rmbsc : rmbscMap.values()) { + if (rmbsc instanceof EventConnection) { + try { + ((EventConnection)rmbsc).getEventClient().close(); + } catch (Exception e) { + // OK + } + } + } + if (rmiNotifClient != null) { try { rmiNotifClient.terminate(); @@ -592,18 +648,19 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable } if (debug) logger.debug("addListenersWithSubjects","registered " - + listenerIDs.length + " listener(s)"); + + ((listenerIDs==null)?0:listenerIDs.length) + + " listener(s)"); return listenerIDs; } //-------------------------------------------------------------------- // Implementation of MBeanServerConnection //-------------------------------------------------------------------- - private class RemoteMBeanServerConnection - implements MBeanServerConnection { - + private class RemoteMBeanServerConnection implements MBeanServerConnection { private Subject delegationSubject; + public EventClient eventClient = null; + public RemoteMBeanServerConnection() { this(null); } @@ -1205,6 +1262,7 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable IOException { final boolean debug = logger.debugOn(); + if (debug) logger.debug("addNotificationListener" + "(ObjectName,NotificationListener,"+ @@ -1226,8 +1284,9 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable public void removeNotificationListener(ObjectName name, NotificationListener listener) throws InstanceNotFoundException, - ListenerNotFoundException, - IOException { + ListenerNotFoundException, + IOException { + final boolean debug = logger.debugOn(); if (debug) logger.debug("removeNotificationListener"+ @@ -1804,6 +1863,26 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable terminated = false; connectionBroadcaster = new NotificationBroadcasterSupport(); + + lostNotifListener = + new NotificationListener() { + public void handleNotification(Notification n, Object hb) { + if (n != null && EventClient.NOTIFS_LOST.equals(n.getType())) { + Long lost = (Long)n.getUserData(); + final String msg = + "May have lost up to " + lost + + " notification" + (lost.longValue() == 1 ? "" : "s"); + sendNotification(new JMXConnectionNotification( + JMXConnectionNotification.NOTIFS_LOST, + RMIConnector.this, + connectionId, + clientNotifCounter++, + msg, + lost)); + + } + } + }; } //-------------------------------------------------------------------- @@ -2528,6 +2607,11 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable private transient ClientCommunicatorAdmin communicatorAdmin; + private boolean eventServiceEnabled; +// private transient EventRelay eventRelay; + + private transient NotificationListener lostNotifListener; + /** * A static WeakReference to an {@link org.omg.CORBA.ORB ORB} to * connect unconnected stubs. @@ -2546,4 +2630,7 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable private static String strings(final String[] strs) { return objects(strs); } + + private static final PerThreadGroupPool listenerDispatchThreadPool = + PerThreadGroupPool.make(); } diff --git a/src/share/classes/javax/management/remote/rmi/RMIConnectorServer.java b/src/share/classes/javax/management/remote/rmi/RMIConnectorServer.java index 28015461b..2cbe315bf 100644 --- a/src/share/classes/javax/management/remote/rmi/RMIConnectorServer.java +++ b/src/share/classes/javax/management/remote/rmi/RMIConnectorServer.java @@ -230,6 +230,8 @@ public class RMIConnectorServer extends JMXConnectorServer { this.address = url; this.rmiServerImpl = rmiServerImpl; + + installStandardForwarders(this.attributes); } /** @@ -380,8 +382,8 @@ public class RMIConnectorServer extends JMXConnectorServer { try { if (tracing) logger.trace("start", "setting default class loader"); - defaultClassLoader = - EnvHelp.resolveServerClassLoader(attributes, getMBeanServer()); + defaultClassLoader = EnvHelp.resolveServerClassLoader( + attributes, getSystemMBeanServer()); } catch (InstanceNotFoundException infc) { IllegalArgumentException x = new IllegalArgumentException("ClassLoader not found: "+infc); @@ -396,7 +398,7 @@ public class RMIConnectorServer extends JMXConnectorServer { else rmiServer = newServer(); - rmiServer.setMBeanServer(getMBeanServer()); + rmiServer.setMBeanServer(getSystemMBeanServer()); rmiServer.setDefaultClassLoader(defaultClassLoader); rmiServer.setRMIConnectorServer(this); rmiServer.export(); @@ -413,7 +415,7 @@ public class RMIConnectorServer extends JMXConnectorServer { final boolean rebind = EnvHelp.computeBooleanFromString( attributes, - JNDI_REBIND_ATTRIBUTE); + JNDI_REBIND_ATTRIBUTE,false); if (tracing) logger.trace("start", JNDI_REBIND_ATTRIBUTE + "=" + rebind); @@ -590,11 +592,39 @@ public class RMIConnectorServer extends JMXConnectorServer { return Collections.unmodifiableMap(map); } - public synchronized - void setMBeanServerForwarder(MBeanServerForwarder mbsf) { + @Override + public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf) { + MBeanServer oldSMBS = getSystemMBeanServer(); super.setMBeanServerForwarder(mbsf); + if (oldSMBS != getSystemMBeanServer()) + updateMBeanServer(); + // If the system chain of MBeanServerForwarders is not empty, then + // there is no need to call rmiServerImpl.setMBeanServer, because + // it is pointing to the head of the system chain and that has not + // changed. (The *end* of the system chain will have been changed + // to point to mbsf.) + } + + private void updateMBeanServer() { if (rmiServerImpl != null) - rmiServerImpl.setMBeanServer(getMBeanServer()); + rmiServerImpl.setMBeanServer(getSystemMBeanServer()); + } + + @Override + public synchronized void setSystemMBeanServerForwarder( + MBeanServerForwarder mbsf) { + super.setSystemMBeanServerForwarder(mbsf); + updateMBeanServer(); + } + + /** + * {@inheritDoc} + * @return true, since this connector server does support a system chain + * of forwarders. + */ + @Override + public boolean supportsSystemMBeanServerForwarder() { + return true; } /* We repeat the definitions of connection{Opened,Closed,Failed} diff --git a/test/javax/management/MBeanServer/DynamicWrapperMBeanTest.java b/test/javax/management/MBeanServer/DynamicWrapperMBeanTest.java new file mode 100644 index 000000000..5867b1aa3 --- /dev/null +++ b/test/javax/management/MBeanServer/DynamicWrapperMBeanTest.java @@ -0,0 +1,164 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test DynamicWrapperMBeanTest + * @bug 6624232 + * @summary Test the DynamicWrapperMBean interface + * @author Eamonn McManus + */ + +import java.lang.management.ManagementFactory; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.StandardMBean; +import javax.management.modelmbean.ModelMBeanInfo; +import javax.management.modelmbean.ModelMBeanInfoSupport; +import javax.management.modelmbean.ModelMBeanOperationInfo; +import javax.management.modelmbean.RequiredModelMBean; +import static javax.management.StandardMBean.Options; + +public class DynamicWrapperMBeanTest { + public static interface WrappedMBean { + public void sayHello(); + } + public static class Wrapped implements WrappedMBean { + public void sayHello() { + System.out.println("Hello"); + } + } + + private static String failure; + + public static void main(String[] args) throws Exception { + if (Wrapped.class.getClassLoader() == + StandardMBean.class.getClassLoader()) { + throw new Exception( + "TEST ERROR: Resource and StandardMBean have same ClassLoader"); + } + + Options wrappedVisOpts = new Options(); + wrappedVisOpts.setWrappedObjectVisible(true); + Options wrappedInvisOpts = new Options(); + wrappedInvisOpts.setWrappedObjectVisible(false); + assertEquals("Options withWrappedObjectVisible(false)", + new Options(), wrappedInvisOpts); + + Wrapped resource = new Wrapped(); + + StandardMBean visible = + new StandardMBean(resource, WrappedMBean.class, wrappedVisOpts); + StandardMBean invisible = + new StandardMBean(resource, WrappedMBean.class, wrappedInvisOpts); + + assertEquals("getResource withWrappedObjectVisible(true)", + resource, visible.getWrappedObject()); + assertEquals("getResource withWrappedObjectVisible(false)", + invisible, invisible.getWrappedObject()); + + System.out.println("===Testing StandardMBean==="); + + ObjectName visibleName = new ObjectName("a:type=visible"); + ObjectName invisibleName = new ObjectName("a:type=invisible"); + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + mbs.registerMBean(visible, visibleName); + mbs.registerMBean(invisible, invisibleName); + + assertEquals("ClassLoader for visible resource", + Wrapped.class.getClassLoader(), + mbs.getClassLoaderFor(visibleName)); + assertEquals("ClassLoader for invisible resource", + StandardMBean.class.getClassLoader(), + mbs.getClassLoaderFor(invisibleName)); + + assertEquals("isInstanceOf(WrappedMBean) for visible wrapped", + true, mbs.isInstanceOf(visibleName, WrappedMBean.class.getName())); + assertEquals("isInstanceOf(WrappedMBean) for invisible wrapped", + false, mbs.isInstanceOf(invisibleName, WrappedMBean.class.getName())); + assertEquals("isInstanceOf(StandardMBean) for visible wrapped", + false, mbs.isInstanceOf(visibleName, StandardMBean.class.getName())); + assertEquals("isInstanceOf(StandardMBean) for invisible wrapped", + true, mbs.isInstanceOf(invisibleName, StandardMBean.class.getName())); + + mbs.unregisterMBean(visibleName); + mbs.unregisterMBean(invisibleName); + + System.out.println("===Testing RequiredModelMBean==="); + + // Godawful Model MBeans... + ModelMBeanOperationInfo mmboi = new ModelMBeanOperationInfo( + "say hello to the nice man", Wrapped.class.getMethod("sayHello")); + ModelMBeanInfo visibleMmbi = new ModelMBeanInfoSupport( + Wrapped.class.getName(), "Visible wrapped", null, null, + new ModelMBeanOperationInfo[] {mmboi}, null); + ModelMBeanInfo invisibleMmbi = new ModelMBeanInfoSupport( + Wrapped.class.getName(), "Invisible wrapped", null, null, + new ModelMBeanOperationInfo[] {mmboi}, null); + RequiredModelMBean visibleRmmb = new RequiredModelMBean(visibleMmbi); + RequiredModelMBean invisibleRmmb = new RequiredModelMBean(invisibleMmbi); + visibleRmmb.setManagedResource(resource, "VisibleObjectReference"); + invisibleRmmb.setManagedResource(resource, "ObjectReference"); + + mbs.registerMBean(visibleRmmb, visibleName); + mbs.registerMBean(invisibleRmmb, invisibleName); + + assertEquals("ClassLoader for visible wrapped", + Wrapped.class.getClassLoader(), + mbs.getClassLoaderFor(visibleName)); + assertEquals("ClassLoader for invisible wrapped", + StandardMBean.class.getClassLoader(), + mbs.getClassLoaderFor(invisibleName)); + + assertEquals("isInstanceOf(WrappedMBean) for visible resource", + true, mbs.isInstanceOf(visibleName, WrappedMBean.class.getName())); + assertEquals("isInstanceOf(WrappedMBean) for invisible resource", + false, mbs.isInstanceOf(invisibleName, WrappedMBean.class.getName())); + assertEquals("isInstanceOf(RequiredModelMBean) for visible resource", + false, mbs.isInstanceOf(visibleName, RequiredModelMBean.class.getName())); + assertEquals("isInstanceOf(RequiredModelMBean) for invisible resource", + true, mbs.isInstanceOf(invisibleName, RequiredModelMBean.class.getName())); + + if (failure != null) + throw new Exception("TEST FAILED: " + failure); + } + + private static void assertEquals(String what, Object expect, Object actual) { + if (equal(expect, actual)) + System.out.println("OK: " + what + " = " + expect); + else + fail(what + " should be " + expect + ", is " + actual); + } + + private static boolean equal(Object x, Object y) { + if (x == y) + return true; + if (x == null || y == null) + return false; + return x.equals(y); + } + + private static void fail(String why) { + failure = why; + System.out.println("FAIL: " + why); + } +} diff --git a/test/javax/management/MBeanServer/OldMBeanServerTest.java b/test/javax/management/MBeanServer/OldMBeanServerTest.java new file mode 100644 index 000000000..f35dd7787 --- /dev/null +++ b/test/javax/management/MBeanServer/OldMBeanServerTest.java @@ -0,0 +1,1410 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.management.ManagementFactory; +import java.lang.ref.WeakReference; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.DynamicMBean; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanConstructorInfo; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanRegistration; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.MBeanServerBuilder; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerDelegate; +import javax.management.MBeanServerFactory; +import javax.management.MBeanServerNotification; +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; +import javax.management.Notification; +import javax.management.NotificationBroadcaster; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.OperationsException; +import javax.management.QueryEval; +import javax.management.QueryExp; +import javax.management.ReflectionException; +import javax.management.RuntimeErrorException; +import javax.management.RuntimeMBeanException; +import javax.management.StandardMBean; +import javax.management.loading.ClassLoaderRepository; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +/* + * @test OldMBeanServerTest.java + * @bug 5072268 + * @summary Test that nothing assumes a post-1.2 MBeanServer + * @author Eamonn McManus + * @run main/othervm -ea OldMBeanServerTest + */ + +/* + * We defined the MBeanServerBuilder class and the associated system + * property javax.management.builder.initial in version 1.2 of the JMX + * spec. That amounts to a guarantee that someone can set the property + * to an MBeanServer that only knows about JMX 1.2 semantics, and if they + * only do JMX 1.2 operations, everything should work. This test is a + * sanity check that ensures we don't inadvertently make any API changes + * that stop that from being true. It includes a complete (if slow) + * MBeanServer implementation. That implementation doesn't replicate the + * mandated exception behaviour everywhere, though, since there's lots of + * arbitrary cruft in that. Also, the behaviour of concurrent unregisterMBean + * calls is incorrect in detail. + */ + +public class OldMBeanServerTest { + private static MBeanServerConnection mbsc; + private static String failure; + + public static void main(String[] args) throws Exception { + if (!OldMBeanServerTest.class.desiredAssertionStatus()) + throw new Exception("Test must be run with -ea"); + + System.setProperty("javax.management.builder.initial", + OldMBeanServerBuilder.class.getName()); + assert MBeanServerFactory.newMBeanServer() instanceof OldMBeanServer; + + System.out.println("=== RUNNING TESTS WITH LOCAL MBEANSERVER ==="); + runTests(new Callable() { + public MBeanServerConnection call() { + return MBeanServerFactory.newMBeanServer(); + } + }, null); + + System.out.println("=== RUNNING TESTS THROUGH CONNECTOR ==="); + ConnectionBuilder builder = new ConnectionBuilder(); + runTests(builder, builder); + + if (failure == null) + System.out.println("TEST PASSED"); + else + throw new Exception("TEST FAILED: " + failure); + } + + private static class ConnectionBuilder + implements Callable, Runnable { + private JMXConnector connector; + public MBeanServerConnection call() { + MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + try { + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://"); + JMXConnectorServer cs = + JMXConnectorServerFactory.newJMXConnectorServer( + url, null, mbs); + cs.start(); + JMXServiceURL addr = cs.getAddress(); + connector = JMXConnectorFactory.connect(addr); + return connector.getMBeanServerConnection(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + public void run() { + if (connector != null) { + try { + connector.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + } + + private static void runTests( + Callable maker, Runnable breaker) + throws Exception { + for (Method m : OldMBeanServerTest.class.getDeclaredMethods()) { + if (Modifier.isStatic(m.getModifiers()) && + m.getName().startsWith("test") && + m.getParameterTypes().length == 0) { + ExpectException expexc = m.getAnnotation(ExpectException.class); + mbsc = maker.call(); + try { + m.invoke(null); + if (expexc != null) { + failure = + m.getName() + " did not got expected exception " + + expexc.value().getName(); + System.out.println(failure); + } else + System.out.println(m.getName() + " OK"); + } catch (InvocationTargetException ite) { + Throwable t = ite.getCause(); + String prob = null; + if (expexc != null) { + if (expexc.value().isInstance(t)) { + System.out.println(m.getName() + " OK (got expected " + + expexc.value().getName() + ")"); + } else + prob = "got wrong exception"; + } else + prob = "got exception"; + if (prob != null) { + failure = m.getName() + ": " + prob + " " + + t.getClass().getName(); + System.out.println(failure); + t.printStackTrace(System.out); + } + } finally { + if (breaker != null) + breaker.run(); + } + } + } + } + + @Retention(RetentionPolicy.RUNTIME) + private static @interface ExpectException { + Class value(); + } + + public static interface BoringMBean { + public String getName(); + public int add(int x, int y); + } + + // This class is Serializable so we can createMBean a StandardMBean + // that contains it. Not recommended practice in general -- + // should we have a StandardMBean constructor that takes a class + // name and constructor parameters? + public static class Boring implements BoringMBean, Serializable { + public String getName() { + return "Jessica"; + } + + public int add(int x, int y) { + return x + y; + } + } + + public static interface BoringNotifierMBean extends BoringMBean { + public void send(); + } + + public static class BoringNotifier + extends Boring implements BoringNotifierMBean, NotificationBroadcaster { + private final NotificationBroadcasterSupport nbs = + new NotificationBroadcasterSupport(); + + public void addNotificationListener( + NotificationListener listener, NotificationFilter filter, Object handback) + throws IllegalArgumentException { + nbs.addNotificationListener(listener, filter, handback); + } + + public void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException { + nbs.removeNotificationListener(listener); + } + + public MBeanNotificationInfo[] getNotificationInfo() { + return null; + } + + public void send() { + Notification n = new Notification("type.type", this, 0L); + nbs.sendNotification(n); + } + } + + private static class CountListener implements NotificationListener { + volatile int count; + public void handleNotification(Notification n, Object h) { + if (h == null) + h = 1; + count += (Integer) h; + } + void waitForCount(int expect) throws InterruptedException { + long deadline = System.currentTimeMillis() + 2000L; + while (count < expect && System.currentTimeMillis() < deadline) + Thread.sleep(1); + assert count == expect; + } + } + + private static void testBasic() throws Exception { + CountListener countListener = new CountListener(); + mbsc.addNotificationListener( + MBeanServerDelegate.DELEGATE_NAME, countListener, null, null); + assert countListener.count == 0; + ObjectName name = new ObjectName("a:b=c"); + if (mbsc instanceof MBeanServer) + ((MBeanServer) mbsc).registerMBean(new Boring(), name); + else + mbsc.createMBean(Boring.class.getName(), name); + countListener.waitForCount(1); + assert mbsc.isRegistered(name); + assert mbsc.queryNames(null, null).contains(name); + assert mbsc.getAttribute(name, "Name").equals("Jessica"); + assert mbsc.invoke( + name, "add", new Object[] {2, 3}, new String[] {"int", "int"}) + .equals(5); + mbsc.unregisterMBean(name); + countListener.waitForCount(2); + assert !mbsc.isRegistered(name); + assert !mbsc.queryNames(null, null).contains(name); + + mbsc.createMBean(BoringNotifier.class.getName(), name); + countListener.waitForCount(3); + CountListener boringListener = new CountListener(); + class AlwaysNotificationFilter implements NotificationFilter { + public boolean isNotificationEnabled(Notification notification) { + return true; + } + } + mbsc.addNotificationListener( + name, boringListener, new AlwaysNotificationFilter(), 5); + mbsc.invoke(name, "send", null, null); + boringListener.waitForCount(5); + } + + private static void testPrintAttrs() throws Exception { + printAttrs(mbsc, null); + } + + private static void testPlatformMBeanServer() throws Exception { + MBeanServer pmbs = ManagementFactory.getPlatformMBeanServer(); + assert pmbs instanceof OldMBeanServer; + // Preceding assertion could be violated if at some stage we wrap + // the Platform MBeanServer. In that case we can still check that + // it is ultimately an OldMBeanServer for example by adding a + // counter to getAttribute and checking that it is incremented + // when we call pmbs.getAttribute. + + printAttrs(pmbs, UnsupportedOperationException.class); + ObjectName memoryMXBeanName = + new ObjectName(ManagementFactory.MEMORY_MXBEAN_NAME); + pmbs.invoke(memoryMXBeanName, "gc", null, null); + } + + private static void printAttrs( + MBeanServerConnection mbsc1, Class expectX) + throws Exception { + Set names = mbsc1.queryNames(null, null); + for (ObjectName name : names) { + System.out.println(name + ":"); + MBeanInfo mbi = mbsc1.getMBeanInfo(name); + MBeanAttributeInfo[] mbais = mbi.getAttributes(); + for (MBeanAttributeInfo mbai : mbais) { + String attr = mbai.getName(); + Object value; + try { + value = mbsc1.getAttribute(name, attr); + } catch (Exception e) { + if (expectX != null && expectX.isInstance(e)) + value = "<" + e + ">"; + else + throw e; + } + String s = " " + attr + " = " + value; + if (s.length() > 80) + s = s.substring(0, 77) + "..."; + System.out.println(s); + } + } + } + + private static void testJavaxManagementStandardMBean() throws Exception { + ObjectName name = new ObjectName("a:b=c"); + Object mbean = new StandardMBean(new Boring(), BoringMBean.class); + mbsc.createMBean( + StandardMBean.class.getName(), name, + new Object[] {new Boring(), BoringMBean.class}, + new String[] {Object.class.getName(), Class.class.getName()}); + assert mbsc.getAttribute(name, "Name").equals("Jessica"); + assert mbsc.invoke( + name, "add", new Object[] {2, 3}, new String[] {"int", "int"}) + .equals(5); + mbsc.unregisterMBean(name); + } + + private static void testConnector() throws Exception { + } + + public static class OldMBeanServerBuilder extends MBeanServerBuilder { + public MBeanServer newMBeanServer( + String defaultDomain, MBeanServer outer, MBeanServerDelegate delegate) { + return new OldMBeanServer(defaultDomain, delegate); + } + } + + public static class OldMBeanServer implements MBeanServer { + // We pretend there's a ClassLoader MBean representing the Class Loader + // Repository and intercept references to it where necessary to keep up + // the pretence. This allows us to fake the right behaviour for + // the omitted-ClassLoader versions of createMBean and instantiate + // (which are not the same as passing a null for the ClassLoader parameter + // of the versions that have one). + private static final ObjectName clrName; + static { + try { + clrName = + new ObjectName("JMImplementation:type=ClassLoaderRepository"); + } catch (MalformedObjectNameException e) { + throw new RuntimeException(e); + } + } + + private final ConcurrentMap mbeans = + new ConcurrentHashMap(); + private final ConcurrentMap listenerMap = + new ConcurrentHashMap(); + private final String defaultDomain; + private final MBeanServerDelegate delegate; + private final ClassLoaderRepositoryImpl clr = + new ClassLoaderRepositoryImpl(); + + OldMBeanServer(String defaultDomain, MBeanServerDelegate delegate) { + this.defaultDomain = defaultDomain; + this.delegate = delegate; + try { + registerMBean(delegate, MBeanServerDelegate.DELEGATE_NAME); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public ObjectInstance createMBean(String className, ObjectName name) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException { + return createMBean(className, name, null, null); + } + + public ObjectInstance createMBean( + String className, ObjectName name, ObjectName loaderName) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException { + return createMBean(className, name, loaderName, null, null); + } + + public ObjectInstance createMBean( + String className, ObjectName name, Object[] params, String[] signature) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException { + try { + return createMBean(className, name, clrName, params, signature); + } catch (InstanceNotFoundException ex) { + throw new RuntimeException(ex); // can't happen + } + } + + public ObjectInstance createMBean( + String className, ObjectName name, ObjectName loaderName, + Object[] params, String[] signature) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException { + Object mbean = instantiate(className, loaderName, params, signature); + return registerMBean(mbean, name); + } + + private void forbidJMImpl(ObjectName name) { + if (name.getDomain().equals("JMImplementation") && + mbeans.containsKey(MBeanServerDelegate.DELEGATE_NAME)) + throw new IllegalArgumentException("JMImplementation reserved"); + } + + public ObjectInstance registerMBean(Object object, ObjectName name) + throws InstanceAlreadyExistsException, MBeanRegistrationException, + NotCompliantMBeanException { + forbidJMImpl(name); + if (name.isPattern()) + throw new IllegalArgumentException(name.toString()); + // This is the only place we check for wildcards. Since you + // can't register a wildcard name, other operations that supply + // one will get InstanceNotFoundException when they look it up. + + DynamicMBean mbean; + if (object instanceof DynamicMBean) + mbean = (DynamicMBean) object; + else + mbean = standardToDynamic(object); + MBeanRegistration reg = mbeanRegistration(object); + try { + name = reg.preRegister(this, name); + } catch (Exception e) { + throw new MBeanRegistrationException(e); + } + DynamicMBean put = mbeans.putIfAbsent(name, mbean); + if (put != null) { + reg.postRegister(false); + throw new InstanceAlreadyExistsException(name.toString()); + } + reg.postRegister(true); + + if (object instanceof ClassLoader) + clr.addLoader((ClassLoader) object); + + Notification n = new MBeanServerNotification( + MBeanServerNotification.REGISTRATION_NOTIFICATION, + MBeanServerDelegate.DELEGATE_NAME, + 0, + name); + delegate.sendNotification(n); + + String className = mbean.getMBeanInfo().getClassName(); + return new ObjectInstance(name, className); + } + + public void unregisterMBean(ObjectName name) + throws InstanceNotFoundException, MBeanRegistrationException { + + forbidJMImpl(name); + + DynamicMBean mbean = getMBean(name); + if (mbean == null) + throw new InstanceNotFoundException(name.toString()); + + MBeanRegistration reg = mbeanRegistration(mbean); + try { + reg.preDeregister(); + } catch (Exception e) { + throw new MBeanRegistrationException(e); + } + if (!mbeans.remove(name, mbean)) + throw new InstanceNotFoundException(name.toString()); + // This is incorrect because we've invoked preDeregister + + Object userMBean = getUserMBean(mbean); + if (userMBean instanceof ClassLoader) + clr.removeLoader((ClassLoader) userMBean); + + Notification n = new MBeanServerNotification( + MBeanServerNotification.REGISTRATION_NOTIFICATION, + MBeanServerDelegate.DELEGATE_NAME, + 0, + name); + delegate.sendNotification(n); + + reg.postDeregister(); + } + + public ObjectInstance getObjectInstance(ObjectName name) + throws InstanceNotFoundException { + DynamicMBean mbean = getMBean(name); + return new ObjectInstance(name, mbean.getMBeanInfo().getClassName()); + } + + private static class TrueQueryExp implements QueryExp { + public boolean apply(ObjectName name) { + return true; + } + + public void setMBeanServer(MBeanServer s) {} + } + private static final QueryExp trueQuery = new TrueQueryExp(); + + public Set queryMBeans(ObjectName name, QueryExp query) { + Set instances = newSet(); + if (name == null) + name = ObjectName.WILDCARD; + if (query == null) + query = trueQuery; + MBeanServer oldMBS = QueryEval.getMBeanServer(); + try { + query.setMBeanServer(this); + for (ObjectName n : mbeans.keySet()) { + if (name.apply(n)) { + try { + if (query.apply(n)) + instances.add(getObjectInstance(n)); + } catch (Exception e) { + // OK: Ignore this MBean in the result + } + } + } + } finally { + query.setMBeanServer(oldMBS); + } + return instances; + } + + public Set queryNames(ObjectName name, QueryExp query) { + Set instances = queryMBeans(name, query); + Set names = newSet(); + for (ObjectInstance instance : instances) + names.add(instance.getObjectName()); + return names; + } + + public boolean isRegistered(ObjectName name) { + return mbeans.containsKey(name); + } + + public Integer getMBeanCount() { + return mbeans.size(); + } + + public Object getAttribute(ObjectName name, String attribute) + throws MBeanException, AttributeNotFoundException, + InstanceNotFoundException, ReflectionException { + return getMBean(name).getAttribute(attribute); + } + + public AttributeList getAttributes(ObjectName name, String[] attributes) + throws InstanceNotFoundException, ReflectionException { + return getMBean(name).getAttributes(attributes); + } + + public void setAttribute(ObjectName name, Attribute attribute) + throws InstanceNotFoundException, AttributeNotFoundException, + InvalidAttributeValueException, MBeanException, + ReflectionException { + getMBean(name).setAttribute(attribute); + } + + public AttributeList setAttributes( + ObjectName name, AttributeList attributes) + throws InstanceNotFoundException, ReflectionException { + return getMBean(name).setAttributes(attributes); + } + + public Object invoke( + ObjectName name, String operationName, Object[] params, + String[] signature) + throws InstanceNotFoundException, MBeanException, ReflectionException { + return getMBean(name).invoke(operationName, params, signature); + } + + public String getDefaultDomain() { + return defaultDomain; + } + + public String[] getDomains() { + Set domains = newSet(); + for (ObjectName name : mbeans.keySet()) + domains.add(name.getDomain()); + return domains.toArray(new String[0]); + } + + // ClassCastException if MBean is not a NotificationBroadcaster + public void addNotificationListener( + ObjectName name, NotificationListener listener, + NotificationFilter filter, Object handback) + throws InstanceNotFoundException { + NotificationBroadcaster userMBean = + (NotificationBroadcaster) getUserMBean(name); + NotificationListener wrappedListener = + wrappedListener(name, userMBean, listener); + userMBean.addNotificationListener(wrappedListener, filter, handback); + } + + public void addNotificationListener( + ObjectName name, ObjectName listener, + NotificationFilter filter, Object handback) + throws InstanceNotFoundException { + NotificationListener nl = + (NotificationListener) getUserMBean(listener); + addNotificationListener(name, nl, filter, handback); + } + + public void removeNotificationListener( + ObjectName name, ObjectName listener) + throws InstanceNotFoundException, ListenerNotFoundException { + NotificationListener nl = + (NotificationListener) getUserMBean(listener); + removeNotificationListener(name, nl); + } + + public void removeNotificationListener( + ObjectName name, ObjectName listener, + NotificationFilter filter, Object handback) + throws InstanceNotFoundException, ListenerNotFoundException { + NotificationListener nl = + (NotificationListener) getUserMBean(listener); + removeNotificationListener(name, nl, filter, handback); + } + + public void removeNotificationListener( + ObjectName name, NotificationListener listener) + throws InstanceNotFoundException, ListenerNotFoundException { + NotificationBroadcaster userMBean = + (NotificationBroadcaster) getUserMBean(name); + NotificationListener wrappedListener = + wrappedListener(name, userMBean, listener); + userMBean.removeNotificationListener(wrappedListener); + } + + public void removeNotificationListener( + ObjectName name, NotificationListener listener, + NotificationFilter filter, Object handback) + throws InstanceNotFoundException, ListenerNotFoundException { + NotificationEmitter userMBean = + (NotificationEmitter) getMBean(name); + NotificationListener wrappedListener = + wrappedListener(name, userMBean, listener); + userMBean.removeNotificationListener(wrappedListener, filter, handback); + } + + public MBeanInfo getMBeanInfo(ObjectName name) + throws InstanceNotFoundException, IntrospectionException, + ReflectionException { + return getMBean(name).getMBeanInfo(); + } + + public boolean isInstanceOf(ObjectName name, String className) + throws InstanceNotFoundException { + DynamicMBean mbean = getMBean(name); + String mbeanClassName = mbean.getMBeanInfo().getClassName(); + if (className.equals(mbeanClassName)) + return true; + ClassLoader loader = getUserMBean(mbean).getClass().getClassLoader(); + try { + Class mbeanClass = Class.forName(mbeanClassName, false, loader); + Class isInstClass = Class.forName(className, false, loader); + return isInstClass.isAssignableFrom(mbeanClass); + } catch (ClassNotFoundException e) { + return false; + } + } + + public Object instantiate(String className) + throws ReflectionException, MBeanException { + return instantiate(className, null, null); + } + + public Object instantiate(String className, ObjectName loaderName) + throws ReflectionException, MBeanException, InstanceNotFoundException { + return instantiate(className, loaderName, null, null); + } + + public Object instantiate( + String className, Object[] params, String[] signature) + throws ReflectionException, MBeanException { + try { + return instantiate(className, clrName, params, signature); + } catch (InstanceNotFoundException e) { + throw new RuntimeException(e); // can't happen + } + } + + public Object instantiate( + String className, ObjectName loaderName, + Object[] params, String[] signature) + throws ReflectionException, MBeanException, InstanceNotFoundException { + + if (params == null) + params = new Object[0]; + if (signature == null) + signature = new String[0]; + + ClassLoader loader; + if (loaderName == null) + loader = this.getClass().getClassLoader(); + else if (loaderName.equals(clrName)) + loader = clr; + else + loader = (ClassLoader) getMBean(loaderName); + + Class c; + try { + c = Class.forName(className, false, loader); + } catch (ClassNotFoundException e) { + throw new ReflectionException(e); + } + + Constructor[] constrs = c.getConstructors(); + Constructor found = null; + findconstr: + for (Constructor constr : constrs) { + Class[] cTypes = constr.getParameterTypes(); + if (cTypes.length == signature.length) { + for (int i = 0; i < cTypes.length; i++) { + if (!cTypes[i].getName().equals(signature[i])) + continue findconstr; + } + found = constr; + break findconstr; + } + } + if (found == null) { + Exception x = new NoSuchMethodException( + className + Arrays.toString(signature)); + throw new ReflectionException(x); + } + return invokeSomething(found, null, params); + } + + @Deprecated + public ObjectInputStream deserialize(ObjectName name, byte[] data) + throws InstanceNotFoundException, OperationsException { + throw new UnsupportedOperationException(); + } + + @Deprecated + public ObjectInputStream deserialize(String className, byte[] data) + throws OperationsException, ReflectionException { + throw new UnsupportedOperationException(); + } + + @Deprecated + public ObjectInputStream deserialize( + String className, ObjectName loaderName, byte[] data) + throws InstanceNotFoundException, OperationsException, ReflectionException { + throw new UnsupportedOperationException(); + } + + public ClassLoader getClassLoaderFor(ObjectName mbeanName) + throws InstanceNotFoundException { + DynamicMBean mbean = getMBean(mbeanName); + Object userMBean = getUserMBean(mbean); + return userMBean.getClass().getClassLoader(); + } + + public ClassLoader getClassLoader(ObjectName loaderName) + throws InstanceNotFoundException { + return (ClassLoader) getMBean(loaderName); + } + + public ClassLoaderRepository getClassLoaderRepository() { + return new ClassLoaderRepository() { + public Class loadClass(String className) + throws ClassNotFoundException { + return clr.loadClass(className); + } + + public Class loadClassWithout( + ClassLoader exclude, String className) + throws ClassNotFoundException { + return clr.loadClassWithout(exclude, className); + } + + public Class loadClassBefore( + ClassLoader stop, String className) + throws ClassNotFoundException { + return clr.loadClassBefore(stop, className); + } + }; + } + + private static class ClassLoaderRepositoryImpl + extends ClassLoader implements ClassLoaderRepository { + private List loaders = newList(); + { + loaders.add(this.getClass().getClassLoader()); + // We also behave as if the system class loader were in + // the repository, since we do nothing to stop delegation + // to the parent, which is the system class loader, and + // that delegation happens before our findClass is called. + } + + void addLoader(ClassLoader loader) { + loaders.add(loader); + } + + void removeLoader(ClassLoader loader) { + if (!loaders.remove(loader)) + throw new RuntimeException("Loader was not in CLR!"); + } + + public Class loadClassWithout( + ClassLoader exclude, String className) + throws ClassNotFoundException { + return loadClassWithoutBefore(exclude, null, className); + } + + public Class loadClassBefore(ClassLoader stop, String className) + throws ClassNotFoundException { + return loadClassWithoutBefore(null, stop, className); + } + + private Class loadClassWithoutBefore( + ClassLoader exclude, ClassLoader stop, String className) + throws ClassNotFoundException { + for (ClassLoader loader : loaders) { + if (loader == exclude) + continue; + if (loader == stop) + break; + try { + return Class.forName(className, false, loader); + } catch (ClassNotFoundException e) { + // OK: try others + } + } + throw new ClassNotFoundException(className); + } + + @Override + protected Class findClass(String className) + throws ClassNotFoundException { + return loadClassWithout(null, className); + } + } + + /* There is zero or one ListenerTable per MBean. + * The ListenerTable stuff is complicated. We want to rewrite the + * source of notifications so that if the source of a notification + * from the MBean X is a reference to X itself, it gets replaced + * by X's ObjectName. To do this, we wrap the user's listener in + * a RewriteListener. But if the same listener is added a second + * time (perhaps with a different filter or handback) we must + * reuse the same RewriteListener so that the two-argument + * removeNotificationListener(ObjectName,NotificationListener) will + * correctly remove both listeners. This means we must remember the + * mapping from listener to WrappedListener. But if the MBean + * discards its listeners (as a result of removeNL or spontaneously) + * then we don't want to keep a reference to the WrappedListener. + * So we have tons of WeakReferences. The key in the ListenerTable + * is an IdentityListener, which wraps the user's listener to ensure + * that identity and not equality is used during the lookup, even if + * the user's listener has an equals method. The value in the + * ListenerTable is a WeakReference wrapping a RewriteListener wrapping + * the same IdentityListener. Since the RewriteListener is what is + * added to the user's MBean, the WeakReference won't disappear as long + * as the MBean still has this listener. And since it references the + * IdentityListener, that won't disappear either. But once the + * RewriteListener is no longer referenced by the user's MBean, + * there's nothing to stop its WeakReference from being cleared, + * and then corresponding IdentityListener that is now only weakly + * referenced from the key in the table. + */ + private static class ListenerTable + extends WeakHashMap> { + } + + private static class IdentityListener implements NotificationListener { + private final NotificationListener userListener; + + IdentityListener(NotificationListener userListener) { + this.userListener = userListener; + } + + public void handleNotification( + Notification notification, Object handback) { + userListener.handleNotification(notification, handback); + } + + @Override + public boolean equals(Object o) { + return (this == o); + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + } + + private static class RewriteListener implements NotificationListener { + private final ObjectName name; + private final Object userMBean; + private final NotificationListener userListener; + + RewriteListener( + ObjectName name, Object userMBean, + NotificationListener userListener) { + this.name = name; + this.userMBean = userMBean; + this.userListener = userListener; + } + + public void handleNotification( + Notification notification, Object handback) { + if (notification.getSource() == userMBean) + notification.setSource(name); + userListener.handleNotification(notification, handback); + } + } + + private NotificationListener wrappedListener( + ObjectName name, Object userMBean, NotificationListener userListener) + throws InstanceNotFoundException { + ListenerTable table = new ListenerTable(); + ListenerTable oldTable = listenerMap.putIfAbsent(name, table); + if (oldTable != null) + table = oldTable; + NotificationListener identityListener = + new IdentityListener(userListener); + synchronized (table) { + NotificationListener rewriteListener = null; + WeakReference wr = + table.get(identityListener); + if (wr != null) + rewriteListener = wr.get(); + if (rewriteListener == null) { + rewriteListener = new RewriteListener( + name, userMBean, identityListener); + wr = new WeakReference(rewriteListener); + table.put(identityListener, wr); + } + return rewriteListener; + } + } + + private DynamicMBean getMBean(ObjectName name) + throws InstanceNotFoundException { + DynamicMBean mbean = mbeans.get(name); + if (mbean == null) + throw new InstanceNotFoundException(name.toString()); + return mbean; + } + + private static interface WrapDynamicMBean extends DynamicMBean { + public Object getWrappedMBean(); + } + + private static class StandardWrapper + implements WrapDynamicMBean, MBeanRegistration { + private final Map attrMap = newMap(); + private final Map> opMap = newMap(); + private static class AttrMethods { + Method getter, setter; + } + + private final Object std; + + StandardWrapper(Object std) throws NotCompliantMBeanException { + this.std = std; + Class intf = mbeanInterface(std.getClass()); + try { + initMaps(intf); + } catch (NotCompliantMBeanException e) { + throw e; + } catch (Exception e) { + NotCompliantMBeanException x = + new NotCompliantMBeanException(e.getMessage()); + x.initCause(e); + throw x; + } + } + + private static Class mbeanInterface(Class c) + throws NotCompliantMBeanException { + do { + Class[] intfs = c.getInterfaces(); + String intfName = c.getName() + "MBean"; + for (Class intf : intfs) { + if (intf.getName().equals(intfName)) + return intf; + } + c = c.getSuperclass(); + } while (c != null); + throw new NotCompliantMBeanException( + "Does not match Standard or Dynamic MBean patterns: " + + c.getName()); + } + + private void initMaps(Class intf) throws NotCompliantMBeanException { + Method[] methods = intf.getMethods(); + + for (Method m : methods) { + final String name = m.getName(); + final int nParams = m.getParameterTypes().length; + + String attrName = ""; + if (name.startsWith("get")) + attrName = name.substring(3); + else if (name.startsWith("is") + && m.getReturnType() == boolean.class) + attrName = name.substring(2); + + if (attrName.length() != 0 && m.getParameterTypes().length == 0 + && m.getReturnType() != void.class) { + // It's a getter + // Check we don't have both isX and getX + AttrMethods am = attrMap.get(attrName); + if (am == null) + am = new AttrMethods(); + else { + if (am.getter != null) { + final String msg = "Attribute " + attrName + + " has more than one getter"; + throw new NotCompliantMBeanException(msg); + } + } + am.getter = m; + attrMap.put(attrName, am); + } else if (name.startsWith("set") && name.length() > 3 + && m.getParameterTypes().length == 1 && + m.getReturnType() == void.class) { + // It's a setter + attrName = name.substring(3); + AttrMethods am = attrMap.get(attrName); + if (am == null) + am = new AttrMethods(); + else if (am.setter != null) { + final String msg = "Attribute " + attrName + + " has more than one setter"; + throw new NotCompliantMBeanException(msg); + } + am.setter = m; + attrMap.put(attrName, am); + } else { + // It's an operation + List ops = opMap.get(name); + if (ops == null) + ops = newList(); + ops.add(m); + opMap.put(name, ops); + } + } + /* Check that getters and setters are consistent. */ + for (Map.Entry entry : attrMap.entrySet()) { + AttrMethods am = entry.getValue(); + if (am.getter != null && am.setter != null && + am.getter.getReturnType() != am.setter.getParameterTypes()[0]) { + final String msg = "Getter and setter for " + entry.getKey() + + " have inconsistent types"; + throw new NotCompliantMBeanException(msg); + } + } + } + + public Object getAttribute(String attribute) + throws AttributeNotFoundException, MBeanException, ReflectionException { + AttrMethods am = attrMap.get(attribute); + if (am == null || am.getter == null) + throw new AttributeNotFoundException(attribute); + return invokeMethod(am.getter); + } + + public void setAttribute(Attribute attribute) + throws AttributeNotFoundException, InvalidAttributeValueException, + MBeanException, ReflectionException { + String name = attribute.getName(); + AttrMethods am = attrMap.get(name); + if (am == null || am.setter == null) + throw new AttributeNotFoundException(name); + invokeMethod(am.setter, attribute.getValue()); + } + + public AttributeList getAttributes(String[] attributes) { + AttributeList list = new AttributeList(); + for (String attr : attributes) { + try { + list.add(new Attribute(attr, getAttribute(attr))); + } catch (Exception e) { + // OK: ignore per spec + } + } + return list; + } + + public AttributeList setAttributes(AttributeList attributes) { + AttributeList list = new AttributeList(); + // We carefully avoid using any new stuff from AttributeList here! + for (Iterator it = attributes.iterator(); it.hasNext(); ) { + Attribute attr = (Attribute) it.next(); + try { + setAttribute(attr); + list.add(attr); + } catch (Exception e) { + // OK: ignore per spec + } + } + return list; + } + + public Object invoke(String actionName, Object[] params, String[] signature) + throws MBeanException, ReflectionException { + if (params == null) + params = new Object[0]; + if (signature == null) + signature = new String[0]; + List methods = opMap.get(actionName); + if (methods == null) { + Exception x = new NoSuchMethodException(actionName); + throw new MBeanException(x); + } + Method found = null; + methodloop: + for (Method m : methods) { + Class[] msig = m.getParameterTypes(); + if (msig.length != signature.length) + continue methodloop; + for (int i = 0; i < msig.length; i++) { + if (!msig[i].getName().equals(signature[i])) + continue methodloop; + } + found = m; + break methodloop; + } + if (found == null) { + Exception x = new NoSuchMethodException( + actionName + Arrays.toString(signature)); + throw new MBeanException(x); + } + return invokeMethod(found, params); + } + + public MBeanInfo getMBeanInfo() { + // Attributes + List attrs = newList(); + for (Map.Entry attr : attrMap.entrySet()) { + String name = attr.getKey(); + AttrMethods am = attr.getValue(); + try { + attrs.add(new MBeanAttributeInfo( + name, name, am.getter, am.setter)); + } catch (IntrospectionException e) { // grrr + throw new RuntimeException(e); + } + } + + // Operations + List ops = newList(); + for (Map.Entry> op : opMap.entrySet()) { + String name = op.getKey(); + List methods = op.getValue(); + for (Method m : methods) + ops.add(new MBeanOperationInfo(name, m)); + } + + // Constructors + List constrs = newList(); + for (Constructor constr : std.getClass().getConstructors()) + constrs.add(new MBeanConstructorInfo("Constructor", constr)); + + // Notifications + MBeanNotificationInfo[] notifs; + if (std instanceof NotificationBroadcaster) + notifs = ((NotificationBroadcaster) std).getNotificationInfo(); + else + notifs = null; + + String className = std.getClass().getName(); + return new MBeanInfo( + className, className, + attrs.toArray(new MBeanAttributeInfo[0]), + constrs.toArray(new MBeanConstructorInfo[0]), + ops.toArray(new MBeanOperationInfo[0]), + notifs); + } + + private Object invokeMethod(Method m, Object... args) + throws MBeanException, ReflectionException { + return invokeSomething(m, std,args); + } + + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + return mbeanRegistration(std).preRegister(server, name); + } + + public void postRegister(Boolean registrationDone) { + mbeanRegistration(std).postRegister(registrationDone); + } + + public void preDeregister() throws Exception { + mbeanRegistration(std).preDeregister(); + } + + public void postDeregister() { + mbeanRegistration(std).postDeregister(); + } + + public Object getWrappedMBean() { + return std; + } + } + + private DynamicMBean standardToDynamic(Object std) + throws NotCompliantMBeanException { + return new StandardWrapper(std); + } + +// private static class NotifWrapper +// implements WrapDynamicMBean, NotificationEmitter { +// private final DynamicMBean mbean; +// +// NotifWrapper(DynamicMBean mbean) { +// this.mbean = mbean; +// } +// +// public Object getAttribute(String attribute) +// throws AttributeNotFoundException, MBeanException, ReflectionException { +// return mbean.getAttribute(attribute); +// } +// +// public void setAttribute(Attribute attribute) +// throws AttributeNotFoundException, InvalidAttributeValueException, +// MBeanException, ReflectionException { +// mbean.setAttribute(attribute); +// } +// +// public AttributeList getAttributes(String[] attributes) { +// return mbean.getAttributes(attributes); +// } +// +// public AttributeList setAttributes(AttributeList attributes) { +// return mbean.setAttributes(attributes); +// } +// +// public Object invoke( +// String actionName, Object[] params, String[] signature) +// throws MBeanException, ReflectionException { +// return mbean.invoke(actionName, params, signature); +// } +// +// public MBeanInfo getMBeanInfo() { +// return mbean.getMBeanInfo(); +// } +// +// public void removeNotificationListener( +// NotificationListener listener, NotificationFilter filter, Object handback) +// throws ListenerNotFoundException { +// ((NotificationEmitter) mbean).removeNotificationListener( +// listener, filter, handback); +// // ClassCastException if MBean is not an emitter +// } +// +// public void addNotificationListener( +// NotificationListener listener, NotificationFilter filter, Object handback) +// throws IllegalArgumentException { +// ((NotificationBroadcaster) mbean).addNotificationListener( +// listener, filter, handback); +// } +// +// public void removeNotificationListener(NotificationListener listener) +// throws ListenerNotFoundException { +// ((NotificationBroadcaster) mbean).removeNotificationListener(listener); +// } +// +// public MBeanNotificationInfo[] getNotificationInfo() { +// return ((NotificationBroadcaster) mbean).getNotificationInfo(); +// } +// +// public Object getWrappedMBean() { +// return getUserMBean(mbean); +// } +// } + + private static Object invokeSomething( + AccessibleObject ao, Object target, Object[] args) + throws MBeanException, ReflectionException { + try { + if (ao instanceof Method) + return ((Method) ao).invoke(target, args); + else + return ((Constructor) ao).newInstance(args); + } catch (InvocationTargetException e) { + try { + throw e.getCause(); + } catch (RuntimeException x) { + throw new RuntimeMBeanException(x); + } catch (Error x) { + throw new RuntimeErrorException(x); + } catch (Exception x) { + throw new MBeanException(x); + } catch (Throwable x) { + throw new RuntimeException(x); // neither Error nor Exception! + } + } catch (Exception e) { + throw new ReflectionException(e); + } + } + + private static Object getUserMBean(DynamicMBean mbean) { + if (mbean instanceof WrapDynamicMBean) + return ((WrapDynamicMBean) mbean).getWrappedMBean(); + return mbean; + } + + private Object getUserMBean(ObjectName name) + throws InstanceNotFoundException { + return getUserMBean(getMBean(name)); + } + + private static final MBeanRegistration noRegistration = + new MBeanRegistration() { + public ObjectName preRegister(MBeanServer server, ObjectName name) { + return name; + } + + public void postRegister(Boolean registrationDone) { + } + + public void preDeregister() throws Exception { + } + + public void postDeregister() { + } + }; + + private static MBeanRegistration mbeanRegistration(Object object) { + if (object instanceof MBeanRegistration) + return (MBeanRegistration) object; + else + return noRegistration; + } + + private static List newList() { + return new ArrayList(); + } + + private static Map newMap() { + return new HashMap(); + } + + private static Set newSet() { + return new HashSet(); + } + } +} diff --git a/test/javax/management/eventService/AddRemoveListenerTest.java b/test/javax/management/eventService/AddRemoveListenerTest.java new file mode 100644 index 000000000..3b906591b --- /dev/null +++ b/test/javax/management/eventService/AddRemoveListenerTest.java @@ -0,0 +1,371 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test AddRemoveListenerTest.java + * @bug 5108776 + * @summary Basic test for EventClient to see internal thread management. + * @author Shanliang JIANG + * @run clean AddRemoveListenerTest + * @run build AddRemoveListenerTest + * @run main AddRemoveListenerTest + */ + +import java.io.IOException; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventClient; +import javax.management.event.EventClientDelegate; +import javax.management.event.EventClientDelegateMBean; +import javax.management.event.FetchingEventRelay; +import javax.management.event.RMIPushEventRelay; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + + +// This thread creates a single MBean that emits a number of parallel +// sequences of notifications. Each sequence is distinguished by an id +// and each id corresponds to a thread that is filtering the notifications +// so it only sees its own ones. The notifications for a given id have +// contiguous sequence numbers and each thread checks that the notifications +// it receives do indeed have these numbers. If notifications are lost or +// if the different sequences interfere with each other then the test will +// fail. As an added tweak, a "noise" thread periodically causes notifications +// to be emitted that do not correspond to any sequence and do not have any id. +public class AddRemoveListenerTest { + + private static MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer(); + private static ObjectName emitter; + private static NotificationSender emitterImpl; + private static JMXServiceURL url; + private static JMXConnectorServer server; + + private static int toSend = 100; + private static final long bigWaiting = 10000; + private static int counter = 0; + private static int jobs = 10; + private static int endedJobs = 0; + + private static volatile String failure; + + public static void main(String[] args) throws Exception { + System.out.println(">>> Test on multiple adding/removing listeners."); + + // for 1.5 + if (System.getProperty("java.version").startsWith("1.5") && + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) { + System.out.print("Working on "+System.getProperty("java.version")+ + " register "+EventClientDelegateMBean.OBJECT_NAME); + + mbeanServer.registerMBean(EventClientDelegate. + getEventClientDelegate(mbeanServer), + EventClientDelegateMBean.OBJECT_NAME); + } + + emitter = new ObjectName("Default:name=NotificationSender"); + emitterImpl = new NotificationSender(); + mbeanServer.registerMBean(emitterImpl, emitter); + + String[] types = new String[]{"PushEventRelay", "FetchingEventRelay"}; + String[] protos = new String[]{"rmi", "iiop", "jmxmp"}; + for (String prot : protos) { + url = new JMXServiceURL(prot, null, 0); + + try { + server = + JMXConnectorServerFactory.newJMXConnectorServer(url, + null, mbeanServer); + server.start(); + } catch (Exception e) { + System.out.println(">>> Skip "+prot+", not supported."); + continue; + } + + url = server.getAddress(); + + // noise + Thread noise = new Thread(new Runnable() { + public void run() { + while (true) { + emitterImpl.sendNotif(1, null); + try { + Thread.sleep(10); + } catch (Exception e) { + // OK + } + } + } + }); + noise.setDaemon(true); + noise.start(); + + try { + for (String type: types) { + System.out.println("\n\n>>> Testing "+type+" on "+url+" ..."); + JMXConnector conn = newConn(); + try { + testType(type, conn); + } finally { + conn.close(); + System.out.println(">>> Testing "+type+" on "+url+" ... done"); + } + } + } finally { + server.stop(); + } + } + } + + private static void testType(String type, JMXConnector conn) throws Exception { + Thread[] threads = new Thread[jobs]; + for (int i=0; i 0 && failure == null) { + AddRemoveListenerTest.class.wait(toWait); + toWait = stopTime - System.currentTimeMillis(); + } + } + + if (endedJobs != jobs && failure == null) { + throw new RuntimeException("Need to set bigger waiting timeout?"); + } + + endedJobs = 0; + } + + public static class Job implements Runnable { + public Job(String type, JMXConnector conn) { + this.type = type; + this.conn = conn; + } + public void run() { + try { + test(type, conn); + + synchronized(AddRemoveListenerTest.class) { + endedJobs++; + if (endedJobs>=jobs) { + AddRemoveListenerTest.class.notify(); + } + } + } catch (RuntimeException re) { + throw re; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private final String type; + private final JMXConnector conn; + } + + private static void test(String type, JMXConnector conn) throws Exception { + EventClient ec = newEventClient(type, conn); + try { + test(type, conn, ec); + } finally { + ec.close(); + } + } + + private static void test(String type, JMXConnector conn, EventClient ec) + throws Exception { + String id = getId(); + + Listener listener = new Listener(id); + Filter filter = new Filter(id); + + System.out.println(">>> ("+id+") To receive notifications "+toSend); + ec.addNotificationListener(emitter, + listener, filter, null); + + emitterImpl.sendNotif(toSend, id); + listener.waitNotifs(bigWaiting, toSend); + if (listener.received != toSend) { + throw new RuntimeException(">>> ("+id+") Expected to receive: " + +toSend+", but got: "+listener.received); + } + + listener.clear(); + ec.removeNotificationListener(emitter, listener, filter, null); + + System.out.println(">>> ("+id+") Repeat adding and removing ..."); + for (int j=0; j<10; j++) { + ec.addNotificationListener(emitter, dummyListener, null, id); + Thread.yield(); // allow to start listening + ec.removeNotificationListener(emitter, dummyListener, null, id); + } + + System.out.println(">>> ("+id+") To receive again notifications "+toSend); + ec.addNotificationListener(emitter, + listener, filter, null); + + emitterImpl.sendNotif(toSend, id); + listener.waitNotifs(bigWaiting, toSend); + Thread.yield(); //any duplicated? + if (listener.received != toSend) { + throw new RuntimeException("("+id+") Expected to receive: " + +toSend+", but got: "+listener.received); + } + } + +//-------------------------- +// private classes +//-------------------------- + + private static class Listener implements NotificationListener { + public Listener(String id) { + this.id = id; + } + public void handleNotification(Notification notif, Object handback) { + if (!id.equals(notif.getUserData())) { + System.out.println("("+id+") Filter error, my id is: "+id+ + ", but got "+notif.getUserData()); + System.exit(1); + } + + synchronized (this) { + received++; + + if(++sequenceNB != notif.getSequenceNumber()) { + fail("(" + id + ") Wrong sequence number, expected: " + +sequenceNB+", but got: "+notif.getSequenceNumber()); + } + if (received >= toSend || failure != null) { + this.notify(); + } + } + } + + public void waitNotifs(long timeout, int nb) throws Exception { + long toWait = timeout; + long stopTime = System.currentTimeMillis() + timeout; + synchronized(this) { + while (received < nb && toWait > 0 && failure == null) { + this.wait(toWait); + toWait = stopTime - System.currentTimeMillis(); + } + } + } + + public void clear() { + synchronized(this) { + received = 0; + sequenceNB = -1; + } + } + + private String id; + private int received = 0; + + private long sequenceNB = -1; + } + + private static class Filter implements NotificationFilter { + public Filter(String id) { + this.id = id; + } + + public boolean isNotificationEnabled(Notification n) { + return id.equals(n.getUserData()); + } + private String id; + } + + private static NotificationListener dummyListener = new NotificationListener() { + public void handleNotification(Notification notif, Object handback) { + } + }; + + public static class NotificationSender extends NotificationBroadcasterSupport + implements NotificationSenderMBean { + + /** + * Send Notification objects. + * + * @param nb The number of notifications to send + */ + public void sendNotif(int nb, String userData) { + long sequenceNumber = 0; + for (int i = 0; i notifQueue = + new ArrayBlockingQueue(10); + NotificationListener countListener = new NotificationListener() { + public void handleNotification(Notification notification, Object handback) { + System.out.println("Received: " + notification); + notifQueue.add(notification); + if (!"tiddly".equals(handback)) { + System.err.println("TEST FAILED: bad handback: " + handback); + System.exit(1); + } + } + }; + + final AtomicInteger filterCount = new AtomicInteger(0); + NotificationFilter countFilter = new NotificationFilter() { + private static final long serialVersionUID = 1234L; + + public boolean isNotificationEnabled(Notification notification) { + System.out.println("Filter called for: " + notification); + filterCount.incrementAndGet(); + return true; + } + }; + + client.addNotificationListener(name, countListener, countFilter, "tiddly"); + + assertEquals("Initial notif count", 0, notifQueue.size()); + assertEquals("Initial filter count", 0, filterCount.get()); + + Notification n = nextNotif(name); + mbean.send(n); + + System.out.println("Waiting for notification to arrive..."); + + Notification n1 = notifQueue.poll(10, TimeUnit.SECONDS); + + assertEquals("Received notif", n, n1); + assertEquals("Notif queue size after receive", 0, notifQueue.size()); + assertEquals("Filter count after notif", 1, filterCount.get()); + assertEquals("Lost notif count", 0, lostCountSema.availablePermits()); + + System.out.println("Dropping notifs"); + + UdpEventForwarder.setDrop(true); + for (int i = 0; i < 3; i++) + mbean.send(nextNotif(name)); + UdpEventForwarder.setDrop(false); + + Thread.sleep(2); + assertEquals("Notif queue size after drops", 0, notifQueue.size()); + + System.out.println("Turning off dropping and sending a notif"); + n = nextNotif(name); + mbean.send(n); + + System.out.println("Waiting for dropped notifications to be detected..."); + boolean acquired = lostCountSema.tryAcquire(3, 5, TimeUnit.SECONDS); + assertEquals("Correct count of lost notifs", true, acquired); + + n1 = notifQueue.poll(10, TimeUnit.SECONDS); + assertEquals("Received non-dropped notif", n, n1); + + assertEquals("Notif queue size", 0, notifQueue.size()); + assertEquals("Filter count after drops", 5, filterCount.get()); + + Thread.sleep(10); + assertEquals("Further lost-notifs", 0, lostCountSema.availablePermits()); + + client.close(); + + System.out.println("TEST PASSED"); + } + + private static AtomicLong nextSeqNo = new AtomicLong(0); + private static Notification nextNotif(ObjectName name) { + long n = nextSeqNo.incrementAndGet(); + return new Notification("type", name, n, "" + n); + } + + private static void assertEquals(String what, Object expected, Object got) { + if (equals(expected, got)) + System.out.println(what + " = " + expected + ", as expected"); + else { + Map traces = Thread.getAllStackTraces(); + for (Thread t : traces.keySet()) { + System.out.println(t.getName()); + for (StackTraceElement elmt : traces.get(t)) { + System.out.println(" " + elmt); + } + } + throw new RuntimeException( + "TEST FAILED: " + what + " is " + got + "; should be " + + expected); + } + } + + private static boolean equals(Object expected, Object got) { + if (!(expected instanceof Notification)) + return expected.equals(got); + if (expected.getClass() != got.getClass()) + return false; + // Notification doesn't override Object.equals so two distinct + // notifs are never equal even if they have the same contents. + // Although the test doesn't serialize the notifs, if at some + // stage it did then it would fail because the deserialized notif + // was not equal to the original one. Therefore we compare enough + // notif fields to detect when notifs really are different. + Notification en = (Notification) expected; + Notification gn = (Notification) got; + return (en.getType().equals(gn.getType()) && + en.getSource().equals(gn.getSource()) && + en.getSequenceNumber() == gn.getSequenceNumber()); + } +} diff --git a/test/javax/management/eventService/EventClientExecutorTest.java b/test/javax/management/eventService/EventClientExecutorTest.java new file mode 100644 index 000000000..19d6afdf8 --- /dev/null +++ b/test/javax/management/eventService/EventClientExecutorTest.java @@ -0,0 +1,191 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 5108776 + * @summary Test that the various Executor parameters in an EventClient do + * what they are supposed to. + * @author Eamonn McManus + */ + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventClient; +import javax.management.event.EventClientDelegate; +import javax.management.event.EventClientDelegateMBean; +import javax.management.event.FetchingEventRelay; +import javax.management.remote.MBeanServerForwarder; + +public class EventClientExecutorTest { + private static volatile String failure; + private static final Set testedPrefixes = new HashSet(); + + public static void main(String[] args) throws Exception { + Executor fetchExecutor = Executors.newSingleThreadExecutor( + new NamedThreadFactory("FETCH")); + Executor listenerExecutor = Executors.newSingleThreadExecutor( + new NamedThreadFactory("LISTENER")); + ScheduledExecutorService leaseScheduler = + Executors.newSingleThreadScheduledExecutor( + new NamedThreadFactory("LEASE")); + + MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + MBeanServerForwarder mbsf = EventClientDelegate.newForwarder(); + mbsf.setMBeanServer(mbs); + mbs = mbsf; + + EventClientDelegateMBean ecd = EventClientDelegate.getProxy(mbs); + ecd = (EventClientDelegateMBean) Proxy.newProxyInstance( + EventClientDelegateMBean.class.getClassLoader(), + new Class[] {EventClientDelegateMBean.class}, + new DelegateCheckIH(ecd)); + + ObjectName mbeanName = new ObjectName("d:type=Notifier"); + Notifier notifier = new Notifier(); + mbs.registerMBean(notifier, mbeanName); + + FetchingEventRelay eventRelay = new FetchingEventRelay( + ecd, fetchExecutor); + EventClient ec = new EventClient( + ecd, eventRelay, listenerExecutor, leaseScheduler, 1000L); + NotificationListener checkListener = new NotificationListener() { + public void handleNotification(Notification notification, + Object handback) { + assertThreadName("listener dispatch", "LISTENER"); + } + }; + ec.addNotificationListener(mbeanName, checkListener, null, null); + + mbs.invoke(mbeanName, "send", null, null); + + // Now wait until we have seen all three thread types. + long deadline = System.currentTimeMillis() + 5000; + synchronized (testedPrefixes) { + while (testedPrefixes.size() < 3 && failure == null) { + long remain = deadline - System.currentTimeMillis(); + if (remain <= 0) { + fail("Timed out waiting for all three thread types to show, " + + "saw only " + testedPrefixes); + break; + } + try { + testedPrefixes.wait(remain); + } catch (InterruptedException e) { + fail("Unexpected InterruptedException"); + break; + } + } + } + + // We deliberately don't close the EventClient to check that it has + // not created any non-daemon threads. + + if (failure != null) + throw new Exception("TEST FAILED: " + failure); + else + System.out.println("TEST PASSED"); + } + + public static interface NotifierMBean { + public void send(); + } + + public static class Notifier extends NotificationBroadcasterSupport + implements NotifierMBean { + public void send() { + Notification n = new Notification("a.b.c", this, 0L); + sendNotification(n); + } + } + + static void fail(String why) { + System.out.println("FAIL: " + why); + failure = why; + } + + static void assertThreadName(String what, String prefix) { + String name = Thread.currentThread().getName(); + if (!name.startsWith(prefix)) { + fail("Wrong thread for " + what + ": " + name); + return; + } + + synchronized (testedPrefixes) { + if (testedPrefixes.add(prefix)) + testedPrefixes.notify(); + } + } + + private static class DelegateCheckIH implements InvocationHandler { + private final EventClientDelegateMBean ecd; + + public DelegateCheckIH(EventClientDelegateMBean ecd) { + this.ecd = ecd; + } + + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + String methodName = method.getName(); + if (methodName.equals("fetchNotifications")) + assertThreadName("fetchNotifications", "FETCH"); + else if (methodName.equals("lease")) + assertThreadName("lease renewal", "LEASE"); + try { + return method.invoke(ecd, args); + } catch (InvocationTargetException e) { + throw e.getCause(); + } + } + } + + private static class NamedThreadFactory implements ThreadFactory { + private final String namePrefix; + private int count; + + NamedThreadFactory(String namePrefix) { + this.namePrefix = namePrefix; + } + + public synchronized Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setName(namePrefix + " " + ++count); + t.setDaemon(true); + return t; + } + } +} diff --git a/test/javax/management/eventService/EventDelegateSecurityTest.java b/test/javax/management/eventService/EventDelegateSecurityTest.java new file mode 100644 index 000000000..a227e13f5 --- /dev/null +++ b/test/javax/management/eventService/EventDelegateSecurityTest.java @@ -0,0 +1,289 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 5108776 + * @summary Test that the EventClientDelegate MBean does not require extra + * permissions compared with plain addNotificationListener. + * @author Eamonn McManus + * @run main/othervm -Dxjava.security.debug=policy,access,failure EventDelegateSecurityTest + */ + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.management.ManagementFactory; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.AllPermission; +import java.security.PrivilegedExceptionAction; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.TimeUnit; +import javax.management.MBeanPermission; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventClient; +import javax.management.remote.JMXAuthenticator; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXPrincipal; +import javax.management.remote.JMXServiceURL; +import javax.management.remote.MBeanServerForwarder; +import javax.security.auth.Subject; + +public class EventDelegateSecurityTest { + private static final BlockingQueue notifQ = + new SynchronousQueue(); + + private static volatile long seqNo; + private static volatile long expectSeqNo; + + private static class QueueListener implements NotificationListener { + public void handleNotification(Notification notification, + Object handback) { + try { + notifQ.put(notification); + } catch (InterruptedException e) { + throw new AssertionError(e); + } + } + } + private static final NotificationListener queueListener = new QueueListener(); + + public static interface SenderMBean { + public void send(); + } + + public static class Sender + extends NotificationBroadcasterSupport implements SenderMBean { + public void send() { + Notification n = new Notification("x", this, seqNo++); + sendNotification(n); + } + } + + private static class LimitInvocationHandler implements InvocationHandler { + private MBeanServer nextMBS; + private final Set allowedMethods = new HashSet(); + + void allow(String... names) { + synchronized (allowedMethods) { + allowedMethods.addAll(Arrays.asList(names)); + } + } + + public Object invoke(Object proxy, Method m, Object[] args) + throws Throwable { + System.out.println( + "filter: " + m.getName() + + ((args == null) ? "[]" : Arrays.deepToString(args))); + String name = m.getName(); + + if (name.equals("getMBeanServer")) + return nextMBS; + + if (name.equals("setMBeanServer")) { + nextMBS = (MBeanServer) args[0]; + return null; + } + + if (m.getDeclaringClass() == Object.class || + allowedMethods.contains(name)) { + try { + return m.invoke(nextMBS, args); + } catch (InvocationTargetException e) { + throw e.getCause(); + } + } else { + System.out.println("...refused"); + throw new SecurityException( + "Method refused: " + m.getDeclaringClass().getName() + + "." + m.getName() + + ((args == null) ? "[]" : Arrays.deepToString(args))); + } + } + + } + + private static interface MakeConnectorServer { + public JMXConnectorServer make(JMXServiceURL url) throws IOException; + } + + + public static void main(String[] args) throws Exception { + JMXPrincipal rootPrincipal = new JMXPrincipal("root"); + Subject rootSubject = new Subject(); + rootSubject.getPrincipals().add(rootPrincipal); + Subject.doAsPrivileged(rootSubject, new PrivilegedExceptionAction() { + public Void run() throws Exception { + mainAsRoot(); + return null; + } + }, null); + } + + private static void mainAsRoot() throws Exception { + AccessControlContext acc = AccessController.getContext(); + Subject subject = Subject.getSubject(acc); + System.out.println("Subject: " + subject); + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + ObjectName name = new ObjectName("a:b=c"); + mbs.registerMBean(new Sender(), name); + + System.out.println("Test with no installed security"); + test(mbs, name, new MakeConnectorServer() { + public JMXConnectorServer make(JMXServiceURL url) throws IOException { + return + JMXConnectorServerFactory.newJMXConnectorServer(url, null, null); + } + }); + + System.out.println("Test with filtering MBeanServerForwarder"); + LimitInvocationHandler limitIH = new LimitInvocationHandler(); + // We allow getClassLoaderRepository because the ConnectorServer + // calls it so any real checking MBeanServerForwarder must accept it. + limitIH.allow( + "addNotificationListener", "removeNotificationListener", + "getClassLoaderRepository" + ); + final MBeanServerForwarder limitMBSF = (MBeanServerForwarder) + Proxy.newProxyInstance( + MBeanServerForwarder.class.getClassLoader(), + new Class[] {MBeanServerForwarder.class}, + limitIH); + // We go to considerable lengths to ensure that the ConnectorServer has + // no MBeanServer when the EventClientDelegate forwarder is activated, + // so that the calls it makes when it is later linked to an MBeanServer + // go through the limitMBSF. + test(mbs, name, new MakeConnectorServer() { + public JMXConnectorServer make(JMXServiceURL url) throws IOException { + JMXConnectorServer cs = + JMXConnectorServerFactory.newJMXConnectorServer(url, null, null); + limitMBSF.setMBeanServer(mbs); + cs.setMBeanServerForwarder(limitMBSF); + return cs; + } + }); + + final File policyFile = + File.createTempFile("EventDelegateSecurityTest", ".policy"); + PrintWriter pw = new PrintWriter(policyFile); + String JMXPrincipal = JMXPrincipal.class.getName(); + String AllPermission = AllPermission.class.getName(); + String MBeanPermission = MBeanPermission.class.getName(); + pw.println("grant principal " + JMXPrincipal + " \"root\" {"); + pw.println(" permission " + AllPermission + ";"); + pw.println("};"); + pw.println("grant principal " + JMXPrincipal + " \"user\" {"); + pw.println(" permission " + MBeanPermission + " \"*\", " + + " \"addNotificationListener\";"); + pw.println(" permission " + MBeanPermission + " \"*\", " + + " \"removeNotificationListener\";"); + pw.println("};"); + pw.close(); + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + policyFile.delete(); + } + }); + System.setProperty("java.security.policy", policyFile.getAbsolutePath()); + System.setSecurityManager(new SecurityManager()); + test(mbs, name, new MakeConnectorServer() { + public JMXConnectorServer make(JMXServiceURL url) throws IOException { + Map env = new HashMap(); + env.put(JMXConnectorServer.AUTHENTICATOR, new JMXAuthenticator() { + public Subject authenticate(Object credentials) { + Subject s = new Subject(); + s.getPrincipals().add(new JMXPrincipal("user")); + return s; + } + }); + return + JMXConnectorServerFactory.newJMXConnectorServer(url, env, null); + } + }); + } + + private static void test(MBeanServer mbs, ObjectName name) throws Exception { + test(mbs, name, null); + } + + private static void test( + MBeanServer mbs, ObjectName name, MakeConnectorServer make) + throws Exception { + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///"); + JMXConnectorServer cs = make.make(url); + ObjectName csName = new ObjectName("a:type=ConnectorServer"); + mbs.registerMBean(cs, csName); + cs.start(); + try { + JMXServiceURL addr = cs.getAddress(); + JMXConnector cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + test(mbs, mbsc, name); + cc.close(); + mbs.unregisterMBean(csName); + } finally { + cs.stop(); + } + } + + private static void test( + MBeanServer mbs, MBeanServerConnection mbsc, ObjectName name) + throws Exception { + EventClient ec = new EventClient(mbsc); + ec.addNotificationListener(name, queueListener, null, null); + mbs.invoke(name, "send", null, null); + + Notification n = notifQ.poll(5, TimeUnit.SECONDS); + if (n == null) + throw new Exception("FAILED: notif not delivered"); + if (n.getSequenceNumber() != expectSeqNo) { + throw new Exception( + "FAILED: notif seqno " + n.getSequenceNumber() + + " should be " + expectSeqNo); + } + expectSeqNo++; + + ec.removeNotificationListener(name, queueListener); + ec.close(); + } +} diff --git a/test/javax/management/eventService/EventManagerTest.java b/test/javax/management/eventService/EventManagerTest.java new file mode 100644 index 000000000..473171ba5 --- /dev/null +++ b/test/javax/management/eventService/EventManagerTest.java @@ -0,0 +1,221 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test EventManagerTest.java 1.8 08/01/22 + * @bug 5108776 + * @summary Basic test for EventManager. + * @author Shanliang JIANG + * @run clean EventManagerTest + * @run build EventManagerTest + * @run main EventManagerTest + */ + +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.*; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +/** + * + */ +public class EventManagerTest { + private static MBeanServer mbeanServer; + private static ObjectName emitter; + private static JMXServiceURL url; + private static JMXConnectorServer server; + private static JMXConnector conn; + private static MBeanServerConnection client; + + /** + * @param args the command line arguments + */ + public static void main(String[] args) throws Exception { + System.out.println(">>> EventManagerTest-main basic tests ..."); + mbeanServer = MBeanServerFactory.createMBeanServer(); + + // for 1.5 + if (System.getProperty("java.version").startsWith("1.5") && + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) { + System.out.print("Working on "+System.getProperty("java.version")+ + " register "+EventClientDelegateMBean.OBJECT_NAME); + + mbeanServer.registerMBean(EventClientDelegate. + getEventClientDelegate(mbeanServer), + EventClientDelegateMBean.OBJECT_NAME); + } + + emitter = new ObjectName("Default:name=NotificationEmitter"); + + url = new JMXServiceURL("rmi", null, 0) ; + server = + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer); + server.start(); + + url = server.getAddress(); + conn = JMXConnectorFactory.connect(url, null); + client = conn.getMBeanServerConnection(); + + mbeanServer.registerMBean(new NotificationEmitter(), emitter); + + boolean succeed; + + System.out.println(">>> EventManagerTest-main: using the fetching EventRelay..."); + succeed = test(new EventClient(client)); + + System.out.println(">>> EventManagerTest-main: using the pushing EventRelay..."); + EventClientDelegateMBean ecd = EventClientDelegate.getProxy(client); + succeed &= test(new EventClient(ecd, + new RMIPushEventRelay(ecd), + null, null, + EventClient.DEFAULT_LEASE_TIMEOUT)); + + conn.close(); + server.stop(); + + if (succeed) { + System.out.println(">>> EventManagerTest-main: PASSE!"); + } else { + System.out.println("\n>>> EventManagerTest-main: FAILED!"); + System.exit(1); + } + } + + public static boolean test(EventClient efClient) throws Exception { + // add listener from the client side + Listener listener = new Listener(); + efClient.subscribe(emitter, listener, null, null); + + // ask to send notifs + Object[] params = new Object[] {new Integer(sendNB)}; + String[] signatures = new String[] {"java.lang.Integer"}; + client.invoke(emitter, "sendNotifications", params, signatures); + + // waiting + long toWait = 6000; + long stopTime = System.currentTimeMillis() + toWait; + + synchronized(listener) { + while(listener.received < sendNB && toWait > 0) { + listener.wait(toWait); + toWait = stopTime - System.currentTimeMillis(); + } + } + + // clean + System.out.println(">>> EventManagerTest-test: cleaning..."); + efClient.unsubscribe(emitter, listener); + efClient.close(); + + if (listener.received != sendNB) { + System.out.println(">>> EventManagerTest-test: FAILED! Expected to receive "+sendNB+", but got "+listener.received); + + return false; + } else if (listener.seqErr > 0) { + System.out.println(">>> EventManagerTest-test: FAILED! The receiving sequence is not correct."); + + return false; + } else { + System.out.println(">>> EventManagerTest-test: got all expected "+listener.received); + return true; + } + } + + private static class Listener implements NotificationListener { + public int received = 0; + public int seqErr = 0; + + private long lastSeq = -1; + + public void handleNotification(Notification notif, Object handback) { + if (!myType.equals(notif.getType())) { + System.out.println(">>> EventManagerTest-Listener: got unexpected notif: "+notif); + System.exit(1); + } + + if (lastSeq == -1) { + lastSeq = notif.getSequenceNumber(); + } else if (notif.getSequenceNumber() - lastSeq++ != 1) { + seqErr++; + } + + //System.out.println(">>> EventManagerTest-Listener: got notif "+notif.getSequenceNumber()); + + synchronized(this) { + if (++received >= sendNB) { + this.notify(); + } + } + } + } + + public static class NotificationEmitter extends NotificationBroadcasterSupport + implements NotificationEmitterMBean { + + public MBeanNotificationInfo[] getNotificationInfo() { + final String[] ntfTypes = {myType}; + + final MBeanNotificationInfo[] ntfInfoArray = { + new MBeanNotificationInfo(ntfTypes, + "javax.management.Notification", + "Notifications sent by the NotificationEmitter")}; + + return ntfInfoArray; + } + + /** + * Send Notification objects. + * + * @param nb The number of notifications to send + */ + public void sendNotifications(Integer nb) { + Notification notif; + for (int i=1; i<=nb.intValue(); i++) { + notif = new Notification(myType, this, count++); + notif.setUserData("jsl"); + //System.out.println(">>> EventManagerService-NotificationEmitter-sendNotifications: "+i); + + sendNotification(notif); + } + } + } + + public interface NotificationEmitterMBean { + public void sendNotifications(Integer nb); + } + + private static int sendNB = 120; + private static long count = 0; + + private static final String myType = "notification.my_notification"; +} diff --git a/test/javax/management/eventService/FetchingTest.java b/test/javax/management/eventService/FetchingTest.java new file mode 100644 index 000000000..9920ea608 --- /dev/null +++ b/test/javax/management/eventService/FetchingTest.java @@ -0,0 +1,276 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 5108776 + * @summary Basic test for EventClient. + * @author Shanliang JIANG + * @run clean FetchingTest MyFetchingEventForwarder + * @run build FetchingTest MyFetchingEventForwarder + * @run main FetchingTest MyFetchingEventForwarder + */ + +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventClient; +import javax.management.event.EventClientDelegate; +import javax.management.event.EventClientDelegateMBean; +import javax.management.event.FetchingEventRelay; +import javax.management.event.RMIPushEventForwarder; +import javax.management.event.RMIPushServer; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +public class FetchingTest { + private static MBeanServer mbeanServer; + private static ObjectName emitter; + private static JMXServiceURL url; + private static JMXConnectorServer server; + private static JMXConnector conn; + private static MBeanServerConnection client; + private static long WAITING_TIME = 6000; + + /** + * @param args the command line arguments + */ + public static void main(String[] args) throws Exception { + + System.out.println(">>> FetchingTest-main basic tests ..."); + mbeanServer = MBeanServerFactory.createMBeanServer(); + + // for 1.5 + if (System.getProperty("java.version").startsWith("1.5") && + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) { + System.out.print("Working on "+System.getProperty("java.version")+ + " register "+EventClientDelegateMBean.OBJECT_NAME); + + mbeanServer.registerMBean(EventClientDelegate. + getEventClientDelegate(mbeanServer), + EventClientDelegateMBean.OBJECT_NAME); + } + + emitter = new ObjectName("Default:name=NotificationEmitter"); + mbeanServer.registerMBean(new NotificationEmitter(), emitter); + boolean succeed = true; + + final String[] protos = new String[] {"rmi", "iiop", "jmxmp"}; + for (String proto : protos) { + System.out.println(">>> FetchingTest-main: testing on "+proto); + + try { + url = new JMXServiceURL(proto, null, 0) ; + server = JMXConnectorServerFactory. + newJMXConnectorServer(url, null, mbeanServer); + server.start(); + } catch (Exception e) { + // OK + System.out.println(">>> FetchingTest-main: skip the proto "+proto); + continue; + } + + url = server.getAddress(); + conn = JMXConnectorFactory.connect(url, null); + client = conn.getMBeanServerConnection(); + + succeed &= test(); + + conn.close(); + server.stop(); + + System.out.println( + ">>> FetchingTest-main: testing on "+proto+" done."); + } + + if (succeed) { + System.out.println(">>> FetchingTest-main: PASSED!"); + } else { + System.out.println("\n>>> FetchingTest-main: FAILED!"); + System.exit(1); + } + } + + public static boolean test() throws Exception { + System.out.println(">>> FetchingTest-test: " + + "using the default fetching forwarder ..."); + EventClient eventClient = + new EventClient(client); + + Listener listener = new Listener(); + eventClient.addNotificationListener(emitter, listener, null, null); + + // ask to send notifs + Object[] params = new Object[] {new Integer(sendNB)}; + String[] signatures = new String[] {"java.lang.Integer"}; + conn.getMBeanServerConnection().invoke(emitter, + "sendNotifications", params, signatures); + + if (listener.waitNotif(WAITING_TIME) != sendNB) { + System.out.println( + ">>> FetchingTest-test: FAILED! Expected to receive "+ + sendNB+", but got "+listener.received); + + return false; + } + + System.out.println( + ">>> ListenerTest-test: got all expected "+listener.received); + //eventClient.removeNotificationListener(emitter, listener); + eventClient.close(); + + System.out.println(">>> FetchingTest-test: " + + "using a user specific List ..."); + + FetchingEventRelay fer = new FetchingEventRelay( + EventClientDelegate.getProxy(client), + 1000, 1000L, 1000, null, + MyFetchingEventForwarder.class.getName(), + null, null); + + eventClient = new EventClient( + EventClientDelegate.getProxy(client), fer, null, null, 10000); + + eventClient.addNotificationListener(emitter, listener, null, null); + listener.received = 0; + + conn.getMBeanServerConnection().invoke(emitter, + "sendNotifications", params, signatures); + + if (listener.waitNotif(WAITING_TIME) != sendNB) { + System.out.println( + ">>> FetchingTest-test: FAILED! Expected to receive "+ + sendNB+", but got "+listener.received); + + return false; + } + + System.out.println( + ">>> FetchingTest-test: got all expected "+listener.received); + + if (!MyFetchingEventForwarder.shared.isUsed()) { + System.out.println( + ">>> FetchingTest-test: FAILED! The user specific list" + + "is not used!"); + + return false; + } + + System.out.println(">>> Negative test to add an EventClient" + + " with a non EventForwarder object."); + try { + MyFetchingEventForwarder.shared.setAgain(); + + System.out.println( + ">>> FetchingTest-test: FAILED! No expected exception" + + "when setting the list after the forwarder started."); + + return false; + } catch (IllegalStateException ise) { + // OK + System.out.println( + ">>> FetchingTest-test: Got expected exception: " + ise); + } + + eventClient.close(); + + try { + fer = new FetchingEventRelay( + EventClientDelegate.getProxy(client), + 1000, 1000L, 1000, null, + Object.class.getName(), + null, null); + + eventClient = new EventClient( + EventClientDelegate.getProxy(client), fer, null, null, 10000); + + System.out.println( + ">>> FetchingTest-test: FAILED! No expected exception" + + "when creating an illegal EventForwarder"); + } catch (IllegalArgumentException iae) { + // OK + // iae.printStackTrace(); + } + + return true; + } + + private static class Listener implements NotificationListener { + public void handleNotification(Notification notif, Object handback) { + synchronized(this) { + if (++received >= sendNB) { + this.notify(); + } + } + + //System.out.println(">>> FetchingTest-Listener: received = "+received); + } + + public int waitNotif(long timeout) throws Exception { + synchronized(this) { + long stopTime = System.currentTimeMillis() + timeout; + long toWait = timeout; + while (toWait > 0 && received < sendNB) { + this.wait(toWait); + toWait = stopTime - System.currentTimeMillis(); + } + } + + return received; + } + + public static int received = 0; + } + + public static class NotificationEmitter extends NotificationBroadcasterSupport + implements NotificationEmitterMBean { + + public void sendNotifications(Integer nb) { + System.out.println( + ">>> FetchingTest-NotificationEmitter-sendNotifications: "+nb); + Notification notif; + for (int i=1; i<=nb.intValue(); i++) { + notif = new Notification(myType, this, count++); + sendNotification(notif); + } + } + } + + public interface NotificationEmitterMBean { + public void sendNotifications(Integer nb); + } + + + + private static int sendNB = 20; + private static int count = 0; + + private static final String myType = "notification.my_notification"; +} diff --git a/test/javax/management/eventService/LeaseManagerDeadlockTest.java b/test/javax/management/eventService/LeaseManagerDeadlockTest.java new file mode 100644 index 000000000..586b24402 --- /dev/null +++ b/test/javax/management/eventService/LeaseManagerDeadlockTest.java @@ -0,0 +1,96 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6717789 + * @summary Check that a lock is not held when a LeaseManager expires. + * @author Eamonn McManus + */ + +import com.sun.jmx.event.LeaseManager; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.util.Arrays; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +public class LeaseManagerDeadlockTest { + public static String failure; + public static LeaseManager leaseManager; + public static Semaphore callbackThreadCompleted = new Semaphore(0); + public static Object lock = new Object(); + + public static Runnable triggerDeadlock = new Runnable() { + public void run() { + Runnable pingLeaseManager = new Runnable() { + public void run() { + System.out.println("Ping thread starts"); + synchronized (lock) { + leaseManager.lease(1); + } + System.out.println("Ping thread completes"); + } + }; + Thread t = new Thread(pingLeaseManager); + t.start(); + try { + Thread.sleep(10); // enough time for ping thread to grab lock + synchronized (lock) { + t.join(); + } + } catch (InterruptedException e) { + fail(e.toString()); + } + System.out.println("Callback thread completes"); + callbackThreadCompleted.release(); + } + }; + + public static void main(String[] args) throws Exception { + // Also test that we can shorten the lease from its initial value. + leaseManager = new LeaseManager(triggerDeadlock, 1000000); + leaseManager.lease(1L); + + boolean callbackRan = + callbackThreadCompleted.tryAcquire(3, TimeUnit.SECONDS); + + if (!callbackRan) { + fail("Callback did not complete - probable deadlock"); + ThreadMXBean threads = ManagementFactory.getThreadMXBean(); + System.out.println(Arrays.toString(threads.findDeadlockedThreads())); + System.out.println("PRESS RETURN"); + System.in.read(); + } + + if (failure == null) + System.out.println("TEST PASSED"); + else + throw new Exception("TEST FAILED: " + failure); + } + + public static void fail(String why) { + System.out.println("TEST FAILS: " + why); + failure = why; + } +} diff --git a/test/javax/management/eventService/LeaseTest.java b/test/javax/management/eventService/LeaseTest.java new file mode 100644 index 000000000..4b543e099 --- /dev/null +++ b/test/javax/management/eventService/LeaseTest.java @@ -0,0 +1,361 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test LeaseTest.java 1.6 08/01/22 + * @bug 5108776 + * @summary Basic test for Event service leasing. + * @author Shanliang JIANG + * @run clean LeaseTest + * @run build LeaseTest + * @run main LeaseTest + */ + + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventClient; +import javax.management.event.EventClientDelegate; +import javax.management.event.EventClientDelegateMBean; +import javax.management.event.EventClientNotFoundException; +import javax.management.event.FetchingEventRelay; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +public class LeaseTest { + + private static MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer(); + private static List notifList = new ArrayList(); + private static ObjectName emitter; + private static NotificationEmitter emitterImpl; + private static JMXServiceURL url; + private static JMXConnectorServer server; + private static JMXConnector conn; + private static Listener listener = new Listener(); + + private static long leaseTime = 100; + private static final int multiple = 5; + private static final long bigWaiting = 6000; + + public static void main(String[] args) throws Exception { + System.out.println(">>> Test the event service lease"); + + // for 1.5 + if (System.getProperty("java.version").startsWith("1.5") && + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) { + System.out.print("Working on "+System.getProperty("java.version")+ + " register "+EventClientDelegateMBean.OBJECT_NAME); + + mbeanServer.registerMBean(EventClientDelegate. + getEventClientDelegate(mbeanServer), + EventClientDelegateMBean.OBJECT_NAME); + } + + System.setProperty("com.sun.event.lease.time", + String.valueOf(leaseTime)); + emitter = new ObjectName("Default:name=NotificationEmitter"); + emitterImpl = new NotificationEmitter(); + mbeanServer.registerMBean(emitterImpl, emitter); + + String[] types = new String[]{"PushingEventRelay", "FetchingEventRelay"}; + String[] protos = new String[]{"rmi", "iiop", "jmxmp"}; + for (String prot : protos) { + url = new JMXServiceURL(prot, null, 0); + + try { + server = + JMXConnectorServerFactory.newJMXConnectorServer(url, + null, mbeanServer); + server.start(); + } catch (Exception e) { + System.out.println(">>> Skip "+prot+", not support."); + continue; + } + + url = server.getAddress(); + + try { + for (String type: types) { + test(type); + } + } finally { + server.stop(); + } + } + } + + private static void test(String type) throws Exception { + System.out.println("\n\n>>> Testing "+type+" on "+url+" ..."); + newConn(); + EventClient ec = newEventClient(type); + + ec.addNotificationListener(emitter, + listener, null, null); + + System.out.println(">>> Send a notification and should receive it."); + emitterImpl.sendNotif(++counter); + + if (!waitNotif(bigWaiting, counter)) { + throw new RuntimeException(">>> Failed to receive notif."); + } + + System.out.println(">>> Sleep 3 times of requested lease time."); + Thread.sleep(leaseTime*3); + System.out.println(">>> Send again a notification and should receive it."); + emitterImpl.sendNotif(++counter); + + if (!waitNotif(bigWaiting, counter)) { + throw new RuntimeException(">>> Failed to receive notif."); + } + + System.out.println(">>> Close the client connection: "+ + conn.getConnectionId()); + conn.close(); + + System.out.println(">>> Waiting lease timeout to do clean."); + + if (!emitterImpl.waitingClean(leaseTime*multiple)) { + throw new RuntimeException( + ">>> The event lease failed to do clean: "+ + emitterImpl.listenerSize); + } else { + System.out.println(">>> The listener has been removed."); + } + + // Check that the client id has indeed been removed, by trying to + // remove it again, which should fail. + newConn(); + try { + EventClientDelegateMBean proxy = + EventClientDelegate.getProxy(conn.getMBeanServerConnection()); + proxy.removeClient(ec.getEventRelay().getClientId()); + + throw new RuntimeException( + ">>> The client id is not removed."); + } catch (EventClientNotFoundException ecnfe) { + // OK + System.out.println(">>> The client id has been removed."); + } + conn.close(); + + System.out.println(">>> Reconnect to the server."); + newConn(); + + System.out.println(">>> Create a new EventClient and add the listeners" + + " in the failed EventClient into new EventClient"); + EventClient newEC = newEventClient(type); + newEC.addListeners(ec.getListeners()); + // We expect ec.close() to get IOException because we closed the + // underlying connection. + try { + ec.close(); + throw new RuntimeException(">>> EventClient.close did not throw " + + "expected IOException"); + } catch (IOException e) { + System.out.println(">>> EventClient.close threw expected exception: " + e); + } + + emitterImpl.sendNotif(++counter); + + if (!waitNotif(bigWaiting, counter)) { + throw new RuntimeException(">>> The event client failed to add " + + "all old registered listeners after re-connection."); + } else { + System.out.println(">>> Successfully received notification from" + + " new EventClient."); + } + + System.out.println(">>> Clean the failed EventClient."); + ec.close(); + if (ec.getListeners().size() != 0) { + throw new RuntimeException(">>> The event client fails to do clean."); + } + + System.out.println(">>> Clean the new EventClient."); + newEC.close(); + if (newEC.getListeners().size() != 0) { + throw new RuntimeException(">>> The event client fails to do clean."); + } + + conn.close(); + System.out.println(">>> Testing "+type+" on "+url+" ... done"); + } + + private static boolean waitNotif(long time, int sequenceNumber) + throws Exception { + synchronized(notifList) { + if (search(sequenceNumber)) { + return true; + } + + long stopTime = System.currentTimeMillis() + time; + long toWait = time; + while (toWait > 0) { + notifList.wait(toWait); + + if (search(sequenceNumber)) { + return true; + } + + toWait = stopTime - System.currentTimeMillis(); + } + + return false; + } + } + + private static boolean search(int sequenceNumber) { + while(notifList.size() > 0) { + Notification n = notifList.remove(0); + if (n.getSequenceNumber() == sequenceNumber) { + return true; + } + } + + return false; + } + +//-------------------------- +// private classes +//-------------------------- + + private static class Listener implements NotificationListener { + public void handleNotification(Notification notif, Object handback) { + synchronized (notifList) { + notifList.add(notif); + notifList.notify(); + } + } + } + + public static class NotificationEmitter extends NotificationBroadcasterSupport + implements NotificationEmitterMBean { + + public MBeanNotificationInfo[] getNotificationInfo() { + final String[] ntfTypes = {myType}; + + final MBeanNotificationInfo[] ntfInfoArray = { + new MBeanNotificationInfo(ntfTypes, + "javax.management.Notification", + "Notifications sent by the NotificationEmitter")}; + + return ntfInfoArray; + } + + /** + * Send Notification objects. + * + * @param nb The number of notifications to send + */ + public void sendNotif(int sequenceNumber) { + Notification notif = new Notification(myType, this, sequenceNumber); + sendNotification(notif); + } + + public void addNotificationListener(NotificationListener listener, + NotificationFilter filter, Object handback) { + super.addNotificationListener(listener, filter, handback); + + listenerSize++; + } + + public void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException { + super.removeNotificationListener(listener); + listenerSize--; + + synchronized(this) { + if (listenerSize == 0) { + this.notifyAll(); + } + } + } + + public void removeNotificationListener(NotificationListener listener, + NotificationFilter filter, Object handback) + throws ListenerNotFoundException { + super.removeNotificationListener(listener, filter, handback); + listenerSize--; + + synchronized(this) { + if (listenerSize == 0) { + this.notifyAll(); + } + } + } + + public boolean waitingClean(long timeout) throws Exception { + synchronized(this) { + long stopTime = System.currentTimeMillis() + timeout; + long toWait = timeout; + while (listenerSize != 0 && toWait > 0) { + this.wait(toWait); + toWait = stopTime - System.currentTimeMillis(); + } + } + + return listenerSize == 0; + } + + public int listenerSize = 0; + + private final String myType = "notification.my_notification"; + } + + public interface NotificationEmitterMBean { + public void sendNotif(int sequenceNumber); + } + + private static void newConn() throws IOException { + conn = JMXConnectorFactory.connect(url); + } + + private static EventClient newEventClient(String type) throws Exception { + EventClientDelegateMBean proxy = + EventClientDelegate.getProxy(conn.getMBeanServerConnection()); + if (type.equals("PushingEventRelay")) { + return new EventClient(proxy, + new FetchingEventRelay(proxy), null, null, leaseTime); + } else if (type.equals("FetchingEventRelay")) { + return new EventClient(proxy, + new FetchingEventRelay(proxy), null, null, leaseTime); + } else { + throw new RuntimeException("Wrong event client type: "+type); + } + } + + private static int counter = 0; +} diff --git a/test/javax/management/eventService/ListenerTest.java b/test/javax/management/eventService/ListenerTest.java new file mode 100644 index 000000000..5825f835e --- /dev/null +++ b/test/javax/management/eventService/ListenerTest.java @@ -0,0 +1,224 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test ListenerTest.java 1.7 08/01/22 + * @bug 5108776 + * @summary Basic test for EventClient. + * @author Shanliang JIANG + * @run clean ListenerTest + * @run build ListenerTest + * @run main ListenerTest + */ + +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.*; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +/** + * + */ +public class ListenerTest { + private static MBeanServer mbeanServer; + private static ObjectName emitter; + private static JMXServiceURL url; + private static JMXConnectorServer server; + private static JMXConnector conn; + private static MBeanServerConnection client; + + /** + * @param args the command line arguments + */ + public static void main(String[] args) throws Exception { + + System.out.println(">>> ListenerTest-main basic tests ..."); + mbeanServer = MBeanServerFactory.createMBeanServer(); + + // for 1.5 + if (System.getProperty("java.version").startsWith("1.5") && + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) { + System.out.print("Working on "+System.getProperty("java.version")+ + " register "+EventClientDelegateMBean.OBJECT_NAME); + + mbeanServer.registerMBean(EventClientDelegate. + getEventClientDelegate(mbeanServer), + EventClientDelegateMBean.OBJECT_NAME); + } + + emitter = new ObjectName("Default:name=NotificationEmitter"); + + url = new JMXServiceURL("rmi", null, 0) ; + server = + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer); + server.start(); + + url = server.getAddress(); + conn = JMXConnectorFactory.connect(url, null); + client = conn.getMBeanServerConnection(); + + mbeanServer.registerMBean(new NotificationEmitter(), emitter); + + boolean succeed; + + System.out.println(">>> ListenerTest-main: using the fetching EventRelay..."); + succeed = test(new EventClient(client)); + + System.out.println(">>> ListenerTest-main: using the pushing EventRelay..."); + EventClientDelegateMBean ecd = EventClientDelegate.getProxy(client); + succeed &= test(new EventClient(ecd, + new RMIPushEventRelay(ecd), + null, null, + EventClient.DEFAULT_LEASE_TIMEOUT)); + + conn.close(); + server.stop(); + + if (succeed) { + System.out.println(">>> ListenerTest-main: PASSED!"); + } else { + System.out.println("\n>>> ListenerTest-main: FAILED!"); + System.exit(1); + } + } + + public static boolean test(EventClient efClient) throws Exception { + // add listener from the client side + Listener listener = new Listener(); + efClient.addNotificationListener(emitter, listener, null, null); + + // ask to send notifs + Object[] params = new Object[] {new Integer(sendNB)}; + String[] signatures = new String[] {"java.lang.Integer"}; + client.invoke(emitter, "sendNotifications", params, signatures); + + // waiting + long toWait = 6000; + long stopTime = System.currentTimeMillis() + toWait; + + synchronized(listener) { + while(listener.received < sendNB && toWait > 0) { + listener.wait(toWait); + toWait = stopTime - System.currentTimeMillis(); + } + } + + // clean + efClient.removeNotificationListener(emitter, listener, null, null); + efClient.close(); + + if (listener.received != sendNB) { + System.out.println(">>> ListenerTest-test: FAILED! Expected to receive "+sendNB+", but got "+listener.received); + + return false; + } else if (listener.seqErr > 0) { + System.out.println(">>> ListenerTest-test: FAILED! The receiving sequence is not correct."); + + return false; + } else { + System.out.println(">>> ListenerTest-test: got all expected "+listener.received); + return true; + } + } + + private static class Listener implements NotificationListener { + public int received = 0; + public int seqErr = 0; + + private long lastSeq = -1; + + public void handleNotification(Notification notif, Object handback) { + if (!myType.equals(notif.getType())) { + System.out.println(">>> EventManagerTest-Listener: got unexpected notif: "+notif); + System.exit(1); + } + + if (lastSeq == -1) { + lastSeq = notif.getSequenceNumber(); + } else if (notif.getSequenceNumber() - lastSeq++ != 1) { + seqErr++; + } + + System.out.println(">>> ListenerTest-Listener: got notif "+notif.getSequenceNumber()); + + synchronized(this) { + if (++received >= sendNB) { + this.notify(); + } + } + + System.out.println(">>> ListenerTest-Listener: received = "+received); + } + } + + public static class NotificationEmitter extends NotificationBroadcasterSupport + implements NotificationEmitterMBean { + + public MBeanNotificationInfo[] getNotificationInfo() { + final String[] ntfTypes = {myType}; + + final MBeanNotificationInfo[] ntfInfoArray = { + new MBeanNotificationInfo(ntfTypes, + "javax.management.Notification", + "Notifications sent by the NotificationEmitter")}; + + return ntfInfoArray; + } + + /** + * Send Notification objects. + * + * @param nb The number of notifications to send + */ + public void sendNotifications(Integer nb) { + Notification notif; + for (int i=1; i<=nb.intValue(); i++) { + notif = new Notification(myType, this, count++); + //System.out.println(">>> ListenerTest-NotificationEmitter-sendNotifications: "+i); + + sendNotification(notif); + } + } + + + } + + public interface NotificationEmitterMBean { + public void sendNotifications(Integer nb); + } + + private static int sendNB = 20; + private static int count = 0; + + private static final String myType = "notification.my_notification"; +} diff --git a/test/javax/management/eventService/MyFetchingEventForwarder.java b/test/javax/management/eventService/MyFetchingEventForwarder.java new file mode 100644 index 000000000..25d308af6 --- /dev/null +++ b/test/javax/management/eventService/MyFetchingEventForwarder.java @@ -0,0 +1,53 @@ +/* + * MyList.java + * + * Created on Oct 23, 2007, 2:45:57 PM + * + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +/** + * + * @author sjiang + */ + +import java.io.IOException; +import java.util.ArrayList; +import javax.management.event.FetchingEventForwarder; + +public class MyFetchingEventForwarder extends FetchingEventForwarder { + + public MyFetchingEventForwarder() { + super(1000); + shared = this; + setList(myList); + } + + public void setAgain() { + setList(myList); + } + + public void setClientId(String clientId) throws IOException { + used = true; + super.setClientId(clientId); + } + + public boolean isUsed() { + return used; + } + + private class MyList + extends ArrayList { + + public boolean add(TargetedNotification e) { + used = true; + + return super.add(e); + } + } + + public MyList myList = new MyList(); + public static MyFetchingEventForwarder shared; + private boolean used = false; +} diff --git a/test/javax/management/eventService/NotSerializableNotifTest.java b/test/javax/management/eventService/NotSerializableNotifTest.java new file mode 100644 index 000000000..aaadc4bde --- /dev/null +++ b/test/javax/management/eventService/NotSerializableNotifTest.java @@ -0,0 +1,227 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + + +/* + * @test NotSerializableNotifTest.java 1.5 08/01/22 + * @bug 5108776 + * @summary Basic test for EventClient. + * @author Shanliang JIANG + * @run clean NotSerializableNotifTest + * @run build NotSerializableNotifTest + * @run main NotSerializableNotifTest + */ + + +// JMX imports +// +import javax.management.* ; +import javax.management.event.EventClient; +import javax.management.event.EventClientDelegate; +import javax.management.event.EventClientDelegateMBean; +import javax.management.event.EventRelay; +import javax.management.event.FetchingEventRelay; + +import javax.management.remote.*; +import javax.management.remote.JMXServiceURL; + +public class NotSerializableNotifTest { + private static MBeanServer mbeanServer = + MBeanServerFactory.createMBeanServer(); + private static ObjectName emitter; + private static int port = 2468; + + private static String[] protocols; + + private static final int sentNotifs = 50; + + public static void main(String[] args) throws Exception { + System.out.println(">>> Test to send a not serializable notification"); + + // for 1.5 + if (System.getProperty("java.version").startsWith("1.5") && + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) { + System.out.print("Working on "+System.getProperty("java.version")+ + " register "+EventClientDelegateMBean.OBJECT_NAME); + + mbeanServer.registerMBean(EventClientDelegate. + getEventClientDelegate(mbeanServer), + EventClientDelegateMBean.OBJECT_NAME); + } + + NotificationEmitter nm = new NotificationEmitter(); + emitter = new ObjectName("Default:name=NotificationEmitter"); + mbeanServer.registerMBean(nm, emitter); + String proto = "rmi"; + + System.out.println(">>> Test for protocol " + proto); + + JMXServiceURL url = new JMXServiceURL(proto, null, 0); + + JMXConnectorServer server = + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer); + + server.start(); + + url = server.getAddress(); + JMXConnector conn = JMXConnectorFactory.connect(url, null); + MBeanServerConnection client = conn.getMBeanServerConnection(); + + EventClientDelegateMBean ecd = EventClientDelegate.getProxy(client); + EventRelay eventRelay = new FetchingEventRelay( + ecd, + FetchingEventRelay.DEFAULT_BUFFER_SIZE, + 10, + FetchingEventRelay.DEFAULT_MAX_NOTIFICATIONS, + null); + EventClient ec = new EventClient(ecd, eventRelay, null, null, + EventClient.DEFAULT_LEASE_TIMEOUT); + + // add listener from the client side + Listener listener = new Listener(); + ec.addNotificationListener(emitter, listener, null, null); + + LostListener lostListener = new LostListener(); + ec.addEventClientListener(lostListener, null, null); + + // ask to send one not serializable notif + System.out.println(">>> sending not serializable notifs ..."); + + Object[] params = new Object[] {new Integer(sentNotifs)}; + String[] signatures = new String[] {"java.lang.Integer"}; + client.invoke(emitter, "sendNotserializableNotifs", params, signatures); + +// nm.sendNotserializableNotifs(sentNotifs); +// nm.sendNotifications(1); + + // waiting + synchronized(lostListener) { + if (lostListener.lostCount != sentNotifs) { + lostListener.wait(6000); + } + } + + Thread.sleep(100); + + if (lostListener.lostCount != sentNotifs) { + System.out.println(">>> FAILED. Expected "+sentNotifs+", but got "+lostListener.lostCount); + System.exit(1); + } + + System.out.println(">>> Passed."); + + ec.close(); + conn.close(); + server.stop(); + } + + +//-------------------------- +// private classes +//-------------------------- + private static class Listener implements NotificationListener { + public void handleNotification(Notification n, Object handback) { + System.out.println(">>> Listener: receive: "+n); + } + } + + + private static class LostListener implements NotificationListener { + public void handleNotification(Notification n, Object handback) { + if (!EventClient.NOTIFS_LOST.equals(n.getType())) { + return; + } + + if (!(n.getUserData() instanceof Long)) { + System.out.println(">>> Listener: JMXConnectionNotification userData " + + "not a Long: " + n.getUserData()); + System.exit(1); + } else { + int lost = ((Long) n.getUserData()).intValue(); + lostCount += lost; + if (lostCount >= sentNotifs) { + synchronized(this) { + this.notifyAll(); + } + } + } + + } + + + private int lostCount = 0; + } + + public static class NotificationEmitter extends NotificationBroadcasterSupport + implements NotificationEmitterMBean { + + public MBeanNotificationInfo[] getNotificationInfo() { + final String[] ntfTypes = {myType}; + + final MBeanNotificationInfo[] ntfInfoArray = { + new MBeanNotificationInfo(ntfTypes, + "javax.management.Notification", + "Notifications sent by the NotificationEmitter")}; + + return ntfInfoArray; + } + + /** + * Send not serializable Notifications. + * + * @param nb The number of notifications to send + */ + public void sendNotserializableNotifs(Integer nb) { + + Notification notif; + for (int i=1; i<=nb.intValue(); i++) { + notif = new Notification(myType, this, i); + + notif.setUserData(new Object()); + sendNotification(notif); + } + } + + /** + * Send Notification objects. + * + * @param nb The number of notifications to send + */ + public void sendNotifications(Integer nb) { + Notification notif; + for (int i=1; i<=nb.intValue(); i++) { + notif = new Notification(myType, this, i); + + sendNotification(notif); + } + } + + private final String myType = "notification.my_notification"; + } + + public interface NotificationEmitterMBean { + public void sendNotifications(Integer nb); + + public void sendNotserializableNotifs(Integer nb); + } +} diff --git a/test/javax/management/eventService/PublishTest.java b/test/javax/management/eventService/PublishTest.java new file mode 100644 index 000000000..f53056bf9 --- /dev/null +++ b/test/javax/management/eventService/PublishTest.java @@ -0,0 +1,184 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.*; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +/** + * + */ +public class PublishTest { + private static MBeanServer mbeanServer; + private static EventManager eventManager; + private static ObjectName emitter; + private static JMXServiceURL url; + private static JMXConnectorServer server; + private static JMXConnector conn; + private static MBeanServerConnection client; + + /** + * @param args the command line arguments + */ + public static void main(String[] args) throws Exception { + System.out.println(">>> PublishTest-main basic tests ..."); + mbeanServer = MBeanServerFactory.createMBeanServer(); + + // for 1.5 + if (System.getProperty("java.version").startsWith("1.5") && + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) { + System.out.print("Working on "+System.getProperty("java.version")+ + " register "+EventClientDelegateMBean.OBJECT_NAME); + + mbeanServer.registerMBean(EventClientDelegate. + getEventClientDelegate(mbeanServer), + EventClientDelegateMBean.OBJECT_NAME); + } + + eventManager = EventManager.getEventManager(mbeanServer); + + emitter = new ObjectName("Default:name=NotificationEmitter"); + + url = new JMXServiceURL("rmi", null, 0) ; + server = + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer); + server.start(); + + url = server.getAddress(); + conn = JMXConnectorFactory.connect(url, null); + client = conn.getMBeanServerConnection(); + + boolean succeed; + + System.out.println(">>> PublishTest-main: using the fetching EventRelay..."); + succeed = test(new EventClient(client)); + + System.out.println(">>> PublishTest-main: using the pushing EventRelay..."); + succeed &= test(new EventClient(client, + new RMIPushEventRelay(EventClientDelegate.getProxy(client)), + null, + EventClient.DEFAULT_LEASE_TIMEOUT)); + + conn.close(); + server.stop(); + + if (succeed) { + System.out.println(">>> PublishTest-main: PASSE!"); + } else { + System.out.println("\n>>> PublishTest-main: FAILED!"); + System.exit(1); + } + } + + public static boolean test(EventClient efClient) throws Exception { + // add listener from the client side + Listener listener = new Listener(); + efClient.subscribe(emitter, listener, null, null); + + ObjectName other = new ObjectName("Default:name=other"); + // publish notifs + for (int i=0; i>> EventManagerService-NotificationEmitter-sendNotifications: "+i); + + eventManager.publish(emitter, notif); + eventManager.publish(other, notif2); // should not received + } + + // waiting + long toWait = 6000; + long stopTime = System.currentTimeMillis() + toWait; + + synchronized(listener) { + while(listener.received < sendNB && toWait > 0) { + listener.wait(toWait); + toWait = stopTime - System.currentTimeMillis(); + } + } + + // clean + efClient.unsubscribe(emitter, listener); + efClient.close(); + + if (listener.received != sendNB) { + System.out.println(">>> PublishTest-test: FAILED! Expected to receive "+sendNB+", but got "+listener.received); + + return false; + } else if (listener.seqErr > 0) { + System.out.println(">>> PublishTest-test: FAILED! The receiving sequence is not correct."); + + return false; + } else { + System.out.println(">>> PublishTest-test: got all expected "+listener.received); + return true; + } + } + + private static class Listener implements NotificationListener { + public int received = 0; + public int seqErr = 0; + + private long lastSeq = -1; + + public void handleNotification(Notification notif, Object handback) { + if (!myType.equals(notif.getType())) { + System.out.println(">>> PublishTest-Listener: got unexpected notif: "+notif); + System.exit(1); + } else if (!emitter.equals(notif.getSource())) { + System.out.println(">>> PublishTest-Listener: unknown ObjectName: "+notif.getSource()); + System.exit(1); + } + + if (lastSeq == -1) { + lastSeq = notif.getSequenceNumber(); + } else if (notif.getSequenceNumber() - lastSeq++ != 1) { + seqErr++; + } + + System.out.println(">>> PublishTest-Listener: got notif "+notif.getSequenceNumber()); + + synchronized(this) { + if (++received >= sendNB) { + this.notify(); + } + } + + System.out.println(">>> PublishTest-Listener: received = "+received); + } + } + + private static int sendNB = 20; + private static long count = 0; + + private static final String myType = "notification.my_notification"; +} diff --git a/test/javax/management/eventService/ReconnectableConnectorTest.java b/test/javax/management/eventService/ReconnectableConnectorTest.java new file mode 100644 index 000000000..8c7954472 --- /dev/null +++ b/test/javax/management/eventService/ReconnectableConnectorTest.java @@ -0,0 +1,488 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test ReconnectableJMXConnector + * @bug 5108776 + * @summary Check that the Event Service can be used to build a + * ReconnectableJMXConnector. + * @author Eamonn McManus + */ + +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Date; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventClient; +import javax.management.remote.JMXConnectionNotification; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; +import javax.security.auth.Subject; + +/* + * This test checks that it is possible to use the Event Service to create + * a "reconnectable connector". + * + * In the JMX Remote API, we deliberately specified that a connector client + * (JMXConnector) that encounters a network failure is then permanently broken. + * The idea being that adding recovery logic to the basic connector client + * would make it much more complicated and less reliable, and the logic would + * in any case never correspond to what a given situation needs. Some of + * the tough questions are: Should the connector try to mask the failure by + * blocking operations until the failure is resolved? How long should the + * connector try to reestablish the connection before giving up? Rather than + * try to solve this problem in the connector, we suggested that people who + * wanted to recover from network failures could implement the JMXConnector + * interface themselves so that it forwards to a wrapped JMXConnector that can + * be replaced in case of network failure. + * + * This works fine except that the connector client has state, + * in the form of listeners added by the user through the + * MBeanServerConnection.addNotificationListener method. It's possible + * for the wrapper to keep track of these listeners as well as forwarding + * them to the wrapped JMXConnector, so that it can reapply them to + * a replacement JMXConnector after failure recover. But it's quite + * tricky, particularly because of the two- and four-argument versions of + * removeNotificationListener. + * + * The Event Service can take care of this for you through the EventClient + * class. Listeners added through that class are implemented in a way that + * doesn't require the connector client to maintain any state, so they should + * continue to work transparently after replacing the wrapped JMXConnector. + * This test is a proof of concept that shows it works. Quite a number of + * details would need to be changed to build a reliable reconnectable + * connector. + * + * The test simulates network failure by rewrapping the wrapped JMXConnector's + * MBeanServerConnection (MBSC) in a "breakable" MBSC which we can cause + * to stop working. We do this in two phases. The first phase suspends + * any MBSC calls just at the point where they would return to the caller. + * The goal here is to block an EventClientDelegateMBean.fetchNotifications + * operation when it has received notifications but not yet delivered them + * to the EventClient. This is the most delicate point where a breakage + * can occur, because the EventClientDelegate must not drop those notifs + * from its buffer until another fetchNotifs call arrives with a later + * sequence number (which is an implicit ack of the previous set of + * notifs). Once the fetchNotifs call is suspended, we "kill" the MBSC, + * causing it to throw IOException from this and any other calls. That + * triggers the reconnect logic, which will make a new MBSC and issue + * the same fetchNotifs call to it. + * + * The test could be improved by synchronizing explicitly between the + * breakable MBSC and the mainline, so we only proceed to kill the MBSC + * when we are sure that the fetchNotifs call is blocked. As it is, + * we have a small delay which both ensures that no notifs are delivered + * while the connection is suspended, and if the machine is fast enough + * allows the fetchNotifs call to reach the blocking point. + */ +public class ReconnectableConnectorTest { + private static class ReconnectableJMXConnector implements JMXConnector { + private final JMXServiceURL url; + private AtomicReference wrappedJMXC = + new AtomicReference(); + private AtomicReference wrappedMBSC = + new AtomicReference(); + private final NotificationBroadcasterSupport broadcaster = + new NotificationBroadcasterSupport(); + private final Lock connectLock = new ReentrantLock(); + + ReconnectableJMXConnector(JMXServiceURL url) { + this.url = url; + } + + private class ReconnectIH implements InvocationHandler { + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + try { + return method.invoke(wrappedMBSC.get(), args); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof IOException) { + connect(); + try { + return method.invoke(wrappedMBSC.get(),args); + } catch (InvocationTargetException ee) { + throw ee.getCause(); + } + } + throw e.getCause(); + } + } + } + + private class FailureListener implements NotificationListener { + public void handleNotification(Notification n, Object h) { + String type = n.getType(); + if (type.equals(JMXConnectionNotification.FAILED)) { + try { + connect(); + } catch (IOException e) { + broadcaster.sendNotification(n); + } + } else if (type.equals(JMXConnectionNotification.NOTIFS_LOST)) + broadcaster.sendNotification(n); + } + } + + public void connect() throws IOException { + connectLock.lock(); + try { + connectWithLock(); + } finally { + connectLock.unlock(); + } + } + + private void connectWithLock() throws IOException { + MBeanServerConnection mbsc = wrappedMBSC.get(); + if (mbsc != null) { + try { + mbsc.getDefaultDomain(); + return; // the connection works + } catch (IOException e) { + // OK: the connection doesn't work, so make a new one + } + } + // This is where we would need to add the fancy logic that + // allows the connection to keep failing for a while + // before giving up. + JMXConnector jmxc = JMXConnectorFactory.connect(url); + jmxc.addConnectionNotificationListener( + new FailureListener(), null, null); + wrappedJMXC.set(jmxc); + if (false) + wrappedMBSC.set(jmxc.getMBeanServerConnection()); + else { + mbsc = jmxc.getMBeanServerConnection(); + InvocationHandler ih = new BreakableIH(mbsc); + mbsc = (MBeanServerConnection) Proxy.newProxyInstance( + MBeanServerConnection.class.getClassLoader(), + new Class[] {MBeanServerConnection.class}, + ih); + wrappedMBSC.set(mbsc); + } + } + + private BreakableIH breakableIH() { + MBeanServerConnection mbsc = wrappedMBSC.get(); + return (BreakableIH) Proxy.getInvocationHandler(mbsc); + } + + void suspend() { + BreakableIH ih = breakableIH(); + ih.suspend(); + } + + void kill() throws IOException { + BreakableIH ih = breakableIH(); + wrappedJMXC.get().close(); + ih.kill(); + } + + public void connect(Map env) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + + private final AtomicReference mbscRef = + new AtomicReference(); + + public MBeanServerConnection getMBeanServerConnection() + throws IOException { + connect(); + // Synchro here is not strictly correct: two threads could make + // an MBSC at the same time. OK for a test but beware for real + // code. + MBeanServerConnection mbsc = mbscRef.get(); + if (mbsc != null) + return mbsc; + mbsc = (MBeanServerConnection) Proxy.newProxyInstance( + MBeanServerConnection.class.getClassLoader(), + new Class[] {MBeanServerConnection.class}, + new ReconnectIH()); + mbsc = EventClient.getEventClientConnection(mbsc); + mbscRef.set(mbsc); + return mbsc; + } + + public MBeanServerConnection getMBeanServerConnection( + Subject delegationSubject) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void close() throws IOException { + wrappedJMXC.get().close(); + } + + public void addConnectionNotificationListener( + NotificationListener l, NotificationFilter f, Object h) { + broadcaster.addNotificationListener(l, f, h); + } + + public void removeConnectionNotificationListener(NotificationListener l) + throws ListenerNotFoundException { + broadcaster.removeNotificationListener(l); + } + + public void removeConnectionNotificationListener( + NotificationListener l, NotificationFilter f, Object h) + throws ListenerNotFoundException { + broadcaster.removeNotificationListener(l, f, h); + } + + public String getConnectionId() throws IOException { + return wrappedJMXC.get().getConnectionId(); + } + } + + // InvocationHandler that allows us to perform a two-phase "break" of + // an object. The first phase suspends the object, so that calls to + // it are blocked just before they return. The second phase unblocks + // suspended threads and causes them to throw IOException. + private static class BreakableIH implements InvocationHandler { + private final Object wrapped; + private final Holder state = new Holder("running"); + + BreakableIH(Object wrapped) { + this.wrapped = wrapped; + } + + void suspend() { + state.set("suspended"); + } + + void kill() { + state.set("killed"); + } + + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + Object result; + try { + result = method.invoke(wrapped, args); + } catch (InvocationTargetException e) { + throw e.getCause(); + } + String s = state.get(); + if (s.equals("suspended")) + state.waitUntilEqual("killed", 3, TimeUnit.SECONDS); + else if (s.equals("killed")) + throw new IOException("Broken"); + return result; + } + } + + private static class Holder { + private T held; + private Lock lock = new ReentrantLock(); + private Condition changed = lock.newCondition(); + + Holder(T value) { + lock.lock(); + this.held = value; + lock.unlock(); + } + + void waitUntilEqual(T value, long timeout, TimeUnit units) + throws InterruptedException { + long millis = units.toMillis(timeout); + long stop = System.currentTimeMillis() + millis; + Date stopDate = new Date(stop); + lock.lock(); + try { + while (!value.equals(held)) { + boolean ok = changed.awaitUntil(stopDate); + if (!ok) + throw new InterruptedException("Timed out"); + } + } finally { + lock.unlock(); + } + } + + void set(T value) { + lock.lock(); + try { + held = value; + changed.signalAll(); + } finally { + lock.unlock(); + } + } + + T get() { + lock.lock(); + try { + return held; + } finally { + lock.unlock(); + } + } + } + + private static class StoreListener implements NotificationListener { + final BlockingQueue queue = + new ArrayBlockingQueue(100); + + public void handleNotification(Notification n, Object h) { + queue.add(n); + } + + Notification nextNotification(long time, TimeUnit units) + throws InterruptedException { + Notification n = queue.poll(time, units); + if (n == null) + throw new NoSuchElementException("Notification wait timed out"); + return n; + } + + int notifCount() { + return queue.size(); + } + } + + public static interface SenderMBean {} + public static class Sender + extends NotificationBroadcasterSupport implements SenderMBean { + private AtomicLong seqNo = new AtomicLong(0); + + void send() { + Notification n = + new Notification("type", this, seqNo.getAndIncrement()); + sendNotification(n); + } + } + + public static void main(String[] args) throws Exception { + MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + Sender sender = new Sender(); + ObjectName name = new ObjectName("a:b=c"); + mbs.registerMBean(sender, name); + + System.out.println("Creating connector server"); + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///"); + JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer( + url, null, mbs); + cs.start(); + + StoreListener csListener = new StoreListener(); + cs.addNotificationListener(csListener, null, null); + + System.out.println("Creating reconnectable client"); + JMXServiceURL addr = cs.getAddress(); + ReconnectableJMXConnector cc = new ReconnectableJMXConnector(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + + System.out.println("Checking server has sent new-client notif"); + Notification csn = csListener.nextNotification(1, TimeUnit.SECONDS); + assertEquals("CS notif type", + JMXConnectionNotification.OPENED, csn.getType()); + + StoreListener listener = new StoreListener(); + mbsc.addNotificationListener(name, listener, null, null); + + System.out.println("Sending 10 notifs and checking they are received"); + for (int i = 0; i < 10; i++) + sender.send(); + checkNotifs(listener, 0, 10); + + System.out.println("Suspending the fetchNotifs operation"); + cc.suspend(); + System.out.println("Sending a notif while fetchNotifs is suspended"); + sender.send(); + System.out.println("Brief wait before checking no notif is received"); + Thread.sleep(2); + // dumpThreads(); + assertEquals("notif queue while connector suspended", + 0, listener.notifCount()); + assertEquals("connector server notif queue while connector suspended", + 0, csListener.notifCount()); + + System.out.println("Breaking the connection so fetchNotifs will fail over"); + cc.kill(); + + System.out.println("Checking that client has reconnected"); + csn = csListener.nextNotification(1, TimeUnit.SECONDS); + assertEquals("First CS notif type after kill", + JMXConnectionNotification.CLOSED, csn.getType()); + csn = csListener.nextNotification(1, TimeUnit.SECONDS); + assertEquals("Second CS notif type after kill", + JMXConnectionNotification.OPENED, csn.getType()); + + System.out.println("Checking that suspended notif has been received"); + checkNotifs(listener, 10, 11); + } + + private static void checkNotifs( + StoreListener sl, long start, long stop) + throws Exception { + for (long i = start; i < stop; i++) { + Notification n = sl.nextNotification(1, TimeUnit.SECONDS); + assertEquals("received sequence number", i, n.getSequenceNumber()); + } + } + + private static void assertEquals(String what, Object expect, Object actual) + throws Exception { + if (!expect.equals(actual)) { + fail(what + " should be " + expect + " but is " + actual); + } + } + + private static void fail(String why) throws Exception { + throw new Exception("TEST FAILED: " + why); + } + + private static void dumpThreads() { + System.out.println("Thread stack dump"); + Map traces = Thread.getAllStackTraces(); + for (Map.Entry entry : traces.entrySet()) { + Thread t = entry.getKey(); + System.out.println("===Thread " + t.getName() + "==="); + for (StackTraceElement ste : entry.getValue()) + System.out.println(" " + ste); + } + } +} diff --git a/test/javax/management/eventService/SharingThreadTest.java b/test/javax/management/eventService/SharingThreadTest.java new file mode 100644 index 000000000..e3bf01b30 --- /dev/null +++ b/test/javax/management/eventService/SharingThreadTest.java @@ -0,0 +1,364 @@ +/*/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test SharingThreadTest.java 1.3 08/01/22 + * @bug 5108776 + * @summary Basic test for EventClient to see internal thread management. + * @author Shanliang JIANG + * @run clean SharingThreadTest + * @run build SharingThreadTest + * @run main SharingThreadTest + */ + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventClient; +import javax.management.event.EventClientDelegate; +import javax.management.event.EventClientDelegateMBean; +import javax.management.event.FetchingEventRelay; +import javax.management.event.RMIPushEventRelay; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + + +public class SharingThreadTest { + + private static MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer(); + private static List notifList = new ArrayList(); + private static ObjectName emitter; + private static NotificationEmitter emitterImpl; + private static JMXServiceURL url; + private static JMXConnectorServer server; + private static JMXConnector conn; + + + private static int toSend = 10; + private static long sequenceNumber = 0; + private static final long bigWaiting = 6000; + private static int counter = 0; + private static int jobs = 10; + private static int endedJobs = 0; + + private static Executor sharedExecutor = new ThreadPoolExecutor(0, 1, 1000, + TimeUnit.MILLISECONDS, new ArrayBlockingQueue(jobs)); + //Executors.newFixedThreadPool(1); + + public static void main(String[] args) throws Exception { + System.out.println(">>> Test on sharing threads for multiple EventClient."); + + // for 1.5 + if (System.getProperty("java.version").startsWith("1.5") && + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) { + System.out.print("Working on "+System.getProperty("java.version")+ + " register "+EventClientDelegateMBean.OBJECT_NAME); + + mbeanServer.registerMBean(EventClientDelegate. + getEventClientDelegate(mbeanServer), + EventClientDelegateMBean.OBJECT_NAME); + + sharedExecutor = new ThreadPoolExecutor(1, 1, 1000, + TimeUnit.MILLISECONDS, new ArrayBlockingQueue(jobs)); + } + + emitter = new ObjectName("Default:name=NotificationEmitter"); + emitterImpl = new NotificationEmitter(); + mbeanServer.registerMBean(emitterImpl, emitter); + + String[] types = new String[]{"PushEventRelay", "FetchingEventRelay"}; + String[] protos = new String[]{"rmi", "iiop", "jmxmp"}; + for (String prot : protos) { + url = new JMXServiceURL(prot, null, 0); + + try { + server = + JMXConnectorServerFactory.newJMXConnectorServer(url, + null, mbeanServer); + server.start(); + } catch (Exception e) { + System.out.println(">>> Skip "+prot+", not support."); + continue; + } + + url = server.getAddress(); + + // noise + Thread noise = new Thread(new Runnable() { + public void run() { + while (true) { + emitterImpl.sendNotif(1, null); + try { + Thread.sleep(10); + } catch (Exception e) { + // OK + } + } + } + }); + noise.setDaemon(true); + noise.start(); + + Thread[] threads = new Thread[jobs]; + try { + for (String type: types) { + System.out.println("\n\n>>> Testing "+type+" on "+url+" ..."); + newConn(); + for (int i=0; i 0) { + SharingThreadTest.class.wait(toWait); + toWait = stopTime - System.currentTimeMillis(); + } + } + + if (endedJobs != jobs) { + throw new RuntimeException("Need to set bigger waiting timeout?"); + } + + endedJobs = 0; + conn.close(); + System.out.println(">>> Testing "+type+" on "+url+" ... done"); + } + } finally { + server.stop(); + } + } + } + + public static class Job implements Runnable { + public Job(String type) { + this.type = type; + } + public void run() { + try { + test(type); + + synchronized(SharingThreadTest.class) { + endedJobs++; + if (endedJobs>=jobs) { + SharingThreadTest.class.notify(); + } + } + } catch (RuntimeException re) { + throw re; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private final String type; + } + + private static void test(String type) throws Exception { + String id = getId(); + + Listener listener = new Listener(id); + Filter filter = new Filter(id); + + //newConn(); + EventClient ec = newEventClient(type); + + System.out.println(">>> ("+id+") To receive notifications "+toSend); + ec.addNotificationListener(emitter, + listener, filter, null); + + emitterImpl.sendNotif(toSend, id); + listener.waitNotifs(bigWaiting, toSend); + if (listener.received != toSend) { + throw new RuntimeException(">>> ("+id+") Expected to receive: " + +toSend+", but got: "+listener.received); + } + + // not close the EventClient to keep using thread + //ec.close(); + } + +//-------------------------- +// private classes +//-------------------------- + + private static class Listener implements NotificationListener { + public Listener(String id) { + this.id = id; + } + public void handleNotification(Notification notif, Object handback) { + if (!id.equals(notif.getUserData())) { + System.out.println("("+id+") Filter error, my id is: "+id+ + ", but got "+notif.getUserData()); + System.exit(1); + } + System.out.println("("+id+") received "+notif.getSequenceNumber()); + synchronized (notifList) { + received++; + + if (sequenceNB < 0) { + sequenceNB = notif.getSequenceNumber(); + } else if(++sequenceNB != notif.getSequenceNumber()) { + throw new RuntimeException("Wrong sequence number, expecte: " + +sequenceNB+", but got: "+notif.getSequenceNumber()); + } + if (received >= toSend) { + this.notify(); + } + } + } + + public void waitNotifs(long timeout, int nb) throws Exception { + long toWait = timeout; + long stopTime = System.currentTimeMillis() + timeout; + synchronized(this) { + while (received < nb && toWait > 0) { + this.wait(toWait); + toWait = stopTime - System.currentTimeMillis(); + } + } + } + + public void clear() { + synchronized(this) { + received = 0; + sequenceNB = -1; + } + } + + private String id; + private int received = 0; + + private long sequenceNB = -1; + } + + private static class Filter implements NotificationFilter { + public Filter(String id) { + this.id = id; + } + + public boolean isNotificationEnabled(Notification n) { + return id.equals(n.getUserData()); + } + private String id; + } + + private static NotificationListener dummyListener = new NotificationListener() { + public void handleNotification(Notification notif, Object handback) { + } + }; + + public static class NotificationEmitter extends NotificationBroadcasterSupport + implements NotificationEmitterMBean { + + /** + * Send Notification objects. + * + * @param nb The number of notifications to send + */ + public void sendNotif(int nb, String userData) { + new Thread(new SendJob(nb, userData)).start(); + } + + private class SendJob implements Runnable { + public SendJob(int nb, String userData) { + this.nb = nb; + this.userData = userData; + } + + public void run() { + if (userData != null) { + System.out.println(">>> ("+userData+") sending "+nb); + } + for (int i = 0; i>> ("+userData+") sending done"); + } + } + private int nb; + private String userData; + } + private final String myType = "notification.my_notification"; + } + + public interface NotificationEmitterMBean { + public void sendNotif(int nb, String userData); + } + + private static void newConn() throws IOException { + conn = JMXConnectorFactory.connect(url); + } + + private static EventClient newEventClient(String type) throws Exception { + EventClientDelegateMBean proxy = + EventClientDelegate.getProxy(conn.getMBeanServerConnection()); + if (type.equals("PushEventRelay")) { + return new EventClient(proxy, + new RMIPushEventRelay(proxy), sharedExecutor, null, 600); + } else if (type.equals("FetchingEventRelay")) { + return new EventClient(proxy, + new FetchingEventRelay(proxy, + FetchingEventRelay.DEFAULT_BUFFER_SIZE, + 10, + FetchingEventRelay.DEFAULT_MAX_NOTIFICATIONS, + sharedExecutor), + null, null, 600); + } else { + throw new RuntimeException("Wrong event client type: "+type); + } + } + + private static String getId() { + synchronized(SharingThreadTest.class) { + return String.valueOf(counter++); + } + } +} diff --git a/test/javax/management/eventService/SubscribeTest.java b/test/javax/management/eventService/SubscribeTest.java new file mode 100644 index 000000000..fa2df8dad --- /dev/null +++ b/test/javax/management/eventService/SubscribeTest.java @@ -0,0 +1,127 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 5108776 + * @summary Test that EventSubscriber.subscribe works + * @author Eamonn McManus + */ + +import java.lang.management.ManagementFactory; +import javax.management.MBeanServer; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventSubscriber; + +public class SubscribeTest { + private static class CountListener implements NotificationListener { + volatile int count; + + public void handleNotification(Notification n, Object h) { + count++; + } + } + + private static class SwitchFilter implements NotificationFilter { + volatile boolean enabled; + + public boolean isNotificationEnabled(Notification n) { + return enabled; + } + } + + public static interface SenderMBean {} + + public static class Sender extends NotificationBroadcasterSupport + implements SenderMBean { + void send() { + Notification n = new Notification("type", this, 1L); + sendNotification(n); + } + } + + public static void main(String[] args) throws Exception { + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + ObjectName name1 = new ObjectName("d:type=Sender,id=1"); + ObjectName name2 = new ObjectName("d:type=Sender,id=2"); + ObjectName pattern = new ObjectName("d:type=Sender,*"); + Sender sender1 = new Sender(); + Sender sender2 = new Sender(); + mbs.registerMBean(sender1, name1); + mbs.registerMBean(sender2, name2); + + EventSubscriber sub = EventSubscriber.getEventSubscriber(mbs); + + System.out.println("Single subscribe covering both MBeans"); + CountListener listen1 = new CountListener(); + sub.subscribe(pattern, listen1, null, null); + sender1.send(); + assertEquals("Notifs after sender1 send", 1, listen1.count); + sender2.send(); + assertEquals("Notifs after sender2 send", 2, listen1.count); + + System.out.println("Unsubscribe"); + sub.unsubscribe(pattern, listen1); + sender1.send(); + assertEquals("Notifs after sender1 send", 2, listen1.count); + + System.out.println("Subscribe twice to same MBean with same listener " + + "but different filters"); + SwitchFilter filter1 = new SwitchFilter(); + sub.subscribe(name1, listen1, null, null); + sub.subscribe(name1, listen1, filter1, null); + listen1.count = 0; + sender1.send(); + // switch is off, so only one notif expected + assertEquals("Notifs after sender1 send", 1, listen1.count); + filter1.enabled = true; + sender1.send(); + // switch is on, so two more notifs expected + assertEquals("Notifs after sender1 send", 3, listen1.count); + + System.out.println("Remove those subscriptions"); + sub.unsubscribe(name1, listen1); + sender1.send(); + assertEquals("Notifs after sender1 send", 3, listen1.count); + } + + private static void assertEquals(String what, Object expected, Object actual) + throws Exception { + if (!equal(expected, actual)) { + String msg = "Expected " + expected + "; got " + actual; + throw new Exception("TEST FAILED: " + what + ": " + msg); + } + } + + private static boolean equal(Object x, Object y) { + if (x == y) + return true; + if (x == null) + return false; + return (x.equals(y)); + } +} diff --git a/test/javax/management/eventService/UsingEventService.java b/test/javax/management/eventService/UsingEventService.java new file mode 100644 index 000000000..3d768ed8d --- /dev/null +++ b/test/javax/management/eventService/UsingEventService.java @@ -0,0 +1,84 @@ +/* + * @test UsingEventService.java 1.10 08/01/22 + * @bug 5108776 + * @summary Basic test for EventManager. + * @author Shanliang JIANG + * @run clean UsingEventService + * @run build UsingEventService + * @run main UsingEventService + */ + +import java.util.HashMap; +import java.util.Map; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventConsumer; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +public class UsingEventService { + private static JMXServiceURL url; + private static JMXConnectorServer server; + private static JMXConnector conn; + private static MBeanServerConnection client; + + public static void main(String[] args) throws Exception { + if (System.getProperty("java.version").startsWith("1.5")) { + System.out.println(">>> UsingEventService-main not available for JDK1.5, bye"); + return; + } + + ObjectName oname = new ObjectName("test:t=t"); + Notification n = new Notification("", oname, 0); + + System.out.println(">>> UsingEventService-main basic tests ..."); + MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer(); + + url = new JMXServiceURL("rmi", null, 0) ; + JMXConnectorServer server = + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer); + server.start(); + url = server.getAddress(); + + System.out.println(">>> UsingEventService-main test to not use the event service..."); + conn = JMXConnectorFactory.connect(url, null); + client = conn.getMBeanServerConnection(); + + System.out.println(">>> UsingEventService-main test to use the event service..."); + Map env = new HashMap(1); + env.put("jmx.remote.use.event.service", "true"); + conn = JMXConnectorFactory.connect(url, env); + client = conn.getMBeanServerConnection(); + + ((EventConsumer)client).subscribe(oname, listener, null, null); + + System.out.println(">>> UsingEventService-main using event service as expected!"); + + System.out.println(">>> UsingEventService-main test to use" + + " the event service with system property..."); + + System.setProperty("jmx.remote.use.event.service", "true"); + conn = JMXConnectorFactory.connect(url, null); + client = conn.getMBeanServerConnection(); + + ((EventConsumer)client).subscribe(oname, listener, null, null); + + System.out.println("" + + ">>> UsingEventService-main using event service as expected!"); + + System.out.println(">>> Happy bye bye!"); + } + + private final static NotificationListener listener = new NotificationListener() { + public void handleNotification(Notification n, Object hk) { + // + } + }; +} diff --git a/test/javax/management/mxbean/GenericArrayTypeTest.java b/test/javax/management/mxbean/GenericArrayTypeTest.java index b99ba96db..56778d051 100644 --- a/test/javax/management/mxbean/GenericArrayTypeTest.java +++ b/test/javax/management/mxbean/GenericArrayTypeTest.java @@ -32,17 +32,19 @@ */ import java.lang.management.ManagementFactory; -import java.lang.management.MonitorInfo; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import javax.management.Attribute; import javax.management.JMX; import javax.management.MBeanServer; import javax.management.MBeanServerConnection; import javax.management.ObjectName; +import javax.management.StandardMBean; +import javax.management.openmbean.CompositeData; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXConnectorServer; @@ -50,6 +52,58 @@ import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; public class GenericArrayTypeTest { + // A version of java.lang.management.MonitorInfo so we can run this test + // on JDK 5, where that class didn't exist. + public static class MonitorInfo { + private final String className; + private final int identityHashCode; + private final int lockedStackDepth; + private final StackTraceElement lockedStackFrame; + + public MonitorInfo( + String className, int identityHashCode, + int lockedStackDepth, StackTraceElement lockedStackFrame) { + this.className = className; + this.identityHashCode = identityHashCode; + this.lockedStackDepth = lockedStackDepth; + this.lockedStackFrame = lockedStackFrame; + } + + public static MonitorInfo from(CompositeData cd) { + try { + CompositeData stecd = (CompositeData) cd.get("lockedStackFrame"); + StackTraceElement ste = new StackTraceElement( + (String) stecd.get("className"), + (String) stecd.get("methodName"), + (String) stecd.get("fileName"), + (Integer) stecd.get("lineNumber")); + return new MonitorInfo( + (String) cd.get("className"), + (Integer) cd.get("identityHashCode"), + (Integer) cd.get("lockedStackDepth"), + ste); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public String getClassName() { + return className; + } + + public int getIdentityHashCode() { + return identityHashCode; + } + + public int getLockedStackDepth() { + return lockedStackDepth; + } + + public StackTraceElement getLockedStackFrame() { + return lockedStackFrame; + } + } + public interface TestMXBean { diff --git a/test/javax/management/mxbean/LeakTest.java b/test/javax/management/mxbean/LeakTest.java index f9248dacb..9125288f6 100644 --- a/test/javax/management/mxbean/LeakTest.java +++ b/test/javax/management/mxbean/LeakTest.java @@ -25,7 +25,7 @@ * @bug 6482247 * @summary Test that creating MXBeans does not introduce memory leaks. * @author Eamonn McManus - * @run build LeakTest + * @run build LeakTest RandomMXBeanTest * @run main LeakTest */ diff --git a/test/javax/management/mxbean/MBeanOperationInfoTest.java b/test/javax/management/mxbean/MBeanOperationInfoTest.java index cff666848..6e462d47c 100644 --- a/test/javax/management/mxbean/MBeanOperationInfoTest.java +++ b/test/javax/management/mxbean/MBeanOperationInfoTest.java @@ -86,7 +86,8 @@ public class MBeanOperationInfoTest { if (error > 0) { System.out.println("\nTEST FAILED"); throw new Exception("TEST FAILED: " + error + " wrong return types"); - } else if (tested != returnTypes.length) { + } else if (tested != returnTypes.length && + !System.getProperty("java.specification.version").equals("1.5")) { System.out.println("\nTEST FAILED"); throw new Exception("TEST FAILED: " + tested + " cases tested, " + returnTypes.length + " expected"); diff --git a/test/javax/management/mxbean/MXBeanTest.java b/test/javax/management/mxbean/MXBeanTest.java index 9415b39fa..5a156514c 100644 --- a/test/javax/management/mxbean/MXBeanTest.java +++ b/test/javax/management/mxbean/MXBeanTest.java @@ -149,7 +149,7 @@ public class MXBeanTest { if (mbai.getName().equals("Ints") && mbai.isReadable() && !mbai.isWritable() && mbai.getDescriptor().getFieldValue("openType") - .equals(new ArrayType(SimpleType.INTEGER, true)) + .equals(new ArrayType(SimpleType.INTEGER, true)) && attrs[0].getType().equals("[I")) success("MBeanAttributeInfo"); else diff --git a/test/javax/management/mxbean/ThreadMXBeanTest.java b/test/javax/management/mxbean/ThreadMXBeanTest.java index 2f79d8a86..1b3e3c9cf 100644 --- a/test/javax/management/mxbean/ThreadMXBeanTest.java +++ b/test/javax/management/mxbean/ThreadMXBeanTest.java @@ -46,7 +46,8 @@ public class ThreadMXBeanTest { long[] ids1 = proxy.getAllThreadIds(); // Add some random ids to the list so we'll get back null ThreadInfo - long[] ids2 = Arrays.copyOf(ids1, ids1.length + 10); + long[] ids2 = new long[ids1.length + 10]; + System.arraycopy(ids1, 0, ids2, 0, ids1.length); Random r = new Random(); for (int i = ids1.length; i < ids2.length; i++) ids2[i] = Math.abs(r.nextLong()); diff --git a/test/javax/management/mxbean/TigerMXBean.java b/test/javax/management/mxbean/TigerMXBean.java index f4f652cc7..4d728dafb 100644 --- a/test/javax/management/mxbean/TigerMXBean.java +++ b/test/javax/management/mxbean/TigerMXBean.java @@ -83,20 +83,20 @@ public interface TigerMXBean { Tuiseal opEnum(Tuiseal x, Tuiseal y); List StringList = Arrays.asList(new String[] {"a", "b", "x"}); - ArrayType StringListType = + ArrayType StringListType = MerlinMXBean.ArrayTypeMaker.make(1, SimpleType.STRING); List getStringList(); void setStringList(List x); List opStringList(List x, List y); - Set StringSet = new HashSet(StringList); - ArrayType StringSetType = StringListType; + Set StringSet = new HashSet(StringList); + ArrayType StringSetType = StringListType; Set getStringSet(); void setStringSet(Set x); Set opStringSet(Set x, Set y); - SortedSet SortedStringSet = new TreeSet(StringList); - ArrayType SortedStringSetType = StringListType; + SortedSet SortedStringSet = new TreeSet(StringList); + ArrayType SortedStringSetType = StringListType; SortedSet getSortedStringSet(); void setSortedStringSet(SortedSet x); SortedSet opSortedStringSet(SortedSet x, @@ -119,7 +119,7 @@ public interface TigerMXBean { Map> y); SortedMap XSortedMap = - new TreeMap(Collections.singletonMap("foo", "bar")); + new TreeMap(Collections.singletonMap("foo", "bar")); String XSortedMapTypeName = "java.util.SortedMap"; CompositeType XSortedMapRowType = MerlinMXBean.CompositeTypeMaker.make( @@ -137,8 +137,8 @@ public interface TigerMXBean { // For bug 6319960, try constructing Set and Map with non-Comparable - Set PointSet = new HashSet(Collections.singleton(Point)); - ArrayType PointSetType = + Set PointSet = new HashSet(Collections.singleton(Point)); + ArrayType PointSetType = MerlinMXBean.ArrayTypeMaker.make(1, PointType); Set getPointSet(); void setPointSet(Set x); diff --git a/test/javax/management/query/QueryNotifFilterTest.java b/test/javax/management/query/QueryNotifFilterTest.java index 2f3dcaa31..8b685741b 100644 --- a/test/javax/management/query/QueryNotifFilterTest.java +++ b/test/javax/management/query/QueryNotifFilterTest.java @@ -98,7 +98,7 @@ public class QueryNotifFilterTest { this.queryString = queryString; } abstract boolean apply(MBeanServer mbs, ObjectName name) throws Exception; - @Override + //@Override - doesn't override in JDK5 public boolean apply(ObjectName name) { try { return apply(getMBeanServer(), name); diff --git a/test/javax/management/remote/mandatory/connection/CloseServerTest.java b/test/javax/management/remote/mandatory/connection/CloseServerTest.java index ca92bc7cf..69412b268 100644 --- a/test/javax/management/remote/mandatory/connection/CloseServerTest.java +++ b/test/javax/management/remote/mandatory/connection/CloseServerTest.java @@ -31,11 +31,16 @@ * @run main CloseServerTest */ +import com.sun.jmx.remote.util.EnvHelp; import java.net.MalformedURLException; -import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; import javax.management.*; import javax.management.remote.*; +import javax.management.remote.rmi.RMIConnectorServer; public class CloseServerTest { private static final String[] protocols = {"rmi", "iiop", "jmxmp"}; @@ -131,40 +136,54 @@ public class CloseServerTest { server.stop(); - // with a client listener, but close the server first - System.out.println(">>> Open, start a server, create a client, add a listener, close the server then the client."); - server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs); - server.start(); + List> envs = Arrays.asList( + Collections.singletonMap( + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false"), + Collections.singletonMap( + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "true")); - addr = server.getAddress(); - client = JMXConnectorFactory.newJMXConnector(addr, null); - client.connect(null); + for (Map env : envs) { + System.out.println( + ">>>>>>>> " + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE + + " = " + env.get(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE)); - mserver = client.getMBeanServerConnection(); - mserver.addNotificationListener(delegateName, dummyListener, null, null); + // with a client listener, but close the server first + System.out.println(">>> Open, start a server, create a client, " + + "add a listener, close the server then the client."); + server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs); + server.start(); - server.stop(); + addr = server.getAddress(); + client = JMXConnectorFactory.newJMXConnector(addr, null); + client.connect(null); - try { - client.close(); - } catch (Exception e) { - // ok, it is because the server has been closed. - } + mserver = client.getMBeanServerConnection(); + mserver.addNotificationListener(delegateName, dummyListener, null, null); - // with a client listener, but close the client first - System.out.println(">>> Open, start a server, create a client, add a listener, close the client then the server."); - server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs); - server.start(); + server.stop(); - addr = server.getAddress(); - client = JMXConnectorFactory.newJMXConnector(addr, null); - client.connect(null); + try { + client.close(); + } catch (Exception e) { + // ok, it is because the server has been closed. + } - mserver = client.getMBeanServerConnection(); - mserver.addNotificationListener(delegateName, dummyListener, null, null); + // with a client listener, but close the client first + System.out.println(">>> Open, start a server, create a client, " + + "add a listener, close the client then the server."); + server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs); + server.start(); - client.close(); - server.stop(); + addr = server.getAddress(); + client = JMXConnectorFactory.newJMXConnector(addr, null); + client.connect(null); + + mserver = client.getMBeanServerConnection(); + mserver.addNotificationListener(delegateName, dummyListener, null, null); + + client.close(); + server.stop(); + } } catch (MalformedURLException e) { System.out.println(">>> Skipping unsupported URL " + u); return true; diff --git a/test/javax/management/remote/mandatory/connection/DeadLockTest.java b/test/javax/management/remote/mandatory/connection/DeadLockTest.java index 52f2b3ca0..3106b499d 100644 --- a/test/javax/management/remote/mandatory/connection/DeadLockTest.java +++ b/test/javax/management/remote/mandatory/connection/DeadLockTest.java @@ -37,6 +37,7 @@ import java.util.HashMap; import javax.management.*; import javax.management.remote.*; +import javax.management.remote.rmi.RMIConnectorServer; public class DeadLockTest { private static final String[] protocols = {"rmi", "iiop", "jmxmp"}; @@ -72,6 +73,9 @@ public class DeadLockTest { // disable the client ping env.put("jmx.remote.x.client.connection.check.period", "0"); + // ensure we are not internally using the Event Service on the server + env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false"); + try { u = new JMXServiceURL(proto, null, 0); server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs); diff --git a/test/javax/management/remote/mandatory/connection/IdleTimeoutTest.java b/test/javax/management/remote/mandatory/connection/IdleTimeoutTest.java index caf0bc770..9cad7787b 100644 --- a/test/javax/management/remote/mandatory/connection/IdleTimeoutTest.java +++ b/test/javax/management/remote/mandatory/connection/IdleTimeoutTest.java @@ -50,6 +50,8 @@ import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; import com.sun.jmx.remote.util.EnvHelp; +import java.util.Collections; +import javax.management.remote.rmi.RMIConnectorServer; public class IdleTimeoutTest { public static void main(String[] args) throws Exception { @@ -88,8 +90,13 @@ public class IdleTimeoutTest { private static long getIdleTimeout(MBeanServer mbs, JMXServiceURL url) throws Exception { + // If the connector server is using the Event Service, then connections + // never time out. This is by design. + Map env = + Collections.singletonMap( + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false"); JMXConnectorServer server = - JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); server.start(); try { url = server.getAddress(); @@ -164,6 +171,7 @@ public class IdleTimeoutTest { Map idleMap = new HashMap(); idleMap.put(EnvHelp.SERVER_CONNECTION_TIMEOUT, new Long(timeout)); + idleMap.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false"); JMXConnectorServer server = JMXConnectorServerFactory.newJMXConnectorServer(url,idleMap,mbs); diff --git a/test/javax/management/remote/mandatory/connection/RMIExitTest.java b/test/javax/management/remote/mandatory/connection/RMIExitTest.java index 08f687f43..d0797a98e 100644 --- a/test/javax/management/remote/mandatory/connection/RMIExitTest.java +++ b/test/javax/management/remote/mandatory/connection/RMIExitTest.java @@ -35,6 +35,8 @@ import java.net.MalformedURLException; import java.io.IOException; +import java.util.Collections; +import java.util.Map; import javax.management.MBeanServerFactory; import javax.management.MBeanServer; import javax.management.ObjectName; @@ -47,12 +49,14 @@ import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.rmi.RMIConnectorServer; /** * VM shutdown hook. Test that the hook is called less than 5 secs * after expected exit. */ class TimeChecker extends Thread { + @Override public void run() { System.out.println("shutdown hook called"); long elapsedTime = @@ -81,12 +85,15 @@ public class RMIExitTest { public static void main(String[] args) { System.out.println("Start test"); Runtime.getRuntime().addShutdownHook(new TimeChecker()); - test(); + test(false); + test(true); exitStartTime = System.currentTimeMillis(); System.out.println("End test"); } - private static void test() { + private static void test(boolean eventService) { + System.out.println( + "---testing with" + (eventService ? "" : "out") + " Event Service"); try { JMXServiceURL u = new JMXServiceURL("rmi", null, 0); JMXConnectorServer server; @@ -105,8 +112,11 @@ public class RMIExitTest { } }; + Map env = Collections.singletonMap( + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, + Boolean.toString(eventService)); server = JMXConnectorServerFactory.newJMXConnectorServer(u, - null, + env, mbs); server.start(); diff --git a/test/javax/management/remote/mandatory/connection/ReconnectTest.java b/test/javax/management/remote/mandatory/connection/ReconnectTest.java index 674b830e5..748cafbef 100644 --- a/test/javax/management/remote/mandatory/connection/ReconnectTest.java +++ b/test/javax/management/remote/mandatory/connection/ReconnectTest.java @@ -33,10 +33,10 @@ import java.util.*; import java.net.MalformedURLException; -import java.io.IOException; import javax.management.*; import javax.management.remote.*; +import javax.management.remote.rmi.RMIConnectorServer; public class ReconnectTest { private static final String[] protocols = {"rmi", "iiop", "jmxmp"}; @@ -48,6 +48,7 @@ public class ReconnectTest { String timeout = "1000"; env.put("jmx.remote.x.server.connection.timeout", timeout); env.put("jmx.remote.x.client.connection.check.period", timeout); + env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false"); } public static void main(String[] args) throws Exception { diff --git a/test/javax/management/remote/mandatory/connectorServer/ForwarderChainTest.java b/test/javax/management/remote/mandatory/connectorServer/ForwarderChainTest.java new file mode 100644 index 000000000..b6c6d8792 --- /dev/null +++ b/test/javax/management/remote/mandatory/connectorServer/ForwarderChainTest.java @@ -0,0 +1,274 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.util.NoSuchElementException; +import java.util.Random; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.remote.IdentityMBeanServerForwarder; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXServiceURL; +import javax.management.remote.MBeanServerForwarder; + +/* + * @test + * @bug 6218920 + * @summary Tests manipulation of MBeanServerForwarder chains. + * @author Eamonn McManus + */ +import javax.management.remote.rmi.RMIConnectorServer; + +public class ForwarderChainTest { + private static final TestMBeanServerForwarder[] forwarders = + new TestMBeanServerForwarder[10]; + static { + for (int i = 0; i < forwarders.length; i++) + forwarders[i] = new TestMBeanServerForwarder(i); + } + + private static class TestMBeanServerForwarder + extends IdentityMBeanServerForwarder { + private final int index; + volatile int defaultDomainCount; + + TestMBeanServerForwarder(int index) { + this.index = index; + } + + @Override + public String getDefaultDomain() { + defaultDomainCount++; + return super.getDefaultDomain(); + } + + @Override + public String toString() { + return "forwarders[" + index + "]"; + } + } + + private static String failure; + + public static void main(String[] args) throws Exception { + + System.out.println("===Test with newly created, unattached server==="); + + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///"); + JMXConnectorServer cs = new RMIConnectorServer(url, null); + test(cs, null); + + System.out.println("===Test with server attached to MBS==="); + MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + cs = new RMIConnectorServer(url, null, mbs); + test(cs, mbs); + + System.out.println("===Remove any leftover forwarders==="); + while (cs.getSystemMBeanServer() instanceof MBeanServerForwarder) { + MBeanServerForwarder mbsf = + (MBeanServerForwarder) cs.getSystemMBeanServer(); + cs.removeMBeanServerForwarder(mbsf); + } + expectChain(cs, "U", mbs); + + System.out.println("===Ensure forwarders are called==="); + cs.setMBeanServerForwarder(forwarders[0]); + cs.setSystemMBeanServerForwarder(forwarders[1]); + expectChain(cs, "1U0", mbs); + cs.start(); + if (forwarders[0].defaultDomainCount != 0 || + forwarders[1].defaultDomainCount != 0) { + fail("defaultDomainCount not zero"); + } + JMXServiceURL addr = cs.getAddress(); + JMXConnector cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + mbsc.getDefaultDomain(); + cc.close(); + cs.stop(); + for (boolean system : new boolean[] {false, true}) { + TestMBeanServerForwarder mbsf = system ? forwarders[1] : forwarders[0]; + if (mbsf.defaultDomainCount != 1) { + fail((system ? "System" : "User") + " forwarder called " + + mbsf.defaultDomainCount + " times"); + } + } + + if (failure == null) + System.out.println("TEST PASSED"); + else + throw new Exception("TEST FAILED: " + failure); + } + + private static void test(JMXConnectorServer cs, MBeanServer end) { + // A newly-created connector server might have system forwarders, + // so get rid of those. + while (cs.getSystemMBeanServer() != cs.getMBeanServer()) + cs.removeMBeanServerForwarder((MBeanServerForwarder) cs.getSystemMBeanServer()); + + expectChain(cs, "U", end); + + System.out.println("Add a user forwarder"); + cs.setMBeanServerForwarder(forwarders[0]); + expectChain(cs, "U0", end); + + System.out.println("Add another user forwarder"); + cs.setMBeanServerForwarder(forwarders[1]); + expectChain(cs, "U10", end); + + System.out.println("Add a system forwarder"); + cs.setSystemMBeanServerForwarder(forwarders[2]); + expectChain(cs, "2U10", end); + + System.out.println("Add another user forwarder"); + cs.setMBeanServerForwarder(forwarders[3]); + expectChain(cs, "2U310", end); + + System.out.println("Add another system forwarder"); + cs.setSystemMBeanServerForwarder(forwarders[4]); + expectChain(cs, "42U310", end); + + System.out.println("Remove the first user forwarder"); + cs.removeMBeanServerForwarder(forwarders[3]); + expectChain(cs, "42U10", end); + + System.out.println("Remove the last user forwarder"); + cs.removeMBeanServerForwarder(forwarders[0]); + expectChain(cs, "42U1", end); + + System.out.println("Remove the first system forwarder"); + cs.removeMBeanServerForwarder(forwarders[4]); + expectChain(cs, "2U1", end); + + System.out.println("Remove the last system forwarder"); + cs.removeMBeanServerForwarder(forwarders[2]); + expectChain(cs, "U1", end); + + System.out.println("Remove the last forwarder"); + cs.removeMBeanServerForwarder(forwarders[1]); + expectChain(cs, "U", end); + + System.out.println("---Doing random manipulations---"); + // In this loop we pick one of the forwarders at random each time. + // If it is already in the chain, then we remove it. If it is not + // in the chain, then we do one of three things: try to remove it + // (expecting an exception); add it to the user chain; or add it + // to the system chain. + // A subtle point is that if there is no MBeanServer then + // cs.setMBeanServerForwarder(mbsf) does not change mbsf.getMBeanServer(). + // Since we're recycling a random forwarder[i], we explicitly + // call mbsf.setMBeanServer(null) in this case. + String chain = "U"; + Random r = new Random(); + for (int i = 0; i < 50; i++) { + int fwdi = r.nextInt(10); + MBeanServerForwarder mbsf = forwarders[fwdi]; + char c = (char) ('0' + fwdi); + int ci = chain.indexOf(c); + if (ci >= 0) { + System.out.println("Remove " + c); + cs.removeMBeanServerForwarder(mbsf); + chain = chain.substring(0, ci) + chain.substring(ci + 1); + } else { + switch (r.nextInt(3)) { + case 0: { // try to remove it + try { + System.out.println("Try to remove absent " + c); + cs.removeMBeanServerForwarder(mbsf); + fail("Remove succeeded but should not have"); + return; + } catch (NoSuchElementException e) { + } + break; + } + case 1: { // add it to the user chain + System.out.println("Add " + c + " to user chain"); + if (cs.getMBeanServer() == null) + mbsf.setMBeanServer(null); + cs.setMBeanServerForwarder(mbsf); + int postu = chain.indexOf('U') + 1; + chain = chain.substring(0, postu) + c + + chain.substring(postu); + break; + } + case 2: { // add it to the system chain + System.out.println("Add " + c + " to system chain"); + if (cs.getSystemMBeanServer() == null) + mbsf.setMBeanServer(null); + cs.setSystemMBeanServerForwarder(mbsf); + chain = c + chain; + break; + } + } + } + expectChain(cs, chain, end); + } + } + + /* + * Check that the forwarder chain has the expected contents. The forwarders + * are encoded as a string. For example, "12U34" means that the system + * chain contains forwarders[1] followed by forwarders[2], and the user + * chain contains forwarders[3] followed by forwarders[4]. Since the + * user chain is attached to the end of the system chain, another way to + * look at this is that the U marks the transition from one to the other. + * + * After traversing the chains, we should be pointing at "end". + */ + private static void expectChain( + JMXConnectorServer cs, String chain, MBeanServer end) { + System.out.println("...expected chain: " + chain); + MBeanServer curr = cs.getSystemMBeanServer(); + int i = 0; + while (i < chain.length()) { + char c = chain.charAt(i); + if (c == 'U') { + if (cs.getMBeanServer() != curr) { + fail("User chain should have started here: " + curr); + return; + } + } else { + int fwdi = c - '0'; + MBeanServerForwarder forwarder = forwarders[fwdi]; + if (curr != forwarder) { + fail("Expected forwarder " + c + " here: " + curr); + return; + } + curr = ((MBeanServerForwarder) curr).getMBeanServer(); + } + i++; + } + if (curr != end) { + fail("End of chain is " + curr + ", should be " + end); + return; + } + System.out.println("...OK"); + } + + private static void fail(String msg) { + System.out.println("FAILED: " + msg); + failure = msg; + } +} diff --git a/test/javax/management/remote/mandatory/connectorServer/StandardForwardersTest.java b/test/javax/management/remote/mandatory/connectorServer/StandardForwardersTest.java new file mode 100644 index 000000000..d2d5e3771 --- /dev/null +++ b/test/javax/management/remote/mandatory/connectorServer/StandardForwardersTest.java @@ -0,0 +1,177 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.lang.management.ManagementFactory; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.management.MBeanServer; +import javax.management.event.EventClientDelegate; +import javax.management.remote.JMXConnectorServer; + +/* + * @test + * @bug 6663757 + * @summary Tests standard MBeanServerForwarders introduced by connector server + * options. + * @author Eamonn McManus + */ +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; +import javax.management.remote.MBeanServerForwarder; + +public class StandardForwardersTest { + private static String failure; + + private static class Forwarder { + private final String attribute; + private final boolean defaultEnabled; + private final Class expectedClass; + + public Forwarder(String attribute, boolean defaultEnabled, + Class expectedClass) { + this.attribute = attribute; + this.defaultEnabled = defaultEnabled; + this.expectedClass = expectedClass; + } + } + + private static enum Status {DISABLED, ENABLED, DEFAULT} + + public static void main(String[] args) throws Exception { + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + MBeanServerForwarder ecdFwd = + EventClientDelegate.newForwarder(); + Forwarder ecd = new Forwarder( + JMXConnectorServer.EVENT_CLIENT_DELEGATE_FORWARDER, true, + ecdFwd.getClass()); + + Forwarder[] forwarders = {ecd}; + + // Now go through every combination of forwarders. Each forwarder + // may be explicitly enabled, explicitly disabled, or left to its + // default value. + int nStatus = Status.values().length; + int limit = (int) Math.pow(nStatus, forwarders.length); + for (int i = 0; i < limit; i++) { + Status[] status = new Status[forwarders.length]; + int ii = i; + for (int j = 0; j < status.length; j++) { + status[j] = Status.values()[ii % nStatus]; + ii /= nStatus; + } + Map env = new HashMap(); + String test = ""; + for (int j = 0; j < status.length; j++) { + if (!test.equals("")) + test += "; "; + test += forwarders[j].attribute; + switch (status[j]) { + case DISABLED: + test += "=false"; + env.put(forwarders[j].attribute, "false"); + break; + case ENABLED: + test += "=true"; + env.put(forwarders[j].attribute, "true"); + break; + case DEFAULT: + test += "=default(" + forwarders[j].defaultEnabled + ")"; + break; + } + } + boolean consistent = isConsistent(env); + test += "; (" + (consistent ? "" : "in") + "consistent)"; + System.out.println(test); + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///"); + try { + JMXConnectorServer cs = + JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); + if (!consistent) { + fail("Inconsistent attributes should have been rejected " + + "but were not"); + } + checkForwarders(cs, forwarders, status); + } catch (IllegalArgumentException e) { + if (consistent) { + fail("Consistent attributes provoked IllegalArgumentException"); + e.printStackTrace(System.out); + } + } + } + + if (failure == null) + System.out.println("TEST PASSED"); + else + throw new Exception(failure); + } + + // Check that the classes of the forwarders in the system chain correspond + // to what we expect given the options we have passed. This check is a bit + // superficial in the sense that a forwarder might be for example a + // SingleMBeanForwarderHandler but that doesn't prove that it is the + // right Single MBean. Nevertheless the test should expose any severe + // wrongness. + // + // The check here makes some assumptions that could become untrue in the + // future. First, it assumes that the forwarders that are added have + // exactly the classes that are in the Forwarder[] array. So for example + // the forwarder for CONTEXT_FORWARDER must be of the same class as an + // explicit call to ClientContext.newContextForwarder. The spec doesn't + // require that - it only requires that the forwarder have the same + // behaviour. The second assumption is that the connector server doesn't + // add any forwarders of its own into the system chain, and again the spec + // doesn't disallow that. + private static void checkForwarders( + JMXConnectorServer cs, Forwarder[] forwarders, Status[] status) { + List> expectedClasses = new ArrayList>(); + for (int i = 0; i < forwarders.length; i++) { + if (status[i] == Status.ENABLED || + (status[i] == Status.DEFAULT && forwarders[i].defaultEnabled)) + expectedClasses.add(forwarders[i].expectedClass); + } + MBeanServer stop = cs.getMBeanServer(); + List> foundClasses = new ArrayList>(); + for (MBeanServer mbs = cs.getSystemMBeanServer(); mbs != stop; + mbs = ((MBeanServerForwarder) mbs).getMBeanServer()) + foundClasses.add(mbs.getClass()); + if (!expectedClasses.equals(foundClasses)) { + fail("Incorrect forwarder chain: expected " + expectedClasses + + "; found " + foundClasses); + } + } + + // env is consistent if either (a) localizer is not enabled or (b) + // localizer is enabled and context is enabled. + // Neither of those is present in this codebase so env is always consistent. + private static boolean isConsistent(Map env) { + return true; + } + + private static void fail(String why) { + System.out.println("FAILED: " + why); + failure = why; + } +} diff --git a/test/javax/management/remote/mandatory/loading/MissingClassTest.java b/test/javax/management/remote/mandatory/loading/MissingClassTest.java index 7aab83079..3b2ce940f 100644 --- a/test/javax/management/remote/mandatory/loading/MissingClassTest.java +++ b/test/javax/management/remote/mandatory/loading/MissingClassTest.java @@ -44,13 +44,33 @@ We also test objects that are of known class but not serializable. The test cases are similar. */ -import java.io.*; -import java.net.*; -import java.util.*; -import javax.management.*; -import javax.management.loading.*; -import javax.management.remote.*; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.NotSerializableException; +import java.io.ObjectOutputStream; +import java.net.MalformedURLException; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import javax.management.Attribute; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.remote.JMXConnectionNotification; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; import javax.management.remote.rmi.RMIConnectorServer; +import org.omg.CORBA.MARSHAL; public class MissingClassTest { private static final int NNOTIFS = 50; @@ -84,7 +104,6 @@ public class MissingClassTest { serverLoader.loadClass("$ClientUnknown$").newInstance(); final String[] protos = {"rmi", /*"iiop",*/ "jmxmp"}; - // iiop commented out until bug 4935098 is fixed boolean ok = true; for (int i = 0; i < protos.length; i++) { try { @@ -105,7 +124,16 @@ public class MissingClassTest { } private static boolean test(String proto) throws Exception { - System.out.println("Testing for proto " + proto); + boolean ok = true; + for (boolean eventService : new boolean[] {false, true}) + ok &= test(proto, eventService); + return ok; + } + + private static boolean test(String proto, boolean eventService) + throws Exception { + System.out.println("Testing for proto " + proto + " with" + + (eventService ? "" : "out") + " Event Service"); boolean ok = true; @@ -117,6 +145,8 @@ public class MissingClassTest { Map serverMap = new HashMap(); serverMap.put(JMXConnectorServerFactory.DEFAULT_CLASS_LOADER, serverLoader); + serverMap.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, + Boolean.toString(eventService)); // make sure no auto-close at server side serverMap.put("jmx.remote.x.server.connection.timeout", "888888888"); @@ -155,6 +185,8 @@ public class MissingClassTest { ok = false; } catch (IOException e) { Throwable cause = e.getCause(); + if (cause instanceof MARSHAL) // see CR 4935098 + cause = cause.getCause(); if (cause instanceof ClassNotFoundException) { System.out.println("Success: got an IOException wrapping " + "a ClassNotFoundException"); @@ -177,6 +209,8 @@ public class MissingClassTest { ok = false; } catch (IOException e) { Throwable wrapped = e.getCause(); + if (wrapped instanceof MARSHAL) // see CR 4935098 + wrapped = wrapped.getCause(); if (wrapped instanceof ClassNotFoundException) { System.out.println("Success: got an IOException wrapping " + "a ClassNotFoundException: " + @@ -228,6 +262,8 @@ public class MissingClassTest { ok = false; } catch (IOException e) { Throwable cause = e.getCause(); + if (cause instanceof MARSHAL) // see CR 4935098 + cause = cause.getCause(); if (cause instanceof ClassNotFoundException) { System.out.println("Success: got an IOException " + "wrapping a ClassNotFoundException"); @@ -461,12 +497,13 @@ public class MissingClassTest { while ((remain = deadline - System.currentTimeMillis()) >= 0) { synchronized (result) { if (result.failed - || (result.knownCount == NNOTIFS - && result.lostCount == NNOTIFS*2)) + || (result.knownCount >= NNOTIFS + && result.lostCount >= NNOTIFS*2)) break; result.wait(remain); } } + Thread.sleep(2); // allow any spurious extra notifs to arrive if (result.failed) { System.out.println("TEST FAILS: Notification strangeness"); return false; @@ -476,6 +513,11 @@ public class MissingClassTest { "got NOTIFS_LOST for unknown and " + "unserializable ones"); return true; + } else if (result.knownCount >= NNOTIFS + || result.lostCount >= NNOTIFS*2) { + System.out.println("TEST FAILS: Received too many notifs: " + + "known=" + result.knownCount + "; lost=" + result.lostCount); + return false; } else { System.out.println("TEST FAILS: Timed out without receiving " + "all notifs: known=" + result.knownCount + diff --git a/test/javax/management/remote/mandatory/notif/AddRemoveTest.java b/test/javax/management/remote/mandatory/notif/AddRemoveTest.java index 126a761ff..6aae3b1bf 100644 --- a/test/javax/management/remote/mandatory/notif/AddRemoveTest.java +++ b/test/javax/management/remote/mandatory/notif/AddRemoveTest.java @@ -33,10 +33,12 @@ */ import java.net.MalformedURLException; -import java.io.IOException; +import java.util.Collections; +import java.util.Map; import javax.management.*; import javax.management.remote.*; +import javax.management.remote.rmi.RMIConnectorServer; public class AddRemoveTest { private static final String[] protocols = {"rmi", "iiop", "jmxmp"}; @@ -69,9 +71,16 @@ public class AddRemoveTest { } } - private static boolean test(String proto) + private static boolean test(String proto) throws Exception { + boolean ok = test(proto, false); + ok &= test(proto, true); + return ok; + } + + private static boolean test(String proto, boolean eventService) throws Exception { - System.out.println(">>> Test for protocol " + proto); + System.out.println(">>> Test for protocol " + proto + " with" + + (eventService ? "" : "out") + " event service"); JMXServiceURL u = new JMXServiceURL(proto, null, 0); JMXConnectorServer server; JMXServiceURL addr; @@ -89,7 +98,10 @@ public class AddRemoveTest { try { // with a client listener, but close the server first - server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs); + Map env = Collections.singletonMap( + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, + Boolean.toString(eventService)); + server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs); server.start(); addr = server.getAddress(); diff --git a/test/javax/management/remote/mandatory/notif/DiffHBTest.java b/test/javax/management/remote/mandatory/notif/DiffHBTest.java index 8903d88dc..8fd8302e4 100644 --- a/test/javax/management/remote/mandatory/notif/DiffHBTest.java +++ b/test/javax/management/remote/mandatory/notif/DiffHBTest.java @@ -31,11 +31,12 @@ * @run main DiffHBTest */ -import java.net.MalformedURLException; -import java.io.IOException; +import java.util.Collections; +import java.util.Map; import javax.management.*; import javax.management.remote.*; +import javax.management.remote.rmi.RMIConnectorServer; /** * This test registeres an unique listener with two different handbacks, @@ -48,11 +49,6 @@ public class DiffHBTest { private static ObjectName delegateName; private static ObjectName timerName; - public static int received = 0; - public static final int[] receivedLock = new int[0]; - public static Notification receivedNotif = null; - - public static Object receivedHB = null; public static final String[] hbs = new String[] {"0", "1"}; public static void main(String[] args) throws Exception { @@ -61,162 +57,174 @@ public class DiffHBTest { delegateName = new ObjectName("JMImplementation:type=MBeanServerDelegate"); timerName = new ObjectName("MBean:name=Timer"); - boolean ok = true; + String errors = ""; + for (int i = 0; i < protocols.length; i++) { - try { - if (!test(protocols[i])) { - System.out.println(">>> Test failed for " + protocols[i]); - ok = false; + final String s = test(protocols[i]); + if (s != null) { + if ("".equals(errors)) { + errors = "Failed to " + protocols[i] + ": "+s; } else { - System.out.println(">>> Test successed for " + protocols[i]); + errors = "\tFailed to " + protocols[i] + ": "+s; } - } catch (Exception e) { - System.out.println(">>> Test failed for " + protocols[i]); - e.printStackTrace(System.out); - ok = false; } } - if (ok) { - System.out.println(">>> Test passed"); + if ("".equals(errors)) { + System.out.println(">>> Passed!"); } else { - System.out.println(">>> TEST FAILED"); - System.exit(1); + System.out.println(">>> Failed!"); + + throw new RuntimeException(errors); } } - private static boolean test(String proto) throws Exception { - System.out.println(">>> Test for protocol " + proto); + private static String test(String proto) throws Exception { + String ret = null; + for (boolean eventService : new boolean[] {false, true}) { + String s = test(proto, eventService); + if (s != null) { + if (ret == null) + ret = s; + else + ret = ret + "; " + s; + } + } + return ret; + } + + private static String test(String proto, boolean eventService) + throws Exception { + System.out.println(">>> Test for protocol " + proto + " with" + + (eventService ? "" : "out") + " event service"); JMXServiceURL u = new JMXServiceURL(proto, null, 0); JMXConnectorServer server; - JMXServiceURL addr; JMXConnector client; - MBeanServerConnection mserver; - - final NotificationListener dummyListener = new NotificationListener() { - public void handleNotification(Notification n, Object o) { - synchronized(receivedLock) { - if (n == null) { - System.out.println(">>> Got a null notification."); - System.exit(1); - } - - // check number - if (received > 2) { - System.out.println(">>> Expect to receive 2 notifs, but get "+received); - System.exit(1); - } - - if (received == 0) { // first time - receivedNotif = n; - receivedHB = o; - - if (!hbs[0].equals(o) && !hbs[1].equals(o)) { - System.out.println(">>> Unkown handback: "+o); - System.exit(1); - } - } else { // second time - if (!receivedNotif.equals(n)) { - System.out.println(">>> Not get same notif twice."); - System.exit(1); - } else if (!hbs[0].equals(o) && !hbs[1].equals(o)) { // validate handback - System.out.println(">>> Unkown handback: "+o); - System.exit(1); - } else if (receivedHB.equals(o)) { - System.out.println(">>> Got same handback twice: "+o); - System.exit(1); - } - } - - ++received; - - if (received == 2) { - receivedLock.notify(); - } - } - } - }; try { - server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs); + Map env = Collections.singletonMap( + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, + Boolean.toString(eventService)); + server = + JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs); server.start(); + JMXServiceURL addr = server.getAddress(); + client = JMXConnectorFactory.connect(addr, null); + } catch (Exception e) { + // not support + System.out.println(">>> not support: " + proto); + return null; + } - addr = server.getAddress(); - client = JMXConnectorFactory.newJMXConnector(addr, null); - client.connect(null); - - mserver = client.getMBeanServerConnection(); - - mserver.addNotificationListener(delegateName, dummyListener, null, hbs[0]); - mserver.addNotificationListener(delegateName, dummyListener, null, hbs[1]); + MBeanServerConnection mserver = client.getMBeanServerConnection(); - for (int i=0; i<20; i++) { - synchronized(receivedLock) { - received = 0; - } + System.out.print(">>>\t"); + for (int i=0; i<5; i++) { + System.out.print(i + "\t"); + final MyListener dummyListener = new MyListener(); + mserver.addNotificationListener( + delegateName, dummyListener, null, hbs[0]); + mserver.addNotificationListener( + delegateName, dummyListener, null, hbs[1]); - mserver.createMBean("javax.management.timer.Timer", timerName); + mserver.createMBean("javax.management.timer.Timer", timerName); - synchronized(receivedLock) { - if (received != 2) { - long remainingTime = waitingTime; - final long startTime = System.currentTimeMillis(); + long remainingTime = waitingTime; + final long startTime = System.currentTimeMillis(); - while (received != 2 && remainingTime > 0) { - receivedLock.wait(remainingTime); - remainingTime = waitingTime - + try { + synchronized(dummyListener) { + while (!dummyListener.done && remainingTime > 0) { + dummyListener.wait(remainingTime); + remainingTime = waitingTime - (System.currentTimeMillis() - startTime); - } } - if (received != 2) { - System.out.println(">>> Expected 2 notifis, but received "+received); - - return false; + if (dummyListener.errorInfo != null) { + return dummyListener.errorInfo; } } + } finally { + //System.out.println("Unregister: "+i); + mserver.unregisterMBean(timerName); + mserver.removeNotificationListener(delegateName, dummyListener); + } + } + System.out.println(""); + client.close(); + server.stop(); - synchronized(receivedLock) { - received = 0; - } + return null; + } - mserver.unregisterMBean(timerName); + private static class MyListener implements NotificationListener { + public boolean done = false; + public String errorInfo = null; + + private int received = 0; + private MBeanServerNotification receivedNotif = null; + private Object receivedHB = null; + public void handleNotification(Notification n, Object o) { + if (!(n instanceof MBeanServerNotification)) { + failed("Received an unexpected notification: "+n); + return; + } - synchronized(receivedLock) { - if (received != 2) { + if (!hbs[0].equals(o) && !hbs[1].equals(o)) { + failed("Unkown handback: "+o); + return; + } - long remainingTime = waitingTime; - final long startTime = System.currentTimeMillis(); + // what we need + final MBeanServerNotification msn = (MBeanServerNotification)n; + if (!(MBeanServerNotification.REGISTRATION_NOTIFICATION.equals( + msn.getType())) || + !msn.getMBeanName().equals(timerName)) { + return; + } - while (received != 2 && remainingTime >0) { - receivedLock.wait(remainingTime); - remainingTime = waitingTime - - (System.currentTimeMillis() - startTime); - } - } + synchronized(this) { + received++; - if (received != 2) { - System.out.println(">>> Expected 2 notifis, but received "+received); + if (received == 1) { // first time + receivedNotif = msn; + receivedHB = o; - return false; - } + return; } - } - mserver.removeNotificationListener(delegateName, dummyListener); + if (received > 2) { + failed("Expect to receive 2 notifs, but get "+received); - client.close(); + return; + } - server.stop(); + // second time + if (receivedHB.equals(o)) { + failed("Got same handback twice: "+o); + } else if(!hbs[0].equals(o) && !hbs[1].equals(o)) { + failed("Unknown handback: "+o); + } else if (receivedNotif.getSequenceNumber() != + msn.getSequenceNumber()) { + failed("expected to receive:\n" + +receivedNotif + +"\n but got\n"+msn); + } - } catch (MalformedURLException e) { - System.out.println(">>> Skipping unsupported URL " + u); - return true; + // passed + done = true; + this.notify(); + } } - return true; + private void failed(String errorInfo) { + this.errorInfo = errorInfo; + done = true; + + this.notify(); + } } - private final static long waitingTime = 10000; + private final static long waitingTime = 2000; } diff --git a/test/javax/management/remote/mandatory/notif/EmptyDomainNotificationTest.java b/test/javax/management/remote/mandatory/notif/EmptyDomainNotificationTest.java index 0e7a69d34..334f2135b 100644 --- a/test/javax/management/remote/mandatory/notif/EmptyDomainNotificationTest.java +++ b/test/javax/management/remote/mandatory/notif/EmptyDomainNotificationTest.java @@ -29,11 +29,12 @@ * @author Shanliang JIANG * @run clean EmptyDomainNotificationTest * @run build EmptyDomainNotificationTest - * @run main EmptyDomainNotificationTest + * @run main EmptyDomainNotificationTest classic + * @run main EmptyDomainNotificationTest event */ -import java.util.ArrayList; -import java.util.List; +import java.util.Collections; +import java.util.Map; import javax.management.MBeanServer; import javax.management.MBeanServerConnection; import javax.management.MBeanServerFactory; @@ -46,6 +47,7 @@ import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; +import javax.management.remote.rmi.RMIConnectorServer; public class EmptyDomainNotificationTest { @@ -80,11 +82,25 @@ public class EmptyDomainNotificationTest { public static void main(String[] args) throws Exception { + String type = args[0]; + boolean eventService; + if (type.equals("classic")) + eventService = false; + else if (type.equals("event")) + eventService = true; + else + throw new IllegalArgumentException(type); + final MBeanServer mbs = MBeanServerFactory.createMBeanServer(); final JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://"); - JMXConnectorServer server = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + Map env = Collections.singletonMap( + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, + Boolean.toString(eventService)); + + JMXConnectorServer server = + JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); server.start(); JMXConnector client = JMXConnectorFactory.connect(server.getAddress(), null); diff --git a/test/javax/management/remote/mandatory/notif/ListenerScaleTest.java b/test/javax/management/remote/mandatory/notif/ListenerScaleTest.java index d0c519e45..609f4d1f4 100644 --- a/test/javax/management/remote/mandatory/notif/ListenerScaleTest.java +++ b/test/javax/management/remote/mandatory/notif/ListenerScaleTest.java @@ -51,18 +51,29 @@ * been compiled by the second. */ -import java.lang.management.ManagementFactory; -import javax.management.*; -import javax.management.remote.*; -import java.util.concurrent.*; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.Semaphore; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.MalformedObjectNameException; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; +import javax.management.remote.rmi.RMIConnectorServer; public class ListenerScaleTest { private static final int WARMUP_WITH_ONE_MBEAN = 1000; private static final int NOTIFS_TO_TIME = 100; private static final int EXTRA_MBEANS = 20000; - private static final MBeanServer mbs = - ManagementFactory.getPlatformMBeanServer(); private static final ObjectName testObjectName; static { try { @@ -87,7 +98,7 @@ public class ListenerScaleTest { } }; - private static final long timeNotif() { + private static final long timeNotif(MBeanServer mbs) { try { startTime = System.nanoTime(); nnotifs = 0; @@ -117,12 +128,20 @@ public class ListenerScaleTest { }; public static void main(String[] args) throws Exception { - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + test(false); + test(true); + } + + private static void test(boolean eventService) throws Exception { + MBeanServer mbs = MBeanServerFactory.newMBeanServer(); Sender sender = new Sender(); mbs.registerMBean(sender, testObjectName); JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://"); + Map env = Collections.singletonMap( + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, + Boolean.toString(eventService)); JMXConnectorServer cs = - JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); cs.start(); JMXServiceURL addr = cs.getAddress(); JMXConnector cc = JMXConnectorFactory.connect(addr); @@ -140,7 +159,7 @@ public class ListenerScaleTest { mbsc.addNotificationListener(testObjectName, timingListener, null, null); long singleMBeanTime = 0; for (int i = 0; i < WARMUP_WITH_ONE_MBEAN; i++) - singleMBeanTime = timeNotif(); + singleMBeanTime = timeNotif(mbs); if (singleMBeanTime == 0) singleMBeanTime = 1; System.out.println("Time with a single MBean: " + singleMBeanTime + "ns"); @@ -165,7 +184,7 @@ public class ListenerScaleTest { } System.out.println(); System.out.println("Timing a notification send now"); - long manyMBeansTime = timeNotif(); + long manyMBeansTime = timeNotif(mbs); System.out.println("Time with many MBeans: " + manyMBeansTime + "ns"); double ratio = (double) manyMBeansTime / singleMBeanTime; if (ratio > 100.0) diff --git a/test/javax/management/remote/mandatory/notif/NotifBufferSizePropertyNameTest.java b/test/javax/management/remote/mandatory/notif/NotifBufferSizePropertyNameTest.java index 254e8712b..f10ef485f 100644 --- a/test/javax/management/remote/mandatory/notif/NotifBufferSizePropertyNameTest.java +++ b/test/javax/management/remote/mandatory/notif/NotifBufferSizePropertyNameTest.java @@ -31,11 +31,11 @@ * @run main NotifBufferSizePropertyNameTest */ -import java.io.IOException; import java.util.*; import javax.management.*; import javax.management.remote.*; +import javax.management.remote.rmi.RMIConnectorServer; /** * This class tests also the size of a server notification buffer. @@ -88,6 +88,9 @@ public class NotifBufferSizePropertyNameTest { private static void test(Map env) throws Exception { final MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + env = new HashMap((env == null) ? Collections.emptyMap() : env); + env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false"); + mbs.registerMBean(new NotificationEmitter(), oname); JMXConnectorServer server = JMXConnectorServerFactory.newJMXConnectorServer( url, diff --git a/test/javax/management/remote/mandatory/notif/NotifReconnectDeadlockTest.java b/test/javax/management/remote/mandatory/notif/NotifReconnectDeadlockTest.java index bb0539998..2b451be95 100644 --- a/test/javax/management/remote/mandatory/notif/NotifReconnectDeadlockTest.java +++ b/test/javax/management/remote/mandatory/notif/NotifReconnectDeadlockTest.java @@ -22,7 +22,7 @@ */ /* - * @test NotifReconnectDeadlockTest + * @test * @bug 6199899 * @summary Tests reconnection done by a fetching notif thread. * @author Shanliang JIANG @@ -31,11 +31,21 @@ * @run main NotifReconnectDeadlockTest */ -import java.io.IOException; -import java.util.*; - -import javax.management.*; -import javax.management.remote.*; +import java.util.HashMap; +import java.util.Map; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.remote.JMXConnectionNotification; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; +import javax.management.remote.rmi.RMIConnectorServer; /** * "This test checks for a bug whereby reconnection did not work if (a) it was @@ -64,6 +74,7 @@ public class NotifReconnectDeadlockTest { Map env = new HashMap(2); env.put("jmx.remote.x.server.connection.timeout", new Long(serverTimeout)); env.put("jmx.remote.x.client.connection.check.period", new Long(Long.MAX_VALUE)); + env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false"); final MBeanServer mbs = MBeanServerFactory.newMBeanServer(); diff --git a/test/javax/management/remote/mandatory/notif/NotificationAccessControllerTest.java b/test/javax/management/remote/mandatory/notif/NotificationAccessControllerTest.java index 42a4eb7aa..c7018800b 100644 --- a/test/javax/management/remote/mandatory/notif/NotificationAccessControllerTest.java +++ b/test/javax/management/remote/mandatory/notif/NotificationAccessControllerTest.java @@ -156,7 +156,8 @@ public class NotificationAccessControllerTest { List received, List expected) { if (received.size() != size) { - echo("Error: expecting " + size + " notifications"); + echo("Error: expecting " + size + " notifications, got " + + received.size()); return 1; } else { for (Notification n : received) { diff --git a/test/javax/management/remote/mandatory/notif/NotificationBufferCreationTest.java b/test/javax/management/remote/mandatory/notif/NotificationBufferCreationTest.java index 172164769..c191ae509 100644 --- a/test/javax/management/remote/mandatory/notif/NotificationBufferCreationTest.java +++ b/test/javax/management/remote/mandatory/notif/NotificationBufferCreationTest.java @@ -32,6 +32,8 @@ */ import java.net.MalformedURLException; +import java.util.Collections; +import java.util.Map; import javax.management.MBeanServerFactory; import javax.management.MBeanServer; import javax.management.MBeanServerConnection; @@ -44,6 +46,7 @@ import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; +import javax.management.remote.rmi.RMIConnectorServer; public class NotificationBufferCreationTest { private static final MBeanServer mbs = @@ -86,6 +89,8 @@ public class NotificationBufferCreationTest { JMXServiceURL u = null; try { u = new JMXServiceURL(protocol, null, 0); + Map env = Collections.singletonMap( + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false"); server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, diff --git a/test/javax/management/remote/mandatory/notif/NotificationBufferDeadlockTest.java b/test/javax/management/remote/mandatory/notif/NotificationBufferDeadlockTest.java index 3698da49d..172cbd7d4 100644 --- a/test/javax/management/remote/mandatory/notif/NotificationBufferDeadlockTest.java +++ b/test/javax/management/remote/mandatory/notif/NotificationBufferDeadlockTest.java @@ -35,7 +35,9 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.MalformedURLException; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.Vector; import javax.management.*; @@ -88,6 +90,7 @@ import javax.management.remote.*; * If the logic for adding the notification buffer's listener is incorrect * we could remove zero or two notifications from an MBean. */ +import javax.management.remote.rmi.RMIConnectorServer; public class NotificationBufferDeadlockTest { public static void main(String[] args) throws Exception { System.out.println("Check no deadlock if notif sent while initial " + @@ -109,7 +112,13 @@ public class NotificationBufferDeadlockTest { } private static void test(String proto) throws Exception { - System.out.println("Testing protocol " + proto); + test(proto, false); + test(proto, true); + } + + private static void test(String proto, boolean eventService) throws Exception { + System.out.println("Testing protocol " + proto + " with" + + (eventService ? "" : "out") + " event service"); MBeanServer mbs = MBeanServerFactory.newMBeanServer(); ObjectName testName = newName(); DeadlockTest test = new DeadlockTest(); @@ -117,8 +126,11 @@ public class NotificationBufferDeadlockTest { JMXServiceURL url = new JMXServiceURL("service:jmx:" + proto + ":///"); JMXConnectorServer cs; try { + Map env = Collections.singletonMap( + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, + Boolean.toString(eventService)); cs = - JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); } catch (MalformedURLException e) { System.out.println("...protocol not supported, ignoring"); return; diff --git a/test/javax/management/remote/mandatory/notif/NotificationEmissionTest.java b/test/javax/management/remote/mandatory/notif/NotificationEmissionTest.java index 1c4204371..5fa0e5184 100644 --- a/test/javax/management/remote/mandatory/notif/NotificationEmissionTest.java +++ b/test/javax/management/remote/mandatory/notif/NotificationEmissionTest.java @@ -29,11 +29,16 @@ * @author Luis-Miguel Alventosa * @run clean NotificationEmissionTest * @run build NotificationEmissionTest - * @run main NotificationEmissionTest 1 - * @run main NotificationEmissionTest 2 - * @run main NotificationEmissionTest 3 - * @run main NotificationEmissionTest 4 - * @run main NotificationEmissionTest 5 + * @run main NotificationEmissionTest 1 Classic + * @run main NotificationEmissionTest 2 Classic + * @run main NotificationEmissionTest 3 Classic + * @run main NotificationEmissionTest 4 Classic + * @run main NotificationEmissionTest 5 Classic + * @run main NotificationEmissionTest 1 EventService + * @run main NotificationEmissionTest 2 EventService + * @run main NotificationEmissionTest 3 EventService + * @run main NotificationEmissionTest 4 EventService + * @run main NotificationEmissionTest 5 EventService */ import java.io.File; @@ -56,9 +61,15 @@ import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXPrincipal; import javax.management.remote.JMXServiceURL; +import javax.management.remote.rmi.RMIConnectorServer; import javax.security.auth.Subject; public class NotificationEmissionTest { + private final boolean eventService; + + public NotificationEmissionTest(boolean eventService) { + this.eventService = eventService; + } public class CustomJMXAuthenticator implements JMXAuthenticator { public Subject authenticate(Object credentials) { @@ -102,7 +113,8 @@ public class NotificationEmissionTest { List received, List expected) { if (received.size() != size) { - echo("Error: expecting " + size + " notifications"); + echo("Error: expecting " + size + " notifications, got " + + received.size()); return 1; } else { for (Notification n : received) { @@ -216,8 +228,13 @@ public class NotificationEmissionTest { // final Map env = new HashMap(); env.put("jmx.remote.authenticator", new CustomJMXAuthenticator()); - if (prop) + env.put(RMIConnectorServer.EVENT_CLIENT_DELEGATE_FORWARDER, + Boolean.toString(eventService)); + if (prop) { + echo("Setting jmx.remote.x.check.notification.emission to " + + propValue); env.put("jmx.remote.x.check.notification.emission", propValue); + } // Create the JMXServiceURL // @@ -282,9 +299,24 @@ public class NotificationEmissionTest { new Object[] {2, nb3}, new String[] {"int", "javax.management.ObjectName"}); + // If the check is effective and we're using policy.negative, + // then we should see the two notifs sent by nb2 (of which one + // has a getSource() that is nb3), but not the notif sent by nb1. + // Otherwise we should see all three notifs. If we're using the + // Event Service with a Security Manager then the logic to + // reapply the addNL permission test for every notification is + // always enabled, regardless of the value of + // jmx.remote.x.check.notification.emission. Otherwise, the + // test is only applied if that property is explicitly true. + int expectedNotifs = + ((prop || eventService) && sm && !policyPositive) ? 2 : 3; + // Wait for notifications to be emitted // - Thread.sleep(2000); + long deadline = System.currentTimeMillis() + 2000; + while (li.notifs.size() < expectedNotifs && + System.currentTimeMillis() < deadline) + Thread.sleep(1); // Remove notification listener // @@ -297,16 +329,10 @@ public class NotificationEmissionTest { sources.add(nb2); sources.add(nb3); - if (prop && sm && !policyPositive) { - // List must contain two notifs from sources nb2 and nb3 - // - result = checkNotifs(2, li.notifs, sources); - } else { - // List must contain three notifs from sources nb1, nb2 and nb3 - // - result = checkNotifs(3, li.notifs, sources); - } + result = checkNotifs(expectedNotifs, li.notifs, sources); if (result > 0) { + echo("...SecurityManager=" + sm + "; policy=" + policyPositive + + "; eventService=" + eventService); return result; } } finally { @@ -336,9 +362,18 @@ public class NotificationEmissionTest { public static void main(String[] args) throws Exception { echo("\n--- Check the emission of notifications " + - "when a Security Manager is installed ---"); - - NotificationEmissionTest net = new NotificationEmissionTest(); + "when a Security Manager is installed [" + + args[1] + "] ---"); + + boolean eventService; + if (args[1].equals("Classic")) + eventService = false; + else if (args[1].equals("EventService")) + eventService = true; + else + throw new IllegalArgumentException(args[1]); + + NotificationEmissionTest net = new NotificationEmissionTest(eventService); int error = 0; diff --git a/test/javax/management/remote/mandatory/notif/RMINotifTest.java b/test/javax/management/remote/mandatory/notif/RMINotifTest.java index 327274c7a..12cf42b52 100644 --- a/test/javax/management/remote/mandatory/notif/RMINotifTest.java +++ b/test/javax/management/remote/mandatory/notif/RMINotifTest.java @@ -22,36 +22,53 @@ */ /* - * @test RMINotifTest.java + * @test * @bug 7654321 - * @summary Tests to receive notifications for opened and closed connect -ions + * @summary Tests to receive notifications for opened and closed connections * @author sjiang * @run clean RMINotifTest * @run build RMINotifTest - * @run main RMINotifTest + * @run main RMINotifTest classic + * @run main RMINotifTest event */ // java imports // -import java.io.IOException; -import java.net.UnknownHostException; -import java.rmi.*; -import java.rmi.registry.*; +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.util.Collections; +import java.util.Map; import java.util.Random; - -// JMX imports -// -import javax.management.* ; - -import javax.management.remote.*; -import javax.management.remote.rmi.*; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationFilterSupport; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; +import javax.management.remote.rmi.RMIConnectorServer; public class RMINotifTest { public static void main(String[] args) { + String eventService; + if (args[0].equals("classic")) + eventService = "false"; + else if (args[0].equals("event")) + eventService = "true"; + else + throw new IllegalArgumentException(args[0]); + try { // create a rmi registry Registry reg = null; @@ -88,9 +105,10 @@ public class RMINotifTest { "/jndi/rmi://:" + port + "/server" + port); System.out.println("RMIConnectorServer address " + url); + Map env = Collections.singletonMap( + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, eventService); JMXConnectorServer sServer = - JMXConnectorServerFactory.newJMXConnectorServer(url, null, - null); + JMXConnectorServerFactory.newJMXConnectorServer(url, env, null); ObjectInstance ss = server.registerMBean(sServer, new ObjectName("Default:name=RmiConnectorServer")); diff --git a/test/javax/management/remote/mandatory/notif/UnexpectedNotifTest.java b/test/javax/management/remote/mandatory/notif/UnexpectedNotifTest.java index 0b3aa2be7..23506241a 100644 --- a/test/javax/management/remote/mandatory/notif/UnexpectedNotifTest.java +++ b/test/javax/management/remote/mandatory/notif/UnexpectedNotifTest.java @@ -32,68 +32,88 @@ * @run main UnexpectedNotifTest */ -// java imports +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; // -import java.io.IOException; - -// JMX imports -// -import javax.management.*; -import javax.management.remote.*; +import javax.management.remote.rmi.RMIConnectorServer; public class UnexpectedNotifTest { public static void main(String[] args) throws Exception { - String[] protos = null; + List protos = new ArrayList(); + protos.add("rmi"); try { Class.forName("javax.management.remote.jmxmp.JMXMPConnectorServer"); - protos = new String[2]; - protos[0] = "rmi"; - protos[1] = "jmxmp"; + protos.add("jmxmp"); } catch (ClassNotFoundException e) { - protos = new String[1]; - protos[0] = "rmi"; + // OK: JMXMP not present so don't test it. } - for (int i = 0; i < protos.length; i++) { - System.out.println("Unexpected notifications test for protocol " + - protos[i]); - MBeanServer mbs = null; - try { - // Create a MBeanServer - // - mbs = MBeanServerFactory.createMBeanServer(); - - // Create a NotificationEmitter MBean - // - mbean = new ObjectName ("Default:name=NotificationEmitter"); - mbs.registerMBean(new NotificationEmitter(), mbean); - - // Create a connector server - // - url = new JMXServiceURL("service:jmx:" + protos[i] + "://"); - server = JMXConnectorServerFactory.newJMXConnectorServer(url, - null, - mbs); - - mbs.registerMBean( - server, - new ObjectName("Default:name=ConnectorServer")); - - server.start(); - - url = server.getAddress(); - - for (int j = 0; j < 2; j++) { - test(); - } - } finally { - // Stop server - // - server.stop(); - // Release the MBeanServer - // - MBeanServerFactory.releaseMBeanServer(mbs); + for (String proto : protos) { + test(proto, false); + test(proto, true); + } + } + + private static void test(String proto, boolean eventService) + throws Exception { + System.out.println("Unexpected notifications test for protocol " + + proto + " with" + + (eventService ? "" : "out") + " event service"); + MBeanServer mbs = null; + try { + // Create a MBeanServer + // + mbs = MBeanServerFactory.createMBeanServer(); + + // Create a NotificationEmitter MBean + // + mbean = new ObjectName ("Default:name=NotificationEmitter"); + mbs.registerMBean(new NotificationEmitter(), mbean); + + // Create a connector server + // + url = new JMXServiceURL("service:jmx:" + proto + "://"); + Map env = Collections.singletonMap( + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, + Boolean.toString(eventService)); + + server = JMXConnectorServerFactory.newJMXConnectorServer(url, + env, + mbs); + + mbs.registerMBean( + server, + new ObjectName("Default:name=ConnectorServer")); + + server.start(); + + url = server.getAddress(); + + for (int j = 0; j < 2; j++) { + test(); } + } finally { + // Stop server + // + server.stop(); + // Release the MBeanServer + // + MBeanServerFactory.releaseMBeanServer(mbs); } } -- GitLab From 6ce5ae980e560d401271c2f4adc3644fd6b3f0e0 Mon Sep 17 00:00:00 2001 From: dfuchs Date: Thu, 31 Jul 2008 17:38:55 +0200 Subject: [PATCH 028/139] 6616825: JMX query returns no value in 1.0 compatibility mode - deserialization bug in readObject() Reviewed-by: emcmanus --- .../classes/javax/management/ObjectName.java | 19 +- .../ObjectName/SerialCompatTest.java | 243 ++++++++++++++---- 2 files changed, 209 insertions(+), 53 deletions(-) diff --git a/src/share/classes/javax/management/ObjectName.java b/src/share/classes/javax/management/ObjectName.java index b45e9406e..d06cb35ee 100644 --- a/src/share/classes/javax/management/ObjectName.java +++ b/src/share/classes/javax/management/ObjectName.java @@ -38,9 +38,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; -import javax.management.MBeanServer; -import javax.management.MalformedObjectNameException; -import javax.management.QueryExp; /** *

    Represents the object name of an MBean, or a pattern that can @@ -1160,9 +1157,19 @@ public class ObjectName implements Comparable, QueryExp { // //in.defaultReadObject(); final ObjectInputStream.GetField fields = in.readFields(); + String propListString = + (String)fields.get("propertyListString", ""); + + // 6616825: take care of property patterns + final boolean propPattern = + fields.get("propertyPattern" , false); + if (propPattern) { + propListString = + (propListString.length()==0?"*":(propListString+",*")); + } + cn = (String)fields.get("domain", "default")+ - ":"+ - (String)fields.get("propertyListString", ""); + ":"+ propListString; } else { // Read an object serialized in the new serial form // @@ -1796,6 +1803,7 @@ public class ObjectName implements Comparable, QueryExp { * @return True if object is an ObjectName whose * canonical form is equal to that of this ObjectName. */ + @Override public boolean equals(Object object) { // same object case @@ -1819,6 +1827,7 @@ public class ObjectName implements Comparable, QueryExp { * Returns a hash code for this object name. * */ + @Override public int hashCode() { return _canonicalName.hashCode(); } diff --git a/test/javax/management/ObjectName/SerialCompatTest.java b/test/javax/management/ObjectName/SerialCompatTest.java index baa748a5f..a18a68876 100644 --- a/test/javax/management/ObjectName/SerialCompatTest.java +++ b/test/javax/management/ObjectName/SerialCompatTest.java @@ -23,9 +23,9 @@ /* * @test - * @bug 6211220 + * @bug 6211220 6616825 * @summary Test that jmx.serial.form=1.0 works for ObjectName - * @author Eamonn McManus + * @author Eamonn McManus, Daniel Fuchs * @run clean SerialCompatTest * @run build SerialCompatTest * @run main/othervm SerialCompatTest @@ -36,19 +36,8 @@ import java.util.*; import javax.management.ObjectName; public class SerialCompatTest { - public static void main(String[] args) throws Exception { - System.setProperty("jmx.serial.form", "1.0"); - /* Check that we really are in jmx.serial.form=1.0 mode. - The property is frozen the first time the ObjectName class - is referenced so checking that it is set to the correct - value now is not enough. */ - ObjectStreamClass osc = ObjectStreamClass.lookup(ObjectName.class); - if (osc.getFields().length != 6) { - throw new Exception("Not using old serial form: fields: " + - Arrays.asList(osc.getFields())); - // new serial form has no fields, uses writeObject - } + public static void check6211220() throws Exception { ObjectName on = new ObjectName("a:b=c"); ByteArrayOutputStream bos = new ByteArrayOutputStream(); @@ -62,56 +51,214 @@ public class SerialCompatTest { // if the bug is present, these will get NullPointerException for (int i = 0; i <= 11; i++) { + String msg = "6211220 case(" + i + ")"; + try { + switch (i) { + case 0: + check(msg, on1.getDomain().equals("a")); + break; + case 1: + check(msg, on1.getCanonicalName().equals("a:b=c")); + break; + case 2: + check(msg, on1.getKeyPropertyListString() + .equals("b=c")); + break; + case 3: + check(msg, on1.getCanonicalKeyPropertyListString() + .equals("b=c")); + break; + case 4: + check(msg, on1.getKeyProperty("b").equals("c")); + break; + case 5: + check(msg, on1.getKeyPropertyList() + .equals(Collections.singletonMap("b", "c"))); + break; + case 6: + check(msg, !on1.isDomainPattern()); + break; + case 7: + check(msg, !on1.isPattern()); + break; + case 8: + check(msg, !on1.isPropertyPattern()); + break; + case 9: + check(msg, on1.equals(on)); + break; + case 10: + check(msg, on.equals(on1)); + break; + case 11: + check(msg, on1.apply(on)); + break; + default: + throw new Exception(msg + ": Test incorrect"); + } + } catch (Exception e) { + System.out.println(msg + ": Test failed with exception:"); + e.printStackTrace(System.out); + failed = true; + } + } + + if (failed) { + throw new Exception("Some tests for 6211220 failed"); + } else { + System.out.println("All tests for 6211220 passed"); + } + } + + static void checkName(String testname, ObjectName on) + throws Exception { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(on); + oos.close(); + byte[] bytes = bos.toByteArray(); + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bis); + ObjectName on1 = (ObjectName) ois.readObject(); + // if the bug is present, these will get NullPointerException + for (int i = 0; i <= 11; i++) { + String msg = testname + " case(" + i + ")"; try { switch (i) { - case 0: - check(on1.getDomain().equals("a")); break; - case 1: - check(on1.getCanonicalName().equals("a:b=c")); break; - case 2: - check(on1.getKeyPropertyListString().equals("b=c")); break; - case 3: - check(on1.getCanonicalKeyPropertyListString().equals("b=c")); - break; - case 4: - check(on1.getKeyProperty("b").equals("c")); break; - case 5: - check(on1.getKeyPropertyList() - .equals(Collections.singletonMap("b", "c"))); break; - case 6: - check(!on1.isDomainPattern()); break; - case 7: - check(!on1.isPattern()); break; - case 8: - check(!on1.isPropertyPattern()); break; - case 9: - check(on1.equals(on)); break; - case 10: - check(on.equals(on1)); break; - case 11: - check(on1.apply(on)); break; - default: - throw new Exception("Test incorrect: case: " + i); + case 0: + check(msg, on1.getDomain().equals(on.getDomain())); + break; + case 1: + check(msg, on1.getCanonicalName(). + equals(on.getCanonicalName())); + break; + case 2: + check(msg, on1.getKeyPropertyListString(). + equals(on.getKeyPropertyListString())); + break; + case 3: + check(msg, on1.getCanonicalKeyPropertyListString(). + equals(on.getCanonicalKeyPropertyListString())); + break; + case 4: + for (Object ko : on1.getKeyPropertyList().keySet()) { + final String key = (String) ko; + check(msg, on1.getKeyProperty(key). + equals(on.getKeyProperty(key))); + } + for (Object ko : on.getKeyPropertyList().keySet()) { + final String key = (String) ko; + check(msg, on1.getKeyProperty(key). + equals(on.getKeyProperty(key))); + } + case 5: + check(msg, on1.getKeyPropertyList() + .equals(on.getKeyPropertyList())); + break; + case 6: + check(msg, on1.isDomainPattern()==on.isDomainPattern()); + break; + case 7: + check(msg, on1.isPattern() == on.isPattern()); + break; + case 8: + check(msg, + on1.isPropertyPattern()==on.isPropertyPattern()); + break; + case 9: + check(msg, on1.equals(on)); + break; + case 10: + check(msg, on.equals(on1)); + break; + case 11: + if (!on.isPattern()) { + check(msg, on1.apply(on)); + } + break; + default: + throw new Exception("Test incorrect: case: " + i); } } catch (Exception e) { - System.out.println("Test failed with exception:"); + System.out.println("Test (" + i + ") failed with exception:"); e.printStackTrace(System.out); failed = true; } } - if (failed) + } + private static String[] names6616825 = { + "a:b=c", "a:b=c,*", "*:*", ":*", ":b=c", ":b=c,*", + "a:*,b=c", ":*", ":*,b=c", "*x?:k=\"x\\*z\"", "*x?:k=\"x\\*z\",*", + "*x?:*,k=\"x\\*z\"", "*x?:k=\"x\\*z\",*,b=c" + }; + + static void check6616825() throws Exception { + System.out.println("Testing 616825"); + for (String n : names6616825) { + final ObjectName on; + try { + on = new ObjectName(n); + } catch (Exception x) { + failed = true; + System.out.println("Unexpected failure for 6616825 [" + n + + "]: " + x); + x.printStackTrace(System.out); + continue; + } + try { + checkName("616825 " + n, on); + } catch (Exception x) { + failed = true; + System.out.println("6616825 failed for [" + n + "]: " + x); + x.printStackTrace(System.out); + } + } + + if (failed) { + throw new Exception("Some tests for 6616825 failed"); + } else { + System.out.println("All tests for 6616825 passed"); + } + } + + public static void main(String[] args) throws Exception { + System.setProperty("jmx.serial.form", "1.0"); + + /* Check that we really are in jmx.serial.form=1.0 mode. + The property is frozen the first time the ObjectName class + is referenced so checking that it is set to the correct + value now is not enough. */ + ObjectStreamClass osc = ObjectStreamClass.lookup(ObjectName.class); + if (osc.getFields().length != 6) { + throw new Exception("Not using old serial form: fields: " + + Arrays.asList(osc.getFields())); + // new serial form has no fields, uses writeObject + } + + try { + check6211220(); + } catch (Exception x) { + System.err.println(x.getMessage()); + } + try { + check6616825(); + } catch (Exception x) { + System.err.println(x.getMessage()); + } + + if (failed) { throw new Exception("Some tests failed"); - else + } else { System.out.println("All tests passed"); + } } - private static void check(boolean condition) { + private static void check(String msg, boolean condition) { if (!condition) { - new Throwable("Test failed").printStackTrace(System.out); + new Throwable("Test failed " + msg).printStackTrace(System.out); failed = true; } } - private static boolean failed; } -- GitLab From d28c93bd9573751f4c7aa045f433e345cbc20471 Mon Sep 17 00:00:00 2001 From: martin Date: Fri, 1 Aug 2008 00:38:02 -0700 Subject: [PATCH 029/139] 6730380: java.util.Timer should use AtomicInteger Reviewed-by: dl, chegar --- src/share/classes/java/util/Timer.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/share/classes/java/util/Timer.java b/src/share/classes/java/util/Timer.java index 224420972..5c4d3bf71 100644 --- a/src/share/classes/java/util/Timer.java +++ b/src/share/classes/java/util/Timer.java @@ -25,6 +25,7 @@ package java.util; import java.util.Date; +import java.util.concurrent.atomic.AtomicInteger; /** * A facility for threads to schedule tasks for future execution in a @@ -116,12 +117,11 @@ public class Timer { }; /** - * This ID is used to generate thread names. (It could be replaced - * by an AtomicInteger as soon as they become available.) + * This ID is used to generate thread names. */ - private static int nextSerialNumber = 0; - private static synchronized int serialNumber() { - return nextSerialNumber++; + private static AtomicInteger nextSerialNumber = new AtomicInteger(0); + private static int serialNumber() { + return nextSerialNumber.getAndIncrement(); } /** -- GitLab From c514cf68bbd3d25f4fee8e0656510dae4515e43e Mon Sep 17 00:00:00 2001 From: dl Date: Fri, 1 Aug 2008 00:42:43 -0700 Subject: [PATCH 030/139] 6725789: ScheduledExecutorService does not work as expected in jdk7/6/5 Reviewed-by: martin, dholmes, chegar --- .../ScheduledThreadPoolExecutor.java | 55 ++++-- .../DelayOverflow.java | 161 ++++++++++++++++++ 2 files changed, 201 insertions(+), 15 deletions(-) create mode 100644 test/java/util/concurrent/ScheduledThreadPoolExecutor/DelayOverflow.java diff --git a/src/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java b/src/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java index dc7fe113f..67a8b3220 100644 --- a/src/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java +++ b/src/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java @@ -223,8 +223,7 @@ public class ScheduledThreadPoolExecutor } public long getDelay(TimeUnit unit) { - long d = unit.convert(time - now(), TimeUnit.NANOSECONDS); - return d; + return unit.convert(time - now(), TimeUnit.NANOSECONDS); } public int compareTo(Delayed other) { @@ -264,7 +263,7 @@ public class ScheduledThreadPoolExecutor if (p > 0) time += p; else - time = now() - p; + time = triggerTime(-p); } public boolean cancel(boolean mayInterruptIfRunning) { @@ -472,6 +471,38 @@ public class ScheduledThreadPoolExecutor new DelayedWorkQueue(), threadFactory, handler); } + /** + * Returns the trigger time of a delayed action. + */ + private long triggerTime(long delay, TimeUnit unit) { + return triggerTime(unit.toNanos((delay < 0) ? 0 : delay)); + } + + /** + * Returns the trigger time of a delayed action. + */ + long triggerTime(long delay) { + return now() + + ((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay)); + } + + /** + * Constrains the values of all delays in the queue to be within + * Long.MAX_VALUE of each other, to avoid overflow in compareTo. + * This may occur if a task is eligible to be dequeued, but has + * not yet been, while some other task is added with a delay of + * Long.MAX_VALUE. + */ + private long overflowFree(long delay) { + Delayed head = (Delayed) super.getQueue().peek(); + if (head != null) { + long headDelay = head.getDelay(TimeUnit.NANOSECONDS); + if (headDelay < 0 && (delay - headDelay < 0)) + delay = Long.MAX_VALUE + headDelay; + } + return delay; + } + /** * @throws RejectedExecutionException {@inheritDoc} * @throws NullPointerException {@inheritDoc} @@ -481,10 +512,9 @@ public class ScheduledThreadPoolExecutor TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); - if (delay < 0) delay = 0; - long triggerTime = now() + unit.toNanos(delay); RunnableScheduledFuture t = decorateTask(command, - new ScheduledFutureTask(command, null, triggerTime)); + new ScheduledFutureTask(command, null, + triggerTime(delay, unit))); delayedExecute(t); return t; } @@ -498,10 +528,9 @@ public class ScheduledThreadPoolExecutor TimeUnit unit) { if (callable == null || unit == null) throw new NullPointerException(); - if (delay < 0) delay = 0; - long triggerTime = now() + unit.toNanos(delay); RunnableScheduledFuture t = decorateTask(callable, - new ScheduledFutureTask(callable, triggerTime)); + new ScheduledFutureTask(callable, + triggerTime(delay, unit))); delayedExecute(t); return t; } @@ -519,12 +548,10 @@ public class ScheduledThreadPoolExecutor throw new NullPointerException(); if (period <= 0) throw new IllegalArgumentException(); - if (initialDelay < 0) initialDelay = 0; - long triggerTime = now() + unit.toNanos(initialDelay); ScheduledFutureTask sft = new ScheduledFutureTask(command, null, - triggerTime, + triggerTime(initialDelay, unit), unit.toNanos(period)); RunnableScheduledFuture t = decorateTask(command, sft); sft.outerTask = t; @@ -545,12 +572,10 @@ public class ScheduledThreadPoolExecutor throw new NullPointerException(); if (delay <= 0) throw new IllegalArgumentException(); - if (initialDelay < 0) initialDelay = 0; - long triggerTime = now() + unit.toNanos(initialDelay); ScheduledFutureTask sft = new ScheduledFutureTask(command, null, - triggerTime, + triggerTime(initialDelay, unit), unit.toNanos(-delay)); RunnableScheduledFuture t = decorateTask(command, sft); sft.outerTask = t; diff --git a/test/java/util/concurrent/ScheduledThreadPoolExecutor/DelayOverflow.java b/test/java/util/concurrent/ScheduledThreadPoolExecutor/DelayOverflow.java new file mode 100644 index 000000000..9df0235ec --- /dev/null +++ b/test/java/util/concurrent/ScheduledThreadPoolExecutor/DelayOverflow.java @@ -0,0 +1,161 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6725789 + * @summary Check for long overflow in task time comparison. + */ + +import java.util.concurrent.*; + +public class DelayOverflow { + static void waitForNanoTimeTick() { + for (long t0 = System.nanoTime(); t0 == System.nanoTime(); ) + ; + } + + void scheduleNow(ScheduledThreadPoolExecutor pool, + Runnable r, int how) { + switch (how) { + case 0: + pool.schedule(r, 0, TimeUnit.MILLISECONDS); + break; + case 1: + pool.schedule(Executors.callable(r), 0, TimeUnit.DAYS); + break; + case 2: + pool.scheduleWithFixedDelay(r, 0, 1000, TimeUnit.NANOSECONDS); + break; + case 3: + pool.scheduleAtFixedRate(r, 0, 1000, TimeUnit.MILLISECONDS); + break; + default: + fail(String.valueOf(how)); + } + } + + void scheduleAtTheEndOfTime(ScheduledThreadPoolExecutor pool, + Runnable r, int how) { + switch (how) { + case 0: + pool.schedule(r, Long.MAX_VALUE, TimeUnit.MILLISECONDS); + break; + case 1: + pool.schedule(Executors.callable(r), Long.MAX_VALUE, TimeUnit.DAYS); + break; + case 2: + pool.scheduleWithFixedDelay(r, Long.MAX_VALUE, 1000, TimeUnit.NANOSECONDS); + break; + case 3: + pool.scheduleAtFixedRate(r, Long.MAX_VALUE, 1000, TimeUnit.MILLISECONDS); + break; + default: + fail(String.valueOf(how)); + } + } + + /** + * Attempts to test exhaustively and deterministically, all 20 + * possible ways that one task can be scheduled in the maximal + * distant future, while at the same time an existing tasks's time + * has already expired. + */ + void test(String[] args) throws Throwable { + for (int nowHow = 0; nowHow < 4; nowHow++) { + for (int thenHow = 0; thenHow < 4; thenHow++) { + + final ScheduledThreadPoolExecutor pool + = new ScheduledThreadPoolExecutor(1); + final CountDownLatch runLatch = new CountDownLatch(1); + final CountDownLatch busyLatch = new CountDownLatch(1); + final CountDownLatch proceedLatch = new CountDownLatch(1); + final Runnable notifier = new Runnable() { + public void run() { runLatch.countDown(); }}; + final Runnable neverRuns = new Runnable() { + public void run() { fail(); }}; + final Runnable keepPoolBusy = new Runnable() { + public void run() { + try { + busyLatch.countDown(); + proceedLatch.await(); + } catch (Throwable t) { unexpected(t); } + }}; + pool.schedule(keepPoolBusy, 0, TimeUnit.SECONDS); + busyLatch.await(); + scheduleNow(pool, notifier, nowHow); + waitForNanoTimeTick(); + scheduleAtTheEndOfTime(pool, neverRuns, thenHow); + proceedLatch.countDown(); + + check(runLatch.await(10L, TimeUnit.SECONDS)); + equal(runLatch.getCount(), 0L); + + pool.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + pool.shutdown(); + } + + final int nowHowCopy = nowHow; + final ScheduledThreadPoolExecutor pool + = new ScheduledThreadPoolExecutor(1); + final CountDownLatch runLatch = new CountDownLatch(1); + final Runnable notifier = new Runnable() { + public void run() { runLatch.countDown(); }}; + final Runnable scheduleNowScheduler = new Runnable() { + public void run() { + try { + scheduleNow(pool, notifier, nowHowCopy); + waitForNanoTimeTick(); + } catch (Throwable t) { unexpected(t); } + }}; + pool.scheduleWithFixedDelay(scheduleNowScheduler, + 0, Long.MAX_VALUE, + TimeUnit.NANOSECONDS); + + check(runLatch.await(10L, TimeUnit.SECONDS)); + equal(runLatch.getCount(), 0L); + + pool.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + pool.shutdown(); + } + } + + //--------------------- Infrastructure --------------------------- + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) pass(); + else fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + Class k = new Object(){}.getClass().getEnclosingClass(); + try {k.getMethod("instanceMain",String[].class) + .invoke( k.newInstance(), (Object) args);} + catch (Throwable e) {throw e.getCause();}} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} -- GitLab From 092c3f3303db9da8fd8f172cae649fa624d08c7c Mon Sep 17 00:00:00 2001 From: dfuchs Date: Fri, 1 Aug 2008 11:41:59 +0200 Subject: [PATCH 031/139] 6732192: CORE_PKGS.gmk: need to declare javax.management.event in the CORE_PKGS variable Reviewed-by: emcmanus --- make/docs/CORE_PKGS.gmk | 1 + 1 file changed, 1 insertion(+) diff --git a/make/docs/CORE_PKGS.gmk b/make/docs/CORE_PKGS.gmk index c768e7023..dfedc7e12 100644 --- a/make/docs/CORE_PKGS.gmk +++ b/make/docs/CORE_PKGS.gmk @@ -155,6 +155,7 @@ CORE_PKGS = \ javax.lang.model.type \ javax.lang.model.util \ javax.management \ + javax.management.event \ javax.management.loading \ javax.management.monitor \ javax.management.relation \ -- GitLab From 68172430eb0ea342954d403e6d1cb264c65f8c54 Mon Sep 17 00:00:00 2001 From: jjh Date: Fri, 1 Aug 2008 13:58:29 -0700 Subject: [PATCH 032/139] 6730273: TEST: JDI_REGRESSION test Solaris32AndSolaris64Test.sh fails if -XX:+UseCompressedOops is used Summary: Fix test to not pass -XX:[+-]UseCompressedOops to the debuggee. Reviewed-by: tbell --- test/com/sun/jdi/Solaris32AndSolaris64Test.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/com/sun/jdi/Solaris32AndSolaris64Test.sh b/test/com/sun/jdi/Solaris32AndSolaris64Test.sh index 5ddb871b2..37be2781d 100644 --- a/test/com/sun/jdi/Solaris32AndSolaris64Test.sh +++ b/test/com/sun/jdi/Solaris32AndSolaris64Test.sh @@ -25,7 +25,7 @@ # # @test Solaris32AndSolaris64Test.sh -# @bug 4478312 4780570 4913748 +# @bug 4478312 4780570 4913748 6730273 # @summary Test debugging with mixed 32/64bit VMs. # @author Tim Bell # Based on test/java/awt/TEMPLATE/AutomaticShellTest.sh @@ -177,8 +177,14 @@ filename=$TESTCLASSES/@debuggeeVMOptions if [ ! -r ${filename} ] ; then filename=$TESTCLASSES/../@debuggeeVMOptions fi +# Remove -d32, -d64 if present, and remove -XX:[+-]UseCompressedOops +# if present since it is illegal in 32 bit mode. if [ -r ${filename} ] ; then - DEBUGGEEFLAGS=`cat ${filename} | sed -e 's/-d32//g' -e 's/-d64//g'` + DEBUGGEEFLAGS=`cat ${filename} | sed \ + -e 's/-d32//g' \ + -e 's/-d64//g' \ + -e 's/-XX:.UseCompressedOops//g' \ + ` fi # -- GitLab From e6ee3891437bcda793f030827af6c003a7d1af31 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Tue, 5 Aug 2008 10:49:58 +0200 Subject: [PATCH 033/139] 6733589: Intermittent failure of test/javax/management/eventService/SharingThreadTest.java Reviewed-by: sjiang --- .../eventService/SharingThreadTest.java | 115 +++++++++--------- 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/test/javax/management/eventService/SharingThreadTest.java b/test/javax/management/eventService/SharingThreadTest.java index e3bf01b30..a3d7fd37a 100644 --- a/test/javax/management/eventService/SharingThreadTest.java +++ b/test/javax/management/eventService/SharingThreadTest.java @@ -32,8 +32,6 @@ */ import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; @@ -60,23 +58,22 @@ import javax.management.remote.JMXServiceURL; public class SharingThreadTest { private static MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer(); - private static List notifList = new ArrayList(); private static ObjectName emitter; private static NotificationEmitter emitterImpl; private static JMXServiceURL url; private static JMXConnectorServer server; - private static JMXConnector conn; private static int toSend = 10; - private static long sequenceNumber = 0; private static final long bigWaiting = 6000; private static int counter = 0; private static int jobs = 10; private static int endedJobs = 0; + private static volatile String failure; + private static Executor sharedExecutor = new ThreadPoolExecutor(0, 1, 1000, - TimeUnit.MILLISECONDS, new ArrayBlockingQueue(jobs)); + TimeUnit.MILLISECONDS, new ArrayBlockingQueue(jobs)); //Executors.newFixedThreadPool(1); public static void main(String[] args) throws Exception { @@ -93,7 +90,7 @@ public class SharingThreadTest { EventClientDelegateMBean.OBJECT_NAME); sharedExecutor = new ThreadPoolExecutor(1, 1, 1000, - TimeUnit.MILLISECONDS, new ArrayBlockingQueue(jobs)); + TimeUnit.MILLISECONDS, new ArrayBlockingQueue(jobs)); } emitter = new ObjectName("Default:name=NotificationEmitter"); @@ -133,35 +130,16 @@ public class SharingThreadTest { noise.setDaemon(true); noise.start(); - Thread[] threads = new Thread[jobs]; try { for (String type: types) { System.out.println("\n\n>>> Testing "+type+" on "+url+" ..."); - newConn(); - for (int i=0; i 0) { - SharingThreadTest.class.wait(toWait); - toWait = stopTime - System.currentTimeMillis(); - } - } - - if (endedJobs != jobs) { - throw new RuntimeException("Need to set bigger waiting timeout?"); + JMXConnector conn = newConn(); + try { + testType(type, conn); + } finally { + conn.close(); + System.out.println(">>> Testing "+type+" on "+url+" ... done"); } - - endedJobs = 0; - conn.close(); - System.out.println(">>> Testing "+type+" on "+url+" ... done"); } } finally { server.stop(); @@ -169,13 +147,40 @@ public class SharingThreadTest { } } + private static void testType(String type, JMXConnector conn) throws Exception { + Thread[] threads = new Thread[jobs]; + for (int i=0; i 0 && failure == null) { + SharingThreadTest.class.wait(toWait); + toWait = stopTime - System.currentTimeMillis(); + } + } + + if (endedJobs != jobs && failure == null) { + throw new RuntimeException("Need to set bigger waiting timeout?"); + } + + endedJobs = 0; + } + public static class Job implements Runnable { - public Job(String type) { + public Job(String type, JMXConnector conn) { this.type = type; + this.conn = conn; } public void run() { try { - test(type); + test(type, conn); synchronized(SharingThreadTest.class) { endedJobs++; @@ -184,6 +189,7 @@ public class SharingThreadTest { } } } catch (RuntimeException re) { + re.printStackTrace(System.out); throw re; } catch (Exception e) { throw new RuntimeException(e); @@ -191,16 +197,17 @@ public class SharingThreadTest { } private final String type; + private final JMXConnector conn; } - private static void test(String type) throws Exception { + private static void test(String type, JMXConnector conn) throws Exception { String id = getId(); Listener listener = new Listener(id); Filter filter = new Filter(id); //newConn(); - EventClient ec = newEventClient(type); + EventClient ec = newEventClient(type, conn); System.out.println(">>> ("+id+") To receive notifications "+toSend); ec.addNotificationListener(emitter, @@ -213,8 +220,7 @@ public class SharingThreadTest { +toSend+", but got: "+listener.received); } - // not close the EventClient to keep using thread - //ec.close(); + ec.close(); } //-------------------------- @@ -232,16 +238,16 @@ public class SharingThreadTest { System.exit(1); } System.out.println("("+id+") received "+notif.getSequenceNumber()); - synchronized (notifList) { + synchronized (this) { received++; if (sequenceNB < 0) { sequenceNB = notif.getSequenceNumber(); } else if(++sequenceNB != notif.getSequenceNumber()) { - throw new RuntimeException("Wrong sequence number, expecte: " + fail("(" + id + ") Wrong sequence number, expected: " +sequenceNB+", but got: "+notif.getSequenceNumber()); } - if (received >= toSend) { + if (received >= toSend || failure != null) { this.notify(); } } @@ -251,20 +257,13 @@ public class SharingThreadTest { long toWait = timeout; long stopTime = System.currentTimeMillis() + timeout; synchronized(this) { - while (received < nb && toWait > 0) { + while (received < nb && toWait > 0 && failure == null) { this.wait(toWait); toWait = stopTime - System.currentTimeMillis(); } } } - public void clear() { - synchronized(this) { - received = 0; - sequenceNB = -1; - } - } - private String id; private int received = 0; @@ -282,11 +281,6 @@ public class SharingThreadTest { private String id; } - private static NotificationListener dummyListener = new NotificationListener() { - public void handleNotification(Notification notif, Object handback) { - } - }; - public static class NotificationEmitter extends NotificationBroadcasterSupport implements NotificationEmitterMBean { @@ -309,6 +303,7 @@ public class SharingThreadTest { if (userData != null) { System.out.println(">>> ("+userData+") sending "+nb); } + long sequenceNumber = 0; for (int i = 0; i Date: Wed, 6 Aug 2008 08:11:49 +0800 Subject: [PATCH 034/139] 6731685: CertificateFactory.generateCertificates throws IOException on PKCS7 cert chain Reviewed-by: mullan --- .../security/util/DerIndefLenConverter.java | 15 ++++++- .../security/util/DerValue/Indefinite.java | 44 +++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 test/sun/security/util/DerValue/Indefinite.java diff --git a/src/share/classes/sun/security/util/DerIndefLenConverter.java b/src/share/classes/sun/security/util/DerIndefLenConverter.java index 20a574545..c94f943b8 100644 --- a/src/share/classes/sun/security/util/DerIndefLenConverter.java +++ b/src/share/classes/sun/security/util/DerIndefLenConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,6 +50,7 @@ class DerIndefLenConverter { private byte[] data, newData; private int newDataPos, dataPos, dataSize, index; + private int unresolved = 0; private ArrayList ndefsList = new ArrayList(); @@ -113,6 +114,7 @@ class DerIndefLenConverter { numOfEncapsulatedLenBytes; byte[] sectionLenBytes = getLengthBytes(sectionLen); ndefsList.set(index, sectionLenBytes); + unresolved--; // Add the number of bytes required to represent this section // to the total number of length bytes, @@ -149,6 +151,7 @@ class DerIndefLenConverter { int lenByte = data[dataPos++] & 0xff; if (isIndefinite(lenByte)) { ndefsList.add(new Integer(dataPos)); + unresolved++; return curLen; } if (isLongForm(lenByte)) { @@ -308,15 +311,21 @@ class DerIndefLenConverter { dataPos=0; index=0; dataSize = data.length; int len=0; + int unused = 0; // parse and set up the vectors of all the indefinite-lengths while (dataPos < dataSize) { parseTag(); len = parseLength(); parseValue(len); + if (unresolved == 0) { + unused = dataSize - dataPos; + dataSize = dataPos; + break; + } } - newData = new byte[dataSize + numOfTotalLenBytes]; + newData = new byte[dataSize + numOfTotalLenBytes + unused]; dataPos=0; newDataPos=0; index=0; // write out the new byte array replacing all the indefinite-lengths @@ -325,6 +334,8 @@ class DerIndefLenConverter { writeTag(); writeLengthAndValue(); } + System.arraycopy(indefData, dataSize, + newData, dataSize + numOfTotalLenBytes, unused); return newData; } diff --git a/test/sun/security/util/DerValue/Indefinite.java b/test/sun/security/util/DerValue/Indefinite.java new file mode 100644 index 000000000..e6ba2f067 --- /dev/null +++ b/test/sun/security/util/DerValue/Indefinite.java @@ -0,0 +1,44 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6731685 + * @summary CertificateFactory.generateCertificates throws IOException on PKCS7 cert chain + */ + +import java.io.*; +import sun.security.util.*; + +public class Indefinite { + + public static void main(String[] args) throws Exception { + byte[] input = { + // An OCTET-STRING in 2 parts + 4, (byte)0x80, 4, 2, 'a', 'b', 4, 2, 'c', 'd', 0, 0, + // Garbage follows, may be falsely recognized as EOC + 0, 0, 0, 0 + }; + new DerValue(new ByteArrayInputStream(input)); + } +} -- GitLab From 9455da5ee4e30273ca0116456370a0392f95dc1c Mon Sep 17 00:00:00 2001 From: chegar Date: Wed, 6 Aug 2008 07:14:41 -0700 Subject: [PATCH 035/139] 6734171: java.net.NetworkInterface reports XCheck:jni warnings Summary: Removed leading "L" or trailing ";" from FindClass classname param Reviewed-by: alanb --- src/windows/native/java/net/NetworkInterface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/windows/native/java/net/NetworkInterface.c b/src/windows/native/java/net/NetworkInterface.c index a9adb1833..cccf7d440 100644 --- a/src/windows/native/java/net/NetworkInterface.c +++ b/src/windows/native/java/net/NetworkInterface.c @@ -554,11 +554,11 @@ Java_java_net_NetworkInterface_init(JNIEnv *env, jclass cls) ni_childsID = (*env)->GetFieldID(env, ni_class, "childs", "[Ljava/net/NetworkInterface;"); ni_ctor = (*env)->GetMethodID(env, ni_class, "", "()V"); - ni_iacls = (*env)->FindClass(env, "Ljava/net/InetAddress;"); + ni_iacls = (*env)->FindClass(env, "java/net/InetAddress"); ni_iacls = (*env)->NewGlobalRef(env, ni_iacls); ni_iaAddr = (*env)->GetFieldID(env, ni_iacls, "address", "I"); - ni_ia4cls = (*env)->FindClass(env, "Ljava/net/Inet4Address;"); + ni_ia4cls = (*env)->FindClass(env, "java/net/Inet4Address"); ni_ia4cls = (*env)->NewGlobalRef(env, ni_ia4cls); ni_ia4Ctor = (*env)->GetMethodID(env, ni_ia4cls, "", "()V"); -- GitLab From a8f8d6dff8cbc0acf9bf007a803aa61c3566c808 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Wed, 6 Aug 2008 18:28:53 +0200 Subject: [PATCH 036/139] 6734273: Minor updates to documentation of Custom MXBean Mappings Reviewed-by: dfuchs --- src/share/classes/javax/management/MXBean.java | 5 +++-- .../classes/javax/management/openmbean/MXBeanMapping.java | 3 +++ .../javax/management/openmbean/MXBeanMappingFactory.java | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/share/classes/javax/management/MXBean.java b/src/share/classes/javax/management/MXBean.java index cf0f70f93..1773328dd 100644 --- a/src/share/classes/javax/management/MXBean.java +++ b/src/share/classes/javax/management/MXBean.java @@ -1081,9 +1081,10 @@ public interface Node { MXBean is determined as follows.

      -
    • If an {@link JMX.MBeanOptions} argument is supplied to +

    • If a {@link JMX.MBeanOptions} argument is supplied to the {@link StandardMBean} constructor that makes an MXBean, - or to the {@link JMX#newMXBeanProxy JMX.newMXBeanProxy} + or to the {@link JMX#newMBeanProxy(MBeanServerConnection, + ObjectName, Class, JMX.MBeanOptions) JMX.newMBeanProxy} method, and the {@code MBeanOptions} object defines a non-null {@code MXBeanMappingFactory}, then that is the value of f.

    • diff --git a/src/share/classes/javax/management/openmbean/MXBeanMapping.java b/src/share/classes/javax/management/openmbean/MXBeanMapping.java index 216912a4e..339fbb21c 100644 --- a/src/share/classes/javax/management/openmbean/MXBeanMapping.java +++ b/src/share/classes/javax/management/openmbean/MXBeanMapping.java @@ -108,6 +108,9 @@ import java.lang.reflect.Type; *

      If we are unable to modify the {@code MyLinkedList} class, * we can define an {@link MXBeanMappingFactory}. See the documentation * of that class for further details.

      + * + * @see MXBean specification, section + * "Custom MXBean type mappings" */ public abstract class MXBeanMapping { private final Type javaType; diff --git a/src/share/classes/javax/management/openmbean/MXBeanMappingFactory.java b/src/share/classes/javax/management/openmbean/MXBeanMappingFactory.java index 0820bf86a..d5808c3a1 100644 --- a/src/share/classes/javax/management/openmbean/MXBeanMappingFactory.java +++ b/src/share/classes/javax/management/openmbean/MXBeanMappingFactory.java @@ -82,6 +82,9 @@ import java.lang.reflect.Type; * appears in, or we can supply the factory to a {@link * javax.management.StandardMBean StandardMBean} constructor or MXBean * proxy.

      + * + * @see MXBean specification, section + * "Custom MXBean type mappings" */ public abstract class MXBeanMappingFactory { /** -- GitLab From 661a5a8b46f3d5a426fbf1e23bc9b36aac01f4e7 Mon Sep 17 00:00:00 2001 From: swamyv Date: Wed, 6 Aug 2008 10:24:33 -0700 Subject: [PATCH 037/139] 6732441: TEST_BUG: ThreadMXBeanProxy test fails intermittently. Summary: Fixed the race condition in the test. Reviewed-by: jjh --- .../ManagementFactory/ThreadMXBeanProxy.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/test/java/lang/management/ManagementFactory/ThreadMXBeanProxy.java b/test/java/lang/management/ManagementFactory/ThreadMXBeanProxy.java index f76794f40..d26e9815d 100644 --- a/test/java/lang/management/ManagementFactory/ThreadMXBeanProxy.java +++ b/test/java/lang/management/ManagementFactory/ThreadMXBeanProxy.java @@ -60,8 +60,8 @@ public class ThreadMXBeanProxy { thread.setDaemon(true); thread.start(); - // wait until myThread acquires mutex - while (!mutex.isLocked()) { + // wait until myThread acquires mutex and lock owner is set. + while (!(mutex.isLocked() && mutex.getLockOwner() == thread)) { try { Thread.sleep(100); } catch (InterruptedException e) { @@ -73,17 +73,17 @@ public class ThreadMXBeanProxy { // validate the local access ThreadInfo[] infos = getThreadMXBean().getThreadInfo(ids, true, true); - if (ids.length != 1) { + if (infos.length != 1) { throw new RuntimeException("Returned ThreadInfo[] of length=" + - ids.length + ". Expected to be 1."); + infos.length + ". Expected to be 1."); } thread.checkThreadInfo(infos[0]); // validate the remote access infos = mbean.getThreadInfo(ids, true, true); - if (ids.length != 1) { + if (infos.length != 1) { throw new RuntimeException("Returned ThreadInfo[] of length=" + - ids.length + ". Expected to be 1."); + infos.length + ". Expected to be 1."); } thread.checkThreadInfo(infos[0]); @@ -160,8 +160,7 @@ public class ThreadMXBeanProxy { LockInfo[] syncs = info.getLockedSynchronizers(); if (syncs.length != OWNED_SYNCS) { throw new RuntimeException("Number of locked syncs = " + - syncs.length + - " not matched. Expected: " + OWNED_SYNCS); + syncs.length + " not matched. Expected: " + OWNED_SYNCS); } AbstractOwnableSynchronizer s = mutex.getSync(); String lockName = s.getClass().getName(); @@ -174,7 +173,6 @@ public class ThreadMXBeanProxy { throw new RuntimeException("LockInfo: " + syncs[0] + " IdentityHashCode not matched. Expected: " + hcode); } - } } static class Mutex implements Lock, java.io.Serializable { @@ -214,6 +212,10 @@ public class ThreadMXBeanProxy { s.defaultReadObject(); setState(0); // reset to unlocked state } + + protected Thread getLockOwner() { + return getExclusiveOwnerThread(); + } } // The sync object does all the hard work. We just forward to it. @@ -232,6 +234,8 @@ public class ThreadMXBeanProxy { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } + public Thread getLockOwner() { return sync.getLockOwner(); } + public AbstractOwnableSynchronizer getSync() { return sync; } } } -- GitLab From e84e5273d8e1d1d1ab3c5e5e658222b67fefb7fc Mon Sep 17 00:00:00 2001 From: ohair Date: Wed, 6 Aug 2008 15:02:15 -0700 Subject: [PATCH 038/139] 6728161: Add SKIP_BOOT_CYCLE feature to create boot jdk and use it during build Summary: Needed BOOT_JAR_JFLAGS. Fixed PREVIOUS_RELEASE_IMAGE. Reviewed-by: tbell --- make/com/sun/crypto/provider/Makefile | 4 ++-- make/com/sun/inputmethods/indicim/Makefile | 2 +- make/com/sun/inputmethods/thaiim/Makefile | 2 +- make/common/BuildToolJar.gmk | 2 +- make/common/Demo.gmk | 8 ++++++-- make/common/Release.gmk | 16 ++++++++-------- make/common/internal/BinaryPlugs.gmk | 6 ++++-- make/common/internal/ImportComponents.gmk | 5 +++-- make/common/shared/Defs-java.gmk | 1 + make/java/management/Makefile | 3 ++- make/javax/crypto/Makefile | 10 +++++----- make/javax/swing/beaninfo/SwingBeans.gmk | 2 +- make/sun/jconsole/Makefile | 2 +- make/sun/net/spi/nameservice/dns/Makefile | 2 +- make/sun/nio/Makefile | 2 +- make/sun/security/mscapi/Makefile | 2 +- make/sun/security/pkcs11/Makefile | 2 +- make/sun/text/Makefile | 2 +- 18 files changed, 41 insertions(+), 32 deletions(-) diff --git a/make/com/sun/crypto/provider/Makefile b/make/com/sun/crypto/provider/Makefile index 64e4320b1..f8134de14 100644 --- a/make/com/sun/crypto/provider/Makefile +++ b/make/com/sun/crypto/provider/Makefile @@ -193,7 +193,7 @@ build-jar: $(UNSIGNED_DIR)/sunjce_provider.jar $(UNSIGNED_DIR)/sunjce_provider.jar: build $(JCE_MANIFEST_FILE) $(prep-target) $(BOOT_JAR_CMD) cmf $(JCE_MANIFEST_FILE) $@ $(JAR_DIRS) \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) @@ -253,7 +253,7 @@ endif @$(CD) $(OBFUS_DIR); $(java-vm-cleanup) $(BOOT_JAR_CMD) cmf $(JCE_MANIFEST_FILE) $@ \ -C $(OBFUS_DIR)/build com \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) $(sign-target) @$(java-vm-cleanup) diff --git a/make/com/sun/inputmethods/indicim/Makefile b/make/com/sun/inputmethods/indicim/Makefile index 638854a0b..4c7dc97ee 100644 --- a/make/com/sun/inputmethods/indicim/Makefile +++ b/make/com/sun/inputmethods/indicim/Makefile @@ -71,7 +71,7 @@ $(IMJAR): $(FILES_class) $(FILES_copy) $(PROVIDER_CONF_FILE) $(BOOT_JAR_CMD) -cf $@ \ -C $(CLASSDESTDIR) com \ -C $(CLASSDESTDIR) $(SERVICESDIR) \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) clean:: diff --git a/make/com/sun/inputmethods/thaiim/Makefile b/make/com/sun/inputmethods/thaiim/Makefile index e565319aa..9b709d7a9 100644 --- a/make/com/sun/inputmethods/thaiim/Makefile +++ b/make/com/sun/inputmethods/thaiim/Makefile @@ -71,7 +71,7 @@ $(IMJAR): $(FILES_class) $(FILES_copy) $(PROVIDER_CONF_FILE) $(BOOT_JAR_CMD) -cf $@ \ -C $(CLASSDESTDIR) com \ -C $(CLASSDESTDIR) $(SERVICESDIR) \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) clean:: diff --git a/make/common/BuildToolJar.gmk b/make/common/BuildToolJar.gmk index 7d4ad4169..638e56b42 100644 --- a/make/common/BuildToolJar.gmk +++ b/make/common/BuildToolJar.gmk @@ -46,7 +46,7 @@ $(BUILDTOOL_JAR_FILE): $(BUILDTOOL_MANIFEST_FILE) \ -sourcepath $(BUILDTOOL_SOURCE_ROOT) $(BUILDTOOL_MAIN_SOURCE_FILE) $(BOOT_JAR_CMD) cfm $@ $(BUILDTOOL_MANIFEST_FILE) \ -C $(BUILDTOOLCLASSDIR) $(PKGDIR) \ - $(JAR_JFLAGS) || $(RM) $@ + $(BOOT_JAR_JFLAGS) || $(RM) $@ @$(java-vm-cleanup) # Printing out a build tool information line diff --git a/make/common/Demo.gmk b/make/common/Demo.gmk index 086a1da97..221bee186 100644 --- a/make/common/Demo.gmk +++ b/make/common/Demo.gmk @@ -251,7 +251,7 @@ $(DEMO_JAR): \ @$(DEMO_JAVAC_INPUT) $(BOOT_JAR_CMD) -cfm $@ $(DEMO_MANIFEST) \ -C $(DEMO_JAR_IMAGE) . \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) endif @@ -317,7 +317,11 @@ bundles: $(DEMO_BUILD_SRCZIP) # Applets are special, no jar file, no src.zip, everything expanded. ifdef DEMO_IS_APPLET @$(ECHO) "Expanding jar file into demos area at $(DEMO_DESTDIR)" - ( $(CD) $(DEMO_DESTDIR) && $(BOOT_JAR_CMD) -xfv $(DEMONAME).jar && $(RM) -r META-INF $(DEMONAME).jar ) + ( $(CD) $(DEMO_DESTDIR) && \ + $(BOOT_JAR_CMD) -xfv $(DEMONAME).jar \ + $(BOOT_JAR_JFLAGS) && \ + $(RM) -r META-INF $(DEMONAME).jar && \ + $(java-vm-cleanup) ) @( $(CD) $(DEMO_DESTDIR) && $(java-vm-cleanup) ) @$(ECHO) "Expanding source into demos area at $(DEMO_DESTDIR)" ( $(CD) $(DEMO_DESTDIR) && $(UNZIP) -o src.zip && $(RM) src.zip ) diff --git a/make/common/Release.gmk b/make/common/Release.gmk index 42de79f70..7b6c2f54a 100644 --- a/make/common/Release.gmk +++ b/make/common/Release.gmk @@ -662,7 +662,7 @@ $(RES_JAR_ARGLIST): $(RES_JAR_FILELIST) $(RESOURCES_JAR): $(RES_JAR_ARGLIST) $(JAR_MANIFEST_FILE) $(prep-target) $(BOOT_JAR_CMD) c0mf $(JAR_MANIFEST_FILE) $@ \ - @$(RES_JAR_ARGLIST) $(JAR_JFLAGS) + @$(RES_JAR_ARGLIST) $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) # Create jsse.jar containing SunJSSE implementation classes @@ -671,7 +671,7 @@ $(JSSE_JAR): $(JAR_MANIFEST_FILE) $(prep-target) $(BOOT_JAR_CMD) c0mf $(JAR_MANIFEST_FILE) $@ \ $(JSSE_CLASSES_DIRS:%=-C $(CLASSBINDIR) %) \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) # Create sec-bin.zip @@ -721,7 +721,7 @@ $(RT_JAR_ARGLIST): $(RT_JAR_FILELIST) $(RT_JAR): $(RT_JAR_ARGLIST) $(JAR_MANIFEST_FILE) $(prep-target) $(BOOT_JAR_CMD) c0mf $(JAR_MANIFEST_FILE) $@ \ - @$(RT_JAR_ARGLIST) $(JAR_JFLAGS) + @$(RT_JAR_ARGLIST) $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) # Meta-index construction to make core class loaders lazier @@ -955,7 +955,7 @@ initial-image-jdk:: initial-image-jdk-setup \ @# $(BOOT_JAR_CMD) c0f $(LIBDIR)/tools.jar $(addprefix \ -C $(CLASSBINDIR) , $(TOOLS)) \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) $(CP) $(LIBDIR)/tools.jar $(JDK_IMAGE_DIR)/lib/tools.jar @# @@ -968,7 +968,7 @@ initial-image-jdk:: initial-image-jdk-setup \ -Acom.sun.tools.javac.sym.Dest=$(OUTPUTDIR)/symbols/META-INF/sym/rt.jar \ $(CORE_PKGS) $(NON_CORE_PKGS) $(EXCLUDE_PROPWARN_PKGS) $(BOOT_JAR_CMD) c0f $(LIBDIR)/ct.sym \ - -C $(OUTPUTDIR)/symbols META-INF $(JAR_JFLAGS) + -C $(OUTPUTDIR)/symbols META-INF $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) $(CP) $(LIBDIR)/ct.sym $(JDK_IMAGE_DIR)/lib/ct.sym @# @@ -1132,11 +1132,11 @@ endef COMPARE_FILTER = | $(EGREP) -v /fastdebug/ | $(EGREP) -v /demo/ | $(EGREP) -v /sample/ # If a previuous image is provided, no need to install it. -ifdef PREVIOUS_JDK_IMAGE +ifdef PREVIOUS_RELEASE_IMAGE # Just use the pre-installed images - PREV_JRE_IMAGE_DIR=$(PREVIOUS_JDK_IMAGE)/jre - PREV_JDK_IMAGE_DIR=$(PREVIOUS_JDK_IMAGE) + PREV_JRE_IMAGE_DIR=$(PREVIOUS_RELEASE_IMAGE)/jre + PREV_JDK_IMAGE_DIR=$(PREVIOUS_RELEASE_IMAGE) else diff --git a/make/common/internal/BinaryPlugs.gmk b/make/common/internal/BinaryPlugs.gmk index a52b61752..bb2d64262 100644 --- a/make/common/internal/BinaryPlugs.gmk +++ b/make/common/internal/BinaryPlugs.gmk @@ -185,7 +185,8 @@ endef # import-binary-plug-file define import-binary-plug-classes @$(MKDIR) -p $(CLASSDESTDIR) @$(CAT) $1 | $(SED) -e 's/^/PLUG IMPORT: /' -($(CD) $(CLASSDESTDIR) && $(BOOT_JAR_CMD) xf $(PLUG_IMPORT_JARFILE) @$1) +($(CD) $(CLASSDESTDIR) && $(BOOT_JAR_CMD) xf $(PLUG_IMPORT_JARFILE) @$1 $(BOOT_JAR_JFLAGS) ) +($(CD) $(CLASSDESTDIR) && $(java-vm-cleanup) ) endef # import-binary-plug-classes # Import specific area classes (the classes are always created) @@ -275,7 +276,8 @@ $(PLUG_EXPORT_JARFILE): $(PLUG_TEMPDIR)/all.clist $(PLUG_TEMPDIR)/all.jargs @$(prep-target) @$(ECHO) "PLUG EXPORT: $(@F)" @$(CAT) $(PLUG_TEMPDIR)/all.clist | $(SED) -e 's/^/PLUG EXPORT: /' - $(BOOT_JAR_CMD) cf $@ @$(PLUG_TEMPDIR)/all.jargs + $(BOOT_JAR_CMD) cf $@ @$(PLUG_TEMPDIR)/all.jargs $(BOOT_JAR_JFLAGS) + @$(java-vm-cleanup) export-binary-plugs-jar: $(PLUG_EXPORT_JARFILE) # Export native libraries diff --git a/make/common/internal/ImportComponents.gmk b/make/common/internal/ImportComponents.gmk index d089f230a..d22783d65 100644 --- a/make/common/internal/ImportComponents.gmk +++ b/make/common/internal/ImportComponents.gmk @@ -107,8 +107,9 @@ endef define Unjar ( \ $(MKDIR) -p $1; \ - $(ECHO) "( $(CD) $1 && $(BOOT_JAR_CMD) xfv $2 $3 )" ; \ - ( $(CD) $1 && $(BOOT_JAR_CMD) xfv $2 $3 ) \ + $(ECHO) "( $(CD) $1 && $(BOOT_JAR_CMD) xfv $2 $3 $(BOOT_JAR_JFLAGS) )" ; \ + ( $(CD) $1 && $(BOOT_JAR_CMD) xfv $2 $3 $(BOOT_JAR_JFLAGS) ) && \ + ( $(CD) $1 && $(java-vm-cleanup) ) \ ) endef diff --git a/make/common/shared/Defs-java.gmk b/make/common/shared/Defs-java.gmk index 179e53a01..f6484f492 100644 --- a/make/common/shared/Defs-java.gmk +++ b/make/common/shared/Defs-java.gmk @@ -190,6 +190,7 @@ ifeq ($(JAVAC_WARNINGS_FATAL), true) BOOT_JAVACFLAGS += -Werror endif BOOT_JAVACFLAGS += -encoding ascii +BOOT_JAR_JFLAGS += $(JAR_JFLAGS) BOOT_JAVA_CMD = $(BOOTDIR)/bin/java $(JAVA_TOOLS_FLAGS) BOOT_JAVAC_CMD = $(BOOTDIR)/bin/javac $(JAVAC_JVM_FLAGS) $(BOOT_JAVACFLAGS) diff --git a/make/java/management/Makefile b/make/java/management/Makefile index 4d39d514e..c1c4edbdf 100644 --- a/make/java/management/Makefile +++ b/make/java/management/Makefile @@ -93,7 +93,8 @@ endif include $(BUILDDIR)/common/Library.gmk $(AGENTJAR): $(LIBDIR) $(TEMPDIR)/manifest - $(BOOT_JAR_CMD) -cfm $(AGENTJAR) $(TEMPDIR)/manifest + $(BOOT_JAR_CMD) -cfm $(AGENTJAR) $(TEMPDIR)/manifest $(BOOT_JAR_JFLAGS) + @$(java-vm-cleanup) $(TEMPDIR)/manifest: $(MANIFEST) $(install-file) diff --git a/make/javax/crypto/Makefile b/make/javax/crypto/Makefile index 86588e855..216ff6e22 100644 --- a/make/javax/crypto/Makefile +++ b/make/javax/crypto/Makefile @@ -230,7 +230,7 @@ build-jar: $(UNSIGNED_DIR)/jce.jar $(UNSIGNED_DIR)/jce.jar: prebuild build $(JCE_MANIFEST_FILE) $(prep-target) $(BOOT_JAR_CMD) cmf $(JCE_MANIFEST_FILE) $@ $(JAR_DIRS) \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) $(CP) -r $(CLASSDESTDIR)/* $(CLASSBINDIR) @$(java-vm-cleanup) @@ -268,7 +268,7 @@ $(UNSIGNED_POLICY_BUILDDIR)/unlimited/US_export_policy.jar: \ $(prep-target) $(BOOT_JAR_CMD) cmf policy/unlimited/UNLIMITED $@ \ -C policy/unlimited default_US_export.policy \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) $(UNSIGNED_POLICY_BUILDDIR)/unlimited/local_policy.jar: \ @@ -277,7 +277,7 @@ $(UNSIGNED_POLICY_BUILDDIR)/unlimited/local_policy.jar: \ $(prep-target) $(BOOT_JAR_CMD) cmf policy/unlimited/UNLIMITED $@ \ -C policy/unlimited default_local.policy \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) # @@ -302,7 +302,7 @@ $(UNSIGNED_POLICY_BUILDDIR)/limited/local_policy.jar: \ $(BOOT_JAR_CMD) cmf policy/limited/LIMITED $@ \ -C policy/limited default_local.policy \ -C policy/limited exempt_local.policy \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) UNSIGNED_POLICY_FILES = \ @@ -402,7 +402,7 @@ endif $(BOOT_JAR_CMD) cmf $(JCE_MANIFEST_FILE) $@ \ -C $(OBFUS_DIR)/build javax \ -C $(OBFUS_DIR)/build sun \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) $(sign-target) @$(java-vm-cleanup) diff --git a/make/javax/swing/beaninfo/SwingBeans.gmk b/make/javax/swing/beaninfo/SwingBeans.gmk index daead86fd..462c2509c 100644 --- a/make/javax/swing/beaninfo/SwingBeans.gmk +++ b/make/javax/swing/beaninfo/SwingBeans.gmk @@ -170,7 +170,7 @@ mkpackaging: $(BOOT_JAR_CMD) cf $(TEMPDIR)/tmp.jar \ -C $(BEANCLASSDIR) javax \ -C $(BEANCLASSDIR) sun \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) $(MV) $(TEMPDIR)/tmp.jar $(JDK_IMAGE_DIR)/lib/dt.jar @$(java-vm-cleanup) diff --git a/make/sun/jconsole/Makefile b/make/sun/jconsole/Makefile index bfbdccbfb..14d1660c2 100644 --- a/make/sun/jconsole/Makefile +++ b/make/sun/jconsole/Makefile @@ -82,7 +82,7 @@ $(JARFILE): $(LIBDIR) $(FILES_class) $(FILES_png) $(FILES_gif) $(TEMPDIR)/manife $(BOOT_JAR_CMD) -cfm $(JARFILE) $(TEMPDIR)/manifest \ -C $(CLASSBINDIR) sun/tools/jconsole \ -C $(CLASSBINDIR) com/sun/tools/jconsole \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) clean clobber:: diff --git a/make/sun/net/spi/nameservice/dns/Makefile b/make/sun/net/spi/nameservice/dns/Makefile index 0a7683f5d..a882eada9 100644 --- a/make/sun/net/spi/nameservice/dns/Makefile +++ b/make/sun/net/spi/nameservice/dns/Makefile @@ -67,7 +67,7 @@ $(JARFILE): $(CLASSDESTDIR)/META-INF/services/$(SERVICE_DESCRIPTION) \ $(BOOT_JAR_CMD) -cf $(JARFILE) \ -C $(CLASSDESTDIR) sun \ -C $(CLASSDESTDIR) META-INF \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) include $(BUILDDIR)/common/Classes.gmk diff --git a/make/sun/nio/Makefile b/make/sun/nio/Makefile index c8e8e37cb..8dd1f98cc 100644 --- a/make/sun/nio/Makefile +++ b/make/sun/nio/Makefile @@ -91,7 +91,7 @@ $(CHARSETS_JAR): $(FILES_class) $(CLASSDESTDIR)/$(SERVICE_DESCRIPTION_PATH) $(FI $(BOOT_JAR_CMD) cf $(CHARSETS_JAR) \ -C $(CLASSDESTDIR) sun \ -C $(CLASSDESTDIR) $(SERVICE_DESCRIPTION_PATH) \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) clean:: diff --git a/make/sun/security/mscapi/Makefile b/make/sun/security/mscapi/Makefile index 8223b5367..08d6609e1 100644 --- a/make/sun/security/mscapi/Makefile +++ b/make/sun/security/mscapi/Makefile @@ -210,7 +210,7 @@ build-jar: $(UNSIGNED_DIR)/sunmscapi.jar $(UNSIGNED_DIR)/sunmscapi.jar: build $(prep-target) $(BOOT_JAR_CMD) cf $@ $(JAR_DIRS) \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) diff --git a/make/sun/security/pkcs11/Makefile b/make/sun/security/pkcs11/Makefile index 4508b20f8..f20910b7e 100644 --- a/make/sun/security/pkcs11/Makefile +++ b/make/sun/security/pkcs11/Makefile @@ -210,7 +210,7 @@ build-jar: $(UNSIGNED_DIR)/sunpkcs11.jar $(UNSIGNED_DIR)/sunpkcs11.jar: build $(prep-target) $(BOOT_JAR_CMD) cf $@ $(JAR_DIRS) \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) diff --git a/make/sun/text/Makefile b/make/sun/text/Makefile index d3d49355b..f0fb4269a 100644 --- a/make/sun/text/Makefile +++ b/make/sun/text/Makefile @@ -112,7 +112,7 @@ $(CLASSDESTDIR)/sun/text/resources/% : $(TEXT_SRCDIR)/% $(LOCALEDATA_JAR): $(EXTDIR) $(FILES_class) $(BIFILES) $(SPECIALFILES) $(prep-target) $(BOOT_JAR_CMD) -cf $@ -C $(CLASSDESTDIR) sun \ - $(JAR_JFLAGS) + $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) build: $(LOCALEDATA_JAR) -- GitLab From 260bb8aea14746a27a57138cf2687fd10544924e Mon Sep 17 00:00:00 2001 From: ohair Date: Wed, 6 Aug 2008 16:21:20 -0700 Subject: [PATCH 039/139] 6724669: JDK7: Official change to Sun Studio 12 compilers on Solaris Reviewed-by: tbell --- make/README-builds.html | 1452 --------------------------- make/README.html | 28 - make/common/shared/Compiler-sun.gmk | 7 +- make/jprt.config | 4 +- 4 files changed, 3 insertions(+), 1488 deletions(-) delete mode 100644 make/README-builds.html delete mode 100644 make/README.html diff --git a/make/README-builds.html b/make/README-builds.html deleted file mode 100644 index 6530865db..000000000 --- a/make/README-builds.html +++ /dev/null @@ -1,1452 +0,0 @@ - - - -OpenJDK Build README - - -
      - -
      -

      OpenJDK Build README

      -
      - - -
      - -

      Introduction

      - -
      -

      - This README file contains build instructions for the - OpenJDK. - Building the source code for the - OpenJDK - requires - a certain degree of technical expertise. -

      - - -
      - -

      Contents

      - -
      - -
      - - -
      - -

      Minimum Build Environments

      - -
      -

      - This file often describes specific requirements for what we call the - "minimum build environments" (MBE) for the JDK. - Building with the MBE will generate the most compatible - bits that install on, and run correctly on, the most variations - of the same base OS and hardware architecture. - These usually represent what is often called the - least common denominator platforms. - It is understood that most developers will NOT be using these - specific platforms, and in fact creating these specific platforms - may be difficult due to the age of some of this software. -

      - -

      - The minimum OS and C/C++ compiler versions needed for building the - OpenJDK: -

      -

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      Base OS and ArchitectureOSCompiler
      Linux X86 (32bit)Red Hat Enterprise Linux 4 gcc 4
      Linux X64 (64bit)Red Hat Enterprise Linux 4 gcc 4
      Solaris SPARC (32bit)Solaris 10 + patches -
      - See SunSolve for patch downloads. -
      Sun Studio 11
      Solaris SPARCV9 (64bit)Solaris 10 + patches -
      - See SunSolve for patch downloads. -
      Sun Studio 11
      Solaris X86 (32bit)Solaris 10 + patches -
      - See SunSolve for patch downloads. -
      Sun Studio 11
      Solaris X64 (64bit)Solaris 10 + patches -
      - See SunSolve for patch downloads. -
      Sun Studio 11
      Windows X86 (32bit)Windows XPMicrosoft Visual Studio .NET 2003 Professional
      Windows X64 (64bit)Windows Server 2003 - Enterprise x64 EditionMicrosoft Platform SDK - April 2005
      -
      -
      - - -
      - -

      Specific Developer Build Environments

      - -
      -

      - We won't be listing all the possible environments, but - we will try to provide what information we have available to us. -

      - -

      Fedora

      - -
      - TBD -
      - -

      Debian

      - -
      - TBD -
      - -

      Ubuntu

      - -
      -

      - In addition to needing the Bootstrap JDK and the Binary Plugs, - when building on Ubuntu you will need to - make sure certain packages are installed. - In particular, certain X11 packages, make, m4, gawk, gcc 4, - binutils, cups, freetype - and alsa. - -

      Ubuntu 6.06

      - -

      - The following list of packages for Ubuntu 6.06 is a working set that - does appear to work. - -

      - Note that it's quite possible that some of these - packages are not required, so anyone discovering that some of the - packages listed below are NOT required, - please let the - OpenJDK - team know. -

      - All the packages below can be installed with the - Synaptic Package manager provided with the base Ubuntu 6.06 release. - -

      -
        -
      • binutils (2.16.1cvs20060117-1ubuntu2.1)
      • -
      • cpp (4:4.0.3-1)
      • -
      • cpp-4.0 (4.0.3-1ubuntu5)
      • -
      • libfreetype6-dev
      • -
      • g++ (4:4.0.3-1)
      • -
      • g++-4.0 (4.0.3-1ubuntu5)
      • -
      • gawk (1:3.1.5-2build1)
      • -
      • gcc (4:4.0.3-1)
      • -
      • gcc-4.0 (4.0.3-1ubuntu5)
      • -
      • libasound2-dev (1.0.10-2ubuntu4)
      • -
      • libc6 (2.3.6-0ubuntu20) to 2.3.6-0ubuntu20.4
      • -
      • libc6-dev (2.3.6-0ubuntu20.4)
      • -
      • libc6-i686 (2.3.6-0ubuntu20) to 2.3.6-0ubuntu20.4
      • -
      • libcupsys2-dev (1.2.2-0ubuntu0.6.06)
      • -
      • libgcrypt11-dev (1.2.2-1)
      • -
      • libgnutls-dev (1.2.9-2ubuntu1.1)
      • -
      • libgnutls12 (1.2.9-2ubuntu1) to 1.2.9-2ubuntu1.1
      • -
      • libgpg-error-dev (1.1-4)
      • -
      • libice-dev (2:1.0.0-0ubuntu2)
      • -
      • liblockfile1 (1.06.1)
      • -
      • libopencdk8-dev (0.5.7-2)
      • -
      • libpopt-dev (1.7-5)
      • -
      • libsm-dev (2:1.0.0-0ubuntu2)
      • -
      • libstdc++6-4.0-dev (4.0.3-1ubuntu5)
      • -
      • libtasn1-2-dev (0.2.17-1ubuntu1)
      • -
      • libx11-dev (2:1.0.0-0ubuntu9)
      • -
      • libxau-dev (1:1.0.0-0ubuntu4)
      • -
      • libxaw-headers (2:1.0.1-0ubuntu3)
      • -
      • libxaw7-dev (2:1.0.1-0ubuntu3)
      • -
      • libxdmcp-dev (1:1.0.0-0ubuntu2)
      • -
      • libxext-dev (2:1.0.0-0ubuntu4)
      • -
      • libxi-dev (2:1.0.0-0ubuntu3)
      • -
      • libxmu-dev (2:1.0.0-0ubuntu3)
      • -
      • libxmu-headers (2:1.0.0-0ubuntu3)
      • -
      • libxmuu-dev (2:1.0.0-0ubuntu3)
      • -
      • libxp-dev (6.8.2-11ubuntu2)
      • -
      • libxpm-dev (1:3.5.4.2-0ubuntu3)
      • -
      • libxrandr-dev (1:1.1.0.2-0ubuntu4)
      • -
      • libxt-dev (1:1.0.0-0ubuntu3)
      • -
      • libxtrap-dev (2:1.0.0-0ubuntu2)
      • -
      • libxtst-dev (2:1.0.1-0ubuntu2)
      • -
      • libxv-dev (2:1.0.1-0ubuntu3)
      • -
      • linux-kernel-headers (2.6.11.2-0ubuntu18)
      • -
      • m4 (1.4.4-1)
      • -
      • make (3.80+3.81.b4-1)
      • -
      • ssl-cert (1.0.13)
      • -
      • x-dev (7.0.4-0ubuntu2)
      • -
      • x11proto-core-dev (7.0.4-0ubuntu2)
      • -
      • x11proto-input-dev (1.3.2-0ubuntu2)
      • -
      • x11proto-kb-dev (1.0.2-0ubuntu2)
      • -
      • x11proto-randr-dev (1.1.2-0ubuntu2)
      • -
      • x11proto-record-dev (1.13.2-0ubuntu2)
      • -
      • x11proto-trap-dev (3.4.3-0ubuntu2)
      • -
      • x11proto-video-dev (2.2.2-0ubuntu2)
      • -
      • x11proto-xext-dev (7.0.2-0ubuntu2)
      • -
      • xlibs-dev (7.0.0-0ubuntu45)
      • -
      • zlib1g-dev (1:1.2.3-6ubuntu4)
      • -
      -
      - -

      Ubuntu 7.04

      - -

      - Using the Synaptic Package Manager, download the following - packages (double indented packages are automatically aquired - due to package dependencies): - -

      -
        -
      • build-essential
      • -
          -
        • dpkg-dev
        • -
        • g++
        • -
        • g++-4.1
        • -
        • libc6-dev
        • -
        • libstdc++6.4.1-dev
        • -
        • linux-libc-dev
        • -
        -
      • gawk
      • -
      • m4
      • -
      • libasound2-dev
      • -
      • libcupsys2-dev
      • -
          -
        • libgcrypt11-dev
        • -
        • lgnutls-dev
        • -
        • libgpg-error-dev
        • -
        • liblzo-dev
        • -
        • libopencdk8-dev
        • -
        • libpopt-dev
        • -
        • libtasn1-3-dev
        • -
        • zlib1g-dev
        • -
        -
      • sun-java6-jdk
      • -
          -
        • java-common
        • -
        • libltdl3
        • -
        • odbcinst1debian1
        • -
        • sun-java6-bin
        • -
        • sun-java6-jre
        • -
        • unixodbc
        • -
        -
      • xlibs-dev
      • -
          -
        • (many)
        • -
        -
      • x11proto-print-dev
      • -
      • libxaw7-dev
      • -
          -
        • libxaw-headers
        • -
        -
      • libxp-dev
      • -
      • libfreetype6-dev
      • -
      -
      -
      - - -
      - -

      Source Directory Structure

      - -
      -

      - The source code for the - OpenJDK is - delivered in 3 sibling directories: - hotspot, - langtools, - corba, - jaxws, - jaxp, - jdk - and - The hotspot directory contains the source code and make - files for - building the - OpenJDK - Hotspot Virtual Machine. - The jdk - directory contains the source code and make files for - building the - OpenJDK - runtime libraries, tools and demos. - The top level Makefile is used to build the complete OpenJDK - release including building the hotspot - VM, staging the VM binaries, and building the - OpenJDK - runtime libraries, - tools and demos. -

      - - -
      - -

      Build Information

      - -
      -

      - Building the - OpenJDK - is done with a gmake - command line and various - environment or make variable settings that direct the make rules - to where various components have been installed. - Where possible the makefiles will attempt to located the various - components in the default locations or any component specific - variable settings. - When the normal defaults fail or components cannot be found, - the various - ALT_* variables (alternates) - can be used to help the makefiles locate components. -

      - Refer to the bash/sh/ksh setup file - jdk/make/jdk_generic_profile.sh - if you need help in setting up your environment variables. - A build could be as simple as: -

      -
      
      -                bash
      -                . jdk/make/jdk_generic_profile.sh
      -                gmake sanity && gmake
      -        
      -
      -

      - Of course ksh or sh would work too. - But some customization will probably be necessary. - The sanity rule will make some basic checks on build - dependencies and generate appropriate warning messages - regarding missing, out of date, or newer than expected components - found on your system. -

      - - -
      - -

      GNU make (gmake)

      - -
      -

      - The Makefiles in the - OpenJDK - are only valid when used with the - GNU version of the utility command make - (gmake). - A few notes about using GNU make: -

        -
      • - In general, you need GNU make version 3.78.1 or newer. -
      • -
      • - Place the location of the GNU make binary in the PATH. -
      • -
      • - Linux: - The /usr/bin/make command should work fine for you. -
      • -
      • - Solaris: - Do NOT use /usr/bin/make on Solaris. - If your Solaris system has the software - from the Solaris Companion CD installed, - you should use gmake - which will be located in either the /opt/sfw/bin or - /usr/sfw/bin directory. -
      • -
      • - Windows: - Make sure you start your build inside a bash/sh/ksh shell. -
        - WARNING: Watch out for make version 3.81, it may - not work due to a lack of support for drive letter paths - like C:/. Use a 3.80 version, or find a newer - version that has this problem fixed. -
      • -
      -

      - Information on GNU make, and access to ftp download sites, are - available on the - - GNU make web site - . - The latest source to GNU make is available at - ftp.gnu.org/pub/gnu/make/. -

      - - -
      - -

      Basic Linux System Setup

      - -
      -

      - i586 only: - The minimum recommended hardware for building the Linux version - is a Pentium class processor or better, at least 256 MB of RAM, and - approximately 1.5 GB of free disk space. -

      - X64 only: - The minimum recommended hardware for building the Linux - version is an AMD Opteron class processor, at least 512 MB of RAM, and - approximately 4 GB of free disk space. -

      - The build will use the tools contained in - /bin and - /usr/bin - of a standard installation of the Linux operating environment. - You should ensure that these directories are in your - PATH. -

      - Note that some Linux systems have a habit of pre-populating - your environment variables for you, for example JAVA_HOME - might get pre-defined for you to refer to the JDK installed on - your Linux system. - You will need to unset JAVA_HOME. - It's a good idea to run env and verify the - environment variables you are getting from the default system - settings make sense for building the - OpenJDK. -

      - - - -

      Basic Linux Check List

      - -
      -
        -
      1. - Install the - Bootstrap JDK, set - ALT_BOOTDIR. -
      2. -
      3. - Install the - Binary Plugs, set - ALT_BINARY_PLUGS_PATH. -
      4. -
      5. - Install or upgrade the FreeType development - package. -
      6. -
      -
      - - -
      - -

      Basic Solaris System Setup

      - -
      -

      - The minimum recommended hardware for building the - Solaris SPARC version is an UltraSPARC with 512 MB of RAM. - For building - the Solaris x86 version, a Pentium class processor or better and at - least 128 MB of RAM are recommended. - Approximately 1.4 GB of free disk - space is needed for a 32-bit build. -

      - If you are building the 64bit version, you should - run the command "isainfo -v" to verify that you have a - 64-bit installation. - An additional 7 GB of free disk space is needed - for a 64-bit build. -

      - The build uses the tools contained in /usr/ccs/bin - and /usr/bin of a standard developer or full installation of - the Solaris operating environment. -

      - - - -

      Basic Solaris Check List

      - -
      -
        -
      1. - Install the - Bootstrap JDK, set - ALT_BOOTDIR. -
      2. -
      3. - Install the - Binary Plugs, set - ALT_BINARY_PLUGS_PATH. -
      4. -
      5. - Install the - Sun Studio Compilers, set - ALT_COMPILER_PATH. -
      6. -
      7. - Install the - CUPS Include files, set - ALT_CUPS_HEADERS_PATH. -
      8. -
      -
      - - -
      - -

      Basic Windows System Setup

      - -
      -

      - i586 only: - The minimum recommended hardware for building the 32bit or X86 - Windows version is an Pentium class processor or better, at least - 512 MB of RAM, and approximately 600 MB of free disk space. - - NOTE: The Windows 2000 build machines need to use the - file system NTFS. - Build machines formatted to FAT32 will not work - because FAT32 doesn't support case-sensitivity in file names. - -

      - X64 only: - The minimum recommended hardware for building - the Windows X64 version is an AMD Opteron class processor, at least 1 - GB of RAM, and approximately 10 GB of free disk space. -

      - - - -

      Windows Paths

      - -
      -

      - Windows: - Note that GNU make is a historic utility and is based very - heavily on shell scripting, so it does not tolerate the Windows habit - of having spaces in pathnames or the use of the \characters in pathnames. - Luckily on most Windows systems, you can use /instead of \, and - there is always a 'short' pathname without spaces for any path that - contains spaces. - Unfortunately, this short pathname can be somewhat dynamic and the - formula is difficult to explain. - You can use cygpath utility to map pathnames with spaces - or the \character into the C:/ style of pathname - (called 'mixed'), e.g. - cygpath -s -m "path". -

      - The makefiles will try to translate any pathnames supplied - to it into the C:/ style automatically. -

      - Note that use of CYGWIN creates a unique problem with regards to - setting PATH. Normally on Windows - the PATH variable contains directories - separated with the ";" character (Solaris and Linux uses ":"). - With CYGWIN, it uses ":", but that means that paths like "C:/path" - cannot be placed in the CYGWIN version of PATH and - instead CYGWIN uses something like /cygdrive/c/path - which CYGWIN understands, but only CYGWIN understands. - So be careful with paths on Windows. -

      - - - -

      Basic Windows Check List

      - -
      -
        -
      1. - Install the - CYGWIN product. -
      2. -
      3. - Install the - Bootstrap JDK, set - ALT_BOOTDIR. -
      4. -
      5. - Install the - Binary Plugs, set - ALT_BINARY_PLUGS_PATH.. -
      6. -
      7. - Install the - Microsoft Visual Studio .NET 2003 Professional or the - Microsoft Platform SDK. -
      8. -
      9. - Setup all environment variables for compilers - (see compilers). -
      10. -
      11. - Install - Microsoft DirectX SDK. -
      12. -
      -
      - - -
      - -

      Build Dependencies

      - -
      -

      - Depending on the platform, the - OpenJDK - build process has some basic - dependencies on components not part of the - OpenJDK - sources. - Some of these are specific to a platform, some even specific to - an architecture. - Each dependency will have a set of ALT variables that can be set - to tell the makefiles where to locate the component. - In most cases setting these ALT variables may not be necessary - and the makefiles will find defaults on the system in standard - install locations or through component specific variables. - -

      Bootstrap JDK

      - -
      -

      - All - OpenJDK - builds require access to the previously released - JDK 6, this is often called a bootstrap JDK. - The JDK 6 binaries can be downloaded from Sun's - JDK 6 download site. - For build performance reasons - is very important that this bootstrap JDK be made available on the - local disk of the machine doing the build. - You should always set - ALT_BOOTDIR - to point to the location of - the bootstrap JDK installation, this is the directory pathname - that contains a bin, lib, and include - It's also a good idea to also place its bin directory - in the PATH environment variable, although it's - not required. -

      - Solaris: - Some pre-installed JDK images may be available to you in the - directory /usr/jdk/instances. - If you don't set - ALT_BOOTDIR - the makefiles will look in that location for a JDK it can use. -

      - -

      Binary Plugs

      - -
      -

      - Not all of the source code that makes up the JDK is available - under an open-source license. - In order to build an OpenJDK binary from source code, - you must first download and install the appropriate - binary plug bundles from the OpenJDK Download area. - During the OpenJDK build process these "binary plugs" - for the encumbered components will be copied into your - resulting OpenJDK binary build image. - These binary plug files are only for the purpose of - building an OpenJDK binary. - Download the Binary Plugs by selecting the Downloads - link at - the OpenJDK site, - install the bundle, - and make sure you set - ALT_BINARY_PLUGS_PATH - to the root of this installation. -

      - -

      Certificate Authority File (cacert)

      - -
      -

      - See - www.wikipedia.org/wiki/CAcert - for a better understanding of the Certificate Authority (CA). - A certificates file named "cacerts" - represents a system-wide keystore with CA certificates. - In JDK and JRE - binary bundles, the "cacerts" file contains root CA certificates from - several public CAs (e.g., VeriSign, Thawte, and Baltimore). - The source contain a cacerts file - without CA root certificates. - Formal JDK builders will need to secure - permission from each public CA and include the certificates into their - own custom cacerts file. - Failure to provide a populated cacerts file - will result in verification errors of a certificate chain during runtime. - The variable - ALT_CACERTS_FILE - can be used to override the default location of the - cacerts file that will get placed in your build. - By default an empty cacerts file is provided and that should be - fine for most JDK developers. -

      - -

      Compilers

      - -
      - - - Linux gcc/binutils - - -
      -

      - The GNU gcc compiler version should be 3.2.2 or newer. - The binutils package should be 2.11.93.0.2-11 or newer. - The compiler used should be the default compiler installed - in /usr/bin. -

      - - Solaris: Sun Studio - -
      -

      - At a minimum, the - - Sun Studio 11 Compilers - (containing version 5.8 of the C and C++ compilers) is required, - with patches from the - - SunSolve web site. -

      - Set - ALT_COMPILER_PATH - to point to the location of - the compiler binaries, and place this location in the PATH. -

      - The Sun Studio Express compilers at: - - Sun Studio Express Download site - are also an option, although these compilers have not - been extensively used yet. -

      - - - Windows i586: Microsoft Visual Studio .NET 2003 Professional - - -
      -

      - The 32-bit - OpenJDK - Windows build - requires Microsoft Visual Studio .NET 2003 (VS2003) Professional - Edition compiler. - The compiler and other tools are expected to reside - in the location defined by the variable VS71COMNTOOLS which - is set by the Microsoft Visual Studio .NET installer. -

      - Once the compiler is installed, - it is recommended that you run VCVARS32.BAT - to set the compiler environment variables - MSVCDIR, - INCLUDE, - LIB, and - PATH - prior to building the - OpenJDK. - The above environment variables MUST be set. -

      - The Microsoft Visual Studio .NET 2005 (VS2005) compiler - will not work at this time due to the new runtime dll - and the manifest requirements. -

      - - - Windows X64: Microsoft Platform SDK April 2005 - - -
      -

      - On X64, - the Microsoft Platform Software - Development Kit (SDK), April 2005 Edition compiler, is required for - building the - OpenJDK - because it contains the C/C++ compiler. - You will need to minimally install the Core SDK and - the MDAC SDK features of this compiler. -

      - Once the Platform SDK is installed, - it is recommended that you run SetEnv.Cmd /X64 - to set the compiler environment variables - MSSDK, - MSTOOLS, - INCLUDE, - LIB, and - PATH - prior to building the - OpenJDK. - The above environment variables MUST be set. -

      - Note that this compiler may say it's version is a - Microsoft Visual Studio .NET 2005 (VS2005), but be careful, - it will not match the official VS2005 product. - This Platform SDK compiler is only used on X64 builds. -

      - -
      - -

      Common UNIX Printing System (CUPS) Headers (Solaris & Linux)

      - -
      -

      - Solaris: - CUPS header files are required for building the - OpenJDK on Solaris. - The Solaris header files can be obtained by installing - the package SFWcups from the Solaris Software - Companion CD/DVD, these often will be installed into - /opt/sfw/cups. -

      - Linux: - CUPS header files are required for building the - OpenJDK on Linux. - The Linux header files are usually available from a "cups" - development package, it's recommended that you try and use - the package provided by the particular version of Linux that - you are using. -

      - The CUPS header files can always be downloaded from - www.cups.org. - The variable - ALT_CUPS_HEADERS_PATH - can be used to override the default location of the - CUPS Header files. -

      - -

      FreeType 2

      - -
      -

      - Version 2.3 or newer of FreeType is required for building the OpenJDK. - On Unix systems required files can be available as part of your - distribution (while you still may need to upgrade them). - Note that you need development version of package that - includes both FreeType library and header files. -

      -

      - You can always download latest FreeType version from the - FreeType website. -

      -

      - Makefiles will try to pick FreeType from /usr/lib and /usr/include. - In case it is installed elsewhere you will need to set environment - variables - ALT_FREETYPE_LIB_PATH - and - ALT_FREETYPE_HEADERS_PATH - to refer to place where library and header files are installed. -

      -
      - -

      Advanced Linux Sound Architecture (ALSA) (Linux only)

      - -
      -

      - Linux only: - Version 0.9.1 or newer of the ALSA files are - required for building the - OpenJDK on Linux. - These Linux files are usually available from an "alsa" - of "libasound" - development package, it's recommended that you try and use - the package provided by the particular version of Linux that - you are using. - The makefiles will check this emit a sanity error if it is - missing or the wrong version. - As a last resort you can go to the - - Advanced Linux Sound Architecture Site. -

      - -

      Windows Specific Dependencies

      - -
      - - Unix Command Tools (CYGWIN) - -
      -

      - The - OpenJDK - requires access to a set of unix command tools - on Windows which can be supplied by - CYGWIN. -

      - The - OpenJDK - build - requires CYGWIN version 1.5.12 or newer. - Information about CYGWIN can - be obtained from the CYGWIN website at - www.cygwin.com. -

      - By default CYGWIN doesn't install all the tools required for building - the OpenJDK. - Along with the default installation, you need to install - the following tools. -

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      Binary NamePackageDescription
      ar.exeDevelbinutils: The GNU assembler, linker and binary - utilities
      make.exeDevelmake: The GNU version of the 'make' utility
      m4.exeInterpretersm4: GNU implementation of the traditional Unix macro - processor
      cpio.exeUtilscpio: A program to manage archives of files
      file.exeUtilsfile: Determines file type using 'magic' numbers
      -
      -
      - - - Microsoft DirectX 9.0 SDK header files and libraries - - -
      -

      - Microsoft DirectX 9.0 SDK (Summer 2004) - headers are required for building - OpenJDK. - This SDK can be downloaded from - - Microsoft DirectX 9.0 SDK (Summer 2004). - If the link above becomes obsolete, the SDK can be found from - the Microsoft Download Site - (search with "DirectX 9.0 SDK Update Summer 2004"). - The location of this SDK can be set with - ALT_DXSDK_PATH - but it's normally found via the DirectX environment variable - DXSDK_DIR. -

      - - - MSVCRT.DLL - - -
      -

      - i586 only: - The - OpenJDK - 32bit build requires - access to MSVCRT.DLL - version 6.00.8337.0 or newer. - If the MSVCRT.DLL is not installed in - the system32 directory set the - ALT_MSVCRT_DLL_PATH - variable to the location. -

      - X64 only: - The OpenJDK 64bit build requires access to - MSVCRT.DLL version 7.0.3790.0 or newer, which is - usually supplied by the - Platform SDK. - If it is not available from the Platform SDK, - set the - ALT_MSVCRT_DLL_PATH - variable to the location. -

      - - - MSVCR71.DLL - - -
      -

      - i586 only: - The - OpenJDK - build requires access to - MSVCR71.DLL version 7.10.3052.4 or newer which should be - supplied by the - Visual Studio product - If the MSVCR71.DLL is not available from the - Visual Studio product - set the - ALT_MSVCR71_DLL_PATH - variable to the location. -

      - -
      - - -
      - - -
      - -

      Creating the Build

      - -
      -

      - Once a machine is setup to build the - OpenJDK, - the steps to create the - build are fairly simple. - The various ALT settings can either be made into variables - or can be supplied on the - gmake - command. -

      -

        -
      1. Use the sanity rule to double check all the ALT settings: -
        - - gmake - sanity - [ARCH_DATA_MODEL=32 or 64] - [other "ALT_" overrides] - -
        -
      2. -
      3. Start the build with the command: -
        - - gmake - [ARCH_DATA_MODEL=32 or 64] - [ALT_OUTPUTDIR=output_directory] - [other "ALT_" overrides] - -
        -
      4. -
      -

      - Solaris: - Note that ARCH_DATA_MODEL is really only needed on Solaris to - indicate you want to built the 64-bit version. - And before the Solaris 64-bit binaries can be used, they - must be merged with the binaries from a separate 32-bit build. - The merged binaries may then be used in either 32-bit or 64-bit mode, with - the selection occurring at runtime - with the -d32 or -d64 options. -

      - - -
      - -

      Testing the Build

      - -
      -

      - When the build is completed, you should see the generated - binaries and associated files in the j2sdk-image - directory in the output directory. - The default output directory is - build/platform, - where platform is one of -

        -
      • solaris-sparc
      • -
      • solaris-sparcv9
      • -
      • solaris-i586
      • -
      • solaris-amd64
      • -
      • linux-i586
      • -
      • linux-amd64
      • -
      • windows-i586
      • -
      • windows-amd64
      • -
      - In particular, the - build/platform/j2sdk-image/bin - directory should contain executables for the - OpenJDK - tools and utilities. -

      - You can test that the build completed properly by using the build - to run the various demos that you will find in the - build/platform/j2sdk-image/demo - directory. -

      - The provided regression tests can be run with the jtreg - utility from - the jtreg site. -

      - - -
      - -

      Environment/Make Variables

      - -

      -Some of the -environment or make variables (just called variables in this -document) that can impact the build are: - -

      - -
      - -
      PATH
      -
      Typically you want to set the PATH to include: -
        -
      • The location of the GNU make binary
      • -
      • The location of the JDK 6 java - (see Bootstrap JDK)
      • -
      • The location of the C/C++ compilers - (see compilers)
      • -
      • The location or locations for the Unix command utilities - (e.g. /usr/bin)
      • -
      -
      - -
      ARCH_DATA_MODEL
      -
      The ARCH_DATA_MODEL variable - is used to specify whether the build is to generate 32-bit or 64-bit - binaries. - The Solaris build supports either 32-bit or 64-bit builds, but - Windows and Linux will support only one, depending on the specific - OS being used. - Normally, setting this variable is only necessary on Solaris. - Set ARCH_DATA_MODEL to 32 for generating 32-bit binaries, - or to 64 for generating 64-bit binaries. -
      - -
      ALT_BOOTDIR
      -
      - The location of the bootstrap JDK installation. - See Bootstrap JDK for more information. - You should always install your own local Bootstrap JDK and - always set ALT_BOOTDIR explicitly. -
      - -
      ALT_OUTPUTDIR
      -
      - An override for specifying the (absolute) path of where the - build output is to go. - The default output directory will be build/platform. -
      - -
      ALT_COMPILER_PATH
      -
      - The location of the C/C++ compiler. - The default varies depending on the platform. -
      - -
      ALT_CACERTS_FILE
      -
      - The location of the cacerts file. - The default will refer to - jdk/src/share/lib/security/cacerts. -
      - -
      ALT_BINARY_PLUGS_PATH
      -
      - The location of the binary plugs installation. - See Binary Plugs for more information. - You should always have a local copy of a - recent Binary Plugs install image - and set this variable to that location. -
      - -
      ALT_CUPS_HEADERS_PATH
      -
      - The location of the CUPS header files. - See CUPS information for more information. - If this path does not exist the fallback path is - /usr/include. -
      - - -
      ALT_FREETYPE_LIB_PATH
      -
      - The location of the FreeType shared library. - See FreeType information for details. -
      - -
      ALT_FREETYPE_HEADERS_PATH
      -
      - The location of the FreeType header files. - See FreeType information for details. -
      - -
      Windows specific:
      -
      -
      -
      ALT_MSDEVTOOLS_PATH
      -
      - The location of the Microsoft Visual Studio .NET 2003 - tools 'bin' directory. - The default is usually derived from - ALT_COMPILER_PATH. -
      - -
      ALT_DXSDK_PATH
      -
      - The location of the - Microsoft DirectX 9 SDK. - The default will be to try and use the DirectX environment - variable DXSDK_DIR, - failing that, look in C:/DXSDK. -
      - -
      ALT_MSVCRT_DLL_PATH
      -
      - The location of the - MSVCRT.DLL. -
      - -
      ALT_MSVCR71_DLL_PATH
      -
      - i586 only: - The location of the - MSVCR71.DLL. -
      -
      -
      - -
      -
      - - -
      - -

      Troubleshooting

      - -
      -

      - A build can fail for any number of reasons. - Most failures - are a result of trying to build in an environment in which all the - pre-build requirements have not been met. - The first step in - troubleshooting a build failure is to recheck that you have satisfied - all the pre-build requirements for your platform. - Look for the check list of the platform you are building on in the - Table of Contents. - -

      - You can validate your build environment by using the sanity - target. - Any errors listed - will stop the build from starting, and any warnings may result in - a flawed product build. - We strongly encourage you to evaluate every - sanity check warning and fix it if required, before you proceed - further with your build. - -

      - Some of the more common problems with builds are briefly described - below, with suggestions for remedies. - -

        -
      • - Slow Builds: -
        -

        - If your build machine seems to be overloaded from too many - simultaneous C++ compiles, try setting the HOTSPOT_BUILD_JOBS - variable to 1 (if you're using a multiple CPU - machine, setting it to more than the the number of CPUs is probably - not a good idea). -

        - Creating the javadocs can be very slow, if you are running - javadoc, consider skipping that step. -

        - Faster hardware and more RAM always helps too. - The VM build tends to be CPU intensive (many C++ compiles), - and the rest of the JDK will often be disk intensive. -

        - Faster compiles are possible using a tool called - ccache. -

        -
      • -
      • - File time issues: -
        -

        - If you see warnings that refer to file time stamps, e.g. -

        - Warning message: File `xxx' has modification time in - the future. -
        - Warning message: Clock skew detected. Your build may - be incomplete. -
        -

        - These warnings can occur when the clock on the build machine is out of - sync with the timestamps on the source files. Other errors, apparently - unrelated but in fact caused by the clock skew, can occur along with - the clock skew warnings. These secondary errors may tend to obscure the - fact that the true root cause of the problem is an out-of-sync clock. - For example, an out-of-sync clock has been known to cause an old - version of javac to be used to compile some files, resulting in errors - when the pre-1.4 compiler ran across the new assert keyword - in the 1.4 source code. -

        - If you see these warnings, reset the clock on the build - machine, run "gmake clobber" or delete the directory - containing the build output, and restart the build from the beginning. -

        -
      • -
      • - Error message: Trouble writing out table to disk -
        -

        - Increase the amount of swap space on your build machine. -

        -
      • -
      • - Error Message: libstdc++ not found: -
        - This is caused by a missing libstdc++.a library. - This is installed as part of a specific package - (e.g. libstdc++.so.devel.386). - By default some 64bit Linux versions (e.g. Fedora) - only install the 64bit version of the libstdc++ package. - Various parts of the JDK build require a static - link of the C++ runtime libraries to allow for maximum - portability of the built images. -
        -
      • -
      • - Error Message: cannot restore segment prot after reloc -
        - This is probably an issue with SELinux (See - http://en.wikipedia.org/wiki/SELinux). - Parts of the VM is built without the -fPIC for - performance reasons. -

        - To completely disable SELinux: -

          - -
        1. $ su root
        2. -
        3. # system-config-securitylevel
        4. -
        5. In the window that appears, select the SELinux tab
        6. -
        7. Disable SELinux
        8. -
        -

        - Alternatively, instead of completely disabling it you could - disable just this one check. -

          -
        1. Select System->Administration->SELinux Management
        2. -
        3. In the SELinux Management Tool which appears, - select "Boolean" from the menu on the left
        4. -
        5. Expand the "Memory Protection" group
        6. -
        7. Check the first item, labeled - "Allow all unconfined executables to use libraries requiring text relocation ..."
        8. -
        -
        -
      • -
      -
      - -
      diff --git a/make/README.html b/make/README.html deleted file mode 100644 index ae415b159..000000000 --- a/make/README.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - OpenJDK README - - -
      -
      -

      OpenJDK README

      -
      - - -
      - -

      TBD

      - -

      Building the OpenJDK

      - -

      - Refer to the OpenJDK Build README - for build instructions. - - - -


      - - - diff --git a/make/common/shared/Compiler-sun.gmk b/make/common/shared/Compiler-sun.gmk index b4806bb4b..b3c4e2a90 100644 --- a/make/common/shared/Compiler-sun.gmk +++ b/make/common/shared/Compiler-sun.gmk @@ -31,11 +31,8 @@ COMPILER_NAME=Sun Studio # Sun Studio Compiler settings specific to Solaris ifeq ($(PLATFORM), solaris) - # FIXUP: Change to SS12 when validated - #COMPILER_VERSION=SS12 - #REQUIRED_CC_VER=5.9 - COMPILER_VERSION=SS11 - REQUIRED_CC_VER=5.8 + COMPILER_VERSION=SS12 + REQUIRED_CC_VER=5.9 CC = $(COMPILER_PATH)cc CPP = $(COMPILER_PATH)cc -E CXX = $(COMPILER_PATH)CC diff --git a/make/jprt.config b/make/jprt.config index 99ee4c4e7..ee83ee5cc 100644 --- a/make/jprt.config +++ b/make/jprt.config @@ -137,9 +137,7 @@ if [ "${osname}" = SunOS ] ; then if [ "${JPRT_SOLARIS_COMPILER_NAME}" != "" ] ; then compiler_name=${JPRT_SOLARIS_COMPILER_NAME} else - # FIXUP: Change to SS12 when validated - #compiler_name=SS12 - compiler_name=SS11 + compiler_name=SS12 fi compiler_path=${jdk_devtools}/${solaris_arch}/SUNWspro/${compiler_name}/bin ALT_COMPILER_PATH="${compiler_path}" -- GitLab From ea401d230cea331ee49ae7af0849c10e50b7cd4a Mon Sep 17 00:00:00 2001 From: martin Date: Thu, 7 Aug 2008 06:36:41 -0700 Subject: [PATCH 040/139] 6730507: java.util.Timer schedule delay Long.MAX_VALUE causes task to execute multiple times Reviewed-by: chegar --- src/share/classes/java/util/Timer.java | 13 ++- test/java/util/Timer/DelayOverflow.java | 115 ++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 test/java/util/Timer/DelayOverflow.java diff --git a/src/share/classes/java/util/Timer.java b/src/share/classes/java/util/Timer.java index 5c4d3bf71..7d1cc6841 100644 --- a/src/share/classes/java/util/Timer.java +++ b/src/share/classes/java/util/Timer.java @@ -93,12 +93,12 @@ public class Timer { * and the timer thread consumes, executing timer tasks as appropriate, * and removing them from the queue when they're obsolete. */ - private TaskQueue queue = new TaskQueue(); + private final TaskQueue queue = new TaskQueue(); /** * The timer thread. */ - private TimerThread thread = new TimerThread(queue); + private final TimerThread thread = new TimerThread(queue); /** * This object causes the timer's task execution thread to exit @@ -107,7 +107,7 @@ public class Timer { * Timer as such a finalizer would be susceptible to a subclass's * finalizer forgetting to call it. */ - private Object threadReaper = new Object() { + private final Object threadReaper = new Object() { protected void finalize() throws Throwable { synchronized(queue) { thread.newTasksMayBeScheduled = false; @@ -119,7 +119,7 @@ public class Timer { /** * This ID is used to generate thread names. */ - private static AtomicInteger nextSerialNumber = new AtomicInteger(0); + private final static AtomicInteger nextSerialNumber = new AtomicInteger(0); private static int serialNumber() { return nextSerialNumber.getAndIncrement(); } @@ -387,6 +387,11 @@ public class Timer { if (time < 0) throw new IllegalArgumentException("Illegal execution time."); + // Constrain value of period sufficiently to prevent numeric + // overflow while still being effectively infinitely large. + if (Math.abs(period) > (Long.MAX_VALUE >> 1)) + period >>= 1; + synchronized(queue) { if (!thread.newTasksMayBeScheduled) throw new IllegalStateException("Timer already cancelled."); diff --git a/test/java/util/Timer/DelayOverflow.java b/test/java/util/Timer/DelayOverflow.java new file mode 100644 index 000000000..96e45d18f --- /dev/null +++ b/test/java/util/Timer/DelayOverflow.java @@ -0,0 +1,115 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6730507 + * @summary java.util.Timer schedule delay Long.MAX_VALUE causes task to execute multiple times + * @author Chris Hegarty + * @author Martin Buchholz + */ + +import java.util.Date; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +public class DelayOverflow +{ + void scheduleNow(Timer timer, TimerTask task, int how) { + switch (how) { + case 0 : + timer.schedule(task, new Date(), Long.MAX_VALUE); + break; + case 1: + timer.schedule(task, 0L, Long.MAX_VALUE); + break; + case 2: + timer.scheduleAtFixedRate(task, new Date(), Long.MAX_VALUE); + break; + case 3: + timer.scheduleAtFixedRate(task, 0L, Long.MAX_VALUE); + break; + default: + fail(String.valueOf(how)); + } + } + + void sleep(long millis) { + try { Thread.sleep(millis); } + catch (Throwable t) { unexpected(t); } + } + + /** Checks that scheduledExecutionTime returns a "recent" time. */ + void checkScheduledExecutionTime(TimerTask task) { + long t = System.currentTimeMillis() + - task.scheduledExecutionTime(); + check(t >= 0 && t < 1000 * 600); + } + + void test(String[] args) throws Throwable { + for (int how=0; how<4; how++) { + final CountDownLatch done = new CountDownLatch(1); + final AtomicInteger count = new AtomicInteger(0); + final Timer timer = new Timer(); + final TimerTask task = new TimerTask() { + @Override + public void run() { + checkScheduledExecutionTime(this); + count.incrementAndGet(); + done.countDown(); + }}; + + scheduleNow(timer, task, how); + done.await(); + equal(count.get(), 1); + checkScheduledExecutionTime(task); + if (new java.util.Random().nextBoolean()) + sleep(10); + check(task.cancel()); + timer.cancel(); + checkScheduledExecutionTime(task); + } + } + + //--------------------- Infrastructure --------------------------- + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) pass(); + else fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + Class k = new Object(){}.getClass().getEnclosingClass(); + try {k.getMethod("instanceMain",String[].class) + .invoke( k.newInstance(), (Object) args);} + catch (Throwable e) {throw e.getCause();}} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} -- GitLab From 055108d877e16488bafa9d74fa2c8213e6e9af28 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Thu, 7 Aug 2008 16:25:45 +0200 Subject: [PATCH 041/139] 6717257: MBeanServer doesn't describe RuntimeException for methods inherited from MBeanServerConnection Reviewed-by: dfuchs --- .../classes/javax/management/MBeanServer.java | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/share/classes/javax/management/MBeanServer.java b/src/share/classes/javax/management/MBeanServer.java index c5a1836b8..a08f64011 100644 --- a/src/share/classes/javax/management/MBeanServer.java +++ b/src/share/classes/javax/management/MBeanServer.java @@ -50,8 +50,8 @@ import javax.management.loading.ClassLoaderRepository; * server. A Java object cannot be registered in the MBean server * unless it is a JMX compliant MBean.

      * - *

      When an MBean is registered or unregistered in the MBean server - * a {@link javax.management.MBeanServerNotification + *

      When an MBean is registered or unregistered in the + * MBean server a {@link javax.management.MBeanServerNotification * MBeanServerNotification} Notification is emitted. To register an * object as listener to MBeanServerNotifications you should call the * MBean server method {@link #addNotificationListener @@ -262,6 +262,8 @@ public interface MBeanServer extends MBeanServerConnection { * {@inheritDoc} *

      If this method successfully creates an MBean, a notification * is sent as described above.

      + * + * @throws RuntimeOperationsException {@inheritDoc} */ public ObjectInstance createMBean(String className, ObjectName name) throws ReflectionException, InstanceAlreadyExistsException, @@ -272,6 +274,8 @@ public interface MBeanServer extends MBeanServerConnection { * {@inheritDoc} *

      If this method successfully creates an MBean, a notification * is sent as described above.

      + * + * @throws RuntimeOperationsException {@inheritDoc} */ public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName) @@ -283,6 +287,8 @@ public interface MBeanServer extends MBeanServerConnection { * {@inheritDoc} *

      If this method successfully creates an MBean, a notification * is sent as described above.

      + * + * @throws RuntimeOperationsException {@inheritDoc} */ public ObjectInstance createMBean(String className, ObjectName name, Object params[], String signature[]) @@ -294,6 +300,8 @@ public interface MBeanServer extends MBeanServerConnection { * {@inheritDoc} *

      If this method successfully creates an MBean, a notification * is sent as described above.

      + * + * @throws RuntimeOperationsException {@inheritDoc} */ public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, Object params[], @@ -362,6 +370,8 @@ public interface MBeanServer extends MBeanServerConnection { * *

      If this method successfully unregisters an MBean, a notification * is sent as described above.

      + * + * @throws RuntimeOperationsException {@inheritDoc} */ public void unregisterMBean(ObjectName name) throws InstanceNotFoundException, MBeanRegistrationException; @@ -377,6 +387,9 @@ public interface MBeanServer extends MBeanServerConnection { public Set queryNames(ObjectName name, QueryExp query); // doc comment inherited from MBeanServerConnection + /** + * @throws RuntimeOperationsException {@inheritDoc} + */ public boolean isRegistered(ObjectName name); /** @@ -389,21 +402,33 @@ public interface MBeanServer extends MBeanServerConnection { public Integer getMBeanCount(); // doc comment inherited from MBeanServerConnection + /** + * @throws RuntimeOperationsException {@inheritDoc} + */ public Object getAttribute(ObjectName name, String attribute) throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException; // doc comment inherited from MBeanServerConnection + /** + * @throws RuntimeOperationsException {@inheritDoc} + */ public AttributeList getAttributes(ObjectName name, String[] attributes) throws InstanceNotFoundException, ReflectionException; // doc comment inherited from MBeanServerConnection + /** + * @throws RuntimeOperationsException {@inheritDoc} + */ public void setAttribute(ObjectName name, Attribute attribute) throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException; // doc comment inherited from MBeanServerConnection + /** + * @throws RuntimeOperationsException {@inheritDoc} + */ public AttributeList setAttributes(ObjectName name, AttributeList attributes) throws InstanceNotFoundException, ReflectionException; @@ -433,7 +458,10 @@ public interface MBeanServer extends MBeanServerConnection { Object handback) throws InstanceNotFoundException; - // doc comment inherited from MBeanServerConnection + /** + * {@inheritDoc} + * @throws RuntimeOperationsException {@inheritDoc} + */ public void addNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, -- GitLab From 4303a2c1d2dd49d2af6493f686c72eddfe7ce7ae Mon Sep 17 00:00:00 2001 From: dfuchs Date: Fri, 8 Aug 2008 14:24:31 +0200 Subject: [PATCH 042/139] 6733294: MBeans tab - UI issues with writable attributes Reviewed-by: emcmanus --- make/netbeans/jconsole/build.properties | 1 + make/netbeans/jconsole/build.xml | 13 +- .../tools/jconsole/inspector/TableSorter.java | 118 +++-- .../jconsole/inspector/XMBeanAttributes.java | 449 ++++++++++++------ .../tools/jconsole/inspector/XPlotter.java | 1 + .../sun/tools/jconsole/inspector/XSheet.java | 94 ++-- .../sun/tools/jconsole/inspector/XTable.java | 29 +- .../jconsole/inspector/XTextFieldEditor.java | 31 +- 8 files changed, 496 insertions(+), 240 deletions(-) diff --git a/make/netbeans/jconsole/build.properties b/make/netbeans/jconsole/build.properties index ab2a0a1ae..189b528ee 100644 --- a/make/netbeans/jconsole/build.properties +++ b/make/netbeans/jconsole/build.properties @@ -44,3 +44,4 @@ build.jdk.version = 1.7.0 build.release = ${build.jdk.version}-opensource build.number = b00 jconsole.version = ${build.release}-${user.name}-${build.number} +jconsole.args = -debug diff --git a/make/netbeans/jconsole/build.xml b/make/netbeans/jconsole/build.xml index 0f51d1c3b..b546fb7de 100644 --- a/make/netbeans/jconsole/build.xml +++ b/make/netbeans/jconsole/build.xml @@ -30,9 +30,9 @@ --> - + - + - + - + + - + - + diff --git a/src/share/classes/sun/tools/jconsole/inspector/TableSorter.java b/src/share/classes/sun/tools/jconsole/inspector/TableSorter.java index 52c6d15f5..79cec51e3 100644 --- a/src/share/classes/sun/tools/jconsole/inspector/TableSorter.java +++ b/src/share/classes/sun/tools/jconsole/inspector/TableSorter.java @@ -25,71 +25,78 @@ package sun.tools.jconsole.inspector; -import java.util.*; -import java.awt.event.*; -import javax.swing.table.*; -import javax.swing.event.*; // Imports for picking up mouse events from the JTable. -import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.awt.event.InputEvent; +import java.awt.event.MouseListener; +import java.util.Vector; import javax.swing.JTable; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.DefaultTableModel; import javax.swing.table.JTableHeader; import javax.swing.table.TableColumnModel; +import sun.tools.jconsole.JConsole; @SuppressWarnings("serial") public class TableSorter extends DefaultTableModel implements MouseListener { private boolean ascending = true; private TableColumnModel columnModel; private JTable tableView; - private Vector listenerList; + private Vector evtListenerList; private int sortColumn = 0; private int[] invertedIndex; public TableSorter() { super(); - listenerList = new Vector(); + evtListenerList = new Vector(); } public TableSorter(Object[] columnNames, int numRows) { super(columnNames,numRows); - listenerList = new Vector(); + evtListenerList = new Vector(); } + @Override public void newDataAvailable(TableModelEvent e) { super.newDataAvailable(e); invertedIndex = new int[getRowCount()]; - for (int i=0;i viewableAttributes; private WeakHashMap> viewersCache = new WeakHashMap>(); - private TableModelListener attributesListener; + private final TableModelListener attributesListener; private MBeansTab mbeansTab; - private XTable table; private TableCellEditor valueCellEditor = new ValueCellEditor(); private int rowMinHeight = -1; private AttributesMouseListener mouseListener = new AttributesMouseListener(); @@ -89,8 +108,8 @@ public class XMBeanAttributes extends XTable { super(); this.mbeansTab = mbeansTab; ((DefaultTableModel)getModel()).setColumnIdentifiers(columnNames); - getModel().addTableModelListener(attributesListener = - new AttributesListener(this)); + attributesListener = new AttributesListener(this); + getModel().addTableModelListener(attributesListener); getColumnModel().getColumn(NAME_COLUMN).setPreferredWidth(40); addMouseListener(mouseListener); @@ -99,6 +118,7 @@ public class XMBeanAttributes extends XTable { addKeyListener(new Utils.CopyKeyAdapter()); } + @Override public synchronized Component prepareRenderer(TableCellRenderer renderer, int row, int column) { //In case we have a repaint thread that is in the process of @@ -124,6 +144,7 @@ public class XMBeanAttributes extends XTable { setRowHeight(row, rowMinHeight); } + @Override public synchronized TableCellRenderer getCellRenderer(int row, int column) { //In case we have a repaint thread that is in the process of @@ -169,26 +190,40 @@ public class XMBeanAttributes extends XTable { } public void cancelCellEditing() { - TableCellEditor editor = getCellEditor(); - if (editor != null) { - editor.cancelCellEditing(); + if (LOGGER.isLoggable(Level.FINER)) { + LOGGER.finer("Cancel Editing Row: "+getEditingRow()); + } + final TableCellEditor tableCellEditor = getCellEditor(); + if (tableCellEditor != null) { + tableCellEditor.cancelCellEditing(); } } public void stopCellEditing() { - TableCellEditor editor = getCellEditor(); - if (editor != null) { - editor.stopCellEditing(); + if (LOGGER.isLoggable(Level.FINER)) { + LOGGER.finer("Stop Editing Row: "+getEditingRow()); + } + final TableCellEditor tableCellEditor = getCellEditor(); + if (tableCellEditor != null) { + tableCellEditor.stopCellEditing(); } } - public final boolean editCellAt(int row, int column, EventObject e) { + @Override + public final boolean editCellAt(final int row, final int column, EventObject e) { + if (LOGGER.isLoggable(Level.FINER)) { + LOGGER.finer("editCellAt(row="+row+", col="+column+ + ", e="+e+")"); + } + if (JConsole.isDebug()) { + System.err.println("edit: "+getValueName(row)+"="+getValue(row)); + } boolean retVal = super.editCellAt(row, column, e); if (retVal) { - TableCellEditor editor = + final TableCellEditor tableCellEditor = getColumnModel().getColumn(column).getCellEditor(); - if (editor == valueCellEditor) { - ((JComponent) editor).requestFocus(); + if (tableCellEditor == valueCellEditor) { + ((JComponent) tableCellEditor).requestFocus(); } } return retVal; @@ -213,6 +248,10 @@ public class XMBeanAttributes extends XTable { public void setValueAt(Object value, int row, int column) { if (!isCellError(row, column) && isColumnEditable(column) && isWritable(row) && Utils.isEditableType(getClassName(row))) { + if (JConsole.isDebug()) { + System.err.println("validating [row="+row+", column="+column+ + "]: "+getValueName(row)+"="+value); + } super.setValueAt(value, row, column); } } @@ -256,12 +295,14 @@ public class XMBeanAttributes extends XTable { } } - public Object getValue(int row) { - return ((DefaultTableModel) getModel()).getValueAt(row, VALUE_COLUMN); + final Object val = ((DefaultTableModel) getModel()) + .getValueAt(row, VALUE_COLUMN); + return val; } //tool tip only for editable column + @Override public String getToolTip(int row, int column) { if (isCellError(row, column)) { return (String) unavailableAttributes.get(getValueName(row)); @@ -302,6 +343,7 @@ public class XMBeanAttributes extends XTable { * Override JTable method in order to make any call to this method * atomic with TableModel elements. */ + @Override public synchronized int getRowCount() { return super.getRowCount(); } @@ -332,24 +374,67 @@ public class XMBeanAttributes extends XTable { return isViewable; } - public void loadAttributes(final XMBean mbean, MBeanInfo mbeanInfo) { + // Call this in EDT + public void loadAttributes(final XMBean mbean, final MBeanInfo mbeanInfo) { + + final SwingWorker load = + new SwingWorker() { + @Override + protected Runnable doInBackground() throws Exception { + return doLoadAttributes(mbean,mbeanInfo); + } + + @Override + protected void done() { + try { + final Runnable updateUI = get(); + if (updateUI != null) updateUI.run(); + } catch (RuntimeException x) { + throw x; + } catch (ExecutionException x) { + if(JConsole.isDebug()) { + System.err.println( + "Exception raised while loading attributes: " + +x.getCause()); + x.printStackTrace(); + } + } catch (InterruptedException x) { + if(JConsole.isDebug()) { + System.err.println( + "Interrupted while loading attributes: "+x); + x.printStackTrace(); + } + } + } + + }; + mbeansTab.workerAdd(load); + } + + // Don't call this in EDT, but execute returned Runnable inside + // EDT - typically in the done() method of a SwingWorker + // This method can return null. + private Runnable doLoadAttributes(final XMBean mbean, MBeanInfo infoOrNull) + throws JMException, IOException { // To avoid deadlock with events coming from the JMX side, // we retrieve all JMX stuff in a non synchronized block. - if(mbean == null) return; - - final MBeanAttributeInfo[] attributesInfo = mbeanInfo.getAttributes(); - final HashMap attributes = - new HashMap(attributesInfo.length); - final HashMap unavailableAttributes = - new HashMap(attributesInfo.length); - final HashMap viewableAttributes = - new HashMap(attributesInfo.length); + if(mbean == null) return null; + final MBeanInfo curMBeanInfo = + (infoOrNull==null)?mbean.getMBeanInfo():infoOrNull; + + final MBeanAttributeInfo[] attrsInfo = curMBeanInfo.getAttributes(); + final HashMap attrs = + new HashMap(attrsInfo.length); + final HashMap unavailableAttrs = + new HashMap(attrsInfo.length); + final HashMap viewableAttrs = + new HashMap(attrsInfo.length); AttributeList list = null; try { - list = mbean.getAttributes(attributesInfo); - } catch (Exception e) { + list = mbean.getAttributes(attrsInfo); + }catch(Exception e) { if (JConsole.isDebug()) { System.err.println("Error calling getAttributes() on MBean \"" + mbean.getObjectName() + "\". JConsole will " + @@ -359,18 +444,18 @@ public class XMBeanAttributes extends XTable { } list = new AttributeList(); //Can't load all attributes, do it one after each other. - for(int i = 0; i < attributesInfo.length; i++) { + for(int i = 0; i < attrsInfo.length; i++) { String name = null; try { - name = attributesInfo[i].getName(); + name = attrsInfo[i].getName(); Object value = - mbean.getMBeanServerConnection().getAttribute(mbean.getObjectName(), name); + mbean.getMBeanServerConnection(). + getAttribute(mbean.getObjectName(), name); list.add(new Attribute(name, value)); }catch(Exception ex) { - if(attributesInfo[i].isReadable()) { - unavailableAttributes.put(name, - Utils.getActualException(ex). - toString()); + if(attrsInfo[i].isReadable()) { + unavailableAttrs.put(name, + Utils.getActualException(ex).toString()); } } } @@ -380,22 +465,22 @@ public class XMBeanAttributes extends XTable { for (int i=0;i stopCellEditing -> setValueAt -> tableChanged + // -> refreshAttributes(false) + // + // Can be called in EDT - as long as the implementation of + // mbeansTab.getCachedMBeanServerConnection() and mbsc.flush() doesn't + // change + // + private void refreshAttributes(final boolean stopCellEditing) { + SwingWorker sw = new SwingWorker() { + + @Override + protected Void doInBackground() throws Exception { + SnapshotMBeanServerConnection mbsc = + mbeansTab.getSnapshotMBeanServerConnection(); + mbsc.flush(); + return null; + } + + @Override + protected void done() { + try { + get(); + if (stopCellEditing) stopCellEditing(); + loadAttributes(mbean, mbeanInfo); + } catch (Exception x) { + if (JConsole.isDebug()) { + x.printStackTrace(); + } + } + } + }; + mbeansTab.workerAdd(sw); } + // We need to call stop editing here - otherwise edits are lost + // when resizing the table. + // + @Override + public void columnMarginChanged(ChangeEvent e) { + if (isEditing()) stopCellEditing(); + super.columnMarginChanged(e); + } + + // We need to call stop editing here - otherwise the edited value + // is transferred to the wrong row... + // + @Override + void sortRequested(int column) { + if (isEditing()) stopCellEditing(); + super.sortRequested(column); + } - public void emptyTable() { - synchronized(this) { - ((DefaultTableModel) getModel()). - removeTableModelListener(attributesListener); - super.emptyTable(); - } + @Override + public synchronized void emptyTable() { + emptyTable((DefaultTableModel)getModel()); } + // Call this in synchronized block. + private void emptyTable(DefaultTableModel model) { + model.removeTableModelListener(attributesListener); + super.emptyTable(); + } + private boolean isViewable(Attribute attribute) { Object data = attribute.getValue(); return XDataViewer.isViewableValue(data); @@ -659,6 +795,7 @@ public class XMBeanAttributes extends XTable { class AttributesMouseListener extends MouseAdapter { + @Override public void mousePressed(MouseEvent e) { if(e.getButton() == MouseEvent.BUTTON1) { if(e.getClickCount() >= 2) { @@ -674,8 +811,10 @@ public class XMBeanAttributes extends XTable { } } + @SuppressWarnings("serial") class ValueCellEditor extends XTextFieldEditor { // implements javax.swing.table.TableCellEditor + @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, @@ -727,6 +866,7 @@ public class XMBeanAttributes extends XTable { } } + @SuppressWarnings("serial") class MaximizedCellRenderer extends DefaultTableCellRenderer { Component comp; MaximizedCellRenderer(Component comp) { @@ -736,6 +876,7 @@ public class XMBeanAttributes extends XTable { comp.setPreferredSize(new Dimension((int) d.getWidth(), 200)); } } + @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, @@ -818,6 +959,7 @@ public class XMBeanAttributes extends XTable { return minHeight; } + @Override public String toString() { if(value == null) return null; @@ -854,45 +996,82 @@ public class XMBeanAttributes extends XTable { this.component = component; } + // Call this in EDT public void tableChanged(final TableModelEvent e) { - final TableModel model = (TableModel)e.getSource(); // only post changes to the draggable column if (isColumnEditable(e.getColumn())) { - mbeansTab.workerAdd(new Runnable() { - public void run() { - try { - Object tableValue = - model.getValueAt(e.getFirstRow(), - e.getColumn()); - // if it's a String, try construct new value - // using the defined type. - if (tableValue instanceof String) { - tableValue = - Utils.createObjectFromString(getClassName(e.getFirstRow()), // type - (String)tableValue);// value - } - String attributeName = - getValueName(e.getFirstRow()); - Attribute attribute = - new Attribute(attributeName,tableValue); - mbean.setAttribute(attribute); - } - catch (Throwable ex) { - if (JConsole.isDebug()) { - ex.printStackTrace(); - } - ex = Utils.getActualException(ex); - - String message = (ex.getMessage() != null) ? ex.getMessage() : ex.toString(); - EventQueue.invokeLater(new ThreadDialog(component, - message+"\n", - Resources.getText("Problem setting attribute"), - JOptionPane.ERROR_MESSAGE)); - } - refreshAttributes(); - } - }); + final TableModel model = (TableModel)e.getSource(); + Object tableValue = model.getValueAt(e.getFirstRow(), + e.getColumn()); + + if (LOGGER.isLoggable(Level.FINER)) { + LOGGER.finer("tableChanged: firstRow="+e.getFirstRow()+ + ", lastRow="+e.getLastRow()+", column="+e.getColumn()+ + ", value="+tableValue); + } + // if it's a String, try construct new value + // using the defined type. + if (tableValue instanceof String) { + try { + tableValue = + Utils.createObjectFromString(getClassName(e.getFirstRow()), // type + (String)tableValue);// value + } catch (Throwable ex) { + popupAndLog(ex,"tableChanged", + "Problem setting attribute"); + } + } + final String attributeName = getValueName(e.getFirstRow()); + final Attribute attribute = + new Attribute(attributeName,tableValue); + setAttribute(attribute, "tableChanged"); } } + + // Call this in EDT + private void setAttribute(final Attribute attribute, final String method) { + final SwingWorker setAttribute = + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + try { + if (JConsole.isDebug()) { + System.err.println("setAttribute("+ + attribute.getName()+ + "="+attribute.getValue()+")"); + } + mbean.setAttribute(attribute); + } catch (Throwable ex) { + popupAndLog(ex,method,"Problem setting attribute"); + } + return null; + } + @Override + protected void done() { + try { + get(); + } catch (Exception x) { + if (JConsole.isDebug()) + x.printStackTrace(); + } + refreshAttributes(false); + } + + }; + mbeansTab.workerAdd(setAttribute); + } + + // Call this outside EDT + private void popupAndLog(Throwable ex, String method, String key) { + ex = Utils.getActualException(ex); + if (JConsole.isDebug()) ex.printStackTrace(); + + String message = (ex.getMessage() != null) ? ex.getMessage() + : ex.toString(); + EventQueue.invokeLater( + new ThreadDialog(component, message+"\n", + Resources.getText(key), + JOptionPane.ERROR_MESSAGE)); + } } } diff --git a/src/share/classes/sun/tools/jconsole/inspector/XPlotter.java b/src/share/classes/sun/tools/jconsole/inspector/XPlotter.java index a30dd08af..24b4104b7 100644 --- a/src/share/classes/sun/tools/jconsole/inspector/XPlotter.java +++ b/src/share/classes/sun/tools/jconsole/inspector/XPlotter.java @@ -37,6 +37,7 @@ public class XPlotter extends Plotter { super(unit); this.table = table; } + @Override public void addValues(long time, long... values) { super.addValues(time, values); table.repaint(); diff --git a/src/share/classes/sun/tools/jconsole/inspector/XSheet.java b/src/share/classes/sun/tools/jconsole/inspector/XSheet.java index 1f2104a17..813ede1da 100644 --- a/src/share/classes/sun/tools/jconsole/inspector/XSheet.java +++ b/src/share/classes/sun/tools/jconsole/inspector/XSheet.java @@ -25,18 +25,39 @@ package sun.tools.jconsole.inspector; -import java.awt.*; -import java.awt.event.*; -import java.io.*; -import javax.management.*; -import javax.swing.*; -import javax.swing.border.*; -import javax.swing.tree.*; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; + +import javax.management.IntrospectionException; +import javax.management.NotificationListener; +import javax.management.MBeanInfo; +import javax.management.InstanceNotFoundException; +import javax.management.ReflectionException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.Notification; +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.SwingWorker; +import javax.swing.border.LineBorder; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; + import sun.tools.jconsole.*; import sun.tools.jconsole.inspector.XNodeInfo.Type; import static sun.tools.jconsole.Resources.*; -import static sun.tools.jconsole.Utilities.*; @SuppressWarnings("serial") public class XSheet extends JPanel @@ -344,34 +365,41 @@ public class XSheet extends JPanel return; } mbean = (XMBean) uo.getData(); - SwingWorker sw = new SwingWorker() { + final XMBean xmb = mbean; + SwingWorker sw = new SwingWorker() { @Override - public Void doInBackground() throws InstanceNotFoundException, + public MBeanInfo doInBackground() throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException { - mbeanAttributes.loadAttributes(mbean, mbean.getMBeanInfo()); - return null; + MBeanInfo mbi = xmb.getMBeanInfo(); + return mbi; } @Override protected void done() { try { - get(); - if (!isSelectedNode(node, currentNode)) { - return; + MBeanInfo mbi = get(); + if (mbi != null && mbi.getAttributes() != null && + mbi.getAttributes().length > 0) { + + mbeanAttributes.loadAttributes(xmb, mbi); + + if (!isSelectedNode(node, currentNode)) { + return; + } + invalidate(); + mainPanel.removeAll(); + JPanel borderPanel = new JPanel(new BorderLayout()); + borderPanel.setBorder(BorderFactory.createTitledBorder( + Resources.getText("Attribute values"))); + borderPanel.add(new JScrollPane(mbeanAttributes)); + mainPanel.add(borderPanel, BorderLayout.CENTER); + // add the refresh button to the south panel + southPanel.removeAll(); + southPanel.add(refreshButton, BorderLayout.SOUTH); + southPanel.setVisible(true); + refreshButton.setEnabled(true); + validate(); + repaint(); } - invalidate(); - mainPanel.removeAll(); - JPanel borderPanel = new JPanel(new BorderLayout()); - borderPanel.setBorder(BorderFactory.createTitledBorder( - Resources.getText("Attribute values"))); - borderPanel.add(new JScrollPane(mbeanAttributes)); - mainPanel.add(borderPanel, BorderLayout.CENTER); - // add the refresh button to the south panel - southPanel.removeAll(); - southPanel.add(refreshButton, BorderLayout.SOUTH); - southPanel.setVisible(true); - refreshButton.setEnabled(true); - validate(); - repaint(); } catch (Exception e) { Throwable t = Utils.getActualException(e); if (JConsole.isDebug()) { @@ -704,13 +732,7 @@ public class XSheet extends JPanel JButton button = (JButton) e.getSource(); // Refresh button if (button == refreshButton) { - new SwingWorker() { - @Override - public Void doInBackground() { - refreshAttributes(); - return null; - } - }.execute(); + refreshAttributes(); return; } // Clear button diff --git a/src/share/classes/sun/tools/jconsole/inspector/XTable.java b/src/share/classes/sun/tools/jconsole/inspector/XTable.java index 7a3cca260..133112fb8 100644 --- a/src/share/classes/sun/tools/jconsole/inspector/XTable.java +++ b/src/share/classes/sun/tools/jconsole/inspector/XTable.java @@ -25,10 +25,13 @@ package sun.tools.jconsole.inspector; -import javax.swing.*; -import javax.swing.table.*; -import java.awt.*; -import java.io.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import javax.swing.JTable; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableCellRenderer; public abstract class XTable extends JTable { static final int NAME_COLUMN = 0; @@ -38,8 +41,9 @@ public abstract class XTable extends JTable { public XTable () { super(); - TableSorter sorter; - setModel(sorter = new TableSorter()); + @SuppressWarnings("serial") + final TableSorter sorter = new TableSorter(); + setModel(sorter); sorter.addMouseListenerToHeaderInTable(this); setRowSelectionAllowed(false); setColumnSelectionAllowed(false); @@ -54,6 +58,14 @@ public abstract class XTable extends JTable { return editableColor; } + /** + * Called by TableSorter if a mouse event requests to sort the rows. + * @param column the column against which the rows are sorted + */ + void sortRequested(int column) { + // This is a hook for subclasses + } + /** * This returns the select index as the table was at initialization */ @@ -67,7 +79,7 @@ public abstract class XTable extends JTable { public int convertRowToIndex(int row) { if (row == -1) return row; if (getModel() instanceof TableSorter) { - return (((TableSorter) getModel()).getInvertedIndex()[row]); + return ((TableSorter) getModel()).getIndexOfRow(row); } else { return row; } @@ -97,6 +109,7 @@ public abstract class XTable extends JTable { //JTable re-implementation //attribute can be editable even if unavailable + @Override public boolean isCellEditable(int row, int col) { return ((isTableEditable() && isColumnEditable(col) && isWritable(row) @@ -118,6 +131,7 @@ public abstract class XTable extends JTable { * This method sets read write rows to be blue, and other rows to be their * default rendered colour. */ + @Override public TableCellRenderer getCellRenderer(int row, int column) { DefaultTableCellRenderer tcr = (DefaultTableCellRenderer) super.getCellRenderer(row,column); @@ -146,6 +160,7 @@ public abstract class XTable extends JTable { return tcr; } + @Override public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { Component comp = super.prepareRenderer(renderer, row, column); diff --git a/src/share/classes/sun/tools/jconsole/inspector/XTextFieldEditor.java b/src/share/classes/sun/tools/jconsole/inspector/XTextFieldEditor.java index 31743aaa4..6adf00e3b 100644 --- a/src/share/classes/sun/tools/jconsole/inspector/XTextFieldEditor.java +++ b/src/share/classes/sun/tools/jconsole/inspector/XTextFieldEditor.java @@ -26,22 +26,30 @@ package sun.tools.jconsole.inspector; import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; import java.util.EventObject; -import java.awt.event.*; -import java.awt.dnd.DragSourceDropEvent; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.table.*; +import javax.swing.JMenuItem; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; +import javax.swing.event.EventListenerList; +import javax.swing.table.TableCellEditor; @SuppressWarnings("serial") public class XTextFieldEditor extends XTextField implements TableCellEditor { - protected EventListenerList listenerList = new EventListenerList(); + protected EventListenerList evtListenerList = new EventListenerList(); protected ChangeEvent changeEvent = new ChangeEvent(this); private FocusListener editorFocusListener = new FocusAdapter() { + @Override public void focusLost(FocusEvent e) { - fireEditingStopped(); + // fireEditingStopped(); + // must not call fireEditingStopped() here! } }; @@ -51,6 +59,7 @@ public class XTextFieldEditor extends XTextField implements TableCellEditor { } //edition stopped ou JMenuItem selection & JTextField selection + @Override public void actionPerformed(ActionEvent e) { super.actionPerformed(e); if ((e.getSource() instanceof JMenuItem) || @@ -67,16 +76,16 @@ public class XTextFieldEditor extends XTextField implements TableCellEditor { //TableCellEditor implementation public void addCellEditorListener(CellEditorListener listener) { - listenerList.add(CellEditorListener.class,listener); + evtListenerList.add(CellEditorListener.class,listener); } public void removeCellEditorListener(CellEditorListener listener) { - listenerList.remove(CellEditorListener.class, listener); + evtListenerList.remove(CellEditorListener.class, listener); } protected void fireEditingStopped() { CellEditorListener listener; - Object[] listeners = listenerList.getListenerList(); + Object[] listeners = evtListenerList.getListenerList(); for (int i=0;i< listeners.length;i++) { if (listeners[i] == CellEditorListener.class) { listener = (CellEditorListener) listeners[i+1]; @@ -87,7 +96,7 @@ public class XTextFieldEditor extends XTextField implements TableCellEditor { protected void fireEditingCanceled() { CellEditorListener listener; - Object[] listeners = listenerList.getListenerList(); + Object[] listeners = evtListenerList.getListenerList(); for (int i=0;i< listeners.length;i++) { if (listeners[i] == CellEditorListener.class) { listener = (CellEditorListener) listeners[i+1]; -- GitLab From fbcf965ac1b9d6932cc7ac84a80040b01b929286 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Fri, 8 Aug 2008 15:08:57 +0200 Subject: [PATCH 043/139] 6334663: TabularDataSupport should be able to return values in the insertion order Reviewed-by: dfuchs --- .../DefaultMXBeanMappingFactory.java | 2 +- .../openmbean/TabularDataSupport.java | 20 +- .../openmbean/TabularDataOrderTest.java | 190 ++++++++++++++++++ 3 files changed, 207 insertions(+), 5 deletions(-) create mode 100644 test/javax/management/openmbean/TabularDataOrderTest.java diff --git a/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java b/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java index 2d97c97b9..3509c40f3 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java @@ -825,7 +825,7 @@ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory { final TabularData table = (TabularData) openValue; final Collection rows = cast(table.values()); final Map valueMap = - sortedMap ? newSortedMap() : newMap(); + sortedMap ? newSortedMap() : newInsertionOrderMap(); for (CompositeData row : rows) { final Object key = keyMapping.fromOpenValue(row.get("key")); diff --git a/src/share/classes/javax/management/openmbean/TabularDataSupport.java b/src/share/classes/javax/management/openmbean/TabularDataSupport.java index f01b8e0a7..369efb2f2 100644 --- a/src/share/classes/javax/management/openmbean/TabularDataSupport.java +++ b/src/share/classes/javax/management/openmbean/TabularDataSupport.java @@ -29,15 +29,18 @@ package javax.management.openmbean; // java import // +import com.sun.jmx.mbeanserver.GetPropertyAction; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; +import java.security.AccessController; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -79,12 +82,13 @@ public class TabularDataSupport /** * @serial This tabular data instance's contents: a {@link HashMap} */ + // field cannot be final because of clone method private Map dataMap; /** * @serial This tabular data instance's tabular type */ - private TabularType tabularType; + private final TabularType tabularType; /** * The array of item names that define the index used for rows (convenience field) @@ -109,7 +113,7 @@ public class TabularDataSupport */ public TabularDataSupport(TabularType tabularType) { - this(tabularType, 101, 0.75f); + this(tabularType, 16, 0.75f); } /** @@ -141,10 +145,18 @@ public class TabularDataSupport List tmpNames = tabularType.getIndexNames(); this.indexNamesArray = tmpNames.toArray(new String[tmpNames.size()]); + // Since LinkedHashMap was introduced in SE 1.4, it's conceivable even + // if very unlikely that we might be the server of a 1.3 client. In + // that case you'll need to set this property. See CR 6334663. + String useHashMapProp = AccessController.doPrivileged( + new GetPropertyAction("jmx.tabular.data.hash.map")); + boolean useHashMap = "true".equalsIgnoreCase(useHashMapProp); + // Construct the empty contents HashMap // - this.dataMap = - new HashMap(initialCapacity, loadFactor); + this.dataMap = useHashMap ? + new HashMap(initialCapacity, loadFactor) : + new LinkedHashMap(initialCapacity, loadFactor); } diff --git a/test/javax/management/openmbean/TabularDataOrderTest.java b/test/javax/management/openmbean/TabularDataOrderTest.java new file mode 100644 index 000000000..877c41b36 --- /dev/null +++ b/test/javax/management/openmbean/TabularDataOrderTest.java @@ -0,0 +1,190 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6334663 + * @summary Test that TabularDataSupport preserves the order elements were added + * @author Eamonn McManus + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import javax.management.JMX; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectName; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +public class TabularDataOrderTest { + private static String failure; + + private static final String COMPAT_PROP_NAME = "jmx.tabular.data.hash.map"; + + private static final String[] intNames = { + "unus", "duo", "tres", "quatuor", "quinque", "sex", "septem", + "octo", "novem", "decim", + }; + private static final Map stringToValue = + new LinkedHashMap(); + static { + for (int i = 0; i < intNames.length; i++) + stringToValue.put(intNames[i], i + 1); + } + + public static interface TestMXBean { + public Map getMap(); + } + + public static class TestImpl implements TestMXBean { + public Map getMap() { + return stringToValue; + } + } + + private static final CompositeType ct; + private static final TabularType tt; + static { + try { + ct = new CompositeType( + "a.b.c", "name and int", + new String[] {"name", "int"}, + new String[] {"name of integer", "value of integer"}, + new OpenType[] {SimpleType.STRING, SimpleType.INTEGER}); + tt = new TabularType( + "d.e.f", "name and int indexed by name", ct, + new String[] {"name"}); + } catch (OpenDataException e) { + throw new AssertionError(e); + } + } + + private static TabularData makeTable() throws OpenDataException { + TabularData td = new TabularDataSupport(tt); + for (Map.Entry entry : stringToValue.entrySet()) { + CompositeData cd = new CompositeDataSupport( + ct, + new String[] {"name", "int"}, + new Object[] {entry.getKey(), entry.getValue()}); + td.put(cd); + } + return td; + } + + public static void main(String[] args) throws Exception { + System.out.println("Testing standard behaviour"); + TabularData td = makeTable(); + System.out.println(td); + + // Test that a default TabularData has the order keys were added in + int last = 0; + boolean ordered = true; + for (Object x : td.values()) { + CompositeData cd = (CompositeData) x; + String name = (String) cd.get("name"); + int value = (Integer) cd.get("int"); + System.out.println(name + " = " + value); + if (last + 1 != value) + ordered = false; + last = value; + } + if (!ordered) + fail("Order not preserved"); + + // Now test the undocumented property that causes HashMap to be used + // instead of LinkedHashMap, in case serializing to a 1.3 client. + // We serialize and deserialize in case the implementation handles + // this at serialization time. Then we look at object fields; that's + // not guaranteed to work but at worst it will fail spuriously and + // we'll have to update the test. + System.out.println("Testing compatible behaviour"); + System.setProperty(COMPAT_PROP_NAME, "true"); + td = makeTable(); + System.out.println(td); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + ObjectOutputStream oout = new ObjectOutputStream(bout); + oout.writeObject(td); + oout.close(); + byte[] bytes = bout.toByteArray(); + ByteArrayInputStream bin = new ByteArrayInputStream(bytes); + ObjectInputStream oin = new ObjectInputStream(bin); + td = (TabularData) oin.readObject(); + boolean found = false; + for (Field f : td.getClass().getDeclaredFields()) { + if (Modifier.isStatic(f.getModifiers())) + continue; + f.setAccessible(true); + Object x = f.get(td); + if (x != null && x.getClass() == HashMap.class) { + found = true; + System.out.println( + x.getClass().getName() + " TabularDataSupport." + + f.getName() + " = " + x); + break; + } + } + if (!found) { + fail("TabularDataSupport does not contain HashMap though " + + COMPAT_PROP_NAME + "=true"); + } + System.clearProperty(COMPAT_PROP_NAME); + + System.out.println("Testing MXBean behaviour"); + MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + ObjectName name = new ObjectName("a:b=c"); + mbs.registerMBean(new TestImpl(), name); + TestMXBean proxy = JMX.newMXBeanProxy(mbs, name, TestMXBean.class); + Map map = proxy.getMap(); + List origNames = new ArrayList(stringToValue.keySet()); + List proxyNames = new ArrayList(map.keySet()); + if (!origNames.equals(proxyNames)) + fail("Order mangled after passage through MXBean: " + proxyNames); + + if (failure == null) + System.out.println("TEST PASSED"); + else + throw new Exception("TEST FAILED: " + failure); + } + + private static void fail(String why) { + System.out.println("FAILED: " + why); + failure = why; + } +} -- GitLab From 5fa4e2bf54c5619798e3e89b7a443136b21f2f57 Mon Sep 17 00:00:00 2001 From: ohair Date: Fri, 8 Aug 2008 08:50:30 -0700 Subject: [PATCH 044/139] 6734977: Fix build failure regarding the now deleted file jdk/README.html Reviewed-by: xdono, tbell --- make/ASSEMBLY_EXCEPTION | 27 - make/LICENSE | 347 --------- make/README | 34 - make/THIRD_PARTY_README | 1616 --------------------------------------- make/common/Release.gmk | 20 +- 5 files changed, 9 insertions(+), 2035 deletions(-) delete mode 100644 make/ASSEMBLY_EXCEPTION delete mode 100644 make/LICENSE delete mode 100644 make/README delete mode 100644 make/THIRD_PARTY_README diff --git a/make/ASSEMBLY_EXCEPTION b/make/ASSEMBLY_EXCEPTION deleted file mode 100644 index 8b7ac1d08..000000000 --- a/make/ASSEMBLY_EXCEPTION +++ /dev/null @@ -1,27 +0,0 @@ - -OPENJDK ASSEMBLY EXCEPTION - -The OpenJDK source code made available by Sun at openjdk.java.net and -openjdk.dev.java.net ("OpenJDK Code") is distributed under the terms of the -GNU General Public License version 2 -only ("GPL2"), with the following clarification and special exception. - - Linking this OpenJDK Code statically or dynamically with other code - is making a combined work based on this library. Thus, the terms - and conditions of GPL2 cover the whole combination. - - As a special exception, Sun gives you permission to link this - OpenJDK Code with certain code licensed by Sun as indicated at - http://openjdk.java.net/legal/exception-modules-2007-05-08.html - ("Designated Exception Modules") to produce an executable, - regardless of the license terms of the Designated Exception Modules, - and to copy and distribute the resulting executable under GPL2, - provided that the Designated Exception Modules continue to be - governed by the licenses under which they were offered by Sun. - -As such, it allows licensees and sublicensees of Sun's GPL2 OpenJDK Code to -build an executable that includes those portions of necessary code that Sun -could not provide under GPL2 (or that Sun has provided under GPL2 with the -Classpath exception). If you modify or add to the OpenJDK code, that new -GPL2 code may still be combined with Designated Exception Modules if the -new code is made subject to this exception by its copyright holder. diff --git a/make/LICENSE b/make/LICENSE deleted file mode 100644 index eeab58c21..000000000 --- a/make/LICENSE +++ /dev/null @@ -1,347 +0,0 @@ -The GNU General Public License (GPL) - -Version 2, June 1991 - -Copyright (C) 1989, 1991 Free Software Foundation, Inc. -59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -Everyone is permitted to copy and distribute verbatim copies of this license -document, but changing it is not allowed. - -Preamble - -The licenses for most software are designed to take away your freedom to share -and change it. By contrast, the GNU General Public License is intended to -guarantee your freedom to share and change free software--to make sure the -software is free for all its users. This General Public License applies to -most of the Free Software Foundation's software and to any other program whose -authors commit to using it. (Some other Free Software Foundation software is -covered by the GNU Library General Public License instead.) You can apply it to -your programs, too. - -When we speak of free software, we are referring to freedom, not price. Our -General Public Licenses are designed to make sure that you have the freedom to -distribute copies of free software (and charge for this service if you wish), -that you receive source code or can get it if you want it, that you can change -the software or use pieces of it in new free programs; and that you know you -can do these things. - -To protect your rights, we need to make restrictions that forbid anyone to deny -you these rights or to ask you to surrender the rights. These restrictions -translate to certain responsibilities for you if you distribute copies of the -software, or if you modify it. - -For example, if you distribute copies of such a program, whether gratis or for -a fee, you must give the recipients all the rights that you have. You must -make sure that they, too, receive or can get the source code. And you must -show them these terms so they know their rights. - -We protect your rights with two steps: (1) copyright the software, and (2) -offer you this license which gives you legal permission to copy, distribute -and/or modify the software. - -Also, for each author's protection and ours, we want to make certain that -everyone understands that there is no warranty for this free software. If the -software is modified by someone else and passed on, we want its recipients to -know that what they have is not the original, so that any problems introduced -by others will not reflect on the original authors' reputations. - -Finally, any free program is threatened constantly by software patents. We -wish to avoid the danger that redistributors of a free program will -individually obtain patent licenses, in effect making the program proprietary. -To prevent this, we have made it clear that any patent must be licensed for -everyone's free use or not licensed at all. - -The precise terms and conditions for copying, distribution and modification -follow. - -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - -0. This License applies to any program or other work which contains a notice -placed by the copyright holder saying it may be distributed under the terms of -this General Public License. The "Program", below, refers to any such program -or work, and a "work based on the Program" means either the Program or any -derivative work under copyright law: that is to say, a work containing the -Program or a portion of it, either verbatim or with modifications and/or -translated into another language. (Hereinafter, translation is included -without limitation in the term "modification".) Each licensee is addressed as -"you". - -Activities other than copying, distribution and modification are not covered by -this License; they are outside its scope. The act of running the Program is -not restricted, and the output from the Program is covered only if its contents -constitute a work based on the Program (independent of having been made by -running the Program). Whether that is true depends on what the Program does. - -1. You may copy and distribute verbatim copies of the Program's source code as -you receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice and -disclaimer of warranty; keep intact all the notices that refer to this License -and to the absence of any warranty; and give any other recipients of the -Program a copy of this License along with the Program. - -You may charge a fee for the physical act of transferring a copy, and you may -at your option offer warranty protection in exchange for a fee. - -2. You may modify your copy or copies of the Program or any portion of it, thus -forming a work based on the Program, and copy and distribute such modifications -or work under the terms of Section 1 above, provided that you also meet all of -these conditions: - - a) You must cause the modified files to carry prominent notices stating - that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in whole or - in part contains or is derived from the Program or any part thereof, to be - licensed as a whole at no charge to all third parties under the terms of - this License. - - c) If the modified program normally reads commands interactively when run, - you must cause it, when started running for such interactive use in the - most ordinary way, to print or display an announcement including an - appropriate copyright notice and a notice that there is no warranty (or - else, saying that you provide a warranty) and that users may redistribute - the program under these conditions, and telling the user how to view a copy - of this License. (Exception: if the Program itself is interactive but does - not normally print such an announcement, your work based on the Program is - not required to print an announcement.) - -These requirements apply to the modified work as a whole. If identifiable -sections of that work are not derived from the Program, and can be reasonably -considered independent and separate works in themselves, then this License, and -its terms, do not apply to those sections when you distribute them as separate -works. But when you distribute the same sections as part of a whole which is a -work based on the Program, the distribution of the whole must be on the terms -of this License, whose permissions for other licensees extend to the entire -whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest your -rights to work written entirely by you; rather, the intent is to exercise the -right to control the distribution of derivative or collective works based on -the Program. - -In addition, mere aggregation of another work not based on the Program with the -Program (or with a work based on the Program) on a volume of a storage or -distribution medium does not bring the other work under the scope of this -License. - -3. You may copy and distribute the Program (or a work based on it, under -Section 2) in object code or executable form under the terms of Sections 1 and -2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable source - code, which must be distributed under the terms of Sections 1 and 2 above - on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three years, to - give any third party, for a charge no more than your cost of physically - performing source distribution, a complete machine-readable copy of the - corresponding source code, to be distributed under the terms of Sections 1 - and 2 above on a medium customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer to - distribute corresponding source code. (This alternative is allowed only - for noncommercial distribution and only if you received the program in - object code or executable form with such an offer, in accord with - Subsection b above.) - -The source code for a work means the preferred form of the work for making -modifications to it. For an executable work, complete source code means all -the source code for all modules it contains, plus any associated interface -definition files, plus the scripts used to control compilation and installation -of the executable. However, as a special exception, the source code -distributed need not include anything that is normally distributed (in either -source or binary form) with the major components (compiler, kernel, and so on) -of the operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the source -code from the same place counts as distribution of the source code, even though -third parties are not compelled to copy the source along with the object code. - -4. You may not copy, modify, sublicense, or distribute the Program except as -expressly provided under this License. Any attempt otherwise to copy, modify, -sublicense or distribute the Program is void, and will automatically terminate -your rights under this License. However, parties who have received copies, or -rights, from you under this License will not have their licenses terminated so -long as such parties remain in full compliance. - -5. You are not required to accept this License, since you have not signed it. -However, nothing else grants you permission to modify or distribute the Program -or its derivative works. These actions are prohibited by law if you do not -accept this License. Therefore, by modifying or distributing the Program (or -any work based on the Program), you indicate your acceptance of this License to -do so, and all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - -6. Each time you redistribute the Program (or any work based on the Program), -the recipient automatically receives a license from the original licensor to -copy, distribute or modify the Program subject to these terms and conditions. -You may not impose any further restrictions on the recipients' exercise of the -rights granted herein. You are not responsible for enforcing compliance by -third parties to this License. - -7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), conditions -are imposed on you (whether by court order, agreement or otherwise) that -contradict the conditions of this License, they do not excuse you from the -conditions of this License. If you cannot distribute so as to satisfy -simultaneously your obligations under this License and any other pertinent -obligations, then as a consequence you may not distribute the Program at all. -For example, if a patent license would not permit royalty-free redistribution -of the Program by all those who receive copies directly or indirectly through -you, then the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply and -the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any patents or -other property right claims or to contest validity of any such claims; this -section has the sole purpose of protecting the integrity of the free software -distribution system, which is implemented by public license practices. Many -people have made generous contributions to the wide range of software -distributed through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing to -distribute software through any other system and a licensee cannot impose that -choice. - -This section is intended to make thoroughly clear what is believed to be a -consequence of the rest of this License. - -8. If the distribution and/or use of the Program is restricted in certain -countries either by patents or by copyrighted interfaces, the original -copyright holder who places the Program under this License may add an explicit -geographical distribution limitation excluding those countries, so that -distribution is permitted only in or among countries not thus excluded. In -such case, this License incorporates the limitation as if written in the body -of this License. - -9. The Free Software Foundation may publish revised and/or new versions of the -General Public License from time to time. Such new versions will be similar in -spirit to the present version, but may differ in detail to address new problems -or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any later -version", you have the option of following the terms and conditions either of -that version or of any later version published by the Free Software Foundation. -If the Program does not specify a version number of this License, you may -choose any version ever published by the Free Software Foundation. - -10. If you wish to incorporate parts of the Program into other free programs -whose distribution conditions are different, write to the author to ask for -permission. For software which is copyrighted by the Free Software Foundation, -write to the Free Software Foundation; we sometimes make exceptions for this. -Our decision will be guided by the two goals of preserving the free status of -all derivatives of our free software and of promoting the sharing and reuse of -software generally. - -NO WARRANTY - -11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR -THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE -STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE -PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND -PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, -YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL -ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE -PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR -INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA -BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER -OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -END OF TERMS AND CONDITIONS - -How to Apply These Terms to Your New Programs - -If you develop a new program, and you want it to be of the greatest possible -use to the public, the best way to achieve this is to make it free software -which everyone can redistribute and change under these terms. - -To do so, attach the following notices to the program. It is safest to attach -them to the start of each source file to most effectively convey the exclusion -of warranty; and each file should have at least the "copyright" line and a -pointer to where the full notice is found. - - One line to give the program's name and a brief idea of what it does. - - Copyright (C) - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the Free - Software Foundation; either version 2 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 General Public License for - more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., 59 - Temple Place, Suite 330, Boston, MA 02111-1307 USA - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this when it -starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author Gnomovision comes - with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free - software, and you are welcome to redistribute it under certain conditions; - type 'show c' for details. - -The hypothetical commands 'show w' and 'show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may be -called something other than 'show w' and 'show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your school, -if any, to sign a "copyright disclaimer" for the program, if necessary. Here -is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - 'Gnomovision' (which makes passes at compilers) written by James Hacker. - - signature of Ty Coon, 1 April 1989 - - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General Public -License instead of this License. - - -"CLASSPATH" EXCEPTION TO THE GPL - -Certain source files distributed by Sun Microsystems, Inc. are subject to -the following clarification and special exception to the GPL, but only where -Sun has expressly included in the particular source file's header the words -"Sun designates this particular file as subject to the "Classpath" exception -as provided by Sun in the LICENSE file that accompanied this code." - - Linking this library statically or dynamically with other modules is making - a combined work based on this library. Thus, the terms and conditions of - the GNU General Public License cover the whole combination. - - As a special exception, the copyright holders of this library give you - permission to link this library with independent modules to produce an - executable, regardless of the license terms of these independent modules, - and to copy and distribute the resulting executable under terms of your - choice, provided that you also meet, for each linked independent module, - the terms and conditions of the license of that module. An independent - module is a module which is not derived from or based on this library. If - you modify this library, you may extend this exception to your version of - the library, but you are not obligated to do so. If you do not wish to do - so, delete this exception statement from your version. diff --git a/make/README b/make/README deleted file mode 100644 index d774ab80b..000000000 --- a/make/README +++ /dev/null @@ -1,34 +0,0 @@ -README: - This file should be located at the top of the jdk Mercurial repository. - - See http://openjdk.java.net/ for more information about the OpenJDK. - -Simple Build Instructions: - - 1. Download and install a JDK 6 from - http://java.sun.com/javase/downloads/index.jsp - Set the environment variable ALT_BOOTDIR to the location of this JDK 6. - - 2. Download and install the Binary Plugs for the most recent JDK7 from - http://download.java.net/openjdk/jdk7/ - Set the environment variable ALT_BINARY_PLUGS_PATH to the location of - these binary plugs. - - 3. Either download and install the latest JDK7 from - http://download.java.net/openjdk/jdk7/, or build your own complete - OpenJDK7 by using the top level Makefile in the OpenJDK Mercurial forest. - Set the environment variable ALT_JDK_IMPORT_PATH to the location of - this latest JDK7 or OpenJDK7 build. - - 4. Check the sanity of doing a build with the current machine: - cd make && gnumake sanity - See README-builds.html if you run into problems. - - 5. Do a partial build of the jdk: - cd make && gnumake all - - 6. Construct the images: - cd make && gnumake images - The resulting JDK image should be found in build/*/j2sdk-image - - diff --git a/make/THIRD_PARTY_README b/make/THIRD_PARTY_README deleted file mode 100644 index 9f4d7e508..000000000 --- a/make/THIRD_PARTY_README +++ /dev/null @@ -1,1616 +0,0 @@ -DO NOT TRANSLATE OR LOCALIZE. - -%% This notice is provided with respect to Thai dictionary for text breaking, which may be included with this software: - ---- begin of LICENSE file --- - -Copyright (C) 1982 The Royal Institute, Thai Royal Government. - -Copyright (C) 1998 National Electronics and Computer Technology Center, - National Science and Technology Development Agency, - Ministry of Science Technology and Environment, - Thai Royal Government. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the "Software"), to -deal in the Software without restriction, including without -limitation the rights to use, copy, modify, merge, publish, distribute, -sublicense, and/or sell copies of the Software, and to permit persons to -whom the Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -USE OR OTHER DEALINGS IN THE SOFTWARE. - ---- end of LICENSE file --- -%% This notice is provided with respect to ASM, which may be included with this software: -Copyright (c) 2000-2005 INRIA, France Telecom -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holders nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -THE POSSIBILITY OF SUCH DAMAGE. -%% This notice is provided with respect to zlib 1.1.3, which may be included with this software: - -Acknowledgments: - - The deflate format used by zlib was defined by Phil Katz. The deflate - and zlib specifications were written by L. Peter Deutsch. Thanks to all the - people who reported problems and suggested various improvements in zlib; - they are too numerous to cite here. - -Copyright notice: - - (C) 1995-1998 Jean-loup Gailly and Mark Adler - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jean-loup Gailly Mark Adler - jloup@gzip.org madler@alumni.caltech.edu - -If you use the zlib library in a product, we would appreciate *not* -receiving lengthy legal documents to sign. The sources are provided -for free but without warranty of any kind. The library has been -entirely written by Jean-loup Gailly and Mark Adler; it does not -include third-party code. - -If you redistribute modified sources, we would appreciate that you include -in the file ChangeLog history information documenting your changes. - -%% This notice is provided with respect to W3C (DTD for XML Signatures), which may be included with this software: -W3C® SOFTWARE NOTICE AND LICENSE -Copyright © 1994-2002 World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/ -This W3C work (including software, documents, or other related items) is being provided by the copyright holders under the following license. By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions: -Permission to use, copy, modify, and distribute this software and its documentation, with or without modification,  for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make: -1.The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. -2.Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, a short notice of the following form (hypertext is preferred, text is permitted) should be used within the body of any redistributed or derivative code: "Copyright © [$date-of-software] World Wide Web Consortium, (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/" -3.Notice of any changes or modifications to the W3C files, including the date changes were made. (We recommend you provide URIs to the location from which the code is derived.) -THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. -COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. -The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the software without specific, written prior permission. Title to copyright in this software and any associated documentation will at all times remain with copyright holders. -____________________________________ -This formulation of W3C's notice and license became active on August 14 1998 so as to improve compatibility with GPL. This version ensures that W3C software licensing terms are no more restrictive than GPL and consequently W3C software may be distributed in GPL packages. See the older formulation for the policy prior to this date. Please see our Copyright FAQ for common questions about using materials from our site, including specific terms and conditions for packages like libwww, Amaya, and Jigsaw. Other questions about this notice can be directed to site-policy@w3.org. -  -%% This notice is provided with respect to jscheme.jar, which may be included with this software: -Software License Agreement -Copyright © 1998-2002 by Peter Norvig. -Permission is granted to anyone to use this software, in source or object code form, on any computer system, and to modify, compile, decompile, run, and redistribute it to anyone else, subject to the following restrictions: -1.The author makes no warranty of any kind, either expressed or implied, about the suitability of this software for any purpose. -2.The author accepts no liability of any kind for damages or other consequences of the use of this software, even if they arise from defects in the software. -3.The origin of this software must not be misrepresented, either by explicit claim or by omission. -4.Altered versions must be plainly marked as such, and must not be misrepresented as being the original software. Altered versions may be distributed in packages under other licenses (such as the GNU license). -If you find this software useful, it would be nice if you let me (peter@norvig.com) know about it, and nicer still if you send me modifications that you are willing to share. However, you are not required to do so. - - -%% This notice is provided with respect to PC/SC Lite for Suse Linux v. 1.1.1, which may be included with this software: - -Copyright (c) 1999-2004 David Corcoran -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -Changes to this license can be made only by the copyright author with -explicit written consent. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -%% This notice is provided with respect to IAIK PKCS Wrapper, which may be included with this software: - -Copyright (c) 2002 Graz University of Technology. All rights reserved. -Redistribution and use in source and binary forms, with or without modification,are permitted provided that the following conditions are met: - - -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: - - "This product includes software developed by IAIK of Graz University of Technology." - - Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. - -4. The names "Graz University of Technology" and "IAIK of Graz University of Technology" must not be used to endorse or promote products derived from this software without prior written permission. - -5. Products derived from this software may not be called "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior written permission of Graz University of Technology. - -THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -%% This notice is provided with respect to Document Object Model (DOM) v. Level 3, which may be included with this software: - -W3Cýý SOFTWARE NOTICE AND LICENSE - -http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 - -This work (and included software, documentation such as READMEs, or other related items) is being -provided by the copyright holders under the following license. By obtaining, using and/or copying this work, you -(the licensee) agree that you have read, understood, and will comply with the following terms and conditions. - -Permission to copy, modify, and distribute this software and its documentation, with or without modification, for -any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies -of the software and documentation or portions thereof, including modifications: - 1.The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. - 2.Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the - W3C Software Short Notice should be included (hypertext is preferred, text is permitted) within the body - of any redistributed or derivative code. - 3.Notice of any changes or modifications to the files, including the date changes were made. (We - recommend you provide URIs to the location from which the code is derived.) -THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKENO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, -WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THEUSE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS,COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. - -COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL ORCONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. -The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the -software without specific, written prior permission. Title to copyright in this software and any associated -documentation will at all times remain with copyright holders. - -____________________________________ - -This formulation of W3C's notice and license became active on December 31 2002. This version removes the -copyright ownership notice such that this license can be used with materials other than those owned by the -W3C, reflects that ERCIM is now a host of the W3C, includes references to this specific dated version of the -license, and removes the ambiguous grant of "use". Otherwise, this version is the same as the previous -version and is written so as to preserve the Free Software Foundation's assessment of GPL compatibility and -OSI's certification under the Open Source Definition. Please see our Copyright FAQ for common questions -about using materials from our site, including specific terms and conditions for packages like libwww, Amaya, -and Jigsaw. Other questions about this notice can be directed to -site-policy@w3.org. - -%% This notice is provided with respect to Xalan, Xerces, which may be included with this software: - -/* - * The Apache Software License, Version 1.1 - * - * - * Copyright (c) 1999-2003 The Apache Software Foundation. All rights * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. * - * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, - * if any, must include the following acknowledgment: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * - * 4. The names "Xerces" and "Apache Software Foundation" must - * not be used to endorse or promote products derived from this - * software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache", - * nor may "Apache" appear in their name, without prior written - * permission of the Apache Software Foundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation and was - * originally based on software copyright (c) 1999, International - * Business Machines, Inc., http://www.ibm.com. For more - * information on the Apache Software Foundation, please see - * - -%% This notice is provided with respect to JavaScript, which may be included with this software: - -AMENDMENTS -The Netscape Public License Version 1.1 ("NPL") consists of the Mozilla Public License Version 1.1 with the following Amendments, including Exhibit A-Netscape Public License.  Files identified with "Exhibit A-Netscape Public License" are governed by the Netscape Public License Version 1.1. -Additional Terms applicable to the Netscape Public License. -I. Effect. -These additional terms described in this Netscape Public License -- Amendments shall apply to the Mozilla Communicator client code and to all Covered Code under this License. -II. ''Netscape's Branded Code'' means Covered Code that Netscape distributes and/or permits others to distribute under one or more trademark(s) which are controlled by Netscape but which are not licensed for use under this License. -III. Netscape and logo. -This License does not grant any rights to use the trademarks "Netscape'', the "Netscape N and horizon'' logo or the "Netscape lighthouse" logo, "Netcenter", "Gecko", "Java" or "JavaScript", "Smart Browsing" even if such marks are included in the Original Code or Modifications. -IV. Inability to Comply Due to Contractual Obligation. -Prior to licensing the Original Code under this License, Netscape has licensed third party code for use in Netscape's Branded Code. To the extent that Netscape is limited contractually from making such third party code available under this License, Netscape may choose to reintegrate such code into Covered Code without being required to distribute such code in Source Code form, even if such code would otherwise be considered ''Modifications'' under this License. -V. Use of Modifications and Covered Code by Initial Developer. -V.1. In General. -The obligations of Section 3 apply to Netscape, except to the extent specified in this Amendment, Section V.2 and V.3. -V.2. Other Products. -Netscape may include Covered Code in products other than the Netscape's Branded Code which are released by Netscape during the two (2) years following the release date of the Original Code, without such additional products becoming subject to the terms of this License, and may license such additional products on different terms from those contained in this License. -V.3. Alternative Licensing. -Netscape may license the Source Code of Netscape's Branded Code, including Modifications incorporated therein, without such Netscape Branded Code becoming subject to the terms of this License, and may license such Netscape Branded Code on different terms from those contained in this License. -  -VI. Litigation. -Notwithstanding the limitations of Section 11 above, the provisions regarding litigation in Section 11(a), (b) and (c) of the License shall apply to all disputes relating to this License. - -EXHIBIT A-Netscape Public License. -  -''The contents of this file are subject to the Netscape Public License Version 1.1 (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.mozilla.org/NPL/ -Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. -The Original Code is Mozilla Communicator client code, released March 31, 1998. -The Initial Developer of the Original Code is Netscape Communications Corporation. Portions created by Netscape are Copyright (C) 1998-1999 Netscape Communications Corporation. All Rights Reserved. -Contributor(s): ______________________________________. -  -Alternatively, the contents of this file may be used under the terms of the _____ license (the  "[___] License"), in which case the provisions of [______] License are applicable  instead of those above.  If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the NPL, indicate your decision by deleting  the provisions above and replace  them with the notice and other provisions required by the [___] License.  If you do not delete the provisions above, a recipient may use your version of this file under either the NPL or the [___] License." - -MOZILLA PUBLIC LICENSE -Version 1.1 - -1. Definitions. -1.0.1. "Commercial Use" means distribution or otherwise making the Covered Code available to a third party. -1.1. ''Contributor'' means each entity that creates or contributes to the creation of Modifications. -1.2. ''Contributor Version'' means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor. -1.3. ''Covered Code'' means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof. -1.4. ''Electronic Distribution Mechanism'' means a mechanism generally accepted in the software development community for the electronic transfer of data. -1.5. ''Executable'' means Covered Code in any form other than Source Code. -1.6. ''Initial Developer'' means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A. -1.7. ''Larger Work'' means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. -1.8. ''License'' means this document. -1.8.1. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. -1.9. ''Modifications'' means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is: -A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications. -B. Any new file that contains any part of the Original Code or previous Modifications. -  -1.10. ''Original Code'' means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License. -1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation,  method, process, and apparatus claims, in any patent Licensable by grantor. -1.11. ''Source Code'' means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge. -1.12. "You'' (or "Your")  means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You'' includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control'' means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. -2. Source Code License. -2.1. The Initial Developer Grant. -The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: -(a)  under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and -(b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof). -  -(c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License. -(d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code;  or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices. -  -2.2. Contributor Grant. -Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license -  -(a)  under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and -(b) under Patent Claims infringed by the making, using, or selling of  Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of  Modifications made by that Contributor with its Contributor Version (or portions of such combination). -(c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first makes Commercial Use of the Covered Code. -(d)    Notwithstanding Section 2.2(b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2)  separate from the Contributor Version;  3)  for infringements caused by: i) third party modifications of Contributor Version or ii)  the combination of Modifications made by that Contributor with other software  (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor. - -3. Distribution Obligations. -3.1. Application of License. -The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5. -3.2. Availability of Source Code. -Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. -3.3. Description of Modifications. -You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code. -3.4. Intellectual Property Matters -(a) Third Party Claims. -If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL'' which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained. -(b) Contributor APIs. -If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file. -  -          (c)    Representations. -Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License. - -3.5. Required Notices. -You must duplicate the notice in Exhibit A in each file of the Source Code.  If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice.  If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A.  You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code.  You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. -3.6. Distribution of Executable Versions. -You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. -3.7. Larger Works. -You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code. -4. Inability to Comply Due to Statute or Regulation. -If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. -5. Application of this License. -This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code. -6. Versions of the License. -6.1. New Versions. -Netscape Communications Corporation (''Netscape'') may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number. -6.2. Effect of New Versions. -Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License. -6.3. Derivative Works. -If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases ''Mozilla'', ''MOZILLAPL'', ''MOZPL'', ''Netscape'', "MPL", ''NPL'' or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.) -7. DISCLAIMER OF WARRANTY. -COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS'' BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. -8. TERMINATION. -8.1.  This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. -8.2.  If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as "Participant")  alleging that: -(a)  such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i)  agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant.  If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above. -(b)  any software, hardware, or device, other than such Participant's Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant. -8.3.  If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license. -8.4.  In the event of termination under Sections 8.1 or 8.2 above,  all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination. -9. LIMITATION OF LIABILITY. -UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. -10. U.S. GOVERNMENT END USERS. -The Covered Code is a ''commercial item,'' as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of ''commercial computer software'' and ''commercial computer software documentation,'' as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein. -11. MISCELLANEOUS. -This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. -12. RESPONSIBILITY FOR CLAIMS. -As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. -13. MULTIPLE-LICENSED CODE. -Initial Developer may designate portions of the Covered Code as "Multiple-Licensed".  "Multiple-Licensed" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the NPL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A. - -EXHIBIT A -Mozilla Public License. -``The contents of this file are subject to the Mozilla Public License Version 1.1 (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.mozilla.org/MPL/ -Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF -ANY KIND, either express or implied. See the License for the specific language governing rights and -limitations under the License. -The Original Code is ______________________________________. -The Initial Developer of the Original Code is ________________________. Portions created by - ______________________ are Copyright (C) ______ _______________________. All Rights -Reserved. -Contributor(s): ______________________________________. -Alternatively, the contents of this file may be used under the terms of the _____ license (the  "[___] License"), in which case the provisions of [______] License are applicable  instead of those above.  If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the MPL, indicate your decision by deleting  the provisions above and replace  them with the notice and other provisions required by the [___] License.  If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the [___] License." -[NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.] - -%% This notice is provided with respect to Mesa 3-D graphics library v. 5, which may be included with this software: - -Copyright (c) 2007 The Khronos Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. - -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. - -%% This notice is provided with respect to Byte Code Engineering Library (BCEL), which may be included with this software: - - Apache Software License - - /* -==================================================================== * The Apache Software License, Version 1.1 - * - * Copyright (c) 2001 The Apache Software Foundation. Allrights - * reserved. - * - * Redistribution and use in source and binary forms, withor without - * modification, are permitted provided that the followingconditions - * are met: - * - * 1. Redistributions of source code must retain the abovecopyright - * notice, this list of conditions and the followingdisclaimer. - * - * 2. Redistributions in binary form must reproduce theabove copyright - * notice, this list of conditions and the followingdisclaimer in - * the documentation and/or other materials providedwith the - * distribution. - * - * 3. The end-user documentation included with theredistribution, - * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation -(http://www.apache.org/)." - * Alternately, this acknowledgment may appear in thesoftware itself, - * if and wherever such third-party acknowledgmentsnormally appear. - * - * 4. The names "Apache" and "Apache Software Foundation"and - * "Apache BCEL" must not be used to endorse or promoteproducts - * derived from this software without prior writtenpermission. For - * written permission, please contact apache@apache.org. * - * 5. Products derived from this software may not be called"Apache", - * "Apache BCEL", nor may "Apache" appear in their name,without - * prior written permission of the Apache SoftwareFoundation. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED ORIMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIEDWARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSEARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWAREFOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVERCAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICTLIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING INANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF - * SUCH DAMAGE. - * -==================================================================== * - * This software consists of voluntary contributions madeby many - * individuals on behalf of the Apache Software -Foundation. For more - * information on the Apache Software Foundation, pleasesee - * . - */ - -%% This notice is provided with respect to Regexp, Regular Expression Package, which may be included with this software: - -The Apache Software License, Version 1.1 -Copyright (c) 2001 The Apache Software Foundation. All rights -reserved. -Redistribution and use in source and binary forms, with or without modification,are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the -distribution. - -3. The end-user documentation included with the redistribution, -if any, must include the following acknowledgment: -"This product includes software developed by the -Apache Software Foundation (http://www.apache.org/)." -Alternately, this acknowledgment may appear in the software itself, -if and wherever such third-party acknowledgments normally appear. - -4. The names "Apache" and "Apache Software Foundation" and -"Apache Turbine" must not be used to endorse or promote products -derived from this software without prior written permission. For -written permission, please contact apache@apache.org. - -5. Products derived from this software may not be called "Apache", -"Apache Turbine", nor may "Apache" appear in their name, without -prior written permission of the Apache Software Foundation. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR -ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -SUCH DAMAGE. - -==================================================================== -This software consists of voluntary contributions made by many -individuals on behalf of the Apache Software Foundation. For more -information on the Apache Software Foundation, please see - -http://www.apache.org. - -%% This notice is provided with respect to CUP Parser Generator for Java, which may be included with this software: - -CUP Parser Generator Copyright Notice, License, and Disclaimer - -Copyright 1996-1999 by Scott Hudson, Frank Flannery, C. Scott Ananian -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, provided thatthe above copyright notice appear in all copies and that both the copyrightnotice and this permission notice and warranty disclaimer appear in -supporting documentation, and that the names of the authors or their employersnot be used in advertising or publicity pertaining to distribution of -the software without specific, written prior permission. - -The authors and their employers disclaim all warranties with regard to thissoftware, including all implied warranties of merchantability and -fitness. In no event shall the authors or their employers be liable for anyspecial, indirect or consequential damages or any damages whatsoever -resulting from loss of use, data or profits, whether in an action of contract,negligence or other tortious action, arising out of or in connection withthe use or performance of this software. - -%% This notice is provided with respect to SAX v. 2.0.1, which may be included with this software: - -Copyright Status - - SAX is free! - - In fact, it's not possible to own a license to SAX, since it's been placed in the public - domain. - - No Warranty - - Because SAX is released to the public domain, there is no warranty for the design or for - the software implementation, to the extent permitted by applicable law. Except when - otherwise stated in writing the copyright holders and/or other parties provide SAX "as is" - without warranty of any kind, either expressed or implied, including, but not limited to, the - implied warranties of merchantability and fitness for a particular purpose. The entire risk as - to the quality and performance of SAX is with you. Should SAX prove defective, you - assume the cost of all necessary servicing, repair or correction. - - In no event unless required by applicable law or agreed to in writing will any copyright - holder, or any other party who may modify and/or redistribute SAX, be liable to you for - damages, including any general, special, incidental or consequential damages arising out of - the use or inability to use SAX (including but not limited to loss of data or data being - rendered inaccurate or losses sustained by you or third parties or a failure of the SAX to - operate with any other programs), even if such holder or other party has been advised of - the possibility of such damages. - - Copyright Disclaimers - - This page includes statements to that effect by David Megginson, who would have been - able to claim copyright for the original work. - SAX 1.0 - - Version 1.0 of the Simple API for XML (SAX), created collectively by the membership of - the XML-DEV mailing list, is hereby released into the public domain. - - No one owns SAX: you may use it freely in both commercial and non-commercial - applications, bundle it with your software distribution, include it on a CD-ROM, list the - source code in a book, mirror the documentation at your own web site, or use it in any - other way you see fit. - - David Megginson, sax@megginson.com - 1998-05-11 - - SAX 2.0 - - I hereby abandon any property rights to SAX 2.0 (the Simple API for XML), and release - all of the SAX 2.0 source code, compiled code, and documentation contained in this - distribution into the Public Domain. SAX comes with NO WARRANTY or guarantee of - fitness for any purpose. - - David Megginson, david@megginson.com - 2000-05-05 - -%% This notice is provided with respect to Cryptix, which may be included with this software: - -Cryptix General License - -Copyright © 1995-2003 The Cryptix Foundation Limited. All rights reserved. -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions aremet: - - 1.Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer. 2.Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY THE CRYPTIX FOUNDATION LIMITED AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS ORIMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FORA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CRYPTIX FOUNDATION LIMITED OR CONTRIBUTORS BELIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOTLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESSINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OFTHE POSSIBILITY OF SUCH DAMAGE. - -%% This notice is provided with respect to X Window System, which may be included with this software: - -Copyright The Open Group - -Permission to use, copy, modify, distribute, and sell this software and itsdocumentation for any purpose is hereby granted without fee, provided that theabove copyright notice appear in all copies and that both that copyright noticeand this permission notice appear in supporting documentation. - -The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESSFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUPBE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OFCONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THESOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The Open Group. - -Portions also covered by other licenses as noted in the above URL. - -%% This notice is provided with respect to Retroweaver, which may be included with this software: - -Copyright (c) February 2004, Toby Reyelts -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -Neither the name of Toby Reyelts nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICTLIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -%% This notice is provided with respect to stripper, which may be included with this software: - -Stripper : debug information stripper - Copyright (c) 2003 Kohsuke Kawaguchi - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holders nor the names of its - contributors may be used to endorse or promote products derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -%% This notice is provided with respect to libpng official PNG reference library, which may be included with this software: - -This copy of the libpng notices is provided for your convenience. In case ofany discrepancy between this copy and the notices in the file png.h that isincluded in the libpng distribution, the latter shall prevail. - -COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: - -If you modify libpng you may insert additional notices immediately followingthis sentence. - -libpng version 1.2.6, December 3, 2004, is -Copyright (c) 2004 Glenn Randers-Pehrson, and is -distributed according to the same disclaimer and license as libpng-1.2.5with the following individual added to the list of Contributing Authors - Cosmin Truta - -libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, areCopyright (c) 2000-2002 Glenn Randers-Pehrson, and are -distributed according to the same disclaimer and license as libpng-1.0.6with the following individuals added to the list of Contributing Authors - Simon-Pierre Cadieux - Eric S. Raymond - Gilles Vollant - -and with the following additions to the disclaimer: - - There is no warranty against interference with your enjoyment of the library or against infringement. There is no warranty that our - efforts or the library will fulfill any of your particular purposes or needs. This library is provided with all faults, and the entire risk of satisfactory quality, performance, accuracy, and effort is with the user. - -libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, areCopyright (c) 1998, 1999 Glenn Randers-Pehrson, and are -distributed according to the same disclaimer and license as libpng-0.96,with the following individuals added to the list of Contributing Authors: - Tom Lane - Glenn Randers-Pehrson - Willem van Schaik - -libpng versions 0.89, June 1996, through 0.96, May 1997, are -Copyright (c) 1996, 1997 Andreas Dilger -Distributed according to the same disclaimer and license as libpng-0.88,with the following individuals added to the list of Contributing Authors: - John Bowler - Kevin Bracey - Sam Bushell - Magnus Holmgren - Greg Roelofs - Tom Tanner - -libpng versions 0.5, May 1995, through 0.88, January 1996, are -Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc. - -For the purposes of this copyright and license, "Contributing Authors"is defined as the following set of individuals: - - Andreas Dilger - Dave Martindale - Guy Eric Schalnat - Paul Schmidt - Tim Wegner - -The PNG Reference Library is supplied "AS IS". The Contributing Authorsand Group 42, Inc. disclaim all warranties, expressed or implied, -including, without limitation, the warranties of merchantability and offitness for any purpose. The Contributing Authors and Group 42, Inc. -assume no liability for direct, indirect, incidental, special, exemplary,or consequential damages, which may result from the use of the PNG -Reference Library, even if advised of the possibility of such damage. - -Permission is hereby granted to use, copy, modify, and distribute thissource code, or portions hereof, for any purpose, without fee, subjectto the following restrictions: - -1. The origin of this source code must not be misrepresented. - -2. Altered versions must be plainly marked as such and must not - be misrepresented as being the original source. - -3. This Copyright notice may not be removed or altered from any - source or altered source distribution. - -The Contributing Authors and Group 42, Inc. specifically permit, withoutfee, and encourage the use of this source code as a component to -supporting the PNG file format in commercial products. If you use thissource code in a product, acknowledgment is not required but would be -appreciated. - - -A "png_get_copyright" function is available, for convenient use in "about"boxes and the like: - - printf("%s",png_get_copyright(NULL)); - -Also, the PNG logo (in PNG format, of course) is supplied in the -files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). - -Libpng is OSI Certified Open Source Software. OSI Certified Open Source is acertification mark of the Open Source Initiative. - -Glenn Randers-Pehrson -glennrp at users.sourceforge.net -December 3, 2004 - -%% This notice is provided with respect to Libungif - An uncompressed GIF library, which may be included with this software: - -The GIFLIB distribution is Copyright (c) 1997 Eric S. Raymond - -Permission is hereby granted, free of charge, to any person obtaining a copyof this software and associated documentation files (the "Software"), to dealin the Software without restriction, including without limitation the rightsto use, copy, modify, merge, publish, distribute, sublicense, and/or sellcopies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included inall copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS INTHE SOFTWARE. - -%% This notice is provided with respect to XML Resolver library, Xalan J2, and StAX API, which may be included with this software: - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - - You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed 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. - -%% Some Portions licensed from IBM are available at: -http://www.ibm.com/software/globalization/icu/ - -%% This notice is provided with respect to ICU4J, ICU 1.8.1 and later, which may be included with this software: - -ICU License - ICU 1.8.1 and later COPYRIGHT AND PERMISSION NOTICE Cop -yright (c) -1995-2003 International Business Machines Corporation and others All rightsreserved. Permission is hereby granted, free of charge, to any person obtaininga copy of this software and associated documentation files (the "Software"), todeal in the Software without restriction, including without limitation therights to use, copy, modify, merge, publish, distribute, and/or sell copies ofthe Software, and to permit persons to whom the Software is furnished to do so,provided that the above copyright notice(s) and this permission notice appear inall copies of the Software and that both the above copyright notice(s) and thispermission notice appear in supporting documentation. THE SOFTWARE IS PROVIDED"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOTLIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSEAND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHTHOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY C - LAIM, OR ANYSPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTINGFROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCEOR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE ORPERFORMANCE OF THIS SOFTWARE. Except as contained in this notice, the name of acopyright holder shall not be used in advertising or otherwise to promote thesale, use or other dealings in this Software without prior written authorizationof the copyright holder. - -%% This notice is provided with respect to Jing, which may be included with this software: - -Jing Copying Conditions - -Copyright (c) 2001-2003 Thai Open Source Software Center Ltd -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification,are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice,this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice,this list of conditions and the following disclaimer in the documentation and/orother materials provided with the distribution. - * Neither the name of the Thai Open Source Software Center Ltd nor the namesof its contributors may be used to endorse or promote products derived from thissoftware without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ANDANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIEDWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AREDISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANYDIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ONANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THISSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -%% This notice is provided with respect to RELAX NG Object Model/Parser, which may be included with this software: - - -The MIT License - -Copyright (c) - -Permission is hereby granted, free of charge, to any person obtaining a copy ofthis software and associated documentation files (the "Software"), to deal inthe Software without restriction, including without limitation the rights touse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies ofthe Software, and to permit persons to whom the Software is furnished to do so,subject to the following conditions: - -The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESSFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS ORCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHERIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR INCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -%% This notice is provided with respect to XFree86-VidMode Extension, which may be included with this software: - -Version 1.1 of XFree86 ProjectLicence. - - Copyright (C) 1994-2004 The XFree86 Project, Inc. All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copyof this software and associated documentation files (the "Software"), to deal inthe Software without restriction, including without limitation the rights touse, copy, modify, merge, publish, distribute, sublicence, and/or sell copies ofthe Software, and to permit persons to whom the Software is furnished to do so,subject to the following conditions: - - 1. Redistributions of source code must retain the above copyright notice,this list of conditions, and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyrightnotice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution, and in thesame place and form as other copyright, license and disclaimer information. 3. The end-user documentation included with the redistribution, if any,must include the following acknowledgment: "This product includes softwaredeveloped by The XFree86 Project, Inc (http://www.xfree86.org/) and itscontributors", in the same place and form as other third-party acknowledgments.Alternately, this acknowledgment may appear in the software itself, in the sameform and location as other such third-party acknowledgments. - 4. Except as contained in this notice, the name of The XFree86 Project,Inc shall not be used in advertising or otherwise to promote the sale, use orother dealings in this Software without prior written authorization from TheXFree86 Project, Inc. - - THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY ANDFITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE XFREE86PROJECT, INC OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ORBUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER INCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISINGIN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITYOF SUCH DAMAGE. - -%% This notice is provided with respect to XML Security, which may be included with this software: - - The Apache Software License, - Version 1.1 - - - PDF - - - Copyright (C) 2002 The Apache SoftwareFoundation. - All rights reserved. Redistribution anduse in - source and binary forms, with or withoutmodifica- - tion, are permitted provided that thefollowing - conditions are met: 1. Redistributions ofsource - code must retain the above copyrightnotice, this - list of conditions and the followingdisclaimer. - 2. Redistributions in binary form mustreproduce - the above copyright notice, this list of conditions and the following disclaimerin the - documentation and/or other materialsprovided with - the distribution. 3. The end-userdocumentation - included with the redistribution, if any,must - include the following acknowledgment:"This - product includes software developed bythe Apache - Software Foundation -(http://www.apache.org/)." - Alternately, this acknowledgment mayappear in the - software itself, if and wherever suchthird-party - acknowledgments normally appear. 4. Thenames - "Apache Forrest" and "Apache SoftwareFoundation" - must not be used to endorse or promoteproducts - derived from this software without priorwritten - permission. For written permission,please contact - apache@apache.org. 5. Products derivedfrom this - software may not be called "Apache", normay - "Apache" appear in their name, withoutprior - written permission of the Apache Software Foundation. THIS SOFTWARE IS PROVIDED``AS IS'' - AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THEIMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESSFOR A - PARTICULAR PURPOSE ARE DISCLAIMED. IN NOEVENT - SHALL THE APACHE SOFTWARE FOUNDATION ORITS - CONTRIBUTORS BE LIABLE FOR ANY DIRECT,INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, ORCONSEQUENTIAL - DAMAGES (INCLU- DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ORSERVICES; LOSS - OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANYTHEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICTLIABILITY, - OR TORT (INCLUDING NEGLIGENCE OROTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF - SUCH DAMAGE. This software consists ofvoluntary - contributions made by many individuals onbehalf - of the Apache Software Foundation. Formore - information on the Apache SoftwareFoundation, - please see . - -%% This notice is provided with respect to Independent JPEG Group's software (libjpeg), which may be included with this software: - -In plain English: - -1. We don't promise that this software works. (But if you find any bugs, - please let us know!) -2. You can use this software for whatever you want. You don't have to pay us. -3. You may not pretend that you wrote this software. If you use it in a program, you must acknowledge somewhere in your documentation that you've used the IJG code. - -In legalese: - -The authors make NO WARRANTY or representation, either express or implied, with respect to this software, its quality, accuracy, merchantability, or fitness for a particular purpose. This software is provided "AS IS", and you, its user, assume the entire risk as to its quality and accuracy. - -This software is copyright (C) 1991-1998, Thomas G. Lane. -All Rights Reserved except as specified below. - -Permission is hereby granted to use, copy, modify, and distribute this software (or portions thereof) for any purpose, without fee, subject to these conditions: - -(1) If any part of the source code for this software is distributed, then this -README file must be included, with this copyright and no-warranty notice unaltered; and any additions, deletions, or changes to the original files must be clearly indicated in accompanying documentation. - -(2) If only executable code is distributed, then the accompanying documentation must state that "this software is based in part on the work of the Independent JPEG Group". - -(3) Permission for use of this software is granted only if the user accepts full responsibility for any undesirable consequences; the authors accept NO LIABILITY for damages of any kind. - -These conditions apply to any software derived from or based on the IJG code, not just to the unmodified library. If you use our work, you ought to acknowledge us. - -Permission is NOT granted for the use of any IJG author's name or company name in advertising or publicity relating to this software or products derived from it. This software may be referred to only as "the Independent JPEG Group's software". - -We specifically permit and encourage the use of this software as the basis of commercial products, provided that all warranty or liability claims are assumed by the product vendor. - -ansi2knr.c is included in this distribution by permission of L. Peter Deutsch, sole proprietor of its copyright holder, Aladdin Enterprises of Menlo Park, CA. ansi2knr.c is NOT covered by the above copyright and conditions, but instead by the usual distribution terms of the Free Software Foundation; principally, that you must include source code if you redistribute it. (See the file ansi2knr.c for full details.) However, since ansi2knr.c is not needed as part of any program generated from the IJG code, this does not limit you more than the foregoing paragraphs do. - -The Unix configuration script "configure" was produced with GNU Autoconf. It is copyright by the Free Software Foundation but is freely distributable. The same holds for its supporting scripts (config.guess, config.sub, ltconfig, ltmain.sh). Another support script, install-sh, is copyright by M.I.T. but is also freely distributable. - -It appears that the arithmetic coding option of the JPEG spec is covered by patents owned by IBM, AT&T, and Mitsubishi. Hence arithmetic coding cannot legally be used without obtaining one or more licenses. For this reason, support for arithmetic coding has been removed from the free JPEG software. (Since arithmetic coding provides only a marginal gain over the unpatented Huffman mode, it is unlikely that very many implementations will support it.) So far as we are aware, there are no patent restrictions on the remaining code. - -The IJG distribution formerly included code to read and write GIF files. To avoid entanglement with the Unisys LZW patent, GIF reading support has been removed altogether, and the GIF writer has been simplified to produce "uncompressed GIFs". This technique does not use the LZW algorithm; the resulting GIF files are larger than usual, but are readable by all standard GIF decoders. - -We are required to state that - "The Graphics Interchange Format(c) is the Copyright property of - CompuServe Incorporated. GIF(sm) is a Service Mark property of - CompuServe Incorporated." - -%% This notice is provided with respect to X Resize and Rotate (Xrandr) Extension, which may be included with this software: -2. XFree86 License - -XFree86 code without an explicit copyright is covered by the following -copyright/license: - -Copyright (C) 1994-2003 The XFree86 Project, Inc. All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 -PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of the XFree86 Project shall not be -used in advertising or otherwise to promote the sale, use or other dealings in -this Software without prior written authorization from the XFree86 Project. - -%% This notice is provided with respect to fontconfig, which may be included with this software: -Id: COPYING,v 1.3 2003/04/04 20:17:40 keithp Exp $ -Copyright 2001,2003 Keith Packard - -Permission to use, copy, modify, distribute, and sell this software and its -documentation for any purpose is hereby granted without fee, provided that -the above copyright notice appear in all copies and that both that -copyright notice and this permission notice appear in supporting -documentation, and that the name of Keith Packard not be used in -advertising or publicity pertaining to distribution of the software without -specific, written prior permission. Keith Packard makes no -representations about the suitability of this software for any purpose. It -is provided "as is" without express or implied warranty. - -KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, -INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO -EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR -CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, -DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER -TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. -%% This notice is provided with respect to XFree86, which may be included with this software: -Copyright (C) 1994-2002 The XFree86 Project, Inc. All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated -documentation files (the "Software"), to deal in the Software without -restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to the -following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the -Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT -NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of the XFree86 Project shall not be -used in advertising or otherwise -to promote the sale, use or other dealings in this Software without prior -written authorization from the XFree86 -Project. -%% This notice is provided with respect to Fast Infoset, which may be included with this software: -* Fast Infoset ver. 0.1 software ("Software") -* -* Copyright, 2004-2005 Sun Microsystems, Inc. All Rights Reserved. -* -* Software is licensed 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. -* -* Sun supports and benefits from the global community of open source -* developers, and thanks the community for its important contributions and -* open standards-based technology, which Sun has adopted into many of its -* products. -* -* Please note that portions of Software may be provided with notices and -* open source licenses from such communities and third parties that govern the -* use of those portions, and any licenses granted hereunder do not alter any -* rights and obligations you may have under such open source licenses, -* however, the disclaimer of warranty and limitation of liability provisions -* in this License will apply to all Software in this distribution. -* -* You acknowledge that the Software is not designed, licensed or intended -* for use in the design, construction, operation or maintenance of any nuclear -* facility. -* -* Apache License -* Version 2.0, January 2004 -* http://www.apache.org/licenses/ -* -*/ -/* -* ==================================================================== -* -* This code is subject to the freebxml License, Version 1.1 -* -* Copyright (c) 2001 - 2005 freebxml.org. All rights reserved. -* -* $Header: /cvs/fi/FastInfoset/src/com/sun/xml/internal/fastinfoset/AbstractResourceBundle.java,v 1.2 -*  ==================================================================== -*/ -%% This notice is provided with respect to Kerberos, which may be included with this software: - -/* - * Copyright (C) 1998 by the FundsXpress, INC. - * - * All rights reserved. - * - * Export of this software from the United States of America may require - * a specific license from the United States Government.  It is the - * responsibility of any person or organization contemplating export to - * obtain such a license before exporting. - * - * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and - * distribute this software and its documentation for any purpose and - * without fee is hereby granted, provided that the above copyright - * notice appear in all copies and that both that copyright notice and - * this permission notice appear in supporting documentation, and that - * the name of FundsXpress. not be used in advertising or publicity pertaining - * to distribution of the software without specific, written prior - * permission. FundsXpress makes no representations about the suitability of - * this software for any purpose. It is provided "as is" without express - * or implied warranty. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -%% This notice is provided with respect to Unicode's CLDR data repository, which may be included with this software: - - Unicode Copyright - - For the general privacy policy governing access to this site, see the -Unicode Privacy Policy. For trademark usage, see the the Unicode Consortium -Trademarks and Logo Policy. - Notice to End User: Terms of Use - Carefully read the following legal agreement ("Agreement"). Use or copying -of the software and/or codes provided with this agreement (The "Software") -constitutes your acceptance of these terms - - 1. Unicode Copyright. - 1. Copyright © 1991-2005 Unicode, Inc. All rights reserved. - 2. Certain documents and files on this website contain a legend -indicating that "Modification is permitted." Any person is hereby authorized, -without fee, to modify such documents and files to create derivative works -conforming to the Unicode® Standard, subject to Terms and Conditions herein. - 3. Any person is hereby authorized, without fee, to view, use, -reproduce, and distribute all documents and files solely for informational -purposes in the creation of products supporting the Unicode Standard, subject to -the Terms and Conditions herein. - 4. Further specifications of rights and restrictions pertaining to -the use of the particular set of data files known as the "Unicode Character -Database" can be found in Exhibit 1. - 5. Further specifications of rights and restrictions pertaining to -the use of the particular set of files that constitute the online edition of The -Unicode Standard, Version 4.0, may be found in V4.0 online edition. - 6. No license is granted to "mirror" the Unicode website where a -fee is charged for access to the "mirror" site. - 7. Modification is not permitted with respect to this document. All -copies of this document must be verbatim. - 2. Restricted Rights Legend. Any technical data or software which is -licensed to the United States of America, its agencies and/or instrumentalities -under this Agreement is commercial technical data or commercial computer -software developed exclusively at private expense as defined in FAR 2.101, or -DFARS 252.227-7014 (June 1995), as applicable. For technical data, use, -duplication, or disclosure by the Government is subject to restrictions as set -forth in DFARS 202.227-7015 Technical Data, Commercial and Items (Nov 1995) and -this Agreement. For Software, in accordance with FAR 12-212 or DFARS 227-7202, -as applicable, use, duplication or disclosure by the Government is subject to -the restrictions set forth in this Agreement. - 3. Warranties and Disclaimers. - 1. This publication and/or website may include technical or -typographical errors or other inaccuracies . Changes are periodically added to -the information herein; these changes will be incorporated in new editions of -the publication and/or website. Unicode may make improvements and/or changes in -the product(s) and/or program(s) described in this publication and/or website at -any time. - 2. If this file has been purchased on magnetic or optical media -from Unicode, Inc. the sole and exclusive remedy for any claim will be exchange -of the defective media within ninety (90) days of original purchase. - 3. EXCEPT AS PROVIDED IN SECTION C.2, THIS PUBLICATION AND/OR -SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND EITHER EXPRESS, -IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. UNICODE -AND ITS LICENSORS ASSUME NO RESPONSIBILITY FOR ERRORS OR OMISSIONS IN THIS -PUBLICATION AND/OR SOFTWARE OR OTHER DOCUMENTS WHICH ARE REFERENCED BY OR LINKED -TO THIS PUBLICATION OR THE UNICODE WEBSITE. - 4. Waiver of Damages. In no event shall Unicode or its licensors be -liable for any special, incidental, indirect or consequential damages of any -kind, or any damages whatsoever, whether or not Unicode was advised of the -possibility of the damage, including, without limitation, those resulting from -the following: loss of use, data or profits, in connection with the use, -modification or distribution of this information or its derivatives. - 5. Trademarks. - 1. Unicode and the Unicode logo are registered trademarks of -Unicode, Inc. - 2. This site contains product names and corporate names of other -companies. All product names and company names and logos mentioned herein are -the trademarks or registered trademarks of their respective owners. Other -products and corporate names mentioned herein which are trademarks of a third -party are used only for explanation and for the owners' benefit and with no -intent to infringe. - 3. Use of third party products or information referred to herein is -at the user's risk. - 6. Miscellaneous. - 1. Jurisdiction and Venue. This server is operated from a location -in the State of California, United States of America. Unicode makes no -representation that the materials are appropriate for use in other locations. If -you access this server from other locations, you are responsible for compliance -with local laws. This Agreement, all use of this site and any claims and damages -resulting from use of this site are governed solely by the laws of the State of -California without regard to any principles which would apply the laws of a -different jurisdiction. The user agrees that any disputes regarding this site -shall be resolved solely in the courts located in Santa Clara County, -California. The user agrees said courts have personal jurisdiction and agree to -waive any right to transfer the dispute to any other forum. - 2. Modification by Unicode Unicode shall have the right to modify -this Agreement at any time by posting it to this site. The user may not assign -any part of this Agreement without Unicode's prior written consent. - 3. Taxes. The user agrees to pay any taxes arising from access to -this website or use of the information herein, except for those based on -Unicode's net income. - 4. Severability. If any provision of this Agreement is declared -invalid or unenforceable, the remaining provisions of this Agreement shall -remain in effect. - 5. Entire Agreement. This Agreement constitutes the entire -agreement between the parties. - -EXHIBIT 1 -UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE - - Unicode Data Files include all data files under the directories -http://www.unicode.org/Public/ and http://www.unicode.org/reports/. Unicode -Software includes any source code under the directories -http://www.unicode.org/Public/ and http://www.unicode.org/reports/. - - NOTICE TO USER: Carefully read the following legal agreement. BY -DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES -("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND -AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU -DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES -OR SOFTWARE. - - COPYRIGHT AND PERMISSION NOTICE - - Copyright Ã?Â,Ã,© 1991-2004 Unicode, Inc. All rights reserved. Distributed under -the Terms of Use in http://www.unicode.org/copyright.html. - - Permission is hereby granted, free of charge, to any person obtaining a copy -of the Unicode data files and associated documentation (the "Data Files") or -Unicode software and associated documentation (the "Software") to deal in the -Data Files or Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, and/or sell copies of -the Data Files or Software, and to permit persons to whom the Data Files or -Software are furnished to do so, provided that (a) the above copyright notice(s) -and this permission notice appear with all copies of the Data Files or Software, -(b) both the above copyright notice(s) and this permission notice appear in -associated documentation, and (c) there is clear notice in each modified Data -File or in the Software as well as in the documentation associated with the Data -File(s) or Software that the data or software has been modified. - - THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD -PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS -NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL -DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING -OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE. - - Except as contained in this notice, the name of a copyright holder shall not -be used in advertising or otherwise to promote the sale, use or other dealings -in these Data Files or Software without prior written authorization of the -copyright holder. - - Unicode and the Unicode logo are trademarks of Unicode, Inc., and may be -registered in some jurisdictions. All other trademarks and registered trademarks -mentioned herein are the property of their respective owners. -%% This notice is provided with respect to RSA PKCS#11 Header Files & Specification, which may be included with this software: - -/* - * Copyright (C) 1998 by the FundsXpress, INC. - * - * All rights reserved. - * - * Export of this software from the United States of America may require - * a specific license from the United States Government.  It is the - * responsibility of any person or organization contemplating export to - * obtain such a license before exporting. - * - * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and - * distribute this software and its documentation for any purpose and - * without fee is hereby granted, provided that the above copyright - * notice appear in all copies and that both that copyright notice and - * this permission notice appear in supporting documentation, and that - * the name of FundsXpress. not be used in advertising or publicity pertaining - * to distribution of the software without specific, written prior - * permission.  FundsXpress makes no representations about the suitability of - * this software for any purpose.  It is provided "as is" without express - * or implied warranty. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -%% This notice is provided with respect to certain files/code which may included in the implementation of AWT within the software: - -****************************************************** -BEGIN  src/solaris/native/sun/awt/HPkeysym.h -Copyright 1987, 1998  The Open Group - -All Rights Reserved. - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of The Open Group shall -not be used in advertising or otherwise to promote the sale, use or -other dealings in this Software without prior written authorization -from The Open Group. - -Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts, - -All Rights Reserved - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appear in all copies and that -both that copyright notice and this permission notice appear in -supporting documentation, and that the names of Hewlett Packard -or Digital not be -used in advertising or publicity pertaining to distribution of the -software without specific, written prior permission. - -DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING -ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL -DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR -ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, -ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS -SOFTWARE. - -HEWLETT-PACKARD MAKES NO WARRANTY OF ANY KIND WITH REGARD -TO THIS SOFWARE, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE.  Hewlett-Packard shall not be liable for errors -contained herein or direct, indirect, special, incidental or -consequential damages in connection with the furnishing, -performance, or use of this material. - -END  src/solaris/native/sun/awt/HPkeysym.h -****************************************************** -****************************************************** -BEGIN src/solaris/native/sun/awt/Xrandr.h -/* - * $XFree86: xc/lib/Xrandr/Xrandr.h,v 1.9 2002/09/29 23:39:44 keithp Exp $ - * - * Copyright © 2000 Compaq Computer Corporation, Inc. - * Copyright © 2002 Hewlett-Packard Company, Inc. - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of Compaq not be used in advertising or - * publicity pertaining to distribution of the software without specific, - * written prior permission.  HP makes no representations about the - * suitability of this software for any purpose.  It is provided "as is" - * without express or implied warranty. - * - * HP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL COMPAQ - * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Author:  Jim Gettys, HP Labs, HP. - */ - - -END src/solaris/native/sun/awt/Xrandr.h -****************************************************** -BEGIN src/solaris/native/sun/awt/extutil.h -/* - * $Xorg: extutil.h,v 1.3 2000/08/18 04:05:45 coskrey Exp $ - * -Copyright 1989, 1998  The Open Group - -All Rights Reserved. - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE -OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN -AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of The Open Group shall not be -used in advertising or otherwise to promote the sale, use or other dealings -in this Software without prior written authorization from The Open Group. - * - * Author:  Jim Fulton, MIT The Open Group - * - *                     Xlib Extension-Writing Utilities - * - * This package contains utilities for writing the client API for various - * protocol extensions.  THESE INTERFACES ARE NOT PART OF THE X STANDARD AND - * ARE SUBJECT TO CHANGE! - */ -/* $XFree86: xc/include/extensions/extutil.h,v 1.5 2001/01/17 17:53:20 dawes Exp $ */ - -END src/solaris/native/sun/awt/extutil.h -****************************************************** -BEGIN   src/solaris/native/sun/awt/fontconfig.h -/* - * $RCSId: xc/lib/fontconfig/fontconfig/fontconfig.h,v 1.30 2002/09/26 00:17:27 -keithp Exp $ - * - * Copyright © 2001 Keith Packard - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of Keith Packard not be used in - * advertising or publicity pertaining to distribution of the software without - * specific, written prior permission.  Keith Packard makes no - * representations about the suitability of this software for any purpose.  It - * is provided "as is" without express or implied warranty. - * - * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - -END   src/solaris/native/sun/awt/fontconfig.h -****************************************************** -BEGIN src/solaris/native/sun/awt/list.c -AND  src/solaris/native/sun/awt/list.h -AND src/solaris/native/sun/awt/multiVis.c -AND  src/solaris/native/sun/awt/multiVis.h -AND  src/solaris/native/sun/awt/wsutils.h - -Copyright (c) 1994 Hewlett-Packard Co. -Copyright (c) 1996  X Consortium - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of the X Consortium shall -not be used in advertising or otherwise to promote the sale, use or -other dealings in this Software without prior written authorization -from the X Consortium. - -END src/solaris/native/sun/awt/list.c -AND  src/solaris/native/sun/awt/list.h -AND src/solaris/native/sun/awt/multiVis.c -AND  src/solaris/native/sun/awt/multiVis.h -AND  src/solaris/native/sun/awt/wsutils.h - -***************************************************************** -BEGIN src/solaris/native/sun/awt/randr.h - - * - * Copyright © 2000, Compaq Computer Corporation, - * Copyright © 2002, Hewlett Packard, Inc. - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of Compaq or HP not be used in advertising - * or publicity pertaining to distribution of the software without specific, - * written prior permission.  HP makes no representations about the - * suitability of this software for any purpose.  It is provided "as is" - * without express or implied warranty. - * - * HP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL HP - * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Author:  Jim Gettys, HP Labs, Hewlett-Packard, Inc. - -END src/solaris/native/sun/awt/randr.h -***************************************************** - -BEGIN src/solaris/native/sun/java2d/opengl/J2D_GL/glx.h - * Mesa 3-D graphics library - * Version:  4.1 - * - * Copyright (C) 1999-2002  Brian Paul   All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL - * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -END src/solaris/native/sun/java2d/opengl/J2D_GL/glx.h diff --git a/make/common/Release.gmk b/make/common/Release.gmk index 42de79f70..b05cf4dd1 100644 --- a/make/common/Release.gmk +++ b/make/common/Release.gmk @@ -63,15 +63,6 @@ endif JTG_DOCS = $(JDK_TOPDIR)/src/solaris/doc -# Choose the right set of documents for the images -ifdef OPENJDK - SHARE_JDK_DOC_SRC = $(JDK_TOPDIR)/make - SHARE_JRE_DOC_SRC = $(JDK_TOPDIR)/make -else - SHARE_JDK_DOC_SRC = $(CLOSED_SHARE_SRC)/doc/jdk - SHARE_JRE_DOC_SRC = $(CLOSED_SHARE_SRC)/doc/jre -endif - #We use this for man page header jdkversion := $(JDK_MAJOR_VERSION).$(JDK_MINOR_VERSION).$(JDK_MICRO_VERSION) @@ -81,13 +72,20 @@ ifeq ($(PLATFORM), windows) endif # The base names of all the license and document files for the jdk and jre +# (These files get placed in the jdk and jre install images) ifdef OPENJDK + # Where to find these files + SHARE_JDK_DOC_SRC = $(JDK_TOPDIR) + SHARE_JRE_DOC_SRC = $(JDK_TOPDIR) # Same files for jdk and jre, no name changes LICENSE_DOCLIST_JDK = LICENSE ASSEMBLY_EXCEPTION LICENSE_DOCLIST_JRE = LICENSE ASSEMBLY_EXCEPTION - OTHER_DOCLIST_JDK = README.html THIRD_PARTY_README - OTHER_DOCLIST_JRE = README.html THIRD_PARTY_README + OTHER_DOCLIST_JDK = THIRD_PARTY_README + OTHER_DOCLIST_JRE = THIRD_PARTY_README else + # Where to find these files + SHARE_JDK_DOC_SRC = $(CLOSED_SHARE_SRC)/doc/jdk + SHARE_JRE_DOC_SRC = $(CLOSED_SHARE_SRC)/doc/jre # Select the pre-release or FCS license version based on the build milestone. LICENSE_VERSION=.pre ifeq ($(MILESTONE), fcs) -- GitLab From 54c1d936744d31e80a4279f7636bbec92b88a44d Mon Sep 17 00:00:00 2001 From: emcmanus Date: Fri, 8 Aug 2008 18:36:19 +0200 Subject: [PATCH 045/139] 6610174: Improve CompositeDataSupport.toString when it includes arrays Reviewed-by: dfuchs --- .../openmbean/CompositeDataSupport.java | 18 +++- .../openmbean/CompositeDataStringTest.java | 89 +++++++++++++++++++ 2 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 test/javax/management/openmbean/CompositeDataStringTest.java diff --git a/src/share/classes/javax/management/openmbean/CompositeDataSupport.java b/src/share/classes/javax/management/openmbean/CompositeDataSupport.java index 172fa7561..12e3cf513 100644 --- a/src/share/classes/javax/management/openmbean/CompositeDataSupport.java +++ b/src/share/classes/javax/management/openmbean/CompositeDataSupport.java @@ -355,6 +355,7 @@ public class CompositeDataSupport * @return true if the specified object is equal to this * CompositeDataSupport instance. */ + @Override public boolean equals(Object obj) { if (this == obj) { return true; @@ -419,6 +420,7 @@ public class CompositeDataSupport * * @return the hash code value for this CompositeDataSupport instance */ + @Override public int hashCode() { int hashcode = compositeType.hashCode(); @@ -457,16 +459,28 @@ public class CompositeDataSupport * * @return a string representation of this CompositeDataSupport instance */ + @Override public String toString() { - return new StringBuilder() .append(this.getClass().getName()) .append("(compositeType=") .append(compositeType.toString()) .append(",contents=") - .append(contents.toString()) + .append(contentString()) .append(")") .toString(); } + private String contentString() { + StringBuilder sb = new StringBuilder("{"); + String sep = ""; + for (Map.Entry entry : contents.entrySet()) { + sb.append(sep).append(entry.getKey()).append("="); + String s = Arrays.deepToString(new Object[] {entry.getValue()}); + sb.append(s.substring(1, s.length() - 1)); + sep = ", "; + } + sb.append("}"); + return sb.toString(); + } } diff --git a/test/javax/management/openmbean/CompositeDataStringTest.java b/test/javax/management/openmbean/CompositeDataStringTest.java new file mode 100644 index 000000000..286bfd1d4 --- /dev/null +++ b/test/javax/management/openmbean/CompositeDataStringTest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; + +/* + * @test + * @bug 6610174 + * @summary Test that CompositeDataSupport.toString() represents arrays correctly + * @author Eamonn McManus + */ +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; + +public class CompositeDataStringTest { + public static void main(String[] args) throws Exception { + CompositeType basicCT = new CompositeType( + "basicCT", "basic CompositeType", + new String[] {"name", "value"}, + new String[] {"name", "value"}, + new OpenType[] {SimpleType.STRING, SimpleType.INTEGER}); + CompositeType ct = new CompositeType( + "noddy", "descr", + new String[] {"strings", "ints", "cds"}, + new String[] {"string array", "int array", "composite data array"}, + new OpenType[] { + ArrayType.getArrayType(SimpleType.STRING), + ArrayType.getPrimitiveArrayType(int[].class), + ArrayType.getArrayType(basicCT) + }); + CompositeData basicCD1 = new CompositeDataSupport( + basicCT, new String[] {"name", "value"}, new Object[] {"ceathar", 4}); + CompositeData basicCD2 = new CompositeDataSupport( + basicCT, new String[] {"name", "value"}, new Object[] {"naoi", 9}); + CompositeData cd = new CompositeDataSupport( + ct, + new String[] {"strings", "ints", "cds"}, + new Object[] { + new String[] {"fred", "jim", "sheila"}, + new int[] {2, 3, 5, 7}, + new CompositeData[] {basicCD1, basicCD2} + }); + String s = cd.toString(); + System.out.println("CompositeDataSupport.toString(): " + s); + String[] expected = { + "fred, jim, sheila", + "2, 3, 5, 7", + "ceathar", + "naoi", + }; + boolean ok = true; + for (String expect : expected) { + if (s.contains(expect)) + System.out.println("OK: string contains <" + expect + ">"); + else { + ok = false; + System.out.println("NOT OK: string does not contain <" + + expect + ">"); + } + } + if (ok) + System.out.println("TEST PASSED"); + else + throw new Exception("TEST FAILED: string did not contain expected substrings"); + } +} -- GitLab From e9fb23206765796bcc26032b32bf3196284a180b Mon Sep 17 00:00:00 2001 From: mlapshin Date: Fri, 8 Aug 2008 20:49:26 +0400 Subject: [PATCH 046/139] 6584657: GTK Look and Feel: Bugs in menu item layout Reviewed-by: peterz, alexp --- .../classes/javax/swing/SwingUtilities.java | 10 +- .../swing/plaf/basic/BasicMenuItemUI.java | 1052 ++----------- .../swing/plaf/basic/DefaultMenuLayout.java | 16 +- .../swing/plaf/synth/DefaultMenuLayout.java | 23 +- .../swing/plaf/synth/SynthGraphicsUtils.java | 196 ++- .../plaf/synth/SynthMenuItemLayoutHelper.java | 307 ++++ .../swing/plaf/synth/SynthMenuItemUI.java | 559 +------ .../javax/swing/plaf/synth/SynthMenuUI.java | 49 +- .../swing/plaf/synth/SynthPopupMenuUI.java | 114 +- .../sun/swing/MenuItemLayoutHelper.java | 1339 +++++++++++++++++ 10 files changed, 2000 insertions(+), 1665 deletions(-) create mode 100644 src/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java create mode 100644 src/share/classes/sun/swing/MenuItemLayoutHelper.java diff --git a/src/share/classes/javax/swing/SwingUtilities.java b/src/share/classes/javax/swing/SwingUtilities.java index d8f29e528..38f18e781 100644 --- a/src/share/classes/javax/swing/SwingUtilities.java +++ b/src/share/classes/javax/swing/SwingUtilities.java @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -974,6 +974,7 @@ public class SwingUtilities implements SwingConstants boolean textIsEmpty = (text == null) || text.equals(""); int lsb = 0; + int rsb = 0; /* Unless both text and icon are non-null, we effectively ignore * the value of textIconGap. */ @@ -1015,7 +1016,7 @@ public class SwingUtilities implements SwingConstants if (lsb < 0) { textR.width -= lsb; } - int rsb = SwingUtilities2.getRightSideBearing(c, fm, text); + rsb = SwingUtilities2.getRightSideBearing(c, fm, text); if (rsb > 0) { textR.width += rsb; } @@ -1118,6 +1119,11 @@ public class SwingUtilities implements SwingConstants // lsb is negative. Shift the x location so that the text is // visually drawn at the right location. textR.x -= lsb; + + textR.width += lsb; + } + if (rsb > 0) { + textR.width -= rsb; } return text; diff --git a/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java b/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java index 5e81bfd06..a58cc467c 100644 --- a/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/src/share/classes/javax/swing/plaf/basic/BasicMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,9 +25,6 @@ package javax.swing.plaf.basic; -import sun.swing.MenuItemCheckIconFactory; -import sun.swing.SwingUtilities2; -import static sun.swing.SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET; import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeEvent; @@ -39,8 +36,7 @@ import javax.swing.border.*; import javax.swing.plaf.*; import javax.swing.text.View; -import sun.swing.UIAction; -import sun.swing.StringUIClientPropertyKey; +import sun.swing.*; /** * BasicMenuItem implementation @@ -91,24 +87,6 @@ public class BasicMenuItemUI extends MenuItemUI private static final boolean VERBOSE = false; // show reuse hits/misses private static final boolean DEBUG = false; // show bad params, misc. - // Allows to reuse layoutInfo object. - // Shouldn't be used directly. Use getLayoutInfo() instead. - private final transient LayoutInfo layoutInfo = new LayoutInfo(); - - /* Client Property keys for calculation of maximal widths */ - static final StringUIClientPropertyKey MAX_ARROW_WIDTH = - new StringUIClientPropertyKey("maxArrowWidth"); - static final StringUIClientPropertyKey MAX_CHECK_WIDTH = - new StringUIClientPropertyKey("maxCheckWidth"); - static final StringUIClientPropertyKey MAX_ICON_WIDTH = - new StringUIClientPropertyKey("maxIconWidth"); - static final StringUIClientPropertyKey MAX_TEXT_WIDTH = - new StringUIClientPropertyKey("maxTextWidth"); - static final StringUIClientPropertyKey MAX_ACC_WIDTH = - new StringUIClientPropertyKey("maxAccWidth"); - static final StringUIClientPropertyKey MAX_LABEL_WIDTH = - new StringUIClientPropertyKey("maxLabelWidth"); - static void loadActionMap(LazyActionMap map) { // NOTE: BasicMenuUI also calls into this method. map.put(new Actions(Actions.CLICK)); @@ -199,13 +177,14 @@ public class BasicMenuItemUI extends MenuItemUI //In case of column layout, .checkIconFactory is defined for this UI, //the icon is compatible with it and useCheckAndArrow() is true, //then the icon is handled by the checkIcon. - boolean isColumnLayout = LayoutInfo.isColumnLayout( + boolean isColumnLayout = MenuItemLayoutHelper.isColumnLayout( BasicGraphicsUtils.isLeftToRight(menuItem), menuItem); if (isColumnLayout) { MenuItemCheckIconFactory iconFactory = (MenuItemCheckIconFactory) UIManager.get(prefix + ".checkIconFactory"); - if (iconFactory != null && useCheckAndArrow() + if (iconFactory != null + && MenuItemLayoutHelper.useCheckAndArrow(menuItem) && iconFactory.isCompatible(checkIcon, prefix)) { checkIcon = iconFactory.getIcon(menuItem); } @@ -256,20 +235,7 @@ public class BasicMenuItemUI extends MenuItemUI uninstallComponents(menuItem); uninstallListeners(); uninstallKeyboardActions(); - - - // Remove values from the parent's Client Properties. - JComponent p = getMenuItemParent(menuItem); - if(p != null) { - p.putClientProperty(BasicMenuItemUI.MAX_ARROW_WIDTH, null ); - p.putClientProperty(BasicMenuItemUI.MAX_CHECK_WIDTH, null ); - p.putClientProperty(BasicMenuItemUI.MAX_ACC_WIDTH, null ); - p.putClientProperty(BasicMenuItemUI.MAX_TEXT_WIDTH, null ); - p.putClientProperty(BasicMenuItemUI.MAX_ICON_WIDTH, null ); - p.putClientProperty(BasicMenuItemUI.MAX_LABEL_WIDTH, null ); - p.putClientProperty(BASICMENUITEMUI_MAX_TEXT_OFFSET, null ); - } - + MenuItemLayoutHelper.clearUsedParentClientProperties(menuItem); menuItem = null; } @@ -405,19 +371,6 @@ public class BasicMenuItemUI extends MenuItemUI return d; } - // Returns parent of this component if it is not a top-level menu - // Otherwise returns null - private static JComponent getMenuItemParent(JMenuItem mi) { - Container parent = mi.getParent(); - if ((parent instanceof JComponent) && - (!(mi instanceof JMenu) || - !((JMenu)mi).isTopLevelMenu())) { - return (JComponent) parent; - } else { - return null; - } - } - protected Dimension getPreferredMenuItemSize(JComponent c, Icon checkIcon, Icon arrowIcon, @@ -447,32 +400,36 @@ public class BasicMenuItemUI extends MenuItemUI // the icon and text when user points a menu item by mouse. JMenuItem mi = (JMenuItem) c; - LayoutInfo li = getLayoutInfo(mi, checkIcon, arrowIcon, - createMaxViewRect(), defaultTextIconGap, acceleratorDelimiter, - BasicGraphicsUtils.isLeftToRight(mi), acceleratorFont, - useCheckAndArrow(), getPropertyPrefix()); + MenuItemLayoutHelper lh = new MenuItemLayoutHelper(mi, checkIcon, + arrowIcon, MenuItemLayoutHelper.createMaxRect(), defaultTextIconGap, + acceleratorDelimiter, BasicGraphicsUtils.isLeftToRight(mi), + mi.getFont(), acceleratorFont, + MenuItemLayoutHelper.useCheckAndArrow(menuItem), + getPropertyPrefix()); Dimension result = new Dimension(); // Calculate the result width - result.width = li.leadingGap; - addWidth(li.maxCheckWidth, li.afterCheckIconGap, result); + result.width = lh.getLeadingGap(); + MenuItemLayoutHelper.addMaxWidth(lh.getCheckSize(), + lh.getAfterCheckIconGap(), result); // Take into account mimimal text offset. - if ((!li.isTopLevelMenu) - && (li.minTextOffset > 0) - && (result.width < li.minTextOffset)) { - result.width = li.minTextOffset; + if ((!lh.isTopLevelMenu()) + && (lh.getMinTextOffset() > 0) + && (result.width < lh.getMinTextOffset())) { + result.width = lh.getMinTextOffset(); } - addWidth(li.maxLabelWidth, li.gap, result); - addWidth(li.maxAccWidth, li.gap, result); - addWidth(li.maxArrowWidth, li.gap, result); + MenuItemLayoutHelper.addMaxWidth(lh.getLabelSize(), lh.getGap(), result); + MenuItemLayoutHelper.addMaxWidth(lh.getAccSize(), lh.getGap(), result); + MenuItemLayoutHelper.addMaxWidth(lh.getArrowSize(), lh.getGap(), result); // Calculate the result height - result.height = max(li.checkRect.height, li.labelRect.height, - li.accRect.height, li.arrowRect.height); + result.height = MenuItemLayoutHelper.max(lh.getCheckSize().getHeight(), + lh.getLabelSize().getHeight(), lh.getAccSize().getHeight(), + lh.getArrowSize().getHeight()); // Take into account menu item insets - Insets insets = li.mi.getInsets(); + Insets insets = lh.getMenuItem().getInsets(); if(insets != null) { result.width += insets.left + insets.right; result.height += insets.top + insets.bottom; @@ -492,500 +449,9 @@ public class BasicMenuItemUI extends MenuItemUI result.height++; } - li.clear(); return result; } - private Rectangle createMaxViewRect() { - return new Rectangle(0,0,Short.MAX_VALUE, Short.MAX_VALUE); - } - - private void addWidth(int width, int gap, Dimension result) { - if (width > 0) { - result.width += width + gap; - } - } - - private static int max(int... values) { - int maxValue = Integer.MIN_VALUE; - for (int i : values) { - if (i > maxValue) { - maxValue = i; - } - } - return maxValue; - } - - // LayoutInfo helps to calculate preferred size and to paint a menu item - private static class LayoutInfo { - JMenuItem mi; - JComponent miParent; - - FontMetrics fm; - FontMetrics accFm; - - Icon icon; - Icon checkIcon; - Icon arrowIcon; - String text; - String accText; - - boolean isColumnLayout; - boolean useCheckAndArrow; - boolean isLeftToRight; - boolean isTopLevelMenu; - View htmlView; - - int verticalAlignment; - int horizontalAlignment; - int verticalTextPosition; - int horizontalTextPosition; - int gap; - int leadingGap; - int afterCheckIconGap; - int minTextOffset; - - Rectangle viewRect; - Rectangle iconRect; - Rectangle textRect; - Rectangle accRect; - Rectangle checkRect; - Rectangle arrowRect; - Rectangle labelRect; - - int origIconWidth; - int origTextWidth; - int origAccWidth; - int origCheckWidth; - int origArrowWidth; - - int maxIconWidth; - int maxTextWidth; - int maxAccWidth; - int maxCheckWidth; - int maxArrowWidth; - int maxLabelWidth; - - // Empty constructor helps to create "final" LayoutInfo object - public LayoutInfo() { - } - - public LayoutInfo(JMenuItem mi, Icon checkIcon, Icon arrowIcon, - Rectangle viewRect, int gap, String accDelimiter, - boolean isLeftToRight, Font acceleratorFont, - boolean useCheckAndArrow, String propertyPrefix) { - reset(mi, checkIcon, arrowIcon, viewRect, gap, accDelimiter, - isLeftToRight, acceleratorFont, useCheckAndArrow, - propertyPrefix); - } - - // Allows to reuse a LayoutInfo object - public void reset(JMenuItem mi, Icon checkIcon, Icon arrowIcon, - Rectangle viewRect, int gap, String accDelimiter, - boolean isLeftToRight, Font acceleratorFont, - boolean useCheckAndArrow, String propertyPrefix) { - this.mi = mi; - this.miParent = getMenuItemParent(mi); - this.accText = getAccText(accDelimiter); - this.verticalAlignment = mi.getVerticalAlignment(); - this.horizontalAlignment = mi.getHorizontalAlignment(); - this.verticalTextPosition = mi.getVerticalTextPosition(); - this.horizontalTextPosition = mi.getHorizontalTextPosition(); - this.useCheckAndArrow = useCheckAndArrow; - this.fm = mi.getFontMetrics(mi.getFont()); - this.accFm = mi.getFontMetrics(acceleratorFont); - this.isLeftToRight = isLeftToRight; - this.isColumnLayout = isColumnLayout(); - this.isTopLevelMenu = (this.miParent == null)? true : false; - this.checkIcon = checkIcon; - this.icon = getIcon(propertyPrefix); - this.arrowIcon = arrowIcon; - this.text = mi.getText(); - this.gap = gap; - this.afterCheckIconGap = getAfterCheckIconGap(propertyPrefix); - this.minTextOffset = getMinTextOffset(propertyPrefix); - this.htmlView = (View) mi.getClientProperty(BasicHTML.propertyKey); - - this.viewRect = viewRect; - this.iconRect = new Rectangle(); - this.textRect = new Rectangle(); - this.accRect = new Rectangle(); - this.checkRect = new Rectangle(); - this.arrowRect = new Rectangle(); - this.labelRect = new Rectangle(); - - calcWidthsAndHeights(); - this.origIconWidth = iconRect.width; - this.origTextWidth = textRect.width; - this.origAccWidth = accRect.width; - this.origCheckWidth = checkRect.width; - this.origArrowWidth = arrowRect.width; - - calcMaxWidths(); - this.leadingGap = getLeadingGap(propertyPrefix); - calcMaxTextOffset(); - } - - // Clears fields to remove all links to other objects - // to prevent memory leaks - public void clear() { - mi = null; - miParent = null; - fm = null; - accFm = null; - icon = null; - checkIcon = null; - arrowIcon = null; - text = null; - accText = null; - htmlView = null; - viewRect = null; - iconRect = null; - textRect = null; - accRect = null; - checkRect = null; - arrowRect = null; - labelRect = null; - } - - private String getAccText(String acceleratorDelimiter) { - String accText = ""; - KeyStroke accelerator = mi.getAccelerator(); - if (accelerator != null) { - int modifiers = accelerator.getModifiers(); - if (modifiers > 0) { - accText = KeyEvent.getKeyModifiersText(modifiers); - accText += acceleratorDelimiter; - } - int keyCode = accelerator.getKeyCode(); - if (keyCode != 0) { - accText += KeyEvent.getKeyText(keyCode); - } else { - accText += accelerator.getKeyChar(); - } - } - return accText; - } - - // In case of column layout, .checkIconFactory is defined for this UI, - // the icon is compatible with it and useCheckAndArrow() is true, - // then the icon is handled by the checkIcon. - private Icon getIcon(String propertyPrefix) { - Icon icon = null; - MenuItemCheckIconFactory iconFactory = - (MenuItemCheckIconFactory) UIManager.get(propertyPrefix - + ".checkIconFactory"); - if (!isColumnLayout || !useCheckAndArrow || iconFactory == null - || !iconFactory.isCompatible(checkIcon, propertyPrefix)) { - icon = mi.getIcon(); - } - return icon; - } - - private int getMinTextOffset(String propertyPrefix) { - int minimumTextOffset = 0; - Object minimumTextOffsetObject = - UIManager.get(propertyPrefix + ".minimumTextOffset"); - if (minimumTextOffsetObject instanceof Integer) { - minimumTextOffset = (Integer) minimumTextOffsetObject; - } - return minimumTextOffset; - } - - private int getAfterCheckIconGap(String propertyPrefix) { - int afterCheckIconGap = gap; - Object afterCheckIconGapObject = - UIManager.get(propertyPrefix + ".afterCheckIconGap"); - if (afterCheckIconGapObject instanceof Integer) { - afterCheckIconGap = (Integer) afterCheckIconGapObject; - } - return afterCheckIconGap; - } - - private int getLeadingGap(String propertyPrefix) { - if (maxCheckWidth > 0) { - return getCheckOffset(propertyPrefix); - } else { - return gap; // There is no any check icon - } - } - - private int getCheckOffset(String propertyPrefix) { - int checkIconOffset = gap; - Object checkIconOffsetObject = - UIManager.get(propertyPrefix + ".checkIconOffset"); - if (checkIconOffsetObject instanceof Integer) { - checkIconOffset = (Integer) checkIconOffsetObject; - } - return checkIconOffset; - } - - private void calcWidthsAndHeights() - { - // iconRect - if (icon != null) { - iconRect.width = icon.getIconWidth(); - iconRect.height = icon.getIconHeight(); - } - - // accRect - if (!accText.equals("")) { - accRect.width = SwingUtilities2.stringWidth( - mi, accFm, accText); - accRect.height = accFm.getHeight(); - } - - // textRect - if (text == null) { - text = ""; - } else if (!text.equals("")) { - if (htmlView != null) { - // Text is HTML - textRect.width = - (int) htmlView.getPreferredSpan(View.X_AXIS); - textRect.height = - (int) htmlView.getPreferredSpan(View.Y_AXIS); - } else { - // Text isn't HTML - textRect.width = - SwingUtilities2.stringWidth(mi, fm, text); - textRect.height = fm.getHeight(); - } - } - - if (useCheckAndArrow) { - // checkIcon - if (checkIcon != null) { - checkRect.width = checkIcon.getIconWidth(); - checkRect.height = checkIcon.getIconHeight(); - } - // arrowRect - if (arrowIcon != null) { - arrowRect.width = arrowIcon.getIconWidth(); - arrowRect.height = arrowIcon.getIconHeight(); - } - } - - // labelRect - if (isColumnLayout) { - labelRect.width = iconRect.width + textRect.width + gap; - labelRect.height = max(checkRect.height, iconRect.height, - textRect.height, accRect.height, arrowRect.height); - } else { - textRect = new Rectangle(); - iconRect = new Rectangle(); - SwingUtilities.layoutCompoundLabel(mi, fm, text, icon, - verticalAlignment, horizontalAlignment, - verticalTextPosition, horizontalTextPosition, - viewRect, iconRect, textRect, gap); - labelRect = iconRect.union(textRect); - } - } - - private void calcMaxWidths() { - maxCheckWidth = calcMaxValue(BasicMenuItemUI.MAX_CHECK_WIDTH, - checkRect.width); - maxArrowWidth = calcMaxValue(BasicMenuItemUI.MAX_ARROW_WIDTH, - arrowRect.width); - maxAccWidth = calcMaxValue(BasicMenuItemUI.MAX_ACC_WIDTH, - accRect.width); - - if (isColumnLayout) { - maxIconWidth = calcMaxValue(BasicMenuItemUI.MAX_ICON_WIDTH, - iconRect.width); - maxTextWidth = calcMaxValue(BasicMenuItemUI.MAX_TEXT_WIDTH, - textRect.width); - int curGap = gap; - if ((maxIconWidth == 0) || (maxTextWidth == 0)) { - curGap = 0; - } - maxLabelWidth = - calcMaxValue(BasicMenuItemUI.MAX_LABEL_WIDTH, - maxIconWidth + maxTextWidth + curGap); - } else { - // We shouldn't use current icon and text widths - // in maximal widths calculation for complex layout. - maxIconWidth = getParentIntProperty(BasicMenuItemUI.MAX_ICON_WIDTH); - maxLabelWidth = calcMaxValue(BasicMenuItemUI.MAX_LABEL_WIDTH, - labelRect.width); - // If maxLabelWidth is wider - // than the widest icon + the widest text + gap, - // we should update the maximal text witdh - int candidateTextWidth = maxLabelWidth - maxIconWidth; - if (maxIconWidth > 0) { - candidateTextWidth -= gap; - } - maxTextWidth = calcMaxValue(BasicMenuItemUI.MAX_TEXT_WIDTH, - candidateTextWidth); - } - } - - // Calculates and returns maximal value - // through specified parent component client property. - private int calcMaxValue(Object propertyName, int value) { - // Get maximal value from parent client property - int maxValue = getParentIntProperty(propertyName); - // Store new maximal width in parent client property - if (value > maxValue) { - if (miParent != null) { - miParent.putClientProperty(propertyName, value); - } - return value; - } else { - return maxValue; - } - } - - // Returns parent client property as int - private int getParentIntProperty(Object propertyName) { - Object value = null; - if (miParent != null) { - value = miParent.getClientProperty(propertyName); - } - if ((value == null) || !(value instanceof Integer)){ - value = 0; - } - return (Integer)value; - } - - private boolean isColumnLayout() { - return isColumnLayout(isLeftToRight, horizontalAlignment, - horizontalTextPosition, verticalTextPosition); - } - - public static boolean isColumnLayout(boolean isLeftToRight, - JMenuItem mi) { - assert(mi != null); - return isColumnLayout(isLeftToRight, mi.getHorizontalAlignment(), - mi.getHorizontalTextPosition(), mi.getVerticalTextPosition()); - } - - // Answers should we do column layout for a menu item or not. - // We do it when a user doesn't set any alignments - // and text positions manually, except the vertical alignment. - public static boolean isColumnLayout( boolean isLeftToRight, - int horizontalAlignment, int horizontalTextPosition, - int verticalTextPosition) { - if (verticalTextPosition != SwingConstants.CENTER) { - return false; - } - if (isLeftToRight) { - if (horizontalAlignment != SwingConstants.LEADING - && horizontalAlignment != SwingConstants.LEFT) { - return false; - } - if (horizontalTextPosition != SwingConstants.TRAILING - && horizontalTextPosition != SwingConstants.RIGHT) { - return false; - } - } else { - if (horizontalAlignment != SwingConstants.LEADING - && horizontalAlignment != SwingConstants.RIGHT) { - return false; - } - if (horizontalTextPosition != SwingConstants.TRAILING - && horizontalTextPosition != SwingConstants.LEFT) { - return false; - } - } - return true; - } - - // Calculates maximal text offset. - // It is required for some L&Fs (ex: Vista L&F). - // The offset is meaningful only for L2R column layout. - private void calcMaxTextOffset() { - if (!isColumnLayout || !isLeftToRight) { - return; - } - - // Calculate the current text offset - int offset = viewRect.x + leadingGap + maxCheckWidth - + afterCheckIconGap + maxIconWidth + gap; - if (maxCheckWidth == 0) { - offset -= afterCheckIconGap; - } - if (maxIconWidth == 0) { - offset -= gap; - } - - // maximal text offset shouldn't be less than minimal text offset; - if (offset < minTextOffset) { - offset = minTextOffset; - } - - // Calculate and store the maximal text offset - calcMaxValue(BASICMENUITEMUI_MAX_TEXT_OFFSET, offset); - } - - public String toString() { - StringBuilder result = new StringBuilder(); - result.append(super.toString()).append("\n"); - result.append("accFm = ").append(accFm).append("\n"); - result.append("accRect = ").append(accRect).append("\n"); - result.append("accText = ").append(accText).append("\n"); - result.append("afterCheckIconGap = ").append(afterCheckIconGap) - .append("\n"); - result.append("arrowIcon = ").append(arrowIcon).append("\n"); - result.append("arrowRect = ").append(arrowRect).append("\n"); - result.append("checkIcon = ").append(checkIcon).append("\n"); - result.append("checkRect = ").append(checkRect).append("\n"); - result.append("fm = ").append(fm).append("\n"); - result.append("gap = ").append(gap).append("\n"); - result.append("horizontalAlignment = ").append(horizontalAlignment) - .append("\n"); - result.append("horizontalTextPosition = ") - .append(horizontalTextPosition).append("\n"); - result.append("htmlView = ").append(htmlView).append("\n"); - result.append("icon = ").append(icon).append("\n"); - result.append("iconRect = ").append(iconRect).append("\n"); - result.append("isColumnLayout = ").append(isColumnLayout).append("\n"); - result.append("isLeftToRight = ").append(isLeftToRight).append("\n"); - result.append("isTopLevelMenu = ").append(isTopLevelMenu).append("\n"); - result.append("labelRect = ").append(labelRect).append("\n"); - result.append("leadingGap = ").append(leadingGap).append("\n"); - result.append("maxAccWidth = ").append(maxAccWidth).append("\n"); - result.append("maxArrowWidth = ").append(maxArrowWidth).append("\n"); - result.append("maxCheckWidth = ").append(maxCheckWidth).append("\n"); - result.append("maxIconWidth = ").append(maxIconWidth).append("\n"); - result.append("maxLabelWidth = ").append(maxLabelWidth).append("\n"); - result.append("maxTextWidth = ").append(maxTextWidth).append("\n"); - result.append("maxTextOffset = ") - .append(getParentIntProperty(BASICMENUITEMUI_MAX_TEXT_OFFSET)) - .append("\n"); - result.append("mi = ").append(mi).append("\n"); - result.append("minTextOffset = ").append(minTextOffset).append("\n"); - result.append("miParent = ").append(miParent).append("\n"); - result.append("origAccWidth = ").append(origAccWidth).append("\n"); - result.append("origArrowWidth = ").append(origArrowWidth).append("\n"); - result.append("origCheckWidth = ").append(origCheckWidth).append("\n"); - result.append("origIconWidth = ").append(origIconWidth).append("\n"); - result.append("origTextWidth = ").append(origTextWidth).append("\n"); - result.append("text = ").append(text).append("\n"); - result.append("textRect = ").append(textRect).append("\n"); - result.append("useCheckAndArrow = ").append(useCheckAndArrow) - .append("\n"); - result.append("verticalAlignment = ").append(verticalAlignment) - .append("\n"); - result.append("verticalTextPosition = ") - .append(verticalTextPosition).append("\n"); - result.append("viewRect = ").append(viewRect).append("\n"); - return result.toString(); - } - } // End of LayoutInfo - - // Reuses layoutInfo object to reduce the amount of produced garbage - private LayoutInfo getLayoutInfo(JMenuItem mi, Icon checkIcon, Icon arrowIcon, - Rectangle viewRect, int gap, String accDelimiter, - boolean isLeftToRight, Font acceleratorFont, - boolean useCheckAndArrow, String propertyPrefix) { - // layoutInfo is final and always not null - layoutInfo.reset(mi, checkIcon, arrowIcon, viewRect, - gap, accDelimiter, isLeftToRight, acceleratorFont, - useCheckAndArrow, propertyPrefix); - return layoutInfo; - } - /** * We draw the background in paintMenuItem() * so override update (which fills the background of opaque @@ -1016,122 +482,132 @@ public class BasicMenuItemUI extends MenuItemUI Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight()); applyInsets(viewRect, mi.getInsets()); - LayoutInfo li = getLayoutInfo(mi, checkIcon, arrowIcon, - viewRect, defaultTextIconGap, acceleratorDelimiter, - BasicGraphicsUtils.isLeftToRight(mi), acceleratorFont, - useCheckAndArrow(), getPropertyPrefix()); - layoutMenuItem(li); + MenuItemLayoutHelper lh = new MenuItemLayoutHelper(mi, checkIcon, + arrowIcon, viewRect, defaultTextIconGap, acceleratorDelimiter, + BasicGraphicsUtils.isLeftToRight(mi), mi.getFont(), + acceleratorFont, MenuItemLayoutHelper.useCheckAndArrow(menuItem), + getPropertyPrefix()); + MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem(); paintBackground(g, mi, background); - paintCheckIcon(g, li, holdc, foreground); - paintIcon(g, li, holdc); - paintText(g, li); - paintAccText(g, li); - paintArrowIcon(g, li, foreground); + paintCheckIcon(g, lh, lr, holdc, foreground); + paintIcon(g, lh, lr, holdc); + paintText(g, lh, lr); + paintAccText(g, lh, lr); + paintArrowIcon(g, lh, lr, foreground); // Restore original graphics font and color g.setColor(holdc); g.setFont(holdf); - - li.clear(); } - private void paintIcon(Graphics g, LayoutInfo li, Color holdc) { - if (li.icon != null) { + private void paintIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, Color holdc) { + if (lh.getIcon() != null) { Icon icon; - ButtonModel model = li.mi.getModel(); + ButtonModel model = lh.getMenuItem().getModel(); if (!model.isEnabled()) { - icon = li.mi.getDisabledIcon(); + icon = lh.getMenuItem().getDisabledIcon(); } else if (model.isPressed() && model.isArmed()) { - icon = li.mi.getPressedIcon(); + icon = lh.getMenuItem().getPressedIcon(); if (icon == null) { // Use default icon - icon = li.mi.getIcon(); + icon = lh.getMenuItem().getIcon(); } } else { - icon = li.mi.getIcon(); + icon = lh.getMenuItem().getIcon(); } if (icon != null) { - icon.paintIcon(li.mi, g, li.iconRect.x, li.iconRect.y); + icon.paintIcon(lh.getMenuItem(), g, lr.getIconRect().x, + lr.getIconRect().y); g.setColor(holdc); } } } - private void paintCheckIcon(Graphics g, LayoutInfo li, + private void paintCheckIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, Color holdc, Color foreground) { - if (li.checkIcon != null) { - ButtonModel model = li.mi.getModel(); - if (model.isArmed() - || (li.mi instanceof JMenu && model.isSelected())) { + if (lh.getCheckIcon() != null) { + ButtonModel model = lh.getMenuItem().getModel(); + if (model.isArmed() || (lh.getMenuItem() instanceof JMenu + && model.isSelected())) { g.setColor(foreground); } else { g.setColor(holdc); } - if (li.useCheckAndArrow) { - li.checkIcon.paintIcon(li.mi, g, li.checkRect.x, - li.checkRect.y); + if (lh.useCheckAndArrow()) { + lh.getCheckIcon().paintIcon(lh.getMenuItem(), g, + lr.getCheckRect().x, lr.getCheckRect().y); } g.setColor(holdc); } } - private void paintAccText(Graphics g, LayoutInfo li) { - if (!li.accText.equals("")) { - ButtonModel model = li.mi.getModel(); - g.setFont(acceleratorFont); + private void paintAccText(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr) { + if (!lh.getAccText().equals("")) { + ButtonModel model = lh.getMenuItem().getModel(); + g.setFont(lh.getAccFontMetrics().getFont()); if (!model.isEnabled()) { // *** paint the accText disabled if (disabledForeground != null) { g.setColor(disabledForeground); - SwingUtilities2.drawString(li.mi, g, li.accText, - li.accRect.x, - li.accRect.y + li.accFm.getAscent()); + SwingUtilities2.drawString(lh.getMenuItem(), g, + lh.getAccText(), lr.getAccRect().x, + lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); } else { - g.setColor(li.mi.getBackground().brighter()); - SwingUtilities2.drawString(li.mi, g, li.accText, li.accRect.x, - li.accRect.y + li.accFm.getAscent()); - g.setColor(li.mi.getBackground().darker()); - SwingUtilities2.drawString(li.mi, g, li.accText, - li.accRect.x - 1, - li.accRect.y + li.accFm.getAscent() - 1); + g.setColor(lh.getMenuItem().getBackground().brighter()); + SwingUtilities2.drawString(lh.getMenuItem(), g, + lh.getAccText(), lr.getAccRect().x, + lr.getAccRect().y + lh.getAccFontMetrics().getAscent()); + g.setColor(lh.getMenuItem().getBackground().darker()); + SwingUtilities2.drawString(lh.getMenuItem(), g, + lh.getAccText(), lr.getAccRect().x - 1, + lr.getAccRect().y + lh.getFontMetrics().getAscent() - 1); } } else { // *** paint the accText normally - if (model.isArmed() || - (li.mi instanceof JMenu && model.isSelected())) { + if (model.isArmed() + || (lh.getMenuItem() instanceof JMenu + && model.isSelected())) { g.setColor(acceleratorSelectionForeground); } else { g.setColor(acceleratorForeground); } - SwingUtilities2.drawString(li.mi, g, li.accText, li.accRect.x, - li.accRect.y + li.accFm.getAscent()); + SwingUtilities2.drawString(lh.getMenuItem(), g, lh.getAccText(), + lr.getAccRect().x, lr.getAccRect().y + + lh.getAccFontMetrics().getAscent()); } } } - private void paintText(Graphics g, LayoutInfo li) { - if (!li.text.equals("")) { - if (li.htmlView != null) { + private void paintText(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr) { + if (!lh.getText().equals("")) { + if (lh.getHtmlView() != null) { // Text is HTML - li.htmlView.paint(g, li.textRect); + lh.getHtmlView().paint(g, lr.getTextRect()); } else { // Text isn't HTML - paintText(g, li.mi, li.textRect, li.text); + paintText(g, lh.getMenuItem(), lr.getTextRect(), lh.getText()); } } } - private void paintArrowIcon(Graphics g, LayoutInfo li, Color foreground) { - if (li.arrowIcon != null) { - ButtonModel model = li.mi.getModel(); - if (model.isArmed() - || (li.mi instanceof JMenu && model.isSelected())) { + private void paintArrowIcon(Graphics g, MenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr, + Color foreground) { + if (lh.getArrowIcon() != null) { + ButtonModel model = lh.getMenuItem().getModel(); + if (model.isArmed() || (lh.getMenuItem() instanceof JMenu + && model.isSelected())) { g.setColor(foreground); } - if (li.useCheckAndArrow) { - li.arrowIcon.paintIcon(li.mi, g, li.arrowRect.x, li.arrowRect.y); + if (lh.useCheckAndArrow()) { + lh.getArrowIcon().paintIcon(lh.getMenuItem(), g, + lr.getArrowRect().x, lr.getArrowRect().y); } } } @@ -1216,346 +692,6 @@ public class BasicMenuItemUI extends MenuItemUI } } - - /** - * Layout icon, text, check icon, accelerator text and arrow icon - * in the viewRect and return their positions. - * - * If horizontalAlignment, verticalTextPosition and horizontalTextPosition - * are default (user doesn't set any manually) the layouting algorithm is: - * Elements are layouted in the five columns: - * check icon + icon + text + accelerator text + arrow icon - * - * In the other case elements are layouted in the four columns: - * check icon + label + accelerator text + arrow icon - * Label is icon and text rectangles union. - * - * The order of columns can be reversed. - * It depends on the menu item orientation. - */ - private void layoutMenuItem(LayoutInfo li) - { - li.checkRect.width = li.maxCheckWidth; - li.accRect.width = li.maxAccWidth; - li.arrowRect.width = li.maxArrowWidth; - - if (li.isColumnLayout) { - if (li.isLeftToRight) { - doLTRColumnLayout(li); - } else { - doRTLColumnLayout(li); - } - } else { - if (li.isLeftToRight) { - doLTRComplexLayout(li); - } else { - doRTLComplexLayout(li); - } - } - - alignAccCheckAndArrowVertically(li); - } - - // Aligns the accelertor text and the check and arrow icons vertically - // with the center of the label rect. - private void alignAccCheckAndArrowVertically(LayoutInfo li) { - li.accRect.y = (int)(li.labelRect.y + (float)li.labelRect.height/2 - - (float)li.accRect.height/2); - fixVerticalAlignment(li, li.accRect); - if (li.useCheckAndArrow) { - li.arrowRect.y = (int)(li.labelRect.y + (float)li.labelRect.height/2 - - (float)li.arrowRect.height/2); - li.checkRect.y = (int)(li.labelRect.y + (float)li.labelRect.height/2 - - (float)li.checkRect.height/2); - fixVerticalAlignment(li, li.arrowRect); - fixVerticalAlignment(li, li.checkRect); - } - } - - // Fixes vertical alignment of all menu item elements if a rect.y - // or (rect.y + rect.height) is out of viewRect bounds - private void fixVerticalAlignment(LayoutInfo li, Rectangle r) { - int delta = 0; - if (r.y < li.viewRect.y) { - delta = li.viewRect.y - r.y; - } else if (r.y + r.height > li.viewRect.y + li.viewRect.height) { - delta = li.viewRect.y + li.viewRect.height - r.y - r.height; - } - if (delta != 0) { - li.checkRect.y += delta; - li.iconRect.y += delta; - li.textRect.y += delta; - li.accRect.y += delta; - li.arrowRect.y += delta; - } - } - - private void doLTRColumnLayout(LayoutInfo li) { - // Set maximal width for all the five basic rects - // (three other ones are already maximal) - li.iconRect.width = li.maxIconWidth; - li.textRect.width = li.maxTextWidth; - - // Set X coordinates - // All rects will be aligned at the left side - calcXPositionsL2R(li.viewRect.x, li.leadingGap, li.gap, li.checkRect, - li.iconRect, li.textRect); - - // Tune afterCheckIconGap - if (li.checkRect.width > 0) { // there is the afterCheckIconGap - li.iconRect.x += li.afterCheckIconGap - li.gap; - li.textRect.x += li.afterCheckIconGap - li.gap; - } - - calcXPositionsR2L(li.viewRect.x + li.viewRect.width, li.gap, - li.arrowRect, li.accRect); - - // Take into account minimal text offset - int textOffset = li.textRect.x - li.viewRect.x; - if (!li.isTopLevelMenu && (textOffset < li.minTextOffset)) { - li.textRect.x += li.minTextOffset - textOffset; - } - - // Take into account the left side bearings for text and accelerator text. - fixTextRects(li); - - // Set Y coordinate for text and icon. - // Y coordinates for other rects - // will be calculated later in layoutMenuItem. - calcTextAndIconYPositions(li); - - // Calculate valid X and Y coordinates for labelRect - li.labelRect = li.textRect.union(li.iconRect); - } - - private void doLTRComplexLayout(LayoutInfo li) { - li.labelRect.width = li.maxLabelWidth; - - // Set X coordinates - calcXPositionsL2R(li.viewRect.x, li.leadingGap, li.gap, li.checkRect, - li.labelRect); - - // Tune afterCheckIconGap - if (li.checkRect.width > 0) { // there is the afterCheckIconGap - li.labelRect.x += li.afterCheckIconGap - li.gap; - } - - calcXPositionsR2L(li.viewRect.x + li.viewRect.width, li.gap, - li.arrowRect, li.accRect); - - // Take into account minimal text offset - int labelOffset = li.labelRect.x - li.viewRect.x; - if (!li.isTopLevelMenu && (labelOffset < li.minTextOffset)) { - li.labelRect.x += li.minTextOffset - labelOffset; - } - - // Take into account the left side bearing for accelerator text. - // The LSB for text is taken into account in layoutCompoundLabel() below. - fixAccTextRect(li); - - // Layout icon and text with SwingUtilities.layoutCompoundLabel() - // within the labelRect - li.textRect = new Rectangle(); - li.iconRect = new Rectangle(); - SwingUtilities.layoutCompoundLabel( - li.mi, li.fm, li.text, li.icon, li.verticalAlignment, - li.horizontalAlignment, li.verticalTextPosition, - li.horizontalTextPosition, li.labelRect, - li.iconRect, li.textRect, li.gap); - } - - private void doRTLColumnLayout(LayoutInfo li) { - // Set maximal width for all the five basic rects - // (three other ones are already maximal) - li.iconRect.width = li.maxIconWidth; - li.textRect.width = li.maxTextWidth; - - // Set X coordinates - calcXPositionsR2L(li.viewRect.x + li.viewRect.width, li.leadingGap, - li.gap, li.checkRect, li.iconRect, li.textRect); - - // Tune the gap after check icon - if (li.checkRect.width > 0) { // there is the gap after check icon - li.iconRect.x -= li.afterCheckIconGap - li.gap; - li.textRect.x -= li.afterCheckIconGap - li.gap; - } - - calcXPositionsL2R(li.viewRect.x, li.gap, li.arrowRect, - li.accRect); - - // Take into account minimal text offset - int textOffset = (li.viewRect.x + li.viewRect.width) - - (li.textRect.x + li.textRect.width); - if (!li.isTopLevelMenu && (textOffset < li.minTextOffset)) { - li.textRect.x -= li.minTextOffset - textOffset; - } - - // Align icon, text, accelerator text, check icon and arrow icon - // at the right side - rightAlignAllRects(li); - - // Take into account the left side bearings for text and accelerator text. - fixTextRects(li); - - // Set Y coordinates for text and icon. - // Y coordinates for other rects - // will be calculated later in layoutMenuItem. - calcTextAndIconYPositions(li); - - // Calculate valid X and Y coordinate for labelRect - li.labelRect = li.textRect.union(li.iconRect); - } - - private void doRTLComplexLayout(LayoutInfo li) { - li.labelRect.width = li.maxLabelWidth; - - // Set X coordinates - calcXPositionsR2L(li.viewRect.x + li.viewRect.width, li.leadingGap, - li.gap, li.checkRect, li.labelRect); - - // Tune the gap after check icon - if (li.checkRect.width > 0) { // there is the gap after check icon - li.labelRect.x -= li.afterCheckIconGap - li.gap; - } - - calcXPositionsL2R(li.viewRect.x, li.gap, li.arrowRect, - li.accRect); - - // Take into account minimal text offset - int labelOffset = (li.viewRect.x + li.viewRect.width) - - (li.labelRect.x + li.labelRect.width); - if (!li.isTopLevelMenu && (labelOffset < li.minTextOffset)) { - li.labelRect.x -= li.minTextOffset - labelOffset; - } - - // Align icon, text, accelerator text, check icon and arrow icon - // at the right side - rightAlignAllRects(li); - - // Take into account the left side bearing for accelerator text. - // The LSB for text is taken into account in layoutCompoundLabel() below. - fixAccTextRect(li); - - // Layout icon and text with SwingUtilities.layoutCompoundLabel() - // within the labelRect - li.textRect = new Rectangle(); - li.iconRect = new Rectangle(); - SwingUtilities.layoutCompoundLabel( - menuItem, li.fm, li.text, li.icon, li.verticalAlignment, - li.horizontalAlignment, li.verticalTextPosition, - li.horizontalTextPosition, li.labelRect, - li.iconRect, li.textRect, li.gap); - } - - private void calcXPositionsL2R(int startXPos, int leadingGap, - int gap, Rectangle... rects) { - int curXPos = startXPos + leadingGap; - for (Rectangle rect : rects) { - rect.x = curXPos; - if (rect.width > 0) { - curXPos += rect.width + gap; - } - } - } - - private void calcXPositionsL2R(int startXPos, int gap, Rectangle... rects) { - calcXPositionsL2R(startXPos, gap, gap, rects); - } - - private void calcXPositionsR2L(int startXPos, int leadingGap, - int gap, Rectangle... rects) { - int curXPos = startXPos - leadingGap; - for (Rectangle rect : rects) { - rect.x = curXPos - rect.width; - if (rect.width > 0) { - curXPos -= rect.width + gap; - } - } - } - - private void calcXPositionsR2L(int startXPos, int gap, Rectangle... rects) { - calcXPositionsR2L(startXPos, gap, gap, rects); - } - - // Takes into account the left side bearings for text and accelerator text - private void fixTextRects(LayoutInfo li) { - if (li.htmlView == null) { // The text isn't a HTML - int lsb = SwingUtilities2.getLeftSideBearing(li.mi, li.fm, li.text); - if (lsb < 0) { - li.textRect.x -= lsb; - } - } - fixAccTextRect(li); - } - - // Takes into account the left side bearing for accelerator text - private void fixAccTextRect(LayoutInfo li) { - int lsb = SwingUtilities2 - .getLeftSideBearing(li.mi, li.accFm, li.accText); - if (lsb < 0) { - li.accRect.x -= lsb; - } - } - - // Sets Y coordinates of text and icon - // taking into account the vertical alignment - private void calcTextAndIconYPositions(LayoutInfo li) { - if (li.verticalAlignment == SwingUtilities.TOP) { - li.textRect.y = (int)(li.viewRect.y - + (float)li.labelRect.height/2 - - (float)li.textRect.height/2); - li.iconRect.y = (int)(li.viewRect.y - + (float)li.labelRect.height/2 - - (float)li.iconRect.height/2); - } else if (li.verticalAlignment == SwingUtilities.CENTER) { - li.textRect.y = (int)(li.viewRect.y - + (float)li.viewRect.height/2 - - (float)li.textRect.height/2); - li.iconRect.y = (int)(li.viewRect.y - + (float)li.viewRect.height/2 - - (float)li.iconRect.height/2); - } - else if (li.verticalAlignment == SwingUtilities.BOTTOM) { - li.textRect.y = (int)(li.viewRect.y + li.viewRect.height - - (float)li.labelRect.height/2 - - (float)li.textRect.height/2); - li.iconRect.y = (int)(li.viewRect.y + li.viewRect.height - - (float)li.labelRect.height/2 - - (float)li.iconRect.height/2); - } - } - - // Aligns icon, text, accelerator text, check icon and arrow icon - // at the right side - private void rightAlignAllRects(LayoutInfo li) { - li.iconRect.x = li.iconRect.x + li.iconRect.width - li.origIconWidth; - li.iconRect.width = li.origIconWidth; - li.textRect.x = li.textRect.x + li.textRect.width - li.origTextWidth; - li.textRect.width = li.origTextWidth; - li.accRect.x = li.accRect.x + li.accRect.width - - li.origAccWidth; - li.accRect.width = li.origAccWidth; - li.checkRect.x = li.checkRect.x + li.checkRect.width - - li.origCheckWidth; - li.checkRect.width = li.origCheckWidth; - li.arrowRect.x = li.arrowRect.x + li.arrowRect.width - - li.origArrowWidth; - li.arrowRect.width = li.origArrowWidth; - } - - /* - * Returns false if the component is a JMenu and it is a top - * level menu (on the menubar). - */ - private boolean useCheckAndArrow(){ - boolean b = true; - if((menuItem instanceof JMenu) && - (((JMenu)menuItem).isTopLevelMenu())) { - b = false; - } - return b; - } - public MenuElement[] getPath() { MenuSelectionManager m = MenuSelectionManager.defaultManager(); MenuElement oldPath[] = m.getSelectedPath(); diff --git a/src/share/classes/javax/swing/plaf/basic/DefaultMenuLayout.java b/src/share/classes/javax/swing/plaf/basic/DefaultMenuLayout.java index b29cfaf7a..5acae97ed 100644 --- a/src/share/classes/javax/swing/plaf/basic/DefaultMenuLayout.java +++ b/src/share/classes/javax/swing/plaf/basic/DefaultMenuLayout.java @@ -1,5 +1,5 @@ /* - * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ import javax.swing.plaf.UIResource; import java.awt.Container; import java.awt.Dimension; -import static sun.swing.SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET; /** * The default layout manager for Popup menus and menubars. This @@ -49,18 +48,7 @@ public class DefaultMenuLayout extends BoxLayout implements UIResource { public Dimension preferredLayoutSize(Container target) { if (target instanceof JPopupMenu) { JPopupMenu popupMenu = (JPopupMenu) target; - - // Before the calculation of menu preferred size - // clear the previously calculated maximal widths and offsets - // in menu's Client Properties - popupMenu.putClientProperty(BasicMenuItemUI.MAX_ACC_WIDTH, null); - popupMenu.putClientProperty(BasicMenuItemUI.MAX_ARROW_WIDTH, null); - popupMenu.putClientProperty(BasicMenuItemUI.MAX_CHECK_WIDTH, null); - popupMenu.putClientProperty(BasicMenuItemUI.MAX_ICON_WIDTH, null); - popupMenu.putClientProperty(BasicMenuItemUI.MAX_LABEL_WIDTH, null); - popupMenu.putClientProperty(BasicMenuItemUI.MAX_TEXT_WIDTH, null); - popupMenu.putClientProperty(BASICMENUITEMUI_MAX_TEXT_OFFSET, null); - + sun.swing.MenuItemLayoutHelper.clearUsedClientProperties(popupMenu); if (popupMenu.getComponentCount() == 0) { return new Dimension(0, 0); } diff --git a/src/share/classes/javax/swing/plaf/synth/DefaultMenuLayout.java b/src/share/classes/javax/swing/plaf/synth/DefaultMenuLayout.java index 678452328..1757a2e66 100644 --- a/src/share/classes/javax/swing/plaf/synth/DefaultMenuLayout.java +++ b/src/share/classes/javax/swing/plaf/synth/DefaultMenuLayout.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,19 +47,22 @@ class DefaultMenuLayout extends BoxLayout implements UIResource { super(target, axis); } - public void invalidateLayout(Container target) { + public Dimension preferredLayoutSize(Container target) { if (target instanceof JPopupMenu) { - SynthPopupMenuUI popupUI = (SynthPopupMenuUI)((JPopupMenu)target). - getUI(); - popupUI.resetAlignmentHints(); + JPopupMenu popupMenu = (JPopupMenu) target; + + popupMenu.putClientProperty( + SynthMenuItemLayoutHelper.MAX_ACC_OR_ARROW_WIDTH, null); + sun.swing.MenuItemLayoutHelper.clearUsedClientProperties(popupMenu); + + if (popupMenu.getComponentCount() == 0) { + return new Dimension(0, 0); + } } + + // Make BoxLayout recalculate cached preferred sizes super.invalidateLayout(target); - } - public Dimension preferredLayoutSize(Container target) { - if (target instanceof JPopupMenu && target.getComponentCount() == 0) { - return new Dimension(0, 0); - } return super.preferredLayoutSize(target); } } diff --git a/src/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java b/src/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java index 62c45ab64..a8ec7728a 100644 --- a/src/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java +++ b/src/share/classes/javax/swing/plaf/synth/SynthGraphicsUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ package javax.swing.plaf.synth; import sun.swing.SwingUtilities2; +import sun.swing.MenuItemLayoutHelper; + import java.awt.*; import javax.swing.*; import javax.swing.plaf.basic.BasicHTML; @@ -411,6 +413,198 @@ public class SynthGraphicsUtils { } + /** + * A quick note about how preferred sizes are calculated... Generally + * speaking, SynthPopupMenuUI will run through the list of its children + * (from top to bottom) and ask each for its preferred size. Each menu + * item will add up the max width of each element (icons, text, + * accelerator spacing, accelerator text or arrow icon) encountered thus + * far, so by the time all menu items have been calculated, we will + * know the maximum (preferred) menu item size for that popup menu. + * Later when it comes time to paint each menu item, we can use those + * same accumulated max element sizes in order to layout the item. + */ + static Dimension getPreferredMenuItemSize(SynthContext context, + SynthContext accContext, JComponent c, + Icon checkIcon, Icon arrowIcon, int defaultTextIconGap, + String acceleratorDelimiter, boolean useCheckAndArrow, + String propertyPrefix) { + + JMenuItem mi = (JMenuItem) c; + SynthMenuItemLayoutHelper lh = new SynthMenuItemLayoutHelper( + context, accContext, mi, checkIcon, arrowIcon, + MenuItemLayoutHelper.createMaxRect(), defaultTextIconGap, + acceleratorDelimiter, SynthLookAndFeel.isLeftToRight(mi), + useCheckAndArrow, propertyPrefix); + + Dimension result = new Dimension(); + + // Calculate the result width + int gap = lh.getGap(); + result.width = 0; + MenuItemLayoutHelper.addMaxWidth(lh.getCheckSize(), gap, result); + MenuItemLayoutHelper.addMaxWidth(lh.getLabelSize(), gap, result); + MenuItemLayoutHelper.addWidth(lh.getMaxAccOrArrowWidth(), 5 * gap, result); + // The last gap is unnecessary + result.width -= gap; + + // Calculate the result height + result.height = MenuItemLayoutHelper.max(lh.getCheckSize().getHeight(), + lh.getLabelSize().getHeight(), lh.getAccSize().getHeight(), + lh.getArrowSize().getHeight()); + + // Take into account menu item insets + Insets insets = lh.getMenuItem().getInsets(); + if (insets != null) { + result.width += insets.left + insets.right; + result.height += insets.top + insets.bottom; + } + + // if the width is even, bump it up one. This is critical + // for the focus dash lhne to draw properly + if (result.width % 2 == 0) { + result.width++; + } + + // if the height is even, bump it up one. This is critical + // for the text to center properly + if (result.height % 2 == 0) { + result.height++; + } + + return result; + } + + static void applyInsets(Rectangle rect, Insets insets) { + if (insets != null) { + rect.x += insets.left; + rect.y += insets.top; + rect.width -= (insets.right + rect.x); + rect.height -= (insets.bottom + rect.y); + } + } + + static void paint(SynthContext context, SynthContext accContext, Graphics g, + Icon checkIcon, Icon arrowIcon, String acceleratorDelimiter, + int defaultTextIconGap, String propertyPrefix) { + JMenuItem mi = (JMenuItem) context.getComponent(); + SynthStyle style = context.getStyle(); + g.setFont(style.getFont(context)); + + Rectangle viewRect = new Rectangle(0, 0, mi.getWidth(), mi.getHeight()); + applyInsets(viewRect, mi.getInsets()); + + SynthMenuItemLayoutHelper lh = new SynthMenuItemLayoutHelper( + context, accContext, mi, checkIcon, + arrowIcon, viewRect, defaultTextIconGap, acceleratorDelimiter, + SynthLookAndFeel.isLeftToRight(mi), + MenuItemLayoutHelper.useCheckAndArrow(mi), propertyPrefix); + MenuItemLayoutHelper.LayoutResult lr = lh.layoutMenuItem(); + + paintMenuItem(g, lh, lr); + } + + static void paintMenuItem(Graphics g, SynthMenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr) { + // Save original graphics font and color + Font holdf = g.getFont(); + Color holdc = g.getColor(); + + paintBackground(g, lh); + paintCheckIcon(g, lh, lr); + paintIcon(g, lh, lr); + paintText(g, lh, lr); + paintAccText(g, lh, lr); + paintArrowIcon(g, lh, lr); + + // Restore original graphics font and color + g.setColor(holdc); + g.setFont(holdf); + } + + static void paintBackground(Graphics g, SynthMenuItemLayoutHelper lh) { + paintBackground(lh.getContext(), g, lh.getMenuItem()); + } + + static void paintBackground(SynthContext context, Graphics g, JComponent c) { + context.getPainter().paintMenuItemBackground(context, g, 0, 0, + c.getWidth(), c.getHeight()); + } + + static void paintIcon(Graphics g, SynthMenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr) { + if (lh.getIcon() != null) { + Icon icon; + JMenuItem mi = lh.getMenuItem(); + ButtonModel model = mi.getModel(); + if (!model.isEnabled()) { + icon = mi.getDisabledIcon(); + } else if (model.isPressed() && model.isArmed()) { + icon = mi.getPressedIcon(); + if (icon == null) { + // Use default icon + icon = mi.getIcon(); + } + } else { + icon = mi.getIcon(); + } + + if (icon != null) { + Rectangle iconRect = lr.getIconRect(); + SynthIcon.paintIcon(icon, lh.getContext(), g, iconRect.x, + iconRect.y, iconRect.width, iconRect.height); + } + } + } + + static void paintCheckIcon(Graphics g, SynthMenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr) { + if (lh.getCheckIcon() != null) { + Rectangle checkRect = lr.getCheckRect(); + SynthIcon.paintIcon(lh.getCheckIcon(), lh.getContext(), g, + checkRect.x, checkRect.y, checkRect.width, checkRect.height); + } + } + + static void paintAccText(Graphics g, SynthMenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr) { + String accText = lh.getAccText(); + if (accText != null && !accText.equals("")) { + g.setColor(lh.getAccStyle().getColor(lh.getAccContext(), + ColorType.TEXT_FOREGROUND)); + g.setFont(lh.getAccStyle().getFont(lh.getAccContext())); + lh.getAccGraphicsUtils().paintText(lh.getAccContext(), g, accText, + lr.getAccRect().x, lr.getAccRect().y, -1); + } + } + + static void paintText(Graphics g, SynthMenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr) { + if (!lh.getText().equals("")) { + if (lh.getHtmlView() != null) { + // Text is HTML + lh.getHtmlView().paint(g, lr.getTextRect()); + } else { + // Text isn't HTML + g.setColor(lh.getStyle().getColor( + lh.getContext(), ColorType.TEXT_FOREGROUND)); + g.setFont(lh.getStyle().getFont(lh.getContext())); + lh.getGraphicsUtils().paintText(lh.getContext(), g, lh.getText(), + lr.getTextRect().x, lr.getTextRect().y, + lh.getMenuItem().getDisplayedMnemonicIndex()); + } + } + } + + static void paintArrowIcon(Graphics g, SynthMenuItemLayoutHelper lh, + MenuItemLayoutHelper.LayoutResult lr) { + if (lh.getArrowIcon() != null) { + Rectangle arrowRect = lr.getArrowRect(); + SynthIcon.paintIcon(lh.getArrowIcon(), lh.getContext(), g, + arrowRect.x, arrowRect.y, arrowRect.width, arrowRect.height); + } + } + /** * Wraps a SynthIcon around the Icon interface, forwarding calls to * the SynthIcon with a given SynthContext. diff --git a/src/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java b/src/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java new file mode 100644 index 000000000..6b890c786 --- /dev/null +++ b/src/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java @@ -0,0 +1,307 @@ +/* + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.swing.plaf.synth; + +import sun.swing.StringUIClientPropertyKey; +import sun.swing.MenuItemLayoutHelper; +import sun.swing.plaf.synth.SynthIcon; + +import javax.swing.*; +import javax.swing.text.View; +import java.awt.*; + +/** + * Calculates preferred size and layouts synth menu items. + * + * All JMenuItems (and JMenus) include enough space for the insets + * plus one or more elements. When we say "label" below, we mean + * "icon and/or text." + * + * Cases to consider for SynthMenuItemUI (visualized here in a + * LTR orientation; the RTL case would be reversed): + * label + * check icon + label + * check icon + label + accelerator + * label + accelerator + * + * Cases to consider for SynthMenuUI (again visualized here in a + * LTR orientation): + * label + arrow + * + * Note that in the above scenarios, accelerator and arrow icon are + * mutually exclusive. This means that if a popup menu contains a mix + * of JMenus and JMenuItems, we only need to allow enough space for + * max(maxAccelerator, maxArrow), and both accelerators and arrow icons + * can occupy the same "column" of space in the menu. + */ +class SynthMenuItemLayoutHelper extends MenuItemLayoutHelper { + + public static final StringUIClientPropertyKey MAX_ACC_OR_ARROW_WIDTH = + new StringUIClientPropertyKey("maxAccOrArrowWidth"); + + public static final ColumnAlignment LTR_ALIGNMENT_1 = + new ColumnAlignment( + SwingConstants.LEFT, + SwingConstants.LEFT, + SwingConstants.LEFT, + SwingConstants.RIGHT, + SwingConstants.RIGHT + ); + public static final ColumnAlignment LTR_ALIGNMENT_2 = + new ColumnAlignment( + SwingConstants.LEFT, + SwingConstants.LEFT, + SwingConstants.LEFT, + SwingConstants.LEFT, + SwingConstants.RIGHT + ); + public static final ColumnAlignment RTL_ALIGNMENT_1 = + new ColumnAlignment( + SwingConstants.RIGHT, + SwingConstants.RIGHT, + SwingConstants.RIGHT, + SwingConstants.LEFT, + SwingConstants.LEFT + ); + public static final ColumnAlignment RTL_ALIGNMENT_2 = + new ColumnAlignment( + SwingConstants.RIGHT, + SwingConstants.RIGHT, + SwingConstants.RIGHT, + SwingConstants.RIGHT, + SwingConstants.LEFT + ); + + private SynthContext context; + private SynthContext accContext; + private SynthStyle style; + private SynthStyle accStyle; + private SynthGraphicsUtils gu; + private SynthGraphicsUtils accGu; + private boolean alignAcceleratorText; + private int maxAccOrArrowWidth; + + public SynthMenuItemLayoutHelper(SynthContext context, SynthContext accContext, + JMenuItem mi, Icon checkIcon, Icon arrowIcon, + Rectangle viewRect, int gap, String accDelimiter, + boolean isLeftToRight, boolean useCheckAndArrow, + String propertyPrefix) { + this.context = context; + this.accContext = accContext; + this.style = context.getStyle(); + this.accStyle = accContext.getStyle(); + this.gu = style.getGraphicsUtils(context); + this.accGu = accStyle.getGraphicsUtils(accContext); + this.alignAcceleratorText = getAlignAcceleratorText(propertyPrefix); + reset(mi, checkIcon, arrowIcon, viewRect, gap, accDelimiter, + isLeftToRight, style.getFont(context), accStyle.getFont(accContext), + useCheckAndArrow, propertyPrefix); + setLeadingGap(0); + } + + private boolean getAlignAcceleratorText(String propertyPrefix) { + return style.getBoolean(context, + propertyPrefix + ".alignAcceleratorText", true); + } + + protected void calcWidthsAndHeights() { + // iconRect + if (getIcon() != null) { + getIconSize().setWidth(SynthIcon.getIconWidth(getIcon(), context)); + getIconSize().setHeight(SynthIcon.getIconHeight(getIcon(), context)); + } + + // accRect + if (!getAccText().equals("")) { + getAccSize().setWidth(accGu.computeStringWidth(getAccContext(), + getAccFontMetrics().getFont(), getAccFontMetrics(), + getAccText())); + getAccSize().setHeight(getAccFontMetrics().getHeight()); + } + + // textRect + if (getText() == null) { + setText(""); + } else if (!getText().equals("")) { + if (getHtmlView() != null) { + // Text is HTML + getTextSize().setWidth( + (int) getHtmlView().getPreferredSpan(View.X_AXIS)); + getTextSize().setHeight( + (int) getHtmlView().getPreferredSpan(View.Y_AXIS)); + } else { + // Text isn't HTML + getTextSize().setWidth(gu.computeStringWidth(context, + getFontMetrics().getFont(), getFontMetrics(), + getText())); + getTextSize().setHeight(getFontMetrics().getHeight()); + } + } + + if (useCheckAndArrow()) { + // checkIcon + if (getCheckIcon() != null) { + getCheckSize().setWidth( + SynthIcon.getIconWidth(getCheckIcon(), context)); + getCheckSize().setHeight( + SynthIcon.getIconHeight(getCheckIcon(), context)); + } + // arrowRect + if (getArrowIcon() != null) { + getArrowSize().setWidth( + SynthIcon.getIconWidth(getArrowIcon(), context)); + getArrowSize().setHeight( + SynthIcon.getIconHeight(getArrowIcon(), context)); + } + } + + // labelRect + if (isColumnLayout()) { + getLabelSize().setWidth(getIconSize().getWidth() + + getTextSize().getWidth() + getGap()); + getLabelSize().setHeight(MenuItemLayoutHelper.max( + getCheckSize().getHeight(), + getIconSize().getHeight(), + getTextSize().getHeight(), + getAccSize().getHeight(), + getArrowSize().getHeight())); + } else { + Rectangle textRect = new Rectangle(); + Rectangle iconRect = new Rectangle(); + gu.layoutText(context, getFontMetrics(), getText(), getIcon(), + getHorizontalAlignment(), getVerticalAlignment(), + getHorizontalTextPosition(), getVerticalTextPosition(), + getViewRect(), iconRect, textRect, getGap()); + Rectangle labelRect = iconRect.union(textRect); + getLabelSize().setHeight(labelRect.height); + getLabelSize().setWidth(labelRect.width); + } + } + + protected void calcMaxWidths() { + calcMaxWidth(getCheckSize(), MAX_CHECK_WIDTH); + maxAccOrArrowWidth = + calcMaxValue(MAX_ACC_OR_ARROW_WIDTH, getArrowSize().getWidth()); + maxAccOrArrowWidth = + calcMaxValue(MAX_ACC_OR_ARROW_WIDTH, getAccSize().getWidth()); + + if (isColumnLayout()) { + calcMaxWidth(getIconSize(), MAX_ICON_WIDTH); + calcMaxWidth(getTextSize(), MAX_TEXT_WIDTH); + int curGap = getGap(); + if ((getIconSize().getMaxWidth() == 0) + || (getTextSize().getMaxWidth() == 0)) { + curGap = 0; + } + getLabelSize().setMaxWidth( + calcMaxValue(MAX_LABEL_WIDTH, getIconSize().getMaxWidth() + + getTextSize().getMaxWidth() + curGap)); + } else { + // We shouldn't use current icon and text widths + // in maximal widths calculation for complex layout. + getIconSize().setMaxWidth(getParentIntProperty( + MAX_ICON_WIDTH)); + calcMaxWidth(getLabelSize(), MAX_LABEL_WIDTH); + // If maxLabelWidth is wider + // than the widest icon + the widest text + gap, + // we should update the maximal text witdh + int candidateTextWidth = getLabelSize().getMaxWidth() - + getIconSize().getMaxWidth(); + if (getIconSize().getMaxWidth() > 0) { + candidateTextWidth -= getGap(); + } + getTextSize().setMaxWidth(calcMaxValue( + MAX_TEXT_WIDTH, candidateTextWidth)); + } + } + + public SynthContext getContext() { + return context; + } + + public SynthContext getAccContext() { + return accContext; + } + + public SynthStyle getStyle() { + return style; + } + + public SynthStyle getAccStyle() { + return accStyle; + } + + public SynthGraphicsUtils getGraphicsUtils() { + return gu; + } + + public SynthGraphicsUtils getAccGraphicsUtils() { + return accGu; + } + + public boolean alignAcceleratorText() { + return alignAcceleratorText; + } + + public int getMaxAccOrArrowWidth() { + return maxAccOrArrowWidth; + } + + protected void prepareForLayout(LayoutResult lr) { + lr.getCheckRect().width = getCheckSize().getMaxWidth(); + // An item can have an arrow or a check icon at once + if (useCheckAndArrow() && (!"".equals(getAccText()))) { + lr.getAccRect().width = maxAccOrArrowWidth; + } else { + lr.getArrowRect().width = maxAccOrArrowWidth; + } + } + + public ColumnAlignment getLTRColumnAlignment() { + if (alignAcceleratorText()) { + return LTR_ALIGNMENT_2; + } else { + return LTR_ALIGNMENT_1; + } + } + + public ColumnAlignment getRTLColumnAlignment() { + if (alignAcceleratorText()) { + return RTL_ALIGNMENT_2; + } else { + return RTL_ALIGNMENT_1; + } + } + + protected void layoutIconAndTextInLabelRect(LayoutResult lr) { + lr.setTextRect(new Rectangle()); + lr.setIconRect(new Rectangle()); + gu.layoutText(context, getFontMetrics(), getText(), getIcon(), + getHorizontalAlignment(), getVerticalAlignment(), + getHorizontalTextPosition(), getVerticalTextPosition(), + lr.getLabelRect(), lr.getIconRect(), lr.getTextRect(), getGap()); + } +} diff --git a/src/share/classes/javax/swing/plaf/synth/SynthMenuItemUI.java b/src/share/classes/javax/swing/plaf/synth/SynthMenuItemUI.java index 39fc82c10..3bcc044a1 100644 --- a/src/share/classes/javax/swing/plaf/synth/SynthMenuItemUI.java +++ b/src/share/classes/javax/swing/plaf/synth/SynthMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,7 @@ import javax.swing.plaf.*; import javax.swing.plaf.basic.*; import javax.swing.text.View; import sun.swing.plaf.synth.*; -import sun.swing.SwingUtilities2; +import sun.swing.MenuItemLayoutHelper; /** @@ -59,542 +59,16 @@ class SynthMenuItemUI extends BasicMenuItemUI implements return new SynthMenuItemUI(); } - // - // The next handful of static methods are used by both SynthMenuUI - // and SynthMenuItemUI. This is necessitated by SynthMenuUI not - // extending SynthMenuItemUI. - // - - /* - * All JMenuItems (and JMenus) include enough space for the insets - * plus one or more elements. When we say "icon(s)" below, we mean - * "check/radio indicator and/or user icon." If both are defined for - * a given menu item, then in a LTR orientation the check/radio indicator - * is on the left side followed by the user icon to the right; it is - * just the opposite in a RTL orientation. - * - * Cases to consider for SynthMenuItemUI (visualized here in a - * LTR orientation; the RTL case would be reversed): - * text - * icon(s) + text - * icon(s) + text + accelerator - * text + accelerator - * - * Cases to consider for SynthMenuUI (again visualized here in a - * LTR orientation): - * text + arrow - * (user)icon + text + arrow - * - * Note that in the above scenarios, accelerator and arrow icon are - * mutually exclusive. This means that if a popup menu contains a mix - * of JMenus and JMenuItems, we only need to allow enough space for - * max(maxAccelerator, maxArrow), and both accelerators and arrow icons - * can occupy the same "column" of space in the menu. - * - * A quick note about how preferred sizes are calculated... Generally - * speaking, SynthPopupMenuUI will run through the list of its children - * (from top to bottom) and ask each for its preferred size. Each menu - * item will add up the max width of each element (icons, text, - * accelerator spacing, accelerator text or arrow icon) encountered thus - * far, so by the time all menu items have been calculated, we will - * know the maximum (preferred) menu item size for that popup menu. - * Later when it comes time to paint each menu item, we can use those - * same accumulated max element sizes in order to layout the item. - */ - static Dimension getPreferredMenuItemSize(SynthContext context, - SynthContext accContext, JComponent c, - Icon checkIcon, Icon arrowIcon, int defaultTextIconGap, - String acceleratorDelimiter) { - JMenuItem b = (JMenuItem) c; - Icon icon = b.getIcon(); - String text = b.getText(); - KeyStroke accelerator = b.getAccelerator(); - String acceleratorText = ""; - - if (accelerator != null) { - int modifiers = accelerator.getModifiers(); - if (modifiers > 0) { - acceleratorText = KeyEvent.getKeyModifiersText(modifiers); - acceleratorText += acceleratorDelimiter; - } - int keyCode = accelerator.getKeyCode(); - if (keyCode != 0) { - acceleratorText += KeyEvent.getKeyText(keyCode); - } else { - acceleratorText += accelerator.getKeyChar(); - } - } - - Font font = context.getStyle().getFont(context); - FontMetrics fm = b.getFontMetrics(font); - FontMetrics fmAccel = b.getFontMetrics(accContext.getStyle(). - getFont(accContext)); - - resetRects(); - - layoutMenuItem( - context, fm, accContext, text, fmAccel, acceleratorText, - icon, checkIcon, arrowIcon, b.getVerticalAlignment(), - b.getHorizontalAlignment(), b.getVerticalTextPosition(), - b.getHorizontalTextPosition(), viewRect, iconRect, textRect, - acceleratorRect, checkIconRect, arrowIconRect, - text == null ? 0 : defaultTextIconGap, defaultTextIconGap); - - r.setBounds(textRect); - - int totalIconWidth = 0; - int maxIconHeight = 0; - if (icon != null) { - // Add in the user icon - totalIconWidth += iconRect.width; - if (textRect.width > 0) { - // Allow for some room between the user icon and the text - totalIconWidth += defaultTextIconGap; - } - maxIconHeight = Math.max(iconRect.height, maxIconHeight); - } - if (checkIcon != null) { - // Add in the checkIcon - totalIconWidth += checkIconRect.width; - if (textRect.width > 0 || icon != null) { - // Allow for some room between the check/radio indicator - // and the text (or user icon, if both are specified) - totalIconWidth += defaultTextIconGap; - } - maxIconHeight = Math.max(checkIconRect.height, maxIconHeight); - } - - int arrowWidth = 0; - if (arrowIcon != null) { - // Add in the arrowIcon - arrowWidth += defaultTextIconGap; - arrowWidth += arrowIconRect.width; - maxIconHeight = Math.max(arrowIconRect.height, maxIconHeight); - } - - int accelSpacing = 0; - if (acceleratorRect.width > 0) { - // Allow for some room between the text and the accelerator - accelSpacing += 4*defaultTextIconGap; - } - - // Take text and all icons into account when determining height - r.height = Math.max(r.height, maxIconHeight); - - // To make the accelerator texts appear in a column, - // find the widest MenuItem text and the widest accelerator text. - - // Get the parent, which stores the information. - Container parent = b.getParent(); - - if (parent instanceof JPopupMenu) { - SynthPopupMenuUI popupUI = (SynthPopupMenuUI)SynthLookAndFeel. - getUIOfType(((JPopupMenu)parent).getUI(), - SynthPopupMenuUI.class); - - if (popupUI != null) { - // This gives us the widest MenuItem text encountered thus - // far in the parent JPopupMenu - r.width = popupUI.adjustTextWidth(r.width); - - // Add in the widest icon (includes both user and - // check/radio icons) encountered thus far - r.width += popupUI.adjustIconWidth(totalIconWidth); - - // Add in the widest text/accelerator spacing - // encountered thus far - r.width += popupUI.adjustAccelSpacingWidth(accelSpacing); - - // Add in the widest accelerator text (or arrow) - // encountered thus far (at least one of these values - // will always be zero, so we combine them here to - // avoid double counting) - int totalAccelOrArrow = acceleratorRect.width + arrowWidth; - r.width += popupUI.adjustAcceleratorWidth(totalAccelOrArrow); - } + public void uninstallUI(JComponent c) { + super.uninstallUI(c); + // Remove values from the parent's Client Properties. + JComponent p = MenuItemLayoutHelper.getMenuItemParent((JMenuItem) c); + if (p != null) { + p.putClientProperty( + SynthMenuItemLayoutHelper.MAX_ACC_OR_ARROW_WIDTH, null); } - else if (parent != null && !(b instanceof JMenu && - ((JMenu)b).isTopLevelMenu())) { - r.width += - totalIconWidth + accelSpacing + - acceleratorRect.width + arrowWidth; - } - - Insets insets = b.getInsets(); - if(insets != null) { - r.width += insets.left + insets.right; - r.height += insets.top + insets.bottom; - } - - // if the width is even, bump it up one. This is critical - // for the focus dash line to draw properly - if(r.width%2 == 0) { - r.width++; - } - - // if the height is even, bump it up one. This is critical - // for the text to center properly - if(r.height%2 == 0) { - r.height++; - } - return r.getSize(); } - static void paint(SynthContext context, SynthContext accContext, - Graphics g, Icon checkIcon, Icon arrowIcon, - String acceleratorDelimiter, - int defaultTextIconGap) { - JComponent c = context.getComponent(); - JMenuItem b = (JMenuItem)c; - ButtonModel model = b.getModel(); - Insets i = b.getInsets(); - - resetRects(); - - viewRect.setBounds(0, 0, b.getWidth(), b.getHeight()); - - viewRect.x += i.left; - viewRect.y += i.top; - viewRect.width -= (i.right + viewRect.x); - viewRect.height -= (i.bottom + viewRect.y); - - SynthStyle style = context.getStyle(); - Font f = style.getFont(context); - g.setFont(f); - FontMetrics fm = SwingUtilities2.getFontMetrics(c, g, f); - FontMetrics accFM = SwingUtilities2.getFontMetrics(c, g, - accContext.getStyle(). - getFont(accContext)); - - // get Accelerator text - KeyStroke accelerator = b.getAccelerator(); - String acceleratorText = ""; - if (accelerator != null) { - int modifiers = accelerator.getModifiers(); - if (modifiers > 0) { - acceleratorText = KeyEvent.getKeyModifiersText(modifiers); - acceleratorText += acceleratorDelimiter; - } - - int keyCode = accelerator.getKeyCode(); - if (keyCode != 0) { - acceleratorText += KeyEvent.getKeyText(keyCode); - } else { - acceleratorText += accelerator.getKeyChar(); - } - } - - // Layout the text and icon - String text = layoutMenuItem(context, fm, accContext, - b.getText(), accFM, acceleratorText, b.getIcon(), - checkIcon, arrowIcon, - b.getVerticalAlignment(), b.getHorizontalAlignment(), - b.getVerticalTextPosition(), b.getHorizontalTextPosition(), - viewRect, iconRect, textRect, acceleratorRect, - checkIconRect, arrowIconRect, - b.getText() == null ? 0 : defaultTextIconGap, - defaultTextIconGap - ); - - // Paint the Check - if (checkIcon != null) { - SynthIcon.paintIcon(checkIcon, context, g, checkIconRect.x, - checkIconRect.y, checkIconRect.width, checkIconRect.height); - } - - // Paint the Icon - if(b.getIcon() != null) { - Icon icon; - if(!model.isEnabled()) { - icon = b.getDisabledIcon(); - } else if(model.isPressed() && model.isArmed()) { - icon = b.getPressedIcon(); - if(icon == null) { - // Use default icon - icon = b.getIcon(); - } - } else { - icon = b.getIcon(); - } - - if (icon!=null) { - SynthIcon.paintIcon(icon, context, g, iconRect.x, - iconRect.y, iconRect.width, iconRect.height); - } - } - - // Draw the Text - if(text != null) { - View v = (View) c.getClientProperty(BasicHTML.propertyKey); - if (v != null) { - v.paint(g, textRect); - } else { - g.setColor(style.getColor(context, ColorType.TEXT_FOREGROUND)); - g.setFont(style.getFont(context)); - style.getGraphicsUtils(context).paintText(context, g, text, - textRect.x, textRect.y, b.getDisplayedMnemonicIndex()); - } - } - - // Draw the Accelerator Text - if(acceleratorText != null && !acceleratorText.equals("")) { - // Get the maxAccWidth from the parent to calculate the offset. - int accOffset = 0; - Container parent = b.getParent(); - if (parent != null && parent instanceof JPopupMenu) { - SynthPopupMenuUI popupUI = (SynthPopupMenuUI) - ((JPopupMenu)parent).getUI(); - - // Note that we can only get here for SynthMenuItemUI - // (not SynthMenuUI) since acceleratorText is defined, - // so this cast should be safe - SynthMenuItemUI miUI = (SynthMenuItemUI) - SynthLookAndFeel.getUIOfType(b.getUI(), - SynthMenuItemUI.class); - - if (popupUI != null && miUI != null) { - String prop = - miUI.getPropertyPrefix() + ".alignAcceleratorText"; - boolean align = style.getBoolean(context, prop, true); - - // Calculate the offset, with which the accelerator texts - // will be drawn. - if (align) { - // When align==true and we're in the LTR case, - // we add an offset here so that all accelerators - // will be left-justified in their own column. - int max = popupUI.getMaxAcceleratorWidth(); - if (max > 0) { - accOffset = max - acceleratorRect.width; - if (!SynthLookAndFeel.isLeftToRight(c)) { - // In the RTL, flip the sign so that all - // accelerators will be right-justified. - accOffset = -accOffset; - } - } - } //else { - // Don't need to do anything special here; in the - // LTR case, the accelerator is already justified - // against the right edge of the menu (and against - // the left edge in the RTL case). - //} - } - } - - SynthStyle accStyle = accContext.getStyle(); - - g.setColor(accStyle.getColor(accContext, - ColorType.TEXT_FOREGROUND)); - g.setFont(accStyle.getFont(accContext)); - accStyle.getGraphicsUtils(accContext).paintText( - accContext, g, acceleratorText, acceleratorRect.x - - accOffset, acceleratorRect.y, -1); - } - - // Paint the Arrow - if (arrowIcon != null) { - SynthIcon.paintIcon(arrowIcon, context, g, arrowIconRect.x, - arrowIconRect.y, arrowIconRect.width, arrowIconRect.height); - } - } - - /** - * Compute and return the location of the icons origin, the - * location of origin of the text baseline, and a possibly clipped - * version of the compound labels string. Locations are computed - * relative to the viewRect rectangle. - */ - - private static String layoutMenuItem( - SynthContext context, - FontMetrics fm, - SynthContext accContext, - String text, - FontMetrics fmAccel, - String acceleratorText, - Icon icon, - Icon checkIcon, - Icon arrowIcon, - int verticalAlignment, - int horizontalAlignment, - int verticalTextPosition, - int horizontalTextPosition, - Rectangle viewRect, - Rectangle iconRect, - Rectangle textRect, - Rectangle acceleratorRect, - Rectangle checkIconRect, - Rectangle arrowIconRect, - int textIconGap, - int menuItemGap - ) - { - // If parent is JPopupMenu, get and store it's UI - SynthPopupMenuUI popupUI = null; - JComponent b = context.getComponent(); - Container parent = b.getParent(); - if(parent instanceof JPopupMenu) { - popupUI = (SynthPopupMenuUI)SynthLookAndFeel. - getUIOfType(((JPopupMenu)parent).getUI(), - SynthPopupMenuUI.class); - } - - context.getStyle().getGraphicsUtils(context).layoutText( - context, fm, text, icon,horizontalAlignment, verticalAlignment, - horizontalTextPosition, verticalTextPosition, viewRect, - iconRect, textRect, textIconGap); - - /* Initialize the acceleratorText bounds rectangle textRect. If a null - * or and empty String was specified we substitute "" here - * and use 0,0,0,0 for acceleratorTextRect. - */ - if( (acceleratorText == null) || acceleratorText.equals("") ) { - acceleratorRect.width = acceleratorRect.height = 0; - acceleratorText = ""; - } - else { - SynthStyle style = accContext.getStyle(); - acceleratorRect.width = style.getGraphicsUtils(accContext). - computeStringWidth(accContext, fmAccel.getFont(), fmAccel, - acceleratorText); - acceleratorRect.height = fmAccel.getHeight(); - } - - // Initialize the checkIcon bounds rectangle width & height. - if (checkIcon != null) { - checkIconRect.width = SynthIcon.getIconWidth(checkIcon, - context); - checkIconRect.height = SynthIcon.getIconHeight(checkIcon, - context); - } - else { - checkIconRect.width = checkIconRect.height = 0; - } - - // Initialize the arrowIcon bounds rectangle width & height. - if (arrowIcon != null) { - arrowIconRect.width = SynthIcon.getIconWidth(arrowIcon, - context); - arrowIconRect.height = SynthIcon.getIconHeight(arrowIcon, - context); - } else { - arrowIconRect.width = arrowIconRect.height = 0; - } - - // Note: layoutText() has already left room for - // the user icon, so no need to adjust textRect below - // to account for the user icon. However, we do have to - // reposition textRect when the check icon is visible. - - Rectangle labelRect = iconRect.union(textRect); - if( SynthLookAndFeel.isLeftToRight(context.getComponent()) ) { - // Position the check and user icons - iconRect.x = viewRect.x; - if (checkIcon != null) { - checkIconRect.x = viewRect.x; - iconRect.x += menuItemGap + checkIconRect.width; - textRect.x += menuItemGap + checkIconRect.width; - } - - // Position the arrow icon - arrowIconRect.x = - viewRect.x + viewRect.width - arrowIconRect.width; - - // Position the accelerator text rect - acceleratorRect.x = - viewRect.x + viewRect.width - acceleratorRect.width; - - /* Align icons and text horizontally */ - if(popupUI != null) { - int thisTextOffset = popupUI.adjustTextOffset(textRect.x - - viewRect.x); - textRect.x = thisTextOffset + viewRect.x; - - if(icon != null) { - // REMIND: The following code currently assumes the - // default (TRAILING) horizontalTextPosition, which means - // it will always place the icon to the left of the text. - // Other values of horizontalTextPosition aren't very - // useful for menu items, so we ignore them for now, but - // someday we might want to fix this situation. - int thisIconOffset = - popupUI.adjustIconOffset(iconRect.x - viewRect.x); - iconRect.x = thisIconOffset + viewRect.x; - } - } - } else { - // Position the accelerator text rect - acceleratorRect.x = viewRect.x; - - // Position the arrow icon - arrowIconRect.x = viewRect.x; - - // Position the check and user icons - iconRect.x = - viewRect.x + viewRect.width - iconRect.width; - if (checkIcon != null) { - checkIconRect.x = - viewRect.x + viewRect.width - checkIconRect.width; - textRect.x -= menuItemGap + checkIconRect.width; - iconRect.x -= menuItemGap + checkIconRect.width; - } - - /* Align icons and text horizontally */ - if(popupUI != null) { - int thisTextOffset = viewRect.x + viewRect.width - - textRect.x - textRect.width; - thisTextOffset = popupUI.adjustTextOffset(thisTextOffset); - textRect.x = viewRect.x + viewRect.width - - thisTextOffset - textRect.width; - if(icon != null) { - // REMIND: The following code currently assumes the - // default (TRAILING) horizontalTextPosition, which means - // it will always place the icon to the right of the text. - // Other values of horizontalTextPosition aren't very - // useful for menu items, so we ignore them for now, but - // someday we might want to fix this situation. - int thisIconOffset = viewRect.x + viewRect.width - - iconRect.x - iconRect.width; - thisIconOffset = - popupUI.adjustIconOffset(thisIconOffset); - iconRect.x = viewRect.x + viewRect.width - - thisIconOffset - iconRect.width; - } - } - } - - // Align the accelerator text and all icons vertically - // with the center of the label rect. - int midY = labelRect.y + (labelRect.height/2); - iconRect.y = midY - (iconRect.height/2); - acceleratorRect.y = midY - (acceleratorRect.height/2); - arrowIconRect.y = midY - (arrowIconRect.height/2); - checkIconRect.y = midY - (checkIconRect.height/2); - - return text; - } - - // these rects are used for painting and preferredsize calculations. - // they used to be regenerated constantly. Now they are reused. - static Rectangle iconRect = new Rectangle(); - static Rectangle textRect = new Rectangle(); - static Rectangle acceleratorRect = new Rectangle(); - static Rectangle checkIconRect = new Rectangle(); - static Rectangle arrowIconRect = new Rectangle(); - static Rectangle viewRect = new Rectangle(Short.MAX_VALUE,Short.MAX_VALUE); - static Rectangle r = new Rectangle(); - - private static void resetRects() { - iconRect.setBounds(0, 0, 0, 0); - textRect.setBounds(0, 0, 0, 0); - acceleratorRect.setBounds(0, 0, 0, 0); - checkIconRect.setBounds(0, 0, 0, 0); - arrowIconRect.setBounds(0, 0, 0, 0); - viewRect.setBounds(0,0,Short.MAX_VALUE, Short.MAX_VALUE); - r.setBounds(0, 0, 0, 0); - } - - protected void installDefaults() { updateStyle(menuItem); } @@ -718,9 +192,11 @@ class SynthMenuItemUI extends BasicMenuItemUI implements int defaultTextIconGap) { SynthContext context = getContext(c); SynthContext accContext = getContext(c, Region.MENU_ITEM_ACCELERATOR); - Dimension value = getPreferredMenuItemSize(context, accContext, - c, checkIcon, arrowIcon, defaultTextIconGap, - acceleratorDelimiter); + Dimension value = SynthGraphicsUtils.getPreferredMenuItemSize( + context, accContext, c, checkIcon, arrowIcon, + defaultTextIconGap, acceleratorDelimiter, + MenuItemLayoutHelper.useCheckAndArrow(menuItem), + getPropertyPrefix()); context.dispose(); accContext.dispose(); return value; @@ -751,14 +227,13 @@ class SynthMenuItemUI extends BasicMenuItemUI implements String prefix = getPropertyPrefix(); Icon checkIcon = style.getIcon(context, prefix + ".checkIcon"); Icon arrowIcon = style.getIcon(context, prefix + ".arrowIcon"); - paint(context, accContext, g, checkIcon, arrowIcon, - acceleratorDelimiter, defaultTextIconGap); + SynthGraphicsUtils.paint(context, accContext, g, checkIcon, arrowIcon, + acceleratorDelimiter, defaultTextIconGap, getPropertyPrefix()); accContext.dispose(); } void paintBackground(SynthContext context, Graphics g, JComponent c) { - context.getPainter().paintMenuItemBackground(context, g, 0, 0, - c.getWidth(), c.getHeight()); + SynthGraphicsUtils.paintBackground(context, g, c); } public void paintBorder(SynthContext context, Graphics g, int x, diff --git a/src/share/classes/javax/swing/plaf/synth/SynthMenuUI.java b/src/share/classes/javax/swing/plaf/synth/SynthMenuUI.java index c59acb09b..78835abf1 100644 --- a/src/share/classes/javax/swing/plaf/synth/SynthMenuUI.java +++ b/src/share/classes/javax/swing/plaf/synth/SynthMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,7 @@ import javax.swing.border.*; import java.util.Arrays; import java.util.ArrayList; import sun.swing.plaf.synth.SynthUI; - +import sun.swing.MenuItemLayoutHelper; /** * Synth's MenuUI. @@ -86,7 +86,7 @@ class SynthMenuUI extends BasicMenuUI implements PropertyChangeListener, acceleratorDelimiter = style.getString(context, prefix + ".acceleratorDelimiter", "+"); - if (useCheckAndArrow()) { + if (MenuItemLayoutHelper.useCheckAndArrow(menuItem)) { checkIcon = style.getIcon(context, prefix + ".checkIcon"); arrowIcon = style.getIcon(context, prefix + ".arrowIcon"); } else { @@ -111,6 +111,16 @@ class SynthMenuUI extends BasicMenuUI implements PropertyChangeListener, accContext.dispose(); } + public void uninstallUI(JComponent c) { + super.uninstallUI(c); + // Remove values from the parent's Client Properties. + JComponent p = MenuItemLayoutHelper.getMenuItemParent((JMenuItem) c); + if (p != null) { + p.putClientProperty( + SynthMenuItemLayoutHelper.MAX_ACC_OR_ARROW_WIDTH, null); + } + } + protected void uninstallDefaults() { SynthContext context = getContext(menuItem, ENABLED); style.uninstallDefaults(context); @@ -182,9 +192,11 @@ class SynthMenuUI extends BasicMenuUI implements PropertyChangeListener, int defaultTextIconGap) { SynthContext context = getContext(c); SynthContext accContext = getContext(c, Region.MENU_ITEM_ACCELERATOR); - Dimension value = SynthMenuItemUI.getPreferredMenuItemSize( - context, accContext, c, checkIcon, arrowIcon, - defaultTextIconGap, acceleratorDelimiter); + Dimension value = SynthGraphicsUtils.getPreferredMenuItemSize( + context, accContext, c, checkIcon, arrowIcon, + defaultTextIconGap, acceleratorDelimiter, + MenuItemLayoutHelper.useCheckAndArrow(menuItem), + getPropertyPrefix()); context.dispose(); accContext.dispose(); return value; @@ -211,21 +223,12 @@ class SynthMenuUI extends BasicMenuUI implements PropertyChangeListener, protected void paint(SynthContext context, Graphics g) { SynthContext accContext = getContext(menuItem, Region.MENU_ITEM_ACCELERATOR); - SynthStyle style = context.getStyle(); - Icon checkIcon; - Icon arrowIcon; - if (useCheckAndArrow()) { - // Refetch the appropriate icons for the current state - String prefix = getPropertyPrefix(); - checkIcon = style.getIcon(context, prefix + ".checkIcon"); - arrowIcon = style.getIcon(context, prefix + ".arrowIcon"); - } else { - // Not needed in this case - checkIcon = null; - arrowIcon = null; - } - SynthMenuItemUI.paint(context, accContext, g, checkIcon, arrowIcon, - acceleratorDelimiter, defaultTextIconGap); + // Refetch the appropriate check indicator for the current state + String prefix = getPropertyPrefix(); + Icon checkIcon = style.getIcon(context, prefix + ".checkIcon"); + Icon arrowIcon = style.getIcon(context, prefix + ".arrowIcon"); + SynthGraphicsUtils.paint(context, accContext, g, checkIcon, arrowIcon, + acceleratorDelimiter, defaultTextIconGap, getPropertyPrefix()); accContext.dispose(); } @@ -239,8 +242,4 @@ class SynthMenuUI extends BasicMenuUI implements PropertyChangeListener, updateStyle((JMenu)e.getSource()); } } - - private boolean useCheckAndArrow() { - return !((JMenu)menuItem).isTopLevelMenu(); - } } diff --git a/src/share/classes/javax/swing/plaf/synth/SynthPopupMenuUI.java b/src/share/classes/javax/swing/plaf/synth/SynthPopupMenuUI.java index e8fb73bc1..50d1c2781 100644 --- a/src/share/classes/javax/swing/plaf/synth/SynthPopupMenuUI.java +++ b/src/share/classes/javax/swing/plaf/synth/SynthPopupMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,34 +58,6 @@ import sun.swing.plaf.synth.SynthUI; */ class SynthPopupMenuUI extends BasicPopupMenuUI implements PropertyChangeListener, SynthUI { - /** - * Maximum size of the text portion of the children menu items. - */ - private int maxTextWidth; - - /** - * Maximum size of the icon portion of the children menu items. - */ - private int maxIconWidth; - - /** - * Maximum size of the spacing between the text and accelerator - * portions of the children menu items. - */ - private int maxAccelSpacingWidth; - - /** - * Maximum size of the text for the accelerator portion of the children - * menu items. - */ - private int maxAcceleratorWidth; - - /* - * Maximum icon and text offsets of the children menu items. - */ - private int maxTextOffset; - private int maxIconOffset; - private SynthStyle style; public static ComponentUI createUI(JComponent x) { @@ -153,90 +125,6 @@ class SynthPopupMenuUI extends BasicPopupMenuUI implements return SynthLookAndFeel.getComponentState(c); } - /** - * Resets the max text and accerator widths, - * text and icon offsets. - */ - void resetAlignmentHints() { - maxTextWidth = maxIconWidth - = maxAccelSpacingWidth = maxAcceleratorWidth - = maxTextOffset = maxIconOffset = 0; - } - - /** - * Adjusts the width needed to display the maximum menu item string. - * - * @param width Text width. - * @return max width - */ - int adjustTextWidth(int width) { - maxTextWidth = Math.max(maxTextWidth, width); - return maxTextWidth; - } - - /** - * Adjusts the width needed to display the maximum menu item icon. - * - * @param width Icon width. - * @return max width - */ - int adjustIconWidth(int width) { - maxIconWidth = Math.max(maxIconWidth, width); - return maxIconWidth; - } - - /** - * Adjusts the width needed to pad between the maximum menu item - * text and accelerator. - * - * @param width Spacing width. - * @return max width - */ - int adjustAccelSpacingWidth(int width) { - maxAccelSpacingWidth = Math.max(maxAccelSpacingWidth, width); - return maxAccelSpacingWidth; - } - - /** - * Adjusts the width needed to display the maximum accelerator. - * - * @param width Text width. - * @return max width - */ - int adjustAcceleratorWidth(int width) { - maxAcceleratorWidth = Math.max(maxAcceleratorWidth, width); - return maxAcceleratorWidth; - } - - /** - * Maximum size needed to display accelerators of children menu items. - */ - int getMaxAcceleratorWidth() { - return maxAcceleratorWidth; - } - - /** - * Adjusts the text offset needed to align text horizontally. - * - * @param offset Text offset - * @return max offset - */ - int adjustTextOffset(int offset) { - maxTextOffset = Math.max(maxTextOffset, offset); - return maxTextOffset; - } - - /** - * Adjusts the icon offset needed to align icons horizontally - * - * @param offset Icon offset - * @return max offset - */ - int adjustIconOffset(int offset) { - maxIconOffset = Math.max(maxIconOffset, offset); - return maxIconOffset; - } - public void update(Graphics g, JComponent c) { SynthContext context = getContext(c); diff --git a/src/share/classes/sun/swing/MenuItemLayoutHelper.java b/src/share/classes/sun/swing/MenuItemLayoutHelper.java new file mode 100644 index 000000000..5c4810b8f --- /dev/null +++ b/src/share/classes/sun/swing/MenuItemLayoutHelper.java @@ -0,0 +1,1339 @@ +/* + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.swing; + +import static sun.swing.SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET; + +import javax.swing.*; +import javax.swing.plaf.basic.BasicHTML; +import javax.swing.text.View; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.util.Map; +import java.util.HashMap; + +/** + * Calculates preferred size and layouts menu items. + */ +public class MenuItemLayoutHelper { + + /* Client Property keys for calculation of maximal widths */ + public static final StringUIClientPropertyKey MAX_ARROW_WIDTH = + new StringUIClientPropertyKey("maxArrowWidth"); + public static final StringUIClientPropertyKey MAX_CHECK_WIDTH = + new StringUIClientPropertyKey("maxCheckWidth"); + public static final StringUIClientPropertyKey MAX_ICON_WIDTH = + new StringUIClientPropertyKey("maxIconWidth"); + public static final StringUIClientPropertyKey MAX_TEXT_WIDTH = + new StringUIClientPropertyKey("maxTextWidth"); + public static final StringUIClientPropertyKey MAX_ACC_WIDTH = + new StringUIClientPropertyKey("maxAccWidth"); + public static final StringUIClientPropertyKey MAX_LABEL_WIDTH = + new StringUIClientPropertyKey("maxLabelWidth"); + + private JMenuItem mi; + private JComponent miParent; + + private Font font; + private Font accFont; + private FontMetrics fm; + private FontMetrics accFm; + + private Icon icon; + private Icon checkIcon; + private Icon arrowIcon; + private String text; + private String accText; + + private boolean isColumnLayout; + private boolean useCheckAndArrow; + private boolean isLeftToRight; + private boolean isTopLevelMenu; + private View htmlView; + + private int verticalAlignment; + private int horizontalAlignment; + private int verticalTextPosition; + private int horizontalTextPosition; + private int gap; + private int leadingGap; + private int afterCheckIconGap; + private int minTextOffset; + + private Rectangle viewRect; + + private RectSize iconSize; + private RectSize textSize; + private RectSize accSize; + private RectSize checkSize; + private RectSize arrowSize; + private RectSize labelSize; + + /** + * The empty protected constructor is necessary for derived classes. + */ + protected MenuItemLayoutHelper() { + } + + public MenuItemLayoutHelper(JMenuItem mi, Icon checkIcon, Icon arrowIcon, + Rectangle viewRect, int gap, String accDelimiter, + boolean isLeftToRight, Font font, Font accFont, + boolean useCheckAndArrow, String propertyPrefix) { + reset(mi, checkIcon, arrowIcon, viewRect, gap, accDelimiter, + isLeftToRight, font, accFont, useCheckAndArrow, propertyPrefix); + } + + protected void reset(JMenuItem mi, Icon checkIcon, Icon arrowIcon, + Rectangle viewRect, int gap, String accDelimiter, + boolean isLeftToRight, Font font, Font accFont, + boolean useCheckAndArrow, String propertyPrefix) { + this.mi = mi; + this.miParent = getMenuItemParent(mi); + this.accText = getAccText(accDelimiter); + this.verticalAlignment = mi.getVerticalAlignment(); + this.horizontalAlignment = mi.getHorizontalAlignment(); + this.verticalTextPosition = mi.getVerticalTextPosition(); + this.horizontalTextPosition = mi.getHorizontalTextPosition(); + this.useCheckAndArrow = useCheckAndArrow; + this.font = font; + this.accFont = accFont; + this.fm = mi.getFontMetrics(font); + this.accFm = mi.getFontMetrics(accFont); + this.isLeftToRight = isLeftToRight; + this.isColumnLayout = isColumnLayout(isLeftToRight, + horizontalAlignment, horizontalTextPosition, + verticalTextPosition); + this.isTopLevelMenu = (this.miParent == null) ? true : false; + this.checkIcon = checkIcon; + this.icon = getIcon(propertyPrefix); + this.arrowIcon = arrowIcon; + this.text = mi.getText(); + this.gap = gap; + this.afterCheckIconGap = getAfterCheckIconGap(propertyPrefix); + this.minTextOffset = getMinTextOffset(propertyPrefix); + this.htmlView = (View) mi.getClientProperty(BasicHTML.propertyKey); + this.viewRect = viewRect; + + this.iconSize = new RectSize(); + this.textSize = new RectSize(); + this.accSize = new RectSize(); + this.checkSize = new RectSize(); + this.arrowSize = new RectSize(); + this.labelSize = new RectSize(); + calcWidthsAndHeights(); + setOriginalWidths(); + calcMaxWidths(); + + this.leadingGap = getLeadingGap(propertyPrefix); + calcMaxTextOffset(viewRect); + } + + private void setOriginalWidths() { + iconSize.origWidth = iconSize.width; + textSize.origWidth = textSize.width; + accSize.origWidth = accSize.width; + checkSize.origWidth = checkSize.width; + arrowSize.origWidth = arrowSize.width; + } + + private String getAccText(String acceleratorDelimiter) { + String accText = ""; + KeyStroke accelerator = mi.getAccelerator(); + if (accelerator != null) { + int modifiers = accelerator.getModifiers(); + if (modifiers > 0) { + accText = KeyEvent.getKeyModifiersText(modifiers); + accText += acceleratorDelimiter; + } + int keyCode = accelerator.getKeyCode(); + if (keyCode != 0) { + accText += KeyEvent.getKeyText(keyCode); + } else { + accText += accelerator.getKeyChar(); + } + } + return accText; + } + + private Icon getIcon(String propertyPrefix) { + // In case of column layout, .checkIconFactory is defined for this UI, + // the icon is compatible with it and useCheckAndArrow() is true, + // then the icon is handled by the checkIcon. + Icon icon = null; + MenuItemCheckIconFactory iconFactory = + (MenuItemCheckIconFactory) UIManager.get(propertyPrefix + + ".checkIconFactory"); + if (!isColumnLayout || !useCheckAndArrow || iconFactory == null + || !iconFactory.isCompatible(checkIcon, propertyPrefix)) { + icon = mi.getIcon(); + } + return icon; + } + + private int getMinTextOffset(String propertyPrefix) { + int minimumTextOffset = 0; + Object minimumTextOffsetObject = + UIManager.get(propertyPrefix + ".minimumTextOffset"); + if (minimumTextOffsetObject instanceof Integer) { + minimumTextOffset = (Integer) minimumTextOffsetObject; + } + return minimumTextOffset; + } + + private int getAfterCheckIconGap(String propertyPrefix) { + int afterCheckIconGap = gap; + Object afterCheckIconGapObject = + UIManager.get(propertyPrefix + ".afterCheckIconGap"); + if (afterCheckIconGapObject instanceof Integer) { + afterCheckIconGap = (Integer) afterCheckIconGapObject; + } + return afterCheckIconGap; + } + + private int getLeadingGap(String propertyPrefix) { + if (checkSize.getMaxWidth() > 0) { + return getCheckOffset(propertyPrefix); + } else { + return gap; // There is no any check icon + } + } + + private int getCheckOffset(String propertyPrefix) { + int checkIconOffset = gap; + Object checkIconOffsetObject = + UIManager.get(propertyPrefix + ".checkIconOffset"); + if (checkIconOffsetObject instanceof Integer) { + checkIconOffset = (Integer) checkIconOffsetObject; + } + return checkIconOffset; + } + + protected void calcWidthsAndHeights() { + // iconRect + if (icon != null) { + iconSize.width = icon.getIconWidth(); + iconSize.height = icon.getIconHeight(); + } + + // accRect + if (!accText.equals("")) { + accSize.width = SwingUtilities2.stringWidth(mi, accFm, accText); + accSize.height = accFm.getHeight(); + } + + // textRect + if (text == null) { + text = ""; + } else if (!text.equals("")) { + if (htmlView != null) { + // Text is HTML + textSize.width = + (int) htmlView.getPreferredSpan(View.X_AXIS); + textSize.height = + (int) htmlView.getPreferredSpan(View.Y_AXIS); + } else { + // Text isn't HTML + textSize.width = SwingUtilities2.stringWidth(mi, fm, text); + textSize.height = fm.getHeight(); + } + } + + if (useCheckAndArrow) { + // checkIcon + if (checkIcon != null) { + checkSize.width = checkIcon.getIconWidth(); + checkSize.height = checkIcon.getIconHeight(); + } + // arrowRect + if (arrowIcon != null) { + arrowSize.width = arrowIcon.getIconWidth(); + arrowSize.height = arrowIcon.getIconHeight(); + } + } + + // labelRect + if (isColumnLayout) { + labelSize.width = iconSize.width + textSize.width + gap; + labelSize.height = max(checkSize.height, iconSize.height, + textSize.height, accSize.height, arrowSize.height); + } else { + Rectangle textRect = new Rectangle(); + Rectangle iconRect = new Rectangle(); + SwingUtilities.layoutCompoundLabel(mi, fm, text, icon, + verticalAlignment, horizontalAlignment, + verticalTextPosition, horizontalTextPosition, + viewRect, iconRect, textRect, gap); + Rectangle labelRect = iconRect.union(textRect); + labelSize.height = labelRect.height; + labelSize.width = labelRect.width; + } + } + + protected void calcMaxWidths() { + calcMaxWidth(checkSize, MAX_CHECK_WIDTH); + calcMaxWidth(arrowSize, MAX_ARROW_WIDTH); + calcMaxWidth(accSize, MAX_ACC_WIDTH); + + if (isColumnLayout) { + calcMaxWidth(iconSize, MAX_ICON_WIDTH); + calcMaxWidth(textSize, MAX_TEXT_WIDTH); + int curGap = gap; + if ((iconSize.getMaxWidth() == 0) + || (textSize.getMaxWidth() == 0)) { + curGap = 0; + } + labelSize.maxWidth = + calcMaxValue(MAX_LABEL_WIDTH, iconSize.maxWidth + + textSize.maxWidth + curGap); + } else { + // We shouldn't use current icon and text widths + // in maximal widths calculation for complex layout. + iconSize.maxWidth = getParentIntProperty(MAX_ICON_WIDTH); + calcMaxWidth(labelSize, MAX_LABEL_WIDTH); + // If maxLabelWidth is wider + // than the widest icon + the widest text + gap, + // we should update the maximal text witdh + int candidateTextWidth = labelSize.maxWidth - iconSize.maxWidth; + if (iconSize.maxWidth > 0) { + candidateTextWidth -= gap; + } + textSize.maxWidth = calcMaxValue(MAX_TEXT_WIDTH, candidateTextWidth); + } + } + + protected void calcMaxWidth(RectSize rs, Object key) { + rs.maxWidth = calcMaxValue(key, rs.width); + } + + /** + * Calculates and returns maximal value through specified parent component + * client property. + * + * @param propertyName name of the property, which stores the maximal value. + * @param value a value which pretends to be maximal + * @return maximal value among the parent property and the value. + */ + protected int calcMaxValue(Object propertyName, int value) { + // Get maximal value from parent client property + int maxValue = getParentIntProperty(propertyName); + // Store new maximal width in parent client property + if (value > maxValue) { + if (miParent != null) { + miParent.putClientProperty(propertyName, value); + } + return value; + } else { + return maxValue; + } + } + + /** + * Returns parent client property as int. + * @param propertyName name of the parent property. + * @return value of the property as int. + */ + protected int getParentIntProperty(Object propertyName) { + Object value = null; + if (miParent != null) { + value = miParent.getClientProperty(propertyName); + } + if ((value == null) || !(value instanceof Integer)) { + value = 0; + } + return (Integer) value; + } + + public static boolean isColumnLayout(boolean isLeftToRight, + JMenuItem mi) { + assert(mi != null); + return isColumnLayout(isLeftToRight, mi.getHorizontalAlignment(), + mi.getHorizontalTextPosition(), mi.getVerticalTextPosition()); + } + + /** + * Answers should we do column layout for a menu item or not. + * We do it when a user doesn't set any alignments + * and text positions manually, except the vertical alignment. + */ + public static boolean isColumnLayout(boolean isLeftToRight, + int horizontalAlignment, + int horizontalTextPosition, + int verticalTextPosition) { + if (verticalTextPosition != SwingConstants.CENTER) { + return false; + } + if (isLeftToRight) { + if (horizontalAlignment != SwingConstants.LEADING + && horizontalAlignment != SwingConstants.LEFT) { + return false; + } + if (horizontalTextPosition != SwingConstants.TRAILING + && horizontalTextPosition != SwingConstants.RIGHT) { + return false; + } + } else { + if (horizontalAlignment != SwingConstants.LEADING + && horizontalAlignment != SwingConstants.RIGHT) { + return false; + } + if (horizontalTextPosition != SwingConstants.TRAILING + && horizontalTextPosition != SwingConstants.LEFT) { + return false; + } + } + return true; + } + + /** + * Calculates maximal text offset. + * It is required for some L&Fs (ex: Vista L&F). + * The offset is meaningful only for L2R column layout. + * + * @param viewRect the rectangle, the maximal text offset + * will be calculated for. + */ + private void calcMaxTextOffset(Rectangle viewRect) { + if (!isColumnLayout || !isLeftToRight) { + return; + } + + // Calculate the current text offset + int offset = viewRect.x + leadingGap + checkSize.maxWidth + + afterCheckIconGap + iconSize.maxWidth + gap; + if (checkSize.maxWidth == 0) { + offset -= afterCheckIconGap; + } + if (iconSize.maxWidth == 0) { + offset -= gap; + } + + // maximal text offset shouldn't be less than minimal text offset; + if (offset < minTextOffset) { + offset = minTextOffset; + } + + // Calculate and store the maximal text offset + calcMaxValue(SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET, offset); + } + + /** + * Layout icon, text, check icon, accelerator text and arrow icon + * in the viewRect and return their positions. + * + * If horizontalAlignment, verticalTextPosition and horizontalTextPosition + * are default (user doesn't set any manually) the layouting algorithm is: + * Elements are layouted in the five columns: + * check icon + icon + text + accelerator text + arrow icon + * + * In the other case elements are layouted in the four columns: + * check icon + label + accelerator text + arrow icon + * Label is union of icon and text. + * + * The order of columns can be reversed. + * It depends on the menu item orientation. + */ + public LayoutResult layoutMenuItem() { + LayoutResult lr = createLayoutResult(); + prepareForLayout(lr); + + if (isColumnLayout()) { + if (isLeftToRight()) { + doLTRColumnLayout(lr, getLTRColumnAlignment()); + } else { + doRTLColumnLayout(lr, getRTLColumnAlignment()); + } + } else { + if (isLeftToRight()) { + doLTRComplexLayout(lr, getLTRColumnAlignment()); + } else { + doRTLComplexLayout(lr, getRTLColumnAlignment()); + } + } + + alignAccCheckAndArrowVertically(lr); + return lr; + } + + private LayoutResult createLayoutResult() { + return new LayoutResult( + new Rectangle(iconSize.width, iconSize.height), + new Rectangle(textSize.width, textSize.height), + new Rectangle(accSize.width, accSize.height), + new Rectangle(checkSize.width, checkSize.height), + new Rectangle(arrowSize.width, arrowSize.height), + new Rectangle(labelSize.width, labelSize.height) + ); + } + + public ColumnAlignment getLTRColumnAlignment() { + return ColumnAlignment.LEFT_ALIGNMENT; + } + + public ColumnAlignment getRTLColumnAlignment() { + return ColumnAlignment.RIGHT_ALIGNMENT; + } + + protected void prepareForLayout(LayoutResult lr) { + lr.checkRect.width = checkSize.maxWidth; + lr.accRect.width = accSize.maxWidth; + lr.arrowRect.width = arrowSize.maxWidth; + } + + /** + * Aligns the accelertor text and the check and arrow icons vertically + * with the center of the label rect. + */ + private void alignAccCheckAndArrowVertically(LayoutResult lr) { + lr.accRect.y = (int)(lr.labelRect.y + + (float)lr.labelRect.height/2 + - (float)lr.accRect.height/2); + fixVerticalAlignment(lr, lr.accRect); + if (useCheckAndArrow) { + lr.arrowRect.y = (int)(lr.labelRect.y + + (float)lr.labelRect.height/2 + - (float)lr.arrowRect.height/2); + lr.checkRect.y = (int)(lr.labelRect.y + + (float)lr.labelRect.height/2 + - (float)lr.checkRect.height/2); + fixVerticalAlignment(lr, lr.arrowRect); + fixVerticalAlignment(lr, lr.checkRect); + } + } + + /** + * Fixes vertical alignment of all menu item elements if rect.y + * or (rect.y + rect.height) is out of viewRect bounds + */ + private void fixVerticalAlignment(LayoutResult lr, Rectangle r) { + int delta = 0; + if (r.y < viewRect.y) { + delta = viewRect.y - r.y; + } else if (r.y + r.height > viewRect.y + viewRect.height) { + delta = viewRect.y + viewRect.height - r.y - r.height; + } + if (delta != 0) { + lr.checkRect.y += delta; + lr.iconRect.y += delta; + lr.textRect.y += delta; + lr.accRect.y += delta; + lr.arrowRect.y += delta; + lr.labelRect.y += delta; + } + } + + private void doLTRColumnLayout(LayoutResult lr, ColumnAlignment alignment) { + // Set maximal width for all the five basic rects + // (three other ones are already maximal) + lr.iconRect.width = iconSize.maxWidth; + lr.textRect.width = textSize.maxWidth; + + // Set X coordinates + // All rects will be aligned at the left side + calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.checkRect, + lr.iconRect, lr.textRect); + + // Tune afterCheckIconGap + if (lr.checkRect.width > 0) { // there is the afterCheckIconGap + lr.iconRect.x += afterCheckIconGap - gap; + lr.textRect.x += afterCheckIconGap - gap; + } + + calcXPositionsRTL(viewRect.x + viewRect.width, leadingGap, gap, + lr.arrowRect, lr.accRect); + + // Take into account minimal text offset + int textOffset = lr.textRect.x - viewRect.x; + if (!isTopLevelMenu && (textOffset < minTextOffset)) { + lr.textRect.x += minTextOffset - textOffset; + } + + alignRects(lr, alignment); + + // Take into account the left side bearings for text and accelerator text. + fixTextRects(lr); + + // Set Y coordinate for text and icon. + // Y coordinates for other rects + // will be calculated later in layoutMenuItem. + calcTextAndIconYPositions(lr); + + // Calculate valid X and Y coordinates for labelRect + lr.setLabelRect(lr.textRect.union(lr.iconRect)); + } + + private void doLTRComplexLayout(LayoutResult lr, ColumnAlignment alignment) { + lr.labelRect.width = labelSize.maxWidth; + + // Set X coordinates + calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.checkRect, + lr.labelRect); + + // Tune afterCheckIconGap + if (lr.checkRect.width > 0) { // there is the afterCheckIconGap + lr.labelRect.x += afterCheckIconGap - gap; + } + + calcXPositionsRTL(viewRect.x + viewRect.width, + leadingGap, gap, lr.arrowRect, lr.accRect); + + // Take into account minimal text offset + int labelOffset = lr.labelRect.x - viewRect.x; + if (!isTopLevelMenu && (labelOffset < minTextOffset)) { + lr.labelRect.x += minTextOffset - labelOffset; + } + + alignRects(lr, alignment); + + // Take into account the left side bearing for accelerator text. + // The LSB for text is taken into account in layoutCompoundLabel() below. + fixAccTextRect(lr); + + // Center labelRect vertically + calcLabelYPosition(lr); + + layoutIconAndTextInLabelRect(lr); + } + + private void doRTLColumnLayout(LayoutResult lr, ColumnAlignment alignment) { + // Set maximal width for all the five basic rects + // (three other ones are already maximal) + lr.iconRect.width = iconSize.maxWidth; + lr.textRect.width = textSize.maxWidth; + + // Set X coordinates + calcXPositionsRTL(viewRect.x + viewRect.width, leadingGap, gap, + lr.checkRect, lr.iconRect, lr.textRect); + + // Tune the gap after check icon + if (lr.checkRect.width > 0) { // there is the gap after check icon + lr.iconRect.x -= afterCheckIconGap - gap; + lr.textRect.x -= afterCheckIconGap - gap; + } + + calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.arrowRect, + lr.accRect); + + // Take into account minimal text offset + int textOffset = (viewRect.x + viewRect.width) + - (lr.textRect.x + lr.textRect.width); + if (!isTopLevelMenu && (textOffset < minTextOffset)) { + lr.textRect.x -= minTextOffset - textOffset; + } + + alignRects(lr, alignment); + + // Take into account the left side bearings for text and accelerator text. + fixTextRects(lr); + + // Set Y coordinates for text and icon. + // Y coordinates for other rects + // will be calculated later in layoutMenuItem. + calcTextAndIconYPositions(lr); + + // Calculate valid X and Y coordinate for labelRect + lr.setLabelRect(lr.textRect.union(lr.iconRect)); + } + + private void doRTLComplexLayout(LayoutResult lr, ColumnAlignment alignment) { + lr.labelRect.width = labelSize.maxWidth; + + // Set X coordinates + calcXPositionsRTL(viewRect.x + viewRect.width, leadingGap, gap, + lr.checkRect, lr.labelRect); + + // Tune the gap after check icon + if (lr.checkRect.width > 0) { // there is the gap after check icon + lr.labelRect.x -= afterCheckIconGap - gap; + } + + calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.arrowRect, lr.accRect); + + // Take into account minimal text offset + int labelOffset = (viewRect.x + viewRect.width) + - (lr.labelRect.x + lr.labelRect.width); + if (!isTopLevelMenu && (labelOffset < minTextOffset)) { + lr.labelRect.x -= minTextOffset - labelOffset; + } + + alignRects(lr, alignment); + + // Take into account the left side bearing for accelerator text. + // The LSB for text is taken into account in layoutCompoundLabel() below. + fixAccTextRect(lr); + + // Center labelRect vertically + calcLabelYPosition(lr); + + layoutIconAndTextInLabelRect(lr); + } + + private void alignRects(LayoutResult lr, ColumnAlignment alignment) { + alignRect(lr.checkRect, alignment.getCheckAlignment(), + checkSize.getOrigWidth()); + alignRect(lr.iconRect, alignment.getIconAlignment(), + iconSize.getOrigWidth()); + alignRect(lr.textRect, alignment.getTextAlignment(), + textSize.getOrigWidth()); + alignRect(lr.accRect, alignment.getAccAlignment(), + accSize.getOrigWidth()); + alignRect(lr.arrowRect, alignment.getArrowAlignment(), + arrowSize.getOrigWidth()); + } + + private void alignRect(Rectangle rect, int alignment, int origWidth) { + if (alignment != SwingUtilities.LEFT) { + rect.x = rect.x + rect.width - origWidth; + rect.width = origWidth; + } + } + + protected void layoutIconAndTextInLabelRect(LayoutResult lr) { + lr.setTextRect(new Rectangle()); + lr.setIconRect(new Rectangle()); + SwingUtilities.layoutCompoundLabel( + mi, fm, text,icon, verticalAlignment, horizontalAlignment, + verticalTextPosition, horizontalTextPosition, lr.labelRect, + lr.iconRect, lr.textRect, gap); + } + + private void calcXPositionsLTR(int startXPos, int leadingGap, + int gap, Rectangle... rects) { + int curXPos = startXPos + leadingGap; + for (Rectangle rect : rects) { + rect.x = curXPos; + if (rect.width > 0) { + curXPos += rect.width + gap; + } + } + } + + private void calcXPositionsRTL(int startXPos, int leadingGap, + int gap, Rectangle... rects) { + int curXPos = startXPos - leadingGap; + for (Rectangle rect : rects) { + rect.x = curXPos - rect.width; + if (rect.width > 0) { + curXPos -= rect.width + gap; + } + } + } + + /** + * Takes into account the left side bearings for text and accelerator text + */ + private void fixTextRects(LayoutResult lr) { + if (htmlView == null) { // The text isn't a HTML + int lsb = SwingUtilities2.getLeftSideBearing(mi, fm, text); + if (lsb < 0) { + lr.textRect.x -= lsb; + } + } + fixAccTextRect(lr); + } + + /** + * Takes into account the left side bearing for accelerator text + */ + private void fixAccTextRect(LayoutResult lr) { + int lsb = SwingUtilities2.getLeftSideBearing(mi, accFm, accText); + if (lsb < 0) { + lr.accRect.x -= lsb; + } + } + + /** + * Sets Y coordinates of text and icon + * taking into account the vertical alignment + */ + private void calcTextAndIconYPositions(LayoutResult lr) { + if (verticalAlignment == SwingUtilities.TOP) { + lr.textRect.y = (int)(viewRect.y + + (float)lr.labelRect.height/2 + - (float)lr.textRect.height/2); + lr.iconRect.y = (int)(viewRect.y + + (float)lr.labelRect.height/2 + - (float)lr.iconRect.height/2); + } else if (verticalAlignment == SwingUtilities.CENTER) { + lr.textRect.y = (int)(viewRect.y + + (float)viewRect.height/2 + - (float)lr.textRect.height/2); + lr.iconRect.y = (int)(viewRect.y + + (float)viewRect.height/2 + - (float)lr.iconRect.height/2); + } + else if (verticalAlignment == SwingUtilities.BOTTOM) { + lr.textRect.y = (int)(viewRect.y + + viewRect.height + - (float)lr.labelRect.height/2 + - (float)lr.textRect.height/2); + lr.iconRect.y = (int)(viewRect.y + + viewRect.height + - (float)lr.labelRect.height/2 + - (float)lr.iconRect.height/2); + } + } + + /** + * Sets labelRect Y coordinate + * taking into account the vertical alignment + */ + private void calcLabelYPosition(LayoutResult lr) { + if (verticalAlignment == SwingUtilities.TOP) { + lr.labelRect.y = viewRect.y; + } else if (verticalAlignment == SwingUtilities.CENTER) { + lr.labelRect.y = (int)(viewRect.y + + (float)viewRect.height/2 + - (float)lr.labelRect.height/2); + } else if (verticalAlignment == SwingUtilities.BOTTOM) { + lr.labelRect.y = viewRect.y + viewRect.height + - lr.labelRect.height; + } + } + + /** + * Returns parent of this component if it is not a top-level menu + * Otherwise returns null. + * @param menuItem the menu item whose parent will be returned. + * @return parent of this component if it is not a top-level menu + * Otherwise returns null. + */ + public static JComponent getMenuItemParent(JMenuItem menuItem) { + Container parent = menuItem.getParent(); + if ((parent instanceof JComponent) && + (!(menuItem instanceof JMenu) || + !((JMenu)menuItem).isTopLevelMenu())) { + return (JComponent) parent; + } else { + return null; + } + } + + public static void clearUsedParentClientProperties(JMenuItem menuItem) { + clearUsedClientProperties(getMenuItemParent(menuItem)); + } + + public static void clearUsedClientProperties(JComponent c) { + if (c != null) { + c.putClientProperty(MAX_ARROW_WIDTH, null); + c.putClientProperty(MAX_CHECK_WIDTH, null); + c.putClientProperty(MAX_ACC_WIDTH, null); + c.putClientProperty(MAX_TEXT_WIDTH, null); + c.putClientProperty(MAX_ICON_WIDTH, null); + c.putClientProperty(MAX_LABEL_WIDTH, null); + c.putClientProperty(BASICMENUITEMUI_MAX_TEXT_OFFSET, null); + } + } + + /** + * Finds and returns maximal integer value in the given array. + * @param values array where the search will be performed. + * @return maximal vaule. + */ + public static int max(int... values) { + int maxValue = Integer.MIN_VALUE; + for (int i : values) { + if (i > maxValue) { + maxValue = i; + } + } + return maxValue; + } + + public static Rectangle createMaxRect() { + return new Rectangle(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + public static void addMaxWidth(RectSize size, int gap, Dimension result) { + if (size.maxWidth > 0) { + result.width += size.maxWidth + gap; + } + } + + public static void addWidth(int width, int gap, Dimension result) { + if (width > 0) { + result.width += width + gap; + } + } + + public JMenuItem getMenuItem() { + return mi; + } + + public JComponent getMenuItemParent() { + return miParent; + } + + public Font getFont() { + return font; + } + + public Font getAccFont() { + return accFont; + } + + public FontMetrics getFontMetrics() { + return fm; + } + + public FontMetrics getAccFontMetrics() { + return accFm; + } + + public Icon getIcon() { + return icon; + } + + public Icon getCheckIcon() { + return checkIcon; + } + + public Icon getArrowIcon() { + return arrowIcon; + } + + public String getText() { + return text; + } + + public String getAccText() { + return accText; + } + + public boolean isColumnLayout() { + return isColumnLayout; + } + + public boolean useCheckAndArrow() { + return useCheckAndArrow; + } + + public boolean isLeftToRight() { + return isLeftToRight; + } + + public boolean isTopLevelMenu() { + return isTopLevelMenu; + } + + public View getHtmlView() { + return htmlView; + } + + public int getVerticalAlignment() { + return verticalAlignment; + } + + public int getHorizontalAlignment() { + return horizontalAlignment; + } + + public int getVerticalTextPosition() { + return verticalTextPosition; + } + + public int getHorizontalTextPosition() { + return horizontalTextPosition; + } + + public int getGap() { + return gap; + } + + public int getLeadingGap() { + return leadingGap; + } + + public int getAfterCheckIconGap() { + return afterCheckIconGap; + } + + public int getMinTextOffset() { + return minTextOffset; + } + + public Rectangle getViewRect() { + return viewRect; + } + + public RectSize getIconSize() { + return iconSize; + } + + public RectSize getTextSize() { + return textSize; + } + + public RectSize getAccSize() { + return accSize; + } + + public RectSize getCheckSize() { + return checkSize; + } + + public RectSize getArrowSize() { + return arrowSize; + } + + public RectSize getLabelSize() { + return labelSize; + } + + protected void setMenuItem(JMenuItem mi) { + this.mi = mi; + } + + protected void setMenuItemParent(JComponent miParent) { + this.miParent = miParent; + } + + protected void setFont(Font font) { + this.font = font; + } + + protected void setAccFont(Font accFont) { + this.accFont = accFont; + } + + protected void setFontMetrics(FontMetrics fm) { + this.fm = fm; + } + + protected void setAccFontMetrics(FontMetrics accFm) { + this.accFm = accFm; + } + + protected void setIcon(Icon icon) { + this.icon = icon; + } + + protected void setCheckIcon(Icon checkIcon) { + this.checkIcon = checkIcon; + } + + protected void setArrowIcon(Icon arrowIcon) { + this.arrowIcon = arrowIcon; + } + + protected void setText(String text) { + this.text = text; + } + + protected void setAccText(String accText) { + this.accText = accText; + } + + protected void setColumnLayout(boolean columnLayout) { + isColumnLayout = columnLayout; + } + + protected void setUseCheckAndArrow(boolean useCheckAndArrow) { + this.useCheckAndArrow = useCheckAndArrow; + } + + protected void setLeftToRight(boolean leftToRight) { + isLeftToRight = leftToRight; + } + + protected void setTopLevelMenu(boolean topLevelMenu) { + isTopLevelMenu = topLevelMenu; + } + + protected void setHtmlView(View htmlView) { + this.htmlView = htmlView; + } + + protected void setVerticalAlignment(int verticalAlignment) { + this.verticalAlignment = verticalAlignment; + } + + protected void setHorizontalAlignment(int horizontalAlignment) { + this.horizontalAlignment = horizontalAlignment; + } + + protected void setVerticalTextPosition(int verticalTextPosition) { + this.verticalTextPosition = verticalTextPosition; + } + + protected void setHorizontalTextPosition(int horizontalTextPosition) { + this.horizontalTextPosition = horizontalTextPosition; + } + + protected void setGap(int gap) { + this.gap = gap; + } + + protected void setLeadingGap(int leadingGap) { + this.leadingGap = leadingGap; + } + + protected void setAfterCheckIconGap(int afterCheckIconGap) { + this.afterCheckIconGap = afterCheckIconGap; + } + + protected void setMinTextOffset(int minTextOffset) { + this.minTextOffset = minTextOffset; + } + + protected void setViewRect(Rectangle viewRect) { + this.viewRect = viewRect; + } + + protected void setIconSize(RectSize iconSize) { + this.iconSize = iconSize; + } + + protected void setTextSize(RectSize textSize) { + this.textSize = textSize; + } + + protected void setAccSize(RectSize accSize) { + this.accSize = accSize; + } + + protected void setCheckSize(RectSize checkSize) { + this.checkSize = checkSize; + } + + protected void setArrowSize(RectSize arrowSize) { + this.arrowSize = arrowSize; + } + + protected void setLabelSize(RectSize labelSize) { + this.labelSize = labelSize; + } + + /** + * Returns false if the component is a JMenu and it is a top + * level menu (on the menubar). + */ + public static boolean useCheckAndArrow(JMenuItem menuItem) { + boolean b = true; + if ((menuItem instanceof JMenu) && + (((JMenu) menuItem).isTopLevelMenu())) { + b = false; + } + return b; + } + + public static class LayoutResult { + private Rectangle iconRect; + private Rectangle textRect; + private Rectangle accRect; + private Rectangle checkRect; + private Rectangle arrowRect; + private Rectangle labelRect; + + public LayoutResult() { + iconRect = new Rectangle(); + textRect = new Rectangle(); + accRect = new Rectangle(); + checkRect = new Rectangle(); + arrowRect = new Rectangle(); + labelRect = new Rectangle(); + } + + public LayoutResult(Rectangle iconRect, Rectangle textRect, + Rectangle accRect, Rectangle checkRect, + Rectangle arrowRect, Rectangle labelRect) { + this.iconRect = iconRect; + this.textRect = textRect; + this.accRect = accRect; + this.checkRect = checkRect; + this.arrowRect = arrowRect; + this.labelRect = labelRect; + } + + public Rectangle getIconRect() { + return iconRect; + } + + public void setIconRect(Rectangle iconRect) { + this.iconRect = iconRect; + } + + public Rectangle getTextRect() { + return textRect; + } + + public void setTextRect(Rectangle textRect) { + this.textRect = textRect; + } + + public Rectangle getAccRect() { + return accRect; + } + + public void setAccRect(Rectangle accRect) { + this.accRect = accRect; + } + + public Rectangle getCheckRect() { + return checkRect; + } + + public void setCheckRect(Rectangle checkRect) { + this.checkRect = checkRect; + } + + public Rectangle getArrowRect() { + return arrowRect; + } + + public void setArrowRect(Rectangle arrowRect) { + this.arrowRect = arrowRect; + } + + public Rectangle getLabelRect() { + return labelRect; + } + + public void setLabelRect(Rectangle labelRect) { + this.labelRect = labelRect; + } + + public Map getAllRects() { + Map result = new HashMap(); + result.put("checkRect", checkRect); + result.put("iconRect", iconRect); + result.put("textRect", textRect); + result.put("accRect", accRect); + result.put("arrowRect", arrowRect); + result.put("labelRect", labelRect); + return result; + } + } + + public static class ColumnAlignment { + private int checkAlignment; + private int iconAlignment; + private int textAlignment; + private int accAlignment; + private int arrowAlignment; + + public static final ColumnAlignment LEFT_ALIGNMENT = + new ColumnAlignment( + SwingConstants.LEFT, + SwingConstants.LEFT, + SwingConstants.LEFT, + SwingConstants.LEFT, + SwingConstants.LEFT + ); + + public static final ColumnAlignment RIGHT_ALIGNMENT = + new ColumnAlignment( + SwingConstants.RIGHT, + SwingConstants.RIGHT, + SwingConstants.RIGHT, + SwingConstants.RIGHT, + SwingConstants.RIGHT + ); + + public ColumnAlignment(int checkAlignment, int iconAlignment, + int textAlignment, int accAlignment, + int arrowAlignment) { + this.checkAlignment = checkAlignment; + this.iconAlignment = iconAlignment; + this.textAlignment = textAlignment; + this.accAlignment = accAlignment; + this.arrowAlignment = arrowAlignment; + } + + public int getCheckAlignment() { + return checkAlignment; + } + + public int getIconAlignment() { + return iconAlignment; + } + + public int getTextAlignment() { + return textAlignment; + } + + public int getAccAlignment() { + return accAlignment; + } + + public int getArrowAlignment() { + return arrowAlignment; + } + } + + public static class RectSize { + private int width; + private int height; + private int origWidth; + private int maxWidth; + + public RectSize() { + } + + public RectSize(int width, int height, int origWidth, int maxWidth) { + this.width = width; + this.height = height; + this.origWidth = origWidth; + this.maxWidth = maxWidth; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public int getOrigWidth() { + return origWidth; + } + + public int getMaxWidth() { + return maxWidth; + } + + public void setWidth(int width) { + this.width = width; + } + + public void setHeight(int height) { + this.height = height; + } + + public void setOrigWidth(int origWidth) { + this.origWidth = origWidth; + } + + public void setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + } + + public String toString() { + return "[w=" + width + ",h=" + height + ",ow=" + + origWidth + ",mw=" + maxWidth + "]"; + } + } +} -- GitLab From b1d4d370cb954f7ca0012d298d4229fab7fd748a Mon Sep 17 00:00:00 2001 From: rupashka Date: Mon, 11 Aug 2008 16:39:17 +0400 Subject: [PATCH 047/139] 6604281: NimbusL&F :Regression in Focus traversal in JFileChooser in pit build. Summary: Fixed calculation of preferred size in SynthButtonUI Reviewed-by: loneid, peterz --- .../javax/swing/plaf/synth/SynthButtonUI.java | 6 +- .../swing/JButton/6604281/bug6604281.java | 76 +++++++++++++++++++ 2 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 test/javax/swing/JButton/6604281/bug6604281.java diff --git a/src/share/classes/javax/swing/plaf/synth/SynthButtonUI.java b/src/share/classes/javax/swing/plaf/synth/SynthButtonUI.java index 78dd60bd3..e0f057e90 100644 --- a/src/share/classes/javax/swing/plaf/synth/SynthButtonUI.java +++ b/src/share/classes/javax/swing/plaf/synth/SynthButtonUI.java @@ -262,7 +262,7 @@ class SynthButtonUI extends BasicButtonUI implements * Returns the default icon. This should NOT callback * to the JComponent. * - * @param b AbstractButton the iocn is associated with + * @param b AbstractButton the icon is associated with * @return default icon */ @@ -445,9 +445,7 @@ class SynthButtonUI extends BasicButtonUI implements * Returns the Icon used in calculating the pref/min/max size. */ protected Icon getSizingIcon(AbstractButton b) { - // NOTE: this is slightly different than BasicButtonUI, where it - // would just use getIcon, but this should be ok. - Icon icon = (b.isEnabled()) ? b.getIcon() : b.getDisabledIcon(); + Icon icon = getEnabledIcon(b, b.getIcon()); if (icon == null) { icon = getDefaultIcon(b); } diff --git a/test/javax/swing/JButton/6604281/bug6604281.java b/test/javax/swing/JButton/6604281/bug6604281.java new file mode 100644 index 000000000..dec89f778 --- /dev/null +++ b/test/javax/swing/JButton/6604281/bug6604281.java @@ -0,0 +1,76 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + @bug 6604281 + @summary NimbusL&F :Regression in Focus traversal in JFileChooser in pit build + @author Pavel Porvatov + @run main bug6604281 +*/ + +import javax.swing.*; +import javax.swing.plaf.IconUIResource; +import javax.swing.plaf.synth.SynthLookAndFeel; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.lang.reflect.InvocationTargetException; + +public class bug6604281 { + public static void main(String[] args) throws InvocationTargetException, InterruptedException { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + SynthLookAndFeel laf = new SynthLookAndFeel(); + try { + UIManager.setLookAndFeel(laf); + } catch (Exception e) { + fail(e.getMessage()); + } + + // Prepare image + BufferedImage image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_RGB); + + Graphics2D graphics = (Graphics2D) image.getGraphics(); + + graphics.setColor(Color.BLUE); + graphics.fillRect(0, 0, image.getWidth(), image.getHeight()); + graphics.setColor(Color.RED); + graphics.drawLine(0, 0, image.getWidth(), image.getHeight()); + + // Use IconUIResource as an icon, because with ImageIcon bug is not reproduced + JButton button1 = new JButton(new IconUIResource(new ImageIcon(image))); + + JButton button2 = new JButton(new IconUIResource(new ImageIcon(image))); + + button2.setEnabled(false); + + if (button1.getPreferredSize().getHeight() != button2.getPreferredSize().getHeight()) { + fail("Two similar buttons have different size"); + } + } + }); + } + + private static void fail(String s) { + throw new RuntimeException("Test failed: " + s); + } +} -- GitLab From 3e96b323595aa5f571d8a7e70e619e5438cddf5f Mon Sep 17 00:00:00 2001 From: mlapshin Date: Mon, 11 Aug 2008 16:49:46 +0400 Subject: [PATCH 048/139] 6579243: Windows, GTK: Internal frame title is drawn wrong if the frame has RTL orientation Summary: Added right-to-left code branches to WindowsInternalFrameTitlePane and Metacity classes Reviewed-by: alexp --- .../com/sun/java/swing/plaf/gtk/Metacity.java | 109 ++++++++++++------ .../WindowsInternalFrameTitlePane.java | 51 +++++--- .../synth/SynthInternalFrameTitlePane.java | 2 +- 3 files changed, 113 insertions(+), 49 deletions(-) diff --git a/src/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java b/src/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java index e61179891..95de6cad6 100644 --- a/src/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java +++ b/src/share/classes/com/sun/java/swing/plaf/gtk/Metacity.java @@ -770,33 +770,56 @@ class Metacity implements SynthConstants { JComponent maximizeButton = findChild(titlePane, "InternalFrameTitlePane.maximizeButton"); JComponent closeButton = findChild(titlePane, "InternalFrameTitlePane.closeButton"); - int buttonGap = 0; - Insets button_border = (Insets)gm.get("button_border"); Dimension buttonDim = calculateButtonSize(titlePane); - int x = getInt("left_titlebar_edge"); int y = (button_border != null) ? button_border.top : 0; + if (titlePaneParent.getComponentOrientation().isLeftToRight()) { + int x = getInt("left_titlebar_edge"); - menuButton.setBounds(x, y, buttonDim.width, buttonDim.height); + menuButton.setBounds(x, y, buttonDim.width, buttonDim.height); - x = w - buttonDim.width - getInt("right_titlebar_edge"); - if (button_border != null) { - x -= button_border.right; - } + x = w - buttonDim.width - getInt("right_titlebar_edge"); + if (button_border != null) { + x -= button_border.right; + } - if (frame.isClosable()) { - closeButton.setBounds(x, y, buttonDim.width, buttonDim.height); - x -= (buttonDim.width + buttonGap); - } + if (frame.isClosable()) { + closeButton.setBounds(x, y, buttonDim.width, buttonDim.height); + x -= buttonDim.width; + } - if (frame.isMaximizable()) { - maximizeButton.setBounds(x, y, buttonDim.width, buttonDim.height); - x -= (buttonDim.width + buttonGap); - } + if (frame.isMaximizable()) { + maximizeButton.setBounds(x, y, buttonDim.width, buttonDim.height); + x -= buttonDim.width; + } - if (frame.isIconifiable()) { - minimizeButton.setBounds(x, y, buttonDim.width, buttonDim.height); + if (frame.isIconifiable()) { + minimizeButton.setBounds(x, y, buttonDim.width, buttonDim.height); + } + } else { + int x = w - buttonDim.width - getInt("right_titlebar_edge"); + + menuButton.setBounds(x, y, buttonDim.width, buttonDim.height); + + x = getInt("left_titlebar_edge"); + if (button_border != null) { + x += button_border.left; + } + + if (frame.isClosable()) { + closeButton.setBounds(x, y, buttonDim.width, buttonDim.height); + x += buttonDim.width; + } + + if (frame.isMaximizable()) { + maximizeButton.setBounds(x, y, buttonDim.width, buttonDim.height); + x += buttonDim.width; + } + + if (frame.isIconifiable()) { + minimizeButton.setBounds(x, y, buttonDim.width, buttonDim.height); + } } } } // end TitlePaneLayout @@ -973,10 +996,8 @@ class Metacity implements SynthConstants { String title = jif.getTitle(); if (title != null) { FontMetrics fm = SwingUtilities2.getFontMetrics(jif, g); - if (jif.getComponentOrientation().isLeftToRight()) { - title = SwingUtilities2.clipStringIfNecessary(jif, fm, title, - calculateTitleTextWidth(g, jif)); - } + title = SwingUtilities2.clipStringIfNecessary(jif, fm, title, + calculateTitleArea(jif).width); g.setColor(color); SwingUtilities2.drawString(jif, g, title, x, y + fm.getAscent()); } @@ -1010,9 +1031,10 @@ class Metacity implements SynthConstants { JComponent titlePane = findChild(jif, "InternalFrame.northPane"); Dimension buttonDim = calculateButtonSize(titlePane); Insets title_border = (Insets)frameGeometry.get("title_border"); - Rectangle r = new Rectangle(); + Insets button_border = (Insets)getFrameGeometry().get("button_border"); - r.x = getInt("left_titlebar_edge") + buttonDim.width; + Rectangle r = new Rectangle(); + r.x = getInt("left_titlebar_edge"); r.y = 0; r.height = titlePane.getHeight(); if (title_border != null) { @@ -1021,15 +1043,36 @@ class Metacity implements SynthConstants { r.height -= (title_border.top + title_border.bottom); } - r.width = titlePane.getWidth() - r.x - getInt("right_titlebar_edge"); - if (jif.isClosable()) { - r.width -= buttonDim.width; - } - if (jif.isMaximizable()) { - r.width -= buttonDim.width; - } - if (jif.isIconifiable()) { - r.width -= buttonDim.width; + if (titlePane.getParent().getComponentOrientation().isLeftToRight()) { + r.x += buttonDim.width; + if (button_border != null) { + r.x += button_border.left; + } + r.width = titlePane.getWidth() - r.x - getInt("right_titlebar_edge"); + if (jif.isClosable()) { + r.width -= buttonDim.width; + } + if (jif.isMaximizable()) { + r.width -= buttonDim.width; + } + if (jif.isIconifiable()) { + r.width -= buttonDim.width; + } + } else { + if (jif.isClosable()) { + r.x += buttonDim.width; + } + if (jif.isMaximizable()) { + r.x += buttonDim.width; + } + if (jif.isIconifiable()) { + r.x += buttonDim.width; + } + r.width = titlePane.getWidth() - r.x - getInt("right_titlebar_edge") + - buttonDim.width; + if (button_border != null) { + r.x -= button_border.right; + } } if (title_border != null) { r.width -= title_border.right; diff --git a/src/share/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java b/src/share/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java index 63d853a28..5222cd5dd 100644 --- a/src/share/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java +++ b/src/share/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameTitlePane.java @@ -137,25 +137,46 @@ public class WindowsInternalFrameTitlePane extends BasicInternalFrameTitlePane { int baseline = (getHeight() + fm.getAscent() - fm.getLeading() - fm.getDescent()) / 2; + Rectangle lastIconBounds = new Rectangle(0, 0, 0, 0); + if (frame.isIconifiable()) { + lastIconBounds = iconButton.getBounds(); + } else if (frame.isMaximizable()) { + lastIconBounds = maxButton.getBounds(); + } else if (frame.isClosable()) { + lastIconBounds = closeButton.getBounds(); + } + int titleX; - Rectangle r = new Rectangle(0, 0, 0, 0); - if (frame.isIconifiable()) r = iconButton.getBounds(); - else if (frame.isMaximizable()) r = maxButton.getBounds(); - else if (frame.isClosable()) r = closeButton.getBounds(); int titleW; - - if(WindowsGraphicsUtils.isLeftToRight(frame) ) { - if (r.x == 0) r.x = frame.getWidth()-frame.getInsets().right; - titleX = systemLabel.getX() + systemLabel.getWidth() + 2; - if (xp != null) { - titleX += 2; - } - titleW = r.x - titleX - 3; - title = getTitle(frame.getTitle(), fm, titleW); + int gap = 2; + if (WindowsGraphicsUtils.isLeftToRight(frame)) { + if (lastIconBounds.x == 0) { // There are no icons + lastIconBounds.x = frame.getWidth() - frame.getInsets().right; + } + titleX = systemLabel.getX() + systemLabel.getWidth() + gap; + if (xp != null) { + titleX += 2; + } + titleW = lastIconBounds.x - titleX - gap; } else { - titleX = systemLabel.getX() - 2 - - SwingUtilities2.stringWidth(frame,fm,title); + if (lastIconBounds.x == 0) { // There are no icons + lastIconBounds.x = frame.getInsets().left; + } + titleW = SwingUtilities2.stringWidth(frame, fm, title); + int minTitleX = lastIconBounds.x + lastIconBounds.width + gap; + if (xp != null) { + minTitleX += 2; + } + int availableWidth = systemLabel.getX() - gap - minTitleX; + if (availableWidth > titleW) { + titleX = systemLabel.getX() - gap - titleW; + } else { + titleX = minTitleX; + titleW = availableWidth; + } } + title = getTitle(frame.getTitle(), fm, titleW); + if (xp != null) { String shadowType = null; if (isSelected) { diff --git a/src/share/classes/javax/swing/plaf/synth/SynthInternalFrameTitlePane.java b/src/share/classes/javax/swing/plaf/synth/SynthInternalFrameTitlePane.java index 353c95c03..f1c888952 100644 --- a/src/share/classes/javax/swing/plaf/synth/SynthInternalFrameTitlePane.java +++ b/src/share/classes/javax/swing/plaf/synth/SynthInternalFrameTitlePane.java @@ -215,7 +215,7 @@ class SynthInternalFrameTitlePane extends BasicInternalFrameTitlePane protected void showSystemMenu() { Insets insets = frame.getInsets(); if (!frame.isIcon()) { - systemPopupMenu.show(frame, insets.left, getY() + getHeight()); + systemPopupMenu.show(frame, menuButton.getX(), getY() + getHeight()); } else { systemPopupMenu.show(menuButton, getX() - insets.left - insets.right, -- GitLab From d42f09c629a38c925b0cb0896ff88622f2115657 Mon Sep 17 00:00:00 2001 From: mlapshin Date: Tue, 12 Aug 2008 12:52:10 +0400 Subject: [PATCH 049/139] 6735918: test/closed/javax/swing/JMenuItem/6458123/bug6458123.java fails on Linux Summary: All the bearings-related code is removed from MenuItemLayoutHelper class Reviewed-by: alexp --- .../sun/swing/MenuItemLayoutHelper.java | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/src/share/classes/sun/swing/MenuItemLayoutHelper.java b/src/share/classes/sun/swing/MenuItemLayoutHelper.java index 5c4810b8f..b1313e431 100644 --- a/src/share/classes/sun/swing/MenuItemLayoutHelper.java +++ b/src/share/classes/sun/swing/MenuItemLayoutHelper.java @@ -572,9 +572,6 @@ public class MenuItemLayoutHelper { alignRects(lr, alignment); - // Take into account the left side bearings for text and accelerator text. - fixTextRects(lr); - // Set Y coordinate for text and icon. // Y coordinates for other rects // will be calculated later in layoutMenuItem. @@ -607,10 +604,6 @@ public class MenuItemLayoutHelper { alignRects(lr, alignment); - // Take into account the left side bearing for accelerator text. - // The LSB for text is taken into account in layoutCompoundLabel() below. - fixAccTextRect(lr); - // Center labelRect vertically calcLabelYPosition(lr); @@ -645,9 +638,6 @@ public class MenuItemLayoutHelper { alignRects(lr, alignment); - // Take into account the left side bearings for text and accelerator text. - fixTextRects(lr); - // Set Y coordinates for text and icon. // Y coordinates for other rects // will be calculated later in layoutMenuItem. @@ -680,10 +670,6 @@ public class MenuItemLayoutHelper { alignRects(lr, alignment); - // Take into account the left side bearing for accelerator text. - // The LSB for text is taken into account in layoutCompoundLabel() below. - fixAccTextRect(lr); - // Center labelRect vertically calcLabelYPosition(lr); @@ -741,29 +727,6 @@ public class MenuItemLayoutHelper { } } - /** - * Takes into account the left side bearings for text and accelerator text - */ - private void fixTextRects(LayoutResult lr) { - if (htmlView == null) { // The text isn't a HTML - int lsb = SwingUtilities2.getLeftSideBearing(mi, fm, text); - if (lsb < 0) { - lr.textRect.x -= lsb; - } - } - fixAccTextRect(lr); - } - - /** - * Takes into account the left side bearing for accelerator text - */ - private void fixAccTextRect(LayoutResult lr) { - int lsb = SwingUtilities2.getLeftSideBearing(mi, accFm, accText); - if (lsb < 0) { - lr.accRect.x -= lsb; - } - } - /** * Sets Y coordinates of text and icon * taking into account the vertical alignment -- GitLab From 3b9a3f984c4ec01b4335112ff852fc336440668b Mon Sep 17 00:00:00 2001 From: xdono Date: Thu, 14 Aug 2008 09:26:34 -0700 Subject: [PATCH 050/139] Added tag jdk7-b33 for changeset fa4c0a6cdd25 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 92c22dd5b..75a9825e9 100644 --- a/.hgtags +++ b/.hgtags @@ -7,3 +7,4 @@ e21f4266466cd1306b176aaa08b2cd8337a9be3d jdk7-b29 b6d6877c1155621a175dccd12dc14c54f938fb8b jdk7-b30 b7474b739d13bacd9972f88ac91f6350b7b0be12 jdk7-b31 c51121419e30eac5f0fbbce45ff1711c4ce0de28 jdk7-b32 +fa4c0a6cdd25d97d4e6f5d7aa180bcbb0e0d56af jdk7-b33 -- GitLab From d5a7c704c2ef3dcd1a8f90717982269f47872b0c Mon Sep 17 00:00:00 2001 From: ohair Date: Thu, 14 Aug 2008 13:33:08 -0700 Subject: [PATCH 051/139] 6674227: Missing LICENSE file during build Summary: Just a JPRT usage issue. The top level files (like LICENSE) are needed to create the jdk image (j2sdk-image directory). Reviewed-by: tbell --- make/jprt.properties | 1 - 1 file changed, 1 deletion(-) diff --git a/make/jprt.properties b/make/jprt.properties index 06abef8c8..e6666cb1d 100644 --- a/make/jprt.properties +++ b/make/jprt.properties @@ -55,6 +55,5 @@ jprt.solaris_x64.build.platform.match32=solaris_i586_5.10 jprt.test.targets=*-*-*-jvm98 # Directories needed to build -jprt.bundle.src.dirs=make src jprt.bundle.exclude.src.dirs=build -- GitLab From 4b3d593880ea5af79d4886bf6fdd3944344bf852 Mon Sep 17 00:00:00 2001 From: jjh Date: Fri, 15 Aug 2008 18:06:42 -0700 Subject: [PATCH 052/139] 6737900: TEST: Some JDI regression tests timeout on slow machines Summary: Don't execute useless code, and split test into multiple @runs. Reviewed-by: tbell --- test/com/sun/jdi/ClassesByName2Test.java | 9 +++-- test/com/sun/jdi/ConnectedVMs.java | 47 +++++++++++------------- test/com/sun/jdi/sde/MangleStepTest.java | 10 +++-- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/test/com/sun/jdi/ClassesByName2Test.java b/test/com/sun/jdi/ClassesByName2Test.java index 96df25d79..3bdab5d47 100644 --- a/test/com/sun/jdi/ClassesByName2Test.java +++ b/test/com/sun/jdi/ClassesByName2Test.java @@ -134,10 +134,11 @@ public class ClassesByName2Test extends TestScaffold { } } } - /* - * resume the target listening for events - */ - listenUntilVMDisconnect(); + + + // Doing vm().exit(0) instead of listenUntilVMDisconnect() + // speeds up the test up by more than 50% in -server -Xcomp (solsparc32-fastdebug) + vm().exit(0); /* * deal with results of test diff --git a/test/com/sun/jdi/ConnectedVMs.java b/test/com/sun/jdi/ConnectedVMs.java index 549efcc9c..bbbc769a6 100644 --- a/test/com/sun/jdi/ConnectedVMs.java +++ b/test/com/sun/jdi/ConnectedVMs.java @@ -28,7 +28,10 @@ * * @run build TestScaffold VMConnection TargetListener TargetAdapter * @run compile -g InstTarg.java - * @run main ConnectedVMs InstTarg + * @run main ConnectedVMs "Kill" + * @run main ConnectedVMs "Resume to exit" + * @run main ConnectedVMs "dispose()" + * @run main ConnectedVMs "exit()" * * @summary ConnectedVMs checks the method * VirtualMachineManager.connectedVirtualMachines() @@ -40,14 +43,10 @@ import java.util.List; public class ConnectedVMs extends TestScaffold { static int failCount = 0;; - static int pass; - static String[] passNames = {"Kill", "Resume to exit", - "dispose()", "exit()"}; + static String passName; public static void main(String args[]) throws Exception { - for (pass=0; pass < passNames.length; pass++) { - new ConnectedVMs(args).startTests(); - } + new ConnectedVMs(args[0]).startTests(); if (failCount > 0) { throw new RuntimeException( "VirtualMachineManager.connectedVirtualMachines() " + @@ -58,16 +57,17 @@ public class ConnectedVMs extends TestScaffold { } } - ConnectedVMs(String args[]) throws Exception { - super(args); - System.out.println("create"); + ConnectedVMs(String name) throws Exception { + super(new String[0]); + passName = name; + System.out.println("create " + passName); } void vms(int expected) { List vms = Bootstrap.virtualMachineManager(). connectedVirtualMachines(); if (vms.size() != expected) { - System.out.println("FAILURE! " + passNames[pass] + + System.out.println("FAILURE! " + passName + " - expected: " + expected + ", got: " + vms.size()); ++failCount; @@ -75,27 +75,22 @@ public class ConnectedVMs extends TestScaffold { } protected void runTests() throws Exception { - System.out.println("Testing " + passNames[pass]); + System.out.println("Testing " + passName); vms(0); startToMain("InstTarg"); ThreadReference thread = waitForVMStart(); StepEvent stepEvent = stepIntoLine(thread); vms(1); - // pick a way to die - switch (pass) { - case 0: - vm().process().destroy(); - break; - case 1: - vm().resume(); - break; - case 2: - vm().dispose(); - break; - case 3: - vm().exit(1); - break; + // pick a way to die based on the input arg. + if (passName.equals("Kill")) { + vm().process().destroy(); + } else if (passName.equals("Resume to exit")) { + vm().resume(); + } else if (passName.equals("dispose()")) { + vm().dispose(); + } else if (passName.equals("exit()")) { + vm().exit(1); } resumeToVMDisconnect(); diff --git a/test/com/sun/jdi/sde/MangleStepTest.java b/test/com/sun/jdi/sde/MangleStepTest.java index 7a1a12c2f..ca10e27b7 100644 --- a/test/com/sun/jdi/sde/MangleStepTest.java +++ b/test/com/sun/jdi/sde/MangleStepTest.java @@ -10,7 +10,11 @@ * @run build TestScaffold VMConnection TargetListener TargetAdapter InstallSDE * @run compile MangleStepTest.java * @run compile -g onion/pickle/Mangle.java - * @run main MangleStepTest unset Java XYZ Rats bogus + * @run main MangleStepTest unset + * @run main MangleStepTest Java + * @run main MangleStepTest XYZ + * @run main MangleStepTest Rats + * @run main MangleStepTest bogus */ import com.sun.jdi.*; import com.sun.jdi.event.*; @@ -32,9 +36,7 @@ public class MangleStepTest extends TestScaffold { public static void main(String[] args) throws Exception { testSetUp(); - for (int i = 0; i < args.length; ++i) { - new MangleStepTest(args[i]).startTests(); - } + new MangleStepTest(args[0]).startTests(); if (aTestFailed) { throw new Exception("MangleStepTest: failed"); } -- GitLab From 8e36b5da83de7f1212d0fdcac89abd1d744ca4f7 Mon Sep 17 00:00:00 2001 From: ohair Date: Sun, 17 Aug 2008 17:02:04 -0700 Subject: [PATCH 053/139] 6496269: Many warnings generated from com/sun/java/util/jar/pack/*.cpp when compiled on Linux Summary: Removal of compiler warnings and fixing of assert logic. Reviewed-by: jrose, ksrini, bristor --- .../com/sun/java/util/jar/pack/bands.cpp | 24 +- .../com/sun/java/util/jar/pack/bytes.cpp | 16 +- .../native/com/sun/java/util/jar/pack/bytes.h | 6 +- .../com/sun/java/util/jar/pack/coding.cpp | 51 ++-- .../com/sun/java/util/jar/pack/coding.h | 8 +- .../com/sun/java/util/jar/pack/defines.h | 24 +- .../native/com/sun/java/util/jar/pack/jni.cpp | 9 +- .../com/sun/java/util/jar/pack/main.cpp | 15 +- .../com/sun/java/util/jar/pack/unpack.cpp | 284 ++++++++++-------- .../com/sun/java/util/jar/pack/unpack.h | 16 +- .../com/sun/java/util/jar/pack/utils.cpp | 7 +- .../native/com/sun/java/util/jar/pack/utils.h | 4 +- .../native/com/sun/java/util/jar/pack/zip.cpp | 141 ++++----- .../native/com/sun/java/util/jar/pack/zip.h | 4 +- 14 files changed, 330 insertions(+), 279 deletions(-) diff --git a/src/share/native/com/sun/java/util/jar/pack/bands.cpp b/src/share/native/com/sun/java/util/jar/pack/bands.cpp index d900404de..c78644c28 100644 --- a/src/share/native/com/sun/java/util/jar/pack/bands.cpp +++ b/src/share/native/com/sun/java/util/jar/pack/bands.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -126,15 +126,15 @@ void band::readData(int expectedLength) { (*save_meta_rp) = (byte) XB; cm.init(u->rp, u->rplimit, u->meta_rp, 0, defc, length, null); (*save_meta_rp) = save_meta_xb; // put it back, just to be tidy - NOT_PRODUCT(cp2 = (u->meta_rp - meta_rp0)); + NOT_PRODUCT(cp2 = (int)(u->meta_rp - meta_rp0)); } rplimit = u->rp; rewind(); #ifndef PRODUCT - printcr(3,"readFrom %s at %p [%d values, %d bytes, cp=%d/%d]", - (name?name:"(band)"), minRP(), length, size(), cp1, cp2); + PRINTCR((3,"readFrom %s at %p [%d values, %d bytes, cp=%d/%d]", + (name?name:"(band)"), minRP(), length, size(), cp1, cp2)); if (u->verbose_bands || u->verbose >= 4) dump(); if (ix != null && u->verbose != 0 && length > 0) { @@ -421,18 +421,22 @@ const band_init all_band_inits[] = { BAND_INIT(file_modtime, DELTA5_spec, 0), BAND_INIT(file_options, UNSIGNED5_spec, 0), //BAND_INIT(file_bits, BYTE1_spec, 0), - {0} +#ifndef PRODUCT + { 0, 0, 0, 0 } +#else + { 0, 0 } +#endif }; #define NUM_BAND_INITS \ (sizeof(all_band_inits)/sizeof(all_band_inits[0])) band* band::makeBands(unpacker* u) { - band* all_bands = U_NEW(band, BAND_LIMIT); + band* tmp_all_bands = U_NEW(band, BAND_LIMIT); for (int i = 0; i < BAND_LIMIT; i++) { assert((byte*)&all_band_inits[i+1] < (byte*)all_band_inits+sizeof(all_band_inits)); const band_init& bi = all_band_inits[i]; - band& b = all_bands[i]; + band& b = tmp_all_bands[i]; coding* defc = coding::findBySpec(bi.defc); assert((defc == null) == (bi.defc == -1)); // no garbage, please assert(defc == null || !defc->isMalloc); @@ -446,13 +450,13 @@ band* band::makeBands(unpacker* u) { b.name = bi.name; #endif } - return all_bands; + return tmp_all_bands; } void band::initIndexes(unpacker* u) { - band* all_bands = u->all_bands; + band* tmp_all_bands = u->all_bands; for (int i = 0; i < BAND_LIMIT; i++) { - band* scan = &all_bands[i]; + band* scan = &tmp_all_bands[i]; uint tag = scan->ixTag; // Cf. #define INDEX(tag) above if (tag != 0 && tag != CONSTANT_Literal && (tag & SUBINDEX_BIT) == 0) { scan->setIndex(u->cp.getIndex(tag)); diff --git a/src/share/native/com/sun/java/util/jar/pack/bytes.cpp b/src/share/native/com/sun/java/util/jar/pack/bytes.cpp index 95c3dde66..ecc45ce2c 100644 --- a/src/share/native/com/sun/java/util/jar/pack/bytes.cpp +++ b/src/share/native/com/sun/java/util/jar/pack/bytes.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -71,15 +71,17 @@ void bytes::realloc(size_t len_) { void bytes::free() { if (ptr == dummy) return; // escaping from an error - if (ptr != null) mtrace('f', ptr, 0); - if (ptr != null) ::free(ptr); + if (ptr != null) { + mtrace('f', ptr, 0); + ::free(ptr); + } len = 0; ptr = 0; } int bytes::indexOf(byte c) { byte* p = (byte*) memchr(ptr, c, len); - return (p == 0) ? -1 : p - ptr; + return (p == 0) ? -1 : (int)(p - ptr); } byte* bytes::writeTo(byte* bp) { @@ -174,8 +176,10 @@ void ptrlist::freeAll() { int len = length(); for (int i = 0; i < len; i++) { void* p = (void*) get(i); - if (p != null) mtrace('f', p, 0); - if (p != null) ::free(p); + if (p != null) { + mtrace('f', p, 0); + ::free(p); + } } free(); } diff --git a/src/share/native/com/sun/java/util/jar/pack/bytes.h b/src/share/native/com/sun/java/util/jar/pack/bytes.h index e0c0f6f59..f47b7eb05 100644 --- a/src/share/native/com/sun/java/util/jar/pack/bytes.h +++ b/src/share/native/com/sun/java/util/jar/pack/bytes.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -117,7 +117,7 @@ struct fillbytes { struct ptrlist : fillbytes { typedef const void* cvptr; - int length() { return size() / sizeof(cvptr); } + int length() { return (int)(size() / sizeof(cvptr)); } cvptr* base() { return (cvptr*) fillbytes::base(); } cvptr& get(int i) { return *(cvptr*)loc(i * sizeof(cvptr)); } cvptr* limit() { return (cvptr*) fillbytes::limit(); } @@ -133,7 +133,7 @@ struct ptrlist : fillbytes { ::qsort((ptrls).base(), (ptrls).length(), sizeof(void*), fn) struct intlist : fillbytes { - int length() { return size() / sizeof(int); } + int length() { return (int)(size() / sizeof(int)); } int* base() { return (int*) fillbytes::base(); } int& get(int i) { return *(int*)loc(i * sizeof(int)); } int* limit() { return (int*) fillbytes::limit(); } diff --git a/src/share/native/com/sun/java/util/jar/pack/coding.cpp b/src/share/native/com/sun/java/util/jar/pack/coding.cpp index d3ec4ae0e..dec681ccf 100644 --- a/src/share/native/com/sun/java/util/jar/pack/coding.cpp +++ b/src/share/native/com/sun/java/util/jar/pack/coding.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -113,7 +113,7 @@ coding* coding::init() { jlong maxNegCode = range-1; while (IS_NEG_CODE(S, maxPosCode)) --maxPosCode; while (!IS_NEG_CODE(S, maxNegCode)) --maxNegCode; - int maxPos = decode_sign(S, maxPosCode); + int maxPos = decode_sign(S, (uint)maxPosCode); if (maxPos < 0) this->max = INT_MAX_VALUE; // 32-bit wraparound else @@ -121,7 +121,7 @@ coding* coding::init() { if (maxNegCode < 0) this->min = 0; // No negative codings at all. else - this->min = decode_sign(S, maxNegCode); + this->min = decode_sign(S, (uint)maxNegCode); } } @@ -149,10 +149,10 @@ coding* coding::findBySpec(int spec) { coding* ptr = NEW(coding, 1); CHECK_NULL_0(ptr); coding* c = ptr->initFrom(spec); - if (c == null) mtrace('f', ptr, 0); - if (c == null) + if (c == null) { + mtrace('f', ptr, 0); ::free(ptr); - else + } else // else caller should free it... c->isMalloc = true; return c; @@ -167,9 +167,10 @@ coding* coding::findBySpec(int B, int H, int S, int D) { } void coding::free() { - if (isMalloc) mtrace('f', this, 0); - if (isMalloc) + if (isMalloc) { + mtrace('f', this, 0); ::free(this); + } } void coding_method::reset(value_stream* state) { @@ -187,7 +188,7 @@ uint coding::parse(byte* &rp, int B, int H) { byte* ptr = rp; // hand peel the i==0 part of the loop: uint b_i = *ptr++ & 0xFF; - if (B == 1 || b_i < L) + if (B == 1 || b_i < (uint)L) { rp = ptr; return b_i; } uint sum = b_i; uint H_i = H; @@ -195,7 +196,7 @@ uint coding::parse(byte* &rp, int B, int H) { for (int i = 2; i <= B_MAX; i++) { // easy for compilers to unroll if desired b_i = *ptr++ & 0xFF; sum += b_i * H_i; - if (i == B || b_i < L) + if (i == B || b_i < (uint)L) { rp = ptr; return sum; } H_i *= H; } @@ -210,7 +211,7 @@ uint coding::parse_lgH(byte* &rp, int B, int H, int lgH) { byte* ptr = rp; // hand peel the i==0 part of the loop: uint b_i = *ptr++ & 0xFF; - if (B == 1 || b_i < L) + if (B == 1 || b_i < (uint)L) { rp = ptr; return b_i; } uint sum = b_i; uint lg_H_i = lgH; @@ -218,7 +219,7 @@ uint coding::parse_lgH(byte* &rp, int B, int H, int lgH) { for (int i = 2; i <= B_MAX; i++) { // easy for compilers to unroll if desired b_i = *ptr++ & 0xFF; sum += b_i << lg_H_i; - if (i == B || b_i < L) + if (i == B || b_i < (uint)L) { rp = ptr; return sum; } lg_H_i += lgH; } @@ -237,7 +238,7 @@ void coding::parseMultiple(byte* &rp, int N, byte* limit, int B, int H) { byte* ptr = rp; if (B == 1 || H == 256) { size_t len = (size_t)N*B; - if (len / B != N || ptr+len > limit) { + if (len / B != (size_t)N || ptr+len > limit) { abort(ERB); return; } @@ -325,7 +326,7 @@ static maybe_inline int getPopValue(value_stream* self, uint uval) { if (uval > 0) { // note that the initial parse performed a range check - assert(uval <= self->cm->fVlength); + assert(uval <= (uint)self->cm->fVlength); return self->cm->fValues[uval-1]; } else { // take an unfavored value @@ -368,7 +369,7 @@ int coding::sumInUnsignedRange(int x, int y) { static maybe_inline int getDeltaValue(value_stream* self, uint uval, bool isSubrange) { - assert((bool)(self->c.isSubrange) == isSubrange); + assert((uint)(self->c.isSubrange) == (uint)isSubrange); assert(self->c.isSubrange | self->c.isFullRange); if (isSubrange) return self->sum = self->c.sumInUnsignedRange(self->sum, (int)uval); @@ -443,7 +444,7 @@ int value_stream::getInt() { uval = coding::parse(rp, B, H); if (S != 0) uval = (uint) decode_sign(S, uval); - return getDeltaValue(this, uval, c.isSubrange); + return getDeltaValue(this, uval, (bool)c.isSubrange); case cmk_BHS1D1full: assert(S == 1 && D == 1 && c.isFullRange); @@ -499,6 +500,9 @@ int value_stream::getInt() { assert(c.spec == BYTE1_spec); assert(B == 1 && H == 256 && S == 0 && D == 0); return getPopValue(this, *rp++ & 0xFF); + + default: + break; } assert(false); return 0; @@ -695,7 +699,7 @@ void coding_method::init(byte* &band_rp, byte* band_limit, for (int i = 0; i < N; i++) { uint val = vs.getInt(); if (val == 0) UN += 1; - if (!(val <= fVlength)) { + if (!(val <= (uint)fVlength)) { abort("pop token out of range"); return; } @@ -728,6 +732,7 @@ void coding_method::init(byte* &band_rp, byte* band_limit, switch (self->vs0.cmk) { case cmk_BHS0: cmk2 = cmk_pop_BHS0; break; case cmk_BYTE1: cmk2 = cmk_pop_BYTE1; break; + default: break; } self->vs0.cmk = cmk2; if (self != this) { @@ -947,15 +952,17 @@ coding basic_codings[] = { CODING_INIT(4,240,1,1), CODING_INIT(4,248,0,1), CODING_INIT(4,248,1,1), - - 0 + CODING_INIT(0,0,0,0) }; #define BASIC_INDEX_LIMIT \ - (sizeof(basic_codings)/sizeof(basic_codings[0])-1) + (int)(sizeof(basic_codings)/sizeof(basic_codings[0])-1) coding* coding::findByIndex(int idx) { - assert(_meta_canon_min == 1); - assert(_meta_canon_max+1 == BASIC_INDEX_LIMIT); +#ifndef PRODUCT + /* Tricky assert here, constants and gcc complains about it without local. */ + int index_limit = BASIC_INDEX_LIMIT; + assert(_meta_canon_min == 1 && _meta_canon_max+1 == index_limit); +#endif if (idx >= _meta_canon_min && idx <= _meta_canon_max) return basic_codings[idx].init(); else diff --git a/src/share/native/com/sun/java/util/jar/pack/coding.h b/src/share/native/com/sun/java/util/jar/pack/coding.h index 4259915d8..65a036215 100644 --- a/src/share/native/com/sun/java/util/jar/pack/coding.h +++ b/src/share/native/com/sun/java/util/jar/pack/coding.h @@ -1,5 +1,5 @@ /* - * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,9 +35,11 @@ struct unpacker; #define CODING_D(x) ((x)>>0 & 0xF) #define CODING_INIT(B, H, S, D) \ - { CODING_SPEC(B, H, S, D) } + { CODING_SPEC(B, H, S, D) , 0, 0, 0, 0, 0, 0, 0, 0} -#define long do_not_use_C_long_types_use_jlong_or_int +// For debugging purposes, some compilers do not like this and will complain. +// #define long do_not_use_C_long_types_use_jlong_or_int +// Use of the type "long" is problematic, do not use it. struct coding { int spec; // B,H,S,D diff --git a/src/share/native/com/sun/java/util/jar/pack/defines.h b/src/share/native/com/sun/java/util/jar/pack/defines.h index 19f7567e7..0bf25d5b0 100644 --- a/src/share/native/com/sun/java/util/jar/pack/defines.h +++ b/src/share/native/com/sun/java/util/jar/pack/defines.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,15 +45,15 @@ #ifdef PRODUCT #define IF_PRODUCT(xxx) xxx #define NOT_PRODUCT(xxx) -#define assert(p) (0) -#define printcr false && +#define assert(p) +#define PRINTCR(args) #else #define IF_PRODUCT(xxx) #define NOT_PRODUCT(xxx) xxx -#define assert(p) ((p) || (assert_failed(#p), 1)) -#define printcr u->verbose && u->printcr_if_verbose +#define assert(p) ((p) || assert_failed(#p)) +#define PRINTCR(args) u->verbose && u->printcr_if_verbose args extern "C" void breakpoint(); -extern void assert_failed(const char*); +extern int assert_failed(const char*); #define BREAK (breakpoint()) #endif @@ -79,7 +79,7 @@ extern void assert_failed(const char*); #define lengthof(array) (sizeof(array)/sizeof(array[0])) -#define NEW(T, n) (T*) must_malloc(sizeof(T)*(n)) +#define NEW(T, n) (T*) must_malloc((int)(sizeof(T)*(n))) #define U_NEW(T, n) (T*) u->alloc(sizeof(T)*(n)) #define T_NEW(T, n) (T*) u->temp_alloc(sizeof(T)*(n)) @@ -121,12 +121,12 @@ enum { false, true }; #define null (0) -#ifndef __sparc -#define intptr_t jlong -#endif - -#define ptrlowbits(x) ((int) (intptr_t)(x)) +/* Must cast to void *, then size_t, then int. */ +#define ptrlowbits(x) ((int)(size_t)(void*)(x)) +/* Back and forth from jlong to pointer */ +#define ptr2jlong(x) ((jlong)(size_t)(void*)(x)) +#define jlong2ptr(x) ((void*)(size_t)(x)) // Keys used by Java: #define UNPACK_DEFLATE_HINT "unpack.deflate.hint" diff --git a/src/share/native/com/sun/java/util/jar/pack/jni.cpp b/src/share/native/com/sun/java/util/jar/pack/jni.cpp index d217341da..eb9898278 100644 --- a/src/share/native/com/sun/java/util/jar/pack/jni.cpp +++ b/src/share/native/com/sun/java/util/jar/pack/jni.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,7 +59,8 @@ static jlong read_input_via_jni(unpacker* self, void* buf, jlong minlen, jlong maxlen); static unpacker* get_unpacker(JNIEnv *env, jobject pObj, bool noCreate=false) { - unpacker* uPtr = (unpacker*) env->GetLongField(pObj, unpackerPtrFID); + unpacker* uPtr; + uPtr = (unpacker*)jlong2ptr(env->GetLongField(pObj, unpackerPtrFID)); //fprintf(stderr, "get_unpacker(%p) uPtr=%p\n", pObj, uPtr); if (uPtr == null) { if (noCreate) return null; @@ -71,7 +72,7 @@ static unpacker* get_unpacker(JNIEnv *env, jobject pObj, bool noCreate=false) { //fprintf(stderr, "get_unpacker(%p) uPtr=%p initializing\n", pObj, uPtr); uPtr->init(read_input_via_jni); uPtr->jniobj = (void*) env->NewGlobalRef(pObj); - env->SetLongField(pObj, unpackerPtrFID, (jlong)uPtr); + env->SetLongField(pObj, unpackerPtrFID, ptr2jlong(uPtr)); } uPtr->jnienv = env; // keep refreshing this in case of MT access return uPtr; @@ -150,7 +151,7 @@ Java_com_sun_java_util_jar_pack_NativeUnpack_start(JNIEnv *env, jobject pObj, size_t buflen = 0; if (pBuf != null) { buf = env->GetDirectBufferAddress(pBuf); - buflen = env->GetDirectBufferCapacity(pBuf); + buflen = (size_t)env->GetDirectBufferCapacity(pBuf); if (buflen == 0) buf = null; if (buf == null) { THROW_IOE(ERROR_INTERNAL); return 0; } if ((size_t)offset >= buflen) diff --git a/src/share/native/com/sun/java/util/jar/pack/main.cpp b/src/share/native/com/sun/java/util/jar/pack/main.cpp index 9e216cd19..789841d22 100644 --- a/src/share/native/com/sun/java/util/jar/pack/main.cpp +++ b/src/share/native/com/sun/java/util/jar/pack/main.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -86,13 +86,13 @@ static jlong read_input_via_stdio(unpacker* u, readlen = (int)(maxlen - numread); int nr = 0; if (u->infileptr != null) { - nr = fread(bufptr, 1, readlen, u->infileptr); + nr = (int)fread(bufptr, 1, readlen, u->infileptr); } else { #ifndef WIN32 // we prefer unbuffered inputs - nr = read(u->infileno, bufptr, readlen); + nr = (int)read(u->infileno, bufptr, readlen); #else - nr = fread(bufptr, 1, readlen, stdin); + nr = (int)fread(bufptr, 1, readlen, stdin); #endif } if (nr <= 0) { @@ -279,7 +279,6 @@ int unpacker::run(int argc, char **argv) { char** argbuf = init_args(argc, argv, envargc); char** arg0 = argbuf+envargc; char** argp = argbuf; - int ach; int verbose = 0; char* logfile = null; @@ -370,7 +369,7 @@ int unpacker::run(int argc, char **argv) { int magic; // check for GZIP input - magic = read_magic(&u, peek, sizeof(peek)); + magic = read_magic(&u, peek, (int)sizeof(peek)); if ((magic & GZIP_MAGIC_MASK) == GZIP_MAGIC) { // Oops; must slap an input filter on this data. setup_gzin(&u); @@ -397,8 +396,8 @@ int unpacker::run(int argc, char **argv) { if (u.aborting()) break; // Peek ahead for more data. - magic = read_magic(&u, peek, sizeof(peek)); - if (magic != JAVA_PACKAGE_MAGIC) { + magic = read_magic(&u, peek, (int)sizeof(peek)); + if (magic != (int)JAVA_PACKAGE_MAGIC) { if (magic != EOF_MAGIC) u.abort("garbage after end of pack archive"); break; // all done diff --git a/src/share/native/com/sun/java/util/jar/pack/unpack.cpp b/src/share/native/com/sun/java/util/jar/pack/unpack.cpp index 6480da700..90b9f72e4 100644 --- a/src/share/native/com/sun/java/util/jar/pack/unpack.cpp +++ b/src/share/native/com/sun/java/util/jar/pack/unpack.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,21 @@ // Program for unpacking specially compressed Java packages. // John R. Rose +/* + * When compiling for a 64bit LP64 system (longs and pointers being 64bits), + * the printf format %ld is correct and use of %lld will cause warning + * errors from some compilers (gcc/g++). + * _LP64 can be explicitly set (used on Linux). + * Solaris compilers will define __sparcv9 or __x86_64 on 64bit compilations. + */ +#if defined(_LP64) || defined(__sparcv9) || defined(__x86_64) + #define LONG_LONG_FORMAT "%ld" + #define LONG_LONG_HEX_FORMAT "%lx" +#else + #define LONG_LONG_FORMAT "%lld" + #define LONG_LONG_HEX_FORMAT "%016llx" +#endif + #include #include @@ -253,12 +268,12 @@ int entry::typeSize() { inline cpindex* cpool::getFieldIndex(entry* classRef) { assert(classRef->tagMatches(CONSTANT_Class)); - assert((uint)classRef->inord < tag_count[CONSTANT_Class]); + assert((uint)classRef->inord < (uint)tag_count[CONSTANT_Class]); return &member_indexes[classRef->inord*2+0]; } inline cpindex* cpool::getMethodIndex(entry* classRef) { assert(classRef->tagMatches(CONSTANT_Class)); - assert((uint)classRef->inord < tag_count[CONSTANT_Class]); + assert((uint)classRef->inord < (uint)tag_count[CONSTANT_Class]); return &member_indexes[classRef->inord*2+1]; } @@ -341,7 +356,7 @@ bool unpacker::ensure_input(jlong more) { rplimit += nr; fetch -= nr; bytes_read += nr; - assert(remaining == (input.limit() - rplimit)); + assert(remaining == (julong)(input.limit() - rplimit)); } return true; } @@ -441,9 +456,13 @@ int unpacker::putref_index(entry* e, int size) { e->requestOutputIndex(cp, -size); // Later on we'll fix the bits. class_fixup_type.addByte(size); - class_fixup_offset.add(wpoffset()); + class_fixup_offset.add((int)wpoffset()); class_fixup_ref.add(e); - return !assert(1) ? 0 : 0x20+size; // 0x22 is easy to eyeball +#ifdef PRODUCT + return 0; +#else + return 0x20+size; // 0x22 is easy to eyeball +#endif } } @@ -472,7 +491,7 @@ enum { CHUNK = (1 << 14), SMALL = (1 << 9) }; void* unpacker::alloc_heap(size_t size, bool smallOK, bool temp) { CHECK_0; if (!smallOK || size > SMALL) { - void* res = must_malloc(size); + void* res = must_malloc((int)size); (temp ? &tmallocs : &mallocs)->add(res); return res; } @@ -481,7 +500,7 @@ void* unpacker::alloc_heap(size_t size, bool smallOK, bool temp) { xsmallbuf.init(CHUNK); (temp ? &tmallocs : &mallocs)->add(xsmallbuf.base()); } - int growBy = size; + int growBy = (int)size; growBy += -growBy & 7; // round up mod 8 return xsmallbuf.grow(growBy); } @@ -514,7 +533,7 @@ void unpacker::read_file_header() { FIRST_READ = MAGIC_BYTES + AH_LENGTH_MIN }; bool foreign_buf = (read_input_fn == null); - byte initbuf[FIRST_READ + C_SLOP + 200]; // 200 is for JAR I/O + byte initbuf[(int)FIRST_READ + (int)C_SLOP + 200]; // 200 is for JAR I/O if (foreign_buf) { // inbytes is all there is input.set(inbytes); @@ -553,7 +572,7 @@ void unpacker::read_file_header() { // Copy until EOF; assume the JAR file is the last segment. fprintf(errstrm, "Copy-mode.\n"); for (;;) { - jarout->write_data(rp, input_remaining()); + jarout->write_data(rp, (int)input_remaining()); if (foreign_buf) break; // one-time use of a passed in buffer if (input.size() < CHUNK) { @@ -572,7 +591,7 @@ void unpacker::read_file_header() { // Read the magic number. magic = 0; - for (int i1 = 0; i1 < sizeof(magic); i1++) { + for (int i1 = 0; i1 < (int)sizeof(magic); i1++) { magic <<= 8; magic += (*rp++ & 0xFF); } @@ -586,7 +605,7 @@ void unpacker::read_file_header() { majver = hdr.getInt(); hdrVals += 2; - if (magic != JAVA_PACKAGE_MAGIC || + if (magic != (int)JAVA_PACKAGE_MAGIC || (majver != JAVA5_PACKAGE_MAJOR_VERSION && majver != JAVA6_PACKAGE_MAJOR_VERSION) || (minver != JAVA5_PACKAGE_MINOR_VERSION && @@ -633,19 +652,19 @@ void unpacker::read_file_header() { // Now we can size the whole archive. // Read everything else into a mega-buffer. rp = hdr.rp; - int header_size_0 = (rp - input.base()); // used-up header (4byte + 3int) - int header_size_1 = (rplimit - rp); // buffered unused initial fragment + int header_size_0 = (int)(rp - input.base()); // used-up header (4byte + 3int) + int header_size_1 = (int)(rplimit - rp); // buffered unused initial fragment int header_size = header_size_0+header_size_1; unsized_bytes_read = header_size_0; CHECK; if (foreign_buf) { - if (archive_size > header_size_1) { + if (archive_size > (size_t)header_size_1) { abort("EOF reading fixed input buffer"); return; } } else if (archive_size > 0) { - input.set(U_NEW(byte, (size_t) header_size_0 + archive_size + C_SLOP), - (size_t) header_size_0 + archive_size); + input.set(U_NEW(byte, (size_t)(header_size_0 + archive_size + C_SLOP)), + (size_t) header_size_0 + (size_t)archive_size); assert(input.limit()[0] == 0); // Move all the bytes we read initially into the real buffer. input.b.copyFrom(initbuf, header_size); @@ -712,7 +731,7 @@ void unpacker::read_file_header() { } int cp_counts[N_TAGS_IN_ORDER]; - for (int k = 0; k < N_TAGS_IN_ORDER; k++) { + for (int k = 0; k < (int)N_TAGS_IN_ORDER; k++) { if (!(archive_options & AO_HAVE_CP_NUMBERS)) { switch (TAGS_IN_ORDER[k]) { case CONSTANT_Integer: @@ -753,7 +772,10 @@ void unpacker::read_file_header() { abort("EOF reading archive header"); // Now size the CP. - assert(N_TAGS_IN_ORDER == cpool::NUM_COUNTS); +#ifndef PRODUCT + bool x = (N_TAGS_IN_ORDER == cpool::NUM_COUNTS); + assert(x); +#endif //PRODUCT cp.init(this, cp_counts); CHECK; @@ -766,7 +788,7 @@ void unpacker::read_file_header() { // meta-bytes, if any, immediately follow archive header //band_headers.readData(band_headers_size); ensure_input(band_headers_size); - if (input_remaining() < band_headers_size) { + if (input_remaining() < (size_t)band_headers_size) { abort("EOF reading band headers"); return; } @@ -789,12 +811,14 @@ void unpacker::read_file_header() { void unpacker::finish() { if (verbose >= 1) { fprintf(errstrm, - "A total of %lld bytes were read in %d segment(s).\n", - bytes_read_before_reset+bytes_read, + "A total of " + LONG_LONG_FORMAT " bytes were read in %d segment(s).\n", + (bytes_read_before_reset+bytes_read), segments_read_before_reset+1); fprintf(errstrm, - "A total of %lld file content bytes were written.\n", - bytes_written_before_reset+bytes_written); + "A total of " + LONG_LONG_FORMAT " file content bytes were written.\n", + (bytes_written_before_reset+bytes_written)); fprintf(errstrm, "A total of %d files (of which %d are classes) were written to output.\n", files_written_before_reset+files_written, @@ -822,7 +846,7 @@ void cpool::init(unpacker* u_, int counts[NUM_COUNTS]) { int next_entry = 0; // Size the constant pool: - for (int k = 0; k < N_TAGS_IN_ORDER; k++) { + for (int k = 0; k < (int)N_TAGS_IN_ORDER; k++) { byte tag = TAGS_IN_ORDER[k]; int len = counts[k]; tag_count[tag] = len; @@ -902,8 +926,8 @@ static byte* skip_Utf8_chars(byte* cp, int len) { } static int compare_Utf8_chars(bytes& b1, bytes& b2) { - int l1 = b1.len; - int l2 = b2.len; + int l1 = (int)b1.len; + int l2 = (int)b2.len; int l0 = (l1 < l2) ? l1 : l2; byte* p1 = b1.ptr; byte* p2 = b2.ptr; @@ -949,10 +973,12 @@ void unpacker::read_Utf8_values(entry* cpMap, int len) { // First band: Read lengths of shared prefixes. if (len > PREFIX_SKIP_2) cp_Utf8_prefix.readData(len - PREFIX_SKIP_2); + NOT_PRODUCT(else cp_Utf8_prefix.readData(0)); // for asserts // Second band: Read lengths of unshared suffixes: if (len > SUFFIX_SKIP_1) cp_Utf8_suffix.readData(len - SUFFIX_SKIP_1); + NOT_PRODUCT(else cp_Utf8_suffix.readData(0)); // for asserts bytes* allsuffixes = T_NEW(bytes, len); CHECK; @@ -999,7 +1025,7 @@ void unpacker::read_Utf8_values(entry* cpMap, int len) { CHECK; tmallocs.add(chars.ptr); // free it later } else { - int shrink = chars.limit() - chp; + int shrink = (int)(chars.limit() - chp); chars.len -= shrink; charbuf.b.len -= shrink; // ungrow to reclaim buffer space // Note that we did not reclaim the final '\0'. @@ -1008,7 +1034,9 @@ void unpacker::read_Utf8_values(entry* cpMap, int len) { } } //cp_Utf8_chars.done(); - if (assert(1)) charbuf.b.set(null, 0); // tidy +#ifndef PRODUCT + charbuf.b.set(null, 0); // tidy +#endif // Fourth band: Go back and size the specially packed strings. int maxlen = 0; @@ -1041,7 +1069,7 @@ void unpacker::read_Utf8_values(entry* cpMap, int len) { for (i = 0; i < len; i++) { bytes& chars = allsuffixes[i]; if (chars.ptr != null) continue; // already input - int suffix = chars.len; // pick up the hack + int suffix = (int)chars.len; // pick up the hack uint size3 = suffix * 3; if (suffix == 0) continue; // done with empty string chars.malloc(size3); @@ -1071,7 +1099,7 @@ void unpacker::read_Utf8_values(entry* cpMap, int len) { for (i = 0; i < len; i++) { bytes& chars = allsuffixes[i]; int prefix = (i < PREFIX_SKIP_2)? 0: cp_Utf8_prefix.getInt(); - int suffix = chars.len; + int suffix = (int)chars.len; byte* fillp; // by induction, the buffer is already filled with the prefix // make sure the prefix value is not corrupted, though: @@ -1084,7 +1112,7 @@ void unpacker::read_Utf8_values(entry* cpMap, int len) { fillp = chars.writeTo(fillp); assert(bigbuf.inBounds(fillp)); *fillp = 0; // bigbuf must contain a well-formed Utf8 string - int length = fillp - bigbuf.ptr; + int length = (int)(fillp - bigbuf.ptr); bytes& value = cpMap[i].value.b; value.set(U_NEW(byte, length+1), length); value.copyFrom(bigbuf.ptr, length); @@ -1215,12 +1243,12 @@ void unpacker::read_cp() { int i; - for (int k = 0; k < N_TAGS_IN_ORDER; k++) { + for (int k = 0; k < (int)N_TAGS_IN_ORDER; k++) { byte tag = TAGS_IN_ORDER[k]; int len = cp.tag_count[tag]; int base = cp.tag_base[tag]; - printcr(1,"Reading %d %s entries...", len, NOT_PRODUCT(TAG_NAME[tag])+0); + PRINTCR((1,"Reading %d %s entries...", len, NOT_PRODUCT(TAG_NAME[tag])+0)); entry* cpMap = &cp.entries[base]; for (i = 0; i < len; i++) { cpMap[i].tag = tag; @@ -1282,7 +1310,7 @@ void unpacker::read_cp() { #ifndef PRODUCT cpindex* ix = &cp.tag_index[tag]; assert(ix->ixTag == tag); - assert(ix->len == len); + assert((int)ix->len == len); assert(ix->base1 == cpMap); #endif CHECK; @@ -1293,7 +1321,7 @@ void unpacker::read_cp() { cp.initMemberIndexes(); CHECK; - printcr(1,"parsed %d constant pool entries in %d bytes", cp.nentries, (rp - rp0)); + PRINTCR((1,"parsed %d constant pool entries in %d bytes", cp.nentries, (rp - rp0))); #define SNAME(n,s) #s "\0" const char* symNames = ( @@ -1307,7 +1335,7 @@ void unpacker::read_cp() { bytes name; name.set(symNames); if (name.len > 0 && name.ptr[0] != '0') { cp.sym[sn] = cp.ensureUtf8(name); - printcr(4, "well-known sym %d=%s", sn, cp.sym[sn]->string()); + PRINTCR((4, "well-known sym %d=%s", sn, cp.sym[sn]->string())); } symNames += name.len + 1; // skip trailing null to next name } @@ -1352,7 +1380,7 @@ unpacker::attr_definitions::defineLayout(int idx, assert(flag_limit != 0); // must be set up already if (idx >= 0) { // Fixed attr. - if (idx >= flag_limit) + if (idx >= (int)flag_limit) abort("attribute index too large"); if (isRedefined(idx)) abort("redefined attribute index"); @@ -1635,7 +1663,7 @@ unpacker::attr_definitions::parseLayout(const char* lp, band** &res, for (;;) { int caseval = 0; lp = parseNumeral(lp, caseval); - band_stack.add((void*)caseval); + band_stack.add((void*)(size_t)caseval); if (*lp == '-') { // new in version 160, allow (1-5) for (1,2,3,4,5) if (u->majver < JAVA6_PACKAGE_MAJOR_VERSION) { @@ -1654,7 +1682,7 @@ unpacker::attr_definitions::parseLayout(const char* lp, band** &res, } for (;;) { ++caseval; - band_stack.add((void*)caseval); + band_stack.add((void*)(size_t)caseval); if (caseval == caselimit) break; } } @@ -1921,7 +1949,7 @@ static int lastIndexOf(int chmin, int chmax, bytes& x, int pos) { for (byte* cp = ptr + pos; --cp >= ptr; ) { assert(x.inBounds(cp)); if (*cp >= chmin && *cp <= chmax) - return cp - ptr; + return (int)(cp - ptr); } return -1; } @@ -1976,7 +2004,7 @@ void unpacker::read_ics() { entry* inner = ic_this_class.getRef(); CHECK; uint inord = inner->inord; - assert(inord < cp.tag_count[CONSTANT_Class]); + assert(inord < (uint)cp.tag_count[CONSTANT_Class]); if (ic_index[inord] != null) { abort("identical inner class"); break; @@ -2003,10 +2031,10 @@ void unpacker::read_ics() { bytes number; bytes name; // Parse n into pkgOuter and name (and number). - printcr(5, "parse short IC name %s", n.ptr); + PRINTCR((5, "parse short IC name %s", n.ptr)); int dollar1, dollar2; // pointers to $ in the pattern // parse n = (/)*($)?($)? - int nlen = n.len; + int nlen = (int)n.len; int pkglen = lastIndexOf(SLASH_MIN, SLASH_MAX, n, nlen) + 1; dollar2 = lastIndexOf(DOLLAR_MIN, DOLLAR_MAX, n, nlen); if (dollar2 < 0) { @@ -2035,8 +2063,8 @@ void unpacker::read_ics() { pkgOuter = n.slice(0, dollar1); else pkgOuter.set(null,0); - printcr(5,"=> %s$ 0%s $%s", - pkgOuter.string(), number.string(), name.string()); + PRINTCR((5,"=> %s$ 0%s $%s", + pkgOuter.string(), number.string(), name.string())); if (pkgOuter.ptr != null) ics[i].outer = cp.ensureClass(pkgOuter); @@ -2049,7 +2077,7 @@ void unpacker::read_ics() { if (ics[i].outer != null) { uint outord = ics[i].outer->inord; if (outord != NO_INORD) { - assert(outord < cp.tag_count[CONSTANT_Class]); + assert(outord < (uint)cp.tag_count[CONSTANT_Class]); ics[i].next_sibling = ic_child_index[outord]; ic_child_index[outord] = &ics[i]; } @@ -2060,8 +2088,7 @@ void unpacker::read_ics() { } void unpacker::read_classes() { - int i; - printcr(1," ...scanning %d classes...", class_count); + PRINTCR((1," ...scanning %d classes...", class_count)); class_this.readData(class_count); class_super.readData(class_count); class_interface_count.readData(class_count); @@ -2070,6 +2097,7 @@ void unpacker::read_classes() { CHECK; #if 0 + int i; // Make a little mark on super-classes. for (i = 0; i < class_count; i++) { entry* e = class_super.getRefN(); @@ -2099,8 +2127,8 @@ void unpacker::read_classes() { read_code_headers(); - printcr(1,"scanned %d classes, %d fields, %d methods, %d code headers", - class_count, field_count, method_count, code_count); + PRINTCR((1,"scanned %d classes, %d fields, %d methods, %d code headers", + class_count, field_count, method_count, code_count)); } maybe_inline @@ -2137,7 +2165,7 @@ void unpacker::read_attrs(int attrc, int obj_count) { } indexBits &= indexMask; // ignore classfile flag bits for (idx = 0; indexBits != 0; idx++, indexBits >>= 1) { - ad.flag_count[idx] += (indexBits & 1); + ad.flag_count[idx] += (int)(indexBits & 1); } } // we'll scan these again later for output: @@ -2337,7 +2365,7 @@ void unpacker::read_attrs(int attrc, int obj_count) { for (idx = 0; idx < ad.layouts.length(); idx++) { if (ad.getLayout(idx) == null) continue; // none at this fixed index <32 - if (idx < ad.flag_limit && ad.isPredefined(idx)) + if (idx < (int)ad.flag_limit && ad.isPredefined(idx)) continue; // already handled if (ad.getCount(idx) == 0) continue; // no attributes of this type (then why transmit layouts?) @@ -2351,9 +2379,9 @@ void unpacker::attr_definitions::readBandData(int idx) { if (count == 0) return; layout_definition* lo = getLayout(idx); if (lo != null) { - printcr(1, "counted %d [redefined = %d predefined = %d] attributes of type %s.%s", + PRINTCR((1, "counted %d [redefined = %d predefined = %d] attributes of type %s.%s", count, isRedefined(idx), isPredefined(idx), - ATTR_CONTEXT_NAME[attrc], lo->name); + ATTR_CONTEXT_NAME[attrc], lo->name)); } bool hasCallables = lo->hasCallables(); band** bands = lo->bands(); @@ -2376,13 +2404,13 @@ void unpacker::attr_definitions::readBandData(int idx) { } } // Now consult whichever callables have non-zero entry counts. - readBandData(bands, -1); + readBandData(bands, (uint)-1); } } // Recursive helper to the previous function: void unpacker::attr_definitions::readBandData(band** body, uint count) { - int i, j, k; + int j, k; for (j = 0; body[j] != null; j++) { band& b = *body[j]; if (b.defc != null) { @@ -2427,7 +2455,7 @@ void unpacker::attr_definitions::readBandData(band** body, uint count) { } break; case EK_CBLE: - assert(count == -1); // incoming count is meaningless + assert((int)count == -1); // incoming count is meaningless k = b.length; assert(k >= 0); // This is intended and required for non production mode. @@ -2490,7 +2518,7 @@ void unpacker::putlayout(band** body) { assert(le_kind == EK_INT || le_kind == EK_REPL || le_kind == EK_UN); x = b.getInt(); - assert(!b.le_bci || prevBCI == to_bci(prevBII)); + assert(!b.le_bci || prevBCI == (int)to_bci(prevBII)); switch (b.le_bci) { case EK_BCI: // PH: transmit R(bci), store bci x = to_bci(prevBII = x); @@ -2505,7 +2533,7 @@ void unpacker::putlayout(band** body) { prevBCI += x; break; } - assert(!b.le_bci || prevBCI == to_bci(prevBII)); + assert(!b.le_bci || prevBCI == (int)to_bci(prevBII)); switch (b.le_len) { case 0: break; @@ -2721,8 +2749,8 @@ band* unpacker::ref_band_for_self_op(int bc, bool& isAloadVar, int& origBCVar) { // Cf. PackageReader.readByteCodes inline // called exactly once => inline void unpacker::read_bcs() { - printcr(3, "reading compressed bytecodes and operands for %d codes...", - code_count); + PRINTCR((3, "reading compressed bytecodes and operands for %d codes...", + code_count)); // read from bc_codes and bc_case_count fillbytes all_switch_ops; @@ -2825,18 +2853,18 @@ void unpacker::read_bcs() { // Go through the formality, so we can use it in a regular fashion later: assert(rp == rp0); - bc_codes.readData(opptr - rp); + bc_codes.readData((int)(opptr - rp)); int i = 0; // To size instruction bands correctly, we need info on switches: - bc_case_count.readData(all_switch_ops.size()); - for (i = 0; i < all_switch_ops.size(); i++) { + bc_case_count.readData((int)all_switch_ops.size()); + for (i = 0; i < (int)all_switch_ops.size(); i++) { int caseCount = bc_case_count.getInt(); int bc = all_switch_ops.getByte(i); bc_label.expectMoreLength(1+caseCount); // default label + cases bc_case_value.expectMoreLength(bc == bc_tableswitch ? 1 : caseCount); - printcr(2, "switch bc=%d caseCount=%d", bc, caseCount); + PRINTCR((2, "switch bc=%d caseCount=%d", bc, caseCount)); } bc_case_count.rewind(); // uses again for output @@ -2849,15 +2877,14 @@ void unpacker::read_bcs() { // The bc_escbyte band is counted by the immediately previous band. bc_escbyte.readData(bc_escsize.getIntTotal()); - printcr(3, "scanned %d opcode and %d operand bytes for %d codes...", + PRINTCR((3, "scanned %d opcode and %d operand bytes for %d codes...", (int)(bc_codes.size()), (int)(bc_escsize.maxRP() - bc_case_value.minRP()), - code_count); + code_count)); } void unpacker::read_bands() { byte* rp0 = rp; - int i; read_file_header(); CHECK; @@ -2886,9 +2913,9 @@ void unpacker::read_bands() { /// CP routines entry*& cpool::hashTabRef(byte tag, bytes& b) { - printcr(5, "hashTabRef tag=%d %s[%d]", tag, b.string(), b.len); - uint hash = tag + b.len; - for (int i = 0; i < b.len; i++) { + PRINTCR((5, "hashTabRef tag=%d %s[%d]", tag, b.string(), b.len)); + uint hash = tag + (int)b.len; + for (int i = 0; i < (int)b.len; i++) { hash = hash * 31 + (0xFF & b.ptr[i]); } entry** ht = hashTab; @@ -2905,15 +2932,15 @@ entry*& cpool::hashTabRef(byte tag, bytes& b) { // Note: hash2 must be relatively prime to hlen, hence the "|1". hash2 = (((hash % 499) & (hlen-1)) | 1); hash1 += hash2; - if (hash1 >= hlen) hash1 -= hlen; - assert(hash1 < hlen); + if (hash1 >= (uint)hlen) hash1 -= hlen; + assert(hash1 < (uint)hlen); assert(++probes < hlen); } #ifndef PRODUCT hash_probes[0] += 1; hash_probes[1] += probes; #endif - printcr(5, " => @%d %p", hash1, ht[hash1]); + PRINTCR((5, " => @%d %p", hash1, ht[hash1])); return ht[hash1]; } @@ -2939,7 +2966,7 @@ entry* cpool::ensureUtf8(bytes& b) { u->saveTo(e.value.b, b); assert(&e >= first_extra_entry); insert_extra(&e, tag_extras[CONSTANT_Utf8]); - printcr(4,"ensureUtf8 miss %s", e.string()); + PRINTCR((4,"ensureUtf8 miss %s", e.string())); return ix = &e; } @@ -2961,7 +2988,7 @@ entry* cpool::ensureClass(bytes& b) { e.value.b = utf->value.b; assert(&e >= first_extra_entry); insert_extra(&e, tag_extras[CONSTANT_Class]); - printcr(4,"ensureClass miss %s", e.string()); + PRINTCR((4,"ensureClass miss %s", e.string())); return &e; } @@ -2980,7 +3007,7 @@ void cpool::expandSignatures() { int refnum = 0; bytes form = e.refs[refnum++]->asUtf8(); buf.empty(); - for (int j = 0; j < form.len; j++) { + for (int j = 0; j < (int)form.len; j++) { int c = form.ptr[j]; buf.addByte(c); if (c == 'L') { @@ -2990,7 +3017,7 @@ void cpool::expandSignatures() { } assert(refnum == e.nrefs); bytes& sig = buf.b; - printcr(5,"signature %d %s -> %s", i, form.ptr, sig.ptr); + PRINTCR((5,"signature %d %s -> %s", i, form.ptr, sig.ptr)); // try to find a pre-existing Utf8: entry* &e2 = hashTabRef(CONSTANT_Utf8, sig); @@ -2999,7 +3026,7 @@ void cpool::expandSignatures() { e.value.b = e2->value.b; e.refs[0] = e2; e.nrefs = 1; - printcr(5,"signature replaced %d => %s", i, e.string()); + PRINTCR((5,"signature replaced %d => %s", i, e.string())); nreused++; } else { // there is no other replacement; reuse this CP entry as a Utf8 @@ -3007,15 +3034,15 @@ void cpool::expandSignatures() { e.tag = CONSTANT_Utf8; e.nrefs = 0; e2 = &e; - printcr(5,"signature changed %d => %s", e.inord, e.string()); + PRINTCR((5,"signature changed %d => %s", e.inord, e.string())); } nsigs++; } - printcr(1,"expanded %d signatures (reused %d utfs)", nsigs, nreused); + PRINTCR((1,"expanded %d signatures (reused %d utfs)", nsigs, nreused)); buf.free(); // go expunge all references to remaining signatures: - for (i = 0; i < nentries; i++) { + for (i = 0; i < (int)nentries; i++) { entry& e = entries[i]; for (int j = 0; j < e.nrefs; j++) { entry*& e2 = e.refs[j]; @@ -3028,7 +3055,7 @@ void cpool::expandSignatures() { void cpool::initMemberIndexes() { // This function does NOT refer to any class schema. // It is totally internal to the cpool. - int i, j, len; + int i, j; // Get the pre-existing indexes: int nclasses = tag_count[CONSTANT_Class]; @@ -3047,13 +3074,13 @@ void cpool::initMemberIndexes() { for (j = 0; j < nfields; j++) { entry& f = fields[j]; i = f.memberClass()->inord; - assert((uint)i < nclasses); + assert(i < nclasses); field_counts[i]++; } for (j = 0; j < nmethods; j++) { entry& m = methods[j]; i = m.memberClass()->inord; - assert((uint)i < nclasses); + assert(i < nclasses); method_counts[i]++; } @@ -3068,8 +3095,8 @@ void cpool::initMemberIndexes() { // reuse field_counts and member_counts as fill pointers: field_counts[i] = fbase; method_counts[i] = mbase; - printcr(3, "class %d fields @%d[%d] methods @%d[%d]", - i, fbase, fc, mbase, mc); + PRINTCR((3, "class %d fields @%d[%d] methods @%d[%d]", + i, fbase, fc, mbase, mc)); fbase += fc+1; mbase += mc+1; // (the +1 leaves a space between every subarray) @@ -3093,18 +3120,18 @@ void cpool::initMemberIndexes() { #ifndef PRODUCT // Test the result immediately on every class and field. int fvisited = 0, mvisited = 0; - int prevord; + int prevord, len; for (i = 0; i < nclasses; i++) { entry* cls = &classes[i]; cpindex* fix = getFieldIndex(cls); cpindex* mix = getMethodIndex(cls); - printcr(2, "field and method index for %s [%d] [%d]", - cls->string(), mix->len, fix->len); + PRINTCR((2, "field and method index for %s [%d] [%d]", + cls->string(), mix->len, fix->len)); prevord = -1; for (j = 0, len = fix->len; j < len; j++) { entry* f = fix->get(j); assert(f != null); - printcr(3, "- field %s", f->string()); + PRINTCR((3, "- field %s", f->string())); assert(f->memberClass() == cls); assert(prevord < (int)f->inord); prevord = f->inord; @@ -3115,7 +3142,7 @@ void cpool::initMemberIndexes() { for (j = 0, len = mix->len; j < len; j++) { entry* m = mix->get(j); assert(m != null); - printcr(3, "- method %s", m->string()); + PRINTCR((3, "- method %s", m->string())); assert(m->memberClass() == cls); assert(prevord < (int)m->inord); prevord = m->inord; @@ -3164,7 +3191,7 @@ void cpool::resetOutputIndexes() { outputEntries.empty(); #ifndef PRODUCT // they must all be clear now - for (i = 0; i < nentries; i++) + for (i = 0; i < (int)nentries; i++) assert(entries[i].outputIndex == NOT_REQUESTED); #endif } @@ -3215,7 +3242,7 @@ void cpool::computeOutputIndexes() { static uint checkStart = 0; int checkStep = 1; if (nentries > 100) checkStep = nentries / 100; - for (i = (checkStart++ % checkStep); i < nentries; i += checkStep) { + for (i = (int)(checkStart++ % checkStep); i < (int)nentries; i += checkStep) { entry& e = entries[i]; if (e.outputIndex != NOT_REQUESTED) { assert(outputEntries.contains(&e)); @@ -3225,7 +3252,7 @@ void cpool::computeOutputIndexes() { } // check hand-initialization of TAG_ORDER - for (i = 0; i < N_TAGS_IN_ORDER; i++) { + for (i = 0; i < (int)N_TAGS_IN_ORDER; i++) { byte tag = TAGS_IN_ORDER[i]; assert(TAG_ORDER[tag] == i+1); } @@ -3247,7 +3274,7 @@ void cpool::computeOutputIndexes() { if (e.isDoubleWord()) nextIndex++; // do not use the next index } outputIndexLimit = nextIndex; - printcr(3,"renumbering CP to %d entries", outputIndexLimit); + PRINTCR((3,"renumbering CP to %d entries", outputIndexLimit)); } #ifndef PRODUCT @@ -3257,9 +3284,9 @@ unpacker* debug_u; static bytes& getbuf(int len) { // for debugging only! static int bn = 0; - static bytes bufs[8] = { 0 }; + static bytes bufs[8]; bytes& buf = bufs[bn++ & 7]; - while (buf.len < len+10) + while ((int)buf.len < len+10) buf.realloc(buf.len ? buf.len * 2 : 1000); buf.ptr[0] = 0; // for the sake of strcat return buf; @@ -3285,7 +3312,7 @@ char* entry::string() { case CONSTANT_Long: case CONSTANT_Double: buf = getbuf(24); - sprintf((char*)buf.ptr, "0x%016llx", value.l); + sprintf((char*)buf.ptr, "0x" LONG_LONG_HEX_FORMAT, value.l); break; default: if (nrefs == 0) { @@ -3296,7 +3323,7 @@ char* entry::string() { } else { char* s1 = refs[0]->string(); char* s2 = refs[1]->string(); - buf = getbuf(strlen(s1) + 1 + strlen(s2) + 4 + 1); + buf = getbuf((int)strlen(s1) + 1 + (int)strlen(s2) + 4 + 1); buf.strcat(s1).strcat(" ").strcat(s2); if (nrefs > 2) buf.strcat(" ..."); } @@ -3409,7 +3436,9 @@ void unpacker::reset() { segments_read_before_reset += 1; if (verbose >= 2) { fprintf(errstrm, - "After segment %d, %lld bytes read and %lld bytes written.\n", + "After segment %d, " + LONG_LONG_FORMAT " bytes read and " + LONG_LONG_FORMAT " bytes written.\n", segments_read_before_reset-1, bytes_read_before_reset, bytes_written_before_reset); fprintf(errstrm, @@ -3475,7 +3504,9 @@ void unpacker::init(read_input_fn_t input_fn) { int i; NOT_PRODUCT(debug_u = this); BYTES_OF(*this).clear(); - if (assert(1)) free(); // just to make sure freeing is idempotent +#ifndef PRODUCT + free(); // just to make sure freeing is idempotent +#endif this->u = this; // self-reference for U_NEW macro errstrm = stdout; // default error-output log_file = LOGFILE_STDOUT; @@ -3621,7 +3652,7 @@ void unpacker::put_stackmap_type() { maybe_inline void unpacker::put_label(int curIP, int size) { code_fixup_type.addByte(size); - code_fixup_offset.add(put_empty(size)); + code_fixup_offset.add((int)put_empty(size)); code_fixup_source.add(curIP); } @@ -3658,7 +3689,7 @@ void unpacker::write_bc_ops() { } for (int curIP = 0; ; curIP++) { - int curPC = wpoffset() - codeBase; + int curPC = (int)(wpoffset() - codeBase); bcimap.add(curPC); ensure_put_space(10); // covers most instrs w/o further bounds check int bc = *opptr++ & 0xFF; @@ -3702,7 +3733,7 @@ void unpacker::write_bc_ops() { put_label(curIP, 4); //int lVal = bc_label.getInt(); } } - assert(to_bci(curIP) == curPC); + assert((int)to_bci(curIP) == curPC); continue; } case bc_iinc: @@ -3805,7 +3836,7 @@ void unpacker::write_bc_ops() { assert(bc <= bc_jsr_w); put_label(curIP, 4); //putu4(lVal); } - assert(to_bci(curIP) == curPC); + assert((int)to_bci(curIP) == curPC); continue; } bc_which = ref_band_for_op(bc); @@ -3880,7 +3911,7 @@ void unpacker::write_bc_ops() { //bcimap.add(curPC); // PC limit is already also in map, from bc_end_marker // Armed with a bcimap, we can now fix up all the labels. - for (int i = 0; i < code_fixup_type.size(); i++) { + for (int i = 0; i < (int)code_fixup_type.size(); i++) { int type = code_fixup_type.getByte(i); byte* bp = wp_at(code_fixup_offset.get(i)); int curIP = code_fixup_source.get(i); @@ -3896,7 +3927,7 @@ void unpacker::write_bc_ops() { inline // called exactly once => inline void unpacker::write_code() { - int i, j; + int j; int max_stack, max_locals, handler_count, cflags; get_code_header(max_stack, max_locals, handler_count, cflags); @@ -3919,7 +3950,7 @@ void unpacker::write_code() { CHECK; byte* bcbasewp = wp_at(bcbase); - putu4_at(bcbasewp, wp - (bcbasewp+4)); // size of code attr + putu4_at(bcbasewp, (int)(wp - (bcbasewp+4))); // size of code attr putu2(handler_count); for (j = 0; j < handler_count; j++) { @@ -3968,10 +3999,10 @@ int unpacker::write_attrs(int attrc, julong indexBits) { if ((indexBits & 1) != 0) bitIndexes[biCount++] = idx; } - assert(biCount <= lengthof(bitIndexes)); + assert(biCount <= (int)lengthof(bitIndexes)); // Write a provisional attribute count, perhaps to be corrected later. - int naOffset = wpoffset(); + int naOffset = (int)wpoffset(); int na0 = biCount + oiCount; putu2(na0); @@ -3986,7 +4017,7 @@ int unpacker::write_attrs(int attrc, julong indexBits) { entry* ref; // scratch size_t abase = put_empty(2+4); CHECK_0; - if (idx < ad.flag_limit && ad.isPredefined(idx)) { + if (idx < (int)ad.flag_limit && ad.isPredefined(idx)) { // Switch on the attrc and idx simultaneously. switch (ADH_BYTE(attrc, idx)) { @@ -4020,16 +4051,16 @@ int unpacker::write_attrs(int attrc, julong indexBits) { if (ref == null) { bytes& n = cur_class->ref(0)->value.b; // parse n = (/)*?($)* - int pkglen = lastIndexOf(SLASH_MIN, SLASH_MAX, n, n.len)+1; + int pkglen = lastIndexOf(SLASH_MIN, SLASH_MAX, n, (int)n.len)+1; bytes prefix = n.slice(pkglen, n.len); for (;;) { // Work backwards, finding all '$', '#', etc. - int dollar = lastIndexOf(DOLLAR_MIN, DOLLAR_MAX, prefix, prefix.len); + int dollar = lastIndexOf(DOLLAR_MIN, DOLLAR_MAX, prefix, (int)prefix.len); if (dollar < 0) break; prefix = prefix.slice(0, dollar); } const char* suffix = ".java"; - int len = prefix.len + strlen(suffix); + int len = (int)(prefix.len + strlen(suffix)); bytes name; name.set(T_NEW(byte, len + 1), len); name.strcat(prefix).strcat(suffix); ref = cp.ensureUtf8(name); @@ -4081,7 +4112,7 @@ int unpacker::write_attrs(int attrc, julong indexBits) { // (253) [(1)(2)(2)] // (254) [(1)(2)(2)(2)] putu2(code_StackMapTable_offset.getInt()); - for (int j2 = (tag - 251); j2 > 0; j2--) { + for (int k = (tag - 251); k > 0; k--) { put_stackmap_type(); } } else { @@ -4165,7 +4196,7 @@ int unpacker::write_attrs(int attrc, julong indexBits) { abort("bad layout index"); break; } - assert(lo->idx == idx); + assert((int)lo->idx == idx); aname = lo->nameEntry; if (aname == null) { bytes nameb; nameb.set(lo->name); @@ -4198,7 +4229,7 @@ int unpacker::write_attrs(int attrc, julong indexBits) { // patch the name and length putref(aname); - putu4(wp1 - (wp+4)); // put the attr size + putu4((int)(wp1 - (wp+4))); // put the attr size wp = wp1; na++; // count the attrs actually written } @@ -4279,7 +4310,7 @@ void unpacker::write_classfile_tail() { cur_class_has_local_ics = false; // may be set true by write_attrs - int naOffset = wpoffset(); + int naOffset = (int)wpoffset(); int na = write_attrs(ATTR_CONTEXT_CLASS, (kflags & indexMask)); @@ -4448,7 +4479,7 @@ void unpacker::write_classfile_head() { putu1(tag); switch (tag) { case CONSTANT_Utf8: - putu2(e.value.b.len); + putu2((int)e.value.b.len); put_bytes(e.value.b); break; case CONSTANT_Integer: @@ -4479,7 +4510,7 @@ void unpacker::write_classfile_head() { #ifndef PRODUCT total_cp_size[0] += cp.outputIndexLimit; - total_cp_size[1] += cur_classfile_head.size(); + total_cp_size[1] += (int)cur_classfile_head.size(); #endif close_output(); } @@ -4544,7 +4575,7 @@ unpacker::file* unpacker::get_next_file() { if (cur_file.name[0] == '\0') { bytes& prefix = cur_class->ref(0)->value.b; const char* suffix = ".class"; - int len = prefix.len + strlen(suffix); + int len = (int)(prefix.len + strlen(suffix)); bytes name; name.set(T_NEW(byte, len + 1), len); cur_file.name = name.strcat(prefix).strcat(suffix).strval(); } @@ -4564,7 +4595,7 @@ unpacker::file* unpacker::get_next_file() { } if (rpleft < cur_file.size) { // Caller must read the rest. - size_t fleft = cur_file.size - rpleft; + size_t fleft = (size_t)cur_file.size - rpleft; bytes_read += fleft; // Credit it to the overall archive size. } } @@ -4580,7 +4611,7 @@ void unpacker::write_file_to_jar(unpacker::file* f) { julong fsize = f->size; #ifndef PRODUCT if (nowrite NOT_PRODUCT(|| skipfiles-- > 0)) { - printcr(2,"would write %d bytes to %s", (int) fsize, f->name); + PRINTCR((2,"would write %d bytes to %s", (int) fsize, f->name)); return; } #endif @@ -4623,7 +4654,8 @@ void unpacker::write_file_to_jar(unpacker::file* f) { part1, part2); } if (verbose >= 3) { - fprintf(errstrm, "Wrote %lld bytes to: %s\n", fsize, f->name); + fprintf(errstrm, "Wrote " + LONG_LONG_FORMAT " bytes to: %s\n", fsize, f->name); } } diff --git a/src/share/native/com/sun/java/util/jar/pack/unpack.h b/src/share/native/com/sun/java/util/jar/pack/unpack.h index 7ab8e07b2..8bad674e0 100644 --- a/src/share/native/com/sun/java/util/jar/pack/unpack.h +++ b/src/share/native/com/sun/java/util/jar/pack/unpack.h @@ -1,5 +1,5 @@ /* - * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -314,7 +314,7 @@ struct unpacker { void readBandData(band** body, uint count); // recursive helper layout_definition* getLayout(uint idx) { - if (idx >= layouts.length()) return null; + if (idx >= (uint)layouts.length()) return null; return (layout_definition*) layouts.get(idx); } @@ -332,12 +332,12 @@ struct unpacker { int predefCount(uint idx); bool isRedefined(uint idx) { - assert(idx < flag_limit); - return ((redef >> idx) & 1); + if (idx >= flag_limit) return false; + return (bool)((redef >> idx) & 1); } bool isPredefined(uint idx) { - assert(idx < flag_limit); - return (((predef & ~redef) >> idx) & 1); + if (idx >= flag_limit) return false; + return (bool)(((predef & ~redef) >> idx) & 1); } julong flagIndexMask() { return (predef | redef); @@ -345,9 +345,9 @@ struct unpacker { bool isIndex(uint idx) { assert(flag_limit != 0); // must be set up already if (idx < flag_limit) - return (((predef | redef) >> idx) & 1); + return (bool)(((predef | redef) >> idx) & 1); else - return (idx - flag_limit < overflow_count.length()); + return (idx - flag_limit < (uint)overflow_count.length()); } int& getCount(uint idx) { assert(isIndex(idx)); diff --git a/src/share/native/com/sun/java/util/jar/pack/utils.cpp b/src/share/native/com/sun/java/util/jar/pack/utils.cpp index 4f45c84b5..9566e5412 100644 --- a/src/share/native/com/sun/java/util/jar/pack/utils.cpp +++ b/src/share/native/com/sun/java/util/jar/pack/utils.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -65,7 +65,7 @@ void* must_malloc(int size) { void mkdirs(int oklen, char* path) { - if (strlen(path) <= oklen) return; + if (strlen(path) <= (size_t)oklen) return; char dir[PATH_MAX]; strcpy(dir, path); @@ -79,12 +79,13 @@ void mkdirs(int oklen, char* path) { #ifndef PRODUCT void breakpoint() { } // hook for debugger -void assert_failed(const char* p) { +int assert_failed(const char* p) { char message[1<<12]; sprintf(message, "@assert failed: %s\n", p); fprintf(stdout, 1+message); breakpoint(); unpack_abort(message); + return 0; } #endif diff --git a/src/share/native/com/sun/java/util/jar/pack/utils.h b/src/share/native/com/sun/java/util/jar/pack/utils.h index 6176634bc..2fc1728d8 100644 --- a/src/share/native/com/sun/java/util/jar/pack/utils.h +++ b/src/share/native/com/sun/java/util/jar/pack/utils.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,7 @@ void* must_malloc(int size); #ifndef USE_MTRACE -#define mtrace(c, ptr, size) (0) +#define mtrace(c, ptr, size) #else void mtrace(char c, void* ptr, size_t size); #endif diff --git a/src/share/native/com/sun/java/util/jar/pack/zip.cpp b/src/share/native/com/sun/java/util/jar/pack/zip.cpp index 1042738f2..6bfb64463 100644 --- a/src/share/native/com/sun/java/util/jar/pack/zip.cpp +++ b/src/share/native/com/sun/java/util/jar/pack/zip.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -85,7 +85,7 @@ void jar::init(unpacker* u_) { // Write data to the ZIP output stream. void jar::write_data(void* buff, int len) { while (len > 0) { - int rc = fwrite(buff, 1, len, jarfp); + int rc = (int)fwrite(buff, 1, len, jarfp); if (rc <= 0) { fprintf(u->errstrm, "Error: write on output file failed err=%d\n",errno); exit(1); // Called only from the native standalone unpacker @@ -98,17 +98,17 @@ void jar::write_data(void* buff, int len) { void jar::add_to_jar_directory(const char* fname, bool store, int modtime, int len, int clen, uLong crc) { - uint fname_length = strlen(fname); + uint fname_length = (uint)strlen(fname); ushort header[23]; if (modtime == 0) modtime = default_modtime; uLong dostime = get_dostime(modtime); - header[0] = SWAP_BYTES(0x4B50); - header[1] = SWAP_BYTES(0x0201); - header[2] = SWAP_BYTES(0xA); + header[0] = (ushort)SWAP_BYTES(0x4B50); + header[1] = (ushort)SWAP_BYTES(0x0201); + header[2] = (ushort)SWAP_BYTES(0xA); // required version - header[3] = SWAP_BYTES(0xA); + header[3] = (ushort)SWAP_BYTES(0xA); // flags 02 = maximum sub-compression flag header[4] = ( store ) ? 0x0 : SWAP_BYTES(0x2); @@ -117,23 +117,23 @@ void jar::add_to_jar_directory(const char* fname, bool store, int modtime, header[5] = ( store ) ? 0x0 : SWAP_BYTES(0x08); // Last modified date and time. - header[6] = GET_INT_LO(dostime); - header[7] = GET_INT_HI(dostime); + header[6] = (ushort)GET_INT_LO(dostime); + header[7] = (ushort)GET_INT_HI(dostime); // CRC - header[8] = GET_INT_LO(crc); - header[9] = GET_INT_HI(crc); + header[8] = (ushort)GET_INT_LO(crc); + header[9] = (ushort)GET_INT_HI(crc); // Compressed length: - header[10] = GET_INT_LO(clen); - header[11] = GET_INT_HI(clen); + header[10] = (ushort)GET_INT_LO(clen); + header[11] = (ushort)GET_INT_HI(clen); // Uncompressed length. - header[12] = GET_INT_LO(len); - header[13] = GET_INT_HI(len); + header[12] = (ushort)GET_INT_LO(len); + header[13] = (ushort)GET_INT_HI(len); // Filename length - header[14] = SWAP_BYTES(fname_length); + header[14] = (ushort)SWAP_BYTES(fname_length); // So called "extra field" length. header[15] = 0; // So called "comment" length. @@ -146,8 +146,8 @@ void jar::add_to_jar_directory(const char* fname, bool store, int modtime, header[19] = 0; header[20] = 0; // Offset within ZIP file. - header[21] = GET_INT_LO(output_file_offset); - header[22] = GET_INT_HI(output_file_offset); + header[21] = (ushort)GET_INT_LO(output_file_offset); + header[22] = (ushort)GET_INT_HI(output_file_offset); // Copy the whole thing into the central directory. central_directory.append(header, sizeof(header)); @@ -160,17 +160,17 @@ void jar::add_to_jar_directory(const char* fname, bool store, int modtime, void jar::write_jar_header(const char* fname, bool store, int modtime, int len, int clen, uint crc) { - uint fname_length = strlen(fname); + uint fname_length = (uint)strlen(fname); ushort header[15]; if (modtime == 0) modtime = default_modtime; uLong dostime = get_dostime(modtime); // ZIP LOC magic. - header[0] = SWAP_BYTES(0x4B50); - header[1] = SWAP_BYTES(0x0403); + header[0] = (ushort)SWAP_BYTES(0x4B50); + header[1] = (ushort)SWAP_BYTES(0x0403); // Version - header[2] = SWAP_BYTES(0xA); + header[2] = (ushort)SWAP_BYTES(0xA); // flags 02 = maximum sub-compression flag header[3] = ( store ) ? 0x0 : SWAP_BYTES(0x2); @@ -179,31 +179,31 @@ void jar::write_jar_header(const char* fname, bool store, int modtime, header[4] = ( store ) ? 0x0 : SWAP_BYTES(0x08); // Last modified date and time. - header[5] = GET_INT_LO(dostime); - header[6] = GET_INT_HI(dostime); + header[5] = (ushort)GET_INT_LO(dostime); + header[6] = (ushort)GET_INT_HI(dostime); // CRC - header[7] = GET_INT_LO(crc); - header[8] = GET_INT_HI(crc); + header[7] = (ushort)GET_INT_LO(crc); + header[8] = (ushort)GET_INT_HI(crc); // Compressed length: - header[9] = GET_INT_LO(clen); - header[10] = GET_INT_HI(clen); + header[9] = (ushort)GET_INT_LO(clen); + header[10] = (ushort)GET_INT_HI(clen); // Uncompressed length. - header[11] = GET_INT_LO(len); - header[12] = GET_INT_HI(len); + header[11] = (ushort)GET_INT_LO(len); + header[12] = (ushort)GET_INT_HI(len); // Filename length - header[13] = SWAP_BYTES(fname_length); + header[13] = (ushort)SWAP_BYTES(fname_length); // So called "extra field" length. header[14] = 0; // Write the LOC header to the output file. - write_data(header, sizeof(header)); + write_data(header, (int)sizeof(header)); // Copy the fname to the header. - write_data((char*)fname, fname_length); + write_data((char*)fname, (int)fname_length); } static const char marker_comment[] = ZIP_ARCHIVE_MARKER_COMMENT; @@ -214,32 +214,32 @@ void jar::write_central_directory() { ushort header[11]; // Create the End of Central Directory structure. - header[0] = SWAP_BYTES(0x4B50); - header[1] = SWAP_BYTES(0x0605); + header[0] = (ushort)SWAP_BYTES(0x4B50); + header[1] = (ushort)SWAP_BYTES(0x0605); // disk numbers header[2] = 0; header[3] = 0; // Number of entries in central directory. - header[4] = SWAP_BYTES(central_directory_count); - header[5] = SWAP_BYTES(central_directory_count); + header[4] = (ushort)SWAP_BYTES(central_directory_count); + header[5] = (ushort)SWAP_BYTES(central_directory_count); // Size of the central directory} - header[6] = GET_INT_LO(central_directory.size()); - header[7] = GET_INT_HI(central_directory.size()); + header[6] = (ushort)GET_INT_LO((int)central_directory.size()); + header[7] = (ushort)GET_INT_HI((int)central_directory.size()); // Offset of central directory within disk. - header[8] = GET_INT_LO(output_file_offset); - header[9] = GET_INT_HI(output_file_offset); + header[8] = (ushort)GET_INT_LO(output_file_offset); + header[9] = (ushort)GET_INT_HI(output_file_offset); // zipfile comment length; - header [10] = SWAP_BYTES(mc.len); + header [10] = (ushort)SWAP_BYTES((int)mc.len); // Write the central directory. - printcr(2, "Central directory at %d\n", output_file_offset); + PRINTCR((2, "Central directory at %d\n", output_file_offset)); write_data(central_directory.b); // Write the End of Central Directory structure. - printcr(2, "end-of-directory at %d\n", output_file_offset); - write_data(header, sizeof(header)); + PRINTCR((2, "end-of-directory at %d\n", output_file_offset)); + write_data(header, (int)sizeof(header)); - printcr(2, "writing zip comment\n"); + PRINTCR((2, "writing zip comment\n")); // Write the comment. write_data(mc); } @@ -249,7 +249,7 @@ void jar::write_central_directory() { // Open a Jar file and initialize. void jar::openJarFile(const char* fname) { if (!jarfp) { - printcr(1, "jar::openJarFile: opening %s\n",fname); + PRINTCR((1, "jar::openJarFile: opening %s\n",fname)); jarfp = fopen(fname, "wb"); if (!jarfp) { fprintf(u->errstrm, "Error: Could not open jar file: %s\n",fname); @@ -262,25 +262,25 @@ void jar::openJarFile(const char* fname) { void jar::addJarEntry(const char* fname, bool deflate_hint, int modtime, bytes& head, bytes& tail) { - int len = head.len + tail.len; + int len = (int)(head.len + tail.len); int clen = 0; - uint crc = get_crc32(0L,Z_NULL,0); + uint crc = get_crc32(0,Z_NULL,0); if (head.len != 0) - crc = get_crc32(crc, (uchar *)head.ptr, head.len); + crc = get_crc32(crc, (uchar *)head.ptr, (uint)head.len); if (tail.len != 0) - crc = get_crc32(crc, (uchar *)tail.ptr, tail.len); + crc = get_crc32(crc, (uchar *)tail.ptr, (uint)tail.len); bool deflate = (deflate_hint && len > 0); if (deflate) { if (deflate_bytes(head, tail) == false) { - printcr(2, "Reverting to store fn=%s\t%d -> %d\n", - fname, len, deflated.size()); + PRINTCR((2, "Reverting to store fn=%s\t%d -> %d\n", + fname, len, deflated.size())); deflate = false; } } - clen = (deflate) ? deflated.size() : len; + clen = (int)((deflate) ? deflated.size() : len); add_to_jar_directory(fname, !deflate, modtime, len, clen, crc); write_jar_header( fname, !deflate, modtime, len, clen, crc); @@ -306,7 +306,7 @@ void jar::closeJarFile(bool central) { if (central) write_central_directory(); fflush(jarfp); fclose(jarfp); - printcr(2, "jar::closeJarFile:closed jar-file\n"); + PRINTCR((2, "jar::closeJarFile:closed jar-file\n")); } reset(); } @@ -338,6 +338,7 @@ uLong jar::get_dostime(int modtime) { default_modtime = modtime; // catch a reasonable default time_t t = modtime; struct tm sbuf; + (void)memset((void*)&sbuf,0, sizeof(sbuf)); struct tm* s = gmtime_r(&t, &sbuf); modtime_cache = modtime; dostime_cache = dostime(s->tm_year + 1900, s->tm_mon + 1, s->tm_mday, @@ -355,7 +356,7 @@ uLong jar::get_dostime(int modtime) { input data */ bool jar::deflate_bytes(bytes& head, bytes& tail) { - int len = head.len + tail.len; + int len = (int)(head.len + tail.len); z_stream zs; BYTES_OF(zs).clear(); @@ -368,26 +369,26 @@ bool jar::deflate_bytes(bytes& head, bytes& tail) { if (error != Z_OK) { switch (error) { case Z_MEM_ERROR: - printcr(2, "Error: deflate error : Out of memory \n"); + PRINTCR((2, "Error: deflate error : Out of memory \n")); break; case Z_STREAM_ERROR: - printcr(2,"Error: deflate error : Invalid compression level \n"); + PRINTCR((2,"Error: deflate error : Invalid compression level \n")); break; case Z_VERSION_ERROR: - printcr(2,"Error: deflate error : Invalid version\n"); + PRINTCR((2,"Error: deflate error : Invalid version\n")); break; default: - printcr(2,"Error: Internal deflate error error = %d\n", error); + PRINTCR((2,"Error: Internal deflate error error = %d\n", error)); } return false; } deflated.empty(); zs.next_out = (uchar*) deflated.grow(len + (len/2)); - zs.avail_out = deflated.size(); + zs.avail_out = (int)deflated.size(); zs.next_in = (uchar*)head.ptr; - zs.avail_in = head.len; + zs.avail_in = (int)head.len; bytes* first = &head; bytes* last = &tail; @@ -400,28 +401,28 @@ bool jar::deflate_bytes(bytes& head, bytes& tail) { if (first != null && error == Z_OK) { zs.next_in = (uchar*) first->ptr; - zs.avail_in = first->len; + zs.avail_in = (int)first->len; error = deflate(&zs, Z_NO_FLUSH); } if (error == Z_OK) { zs.next_in = (uchar*) last->ptr; - zs.avail_in = last->len; + zs.avail_in = (int)last->len; error = deflate(&zs, Z_FINISH); } if (error == Z_STREAM_END) { - if (len > zs.total_out ) { - printcr(2, "deflate compressed data %d -> %d\n", len, zs.total_out); + if (len > (int)zs.total_out ) { + PRINTCR((2, "deflate compressed data %d -> %d\n", len, zs.total_out)); deflated.b.len = zs.total_out; deflateEnd(&zs); return true; } - printcr(2, "deflate expanded data %d -> %d\n", len, zs.total_out); + PRINTCR((2, "deflate expanded data %d -> %d\n", len, zs.total_out)); deflateEnd(&zs); return false; } deflateEnd(&zs); - printcr(2, "Error: deflate error deflate did not finish error=%d\n",error); + PRINTCR((2, "Error: deflate error deflate did not finish error=%d\n",error)); return false; } @@ -486,7 +487,7 @@ void gunzip::init(unpacker* u_) { BYTES_OF(*this).clear(); u = u_; assert(u->gzin == null); // once only, please - read_input_fn = (void*)(intptr_t)u->read_input_fn; + read_input_fn = (void*)u->read_input_fn; zstream = NEW(z_stream, 1); u->gzin = this; u->read_input_fn = read_input_via_gzip; @@ -555,7 +556,7 @@ void gunzip::read_fixed_field(char* buf, size_t buflen) { if (aborting()) return; jlong nr = ((unpacker::read_input_fn_t)read_input_fn) (u, buf, buflen, buflen); - if (nr != buflen) + if ((size_t)nr != buflen) u->abort("short stream header"); } diff --git a/src/share/native/com/sun/java/util/jar/pack/zip.h b/src/share/native/com/sun/java/util/jar/pack/zip.h index 358dbe51f..007dfc3f7 100644 --- a/src/share/native/com/sun/java/util/jar/pack/zip.h +++ b/src/share/native/com/sun/java/util/jar/pack/zip.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,7 +69,7 @@ struct jar { // Private Methods void write_data(void* ptr, int len); - void write_data(bytes& b) { write_data(b.ptr, b.len); } + void write_data(bytes& b) { write_data(b.ptr, (int)b.len); } void add_to_jar_directory(const char* fname, bool store, int modtime, int len, int clen, uLong crc); void write_jar_header(const char* fname, bool store, int modtime, -- GitLab From aeaea3dd969104ce35201342583c8c74b8cbca4f Mon Sep 17 00:00:00 2001 From: swamyv Date: Mon, 18 Aug 2008 15:28:54 -0700 Subject: [PATCH 054/139] 6705893: javax.script tests should not require a js engine on OpenJDK Summary: Fixed the tests to pass with open JDK. Reviewed-by: darcy --- test/javax/script/E4XErrorTest.java | 7 +-- test/javax/script/Helper.java | 41 +++++++++++++++++ test/javax/script/JavaScriptScopeTest.java | 7 +-- test/javax/script/NullUndefinedVarTest.java | 7 +-- test/javax/script/PluggableContextTest.java | 8 +++- test/javax/script/ProviderTest.java | 5 ++- test/javax/script/RhinoExceptionTest.java | 10 +++-- test/javax/script/Test1.java | 7 +-- test/javax/script/Test2.java | 6 ++- test/javax/script/Test3.java | 9 +++- test/javax/script/Test4.java | 8 +++- test/javax/script/Test5.java | 8 +++- test/javax/script/Test6.java | 8 +++- test/javax/script/Test7.java | 8 +++- test/javax/script/Test8.java | 8 +++- test/javax/script/VersionTest.java | 7 +-- test/sun/tools/jrunscript/CheckEngine.java | 45 +++++++++++++++++++ test/sun/tools/jrunscript/common.sh | 1 + test/sun/tools/jrunscript/jrunscript-DTest.sh | 8 +++- .../tools/jrunscript/jrunscript-argsTest.sh | 8 +++- .../sun/tools/jrunscript/jrunscript-cpTest.sh | 8 +++- test/sun/tools/jrunscript/jrunscript-eTest.sh | 8 +++- test/sun/tools/jrunscript/jrunscript-fTest.sh | 8 +++- test/sun/tools/jrunscript/jrunscriptTest.sh | 8 +++- 24 files changed, 207 insertions(+), 41 deletions(-) create mode 100644 test/javax/script/Helper.java create mode 100644 test/sun/tools/jrunscript/CheckEngine.java diff --git a/test/javax/script/E4XErrorTest.java b/test/javax/script/E4XErrorTest.java index 5936fd42e..11aa5f004 100644 --- a/test/javax/script/E4XErrorTest.java +++ b/test/javax/script/E4XErrorTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6346734 + * @bug 6346734 6705893 * @summary We do *not* support E4X (ECMAScript for XML) in our * implementation. We want to throw error on XML literals * as early as possible rather than at "runtime" - i.e., when @@ -37,9 +37,10 @@ public class E4XErrorTest { public static void main(String[] args) throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); - ScriptEngine jsengine = manager.getEngineByName("js"); + ScriptEngine jsengine = Helper.getJsEngine(manager); if (jsengine == null) { - throw new RuntimeException("no js engine found"); + System.out.println("Warning: No js engine found; test vacuously passes."); + return; } // The test below depends on the error message content diff --git a/test/javax/script/Helper.java b/test/javax/script/Helper.java new file mode 100644 index 000000000..8d259cca8 --- /dev/null +++ b/test/javax/script/Helper.java @@ -0,0 +1,41 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +import javax.script.*; + +/** + * Helper class to consolidate testing requirements for a js engine. + * A js engine is required as part of Sun's product JDK. + */ +public class Helper { + private Helper() {}; // Don't instantiate + + public static ScriptEngine getJsEngine(ScriptEngineManager m) { + ScriptEngine e = m.getEngineByName("js"); + if (e == null && + System.getProperty("java.runtime.name").startsWith("Java(TM)")) { + // A js engine is requied for Sun's product JDK + throw new RuntimeException("no js engine found"); + } + return e; + } +} diff --git a/test/javax/script/JavaScriptScopeTest.java b/test/javax/script/JavaScriptScopeTest.java index 1bce7b6e0..9793d7a08 100644 --- a/test/javax/script/JavaScriptScopeTest.java +++ b/test/javax/script/JavaScriptScopeTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6346733 + * @bug 6346733 6705893 * @summary Verify that independent Bindings instances don't * get affected by default scope assignments. Also, verify * that script globals can be created and accessed from Java @@ -36,9 +36,10 @@ public class JavaScriptScopeTest { public static void main(String[] args) throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); - ScriptEngine jsengine = manager.getEngineByName("js"); + ScriptEngine jsengine = Helper.getJsEngine(manager); if (jsengine == null) { - throw new RuntimeException("no js engine found"); + System.out.println("Warning: No js engine found; test vacuously passes."); + return; } jsengine.eval("var v = 'hello';"); // Create a new scope diff --git a/test/javax/script/NullUndefinedVarTest.java b/test/javax/script/NullUndefinedVarTest.java index b07d5afdf..646009cd6 100644 --- a/test/javax/script/NullUndefinedVarTest.java +++ b/test/javax/script/NullUndefinedVarTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6346732 + * @bug 6346732 6705893 * @summary should be able to assign null and undefined * value to JavaScript global variables. */ @@ -34,9 +34,10 @@ public class NullUndefinedVarTest { public static void main(String[] args) throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); - ScriptEngine jsengine = manager.getEngineByName("js"); + ScriptEngine jsengine = Helper.getJsEngine(manager); if (jsengine == null) { - throw new RuntimeException("no js engine found"); + System.out.println("Warning: No js engine found; test vacuously passes."); + return; } jsengine.eval("var n = null; " + "if (n !== null) throw 'expecting null';" + diff --git a/test/javax/script/PluggableContextTest.java b/test/javax/script/PluggableContextTest.java index 1e6f4fa52..eeb2cb008 100644 --- a/test/javax/script/PluggableContextTest.java +++ b/test/javax/script/PluggableContextTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6398614 + * @bug 6398614 6705893 * @summary Create a user defined ScriptContext and check * that script can access variables from non-standard scopes */ @@ -35,7 +35,11 @@ public class PluggableContextTest { ScriptEngineManager m = new ScriptEngineManager(); ScriptContext ctx = new MyContext(); ctx.setAttribute("x", "hello", MyContext.APP_SCOPE); - ScriptEngine e = m.getEngineByName("js"); + ScriptEngine e = Helper.getJsEngine(m); + if (e == null) { + System.out.println("Warning: No js engine found; test vacuously passes."); + return; + } // the following reference to 'x' throws exception // if APP_SCOPE is not searched. e.eval("x", ctx); diff --git a/test/javax/script/ProviderTest.java b/test/javax/script/ProviderTest.java index 79d6f1661..9cdcccdb2 100644 --- a/test/javax/script/ProviderTest.java +++ b/test/javax/script/ProviderTest.java @@ -35,9 +35,10 @@ public class ProviderTest { if (se == null) { throw new RuntimeException("can't locate dummy engine"); } - se = manager.getEngineByName("js"); + se = Helper.getJsEngine(manager); if (se == null) { - throw new RuntimeException("can't locate JavaScript engine"); + System.out.println("Warning: No js engine found; test vacuously passes."); + return; } } } diff --git a/test/javax/script/RhinoExceptionTest.java b/test/javax/script/RhinoExceptionTest.java index 439b947b2..cf52f128a 100644 --- a/test/javax/script/RhinoExceptionTest.java +++ b/test/javax/script/RhinoExceptionTest.java @@ -23,8 +23,8 @@ /* * @test - * @bug 6474943 - * @summary Test that Rhion exception messages are + * @bug 6474943 6705893 + * @summary Test that Rhino exception messages are * available from ScriptException. */ @@ -36,7 +36,11 @@ public class RhinoExceptionTest { public static void main(String[] args) throws Exception { ScriptEngineManager m = new ScriptEngineManager(); - ScriptEngine engine = m.getEngineByName("js"); + ScriptEngine engine = Helper.getJsEngine(m); + if (engine == null) { + System.out.println("Warning: No js engine found; test vacuously passes."); + return; + } engine.put("msg", ERROR_MSG); try { engine.eval("throw new Error(msg);"); diff --git a/test/javax/script/Test1.java b/test/javax/script/Test1.java index fdfdef9bc..ba6bcebdb 100644 --- a/test/javax/script/Test1.java +++ b/test/javax/script/Test1.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6249843 + * @bug 6249843 6705893 * @summary Create JavaScript engine and execute a simple script. * Tests script engine discovery mechanism. */ @@ -35,9 +35,10 @@ public class Test1 { public static void main(String[] args) throws Exception { System.out.println("\nTest1\n"); ScriptEngineManager manager = new ScriptEngineManager(); - ScriptEngine jsengine = manager.getEngineByName("js"); + ScriptEngine jsengine = Helper.getJsEngine(manager); if (jsengine == null) { - throw new RuntimeException("no js engine found"); + System.out.println("Warning: No js engine found; test vacuously passes."); + return; } jsengine.eval(new FileReader( new File(System.getProperty("test.src", "."), "Test1.js"))); diff --git a/test/javax/script/Test2.java b/test/javax/script/Test2.java index 0a70eb1b1..7e0eef21c 100644 --- a/test/javax/script/Test2.java +++ b/test/javax/script/Test2.java @@ -50,7 +50,11 @@ public class Test2 { public static void main(String[] args) throws Exception { System.out.println("\nTest2\n"); ScriptEngineManager m = new ScriptEngineManager(); - ScriptEngine eng = m.getEngineByName("js"); + ScriptEngine eng = Helper.getJsEngine(m); + if (eng == null) { + System.out.println("Warning: No js engine found; test vacuously passes."); + return; + } eng.put("Testobj", new Testobj("Hello World")); eng.eval(new FileReader( new File(System.getProperty("test.src", "."), "Test2.js"))); diff --git a/test/javax/script/Test3.java b/test/javax/script/Test3.java index 84b610aaa..4c14f4633 100644 --- a/test/javax/script/Test3.java +++ b/test/javax/script/Test3.java @@ -4,6 +4,7 @@ * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT @@ -23,7 +24,7 @@ /* * @test - * @bug 6249843 + * @bug 6249843 6705893 * @summary Test engine and global scopes */ @@ -37,7 +38,11 @@ public class Test3 { final Reader reader = new FileReader( new File(System.getProperty("test.src", "."), "Test3.js")); ScriptEngineManager m = new ScriptEngineManager(); - final ScriptEngine engine = m.getEngineByName("js"); + final ScriptEngine engine = Helper.getJsEngine(m); + if (engine == null) { + System.out.println("Warning: No js engine found; test vacuously passes."); + return; + } Bindings en = new SimpleBindings(); engine.setBindings(en, ScriptContext.ENGINE_SCOPE); en.put("key", "engine value"); diff --git a/test/javax/script/Test4.java b/test/javax/script/Test4.java index bae255097..de6754235 100644 --- a/test/javax/script/Test4.java +++ b/test/javax/script/Test4.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6249843 + * @bug 6249843 6705893 * @summary Test script functions implementing Java interface */ @@ -34,7 +34,11 @@ public class Test4 { public static void main(String[] args) throws Exception { System.out.println("\nTest4\n"); ScriptEngineManager m = new ScriptEngineManager(); - ScriptEngine e = m.getEngineByName("js"); + ScriptEngine e = Helper.getJsEngine(m); + if (e == null) { + System.out.println("Warning: No js engine found; test vacuously passes."); + return; + } e.eval(new FileReader( new File(System.getProperty("test.src", "."), "Test4.js"))); Invocable inv = (Invocable)e; diff --git a/test/javax/script/Test5.java b/test/javax/script/Test5.java index cf32cff3e..037e82258 100644 --- a/test/javax/script/Test5.java +++ b/test/javax/script/Test5.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6249843 + * @bug 6249843 6705893 * @summary Tests engine, global scopes and scope hiding. */ @@ -34,7 +34,11 @@ public class Test5 { public static void main(String[] args) throws Exception { System.out.println("\nTest5\n"); ScriptEngineManager m = new ScriptEngineManager(); - ScriptEngine engine = m.getEngineByName("js"); + ScriptEngine engine = Helper.getJsEngine(m); + if (engine == null) { + System.out.println("Warning: No js engine found; test vacuously passes."); + return; + } Bindings g = new SimpleBindings(); Bindings e = new SimpleBindings(); g.put("key", "value in global"); diff --git a/test/javax/script/Test6.java b/test/javax/script/Test6.java index 15bbda1e0..a347dd844 100644 --- a/test/javax/script/Test6.java +++ b/test/javax/script/Test6.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6249843 + * @bug 6249843 6705893 * @summary Test basic script compilation. Value eval'ed from * compiled and interpreted scripts should be same. */ @@ -35,7 +35,11 @@ public class Test6 { public static void main(String[] args) throws Exception { System.out.println("\nTest6\n"); ScriptEngineManager m = new ScriptEngineManager(); - ScriptEngine engine = m.getEngineByName("js"); + ScriptEngine engine = Helper.getJsEngine(m); + if (engine == null) { + System.out.println("Warning: No js engine found; test vacuously passes."); + return; + } Reader reader = new FileReader( new File(System.getProperty("test.src", "."), "Test6.js")); engine.eval(reader); diff --git a/test/javax/script/Test7.java b/test/javax/script/Test7.java index 9cdd8ac3e..204883456 100644 --- a/test/javax/script/Test7.java +++ b/test/javax/script/Test7.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6249843 + * @bug 6249843 6705893 * @summary Tests importPackage and java access in script */ @@ -37,7 +37,11 @@ public class Test7 { new File(System.getProperty("test.src", "."), "Test7.js"); Reader r = new FileReader(file); ScriptEngineManager m = new ScriptEngineManager(); - ScriptEngine eng = m.getEngineByName("js"); + ScriptEngine eng = Helper.getJsEngine(m); + if (eng == null) { + System.out.println("Warning: No js engine found; test vacuously passes."); + return; + } eng.put("filename", file.getAbsolutePath()); eng.eval(r); String str = (String)eng.get("firstLine"); diff --git a/test/javax/script/Test8.java b/test/javax/script/Test8.java index 80f0a6afd..b55f849f2 100644 --- a/test/javax/script/Test8.java +++ b/test/javax/script/Test8.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6249843 + * @bug 6249843 6705893 * @summary Test invoking script function or method from Java */ @@ -34,7 +34,11 @@ public class Test8 { public static void main(String[] args) throws Exception { System.out.println("\nTest8\n"); ScriptEngineManager m = new ScriptEngineManager(); - ScriptEngine e = m.getEngineByName("js"); + ScriptEngine e = Helper.getJsEngine(m); + if (e == null) { + System.out.println("Warning: No js engine found; test vacuously passes."); + return; + } e.eval(new FileReader( new File(System.getProperty("test.src", "."), "Test8.js"))); Invocable inv = (Invocable)e; diff --git a/test/javax/script/VersionTest.java b/test/javax/script/VersionTest.java index b4fc2fc76..773a9843f 100644 --- a/test/javax/script/VersionTest.java +++ b/test/javax/script/VersionTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6346729 + * @bug 6346729 6705893 * @summary Create JavaScript engine and check language and engine version */ @@ -37,9 +37,10 @@ public class VersionTest { public static void main(String[] args) throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); - ScriptEngine jsengine = manager.getEngineByName("js"); + ScriptEngine jsengine = Helper.getJsEngine(manager); if (jsengine == null) { - throw new RuntimeException("no js engine found"); + System.out.println("Warning: No js engine found; test vacuously passes."); + return; } String langVersion = jsengine.getFactory().getLanguageVersion(); if (! langVersion.equals(JS_LANG_VERSION)) { diff --git a/test/sun/tools/jrunscript/CheckEngine.java b/test/sun/tools/jrunscript/CheckEngine.java new file mode 100644 index 000000000..5301fc4fb --- /dev/null +++ b/test/sun/tools/jrunscript/CheckEngine.java @@ -0,0 +1,45 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import javax.script.*; + +/* + * If the JDK being tested is not a Sun product JDK and a js + * engine is not present, return an exit code of 2 to indicate that + * the jrunscript tests which assume a js engine can be vacuously + * passed. + */ +public class CheckEngine { + public static void main(String... args) { + int exitCode = 0; + ScriptEngine engine = + (new ScriptEngineManager()).getEngineByName("js"); + + if (engine == null && + !(System.getProperty("java.runtime.name").startsWith("Java(TM)"))) { + exitCode = 2; + } + + System.exit(exitCode); + } +} diff --git a/test/sun/tools/jrunscript/common.sh b/test/sun/tools/jrunscript/common.sh index cfb6b0137..2f63c7e33 100644 --- a/test/sun/tools/jrunscript/common.sh +++ b/test/sun/tools/jrunscript/common.sh @@ -52,4 +52,5 @@ setup() { JRUNSCRIPT="${TESTJAVA}/bin/jrunscript" JAVAC="${TESTJAVA}/bin/javac" + JAVA="${TESTJAVA}/bin/java" } diff --git a/test/sun/tools/jrunscript/jrunscript-DTest.sh b/test/sun/tools/jrunscript/jrunscript-DTest.sh index 448d4c616..6919493a5 100644 --- a/test/sun/tools/jrunscript/jrunscript-DTest.sh +++ b/test/sun/tools/jrunscript/jrunscript-DTest.sh @@ -25,13 +25,19 @@ # @test -# @bug 6265810 +# @bug 6265810 6705893 +# @build CheckEngine # @run shell jrunscript-DTest.sh # @summary Test that output of 'jrunscript -D' . ${TESTSRC-.}/common.sh setup +${JAVA} -cp ${TESTCLASSES} CheckEngine +if [ $? -eq 2 ]; then + echo "No js engine found and engine not required; test vacuously passes." + exit 0 +fi # test whether value specifieD by -D option is passed # to script as java.lang.System property. sysProps is diff --git a/test/sun/tools/jrunscript/jrunscript-argsTest.sh b/test/sun/tools/jrunscript/jrunscript-argsTest.sh index cdced76c8..4c7282cc8 100644 --- a/test/sun/tools/jrunscript/jrunscript-argsTest.sh +++ b/test/sun/tools/jrunscript/jrunscript-argsTest.sh @@ -25,13 +25,19 @@ # @test -# @bug 6265810 +# @bug 6265810 6705893 +# @build CheckEngine # @run shell jrunscript-argsTest.sh # @summary Test passing of script arguments from command line . ${TESTSRC-.}/common.sh setup +${JAVA} -cp ${TESTCLASSES} CheckEngine +if [ $? -eq 2 ]; then + echo "No js engine found and engine not required; test vacuously passes." + exit 0 +fi # we check whether "excess" args are passed as script arguments diff --git a/test/sun/tools/jrunscript/jrunscript-cpTest.sh b/test/sun/tools/jrunscript/jrunscript-cpTest.sh index 599234db8..5f1dde4b1 100644 --- a/test/sun/tools/jrunscript/jrunscript-cpTest.sh +++ b/test/sun/tools/jrunscript/jrunscript-cpTest.sh @@ -25,13 +25,19 @@ # @test -# @bug 6265810 +# @bug 6265810 6705893 +# @build CheckEngine # @run shell jrunscript-cpTest.sh # @summary Test -cp option to set classpath . ${TESTSRC-.}/common.sh setup +${JAVA} -cp ${TESTCLASSES} CheckEngine +if [ $? -eq 2 ]; then + echo "No js engine found and engine not required; test vacuously passes." + exit 0 +fi rm -f Hello.class ${JAVAC} ${TESTSRC}/Hello.java -d . diff --git a/test/sun/tools/jrunscript/jrunscript-eTest.sh b/test/sun/tools/jrunscript/jrunscript-eTest.sh index fb67830cf..52aee2213 100644 --- a/test/sun/tools/jrunscript/jrunscript-eTest.sh +++ b/test/sun/tools/jrunscript/jrunscript-eTest.sh @@ -25,13 +25,19 @@ # @test -# @bug 6265810 +# @bug 6265810 6705893 +# @build CheckEngine # @run shell jrunscript-eTest.sh # @summary Test that output of 'jrunscript -e' matches the dash-e.out file . ${TESTSRC-.}/common.sh setup +${JAVA} -cp ${TESTCLASSES} CheckEngine +if [ $? -eq 2 ]; then + echo "No js engine found and engine not required; test vacuously passes." + exit 0 +fi rm -f jrunscript-eTest.out 2>/dev/null ${JRUNSCRIPT} -e "println('hello')" > jrunscript-eTest.out 2>&1 diff --git a/test/sun/tools/jrunscript/jrunscript-fTest.sh b/test/sun/tools/jrunscript/jrunscript-fTest.sh index a5eb5c7d2..3dfe64b21 100644 --- a/test/sun/tools/jrunscript/jrunscript-fTest.sh +++ b/test/sun/tools/jrunscript/jrunscript-fTest.sh @@ -25,13 +25,19 @@ # @test -# @bug 6265810 +# @bug 6265810 6705893 +# @build CheckEngine # @run shell jrunscript-fTest.sh # @summary Test that output of 'jrunscript -f' matches the dash-f.out file . ${TESTSRC-.}/common.sh setup +${JAVA} -cp ${TESTCLASSES} CheckEngine +if [ $? -eq 2 ]; then + echo "No js engine found and engine not required; test vacuously passes." + exit 0 +fi rm -f jrunscript-fTest.out 2>/dev/null ${JRUNSCRIPT} -f ${TESTSRC}/hello.js > jrunscript-fTest.out 2>&1 diff --git a/test/sun/tools/jrunscript/jrunscriptTest.sh b/test/sun/tools/jrunscript/jrunscriptTest.sh index 0e17b771c..64cd14f58 100644 --- a/test/sun/tools/jrunscript/jrunscriptTest.sh +++ b/test/sun/tools/jrunscript/jrunscriptTest.sh @@ -25,13 +25,19 @@ # @test -# @bug 6265810 +# @bug 6265810 6705893 +# @build CheckEngine # @run shell jrunscriptTest.sh # @summary Test that output of 'jrunscript' interactive matches the repl.out file . ${TESTSRC-.}/common.sh setup +${JAVA} -cp ${TESTCLASSES} CheckEngine +if [ $? -eq 2 ]; then + echo "No js engine found and engine not required; test vacuously passes." + exit 0 +fi rm -f jrunscriptTest.out 2>/dev/null ${JRUNSCRIPT} > jrunscriptTest.out 2>&1 < Date: Tue, 19 Aug 2008 07:50:03 -0700 Subject: [PATCH 055/139] 6614210: JPRT Windows 32bit msival2 build failure when building 'install' workspace Summary: suppresses wscript's modal dialog on error and no msi validation for jprt. Reviewed-by: ohair, jmelvin --- make/common/shared/Defs-windows.gmk | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/make/common/shared/Defs-windows.gmk b/make/common/shared/Defs-windows.gmk index 250604136..d0be243d8 100644 --- a/make/common/shared/Defs-windows.gmk +++ b/make/common/shared/Defs-windows.gmk @@ -539,6 +539,8 @@ else WSCRIPT :=$(call FileExists,$(_WSCRIPT1),$(_WSCRIPT2)) endif WSCRIPT:=$(call AltCheckSpaces,WSCRIPT) +# batch mode no modal dialogs on errors, please. +WSCRIPT += -B # CSCRIPT: path to cscript.exe (used in creating install bundles) ifdef ALT_CSCRIPT @@ -561,6 +563,10 @@ else MSIVAL2 :=$(call FileExists,$(_MSIVAL2_1),$(_MSIVAL2_2)) endif MSIVAL2:=$(call AltCheckSpaces,MSIVAL2) +# suppress msival2 checks, as it hangs jprt builds +ifdef SKIP_MSIVAL2 + MSIVAL2 := $(ECHO) +endif # LOGOCUB: path to cub file for (used in validating install msi files) ifdef ALT_LOGOCUB -- GitLab From 904e3c096a9e414caae8bdd14efd2579957236c8 Mon Sep 17 00:00:00 2001 From: swamyv Date: Tue, 19 Aug 2008 12:46:34 -0700 Subject: [PATCH 056/139] 6736461: ThreadMXBean Locks.java fails intermittently. Summary: Fixed the test to wait for the right state before calling check thread information. Reviewed-by: jjh --- test/java/lang/management/ThreadMXBean/Locks.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/java/lang/management/ThreadMXBean/Locks.java b/test/java/lang/management/ThreadMXBean/Locks.java index 9819375d1..cd81d89f3 100644 --- a/test/java/lang/management/ThreadMXBean/Locks.java +++ b/test/java/lang/management/ThreadMXBean/Locks.java @@ -197,8 +197,12 @@ public class Locks { synchronized (ready) { // wait until WaitingThread about to wait for objC thrsync.waitForSignal(); - // give chance to enter wait. - goSleep(100); + + int retryCount = 0; + while (waiter.getState() != Thread.State.WAITING + && retryCount++ < 500) { + goSleep(100); + } checkBlockedObject(waiter, objC, null, Thread.State.WAITING); synchronized (objC) { -- GitLab From 511680fa278cd32291cec5bd22f3823d0700ad84 Mon Sep 17 00:00:00 2001 From: martin Date: Wed, 20 Aug 2008 13:45:12 -0700 Subject: [PATCH 057/139] 6739302: Check that deserialization preserves EnumSet integrity Reviewed-by: dl, chegar Contributed-by: jjb@google.com --- src/share/classes/java/util/EnumSet.java | 7 ++ test/java/util/EnumSet/BogusEnumSet.java | 93 ++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 test/java/util/EnumSet/BogusEnumSet.java diff --git a/src/share/classes/java/util/EnumSet.java b/src/share/classes/java/util/EnumSet.java index d5c8d23df..5b92a972b 100644 --- a/src/share/classes/java/util/EnumSet.java +++ b/src/share/classes/java/util/EnumSet.java @@ -432,4 +432,11 @@ public abstract class EnumSet> extends AbstractSet Object writeReplace() { return new SerializationProxy(this); } + + // readObject method for the serialization proxy pattern + // See Effective Java, Second Ed., Item 78. + private void readObject(java.io.ObjectInputStream stream) + throws java.io.InvalidObjectException { + throw new java.io.InvalidObjectException("Proxy required"); + } } diff --git a/test/java/util/EnumSet/BogusEnumSet.java b/test/java/util/EnumSet/BogusEnumSet.java new file mode 100644 index 000000000..3fcb0a789 --- /dev/null +++ b/test/java/util/EnumSet/BogusEnumSet.java @@ -0,0 +1,93 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6739302 + * @summary Check that deserialization preserves EnumSet integrity + * @author Josh Bloch + */ + +import java.util.*; +import java.io.*; + +public class BogusEnumSet { + public static void main(String[] args) throws Throwable { + byte[] serializedForm = { + (byte)0xac, (byte)0xed, 0x0, 0x5, 0x73, 0x72, 0x0, 0x18, + 0x6a, 0x61, 0x76, 0x61, 0x2e, 0x75, 0x74, 0x69, + 0x6c, 0x2e, 0x52, 0x65, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x45, + 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x74, 0x2f, 0x58, 0x6f, (byte)0xc7, + 0x7e, (byte)0xb0, (byte)0xd0, 0x7e, 0x2, 0x0, 0x1, 0x4a, 0x0, 0x8, + 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x78, 0x72, 0x0, + 0x11, 0x6a, 0x61, 0x76, 0x61, 0x2e, 0x75, 0x74, 0x69, + 0x6c, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x74, 0xe, + 0x3, 0x21, 0x6a, (byte)0xcd, (byte)0x8c, 0x29, (byte)0xdd, 0x2, + 0x0, 0x2, 0x4c, 0x0, 0xb, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x74, 0x0, 0x11, 0x4c, 0x6a, 0x61, 0x76, + 0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x43, 0x6c, 0x61, 0x73, + 0x73, 0x3b, 0x5b, 0x0, 0x8, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, + 0x73, 0x65, 0x74, 0x0, 0x11, 0x5b, 0x4c, 0x6a, 0x61, 0x76, 0x61, + 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x45, 0x6e, 0x75, 0x6d, 0x3b, + 0x78, 0x70, 0x76, 0x72, 0x0, 0x16, 0x6a, 0x61, 0x76, 0x61, 0x2e, + 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x54, 0x68, 0x72, 0x65, 0x61, + 0x64, 0x24, 0x53, 0x74, 0x61, 0x74, 0x65, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x12, 0x0, 0x0, 0x78, 0x72, 0x0, 0xe, 0x6a, 0x61, + 0x76, 0x61, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x45, 0x6e, 0x75, + 0x6d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x0, 0x0, 0x78, + 0x70, 0x75, 0x72, 0x0, 0x19, 0x5b, 0x4c, 0x6a, 0x61, 0x76, 0x61, + 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x54, 0x68, 0x72, 0x65, 0x61, + 0x64, 0x24, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3b, 0x68, (byte)0xa3, + (byte)0xb5, (byte)0xd5, 0x11, 0x7d, 0x1b, (byte)0xb3, 0x2, 0x0, + 0x0, 0x78, 0x70, 0x0, 0x0, 0x0, 0x6, 0x7e, 0x71, 0x0, 0x7e, 0x0, + 0x5, 0x74, 0x0, 0x3, 0x4e, 0x45, 0x57, 0x7e, 0x71, 0x0, 0x7e, 0x0, + 0x5, 0x74, 0x0, 0x8, 0x52, 0x55, 0x4e, 0x4e, 0x41, 0x42, 0x4c, 0x45, + 0x7e, 0x71, 0x0, 0x7e, 0x0, 0x5, 0x74, 0x0, 0x7, 0x42, 0x4c, 0x4f, + 0x43, 0x4b, 0x45, 0x44, 0x7e, 0x71, 0x0, 0x7e, 0x0, 0x5, 0x74, 0x0, + 0x7, 0x57, 0x41, 0x49, 0x54, 0x49, 0x4e, 0x47, 0x7e, 0x71, 0x0, + 0x7e, 0x0, 0x5, 0x74, 0x0, 0xd, 0x54, 0x49, 0x4d, 0x45, 0x44, + 0x5f, 0x57, 0x41, 0x49, 0x54, 0x49, 0x4e, 0x47, 0x7e, 0x71, 0x0, + 0x7e, 0x0, 0x5, 0x74, 0x0, 0xa, 0x54, 0x45, 0x52, 0x4d, 0x49, + 0x4e, 0x41, 0x54, 0x45, 0x44, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff + }; + + try { + // Should fail, but instead creates corrupt EnumSet + @SuppressWarnings("unchecked") + EnumSet es = (EnumSet) + deserialize(serializedForm); + + // Demonstrates corruption + System.out.println("Enum size: " + Thread.State.values().length); // 6 + System.out.println("Set size: " + es.size()); // 64 + System.out.println("Set: " + es); // Throws IndexOutOfBoundsException + throw new AssertionError("Expected exception InvalidObjectException not thrown"); + } catch (java.io.InvalidObjectException _) { /* OK */ } + } + + private static Object deserialize(byte[] sf) throws Throwable { + return new ObjectInputStream( + new ByteArrayInputStream(sf)) + .readObject(); + } +} -- GitLab From ef416a636ff5a8bb283e5e3131bdcc5e45596664 Mon Sep 17 00:00:00 2001 From: michaelm Date: Thu, 21 Aug 2008 10:04:55 -0700 Subject: [PATCH 058/139] 6258215: Num of backlog in ServerSocket(int, int) should be mentioned more explicitly Summary: updated javadoc Reviewed-by: chegar --- src/share/classes/java/net/ServerSocket.java | 36 ++++++++++++------- .../javax/net/ssl/SSLServerSocket.java | 26 ++++++++------ 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/share/classes/java/net/ServerSocket.java b/src/share/classes/java/net/ServerSocket.java index d28d874b2..983c737c3 100644 --- a/src/share/classes/java/net/ServerSocket.java +++ b/src/share/classes/java/net/ServerSocket.java @@ -142,14 +142,18 @@ class ServerSocket implements java.io.Closeable { * as its argument to ensure the operation is allowed. * This could result in a SecurityException. * - *

      The backlog argument must be a positive - * value greater than 0. If the value passed is equal or less - * than 0, then the default value will be assumed. + * The backlog argument is the requested maximum number of + * pending connections on the socket. Its exact semantics are implementation + * specific. In particular, an implementation may impose a maximum length + * or may choose to ignore the parameter altogther. The value provided + * should be greater than 0. If it is less than or equal to + * 0, then an implementation specific default will be used. *

      * * @param port the port number, or 0 to use a port * number that is automatically allocated. - * @param backlog the maximum length of the queue. + * @param backlog requested maximum length of the queue of incoming + * connections. * * @exception IOException if an I/O error occurs when opening the socket. * @exception SecurityException @@ -187,13 +191,17 @@ class ServerSocket implements java.io.Closeable { * as its argument to ensure the operation is allowed. * This could result in a SecurityException. * - *

      The backlog argument must be a positive - * value greater than 0. If the value passed is equal or less - * than 0, then the default value will be assumed. + * The backlog argument is the requested maximum number of + * pending connections on the socket. Its exact semantics are implementation + * specific. In particular, an implementation may impose a maximum length + * or may choose to ignore the parameter altogther. The value provided + * should be greater than 0. If it is less than or equal to + * 0, then an implementation specific default will be used. *

      * @param port the port number, or 0 to use a port * number that is automatically allocated. - * @param backlog the listen backlog + * @param backlog requested maximum length of the queue of incoming + * connections. * @param bindAddr the local InetAddress the server will bind to * * @throws SecurityException if a security manager exists and @@ -321,11 +329,15 @@ class ServerSocket implements java.io.Closeable { * If the address is null, then the system will pick up * an ephemeral port and a valid local address to bind the socket. *

      - * The backlog argument must be a positive - * value greater than 0. If the value passed is equal or less - * than 0, then the default value will be assumed. + * The backlog argument is the requested maximum number of + * pending connections on the socket. Its exact semantics are implementation + * specific. In particular, an implementation may impose a maximum length + * or may choose to ignore the parameter altogther. The value provided + * should be greater than 0. If it is less than or equal to + * 0, then an implementation specific default will be used. * @param endpoint The IP address & port number to bind to. - * @param backlog The listen backlog length. + * @param backlog requested maximum length of the queue of + * incoming connections. * @throws IOException if the bind operation fails, or if the socket * is already bound. * @throws SecurityException if a SecurityManager is present and diff --git a/src/share/classes/javax/net/ssl/SSLServerSocket.java b/src/share/classes/javax/net/ssl/SSLServerSocket.java index cdeef2baf..cafc72df8 100644 --- a/src/share/classes/javax/net/ssl/SSLServerSocket.java +++ b/src/share/classes/javax/net/ssl/SSLServerSocket.java @@ -108,9 +108,12 @@ public abstract class SSLServerSocket extends ServerSocket *

      * A port number of 0 creates a socket on any free port. *

      - * The backlog argument must be a positive - * value greater than 0. If the value passed if equal or less - * than 0, then the default value will be assumed. + * The backlog argument is the requested maximum number of + * pending connections on the socket. Its exact semantics are implementation + * specific. In particular, an implementation may impose a maximum length + * or may choose to ignore the parameter altogther. The value provided + * should be greater than 0. If it is less than or equal to + * 0, then an implementation specific default will be used. *

      * If there is a security manager, its checkListen * method is called with the port argument as its @@ -118,8 +121,8 @@ public abstract class SSLServerSocket extends ServerSocket * in a SecurityException. * * @param port the port on which to listen - * @param backlog how many connections may be pending before - * the system should start rejecting new requests + * @param backlog requested maximum length of the queue of incoming + * connections. * @throws IOException if an I/O error occurs when creating the socket * @throws SecurityException if a security manager exists and its * checkListen method doesn't allow the operation. @@ -150,16 +153,19 @@ public abstract class SSLServerSocket extends ServerSocket *

      * A port number of 0 creates a socket on any free port. *

      - *

      The backlog argument must be a positive - * value greater than 0. If the value passed if equal or less - * than 0, then the default value will be assumed. + * The backlog argument is the requested maximum number of + * pending connections on the socket. Its exact semantics are implementation + * specific. In particular, an implementation may impose a maximum length + * or may choose to ignore the parameter altogther. The value provided + * should be greater than 0. If it is less than or equal to + * 0, then an implementation specific default will be used. *

      * If address is null, it will default accepting connections * on any/all local addresses. * * @param port the port on which to listen - * @param backlog how many connections may be pending before - * the system should start rejecting new requests + * @param backlog requested maximum length of the queue of incoming + * connections. * @param address the address of the network interface through * which connections will be accepted * @throws IOException if an I/O error occurs when creating the socket -- GitLab From 894372048cdcffe0c704c9867b0b73f271bbd62e Mon Sep 17 00:00:00 2001 From: swamyv Date: Fri, 22 Aug 2008 10:37:03 -0700 Subject: [PATCH 059/139] 6653883: jmap with no option should print mmap instead of heap information. Summary: Changed the default option of jmap to print mmap. Reviewed-by: jjh --- src/share/classes/sun/tools/jmap/JMap.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/share/classes/sun/tools/jmap/JMap.java b/src/share/classes/sun/tools/jmap/JMap.java index b770c4cd8..39100523c 100644 --- a/src/share/classes/sun/tools/jmap/JMap.java +++ b/src/share/classes/sun/tools/jmap/JMap.java @@ -56,7 +56,7 @@ public class JMap { private static String FORCE_SA_OPTION = "-F"; // Default option (if nothing provided) - private static String DEFAULT_OPTION = "-heap"; + private static String DEFAULT_OPTION = "-pmap"; public static void main(String[] args) throws Exception { if (args.length == 0) { @@ -147,6 +147,7 @@ public class JMap { // Invoke SA tool with the given arguments private static void runTool(String option, String args[]) throws Exception { String[][] tools = { + { "-pmap", "sun.jvm.hotspot.tools.PMap" }, { "-heap", "sun.jvm.hotspot.tools.HeapSummary" }, { "-heap:format=b", "sun.jvm.hotspot.tools.HeapDumper" }, { "-histo", "sun.jvm.hotspot.tools.ObjectHistogram" }, -- GitLab From 4f09cdc23111627f68588876133f598ab393cd9e Mon Sep 17 00:00:00 2001 From: ohair Date: Fri, 22 Aug 2008 12:24:27 -0700 Subject: [PATCH 060/139] 6732421: Removed old javavm and Classic VM files from the jdk7 sources Reviewed-by: alanb --- make/common/Defs.gmk | 2 +- make/java/verify/Makefile | 8 +- make/netbeans/awt2d/README | 1 - .../GenerateCharacter/check_class.c.template | 3 +- src/share/back/debugDispatch.c | 3 +- src/share/back/error_messages.c | 3 +- src/share/back/inStream.c | 3 +- src/share/back/outStream.h | 4 +- .../InstrumentationImplNativeMethods.c | 5 +- src/share/instrument/JPLISAgent.c | 3 +- src/share/javavm/export/jvm.h | 103 +---- src/share/javavm/include/opcodes.h | 268 ------------ src/share/javavm/include/opcodes.length | 283 ------------ src/share/javavm/include/opcodes.list | 301 ------------- src/share/javavm/include/opcodes.weight | 283 ------------ src/share/javavm/include/opcodes.wide | 256 ----------- src/share/javavm/include/sys_api.h | 178 -------- src/share/javavm/include/typedefs.h | 120 ----- src/share/native/common/check_code.c | 414 +++++++++--------- src/share/native/common/check_format.c | 4 +- src/solaris/back/util_md.h | 5 +- src/solaris/instrument/FileSystemSupport_md.h | 5 +- src/solaris/javavm/export/jvm_md.h | 4 +- src/solaris/javavm/include/typedefs_md.h | 174 -------- src/solaris/native/common/gdefs_md.h | 15 +- src/solaris/native/common/jlong_md.h | 4 +- src/windows/back/util_md.h | 3 +- src/windows/hpi/src/socket_md.c | 3 +- src/windows/hpi/src/threads_md.c | 3 +- src/windows/instrument/FileSystemSupport_md.h | 3 +- src/windows/javavm/export/jvm_md.h | 14 + src/windows/javavm/include/typedefs_md.h | 125 ------ src/windows/native/java/net/net_util_md.c | 3 +- 33 files changed, 260 insertions(+), 2346 deletions(-) delete mode 100644 src/share/javavm/include/opcodes.h delete mode 100644 src/share/javavm/include/opcodes.length delete mode 100644 src/share/javavm/include/opcodes.list delete mode 100644 src/share/javavm/include/opcodes.weight delete mode 100644 src/share/javavm/include/opcodes.wide delete mode 100644 src/share/javavm/include/sys_api.h delete mode 100644 src/share/javavm/include/typedefs.h delete mode 100644 src/solaris/javavm/include/typedefs_md.h delete mode 100644 src/windows/javavm/include/typedefs_md.h diff --git a/make/common/Defs.gmk b/make/common/Defs.gmk index a0cb9481c..0c7e34014 100644 --- a/make/common/Defs.gmk +++ b/make/common/Defs.gmk @@ -451,7 +451,7 @@ vpath %.$(OBJECT_SUFFIX) $(OBJDIR) # namely jni.h, jvm.h, and jni_utils.h, plus their platform-specific # relatives. # -VPATH.h = $(PLATFORM_SRC)/javavm/export$(CLASSPATH_SEPARATOR)$(SHARE_SRC)/javavm/export$(CLASSPATH_SEPARATOR)$(SHARE_SRC)/javavm/include$(CLASSPATH_SEPARATOR)$(PLATFORM_SRC)/javavm/include +VPATH.h = $(PLATFORM_SRC)/javavm/export$(CLASSPATH_SEPARATOR)$(SHARE_SRC)/javavm/export vpath %.h $(VPATH.h) # diff --git a/make/java/verify/Makefile b/make/java/verify/Makefile index c647d5085..24bcc0f48 100644 --- a/make/java/verify/Makefile +++ b/make/java/verify/Makefile @@ -1,5 +1,5 @@ # -# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -52,12 +52,6 @@ FILES_c = \ check_code.c \ check_format.c -# -# libverify.so needs these 2 header files (opcodes.h opcodes.length) -# from the VM. -# -CPPFLAGS += -I$(SHARE_SRC)/javavm/include - # # Targets. # diff --git a/make/netbeans/awt2d/README b/make/netbeans/awt2d/README index a990726b7..040c9e769 100644 --- a/make/netbeans/awt2d/README +++ b/make/netbeans/awt2d/README @@ -145,7 +145,6 @@ Notes on using CND (C/C++ pack) with this project and NetBeans. (a somewhat complete list of awt and 2d native directories on windows): ../../src/share/javavm/export; - ../../src/share/javavm/include; ../../src/share/native/common; ../../src/share/native/sun/awt/debug; ../../src/share/native/sun/awt/image/cvutils; diff --git a/make/tools/GenerateCharacter/check_class.c.template b/make/tools/GenerateCharacter/check_class.c.template index d9a880f09..02307e02a 100644 --- a/make/tools/GenerateCharacter/check_class.c.template +++ b/make/tools/GenerateCharacter/check_class.c.template @@ -1,5 +1,5 @@ /* - * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,6 @@ #include "bool.h" #include "utf.h" #include "tree.h" -#include "sys_api.h" extern bool_t verify_class_codes(ClassClass *cb); diff --git a/src/share/back/debugDispatch.c b/src/share/back/debugDispatch.c index 98a86fe58..fa6337990 100644 --- a/src/share/back/debugDispatch.c +++ b/src/share/back/debugDispatch.c @@ -1,5 +1,5 @@ /* - * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,6 @@ #include "ArrayReferenceImpl.h" #include "EventRequestImpl.h" #include "StackFrameImpl.h" -#include "typedefs.h" static void **l1Array; diff --git a/src/share/back/error_messages.c b/src/share/back/error_messages.c index f40af69a3..30acacd4e 100644 --- a/src/share/back/error_messages.c +++ b/src/share/back/error_messages.c @@ -1,5 +1,5 @@ /* - * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,7 +48,6 @@ #include "util.h" #include "proc_md.h" -#include "typedefs.h" /* Maximim length of a message */ #define MAX_MESSAGE_LEN MAXPATHLEN*2+512 diff --git a/src/share/back/inStream.c b/src/share/back/inStream.c index 45534fcdc..1f5b685c5 100644 --- a/src/share/back/inStream.c +++ b/src/share/back/inStream.c @@ -1,5 +1,5 @@ /* - * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ #include "bag.h" #include "commonRef.h" #include "FrameID.h" -#include "typedefs.h" #define INITIAL_REF_ALLOC 50 #define SMALLEST(a, b) ((a) < (b)) ? (a) : (b) diff --git a/src/share/back/outStream.h b/src/share/back/outStream.h index a57cc4711..83d1eb10f 100644 --- a/src/share/back/outStream.h +++ b/src/share/back/outStream.h @@ -1,5 +1,5 @@ /* - * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,8 +26,6 @@ #ifndef JDWP_OUTSTREAM_H #define JDWP_OUTSTREAM_H -#include "typedefs.h" - #include "transport.h" #include "FrameID.h" diff --git a/src/share/instrument/InstrumentationImplNativeMethods.c b/src/share/instrument/InstrumentationImplNativeMethods.c index f4a123969..7acd7298b 100644 --- a/src/share/instrument/InstrumentationImplNativeMethods.c +++ b/src/share/instrument/InstrumentationImplNativeMethods.c @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,15 +23,14 @@ * have any questions. */ - #include #include "JPLISAgent.h" #include "JPLISAssert.h" #include "Utilities.h" #include "JavaExceptions.h" +#include "FileSystemSupport.h" /* For uintptr_t */ #include "sun_instrument_InstrumentationImpl.h" -#include "typedefs.h" /* * Copyright 2003 Wily Technology, Inc. diff --git a/src/share/instrument/JPLISAgent.c b/src/share/instrument/JPLISAgent.c index 9e1a0b098..e34beac79 100644 --- a/src/share/instrument/JPLISAgent.c +++ b/src/share/instrument/JPLISAgent.c @@ -38,10 +38,9 @@ #include "JavaExceptions.h" #include "EncodingSupport.h" -#include "FileSystemSupport.h" /* MAXPATHLEN */ +#include "FileSystemSupport.h" /* For MAXPATHLEN & uintptr_t */ #include "sun_instrument_InstrumentationImpl.h" -#include "typedefs.h" /* * The JPLISAgent manages the initialization all of the Java programming language Agents. diff --git a/src/share/javavm/export/jvm.h b/src/share/javavm/export/jvm.h index 3778d4309..df8f27e66 100644 --- a/src/share/javavm/export/jvm.h +++ b/src/share/javavm/export/jvm.h @@ -948,90 +948,8 @@ JVM_ReleaseUTF(const char *utf); JNIEXPORT jboolean JNICALL JVM_IsSameClassPackage(JNIEnv *env, jclass class1, jclass class2); -/* Constants in class files */ - -#define JVM_ACC_PUBLIC 0x0001 /* visible to everyone */ -#define JVM_ACC_PRIVATE 0x0002 /* visible only to the defining class */ -#define JVM_ACC_PROTECTED 0x0004 /* visible to subclasses */ -#define JVM_ACC_STATIC 0x0008 /* instance variable is static */ -#define JVM_ACC_FINAL 0x0010 /* no further subclassing, overriding */ -#define JVM_ACC_SYNCHRONIZED 0x0020 /* wrap method call in monitor lock */ -#define JVM_ACC_SUPER 0x0020 /* funky handling of invokespecial */ -#define JVM_ACC_VOLATILE 0x0040 /* can not cache in registers */ -#define JVM_ACC_BRIDGE 0x0040 /* bridge method generated by compiler */ -#define JVM_ACC_TRANSIENT 0x0080 /* not persistant */ -#define JVM_ACC_VARARGS 0x0080 /* method declared with variable number of args */ -#define JVM_ACC_NATIVE 0x0100 /* implemented in C */ -#define JVM_ACC_INTERFACE 0x0200 /* class is an interface */ -#define JVM_ACC_ABSTRACT 0x0400 /* no definition provided */ -#define JVM_ACC_STRICT 0x0800 /* strict floating point */ -#define JVM_ACC_SYNTHETIC 0x1000 /* compiler-generated class, method or field */ - -#define JVM_ACC_ANNOTATION 0x2000 /* annotation type */ -#define JVM_ACC_ENUM 0x4000 /* field is declared as element of enum */ - -#define JVM_ACC_PUBLIC_BIT 0 -#define JVM_ACC_PRIVATE_BIT 1 -#define JVM_ACC_PROTECTED_BIT 2 -#define JVM_ACC_STATIC_BIT 3 -#define JVM_ACC_FINAL_BIT 4 -#define JVM_ACC_SYNCHRONIZED_BIT 5 -#define JVM_ACC_SUPER_BIT 5 -#define JVM_ACC_VOLATILE_BIT 6 -#define JVM_ACC_BRIDGE_BIT 6 -#define JVM_ACC_TRANSIENT_BIT 7 -#define JVM_ACC_VARARGS_BIT 7 -#define JVM_ACC_NATIVE_BIT 8 -#define JVM_ACC_INTERFACE_BIT 9 -#define JVM_ACC_ABSTRACT_BIT 10 -#define JVM_ACC_STRICT_BIT 11 -#define JVM_ACC_SYNTHETIC_BIT 12 -#define JVM_ACC_ANNOTATION_BIT 13 -#define JVM_ACC_ENUM_BIT 14 - -enum { - JVM_CONSTANT_Utf8 = 1, - JVM_CONSTANT_Unicode, /* unused */ - JVM_CONSTANT_Integer, - JVM_CONSTANT_Float, - JVM_CONSTANT_Long, - JVM_CONSTANT_Double, - JVM_CONSTANT_Class, - JVM_CONSTANT_String, - JVM_CONSTANT_Fieldref, - JVM_CONSTANT_Methodref, - JVM_CONSTANT_InterfaceMethodref, - JVM_CONSTANT_NameAndType -}; - -/* Used in the newarray instruction. */ - -#define JVM_T_BOOLEAN 4 -#define JVM_T_CHAR 5 -#define JVM_T_FLOAT 6 -#define JVM_T_DOUBLE 7 -#define JVM_T_BYTE 8 -#define JVM_T_SHORT 9 -#define JVM_T_INT 10 -#define JVM_T_LONG 11 - -/* JVM method signatures */ - -#define JVM_SIGNATURE_ARRAY '[' -#define JVM_SIGNATURE_BYTE 'B' -#define JVM_SIGNATURE_CHAR 'C' -#define JVM_SIGNATURE_CLASS 'L' -#define JVM_SIGNATURE_ENDCLASS ';' -#define JVM_SIGNATURE_ENUM 'E' -#define JVM_SIGNATURE_FLOAT 'F' -#define JVM_SIGNATURE_DOUBLE 'D' -#define JVM_SIGNATURE_FUNC '(' -#define JVM_SIGNATURE_ENDFUNC ')' -#define JVM_SIGNATURE_INT 'I' -#define JVM_SIGNATURE_LONG 'J' -#define JVM_SIGNATURE_SHORT 'S' -#define JVM_SIGNATURE_VOID 'V' -#define JVM_SIGNATURE_BOOLEAN 'Z' +/* Get classfile constants */ +#include "classfile_constants.h" /* * A function defined by the byte-code verifier and called by the VM. @@ -1329,23 +1247,6 @@ JVM_GetSockOpt(jint fd, int level, int optname, char *optval, int *optlen); JNIEXPORT jint JNICALL JVM_SetSockOpt(jint fd, int level, int optname, const char *optval, int optlen); -/* - * These routines are only reentrant on Windows - */ - -#ifdef WIN32 - -JNIEXPORT struct protoent * JNICALL -JVM_GetProtoByName(char* name); - -JNIEXPORT struct hostent* JNICALL -JVM_GetHostByAddr(const char* name, int len, int type); - -JNIEXPORT struct hostent* JNICALL -JVM_GetHostByName(char* name); - -#endif /* _WINDOWS */ - JNIEXPORT int JNICALL JVM_GetHostName(char* name, int namelen); diff --git a/src/share/javavm/include/opcodes.h b/src/share/javavm/include/opcodes.h deleted file mode 100644 index e13a4b203..000000000 --- a/src/share/javavm/include/opcodes.h +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright 1998-2003 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -#ifndef _JAVASOFT_OPCODES_H_ -#define _JAVASOFT_OPCODES_H_ - -typedef enum { - opc_nop = 0, - opc_aconst_null = 1, - opc_iconst_m1 = 2, - opc_iconst_0 = 3, - opc_iconst_1 = 4, - opc_iconst_2 = 5, - opc_iconst_3 = 6, - opc_iconst_4 = 7, - opc_iconst_5 = 8, - opc_lconst_0 = 9, - opc_lconst_1 = 10, - opc_fconst_0 = 11, - opc_fconst_1 = 12, - opc_fconst_2 = 13, - opc_dconst_0 = 14, - opc_dconst_1 = 15, - opc_bipush = 16, - opc_sipush = 17, - opc_ldc = 18, - opc_ldc_w = 19, - opc_ldc2_w = 20, - opc_iload = 21, - opc_lload = 22, - opc_fload = 23, - opc_dload = 24, - opc_aload = 25, - opc_iload_0 = 26, - opc_iload_1 = 27, - opc_iload_2 = 28, - opc_iload_3 = 29, - opc_lload_0 = 30, - opc_lload_1 = 31, - opc_lload_2 = 32, - opc_lload_3 = 33, - opc_fload_0 = 34, - opc_fload_1 = 35, - opc_fload_2 = 36, - opc_fload_3 = 37, - opc_dload_0 = 38, - opc_dload_1 = 39, - opc_dload_2 = 40, - opc_dload_3 = 41, - opc_aload_0 = 42, - opc_aload_1 = 43, - opc_aload_2 = 44, - opc_aload_3 = 45, - opc_iaload = 46, - opc_laload = 47, - opc_faload = 48, - opc_daload = 49, - opc_aaload = 50, - opc_baload = 51, - opc_caload = 52, - opc_saload = 53, - opc_istore = 54, - opc_lstore = 55, - opc_fstore = 56, - opc_dstore = 57, - opc_astore = 58, - opc_istore_0 = 59, - opc_istore_1 = 60, - opc_istore_2 = 61, - opc_istore_3 = 62, - opc_lstore_0 = 63, - opc_lstore_1 = 64, - opc_lstore_2 = 65, - opc_lstore_3 = 66, - opc_fstore_0 = 67, - opc_fstore_1 = 68, - opc_fstore_2 = 69, - opc_fstore_3 = 70, - opc_dstore_0 = 71, - opc_dstore_1 = 72, - opc_dstore_2 = 73, - opc_dstore_3 = 74, - opc_astore_0 = 75, - opc_astore_1 = 76, - opc_astore_2 = 77, - opc_astore_3 = 78, - opc_iastore = 79, - opc_lastore = 80, - opc_fastore = 81, - opc_dastore = 82, - opc_aastore = 83, - opc_bastore = 84, - opc_castore = 85, - opc_sastore = 86, - opc_pop = 87, - opc_pop2 = 88, - opc_dup = 89, - opc_dup_x1 = 90, - opc_dup_x2 = 91, - opc_dup2 = 92, - opc_dup2_x1 = 93, - opc_dup2_x2 = 94, - opc_swap = 95, - opc_iadd = 96, - opc_ladd = 97, - opc_fadd = 98, - opc_dadd = 99, - opc_isub = 100, - opc_lsub = 101, - opc_fsub = 102, - opc_dsub = 103, - opc_imul = 104, - opc_lmul = 105, - opc_fmul = 106, - opc_dmul = 107, - opc_idiv = 108, - opc_ldiv = 109, - opc_fdiv = 110, - opc_ddiv = 111, - opc_irem = 112, - opc_lrem = 113, - opc_frem = 114, - opc_drem = 115, - opc_ineg = 116, - opc_lneg = 117, - opc_fneg = 118, - opc_dneg = 119, - opc_ishl = 120, - opc_lshl = 121, - opc_ishr = 122, - opc_lshr = 123, - opc_iushr = 124, - opc_lushr = 125, - opc_iand = 126, - opc_land = 127, - opc_ior = 128, - opc_lor = 129, - opc_ixor = 130, - opc_lxor = 131, - opc_iinc = 132, - opc_i2l = 133, - opc_i2f = 134, - opc_i2d = 135, - opc_l2i = 136, - opc_l2f = 137, - opc_l2d = 138, - opc_f2i = 139, - opc_f2l = 140, - opc_f2d = 141, - opc_d2i = 142, - opc_d2l = 143, - opc_d2f = 144, - opc_i2b = 145, - opc_i2c = 146, - opc_i2s = 147, - opc_lcmp = 148, - opc_fcmpl = 149, - opc_fcmpg = 150, - opc_dcmpl = 151, - opc_dcmpg = 152, - opc_ifeq = 153, - opc_ifne = 154, - opc_iflt = 155, - opc_ifge = 156, - opc_ifgt = 157, - opc_ifle = 158, - opc_if_icmpeq = 159, - opc_if_icmpne = 160, - opc_if_icmplt = 161, - opc_if_icmpge = 162, - opc_if_icmpgt = 163, - opc_if_icmple = 164, - opc_if_acmpeq = 165, - opc_if_acmpne = 166, - opc_goto = 167, - opc_jsr = 168, - opc_ret = 169, - opc_tableswitch = 170, - opc_lookupswitch = 171, - opc_ireturn = 172, - opc_lreturn = 173, - opc_freturn = 174, - opc_dreturn = 175, - opc_areturn = 176, - opc_return = 177, - opc_getstatic = 178, - opc_putstatic = 179, - opc_getfield = 180, - opc_putfield = 181, - opc_invokevirtual = 182, - opc_invokespecial = 183, - opc_invokestatic = 184, - opc_invokeinterface = 185, - opc_xxxunusedxxx = 186, - opc_new = 187, - opc_newarray = 188, - opc_anewarray = 189, - opc_arraylength = 190, - opc_athrow = 191, - opc_checkcast = 192, - opc_instanceof = 193, - opc_monitorenter = 194, - opc_monitorexit = 195, - opc_wide = 196, - opc_multianewarray = 197, - opc_ifnull = 198, - opc_ifnonnull = 199, - opc_goto_w = 200, - opc_jsr_w = 201, - opc_breakpoint = 202, - opc_ldc_quick = 203, - opc_ldc_w_quick = 204, - opc_ldc2_w_quick = 205, - opc_getfield_quick = 206, - opc_putfield_quick = 207, - opc_getfield2_quick = 208, - opc_putfield2_quick = 209, - opc_getstatic_quick = 210, - opc_putstatic_quick = 211, - opc_getstatic2_quick = 212, - opc_putstatic2_quick = 213, - opc_invokevirtual_quick = 214, - opc_invokenonvirtual_quick = 215, - opc_invokesuper_quick = 216, - opc_invokestatic_quick = 217, - opc_invokeinterface_quick = 218, - opc_invokevirtualobject_quick = 219, - opc_invokeignored_quick = 220, - opc_new_quick = 221, - opc_anewarray_quick = 222, - opc_multianewarray_quick = 223, - opc_checkcast_quick = 224, - opc_instanceof_quick = 225, - opc_invokevirtual_quick_w = 226, - opc_getfield_quick_w = 227, - opc_putfield_quick_w = 228, - opc_nonnull_quick = 229, - opc_first_unused_index = 230, - opc_software = 254, - opc_hardware = 255, - opc_dummy = (int)0xF0000000U /* portability change, opc_invokeinit in the - * verifier requires more than 8 bits. - */ -} opcode_type; - -#endif /* !_JAVASOFT_OPCODES_H_ */ diff --git a/src/share/javavm/include/opcodes.length b/src/share/javavm/include/opcodes.length deleted file mode 100644 index 6e93ec638..000000000 --- a/src/share/javavm/include/opcodes.length +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -const short opcode_length[256] = { - 1, /* nop */ - 1, /* aconst_null */ - 1, /* iconst_m1 */ - 1, /* iconst_0 */ - 1, /* iconst_1 */ - 1, /* iconst_2 */ - 1, /* iconst_3 */ - 1, /* iconst_4 */ - 1, /* iconst_5 */ - 1, /* lconst_0 */ - 1, /* lconst_1 */ - 1, /* fconst_0 */ - 1, /* fconst_1 */ - 1, /* fconst_2 */ - 1, /* dconst_0 */ - 1, /* dconst_1 */ - 2, /* bipush */ - 3, /* sipush */ - 2, /* ldc */ - 3, /* ldc_w */ - 3, /* ldc2_w */ - 2, /* iload */ - 2, /* lload */ - 2, /* fload */ - 2, /* dload */ - 2, /* aload */ - 1, /* iload_0 */ - 1, /* iload_1 */ - 1, /* iload_2 */ - 1, /* iload_3 */ - 1, /* lload_0 */ - 1, /* lload_1 */ - 1, /* lload_2 */ - 1, /* lload_3 */ - 1, /* fload_0 */ - 1, /* fload_1 */ - 1, /* fload_2 */ - 1, /* fload_3 */ - 1, /* dload_0 */ - 1, /* dload_1 */ - 1, /* dload_2 */ - 1, /* dload_3 */ - 1, /* aload_0 */ - 1, /* aload_1 */ - 1, /* aload_2 */ - 1, /* aload_3 */ - 1, /* iaload */ - 1, /* laload */ - 1, /* faload */ - 1, /* daload */ - 1, /* aaload */ - 1, /* baload */ - 1, /* caload */ - 1, /* saload */ - 2, /* istore */ - 2, /* lstore */ - 2, /* fstore */ - 2, /* dstore */ - 2, /* astore */ - 1, /* istore_0 */ - 1, /* istore_1 */ - 1, /* istore_2 */ - 1, /* istore_3 */ - 1, /* lstore_0 */ - 1, /* lstore_1 */ - 1, /* lstore_2 */ - 1, /* lstore_3 */ - 1, /* fstore_0 */ - 1, /* fstore_1 */ - 1, /* fstore_2 */ - 1, /* fstore_3 */ - 1, /* dstore_0 */ - 1, /* dstore_1 */ - 1, /* dstore_2 */ - 1, /* dstore_3 */ - 1, /* astore_0 */ - 1, /* astore_1 */ - 1, /* astore_2 */ - 1, /* astore_3 */ - 1, /* iastore */ - 1, /* lastore */ - 1, /* fastore */ - 1, /* dastore */ - 1, /* aastore */ - 1, /* bastore */ - 1, /* castore */ - 1, /* sastore */ - 1, /* pop */ - 1, /* pop2 */ - 1, /* dup */ - 1, /* dup_x1 */ - 1, /* dup_x2 */ - 1, /* dup2 */ - 1, /* dup2_x1 */ - 1, /* dup2_x2 */ - 1, /* swap */ - 1, /* iadd */ - 1, /* ladd */ - 1, /* fadd */ - 1, /* dadd */ - 1, /* isub */ - 1, /* lsub */ - 1, /* fsub */ - 1, /* dsub */ - 1, /* imul */ - 1, /* lmul */ - 1, /* fmul */ - 1, /* dmul */ - 1, /* idiv */ - 1, /* ldiv */ - 1, /* fdiv */ - 1, /* ddiv */ - 1, /* irem */ - 1, /* lrem */ - 1, /* frem */ - 1, /* drem */ - 1, /* ineg */ - 1, /* lneg */ - 1, /* fneg */ - 1, /* dneg */ - 1, /* ishl */ - 1, /* lshl */ - 1, /* ishr */ - 1, /* lshr */ - 1, /* iushr */ - 1, /* lushr */ - 1, /* iand */ - 1, /* land */ - 1, /* ior */ - 1, /* lor */ - 1, /* ixor */ - 1, /* lxor */ - 3, /* iinc */ - 1, /* i2l */ - 1, /* i2f */ - 1, /* i2d */ - 1, /* l2i */ - 1, /* l2f */ - 1, /* l2d */ - 1, /* f2i */ - 1, /* f2l */ - 1, /* f2d */ - 1, /* d2i */ - 1, /* d2l */ - 1, /* d2f */ - 1, /* i2b */ - 1, /* i2c */ - 1, /* i2s */ - 1, /* lcmp */ - 1, /* fcmpl */ - 1, /* fcmpg */ - 1, /* dcmpl */ - 1, /* dcmpg */ - 3, /* ifeq */ - 3, /* ifne */ - 3, /* iflt */ - 3, /* ifge */ - 3, /* ifgt */ - 3, /* ifle */ - 3, /* if_icmpeq */ - 3, /* if_icmpne */ - 3, /* if_icmplt */ - 3, /* if_icmpge */ - 3, /* if_icmpgt */ - 3, /* if_icmple */ - 3, /* if_acmpeq */ - 3, /* if_acmpne */ - 3, /* goto */ - 3, /* jsr */ - 2, /* ret */ - 99, /* tableswitch */ - 99, /* lookupswitch */ - 1, /* ireturn */ - 1, /* lreturn */ - 1, /* freturn */ - 1, /* dreturn */ - 1, /* areturn */ - 1, /* return */ - 3, /* getstatic */ - 3, /* putstatic */ - 3, /* getfield */ - 3, /* putfield */ - 3, /* invokevirtual */ - 3, /* invokespecial */ - 3, /* invokestatic */ - 5, /* invokeinterface */ - 0, /* xxxunusedxxx */ - 3, /* new */ - 2, /* newarray */ - 3, /* anewarray */ - 1, /* arraylength */ - 1, /* athrow */ - 3, /* checkcast */ - 3, /* instanceof */ - 1, /* monitorenter */ - 1, /* monitorexit */ - 0, /* wide */ - 4, /* multianewarray */ - 3, /* ifnull */ - 3, /* ifnonnull */ - 5, /* goto_w */ - 5, /* jsr_w */ - 1, /* breakpoint */ - 2, /* ldc_quick */ - 3, /* ldc_w_quick */ - 3, /* ldc2_w_quick */ - 3, /* getfield_quick */ - 3, /* putfield_quick */ - 3, /* getfield2_quick */ - 3, /* putfield2_quick */ - 3, /* getstatic_quick */ - 3, /* putstatic_quick */ - 3, /* getstatic2_quick */ - 3, /* putstatic2_quick */ - 3, /* invokevirtual_quick */ - 3, /* invokenonvirtual_quick */ - 3, /* invokesuper_quick */ - 3, /* invokestatic_quick */ - 5, /* invokeinterface_quick */ - 3, /* invokevirtualobject_quick */ - 3, /* invokeignored_quick */ - 3, /* new_quick */ - 3, /* anewarray_quick */ - 4, /* multianewarray_quick */ - 3, /* checkcast_quick */ - 3, /* instanceof_quick */ - 3, /* invokevirtual_quick_w */ - 3, /* getfield_quick_w */ - 3, /* putfield_quick_w */ - 1, /* nonnull_quick */ - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, -}; diff --git a/src/share/javavm/include/opcodes.list b/src/share/javavm/include/opcodes.list deleted file mode 100644 index db546070a..000000000 --- a/src/share/javavm/include/opcodes.list +++ /dev/null @@ -1,301 +0,0 @@ -# Copyright 1994-2007 Sun Microsystems, Inc. All Rights Reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Sun designates this -# particular file as subject to the "Classpath" exception as provided -# by Sun in the LICENSE file that accompanied this code. -# -# This code 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 General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, -# CA 95054 USA or visit www.sun.com if you need additional information or -# have any questions. -# - -# Any line that doesn't have a-z in the 1st column is a comment. -# -# The first column is the name of the opcodes. The second column is the -# total length of the instruction. We use 99 for tableswitch and -# tablelookup, which must always be treated as special cases. -# -# The third and fourth colum give what the opcode pops off the stack, and -# what it then pushes back onto the stack -# - -# I integer -# L long integer -# F float -# D double float -# A address [array or object] -# O object only -# R return address (for jsr) -# a integer, array, or object -# ? unknown -# [I], [L], [F], [D], [A], [B], [C], [?] -# array of integer, long, float, double, address, bytes, -# chars, or anything -# 1,2,3,4,+ used by stack duplicating/popping routines. -# -# 1,2,3,4 represent >>any<< stack type except long or double. Two numbers -# separated by a + (in the third column) indicate that the two, together, can -# be used for a double or long. (Or they can represent two non-long items). -# -# The fifth column provides an *approximate* relative cost of executing the -# opcode. It is used by the instruction profiler. See profiler.h for -# blurb. - -nop 1 - - 1 /* nop */ -aconst_null 1 - A 1 /* push null object */ -iconst_m1 1 - I 1 /* push integer constant -1 */ -iconst_0 1 - I 1 /* push integer constant 0 */ -iconst_1 1 - I 1 /* push integer constant 1 */ -iconst_2 1 - I 1 /* push integer constant 2 */ -iconst_3 1 - I 1 /* push integer constant 3 */ -iconst_4 1 - I 1 /* push integer constant 4 */ -iconst_5 1 - I 1 /* push integer constant 5 */ -lconst_0 1 - L 1 /* push long 0L */ -lconst_1 1 - L 1 /* push long 1L */ -fconst_0 1 - F 1 /* push float constant 0.0 */ -fconst_1 1 - F 1 /* push float constant 1.0 */ -fconst_2 1 - F 1 /* push float constant 2.0 */ -dconst_0 1 - D 1 /* push double float constant 0.0d */ -dconst_1 1 - D 1 /* push double float constant 1.0d */ -bipush 2 - I 1 /* push byte-sized value */ -sipush 3 - I 1 /* push two-byte value */ -ldc 2 - ? 1 /* load a const from constant table */ -ldc_w 3 - ? 1 -ldc2_w 3 - ? 1 /* load a 2-word constant . . . */ -iload 2 - I 1 /* load local integer variable */ -lload 2 - L 1 /* load local long variable */ -fload 2 - F 1 /* load local floating variable */ -dload 2 - D 1 /* load local double variable */ -aload 2 - A 1 /* load local object variable */ -iload_0 1 - I 1 /* load local integer variable #0 */ -iload_1 1 - I 1 /* load local integer variable #1 */ -iload_2 1 - I 1 /* load local integer variable #2 */ -iload_3 1 - I 1 /* load local integer variable #3 */ -lload_0 1 - L 1 /* load local long variable #0 */ -lload_1 1 - L 1 /* load local long variable #1 */ -lload_2 1 - L 1 /* load local long variable #2 */ -lload_3 1 - L 1 /* load local long variable #3 */ -fload_0 1 - F 1 /* load local float variable #0 */ -fload_1 1 - F 1 /* load local float variable #1 */ -fload_2 1 - F 1 /* load local float variable #2 */ -fload_3 1 - F 1 /* load local float variable #3 */ -dload_0 1 - D 1 /* load lcl double float variable #0 */ -dload_1 1 - D 1 /* load lcl double float variable #1 */ -dload_2 1 - D 1 /* load lcl double float variable #2 */ -dload_3 1 - D 1 /* load lcl double float variable #3 */ -aload_0 1 - A 1 /* load local object variable #0 */ -aload_1 1 - A 1 /* load local object variable #1 */ -aload_2 1 - A 1 /* load local object variable #2 */ -aload_3 1 - A 1 /* load local object variable #3 */ -iaload 1 [I]I I 1 /* load from array of integer */ -laload 1 [L]I L 1 /* load from array of long */ -faload 1 [F]I F 1 /* load from array of float */ -daload 1 [D]I D 1 /* load from array of double */ -aaload 1 [A]I A 1 /* load from array of object */ -baload 1 [B]I I 1 /* load from array of (signed) bytes */ -caload 1 [C]I I 1 /* load from array of chars */ -saload 1 [S]I I 1 /* load from array of (signed) shorts */ -istore 2 I - 1 /* store local integer variable */ -lstore 2 L - 1 /* store local long variable */ -fstore 2 F - 1 /* store local float variable */ -dstore 2 D - 1 /* store local double variable */ -astore 2 A - 1 /* store local object variable */ -istore_0 1 I - 1 /* store local integer variable #0 */ -istore_1 1 I - 1 /* store local integer variable #1 */ -istore_2 1 I - 1 /* store local integer variable #2 */ -istore_3 1 I - 1 /* store local integer variable #3 */ -lstore_0 1 L - 1 /* store local long variable #0 */ -lstore_1 1 L - 1 /* store local long variable #1 */ -lstore_2 1 L - 1 /* store local long variable #2 */ -lstore_3 1 L - 1 /* store local long variable #3 */ -fstore_0 1 F - 1 /* store local float variable #0 */ -fstore_1 1 F - 1 /* store local float variable #1 */ -fstore_2 1 F - 1 /* store local float variable #2 */ -fstore_3 1 F - 1 /* store local float variable #3 */ -dstore_0 1 D - 1 /* store lcl double float variable #0 */ -dstore_1 1 D - 1 /* store lcl double float variable #1 */ -dstore_2 1 D - 1 /* store lcl double float variable #2 */ -dstore_3 1 D - 1 /* store lcl double float variable #3 */ -astore_0 1 A - 1 /* store local object variable #0 */ -astore_1 1 A - 1 /* store local object variable #1 */ -astore_2 1 A - 1 /* store local object variable #2 */ -astore_3 1 A - 1 /* store local object variable #3 */ -iastore 1 [I]II - 1 /* store into array of int */ -lastore 1 [L]IL - 1 /* store into array of long */ -fastore 1 [F]IF - 1 /* store into array of float */ -dastore 1 [D]ID - 1 /* store into array of double float */ -aastore 1 [A]IA - 1 /* store into array of object */ -bastore 1 [B]II - 1 /* store into array of (signed) bytes */ -castore 1 [C]II - 1 /* store into array of chars */ -sastore 1 [S]II - 1 /* store into array of (signed) shorts*/ -pop 1 1 - 1 /* pop top element */ -pop2 1 2+1 - 1 /* pop top two elements */ -dup 1 1 11 1 /* dup top element */ -dup_x1 1 21 121 1 /* dup top element. Skip one */ -dup_x2 1 3+21 1321 1 /* dup top element. Skip two */ -dup2 1 2+1 2121 1 /* dup top two elements. */ -dup2_x1 1 32+1 21321 1 /* dup top two elements. Skip one */ -dup2_x2 1 4+32+1 214321 1 /* dup top two elements. Skip two */ -swap 1 21 12 1 /* swap top two elements of stack. */ -iadd 1 II I 1 /* integer add */ -ladd 1 LL L 1 /* long add */ -fadd 1 FF F 1 /* floating add */ -dadd 1 DD D 1 /* double float add */ -isub 1 II I 1 /* integer subtract */ -lsub 1 LL L 1 /* long subtract */ -fsub 1 FF F 1 /* floating subtract */ -dsub 1 DD D 1 /* floating double subtract */ -imul 1 II I 1 /* integer multiply */ -lmul 1 LL L 1 /* long multiply */ -fmul 1 FF F 1 /* floating multiply */ -dmul 1 DD D 1 /* double float multiply */ -idiv 1 II I 1 /* integer divide */ -ldiv 1 LL L 1 /* long divide */ -fdiv 1 FF F 1 /* floating divide */ -ddiv 1 DD D 1 /* double float divide */ -irem 1 II I 1 /* integer mod */ -lrem 1 LL L 1 /* long mod */ -frem 1 FF F 1 /* floating mod */ -drem 1 DD D 1 /* double float mod */ -ineg 1 I I 1 /* integer negate */ -lneg 1 L L 1 /* long negate */ -fneg 1 F F 1 /* floating negate */ -dneg 1 D D 1 /* double float negate */ -ishl 1 II I 1 /* shift left */ -lshl 1 LI L 1 /* long shift left */ -ishr 1 II I 1 /* shift right */ -lshr 1 LI L 1 /* long shift right */ -iushr 1 II I 1 /* unsigned shift right */ -lushr 1 LI L 1 /* long unsigned shift right */ -iand 1 II I 1 /* boolean and */ -land 1 LL L 1 /* long boolean and */ -ior 1 II I 1 /* boolean or */ -lor 1 LL L 1 /* long boolean or */ -ixor 1 II I 1 /* boolean xor */ -lxor 1 LL L 1 /* long boolean xor */ -iinc 3 - - 1 /* increment lcl variable by constant */ -i2l 1 I L 1 /* integer to long */ -i2f 1 I F 1 /* integer to float */ -i2d 1 I D 1 /* integer to double */ -l2i 1 L I 1 /* long to integer */ -l2f 1 L F 1 /* long to float */ -l2d 1 L D 1 /* long to double */ -f2i 1 F I 1 /* float to integer */ -f2l 1 F L 1 /* float to long */ -f2d 1 F D 1 /* float to double */ -d2i 1 D I 1 /* double to integer */ -d2l 1 D L 1 /* double to long */ -d2f 1 D F 1 /* double to float */ -i2b 1 I I 1 /* integer to byte */ -i2c 1 I I 1 /* integer to character */ -i2s 1 I I 1 /* integer to signed short */ -lcmp 1 LL I 1 /* long compare */ -fcmpl 1 FF I 1 /* float compare. -1 on incomparable */ -fcmpg 1 FF I 1 /* float compare. 1 on incomparable */ -dcmpl 1 DD I 1 /* dbl floating cmp. -1 on incomp */ -dcmpg 1 DD I 1 /* dbl floating cmp. 1 on incomp */ -ifeq 3 I - 1 /* goto if equal */ -ifne 3 I - 1 /* goto if not equal */ -iflt 3 I - 1 /* goto if less than */ -ifge 3 I - 1 /* goto if greater than or equal */ -ifgt 3 I - 1 /* goto if greater than */ -ifle 3 I - 1 /* goto if less than or equal */ -if_icmpeq 3 II - 1 /* compare top two elements of stack */ -if_icmpne 3 II - 1 /* compare top two elements of stack */ -if_icmplt 3 II - 1 /* compare top two elements of stack */ -if_icmpge 3 II - 1 /* compare top two elements of stack */ -if_icmpgt 3 II - 1 /* compare top two elements of stack */ -if_icmple 3 II - 1 /* compare top two elements of stack */ -if_acmpeq 3 AA - 1 /* compare top two objects of stack */ -if_acmpne 3 AA - 1 /* compare top two objects of stack */ -goto 3 - - 1 /* unconditional goto */ -jsr 3 - R 1 /* jump subroutine */ -ret 2 - - 1 /* return from subroutine */ -tableswitch 99 I - 1 /* goto (case) */ -lookupswitch 99 I - 1 /* goto (case) */ -ireturn 1 I - 1 /* return integer from procedure */ -lreturn 1 L - 1 /* return long from procedure */ -freturn 1 F - 1 /* return float from procedure */ -dreturn 1 D - 1 /* return double from procedure */ -areturn 1 A - 1 /* return object from procedure */ -return 1 - - 1 /* return (void) from procedure */ -getstatic 3 - ? 1 /* get static field value. */ -putstatic 3 ? - 1 /* assign static field value */ -getfield 3 A ? 1 /* get field value from object. */ -putfield 3 ? - 1 /* assign field value to object. */ -invokevirtual 3 ? ? 1 /* call method, based on object. */ -invokespecial 3 ? ? 1 /* call method, not based on object. */ -invokestatic 3 ? ? 1 /* call a static method. */ -invokeinterface 5 ? ? 1 /* call an interface method */ -xxxunusedxxx 0 ? ? 1 /* was newfromname */ -new 3 - A 1 /* Create a new object */ -newarray 2 I A 1 /* Create a new array of non-objects*/ -anewarray 3 I A 1 /* Create a new array of objects */ -arraylength 1 [?] I 1 /* get length of array */ -athrow 1 O - 1 /* throw an exception */ -checkcast 3 A A 1 /* error if object not of given type */ -instanceof 3 A I 1 /* is object of given type? */ -monitorenter 1 A - 1 /* enter a monitored region of code */ -monitorexit 1 A - 1 /* exit a monitored region of code */ -wide 0 - - 1 /* prefix operation. */ -multianewarray 4 ? A 1 /* create multidimensional array */ -ifnull 3 A - 1 /* goto if null */ -ifnonnull 3 A - 1 /* goto if not null */ - -# The following instructions are "long" versions. They allow access to -# variables with index greater than 255. - -goto_w 5 - - 1 /* unconditional goto. 4byte offset */ -jsr_w 5 - R 1 /* jump subroutine. 4byte offset */ - -breakpoint 1 - - 1 /* call breakpoint handler */ - -# The compiler will not generate any of the following instructions. That -# are created by the interpreter from the non _quick versions of the -# instructions. - -ldc_quick 2 - ? 1 -ldc_w_quick 3 - ? 1 -ldc2_w_quick 3 - ? 1 -getfield_quick 3 A ? 1 -putfield_quick 3 ? - 1 -getfield2_quick 3 A ? 1 -putfield2_quick 3 ? - 1 -getstatic_quick 3 - ? 1 -putstatic_quick 3 ? - 1 -getstatic2_quick 3 - ? 1 -putstatic2_quick 3 ? _ 1 -invokevirtual_quick 3 ? ? 1 -invokenonvirtual_quick 3 ? ? 1 -invokesuper_quick 3 ? ? 1 -invokestatic_quick 3 ? ? 1 -invokeinterface_quick 5 ? ? 1 -invokevirtualobject_quick 3 ? ? 1 -invokeignored_quick 3 ? ? 1 -new_quick 3 - A 1 -anewarray_quick 3 I A 1 -multianewarray_quick 4 ? A 1 -checkcast_quick 3 A A 1 -instanceof_quick 3 A I 1 - -# The following are generated when the offset is bigger than 255 - -invokevirtual_quick_w 3 ? ? 1 -getfield_quick_w 3 A ? 1 -putfield_quick_w 3 ? - 1 - -# used for simplification - -nonnull_quick 1 A - 1 /* throw exception if stacktop null */ diff --git a/src/share/javavm/include/opcodes.weight b/src/share/javavm/include/opcodes.weight deleted file mode 100644 index e1a2d6d51..000000000 --- a/src/share/javavm/include/opcodes.weight +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -char const opcode_weight[256] = { - 1, /* nop */ - 1, /* aconst_null */ - 1, /* iconst_m1 */ - 1, /* iconst_0 */ - 1, /* iconst_1 */ - 1, /* iconst_2 */ - 1, /* iconst_3 */ - 1, /* iconst_4 */ - 1, /* iconst_5 */ - 1, /* lconst_0 */ - 1, /* lconst_1 */ - 1, /* fconst_0 */ - 1, /* fconst_1 */ - 1, /* fconst_2 */ - 1, /* dconst_0 */ - 1, /* dconst_1 */ - 1, /* bipush */ - 1, /* sipush */ - 1, /* ldc */ - 1, /* ldc_w */ - 1, /* ldc2_w */ - 1, /* iload */ - 1, /* lload */ - 1, /* fload */ - 1, /* dload */ - 1, /* aload */ - 1, /* iload_0 */ - 1, /* iload_1 */ - 1, /* iload_2 */ - 1, /* iload_3 */ - 1, /* lload_0 */ - 1, /* lload_1 */ - 1, /* lload_2 */ - 1, /* lload_3 */ - 1, /* fload_0 */ - 1, /* fload_1 */ - 1, /* fload_2 */ - 1, /* fload_3 */ - 1, /* dload_0 */ - 1, /* dload_1 */ - 1, /* dload_2 */ - 1, /* dload_3 */ - 1, /* aload_0 */ - 1, /* aload_1 */ - 1, /* aload_2 */ - 1, /* aload_3 */ - 1, /* iaload */ - 1, /* laload */ - 1, /* faload */ - 1, /* daload */ - 1, /* aaload */ - 1, /* baload */ - 1, /* caload */ - 1, /* saload */ - 1, /* istore */ - 1, /* lstore */ - 1, /* fstore */ - 1, /* dstore */ - 1, /* astore */ - 1, /* istore_0 */ - 1, /* istore_1 */ - 1, /* istore_2 */ - 1, /* istore_3 */ - 1, /* lstore_0 */ - 1, /* lstore_1 */ - 1, /* lstore_2 */ - 1, /* lstore_3 */ - 1, /* fstore_0 */ - 1, /* fstore_1 */ - 1, /* fstore_2 */ - 1, /* fstore_3 */ - 1, /* dstore_0 */ - 1, /* dstore_1 */ - 1, /* dstore_2 */ - 1, /* dstore_3 */ - 1, /* astore_0 */ - 1, /* astore_1 */ - 1, /* astore_2 */ - 1, /* astore_3 */ - 1, /* iastore */ - 1, /* lastore */ - 1, /* fastore */ - 1, /* dastore */ - 1, /* aastore */ - 1, /* bastore */ - 1, /* castore */ - 1, /* sastore */ - 1, /* pop */ - 1, /* pop2 */ - 1, /* dup */ - 1, /* dup_x1 */ - 1, /* dup_x2 */ - 1, /* dup2 */ - 1, /* dup2_x1 */ - 1, /* dup2_x2 */ - 1, /* swap */ - 1, /* iadd */ - 1, /* ladd */ - 1, /* fadd */ - 1, /* dadd */ - 1, /* isub */ - 1, /* lsub */ - 1, /* fsub */ - 1, /* dsub */ - 1, /* imul */ - 1, /* lmul */ - 1, /* fmul */ - 1, /* dmul */ - 1, /* idiv */ - 1, /* ldiv */ - 1, /* fdiv */ - 1, /* ddiv */ - 1, /* irem */ - 1, /* lrem */ - 1, /* frem */ - 1, /* drem */ - 1, /* ineg */ - 1, /* lneg */ - 1, /* fneg */ - 1, /* dneg */ - 1, /* ishl */ - 1, /* lshl */ - 1, /* ishr */ - 1, /* lshr */ - 1, /* iushr */ - 1, /* lushr */ - 1, /* iand */ - 1, /* land */ - 1, /* ior */ - 1, /* lor */ - 1, /* ixor */ - 1, /* lxor */ - 1, /* iinc */ - 1, /* i2l */ - 1, /* i2f */ - 1, /* i2d */ - 1, /* l2i */ - 1, /* l2f */ - 1, /* l2d */ - 1, /* f2i */ - 1, /* f2l */ - 1, /* f2d */ - 1, /* d2i */ - 1, /* d2l */ - 1, /* d2f */ - 1, /* i2b */ - 1, /* i2c */ - 1, /* i2s */ - 1, /* lcmp */ - 1, /* fcmpl */ - 1, /* fcmpg */ - 1, /* dcmpl */ - 1, /* dcmpg */ - 1, /* ifeq */ - 1, /* ifne */ - 1, /* iflt */ - 1, /* ifge */ - 1, /* ifgt */ - 1, /* ifle */ - 1, /* if_icmpeq */ - 1, /* if_icmpne */ - 1, /* if_icmplt */ - 1, /* if_icmpge */ - 1, /* if_icmpgt */ - 1, /* if_icmple */ - 1, /* if_acmpeq */ - 1, /* if_acmpne */ - 1, /* goto */ - 1, /* jsr */ - 1, /* ret */ - 1, /* tableswitch */ - 1, /* lookupswitch */ - 1, /* ireturn */ - 1, /* lreturn */ - 1, /* freturn */ - 1, /* dreturn */ - 1, /* areturn */ - 1, /* return */ - 1, /* getstatic */ - 1, /* putstatic */ - 1, /* getfield */ - 1, /* putfield */ - 1, /* invokevirtual */ - 1, /* invokespecial */ - 1, /* invokestatic */ - 1, /* invokeinterface */ - 1, /* xxxunusedxxx */ - 1, /* new */ - 1, /* newarray */ - 1, /* anewarray */ - 1, /* arraylength */ - 1, /* athrow */ - 1, /* checkcast */ - 1, /* instanceof */ - 1, /* monitorenter */ - 1, /* monitorexit */ - 1, /* wide */ - 1, /* multianewarray */ - 1, /* ifnull */ - 1, /* ifnonnull */ - 1, /* goto_w */ - 1, /* jsr_w */ - 1, /* breakpoint */ - 1, /* ldc_quick */ - 1, /* ldc_w_quick */ - 1, /* ldc2_w_quick */ - 1, /* getfield_quick */ - 1, /* putfield_quick */ - 1, /* getfield2_quick */ - 1, /* putfield2_quick */ - 1, /* getstatic_quick */ - 1, /* putstatic_quick */ - 1, /* getstatic2_quick */ - 1, /* putstatic2_quick */ - 1, /* invokevirtual_quick */ - 1, /* invokenonvirtual_quick */ - 1, /* invokesuper_quick */ - 1, /* invokestatic_quick */ - 1, /* invokeinterface_quick */ - 1, /* invokevirtualobject_quick */ - 1, /* invokeignored_quick */ - 1, /* new_quick */ - 1, /* anewarray_quick */ - 1, /* multianewarray_quick */ - 1, /* checkcast_quick */ - 1, /* instanceof_quick */ - 1, /* invokevirtual_quick_w */ - 1, /* getfield_quick_w */ - 1, /* putfield_quick_w */ - 1, /* nonnull_quick */ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, -}; diff --git a/src/share/javavm/include/opcodes.wide b/src/share/javavm/include/opcodes.wide deleted file mode 100644 index ed33d819b..000000000 --- a/src/share/javavm/include/opcodes.wide +++ /dev/null @@ -1,256 +0,0 @@ -NoWideOpcode(nop) -NoWideOpcode(aconst_null) -NoWideOpcode(iconst_m1) -NoWideOpcode(iconst_0) -NoWideOpcode(iconst_1) -NoWideOpcode(iconst_2) -NoWideOpcode(iconst_3) -NoWideOpcode(iconst_4) -NoWideOpcode(iconst_5) -NoWideOpcode(lconst_0) -NoWideOpcode(lconst_1) -NoWideOpcode(fconst_0) -NoWideOpcode(fconst_1) -NoWideOpcode(fconst_2) -NoWideOpcode(dconst_0) -NoWideOpcode(dconst_1) -NoWideOpcode(bipush) -NoWideOpcode(sipush) -NoWideOpcode(ldc) -NoWideOpcode(ldc_w) -NoWideOpcode(ldc2_w) -WideOpcode(iload) -WideOpcode(lload) -WideOpcode(fload) -WideOpcode(dload) -WideOpcode(aload) -NoWideOpcode(iload_0) -NoWideOpcode(iload_1) -NoWideOpcode(iload_2) -NoWideOpcode(iload_3) -NoWideOpcode(lload_0) -NoWideOpcode(lload_1) -NoWideOpcode(lload_2) -NoWideOpcode(lload_3) -NoWideOpcode(fload_0) -NoWideOpcode(fload_1) -NoWideOpcode(fload_2) -NoWideOpcode(fload_3) -NoWideOpcode(dload_0) -NoWideOpcode(dload_1) -NoWideOpcode(dload_2) -NoWideOpcode(dload_3) -NoWideOpcode(aload_0) -NoWideOpcode(aload_1) -NoWideOpcode(aload_2) -NoWideOpcode(aload_3) -NoWideOpcode(iaload) -NoWideOpcode(laload) -NoWideOpcode(faload) -NoWideOpcode(daload) -NoWideOpcode(aaload) -NoWideOpcode(baload) -NoWideOpcode(caload) -NoWideOpcode(saload) -WideOpcode(istore) -WideOpcode(lstore) -WideOpcode(fstore) -WideOpcode(dstore) -WideOpcode(astore) -NoWideOpcode(istore_0) -NoWideOpcode(istore_1) -NoWideOpcode(istore_2) -NoWideOpcode(istore_3) -NoWideOpcode(lstore_0) -NoWideOpcode(lstore_1) -NoWideOpcode(lstore_2) -NoWideOpcode(lstore_3) -NoWideOpcode(fstore_0) -NoWideOpcode(fstore_1) -NoWideOpcode(fstore_2) -NoWideOpcode(fstore_3) -NoWideOpcode(dstore_0) -NoWideOpcode(dstore_1) -NoWideOpcode(dstore_2) -NoWideOpcode(dstore_3) -NoWideOpcode(astore_0) -NoWideOpcode(astore_1) -NoWideOpcode(astore_2) -NoWideOpcode(astore_3) -NoWideOpcode(iastore) -NoWideOpcode(lastore) -NoWideOpcode(fastore) -NoWideOpcode(dastore) -NoWideOpcode(aastore) -NoWideOpcode(bastore) -NoWideOpcode(castore) -NoWideOpcode(sastore) -NoWideOpcode(pop) -NoWideOpcode(pop2) -NoWideOpcode(dup) -NoWideOpcode(dup_x1) -NoWideOpcode(dup_x2) -NoWideOpcode(dup2) -NoWideOpcode(dup2_x1) -NoWideOpcode(dup2_x2) -NoWideOpcode(swap) -NoWideOpcode(iadd) -NoWideOpcode(ladd) -NoWideOpcode(fadd) -NoWideOpcode(dadd) -NoWideOpcode(isub) -NoWideOpcode(lsub) -NoWideOpcode(fsub) -NoWideOpcode(dsub) -NoWideOpcode(imul) -NoWideOpcode(lmul) -NoWideOpcode(fmul) -NoWideOpcode(dmul) -NoWideOpcode(idiv) -NoWideOpcode(ldiv) -NoWideOpcode(fdiv) -NoWideOpcode(ddiv) -NoWideOpcode(irem) -NoWideOpcode(lrem) -NoWideOpcode(frem) -NoWideOpcode(drem) -NoWideOpcode(ineg) -NoWideOpcode(lneg) -NoWideOpcode(fneg) -NoWideOpcode(dneg) -NoWideOpcode(ishl) -NoWideOpcode(lshl) -NoWideOpcode(ishr) -NoWideOpcode(lshr) -NoWideOpcode(iushr) -NoWideOpcode(lushr) -NoWideOpcode(iand) -NoWideOpcode(land) -NoWideOpcode(ior) -NoWideOpcode(lor) -NoWideOpcode(ixor) -NoWideOpcode(lxor) -WideOpcode(iinc) -NoWideOpcode(i2l) -NoWideOpcode(i2f) -NoWideOpcode(i2d) -NoWideOpcode(l2i) -NoWideOpcode(l2f) -NoWideOpcode(l2d) -NoWideOpcode(f2i) -NoWideOpcode(f2l) -NoWideOpcode(f2d) -NoWideOpcode(d2i) -NoWideOpcode(d2l) -NoWideOpcode(d2f) -NoWideOpcode(i2b) -NoWideOpcode(i2c) -NoWideOpcode(i2s) -NoWideOpcode(lcmp) -NoWideOpcode(fcmpl) -NoWideOpcode(fcmpg) -NoWideOpcode(dcmpl) -NoWideOpcode(dcmpg) -NoWideOpcode(ifeq) -NoWideOpcode(ifne) -NoWideOpcode(iflt) -NoWideOpcode(ifge) -NoWideOpcode(ifgt) -NoWideOpcode(ifle) -NoWideOpcode(if_icmpeq) -NoWideOpcode(if_icmpne) -NoWideOpcode(if_icmplt) -NoWideOpcode(if_icmpge) -NoWideOpcode(if_icmpgt) -NoWideOpcode(if_icmple) -NoWideOpcode(if_acmpeq) -NoWideOpcode(if_acmpne) -NoWideOpcode(goto) -NoWideOpcode(jsr) -WideOpcode(ret) -NoWideOpcode(tableswitch) -NoWideOpcode(lookupswitch) -NoWideOpcode(ireturn) -NoWideOpcode(lreturn) -NoWideOpcode(freturn) -NoWideOpcode(dreturn) -NoWideOpcode(areturn) -NoWideOpcode(return) -NoWideOpcode(getstatic) -NoWideOpcode(putstatic) -NoWideOpcode(getfield) -NoWideOpcode(putfield) -NoWideOpcode(invokevirtual) -NoWideOpcode(invokespecial) -NoWideOpcode(invokestatic) -NoWideOpcode(invokeinterface) -NoWideOpcode(xxxunusedxxx) -NoWideOpcode(new) -NoWideOpcode(newarray) -NoWideOpcode(anewarray) -NoWideOpcode(arraylength) -NoWideOpcode(athrow) -NoWideOpcode(checkcast) -NoWideOpcode(instanceof) -NoWideOpcode(monitorenter) -NoWideOpcode(monitorexit) -NoWideOpcode(wide) -NoWideOpcode(multianewarray) -NoWideOpcode(ifnull) -NoWideOpcode(ifnonnull) -NoWideOpcode(goto_w) -NoWideOpcode(jsr_w) -NoWideOpcode(breakpoint) -NoWideOpcode(ldc_quick) -NoWideOpcode(ldc_w_quick) -NoWideOpcode(ldc2_w_quick) -NoWideOpcode(getfield_quick) -NoWideOpcode(putfield_quick) -NoWideOpcode(getfield2_quick) -NoWideOpcode(putfield2_quick) -NoWideOpcode(getstatic_quick) -NoWideOpcode(putstatic_quick) -NoWideOpcode(getstatic2_quick) -NoWideOpcode(putstatic2_quick) -NoWideOpcode(invokevirtual_quick) -NoWideOpcode(invokenonvirtual_quick) -NoWideOpcode(invokesuper_quick) -NoWideOpcode(invokestatic_quick) -NoWideOpcode(invokeinterface_quick) -NoWideOpcode(invokevirtualobject_quick) -NoWideOpcode(invokeignored_quick) -NoWideOpcode(new_quick) -NoWideOpcode(anewarray_quick) -NoWideOpcode(multianewarray_quick) -NoWideOpcode(checkcast_quick) -NoWideOpcode(instanceof_quick) -NoWideOpcode(invokevirtual_quick_w) -NoWideOpcode(getfield_quick_w) -NoWideOpcode(putfield_quick_w) -NoWideOpcode(nonnull_quick) -NoWideOpcode(Illegal230) -NoWideOpcode(Illegal231) -NoWideOpcode(Illegal232) -NoWideOpcode(Illegal233) -NoWideOpcode(Illegal234) -NoWideOpcode(Illegal235) -NoWideOpcode(Illegal236) -NoWideOpcode(Illegal237) -NoWideOpcode(Illegal238) -NoWideOpcode(Illegal239) -NoWideOpcode(Illegal240) -NoWideOpcode(Illegal241) -NoWideOpcode(Illegal242) -NoWideOpcode(Illegal243) -NoWideOpcode(Illegal244) -NoWideOpcode(Illegal245) -NoWideOpcode(Illegal246) -NoWideOpcode(Illegal247) -NoWideOpcode(Illegal248) -NoWideOpcode(Illegal249) -NoWideOpcode(Illegal250) -NoWideOpcode(Illegal251) -NoWideOpcode(Illegal252) -NoWideOpcode(Illegal253) -NoWideOpcode(Illegal254) -NoWideOpcode(Illegal255) diff --git a/src/share/javavm/include/sys_api.h b/src/share/javavm/include/sys_api.h deleted file mode 100644 index d7856c3f6..000000000 --- a/src/share/javavm/include/sys_api.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 1994-1999 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -#ifndef _JAVASOFT_SYS_API_H_ -#define _JAVASOFT_SYS_API_H_ - -#include "hpi.h" - -extern HPI_MemoryInterface *hpi_memory_interface; -extern HPI_LibraryInterface *hpi_library_interface; -extern HPI_SystemInterface *hpi_system_interface; -extern HPI_ThreadInterface *hpi_thread_interface; -extern HPI_FileInterface *hpi_file_interface; -extern HPI_SocketInterface *hpi_socket_interface; - -#define sysMalloc(x) hpi_memory_interface->Malloc(x) -#define sysRealloc(x,y) hpi_memory_interface->Realloc(x,y) -#define sysFree(x) hpi_memory_interface->Free(x) -#define sysCalloc(x,y) hpi_memory_interface->Calloc(x,y) -#define sysStrdup(x) hpi_memory_interface->Strdup(x) -#define sysMapMem(x,y) hpi_memory_interface->MapMem(x,y) -#define sysUnmapMem(x,y,z) hpi_memory_interface->UnmapMem(x,y,z) -#define sysCommitMem(x,y,z) hpi_memory_interface->CommitMem(x,y,z) -#define sysDecommitMem(x,y,z) hpi_memory_interface->DecommitMem(x,y,z) -#define sysAllocBlock(x,y) hpi_memory_interface->AllocBlock(x,y) -#define sysFreeBlock(x) hpi_memory_interface->FreeBlock(x) - -#define sysBuildLibName(a,b,c,d) hpi_library_interface->BuildLibName(a,b,c,d) -#define sysBuildFunName(a,b,c,d) hpi_library_interface->BuildFunName(a,b,c,d) -#define sysLoadLibrary(a,b,c) hpi_library_interface->LoadLibrary(a,b,c) -#define sysUnloadLibrary(a) hpi_library_interface->UnloadLibrary(a) -#define sysFindLibraryEntry(a,b) hpi_library_interface->FindLibraryEntry(a,b) - -#define sysGetSysInfo() hpi_system_interface->GetSysInfo() -#define sysGetMilliTicks() hpi_system_interface->GetMilliTicks() -#define sysTimeMillis() hpi_system_interface->TimeMillis() - -#define sysSignal(a,b) hpi_system_interface->Signal(a,b) -#define sysRaise(a) hpi_system_interface->Raise(a) -#define sysSignalNotify(a) hpi_system_interface->SignalNotify(a) -#define sysSignalWait() hpi_system_interface->SignalWait() -#define sysShutdown() hpi_system_interface->Shutdown() -#define sysSetLoggingLevel(a) hpi_system_interface->SetLoggingLevel(a) -#define sysSetMonitoringOn(a) hpi_system_interface->SetMonitoringOn(a) -#define sysGetLastErrorString(a,b) hpi_system_interface->GetLastErrorString(a,b) - -#define sysThreadBootstrap(a,b,c) hpi_thread_interface->ThreadBootstrap(a,b,c) -#define sysThreadCreate(a,b,c,d) hpi_thread_interface->ThreadCreate(a,b,c,d) -#define sysThreadSelf() hpi_thread_interface->ThreadSelf() -#define sysThreadYield() hpi_thread_interface->ThreadYield() -#define sysThreadSuspend(a) hpi_thread_interface->ThreadSuspend(a) -#define sysThreadResume(a) hpi_thread_interface->ThreadResume(a) -#define sysThreadSetPriority(a,b) hpi_thread_interface->ThreadSetPriority(a,b) -#define sysThreadGetPriority(a,b) hpi_thread_interface->ThreadGetPriority(a,b) -#define sysThreadStackPointer(a) hpi_thread_interface->ThreadStackPointer(a) -#define sysThreadStackTop(a) hpi_thread_interface->ThreadStackTop(a) -#define sysThreadRegs(a,b) hpi_thread_interface->ThreadRegs(a,b) -#define sysThreadSingle() hpi_thread_interface->ThreadSingle() -#define sysThreadMulti() hpi_thread_interface->ThreadMulti() -#define sysThreadCheckStack() hpi_thread_interface->ThreadCheckStack() -#define sysThreadPostException(a,b) \ - hpi_thread_interface->ThreadPostException(a,b) -#define sysThreadInterrupt(a) hpi_thread_interface->ThreadInterrupt(a) -#define sysThreadIsInterrupted(a,b) \ - hpi_thread_interface->ThreadIsInterrupted(a,b) -#define sysThreadAlloc(a) hpi_thread_interface->ThreadAlloc(a) -#define sysThreadFree() hpi_thread_interface->ThreadFree() -#define sysThreadCPUTime() hpi_thread_interface->ThreadCPUTime() -#define sysThreadGetStatus(a,b) hpi_thread_interface->ThreadGetStatus(a,b) -#define sysThreadEnumerateOver(a,b) \ - hpi_thread_interface->ThreadEnumerateOver(a,b) -#define sysThreadIsRunning(a) hpi_thread_interface->ThreadIsRunning(a) -#define sysThreadProfSuspend(a) hpi_thread_interface->ThreadProfSuspend(a) -#define sysThreadProfResume(a) hpi_thread_interface->ThreadProfResume(a) -#define sysAdjustTimeSlice(a) hpi_thread_interface->AdjustTimeSlice(a) - -#define sysMonitorSizeof() hpi_thread_interface->MonitorSizeof() -#define sysMonitorInit(a) hpi_thread_interface->MonitorInit(a) -#define sysMonitorDestroy(a) hpi_thread_interface->MonitorDestroy(a) -#define sysMonitorEnter(a,b) hpi_thread_interface->MonitorEnter(a,b) -#define sysMonitorEntered(a,b) hpi_thread_interface->MonitorEntered(a,b) -#define sysMonitorExit(a,b) hpi_thread_interface->MonitorExit(a,b) -#define sysMonitorNotify(a,b) hpi_thread_interface->MonitorNotify(a,b) -#define sysMonitorNotifyAll(a,b) hpi_thread_interface->MonitorNotifyAll(a,b) -#define sysMonitorWait(a,b,c) hpi_thread_interface->MonitorWait(a,b,c) -#define sysMonitorInUse(a) hpi_thread_interface->MonitorInUse(a) -#define sysMonitorOwner(a) hpi_thread_interface->MonitorOwner(a) -#define sysMonitorGetInfo(a,b) hpi_thread_interface->MonitorGetInfo(a,b) - -#define sysThreadInterruptEvent() hpi_thread_interface->ThreadInterruptEvent() -#define sysThreadNativeID(a) hpi_thread_interface->ThreadNativeID(a) - -#define sysNativePath(a) hpi_file_interface->NativePath(a) -#define sysFileType(a) hpi_file_interface->FileType(a) -#define sysOpen(a,b,c) hpi_file_interface->Open(a,b,c) -#define sysClose(a) hpi_file_interface->Close(a) -#define sysSeek(a,b,c) hpi_file_interface->Seek(a,b,c) -#define sysSetLength(a,b) hpi_file_interface->SetLength(a,b) -#define sysSync(a) hpi_file_interface->Sync(a) -#define sysAvailable(a,b) hpi_file_interface->Available(a,b) -#define sysRead(a,b,c) hpi_file_interface->Read(a,b,c) -#define sysWrite(a,b,c) hpi_file_interface->Write(a,b,c) -#define sysFileSizeFD(a,b) hpi_file_interface->FileSizeFD(a,b) - -#define sysSocketClose(a) hpi_socket_interface->Close(a) -#define sysSocketShutdown(a,b) hpi_socket_interface->SocketShutdown(a,b) -#define sysSocketAvailable(a,b) hpi_socket_interface->Available(a,b) -#define sysConnect(a,b,c) hpi_socket_interface->Connect(a,b,c) -#define sysBind(a,b,c) hpi_socket_interface->Bind(a,b,c) -#define sysAccept(a,b,c) hpi_socket_interface->Accept(a,b,c) -#define sysGetSockName(a,b,c) hpi_socket_interface->GetSocketName(a,b,c) -#define sysSendTo(a,b,c,d,e,f) hpi_socket_interface->SendTo(a,b,c,d,e,f) -#define sysRecvFrom(a,b,c,d,e,f) hpi_socket_interface->RecvFrom(a,b,c,d,e,f) -#define sysListen(a,b) hpi_socket_interface->Listen(a,b) -#define sysRecv(a,b,c,d) hpi_socket_interface->Recv(a,b,c,d) -#define sysSend(a,b,c,d) hpi_socket_interface->Send(a,b,c,d) -#define sysTimeout(a,b) hpi_socket_interface->Timeout(a,b) -#define sysGetHostName(a, b) hpi_socket_interface->GetHostName(a, b) -#define sysGetHostByAddr(a, b, c) hpi_socket_interface->GetHostByAddr(a, b, c) -#define sysGetHostByName(a) hpi_socket_interface->GetHostByName(a) -#define sysSocket(a,b,c) hpi_socket_interface->Socket(a,b,c) -#define sysGetSockOpt(a, b, c, d, e) hpi_socket_interface->SocketGetOption(a, b, c, d, e) -#define sysSetSockOpt(a, b, c, d, e) hpi_socket_interface->SocketSetOption(a, b, c, d, e) -#define sysGetProtoByName(a) hpi_socket_interface->GetProtoByName(a) - -#define SYS_SIG_DFL HPI_SIG_DFL -#define SYS_SIG_ERR HPI_SIG_ERR -#define SYS_SIG_IGN HPI_SIG_IGN - -#define SYS_OK HPI_OK -#define SYS_ERR HPI_ERR -#define SYS_INTRPT HPI_INTRPT -#define SYS_TIMEOUT HPI_TIMEOUT -#define SYS_NOMEM HPI_NOMEM -#define SYS_NORESOURCE HPI_NORESOURCE - -#define SYS_THREAD_RUNNABLE HPI_THREAD_RUNNABLE -#define SYS_THREAD_MONITOR_WAIT HPI_THREAD_MONITOR_WAIT -#define SYS_THREAD_CONDVAR_WAIT HPI_THREAD_CONDVAR_WAIT - -#define MinimumPriority HPI_MINIMUM_PRIORITY -#define MaximumPriority HPI_MAXIMUM_PRIORITY -#define NormalPriority HPI_NORMAL_PRIORITY - -#define SYS_THREAD_SUSPENDED HPI_THREAD_SUSPENDED -#define SYS_THREAD_INTERRUPTED HPI_THREAD_INTERRUPTED - -#define PAGE_ALIGNMENT HPI_PAGE_ALIGNMENT - -#define SYS_TIMEOUT_INFINITY HPI_TIMEOUT_INFINITY - -#define SYS_FILETYPE_REGULAR HPI_FILETYPE_REGULAR -#define SYS_FILETYPE_DIRECTORY HPI_FILETYPE_DIRECTORY -#define SYS_FILETYPE_OTHER HPI_FILETYPE_OTHER - -#endif /* !_JAVASOFT_SYS_API_H_ */ diff --git a/src/share/javavm/include/typedefs.h b/src/share/javavm/include/typedefs.h deleted file mode 100644 index 6c35063bc..000000000 --- a/src/share/javavm/include/typedefs.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 1994-2002 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -#ifndef _JAVASOFT_TYPEDEFS_H_ -#define _JAVASOFT_TYPEDEFS_H_ - -#include "typedefs_md.h" /* for int64_t */ - -/* - * Macros to deal with the JavaVM's stack alignment. Many machines - * require doublewords to be double aligned. This union is used by - * code in math.h as a more portable way do alingnment on machines - * that require it. This union and the macros that use it came from - * Netscape. - */ - -#ifdef HAVE_ALIGNED_LONGLONGS -#define GET_INT64(_t,_addr) \ - ((((int32_t*) &(_t))[0] = ((int32_t*)(_addr))[0]), \ - (((int32_t*) &(_t))[1] = ((int32_t*)(_addr))[1]), \ - (_t).j ) -#define SET_INT64(_t, _addr, _v) \ - ( (_t).j = (_v), \ - ((int32_t*)(_addr))[0] = ((int32_t*) &(_t))[0], \ - ((int32_t*)(_addr))[1] = ((int32_t*) &(_t))[1] ) -#else -#define GET_INT64(_t,_addr) (*(int64_t*)(_addr)) -#define SET_INT64(_t, _addr, _v) (*(int64_t*)(_addr) = (_v)) -#endif - -/* If double's must be aligned on doubleword boundaries then define this */ -#ifdef HAVE_ALIGNED_DOUBLES -#define GET_DOUBLE(_t,_addr) \ - ((((int32_t*) &(_t))[0] = ((int32_t*)(_addr))[0]), \ - (((int32_t*) &(_t))[1] = ((int32_t*)(_addr))[1]), \ - (_t).d ) -#define SET_DOUBLE(_t, _addr, _v) \ - ( (_t).d = (_v), \ - ((int32_t*)(_addr))[0] = ((int32_t*) &(_t))[0], \ - ((int32_t*)(_addr))[1] = ((int32_t*) &(_t))[1] ) -#else -#define GET_DOUBLE(_t,_addr) (*(jdouble*)(_addr)) -#define SET_DOUBLE(_t, _addr, _v) (*(jdouble*)(_addr) = (_v)) -#endif - -/* If pointers are 64bits then define this */ -#ifdef HAVE_64BIT_POINTERS -#define GET_HANDLE(_t,_addr) \ - ( ((int32_t*) &(_t))[0] = ((int32_t*)(_addr))[0]), \ - ((int32_t*) &(_t))[1] = ((int32_t*)(_addr))[1]), \ - (void*) (_t).l ) -#define SET_HANDLE(_t, _addr, _v) \ - ( *(void**) &((_t).l) = (_v), \ - ((int32_t*)(_addr))[0] = ((int32_t*) &(_t))[0], \ - ((int32_t*)(_addr))[1] = ((int32_t*) &(_t))[1] ) -#else -#define GET_HANDLE(_t,_addr) (*(JHandle*)(_addr)) -#define SET_HANDLE(_t, _addr, _v) (*(JHandle*)(_addr) = (_v)) -#endif - - -/* - * Printf-style formatters for fixed- and variable-width types as pointers and - * integers. - * - * Each platform-specific definitions file "typedefs_md.h" - * must define the macro FORMAT64_MODIFIER, which is the modifier for '%x' or - * '%d' formats to indicate a 64-bit quantity; commonly "l" (in LP64) or "ll" - * (in ILP32). - */ - -/* Format 32-bit quantities. */ -#define INT32_FORMAT "%d" -#define UINT32_FORMAT "%u" -#define PTR32_FORMAT "0x%08x" - -/* Format 64-bit quantities. */ -#define INT64_FORMAT "%" FORMAT64_MODIFIER "d" -#define UINT64_FORMAT "%" FORMAT64_MODIFIER "u" -#define PTR64_FORMAT "0x%016" FORMAT64_MODIFIER "x" - -/* Format pointers and size_t (or size_t-like integer types) which change size - * between 32- and 64-bit. - */ -#if defined(_LP64) || defined(_WIN64) -#define PTR_FORMAT PTR64_FORMAT -#define SIZE_FORMAT UINT64_FORMAT -#define SSIZE_FORMAT INT64_FORMAT -#else -#define PTR_FORMAT PTR32_FORMAT -#define SIZE_FORMAT UINT32_FORMAT -#define SSIZE_FORMAT INT32_FORMAT -#endif - -#define INTPTR_FORMAT PTR_FORMAT - - -#endif /* !_JAVASOFT_TYPEDEFS_H_ */ diff --git a/src/share/native/common/check_code.c b/src/share/native/common/check_code.c index d06b2eddd..7e1613255 100644 --- a/src/share/native/common/check_code.c +++ b/src/share/native/common/check_code.c @@ -1,5 +1,5 @@ /* - * Copyright 1994-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -87,10 +87,7 @@ #include "jni.h" #include "jvm.h" -#include "typedefs.h" - -#include "opcodes.h" -#include "opcodes.length" +#include "classfile_constants.h" #include "opcodes.in_out" #define MAX_ARRAY_DIMENSIONS 255 @@ -161,8 +158,8 @@ typedef unsigned int *bitvector; #define NULL_FULLINFO MAKE_FULLINFO(ITEM_Object, 0, 0) -/* opc_invokespecial calls to need to be treated special */ -#define opc_invokeinit 0x100 +/* JVM_OPC_invokespecial calls to need to be treated special */ +#define JVM_OPC_invokeinit 0x100 /* A hash mechanism used by the verifier. * Maps class names to unique 16 bit integers. @@ -301,7 +298,7 @@ struct mask_type { typedef unsigned short flag_type; struct instruction_data_type { - opcode_type opcode; /* may turn into "canonical" opcode */ + int opcode; /* may turn into "canonical" opcode */ unsigned changed:1; /* has it changed */ unsigned protected:1; /* must accessor be a subclass of "this" */ union { @@ -345,7 +342,7 @@ static void free_all_code(int num_methods, int* lengths, unsigned char** code); static void verify_field(context_type *context, jclass cb, int index); static void verify_opcode_operands (context_type *, unsigned int inumber, int offset); -static void set_protected(context_type *, unsigned int inumber, int key, opcode_type); +static void set_protected(context_type *, unsigned int inumber, int key, int); static jboolean is_superclass(context_type *, fullinfo_type); static void initialize_exception_table(context_type *); @@ -1084,7 +1081,7 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) int *code_data = context->code_data; int mi = context->method_index; unsigned char *code = context->code; - opcode_type opcode = this_idata->opcode; + int opcode = this_idata->opcode; int var; /* @@ -1096,17 +1093,17 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) switch (opcode) { - case opc_jsr: + case JVM_OPC_jsr: /* instruction of ret statement */ this_idata->operand2.i = UNKNOWN_RET_INSTRUCTION; /* FALLTHROUGH */ - case opc_ifeq: case opc_ifne: case opc_iflt: - case opc_ifge: case opc_ifgt: case opc_ifle: - case opc_ifnull: case opc_ifnonnull: - case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmplt: - case opc_if_icmpge: case opc_if_icmpgt: case opc_if_icmple: - case opc_if_acmpeq: case opc_if_acmpne: - case opc_goto: { + case JVM_OPC_ifeq: case JVM_OPC_ifne: case JVM_OPC_iflt: + case JVM_OPC_ifge: case JVM_OPC_ifgt: case JVM_OPC_ifle: + case JVM_OPC_ifnull: case JVM_OPC_ifnonnull: + case JVM_OPC_if_icmpeq: case JVM_OPC_if_icmpne: case JVM_OPC_if_icmplt: + case JVM_OPC_if_icmpge: case JVM_OPC_if_icmpgt: case JVM_OPC_if_icmple: + case JVM_OPC_if_acmpeq: case JVM_OPC_if_acmpne: + case JVM_OPC_goto: { /* Set the ->operand to be the instruction number of the target. */ int jump = (((signed char)(code[offset+1])) << 8) + code[offset+2]; int target = offset + jump; @@ -1116,11 +1113,11 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) break; } - case opc_jsr_w: + case JVM_OPC_jsr_w: /* instruction of ret statement */ this_idata->operand2.i = UNKNOWN_RET_INSTRUCTION; /* FALLTHROUGH */ - case opc_goto_w: { + case JVM_OPC_goto_w: { /* Set the ->operand to be the instruction number of the target. */ int jump = (((signed char)(code[offset+1])) << 24) + (code[offset+2] << 16) + (code[offset+3] << 8) + @@ -1132,8 +1129,8 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) break; } - case opc_tableswitch: - case opc_lookupswitch: { + case JVM_OPC_tableswitch: + case JVM_OPC_lookupswitch: { /* Set the ->operand to be a table of possible instruction targets. */ int *lpc = (int *) UCALIGN(code + offset + 1); int *lptr; @@ -1147,7 +1144,7 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) CCerror(context, "Non zero padding bytes in switch"); } } - if (opcode == opc_tableswitch) { + if (opcode == JVM_OPC_tableswitch) { keys = ntohl(lpc[2]) - ntohl(lpc[1]) + 1; delta = 1; } else { @@ -1169,7 +1166,7 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) for (k = keys, lptr = &lpc[3]; --k >= 0; lptr += delta) { int target = offset + ntohl(lptr[0]); if (!isLegalTarget(context, target)) - CCerror(context, "Illegal branch in opc_tableswitch"); + CCerror(context, "Illegal branch in tableswitch"); saved_operand[k + 1] = code_data[target]; } saved_operand[0] = keys + 1; /* number of successors */ @@ -1177,7 +1174,7 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) break; } - case opc_ldc: { + case JVM_OPC_ldc: { /* Make sure the constant pool item is the right type. */ int key = code[offset + 1]; int types = (1 << JVM_CONSTANT_Integer) | (1 << JVM_CONSTANT_Float) | @@ -1190,7 +1187,7 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) break; } - case opc_ldc_w: { + case JVM_OPC_ldc_w: { /* Make sure the constant pool item is the right type. */ int key = (code[offset + 1] << 8) + code[offset + 2]; int types = (1 << JVM_CONSTANT_Integer) | (1 << JVM_CONSTANT_Float) | @@ -1203,7 +1200,7 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) break; } - case opc_ldc2_w: { + case JVM_OPC_ldc2_w: { /* Make sure the constant pool item is the right type. */ int key = (code[offset + 1] << 8) + code[offset + 2]; int types = (1 << JVM_CONSTANT_Double) | (1 << JVM_CONSTANT_Long); @@ -1212,28 +1209,28 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) break; } - case opc_getfield: case opc_putfield: - case opc_getstatic: case opc_putstatic: { + case JVM_OPC_getfield: case JVM_OPC_putfield: + case JVM_OPC_getstatic: case JVM_OPC_putstatic: { /* Make sure the constant pool item is the right type. */ int key = (code[offset + 1] << 8) + code[offset + 2]; this_idata->operand.i = key; verify_constant_pool_type(context, key, 1 << JVM_CONSTANT_Fieldref); - if (opcode == opc_getfield || opcode == opc_putfield) + if (opcode == JVM_OPC_getfield || opcode == JVM_OPC_putfield) set_protected(context, inumber, key, opcode); break; } - case opc_invokevirtual: - case opc_invokespecial: - case opc_invokestatic: - case opc_invokeinterface: { + case JVM_OPC_invokevirtual: + case JVM_OPC_invokespecial: + case JVM_OPC_invokestatic: + case JVM_OPC_invokeinterface: { /* Make sure the constant pool item is the right type. */ int key = (code[offset + 1] << 8) + code[offset + 2]; const char *methodname; jclass cb = context->class; fullinfo_type clazz_info; int is_constructor, is_internal; - int kind = (opcode == opc_invokeinterface + int kind = (opcode == JVM_OPC_invokeinterface ? 1 << JVM_CONSTANT_InterfaceMethodref : 1 << JVM_CONSTANT_Methodref); /* Make sure the constant pool item is the right type. */ @@ -1249,16 +1246,16 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) this_idata->operand.i = key; this_idata->operand2.fi = clazz_info; if (is_constructor) { - if (opcode != opc_invokespecial) { + if (opcode != JVM_OPC_invokespecial) { CCerror(context, "Must call initializers using invokespecial"); } - this_idata->opcode = opc_invokeinit; + this_idata->opcode = JVM_OPC_invokeinit; } else { if (is_internal) { CCerror(context, "Illegal call to internal method"); } - if (opcode == opc_invokespecial + if (opcode == JVM_OPC_invokespecial && clazz_info != context->currentclass_info && clazz_info != context->superclass_info) { int not_found = 1; @@ -1290,7 +1287,7 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) } } } - if (opcode == opc_invokeinterface) { + if (opcode == JVM_OPC_invokeinterface) { unsigned int args1; unsigned int args2; const char *signature = @@ -1300,25 +1297,25 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) args2 = code[offset + 3]; if (args1 != args2) { CCerror(context, - "Inconsistent args_size for opc_invokeinterface"); + "Inconsistent args_size for invokeinterface"); } if (code[offset + 4] != 0) { CCerror(context, "Fourth operand byte of invokeinterface must be zero"); } pop_and_free(context); - } else if (opcode == opc_invokevirtual - || opcode == opc_invokespecial) + } else if (opcode == JVM_OPC_invokevirtual + || opcode == JVM_OPC_invokespecial) set_protected(context, inumber, key, opcode); break; } - case opc_instanceof: - case opc_checkcast: - case opc_new: - case opc_anewarray: - case opc_multianewarray: { + case JVM_OPC_instanceof: + case JVM_OPC_checkcast: + case JVM_OPC_new: + case JVM_OPC_anewarray: + case JVM_OPC_multianewarray: { /* Make sure the constant pool item is a class */ int key = (code[offset + 1] << 8) + code[offset + 2]; fullinfo_type target; @@ -1327,14 +1324,14 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) if (GET_ITEM_TYPE(target) == ITEM_Bogus) CCerror(context, "Illegal type"); switch(opcode) { - case opc_anewarray: + case JVM_OPC_anewarray: if ((GET_INDIRECTION(target)) >= MAX_ARRAY_DIMENSIONS) CCerror(context, "Array with too many dimensions"); this_idata->operand.fi = MAKE_FULLINFO(GET_ITEM_TYPE(target), GET_INDIRECTION(target) + 1, GET_EXTRA_INFO(target)); break; - case opc_new: + case JVM_OPC_new: if (WITH_ZERO_EXTRA_INFO(target) != MAKE_FULLINFO(ITEM_Object, 0, 0)) CCerror(context, "Illegal creation of multi-dimensional array"); @@ -1343,10 +1340,10 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) this_idata->operand.fi = MAKE_FULLINFO(ITEM_NewObject, 0, inumber); this_idata->operand2.fi = target; break; - case opc_multianewarray: + case JVM_OPC_multianewarray: this_idata->operand.fi = target; this_idata->operand2.i = code[offset + 3]; - if ( (this_idata->operand2.i > GET_INDIRECTION(target)) + if ( (this_idata->operand2.i > (int)GET_INDIRECTION(target)) || (this_idata->operand2.i == 0)) CCerror(context, "Illegal dimension argument"); break; @@ -1356,8 +1353,8 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) break; } - case opc_newarray: { - /* Cache the result of the opc_newarray into the operand slot */ + case JVM_OPC_newarray: { + /* Cache the result of the JVM_OPC_newarray into the operand slot */ fullinfo_type full_info; switch (code[offset + 1]) { case JVM_T_INT: @@ -1376,78 +1373,78 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) full_info = MAKE_FULLINFO(ITEM_Short, 1, 0); break; default: full_info = 0; /* Keep lint happy */ - CCerror(context, "Bad type passed to opc_newarray"); + CCerror(context, "Bad type passed to newarray"); } this_idata->operand.fi = full_info; break; } /* Fudge iload_x, aload_x, etc to look like their generic cousin. */ - case opc_iload_0: case opc_iload_1: case opc_iload_2: case opc_iload_3: - this_idata->opcode = opc_iload; - var = opcode - opc_iload_0; + case JVM_OPC_iload_0: case JVM_OPC_iload_1: case JVM_OPC_iload_2: case JVM_OPC_iload_3: + this_idata->opcode = JVM_OPC_iload; + var = opcode - JVM_OPC_iload_0; goto check_local_variable; - case opc_fload_0: case opc_fload_1: case opc_fload_2: case opc_fload_3: - this_idata->opcode = opc_fload; - var = opcode - opc_fload_0; + case JVM_OPC_fload_0: case JVM_OPC_fload_1: case JVM_OPC_fload_2: case JVM_OPC_fload_3: + this_idata->opcode = JVM_OPC_fload; + var = opcode - JVM_OPC_fload_0; goto check_local_variable; - case opc_aload_0: case opc_aload_1: case opc_aload_2: case opc_aload_3: - this_idata->opcode = opc_aload; - var = opcode - opc_aload_0; + case JVM_OPC_aload_0: case JVM_OPC_aload_1: case JVM_OPC_aload_2: case JVM_OPC_aload_3: + this_idata->opcode = JVM_OPC_aload; + var = opcode - JVM_OPC_aload_0; goto check_local_variable; - case opc_lload_0: case opc_lload_1: case opc_lload_2: case opc_lload_3: - this_idata->opcode = opc_lload; - var = opcode - opc_lload_0; + case JVM_OPC_lload_0: case JVM_OPC_lload_1: case JVM_OPC_lload_2: case JVM_OPC_lload_3: + this_idata->opcode = JVM_OPC_lload; + var = opcode - JVM_OPC_lload_0; goto check_local_variable2; - case opc_dload_0: case opc_dload_1: case opc_dload_2: case opc_dload_3: - this_idata->opcode = opc_dload; - var = opcode - opc_dload_0; + case JVM_OPC_dload_0: case JVM_OPC_dload_1: case JVM_OPC_dload_2: case JVM_OPC_dload_3: + this_idata->opcode = JVM_OPC_dload; + var = opcode - JVM_OPC_dload_0; goto check_local_variable2; - case opc_istore_0: case opc_istore_1: case opc_istore_2: case opc_istore_3: - this_idata->opcode = opc_istore; - var = opcode - opc_istore_0; + case JVM_OPC_istore_0: case JVM_OPC_istore_1: case JVM_OPC_istore_2: case JVM_OPC_istore_3: + this_idata->opcode = JVM_OPC_istore; + var = opcode - JVM_OPC_istore_0; goto check_local_variable; - case opc_fstore_0: case opc_fstore_1: case opc_fstore_2: case opc_fstore_3: - this_idata->opcode = opc_fstore; - var = opcode - opc_fstore_0; + case JVM_OPC_fstore_0: case JVM_OPC_fstore_1: case JVM_OPC_fstore_2: case JVM_OPC_fstore_3: + this_idata->opcode = JVM_OPC_fstore; + var = opcode - JVM_OPC_fstore_0; goto check_local_variable; - case opc_astore_0: case opc_astore_1: case opc_astore_2: case opc_astore_3: - this_idata->opcode = opc_astore; - var = opcode - opc_astore_0; + case JVM_OPC_astore_0: case JVM_OPC_astore_1: case JVM_OPC_astore_2: case JVM_OPC_astore_3: + this_idata->opcode = JVM_OPC_astore; + var = opcode - JVM_OPC_astore_0; goto check_local_variable; - case opc_lstore_0: case opc_lstore_1: case opc_lstore_2: case opc_lstore_3: - this_idata->opcode = opc_lstore; - var = opcode - opc_lstore_0; + case JVM_OPC_lstore_0: case JVM_OPC_lstore_1: case JVM_OPC_lstore_2: case JVM_OPC_lstore_3: + this_idata->opcode = JVM_OPC_lstore; + var = opcode - JVM_OPC_lstore_0; goto check_local_variable2; - case opc_dstore_0: case opc_dstore_1: case opc_dstore_2: case opc_dstore_3: - this_idata->opcode = opc_dstore; - var = opcode - opc_dstore_0; + case JVM_OPC_dstore_0: case JVM_OPC_dstore_1: case JVM_OPC_dstore_2: case JVM_OPC_dstore_3: + this_idata->opcode = JVM_OPC_dstore; + var = opcode - JVM_OPC_dstore_0; goto check_local_variable2; - case opc_wide: + case JVM_OPC_wide: this_idata->opcode = code[offset + 1]; var = (code[offset + 2] << 8) + code[offset + 3]; switch(this_idata->opcode) { - case opc_lload: case opc_dload: - case opc_lstore: case opc_dstore: + case JVM_OPC_lload: case JVM_OPC_dload: + case JVM_OPC_lstore: case JVM_OPC_dstore: goto check_local_variable2; default: goto check_local_variable; } - case opc_iinc: /* the increment amount doesn't matter */ - case opc_ret: - case opc_aload: case opc_iload: case opc_fload: - case opc_astore: case opc_istore: case opc_fstore: + case JVM_OPC_iinc: /* the increment amount doesn't matter */ + case JVM_OPC_ret: + case JVM_OPC_aload: case JVM_OPC_iload: case JVM_OPC_fload: + case JVM_OPC_astore: case JVM_OPC_istore: case JVM_OPC_fstore: var = code[offset + 1]; check_local_variable: /* Make sure that the variable number isn't illegal. */ @@ -1456,7 +1453,7 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) CCerror(context, "Illegal local variable number"); break; - case opc_lload: case opc_dload: case opc_lstore: case opc_dstore: + case JVM_OPC_lload: case JVM_OPC_dload: case JVM_OPC_lstore: case JVM_OPC_dstore: var = code[offset + 1]; check_local_variable2: /* Make sure that the variable number isn't illegal. */ @@ -1466,7 +1463,7 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) break; default: - if (opcode >= opc_breakpoint) + if (opcode > JVM_OPC_MAX) CCerror(context, "Quick instructions shouldn't appear yet."); break; } /* of switch */ @@ -1474,11 +1471,11 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) static void -set_protected(context_type *context, unsigned int inumber, int key, opcode_type opcode) +set_protected(context_type *context, unsigned int inumber, int key, int opcode) { JNIEnv *env = context->env; fullinfo_type clazz_info; - if (opcode != opc_invokevirtual && opcode != opc_invokespecial) { + if (opcode != JVM_OPC_invokevirtual && opcode != JVM_OPC_invokespecial) { clazz_info = cp_index_to_class_fullinfo(context, key, JVM_CONSTANT_Fieldref); } else { @@ -1497,7 +1494,7 @@ set_protected(context_type *context, unsigned int inumber, int key, opcode_type calledClass = (*env)->NewLocalRef(env, calledClass); do { jclass tmp_cb; - if (opcode != opc_invokevirtual && opcode != opc_invokespecial) { + if (opcode != JVM_OPC_invokevirtual && opcode != JVM_OPC_invokespecial) { access = JVM_GetCPFieldModifiers (env, context->class, key, calledClass); } else { @@ -1607,9 +1604,10 @@ initialize_exception_table(context_type *context) */ static int instruction_length(unsigned char *iptr, unsigned char *end) { + static unsigned char opcode_length[] = JVM_OPCODE_LENGTH_INITIALIZER; int instruction = *iptr; switch (instruction) { - case opc_tableswitch: { + case JVM_OPC_tableswitch: { int *lpc = (int *)UCALIGN(iptr + 1); int index; if (lpc + 2 >= (int *)end) { @@ -1623,7 +1621,7 @@ static int instruction_length(unsigned char *iptr, unsigned char *end) } } - case opc_lookupswitch: { + case JVM_OPC_lookupswitch: { int *lpc = (int *) UCALIGN(iptr + 1); int npairs; if (lpc + 1 >= (int *)end) @@ -1638,18 +1636,18 @@ static int instruction_length(unsigned char *iptr, unsigned char *end) return (unsigned char *)(&lpc[2 * (npairs + 1)]) - iptr; } - case opc_wide: + case JVM_OPC_wide: if (iptr + 1 >= end) return -1; /* do not read pass the end */ switch(iptr[1]) { - case opc_ret: - case opc_iload: case opc_istore: - case opc_fload: case opc_fstore: - case opc_aload: case opc_astore: - case opc_lload: case opc_lstore: - case opc_dload: case opc_dstore: + case JVM_OPC_ret: + case JVM_OPC_iload: case JVM_OPC_istore: + case JVM_OPC_fload: case JVM_OPC_fstore: + case JVM_OPC_aload: case JVM_OPC_astore: + case JVM_OPC_lload: case JVM_OPC_lstore: + case JVM_OPC_dload: case JVM_OPC_dstore: return 4; - case opc_iinc: + case JVM_OPC_iinc: return 6; default: return -1; @@ -1767,7 +1765,7 @@ run_dataflow(context_type *context) { jclass cb = context->class; int max_stack_size = JVM_GetMethodIxMaxStack(env, cb, mi); instruction_data_type *idata = context->instruction_data; - int icount = context->instruction_count; + unsigned int icount = context->instruction_count; jboolean work_to_do = JNI_TRUE; unsigned int inumber; @@ -1839,7 +1837,7 @@ check_register_values(context_type *context, unsigned int inumber) { instruction_data_type *idata = context->instruction_data; instruction_data_type *this_idata = &idata[inumber]; - opcode_type opcode = this_idata->opcode; + int opcode = this_idata->opcode; int operand = this_idata->operand.i; int register_count = this_idata->register_info.register_count; fullinfo_type *registers = this_idata->register_info.registers; @@ -1849,17 +1847,17 @@ check_register_values(context_type *context, unsigned int inumber) switch (opcode) { default: return; - case opc_iload: case opc_iinc: + case JVM_OPC_iload: case JVM_OPC_iinc: type = ITEM_Integer; break; - case opc_fload: + case JVM_OPC_fload: type = ITEM_Float; break; - case opc_aload: + case JVM_OPC_aload: type = ITEM_Object; break; - case opc_ret: + case JVM_OPC_ret: type = ITEM_ReturnAddress; break; - case opc_lload: + case JVM_OPC_lload: type = ITEM_Long; double_word = JNI_TRUE; break; - case opc_dload: + case JVM_OPC_dload: type = ITEM_Double; double_word = JNI_TRUE; break; } if (!double_word) { @@ -1871,7 +1869,7 @@ check_register_values(context_type *context, unsigned int inumber) } reg = registers[operand]; - if (WITH_ZERO_EXTRA_INFO(reg) == MAKE_FULLINFO(type, 0, 0)) { + if (WITH_ZERO_EXTRA_INFO(reg) == (unsigned)MAKE_FULLINFO(type, 0, 0)) { /* the register is obviously of the given type */ return; } else if (GET_INDIRECTION(reg) > 0 && type == ITEM_Object) { @@ -1882,7 +1880,7 @@ check_register_values(context_type *context, unsigned int inumber) operand); /* alternatively (GET_ITEM_TYPE(reg) == ITEM_ReturnAddress) - && (opcode == opc_iload) + && (opcode == JVM_OPC_iload) && (type == ITEM_Object || type == ITEM_Integer) but this never occurs */ @@ -1902,8 +1900,8 @@ check_register_values(context_type *context, unsigned int inumber) "Accessing value from uninitialized register pair %d/%d", operand, operand+1); } else { - if ((registers[operand] == MAKE_FULLINFO(type, 0, 0)) && - (registers[operand + 1] == MAKE_FULLINFO(type + 1, 0, 0))) { + if ((registers[operand] == (unsigned)MAKE_FULLINFO(type, 0, 0)) && + (registers[operand + 1] == (unsigned)MAKE_FULLINFO(type + 1, 0, 0))) { return; } else { CCerror(context, "Register pair %d/%d contains wrong type", @@ -1922,16 +1920,16 @@ check_flags(context_type *context, unsigned int inumber) { instruction_data_type *idata = context->instruction_data; instruction_data_type *this_idata = &idata[inumber]; - opcode_type opcode = this_idata->opcode; + int opcode = this_idata->opcode; switch (opcode) { - case opc_return: + case JVM_OPC_return: /* We need a constructor, but we aren't guaranteed it's called */ if ((this_idata->or_flags & FLAG_NEED_CONSTRUCTOR) && !(this_idata->and_flags & FLAG_CONSTRUCTED)) CCerror(context, "Constructor must call super() or this()"); /* fall through */ - case opc_ireturn: case opc_lreturn: - case opc_freturn: case opc_dreturn: case opc_areturn: + case JVM_OPC_ireturn: case JVM_OPC_lreturn: + case JVM_OPC_freturn: case JVM_OPC_dreturn: case JVM_OPC_areturn: if (this_idata->or_flags & FLAG_NO_RETURN) /* This method cannot exit normally */ CCerror(context, "Cannot return normally"); @@ -1950,7 +1948,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac { instruction_data_type *idata = context->instruction_data; instruction_data_type *this_idata = &idata[inumber]; - opcode_type opcode = this_idata->opcode; + int opcode = this_idata->opcode; stack_item_type *stack = this_idata->stack_info.stack; int stack_size = this_idata->stack_info.stack_size; char *stack_operands, *p; @@ -1958,7 +1956,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac fullinfo_type stack_extra_info_buffer[256]; /* save info popped off stack */ fullinfo_type *stack_extra_info = &stack_extra_info_buffer[256]; fullinfo_type full_info; /* only used in case of invoke instructions */ - fullinfo_type put_full_info; /* only used in case opc_putstatic and opc_putfield */ + fullinfo_type put_full_info; /* only used in case JVM_OPC_putstatic and JVM_OPC_putfield */ switch(opcode) { default: @@ -1966,7 +1964,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac stack_operands = opcode_in_out[opcode][0]; break; - case opc_putstatic: case opc_putfield: { + case JVM_OPC_putstatic: case JVM_OPC_putfield: { /* The top thing on the stack depends on the signature of * the object. */ int operand = this_idata->operand.i; @@ -1981,7 +1979,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac print_formatted_fieldname(context, operand); } #endif - if (opcode == opc_putfield) + if (opcode == JVM_OPC_putfield) *ip++ = 'A'; /* object for putfield */ *ip++ = signature_to_fieldtype(context, &signature, &put_full_info); *ip = '\0'; @@ -1990,9 +1988,9 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac break; } - case opc_invokevirtual: case opc_invokespecial: - case opc_invokeinit: /* invokespecial call to */ - case opc_invokestatic: case opc_invokeinterface: { + case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial: + case JVM_OPC_invokeinit: /* invokespecial call to */ + case JVM_OPC_invokestatic: case JVM_OPC_invokeinterface: { /* The top stuff on the stack depends on the method signature */ int operand = this_idata->operand.i; const char *signature = @@ -2007,9 +2005,9 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac print_formatted_methodname(context, operand); } #endif - if (opcode != opc_invokestatic) + if (opcode != JVM_OPC_invokestatic) /* First, push the object */ - *ip++ = (opcode == opc_invokeinit ? '@' : 'A'); + *ip++ = (opcode == JVM_OPC_invokeinit ? '@' : 'A'); for (p = signature + 1; *p != JVM_SIGNATURE_ENDFUNC; ) { *ip++ = signature_to_fieldtype(context, &p, &full_info); if (ip >= buffer + sizeof(buffer) - 1) @@ -2022,7 +2020,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac break; } - case opc_multianewarray: { + case JVM_OPC_multianewarray: { /* Count can't be larger than 255. So can't overflow buffer */ int count = this_idata->operand2.i; /* number of ints on stack */ memset(buffer, 'I', count); @@ -2062,19 +2060,19 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac * one of the special cases */ if ( (WITH_ZERO_EXTRA_INFO(top_type) == MAKE_FULLINFO(ITEM_ReturnAddress, 0, 0)) - && (opcode == opc_astore)) + && (opcode == JVM_OPC_astore)) break; if ( (GET_ITEM_TYPE(top_type) == ITEM_NewObject || (GET_ITEM_TYPE(top_type) == ITEM_InitObject)) - && ((opcode == opc_astore) || (opcode == opc_aload) - || (opcode == opc_ifnull) || (opcode == opc_ifnonnull))) + && ((opcode == JVM_OPC_astore) || (opcode == JVM_OPC_aload) + || (opcode == JVM_OPC_ifnull) || (opcode == JVM_OPC_ifnonnull))) break; /* The 2nd edition VM of the specification allows field * initializations before the superclass initializer, * if the field is defined within the current class. */ if ( (GET_ITEM_TYPE(top_type) == ITEM_InitObject) - && (opcode == opc_putfield)) { + && (opcode == JVM_OPC_putfield)) { int operand = this_idata->operand.i; int access_bits = JVM_GetCPFieldModifiers(context->env, context->class, @@ -2231,7 +2229,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac */ switch (opcode) { default: break; - case opc_aastore: { /* array index object */ + case JVM_OPC_aastore: { /* array index object */ fullinfo_type array_type = stack_extra_info[0]; fullinfo_type object_type = stack_extra_info[2]; fullinfo_type target_type = decrement_indirection(array_type); @@ -2246,12 +2244,12 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac break; } - case opc_putfield: - case opc_getfield: - case opc_putstatic: { + case JVM_OPC_putfield: + case JVM_OPC_getfield: + case JVM_OPC_putstatic: { int operand = this_idata->operand.i; fullinfo_type stack_object = stack_extra_info[0]; - if (opcode == opc_putfield || opcode == opc_getfield) { + if (opcode == JVM_OPC_putfield || opcode == JVM_OPC_getfield) { if (!isAssignableTo (context, stack_object, @@ -2266,8 +2264,8 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac CCerror(context, "Bad access to protected data"); } } - if (opcode == opc_putfield || opcode == opc_putstatic) { - int item = (opcode == opc_putfield ? 1 : 0); + if (opcode == JVM_OPC_putfield || opcode == JVM_OPC_putstatic) { + int item = (opcode == JVM_OPC_putfield ? 1 : 0); if (!isAssignableTo(context, stack_extra_info[item], put_full_info)) { CCerror(context, "Bad type in putfield/putstatic"); @@ -2276,23 +2274,23 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac break; } - case opc_athrow: + case JVM_OPC_athrow: if (!isAssignableTo(context, stack_extra_info[0], context->throwable_info)) { CCerror(context, "Can only throw Throwable objects"); } break; - case opc_aaload: { /* array index */ + case JVM_OPC_aaload: { /* array index */ /* We need to pass the information to the stack updater */ fullinfo_type array_type = stack_extra_info[0]; context->swap_table[0] = decrement_indirection(array_type); break; } - case opc_invokevirtual: case opc_invokespecial: - case opc_invokeinit: - case opc_invokeinterface: case opc_invokestatic: { + case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial: + case JVM_OPC_invokeinit: + case JVM_OPC_invokeinterface: case JVM_OPC_invokestatic: { int operand = this_idata->operand.i; const char *signature = JVM_GetCPMethodSignatureUTF(context->env, @@ -2301,15 +2299,15 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac int item; const char *p; check_and_push(context, signature, VM_STRING_UTF); - if (opcode == opc_invokestatic) { + if (opcode == JVM_OPC_invokestatic) { item = 0; - } else if (opcode == opc_invokeinit) { + } else if (opcode == JVM_OPC_invokeinit) { fullinfo_type init_type = this_idata->operand2.fi; fullinfo_type object_type = stack_extra_info[0]; context->swap_table[0] = object_type; /* save value */ if (GET_ITEM_TYPE(stack_extra_info[0]) == ITEM_NewObject) { /* We better be calling the appropriate init. Find the - * inumber of the "opc_new" instruction", and figure + * inumber of the "JVM_OPC_new" instruction", and figure * out what the type really is. */ unsigned int new_inumber = GET_EXTRA_INFO(stack_extra_info[0]); @@ -2341,7 +2339,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac CCerror(context, "Incompatible object argument for function call"); } - if (opcode == opc_invokespecial + if (opcode == JVM_OPC_invokespecial && !isAssignableTo(context, object_type, context->currentclass_info)) { /* Make sure object argument is assignment compatible to current class */ @@ -2381,13 +2379,13 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac break; } - case opc_return: + case JVM_OPC_return: if (context->return_type != MAKE_FULLINFO(ITEM_Void, 0, 0)) CCerror(context, "Wrong return type in function"); break; - case opc_ireturn: case opc_lreturn: case opc_freturn: - case opc_dreturn: case opc_areturn: { + case JVM_OPC_ireturn: case JVM_OPC_lreturn: case JVM_OPC_freturn: + case JVM_OPC_dreturn: case JVM_OPC_areturn: { fullinfo_type target_type = context->return_type; fullinfo_type object_type = stack_extra_info[0]; if (!isAssignableTo(context, object_type, target_type)) { @@ -2396,7 +2394,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac break; } - case opc_new: { + case JVM_OPC_new: { /* Make sure that nothing on the stack already looks like what * we want to create. I can't image how this could possibly happen * but we should test for it anyway, since if it could happen, the @@ -2433,7 +2431,7 @@ update_registers(context_type *context, unsigned int inumber, { instruction_data_type *idata = context->instruction_data; instruction_data_type *this_idata = &idata[inumber]; - opcode_type opcode = this_idata->opcode; + int opcode = this_idata->opcode; int operand = this_idata->operand.i; int register_count = this_idata->register_info.register_count; fullinfo_type *registers = this_idata->register_info.registers; @@ -2453,11 +2451,11 @@ update_registers(context_type *context, unsigned int inumber, /* Remember, we've already verified the type at the top of the stack. */ switch (opcode) { default: break; - case opc_istore: case opc_fstore: case opc_astore: + case JVM_OPC_istore: case JVM_OPC_fstore: case JVM_OPC_astore: access = ACCESS_SINGLE; goto continue_store; - case opc_lstore: case opc_dstore: + case JVM_OPC_lstore: case JVM_OPC_dstore: access = ACCESS_DOUBLE; goto continue_store; @@ -2484,16 +2482,16 @@ update_registers(context_type *context, unsigned int inumber, break; } - case opc_iload: case opc_fload: case opc_aload: - case opc_iinc: case opc_ret: + case JVM_OPC_iload: case JVM_OPC_fload: case JVM_OPC_aload: + case JVM_OPC_iinc: case JVM_OPC_ret: access = ACCESS_SINGLE; break; - case opc_lload: case opc_dload: + case JVM_OPC_lload: case JVM_OPC_dload: access = ACCESS_DOUBLE; break; - case opc_jsr: case opc_jsr_w: + case JVM_OPC_jsr: case JVM_OPC_jsr_w: for (i = 0; i < new_mask_count; i++) if (new_masks[i].entry == operand) CCerror(context, "Recursive call to jsr entry"); @@ -2501,8 +2499,8 @@ update_registers(context_type *context, unsigned int inumber, new_mask_count++; break; - case opc_invokeinit: - case opc_new: { + case JVM_OPC_invokeinit: + case JVM_OPC_new: { /* For invokeinit, an uninitialized object has been initialized. * For new, all previous occurrences of an uninitialized object * from the same instruction must be made bogus. @@ -2588,7 +2586,7 @@ update_flags(context_type *context, unsigned int inumber, flag_type or_flags = this_idata->or_flags; /* Set the "we've done a constructor" flag */ - if (this_idata->opcode == opc_invokeinit) { + if (this_idata->opcode == JVM_OPC_invokeinit) { fullinfo_type from = context->swap_table[0]; if (from == MAKE_FULLINFO(ITEM_InitObject, 0, 0)) and_flags |= FLAG_CONSTRUCTED; @@ -2611,7 +2609,7 @@ push_stack(context_type *context, unsigned int inumber, stack_info_type *new_sta { instruction_data_type *idata = context->instruction_data; instruction_data_type *this_idata = &idata[inumber]; - opcode_type opcode = this_idata->opcode; + int opcode = this_idata->opcode; int operand = this_idata->operand.i; int stack_size = new_stack_info->stack_size; @@ -2631,7 +2629,7 @@ push_stack(context_type *context, unsigned int inumber, stack_info_type *new_sta stack_results = opcode_in_out[opcode][1]; break; - case opc_ldc: case opc_ldc_w: case opc_ldc2_w: { + case JVM_OPC_ldc: case JVM_OPC_ldc_w: case JVM_OPC_ldc2_w: { /* Look to constant pool to determine correct result. */ unsigned char *type_table = context->constant_types; switch (type_table[operand]) { @@ -2661,7 +2659,7 @@ push_stack(context_type *context, unsigned int inumber, stack_info_type *new_sta break; } - case opc_getstatic: case opc_getfield: { + case JVM_OPC_getstatic: case JVM_OPC_getfield: { /* Look to signature to determine correct result. */ int operand = this_idata->operand.i; const char *signature = JVM_GetCPFieldSignatureUTF(context->env, @@ -2680,9 +2678,9 @@ push_stack(context_type *context, unsigned int inumber, stack_info_type *new_sta break; } - case opc_invokevirtual: case opc_invokespecial: - case opc_invokeinit: - case opc_invokestatic: case opc_invokeinterface: { + case JVM_OPC_invokevirtual: case JVM_OPC_invokespecial: + case JVM_OPC_invokeinit: + case JVM_OPC_invokestatic: case JVM_OPC_invokeinterface: { /* Look to signature to determine correct result. */ int operand = this_idata->operand.i; const char *signature = JVM_GetCPMethodSignatureUTF(context->env, @@ -2703,28 +2701,28 @@ push_stack(context_type *context, unsigned int inumber, stack_info_type *new_sta break; } - case opc_aconst_null: + case JVM_OPC_aconst_null: stack_results = opcode_in_out[opcode][1]; full_info = NULL_FULLINFO; /* special NULL */ break; - case opc_new: - case opc_checkcast: - case opc_newarray: - case opc_anewarray: - case opc_multianewarray: + case JVM_OPC_new: + case JVM_OPC_checkcast: + case JVM_OPC_newarray: + case JVM_OPC_anewarray: + case JVM_OPC_multianewarray: stack_results = opcode_in_out[opcode][1]; /* Conveniently, this result type is stored here */ full_info = this_idata->operand.fi; break; - case opc_aaload: + case JVM_OPC_aaload: stack_results = opcode_in_out[opcode][1]; /* pop_stack() saved value for us. */ full_info = context->swap_table[0]; break; - case opc_aload: + case JVM_OPC_aload: stack_results = opcode_in_out[opcode][1]; /* The register hasn't been modified, so we can use its value. */ full_info = this_idata->register_info.registers[operand]; @@ -2772,7 +2770,7 @@ push_stack(context_type *context, unsigned int inumber, stack_info_type *new_sta stack_size++; } /* outer for loop */ - if (opcode == opc_invokeinit) { + if (opcode == JVM_OPC_invokeinit) { /* If there are any instances of "from" on the stack, we need to * replace it with "to", since calling initializes all versions * of the object, obviously. */ @@ -2807,7 +2805,7 @@ merge_into_successors(context_type *context, unsigned int inumber, { instruction_data_type *idata = context->instruction_data; instruction_data_type *this_idata = &idata[inumber]; - opcode_type opcode = this_idata->opcode; + int opcode = this_idata->opcode; int operand = this_idata->operand.i; struct handler_info_type *handler_info = context->handler_info; int handler_info_length = @@ -2827,35 +2825,35 @@ merge_into_successors(context_type *context, unsigned int inumber, buffer[0] = inumber + 1; break; - case opc_ifeq: case opc_ifne: case opc_ifgt: - case opc_ifge: case opc_iflt: case opc_ifle: - case opc_ifnull: case opc_ifnonnull: - case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpgt: - case opc_if_icmpge: case opc_if_icmplt: case opc_if_icmple: - case opc_if_acmpeq: case opc_if_acmpne: + case JVM_OPC_ifeq: case JVM_OPC_ifne: case JVM_OPC_ifgt: + case JVM_OPC_ifge: case JVM_OPC_iflt: case JVM_OPC_ifle: + case JVM_OPC_ifnull: case JVM_OPC_ifnonnull: + case JVM_OPC_if_icmpeq: case JVM_OPC_if_icmpne: case JVM_OPC_if_icmpgt: + case JVM_OPC_if_icmpge: case JVM_OPC_if_icmplt: case JVM_OPC_if_icmple: + case JVM_OPC_if_acmpeq: case JVM_OPC_if_acmpne: successors_count = 2; buffer[0] = inumber + 1; buffer[1] = operand; break; - case opc_jsr: case opc_jsr_w: + case JVM_OPC_jsr: case JVM_OPC_jsr_w: if (this_idata->operand2.i != UNKNOWN_RET_INSTRUCTION) idata[this_idata->operand2.i].changed = JNI_TRUE; /* FALLTHROUGH */ - case opc_goto: case opc_goto_w: + case JVM_OPC_goto: case JVM_OPC_goto_w: successors_count = 1; buffer[0] = operand; break; - case opc_ireturn: case opc_lreturn: case opc_return: - case opc_freturn: case opc_dreturn: case opc_areturn: - case opc_athrow: + case JVM_OPC_ireturn: case JVM_OPC_lreturn: case JVM_OPC_return: + case JVM_OPC_freturn: case JVM_OPC_dreturn: case JVM_OPC_areturn: + case JVM_OPC_athrow: /* The testing for the returns is handled in pop_stack() */ successors_count = 0; break; - case opc_ret: { + case JVM_OPC_ret: { /* This is slightly slow, but good enough for a seldom used instruction. * The EXTRA_ITEM_INFO of the ITEM_ReturnAddress indicates the * address of the first instruction of the subroutine. We can return @@ -2866,16 +2864,16 @@ merge_into_successors(context_type *context, unsigned int inumber, int called_instruction = GET_EXTRA_INFO(registers[operand]); int i, count, *ptr;; for (i = context->instruction_count, count = 0; --i >= 0; ) { - if (((idata[i].opcode == opc_jsr) || - (idata[i].opcode == opc_jsr_w)) && + if (((idata[i].opcode == JVM_OPC_jsr) || + (idata[i].opcode == JVM_OPC_jsr_w)) && (idata[i].operand.i == called_instruction)) count++; } this_idata->operand2.ip = ptr = NEW(int, count + 1); *ptr++ = count; for (i = context->instruction_count, count = 0; --i >= 0; ) { - if (((idata[i].opcode == opc_jsr) || - (idata[i].opcode == opc_jsr_w)) && + if (((idata[i].opcode == JVM_OPC_jsr) || + (idata[i].opcode == JVM_OPC_jsr_w)) && (idata[i].operand.i == called_instruction)) *ptr++ = i + 1; } @@ -2886,8 +2884,8 @@ merge_into_successors(context_type *context, unsigned int inumber, } - case opc_tableswitch: - case opc_lookupswitch: + case JVM_OPC_tableswitch: + case JVM_OPC_lookupswitch: successors = this_idata->operand.ip; /* use this instead */ successors_count = *successors++; break; @@ -2907,9 +2905,9 @@ merge_into_successors(context_type *context, unsigned int inumber, handler_info = context->handler_info; for (i = handler_info_length; --i >= 0; handler_info++) { - if (handler_info->start <= inumber && handler_info->end > inumber) { + if (handler_info->start <= (int)inumber && handler_info->end > (int)inumber) { int handler = handler_info->handler; - if (opcode != opc_invokeinit) { + if (opcode != JVM_OPC_invokeinit) { merge_into_one_successor(context, inumber, handler, &this_idata->register_info, /* old */ &handler_info->stack_info, @@ -2984,9 +2982,9 @@ merge_into_one_successor(context_type *context, * ret are executed. Thus uninitialized objects can't propagate * into or out of a subroutine. */ - if (idata[from_inumber].opcode == opc_ret || - idata[from_inumber].opcode == opc_jsr || - idata[from_inumber].opcode == opc_jsr_w) { + if (idata[from_inumber].opcode == JVM_OPC_ret || + idata[from_inumber].opcode == JVM_OPC_jsr || + idata[from_inumber].opcode == JVM_OPC_jsr_w) { int new_register_count = new_register_info->register_count; fullinfo_type *new_registers = new_register_info->registers; int i; @@ -3036,7 +3034,7 @@ merge_into_one_successor(context_type *context, * that needs to get merged into the new instruction is a joining * of info from the ret instruction with stuff in the jsr instruction */ - if (idata[from_inumber].opcode == opc_ret && !isException) { + if (idata[from_inumber].opcode == JVM_OPC_ret && !isException) { int new_register_count = new_register_info->register_count; fullinfo_type *new_registers = new_register_info->registers; int new_mask_count = new_register_info->mask_count; @@ -3045,7 +3043,7 @@ merge_into_one_successor(context_type *context, int called_instruction = GET_EXTRA_INFO(new_registers[operand]); instruction_data_type *jsr_idata = &idata[to_inumber - 1]; register_info_type *jsr_reginfo = &jsr_idata->register_info; - if (jsr_idata->operand2.i != from_inumber) { + if (jsr_idata->operand2.i != (int)from_inumber) { if (jsr_idata->operand2.i != UNKNOWN_RET_INSTRUCTION) CCerror(context, "Multiple returns to single jsr"); jsr_idata->operand2.i = from_inumber; @@ -3675,7 +3673,7 @@ signature_to_fieldtype(context_type *context, char *buffer = buffer_space; char *finish = strchr(p, JVM_SIGNATURE_ENDCLASS); int length = finish - p; - if (length + 1 > sizeof(buffer_space)) { + if (length + 1 > (int)sizeof(buffer_space)) { buffer = malloc(length + 1); check_and_push(context, buffer, VM_MALLOC_BLK); } diff --git a/src/share/native/common/check_format.c b/src/share/native/common/check_format.c index 26539d133..0b5f42077 100644 --- a/src/share/native/common/check_format.c +++ b/src/share/native/common/check_format.c @@ -1,5 +1,5 @@ /* - * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -246,7 +246,7 @@ VerifyClassname(char *name, jboolean allowArrayClass) /* skip over the fieldname. Slashes are okay */ p = skip_over_fieldname(name, JNI_TRUE, length); } - return (p != 0 && p - name == length); + return (p != 0 && p - name == (ptrdiff_t)length); } /* diff --git a/src/solaris/back/util_md.h b/src/solaris/back/util_md.h index 52f7d18aa..10853f4e6 100644 --- a/src/solaris/back/util_md.h +++ b/src/solaris/back/util_md.h @@ -1,5 +1,5 @@ /* - * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,9 @@ #ifndef JDWP_UTIL_MD_H #define JDWP_UTIL_MD_H +#include +#include /* To get uintptr_t */ + #include #include diff --git a/src/solaris/instrument/FileSystemSupport_md.h b/src/solaris/instrument/FileSystemSupport_md.h index c092193f1..a83e3d16f 100644 --- a/src/solaris/instrument/FileSystemSupport_md.h +++ b/src/solaris/instrument/FileSystemSupport_md.h @@ -1,5 +1,5 @@ /* - * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,5 +23,8 @@ * have any questions. */ +#include +#include /* For uintprt_t */ #include #include /* For MAXPATHLEN */ + diff --git a/src/solaris/javavm/export/jvm_md.h b/src/solaris/javavm/export/jvm_md.h index 2789492ee..4c6b8e14e 100644 --- a/src/solaris/javavm/export/jvm_md.h +++ b/src/solaris/javavm/export/jvm_md.h @@ -1,5 +1,5 @@ /* - * Copyright 1997-1999 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,8 @@ #include /* For DIR */ #include /* For MAXPATHLEN */ #include /* For F_OK, R_OK, W_OK */ +#include /* For ptrdiff_t */ +#include /* For uintptr_t */ #define JNI_ONLOAD_SYMBOLS {"JNI_OnLoad"} #define JNI_ONUNLOAD_SYMBOLS {"JNI_OnUnload"} diff --git a/src/solaris/javavm/include/typedefs_md.h b/src/solaris/javavm/include/typedefs_md.h deleted file mode 100644 index 552a7951a..000000000 --- a/src/solaris/javavm/include/typedefs_md.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 1994-2002 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -/* - * Solaris-dependent types for Green threads - */ - -#ifndef _JAVASOFT_SOLARIS_TYPES_MD_H_ -#define _JAVASOFT_SOLARIS_TYPES_MD_H_ - -#include -#include - -#ifdef __linux__ -#include -#define HAVE_INTPTR_T -#define _UINT64_T -#endif - -#define int8_t char - -/* Fix for varargs differences on PowerPC */ -#if defined(__powerpc__) -#define VARGS(x) (x) -#else -#define VARGS(x) (&x) -#endif /* __powerpc__ */ - - -#if defined(__alpha__) -#define PTR_IS_64 1 -#define LONG_IS_64 1 -#else -#define PTR_IS_32 1 -#endif - -/* don't redefine typedef's on Solaris 2.6 or Later */ - -#if !defined(_ILP32) && !defined(_LP64) - -#ifndef HAVE_INTPTR_T -#ifdef LONG_IS_64 -typedef long intptr_t; -typedef unsigned long uintptr_t; -#else -typedef int intptr_t; -typedef unsigned int uintptr_t; -#endif /* LONG_IS_64 */ -#endif /* don't HAVE_INTPTR_T */ - -#ifndef _UINT64_T -#define _UINT64_T -#ifdef LONG_IS_64 -typedef unsigned long uint64_t; -#else -typedef unsigned long long uint64_t; -#endif -#define _UINT32_T -#ifndef uint32_t /* [sbb] scaffolding */ -typedef unsigned int uint32_t; -#endif /* [sbb] scaffolding */ -#if defined(__linux__) -typedef unsigned int uint_t; -#endif -#endif - -#ifndef __BIT_TYPES_DEFINED__ -/* that should get Linux, at least */ -#ifndef _INT64_T -#define _INT64_T -#ifdef LONG_IS_64 -typedef long int64_t; -#else -typedef long long int64_t; -#endif -#define _INT32_T -#ifndef int32_t /* [sbb] scaffolding */ -typedef int int32_t; -#endif /* [sbb] scaffolding */ -#if defined(__linux__) -typedef int int_t; -#endif -#endif -#endif /* __BIT_TYPES_DEFINED__ */ - -#endif /* !defined(_ILP32) && !defined(_LP64) */ - -/* use these macros when the compiler supports the long long type */ - -#define ll_high(a) ((uint32_t)(((uint64_t)(a))>>32)) -#define ll_low(a) ((uint32_t)(a)) -#define int2ll(a) ((int64_t)(a)) -#define ll2int(a) ((int)(a)) -#define ll_add(a, b) ((int64_t)(a) + (int64_t)(b)) -#define ll_and(a, b) ((int64_t)(a) & (int64_t)(b)) -#define ll_div(a, b) ((int64_t)(a) / (int64_t)(b)) -#define ll_mul(a, b) ((int64_t)(a) * (int64_t)(b)) -#define ll_neg(a) (-(a)) -#define ll_not(a) (~(uint64_t)(a)) -#define ll_or(a, b) ((uint64_t)(a) | (b)) -#define ll_shl(a, n) ((uint64_t)(a) << (n)) -#define ll_shr(a, n) ((int64_t)(a) >> (n)) -#define ll_sub(a, b) ((uint64_t)(a) - (b)) -#define ll_ushr(a, n) ((uint64_t)(a) >>(n)) -#define ll_xor(a, b) ((int64_t)(a) ^ (int64_t)(b)) -#define uint2ll(a) ((uint64_t)(a)) -#define ll_rem(a,b) ((int64_t)(a) % (int64_t)(b)) - -extern int32_t float2l(float f); -extern int32_t double2l(double d); -extern int64_t float2ll(float f); -extern int64_t double2ll(double d); - -#define ll2float(a) ((float) (a)) -#define ll2double(a) ((double) (a)) - -/* Useful on machines where jlong and jdouble have different endianness. */ -#define ll2double_bits(a) ((void) 0) - -/* comparison operators */ -#define ll_ltz(ll) ((ll)<0) -#define ll_gez(ll) ((ll)>=0) -#define ll_eqz(a) ((a) == 0) -#define ll_nez(a) ((a) != 0) -#define ll_eq(a, b) ((a) == (b)) -#define ll_ne(a,b) ((a) != (b)) -#define ll_ge(a,b) ((a) >= (b)) -#define ll_le(a,b) ((a) <= (b)) -#define ll_lt(a,b) ((a) < (b)) -#define ll_gt(a,b) ((a) > (b)) - -#define ll_zero_const ((int64_t) 0) -#define ll_one_const ((int64_t) 1) - -extern void ll2str(int64_t a, char *s, char *limit); - -#define ll2ptr(a) ((void*)(uintptr_t)(a)) -#define ptr2ll(a) ((int64_t)(uintptr_t)(a)) - -#ifdef ppc -#define HAVE_ALIGNED_DOUBLES -#define HAVE_ALIGNED_LONGLONGS -#endif - -/* printf format modifier for printing pointers */ -#ifdef _LP64 -#define FORMAT64_MODIFIER "l" -#else -#define FORMAT64_MODIFIER "ll" -#endif - -#endif /* !_JAVASOFT_SOLARIS_TYPES_MD_H_ */ diff --git a/src/solaris/native/common/gdefs_md.h b/src/solaris/native/common/gdefs_md.h index 006e753f6..dd74cfc2c 100644 --- a/src/solaris/native/common/gdefs_md.h +++ b/src/solaris/native/common/gdefs_md.h @@ -1,5 +1,5 @@ /* - * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,15 +24,12 @@ */ /* - * Solaris dependent type definitions includes intptr_t, etc + * Solaris/Linux dependent type definitions includes intptr_t, etc */ +#include +#include /* For uintptr_t */ +#include #include -/* - * Linux version of does not define intptr_t - */ -#ifdef __linux__ -#include -#include -#endif /* __linux__ */ + diff --git a/src/solaris/native/common/jlong_md.h b/src/solaris/native/common/jlong_md.h index 446070106..a12044e47 100644 --- a/src/solaris/native/common/jlong_md.h +++ b/src/solaris/native/common/jlong_md.h @@ -1,5 +1,5 @@ /* - * Copyright 1997-2002 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ /* Make sure ptrdiff_t is defined */ #include -#include "typedefs.h" +#include /* For uintptr_t */ #define jlong_high(a) ((jint)((a)>>32)) #define jlong_low(a) ((jint)(a)) diff --git a/src/windows/back/util_md.h b/src/windows/back/util_md.h index 2b9200b77..604b3cccb 100644 --- a/src/windows/back/util_md.h +++ b/src/windows/back/util_md.h @@ -1,5 +1,5 @@ /* - * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ #ifndef JDWP_UTIL_MD_H #define JDWP_UTIL_MD_H +#include /* for uintptr_t */ #include /* for _MAx_PATH */ typedef unsigned __int64 UNSIGNED_JLONG; diff --git a/src/windows/hpi/src/socket_md.c b/src/windows/hpi/src/socket_md.c index 852022a49..8dd5f9568 100644 --- a/src/windows/hpi/src/socket_md.c +++ b/src/windows/hpi/src/socket_md.c @@ -1,5 +1,5 @@ /* - * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ #include "hpi_impl.h" #include "mutex_md.h" -#include "typedefs.h" struct sockaddr; diff --git a/src/windows/hpi/src/threads_md.c b/src/windows/hpi/src/threads_md.c index 410f61c98..1c6b63f33 100644 --- a/src/windows/hpi/src/threads_md.c +++ b/src/windows/hpi/src/threads_md.c @@ -1,5 +1,5 @@ /* - * Copyright 1994-2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,6 @@ #include "threads_md.h" #include "monitor_md.h" -#include "typedefs.h" /* diff --git a/src/windows/instrument/FileSystemSupport_md.h b/src/windows/instrument/FileSystemSupport_md.h index bd9c029bc..9d29ea6fe 100644 --- a/src/windows/instrument/FileSystemSupport_md.h +++ b/src/windows/instrument/FileSystemSupport_md.h @@ -1,5 +1,5 @@ /* - * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ * have any questions. */ +#include /* For uintprt_t */ #include #define MAXPATHLEN _MAX_PATH diff --git a/src/windows/javavm/export/jvm_md.h b/src/windows/javavm/export/jvm_md.h index 76560f6ec..cf98f61ac 100644 --- a/src/windows/javavm/export/jvm_md.h +++ b/src/windows/javavm/export/jvm_md.h @@ -53,6 +53,7 @@ typedef struct { WIN32_FIND_DATA find_data; } DIR; +#include /* For uintptr_t */ #include #define JVM_MAXPATHLEN _MAX_PATH @@ -65,6 +66,19 @@ typedef struct { JNIEXPORT void * JNICALL JVM_GetThreadInterruptEvent(); +/* + * These routines are only reentrant on Windows + */ + +JNIEXPORT struct protoent * JNICALL +JVM_GetProtoByName(char* name); + +JNIEXPORT struct hostent* JNICALL +JVM_GetHostByAddr(const char* name, int len, int type); + +JNIEXPORT struct hostent* JNICALL +JVM_GetHostByName(char* name); + /* * File I/O */ diff --git a/src/windows/javavm/include/typedefs_md.h b/src/windows/javavm/include/typedefs_md.h deleted file mode 100644 index 7c41e9aa7..000000000 --- a/src/windows/javavm/include/typedefs_md.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 1994-2002 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -/* - * Win32 dependent type definitions - */ - -#ifndef _JAVASOFT_WIN32_TYPEDEF_MD_H_ -#define _JAVASOFT_WIN32_TYPEDEF_MD_H_ - -#include - -#define VARGS(x) (&x) - -typedef char int8_t; -typedef __int16 int16_t; -typedef __int32 int32_t; -typedef __int64 int64_t; - -typedef unsigned char uint8_t; -typedef unsigned __int16 uint16_t; -typedef unsigned int uint_t; -typedef unsigned __int32 uint32_t; -typedef unsigned __int64 uint64_t; - -/* Make sure that we have the intptr_t and uintptr_t definitions */ -#ifndef _INTPTR_T_DEFINED -#ifdef _WIN64 -typedef __int64 intptr_t; -#else -typedef int intptr_t; -#endif -#define _INTPTR_T_DEFINED -#endif - -#ifndef _UINTPTR_T_DEFINED -#ifdef _WIN64 -typedef unsigned __int64 uintptr_t; -#else -typedef unsigned int uintptr_t; -#endif -#define _UINTPTR_T_DEFINED -#endif - -typedef intptr_t ssize_t; - -/* use these macros when the compiler supports the long long type */ - -#define ll_high(a) ((long)((a)>>32)) -#define ll_low(a) ((long)(a)) -#define int2ll(a) ((int64_t)(a)) -#define ll2int(a) ((int)(a)) -#define ll_add(a, b) ((a) + (b)) -#define ll_and(a, b) ((a) & (b)) -#define ll_div(a, b) ((a) / (b)) -#define ll_mul(a, b) ((a) * (b)) -#define ll_neg(a) (-(a)) -#define ll_not(a) (~(a)) -#define ll_or(a, b) ((a) | (b)) -/* THE FOLLOWING DEFINITION IS NOW A FUNCTION CALL IN ORDER TO WORKAROUND - OPTIMIZER BUG IN MSVC++ 2.1 (see system_md.c) - #define ll_shl(a, n) ((a) << (n)) */ -#define ll_shr(a, n) ((a) >> (n)) -#define ll_sub(a, b) ((a) - (b)) -#define ll_ushr(a, n) ((uint64_t)(a) >> (n)) -#define ll_xor(a, b) ((a) ^ (b)) -#define uint2ll(a) ((uint64_t)(unsigned long)(a)) -#define ll_rem(a,b) ((a) % (b)) - -int32_t float2l(float f); -int32_t double2l(double f); -int64_t float2ll(float f); -int64_t double2ll(double f); -#define ll2float(a) ((float) (a)) -#define ll2double(a) ((double) (a)) - -/* Useful on machines where jlong and jdouble have different endianness. */ -#define ll2double_bits(a) ((void) 0) - -/* comparison operators */ -#define ll_ltz(ll) ((ll) < 0) -#define ll_gez(ll) ((ll) >= 0) -#define ll_eqz(a) ((a) == 0) -#define ll_nez(a) ((a) != 0) -#define ll_eq(a, b) ((a) == (b)) -#define ll_ne(a,b) ((a) != (b)) -#define ll_ge(a,b) ((a) >= (b)) -#define ll_le(a,b) ((a) <= (b)) -#define ll_lt(a,b) ((a) < (b)) -#define ll_gt(a,b) ((a) > (b)) - -#define ll_zero_const ((int64_t) 0) -#define ll_one_const ((int64_t) 1) - -int64_t ll_shl(int64_t a, int bits); - -#define ll2ptr(a) ((void*)(a)) -#define ptr2ll(a) ((jlong)(a)) - -/* printf format modifier for printing pointers */ -#define FORMAT64_MODIFIER "I64" - -#endif /* !_JAVASOFT_WIN32_TYPEDEF_MD_H_ */ diff --git a/src/windows/native/java/net/net_util_md.c b/src/windows/native/java/net/net_util_md.c index 87dbdcf79..5fe1dc5d9 100644 --- a/src/windows/native/java/net/net_util_md.c +++ b/src/windows/native/java/net/net_util_md.c @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ #include "net_util.h" #include "jni.h" -#include "typedefs.h" #ifndef IPTOS_TOS_MASK #define IPTOS_TOS_MASK 0x1e -- GitLab From c2c89ab0ece6dc74c412bb70a39cf490b9d1ccea Mon Sep 17 00:00:00 2001 From: sherman Date: Fri, 22 Aug 2008 14:37:46 -0700 Subject: [PATCH 061/139] 4486841: UTF-8 decoder should adhere to corrigendum to Unicode 3.0.1 6636317: Optimize UTF-8 coder for ASCII input Summary: re-write the UTF-8 charset to obey the standard and improve the performance Reviewed-by: alanb --- src/share/classes/sun/nio/cs/UTF_8.java | 728 +++++++++++------------- test/sun/nio/cs/TestUTF8.java | 393 +++++++++++++ 2 files changed, 723 insertions(+), 398 deletions(-) create mode 100644 test/sun/nio/cs/TestUTF8.java diff --git a/src/share/classes/sun/nio/cs/UTF_8.java b/src/share/classes/sun/nio/cs/UTF_8.java index b665b4f56..e5eef85bf 100644 --- a/src/share/classes/sun/nio/cs/UTF_8.java +++ b/src/share/classes/sun/nio/cs/UTF_8.java @@ -25,34 +25,39 @@ package sun.nio.cs; +import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.CharBuffer; -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; -import java.nio.charset.CharacterCodingException; -import java.nio.charset.MalformedInputException; -import java.nio.charset.UnmappableCharacterException; - -/* - * # Bits Bit pattern - * 1 7 0xxxxxxx - * 2 11 110xxxxx 10xxxxxx - * 3 16 1110xxxx 10xxxxxx 10xxxxxx - * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - * 5 26 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - * 6 31 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx +/* Legal UTF-8 Byte Sequences + * + * # Code Points Bits Bit/Byte pattern + * 1 7 0xxxxxxx + * U+0000..U+007F 00..7F + * + * 2 11 110xxxxx 10xxxxxx + * U+0080..U+07FF C2..DF 80..BF + * + * 3 16 1110xxxx 10xxxxxx 10xxxxxx + * U+0800..U+0FFF E0 A0..BF 80..BF + * U+1000..U+FFFF E1..EF 80..BF 80..BF + * + * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + * U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + * U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + * U+100000..U10FFFF F4 80..8F 80..BF 80..BF + * + * @author Xueming Shen + * @author Martin Buchholz * - * UCS-2 uses 1-3, UTF-16 uses 1-4, UCS-4 uses 1-6 */ class UTF_8 extends Unicode { - public UTF_8() { super("UTF-8", StandardCharsets.aliases_UTF_8); } @@ -69,304 +74,250 @@ class UTF_8 extends Unicode return new Encoder(this); } + static final void updatePositions(Buffer src, int sp, + Buffer dst, int dp) { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } private static class Decoder extends CharsetDecoder { private Decoder(Charset cs) { super(cs, 1.0f, 1.0f); } - private boolean isContinuation(int b) { - return ((b & 0xc0) == 0x80); + private static boolean isNotContinuation(int b) { + return (b & 0xc0) != 0x80; + } + + // [C2..DF] [80..BF] + private static boolean isMalformed2(int b1, int b2) { + return (b1 & 0x1e) == 0x0 || (b2 & 0xc0) != 0x80; + } + + // [E0] [A0..BF] [80..BF] + // [E1..EF] [80..BF] [80..BF] + private static boolean isMalformed3(int b1, int b2, int b3) { + return (b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) || + (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80; + } + + // [F0] [90..BF] [80..BF] [80..BF] + // [F1..F3] [80..BF] [80..BF] [80..BF] + // [F4] [80..8F] [80..BF] [80..BF] + // only check 80-be range here, the [0xf0,0x80...] and [0xf4,0x90-...] + // will be checked by Surrogate.neededFor(uc) + private static boolean isMalformed4(int b2, int b3, int b4) { + return (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || + (b4 & 0xc0) != 0x80; + } + + private static CoderResult lookupN(ByteBuffer src, int n) + { + for (int i = 1; i < n; i++) { + if (isNotContinuation(src.get())) + return CoderResult.malformedForLength(i); + } + return CoderResult.malformedForLength(n); + } + + private static CoderResult malformedN(ByteBuffer src, int nb) { + switch (nb) { + case 1: + int b1 = src.get(); + if ((b1 >> 2) == -2) { + // 5 bytes 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + if (src.remaining() < 4) + return CoderResult.UNDERFLOW; + return lookupN(src, 5); + } + if ((b1 >> 1) == -2) { + // 6 bytes 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + if (src.remaining() < 5) + return CoderResult.UNDERFLOW; + return lookupN(src, 6); + } + return CoderResult.malformedForLength(1); + case 2: // always 1 + return CoderResult.malformedForLength(1); + case 3: + b1 = src.get(); + int b2 = src.get(); // no need to lookup b3 + return CoderResult.malformedForLength( + ((b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) || + isNotContinuation(b2))?1:2); + case 4: // we don't care the speed here + b1 = src.get() & 0xff; + b2 = src.get() & 0xff; + if (b1 > 0xf4 || + (b1 == 0xf0 && (b2 < 0x90 || b2 > 0xbf)) || + (b1 == 0xf4 && (b2 & 0xf0) != 0x80) || + isNotContinuation(b2)) + return CoderResult.malformedForLength(1); + if (isNotContinuation(src.get())) + return CoderResult.malformedForLength(2); + return CoderResult.malformedForLength(3); + default: + assert false; + return null; + } + } + + private static CoderResult malformed(ByteBuffer src, int sp, + CharBuffer dst, int dp, + int nb) + { + src.position(sp - src.arrayOffset()); + CoderResult cr = malformedN(src, nb); + updatePositions(src, sp, dst, dp); + return cr; + } + + private static CoderResult malformed(ByteBuffer src, + int mark, int nb) + { + src.position(mark); + CoderResult cr = malformedN(src, nb); + src.position(mark); + return cr; + } + + private static CoderResult xflow(Buffer src, int sp, int sl, + Buffer dst, int dp, int nb) { + updatePositions(src, sp, dst, dp); + return (nb == 0 || sl - sp < nb) + ?CoderResult.UNDERFLOW:CoderResult.OVERFLOW; } - private final Surrogate.Generator sgg = new Surrogate.Generator(); + private static CoderResult xflow(Buffer src, int mark, int nb) { + CoderResult cr = (nb == 0 || src.remaining() < (nb - 1)) + ?CoderResult.UNDERFLOW:CoderResult.OVERFLOW; + src.position(mark); + return cr; + } private CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) { + // This method is optimized for ASCII input. byte[] sa = src.array(); int sp = src.arrayOffset() + src.position(); int sl = src.arrayOffset() + src.limit(); - assert (sp <= sl); - sp = (sp <= sl ? sp : sl); + char[] da = dst.array(); int dp = dst.arrayOffset() + dst.position(); int dl = dst.arrayOffset() + dst.limit(); - assert (dp <= dl); - dp = (dp <= dl ? dp : dl); - - try { - while (sp < sl) { - int b1 = sa[sp]; - int b2, b3; - switch ((b1 >> 4) & 0x0f) { - - case 0: case 1: case 2: case 3: - case 4: case 5: case 6: case 7: - // 1 byte, 7 bits: 0xxxxxxx - if (dl - dp < 1) - return CoderResult.OVERFLOW; - da[dp++] = (char)(b1 & 0x7f); - sp++; - continue; - - case 12: case 13: - // 2 bytes, 11 bits: 110xxxxx 10xxxxxx - if (sl - sp < 2) - return CoderResult.UNDERFLOW; - if (dl - dp < 1) - return CoderResult.OVERFLOW; - if (!isContinuation(b2 = sa[sp + 1])) - return CoderResult.malformedForLength(1); - da[dp++] = ((char)(((b1 & 0x1f) << 6) | - ((b2 & 0x3f) << 0))); - sp += 2; - continue; - - case 14: - // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx - if (sl - sp < 3) - return CoderResult.UNDERFLOW; - if (dl - dp < 1) - return CoderResult.OVERFLOW; - if (!isContinuation(b2 = sa[sp + 1])) - return CoderResult.malformedForLength(1); - if (!isContinuation(b3 = sa[sp + 2])) - return CoderResult.malformedForLength(2); - da[dp++] = ((char)(((b1 & 0x0f) << 12) | - ((b2 & 0x3f) << 06) | - ((b3 & 0x3f) << 0))); - sp += 3; - continue; - - case 15: - // 4, 5, or 6 bytes - - int b4, b5, b6, uc, n; - switch (b1 & 0x0f) { - - case 0: case 1: case 2: case 3: - case 4: case 5: case 6: case 7: - // 4 bytes, 21 bits - if (sl - sp < 4) - return CoderResult.UNDERFLOW; - if (!isContinuation(b2 = sa[sp + 1])) - return CoderResult.malformedForLength(1); - if (!isContinuation(b3 = sa[sp + 2])) - return CoderResult.malformedForLength(2); - if (!isContinuation(b4 = sa[sp + 3])) - return CoderResult.malformedForLength(3); - uc = (((b1 & 0x07) << 18) | - ((b2 & 0x3f) << 12) | - ((b3 & 0x3f) << 06) | - ((b4 & 0x3f) << 00)); - n = 4; - break; - - case 8: case 9: case 10: case 11: - // 5 bytes, 26 bits - if (sl - sp < 5) - return CoderResult.UNDERFLOW; - if (!isContinuation(b2 = sa[sp + 1])) - return CoderResult.malformedForLength(1); - if (!isContinuation(b3 = sa[sp + 2])) - return CoderResult.malformedForLength(2); - if (!isContinuation(b4 = sa[sp + 3])) - return CoderResult.malformedForLength(3); - if (!isContinuation(b5 = sa[sp + 4])) - return CoderResult.malformedForLength(4); - uc = (((b1 & 0x03) << 24) | - ((b2 & 0x3f) << 18) | - ((b3 & 0x3f) << 12) | - ((b4 & 0x3f) << 06) | - ((b5 & 0x3f) << 00)); - n = 5; - break; - - case 12: case 13: - // 6 bytes, 31 bits - if (sl - sp < 6) - return CoderResult.UNDERFLOW; - if (!isContinuation(b2 = sa[sp + 1])) - return CoderResult.malformedForLength(1); - if (!isContinuation(b3 = sa[sp + 2])) - return CoderResult.malformedForLength(2); - if (!isContinuation(b4 = sa[sp + 3])) - return CoderResult.malformedForLength(3); - if (!isContinuation(b5 = sa[sp + 4])) - return CoderResult.malformedForLength(4); - if (!isContinuation(b6 = sa[sp + 5])) - return CoderResult.malformedForLength(5); - uc = (((b1 & 0x01) << 30) | - ((b2 & 0x3f) << 24) | - ((b3 & 0x3f) << 18) | - ((b4 & 0x3f) << 12) | - ((b5 & 0x3f) << 06) | - ((b6 & 0x3f))); - n = 6; - break; - - default: - return CoderResult.malformedForLength(1); - - } - - int gn = sgg.generate(uc, n, da, dp, dl); - if (gn < 0) - return sgg.error(); - dp += gn; - sp += n; - continue; - - default: - return CoderResult.malformedForLength(1); - + int dlASCII = dp + Math.min(sl - sp, dl - dp); + + // ASCII only loop + while (dp < dlASCII && sa[sp] >= 0) + da[dp++] = (char)sa[sp++]; + + while (sp < sl) { + int b1 = sa[sp]; + if (b1 >= 0) { + // 1 byte, 7 bits: 0xxxxxxx + if (dp >= dl) + return xflow(src, sp, sl, dst, dp, 1); + da[dp++] = (char)b1; + sp++; + } else if ((b1 >> 5) == -2) { + // 2 bytes, 11 bits: 110xxxxx 10xxxxxx + if (sl - sp < 2 || dp >= dl) + return xflow(src, sp, sl, dst, dp, 2); + int b2 = sa[sp + 1]; + if (isMalformed2(b1, b2)) + return malformed(src, sp, dst, dp, 2); + da[dp++] = (char) (((b1 << 6) ^ b2) ^ 0x0f80); + sp += 2; + } else if ((b1 >> 4) == -2) { + // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx + if (sl - sp < 3 || dp >= dl) + return xflow(src, sp, sl, dst, dp, 3); + int b2 = sa[sp + 1]; + int b3 = sa[sp + 2]; + if (isMalformed3(b1, b2, b3)) + return malformed(src, sp, dst, dp, 3); + da[dp++] = (char) (((b1 << 12) ^ (b2 << 6) ^ b3) ^ 0x1f80); + sp += 3; + } else if ((b1 >> 3) == -2) { + // 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + if (sl - sp < 4 || dl - dp < 2) + return xflow(src, sp, sl, dst, dp, 4); + int b2 = sa[sp + 1]; + int b3 = sa[sp + 2]; + int b4 = sa[sp + 3]; + int uc = ((b1 & 0x07) << 18) | + ((b2 & 0x3f) << 12) | + ((b3 & 0x3f) << 06) | + (b4 & 0x3f); + if (isMalformed4(b2, b3, b4) || + !Surrogate.neededFor(uc)) { + return malformed(src, sp, dst, dp, 4); } - - } - - return CoderResult.UNDERFLOW; - } finally { - src.position(sp - src.arrayOffset()); - dst.position(dp - dst.arrayOffset()); + da[dp++] = Surrogate.high(uc); + da[dp++] = Surrogate.low(uc); + sp += 4; + } else + return malformed(src, sp, dst, dp, 1); } + return xflow(src, sp, sl, dst, dp, 0); } private CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) { int mark = src.position(); - try { - while (src.hasRemaining()) { - int b1 = src.get(); - int b2, b3; - switch ((b1 >> 4) & 0x0f) { - - case 0: case 1: case 2: case 3: - case 4: case 5: case 6: case 7: - // 1 byte, 7 bits: 0xxxxxxx - if (dst.remaining() < 1) - return CoderResult.OVERFLOW; - dst.put((char)b1); - mark++; - continue; - - case 12: case 13: - // 2 bytes, 11 bits: 110xxxxx 10xxxxxx - if (src.remaining() < 1) - return CoderResult.UNDERFLOW; - if (dst.remaining() < 1) - return CoderResult.OVERFLOW; - if (!isContinuation(b2 = src.get())) - return CoderResult.malformedForLength(1); - dst.put((char)(((b1 & 0x1f) << 6) | - ((b2 & 0x3f) << 0))); - mark += 2; - continue; - - case 14: - // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx - if (src.remaining() < 2) - return CoderResult.UNDERFLOW; - if (dst.remaining() < 1) - return CoderResult.OVERFLOW; - if (!isContinuation(b2 = src.get())) - return CoderResult.malformedForLength(1); - if (!isContinuation(b3 = src.get())) - return CoderResult.malformedForLength(2); - dst.put((char)(((b1 & 0x0f) << 12) | - ((b2 & 0x3f) << 06) | - ((b3 & 0x3f) << 0))); - mark += 3; - continue; - - case 15: - // 4, 5, or 6 bytes - - int b4, b5, b6, uc, n; - switch (b1 & 0x0f) { - - case 0: case 1: case 2: case 3: - case 4: case 5: case 6: case 7: - // 4 bytes, 21 bits - if (src.remaining() < 3) - return CoderResult.UNDERFLOW; - if (!isContinuation(b2 = src.get())) - return CoderResult.malformedForLength(1); - if (!isContinuation(b3 = src.get())) - return CoderResult.malformedForLength(2); - if (!isContinuation(b4 = src.get())) - return CoderResult.malformedForLength(3); - uc = (((b1 & 0x07) << 18) | - ((b2 & 0x3f) << 12) | - ((b3 & 0x3f) << 06) | - ((b4 & 0x3f) << 00)); - n = 4; - break; - - case 8: case 9: case 10: case 11: - // 5 bytes, 26 bits - if (src.remaining() < 4) - return CoderResult.UNDERFLOW; - if (!isContinuation(b2 = src.get())) - return CoderResult.malformedForLength(1); - if (!isContinuation(b3 = src.get())) - return CoderResult.malformedForLength(2); - if (!isContinuation(b4 = src.get())) - return CoderResult.malformedForLength(3); - if (!isContinuation(b5 = src.get())) - return CoderResult.malformedForLength(4); - uc = (((b1 & 0x03) << 24) | - ((b2 & 0x3f) << 18) | - ((b3 & 0x3f) << 12) | - ((b4 & 0x3f) << 06) | - ((b5 & 0x3f) << 00)); - n = 5; - break; - - case 12: case 13: - // 6 bytes, 31 bits - if (src.remaining() < 5) - return CoderResult.UNDERFLOW; - if (!isContinuation(b2 = src.get())) - return CoderResult.malformedForLength(1); - if (!isContinuation(b3 = src.get())) - return CoderResult.malformedForLength(2); - if (!isContinuation(b4 = src.get())) - return CoderResult.malformedForLength(3); - if (!isContinuation(b5 = src.get())) - return CoderResult.malformedForLength(4); - if (!isContinuation(b6 = src.get())) - return CoderResult.malformedForLength(5); - uc = (((b1 & 0x01) << 30) | - ((b2 & 0x3f) << 24) | - ((b3 & 0x3f) << 18) | - ((b4 & 0x3f) << 12) | - ((b5 & 0x3f) << 06) | - ((b6 & 0x3f))); - n = 6; - break; - - default: - return CoderResult.malformedForLength(1); - - } - - if (sgg.generate(uc, n, dst) < 0) - return sgg.error(); - mark += n; - continue; - - default: - return CoderResult.malformedForLength(1); - + int limit = src.limit(); + while (mark < limit) { + int b1 = src.get(); + if (b1 >= 0) { + // 1 byte, 7 bits: 0xxxxxxx + if (dst.remaining() < 1) + return xflow(src, mark, 1); //overflow + dst.put((char)b1); + mark++; + } else if ((b1 >> 5) == -2) { + // 2 bytes, 11 bits: 110xxxxx 10xxxxxx + if (limit - mark < 2|| dst.remaining() < 1) + return xflow(src, mark, 2); + int b2 = src.get(); + if (isMalformed2(b1, b2)) + return malformed(src, mark, 2); + dst.put((char) (((b1 << 6) ^ b2) ^ 0x0f80)); + mark += 2; + } else if ((b1 >> 4) == -2) { + // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx + if (limit - mark < 3 || dst.remaining() < 1) + return xflow(src, mark, 3); + int b2 = src.get(); + int b3 = src.get(); + if (isMalformed3(b1, b2, b3)) + return malformed(src, mark, 3); + dst.put((char) (((b1 << 12) ^ (b2 << 6) ^ b3) ^ 0x1f80)); + mark += 3; + } else if ((b1 >> 3) == -2) { + // 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + if (limit - mark < 4 || dst.remaining() < 2) + return xflow(src, mark, 4); + int b2 = src.get(); + int b3 = src.get(); + int b4 = src.get(); + int uc = ((b1 & 0x07) << 18) | + ((b2 & 0x3f) << 12) | + ((b3 & 0x3f) << 06) | + (b4 & 0x3f); + if (isMalformed4(b2, b3, b4) || + !Surrogate.neededFor(uc)) { // shortest form check + return malformed(src, mark, 4); } - + dst.put(Surrogate.high(uc)); + dst.put(Surrogate.low(uc)); + mark += 4; + } else { + return malformed(src, mark, 1); } - return CoderResult.UNDERFLOW; - } finally { - src.position(mark); } + return xflow(src, mark, 0); } protected CoderResult decodeLoop(ByteBuffer src, @@ -377,10 +328,8 @@ class UTF_8 extends Unicode else return decodeBufferLoop(src, dst); } - } - private static class Encoder extends CharsetEncoder { private Encoder(Charset cs) { @@ -391,141 +340,126 @@ class UTF_8 extends Unicode return !Surrogate.is(c); } - private final Surrogate.Parser sgp = new Surrogate.Parser(); + public boolean isLegalReplacement(byte[] repl) { + return ((repl.length == 1 && repl[0] >= 0) || + super.isLegalReplacement(repl)); + } + + private static CoderResult overflow(CharBuffer src, int sp, + ByteBuffer dst, int dp) { + updatePositions(src, sp, dst, dp); + return CoderResult.OVERFLOW; + } + + private static CoderResult overflow(CharBuffer src, int mark) { + src.position(mark); + return CoderResult.OVERFLOW; + } + private Surrogate.Parser sgp; private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { char[] sa = src.array(); int sp = src.arrayOffset() + src.position(); int sl = src.arrayOffset() + src.limit(); - assert (sp <= sl); - sp = (sp <= sl ? sp : sl); + byte[] da = dst.array(); int dp = dst.arrayOffset() + dst.position(); int dl = dst.arrayOffset() + dst.limit(); - assert (dp <= dl); - dp = (dp <= dl ? dp : dl); - - try { - while (sp < sl) { - char c = sa[sp]; - - if (c < 0x80) { - // Have at most seven bits - if (dp >= dl) - return CoderResult.OVERFLOW; - da[dp++] = (byte)c; - sp++; - continue; - } - - if (!Surrogate.is(c)) { - // 2 bytes, 11 bits - if (c < 0x800) { - if (dl - dp < 2) - return CoderResult.OVERFLOW; - da[dp++] = (byte)(0xc0 | ((c >> 06))); - da[dp++] = (byte)(0x80 | ((c >> 00) & 0x3f)); - sp++; - continue; - } - if (c <= '\uFFFF') { - // 3 bytes, 16 bits - if (dl - dp < 3) - return CoderResult.OVERFLOW; - da[dp++] = (byte)(0xe0 | ((c >> 12))); - da[dp++] = (byte)(0x80 | ((c >> 06) & 0x3f)); - da[dp++] = (byte)(0x80 | ((c >> 00) & 0x3f)); - sp++; - continue; - } - } - + int dlASCII = dp + Math.min(sl - sp, dl - dp); + + //ASCII only loop + while (dp < dlASCII && sa[sp] < '\u0080') + da[dp++] = (byte) sa[sp++]; + while (sp < sl) { + int c = sa[sp]; + if (c < 0x80) { + // Have at most seven bits + if (dp >= dl) + return overflow(src, sp, dst, dp); + da[dp++] = (byte)c; + } else if (c < 0x800) { + // 2 bytes, 11 bits + if (dl - dp < 2) + return overflow(src, sp, dst, dp); + da[dp++] = (byte)(0xc0 | ((c >> 06))); + da[dp++] = (byte)(0x80 | (c & 0x3f)); + } else if (Surrogate.is(c)) { // Have a surrogate pair - int uc = sgp.parse(c, sa, sp, sl); - if (uc < 0) + if (sgp == null) + sgp = new Surrogate.Parser(); + int uc = sgp.parse((char)c, sa, sp, sl); + if (uc < 0) { + updatePositions(src, sp, dst, dp); return sgp.error(); - if (uc < 0x200000) { - if (dl - dp < 4) - return CoderResult.OVERFLOW; - da[dp++] = (byte)(0xf0 | ((uc >> 18))); - da[dp++] = (byte)(0x80 | ((uc >> 12) & 0x3f)); - da[dp++] = (byte)(0x80 | ((uc >> 06) & 0x3f)); - da[dp++] = (byte)(0x80 | ((uc >> 00) & 0x3f)); - sp += sgp.increment(); - continue; } - assert false; - + if (dl - dp < 4) + return overflow(src, sp, dst, dp); + da[dp++] = (byte)(0xf0 | ((uc >> 18))); + da[dp++] = (byte)(0x80 | ((uc >> 12) & 0x3f)); + da[dp++] = (byte)(0x80 | ((uc >> 06) & 0x3f)); + da[dp++] = (byte)(0x80 | (uc & 0x3f)); + sp++; // 2 chars + } else { + // 3 bytes, 16 bits + if (dl - dp < 3) + return overflow(src, sp, dst, dp); + da[dp++] = (byte)(0xe0 | ((c >> 12))); + da[dp++] = (byte)(0x80 | ((c >> 06) & 0x3f)); + da[dp++] = (byte)(0x80 | (c & 0x3f)); } - return CoderResult.UNDERFLOW; - } finally { - src.position(sp - src.arrayOffset()); - dst.position(dp - dst.arrayOffset()); + sp++; } + updatePositions(src, sp, dst, dp); + return CoderResult.UNDERFLOW; } private CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) { int mark = src.position(); - try { - while (src.hasRemaining()) { - char c = src.get(); - - if (c < 0x80) { - // Have at most seven bits - if (!dst.hasRemaining()) - return CoderResult.OVERFLOW; - dst.put((byte)c); - mark++; - continue; - } - - if (!Surrogate.is(c)) { - if (c < 0x800) { - // 2 bytes, 11 bits - if (dst.remaining() < 2) - return CoderResult.OVERFLOW; - dst.put((byte)(0xc0 | ((c >> 06)))); - dst.put((byte)(0x80 | ((c >> 00) & 0x3f))); - mark++; - continue; - } - if (c <= '\uFFFF') { - // 3 bytes, 16 bits - if (dst.remaining() < 3) - return CoderResult.OVERFLOW; - dst.put((byte)(0xe0 | ((c >> 12)))); - dst.put((byte)(0x80 | ((c >> 06) & 0x3f))); - dst.put((byte)(0x80 | ((c >> 00) & 0x3f))); - mark++; - continue; - } - } - + while (src.hasRemaining()) { + int c = src.get(); + if (c < 0x80) { + // Have at most seven bits + if (!dst.hasRemaining()) + return overflow(src, mark); + dst.put((byte)c); + } else if (c < 0x800) { + // 2 bytes, 11 bits + if (dst.remaining() < 2) + return overflow(src, mark); + dst.put((byte)(0xc0 | ((c >> 06)))); + dst.put((byte)(0x80 | (c & 0x3f))); + } else if (Surrogate.is(c)) { // Have a surrogate pair - int uc = sgp.parse(c, src); - if (uc < 0) + if (sgp == null) + sgp = new Surrogate.Parser(); + int uc = sgp.parse((char)c, src); + if (uc < 0) { + src.position(mark); return sgp.error(); - if (uc < 0x200000) { - if (dst.remaining() < 4) - return CoderResult.OVERFLOW; - dst.put((byte)(0xf0 | ((uc >> 18)))); - dst.put((byte)(0x80 | ((uc >> 12) & 0x3f))); - dst.put((byte)(0x80 | ((uc >> 06) & 0x3f))); - dst.put((byte)(0x80 | ((uc >> 00) & 0x3f))); - mark += sgp.increment(); - continue; } - assert false; - + if (dst.remaining() < 4) + return overflow(src, mark); + dst.put((byte)(0xf0 | ((uc >> 18)))); + dst.put((byte)(0x80 | ((uc >> 12) & 0x3f))); + dst.put((byte)(0x80 | ((uc >> 06) & 0x3f))); + dst.put((byte)(0x80 | (uc & 0x3f))); + mark++; //2 chars + } else { + // 3 bytes, 16 bits + if (dst.remaining() < 3) + return overflow(src, mark); + dst.put((byte)(0xe0 | ((c >> 12)))); + dst.put((byte)(0x80 | ((c >> 06) & 0x3f))); + dst.put((byte)(0x80 | (c & 0x3f))); } - return CoderResult.UNDERFLOW; - } finally { - src.position(mark); + mark++; } + src.position(mark); + return CoderResult.UNDERFLOW; } protected final CoderResult encodeLoop(CharBuffer src, @@ -536,7 +470,5 @@ class UTF_8 extends Unicode else return encodeBufferLoop(src, dst); } - } - } diff --git a/test/sun/nio/cs/TestUTF8.java b/test/sun/nio/cs/TestUTF8.java new file mode 100644 index 000000000..967054573 --- /dev/null +++ b/test/sun/nio/cs/TestUTF8.java @@ -0,0 +1,393 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4486841 + * @summary Test UTF-8 charset + */ + +import java.nio.charset.*; +import java.nio.*; +import java.util.*; + +public class TestUTF8 { + static char[] decode(byte[] bb, String csn, boolean testDirect) + throws Exception { + CharsetDecoder dec = Charset.forName(csn).newDecoder(); + ByteBuffer bbf; + CharBuffer cbf; + if (testDirect) { + bbf = ByteBuffer.allocateDirect(bb.length); + cbf = ByteBuffer.allocateDirect(bb.length*2).asCharBuffer(); + bbf.put(bb).flip(); + } else { + bbf = ByteBuffer.wrap(bb); + cbf = CharBuffer.allocate(bb.length); + } + CoderResult cr = dec.decode(bbf, cbf, true); + if (cr != CoderResult.UNDERFLOW) + throw new RuntimeException("Decoding err: " + csn); + char[] cc = new char[cbf.position()]; + cbf.flip(); cbf.get(cc); + return cc; + + } + + static CoderResult decodeCR(byte[] bb, String csn, boolean testDirect) + throws Exception { + CharsetDecoder dec = Charset.forName(csn).newDecoder(); + ByteBuffer bbf; + CharBuffer cbf; + if (testDirect) { + bbf = ByteBuffer.allocateDirect(bb.length); + cbf = ByteBuffer.allocateDirect(bb.length*2).asCharBuffer(); + bbf.put(bb).flip(); + } else { + bbf = ByteBuffer.wrap(bb); + cbf = CharBuffer.allocate(bb.length); + } + return dec.decode(bbf, cbf, true); + } + + static byte[] encode(char[] cc, String csn, boolean testDirect) + throws Exception { + ByteBuffer bbf; + CharBuffer cbf; + CharsetEncoder enc = Charset.forName(csn).newEncoder(); + if (testDirect) { + bbf = ByteBuffer.allocateDirect(cc.length * 4); + cbf = ByteBuffer.allocateDirect(cc.length * 2).asCharBuffer(); + cbf.put(cc).flip(); + } else { + bbf = ByteBuffer.allocate(cc.length * 4); + cbf = CharBuffer.wrap(cc); + } + + CoderResult cr = enc.encode(cbf, bbf, true); + if (cr != CoderResult.UNDERFLOW) + throw new RuntimeException("Encoding err: " + csn); + byte[] bb = new byte[bbf.position()]; + bbf.flip(); bbf.get(bb); + return bb; + } + + static CoderResult encodeCR(char[] cc, String csn, boolean testDirect) + throws Exception { + ByteBuffer bbf; + CharBuffer cbf; + CharsetEncoder enc = Charset.forName(csn).newEncoder(); + if (testDirect) { + bbf = ByteBuffer.allocateDirect(cc.length * 4); + cbf = ByteBuffer.allocateDirect(cc.length * 2).asCharBuffer(); + cbf.put(cc).flip(); + } else { + bbf = ByteBuffer.allocate(cc.length * 4); + cbf = CharBuffer.wrap(cc); + } + return enc.encode(cbf, bbf, true); + } + + static char[] getUTFChars() { + char[] cc = new char[0x10000 - 0xe000 + 0xd800 + //bmp + (0x110000 - 0x10000) * 2]; //supp + int pos = 0; + int i = 0; + for (i = 0; i < 0xd800; i++) + cc[pos++] = (char)i; + for (i = 0xe000; i < 0x10000; i++) + cc[pos++] = (char)i; + for (i = 0x10000; i < 0x110000; i++) { + pos += Character.toChars(i, cc, pos); + } + return cc; + } + + static int to3ByteUTF8(char c, byte[] bb, int pos) { + bb[pos++] = (byte)(0xe0 | ((c >> 12))); + bb[pos++] = (byte)(0x80 | ((c >> 06) & 0x3f)); + bb[pos++] = (byte)(0x80 | ((c >> 00) & 0x3f)); + return 3; + } + + static void checkRoundtrip(String csn) throws Exception { + System.out.printf(" Check roundtrip <%s>...", csn); + char[] cc = getUTFChars(); + byte[] bb = encode(cc, csn, false); + char[] ccO = decode(bb, csn, false); + + if (!Arrays.equals(cc, ccO)) { + System.out.printf(" non-direct failed"); + } + bb = encode(cc, csn, true); + ccO = decode(bb, csn, true); + if (!Arrays.equals(cc, ccO)) { + System.out.printf(" (direct) failed"); + } + System.out.println(); + } + + static void check6ByteSurrs(String csn) throws Exception { + System.out.printf(" Check 6-byte Surrogates <%s>...%n", csn); + byte[] bb = new byte[(0x110000 - 0x10000) * 6]; + char[] cc = new char[(0x110000 - 0x10000) * 2]; + int bpos = 0; + int cpos = 0; + for (int i = 0x10000; i < 0x110000; i++) { + Character.toChars(i, cc, cpos); + bpos += to3ByteUTF8(cc[cpos], bb, bpos); + bpos += to3ByteUTF8(cc[cpos + 1], bb, bpos); + cpos += 2; + } + + char[] ccO = decode(bb, csn, false); + if (!Arrays.equals(cc, ccO)) { + System.out.printf(" decoding failed%n"); + } + ccO = decode(bb, csn, true); + if (!Arrays.equals(cc, ccO)) { + System.out.printf(" decoding(direct) failed%n"); + } + } + + static void compare(String csn1, String csn2) throws Exception { + System.out.printf(" Diff <%s> <%s>...%n", csn1, csn2); + char[] cc = getUTFChars(); + + byte[] bb1 = encode(cc, csn1, false); + byte[] bb2 = encode(cc, csn2, false); + if (!Arrays.equals(bb1, bb2)) + System.out.printf(" encoding failed%n"); + char[] cc1 = decode(bb1, csn1, false); + char[] cc2 = decode(bb1, csn2, false); + if (!Arrays.equals(cc1, cc2)) { + System.out.printf(" decoding failed%n"); + } + + bb1 = encode(cc, csn1, true); + bb2 = encode(cc, csn2, true); + if (!Arrays.equals(bb1, bb2)) + System.out.printf(" encoding (direct) failed%n"); + cc1 = decode(bb1, csn1, true); + cc2 = decode(bb1, csn2, true); + if (!Arrays.equals(cc1, cc2)) { + System.out.printf(" decoding (direct) failed%n"); + } + } + + // The first byte is the length of malformed bytes + static byte[][] malformed = { + // One-byte sequences: + {1, (byte)0xFF }, + {1, (byte)0xC0 }, + {1, (byte)0x80 }, + + {1, (byte)0xFF, (byte)0xFF}, // all ones + {1, (byte)0xA0, (byte)0x80}, // 101x first byte first nibble + + // Two-byte sequences: + {1, (byte)0xC0, (byte)0x80}, // invalid first byte + {1, (byte)0xC1, (byte)0xBF}, // invalid first byte + {1, (byte)0xC2, (byte)0x00}, // invalid second byte + {1, (byte)0xC2, (byte)0xC0}, // invalid second byte + {1, (byte)0xD0, (byte)0x00}, // invalid second byte + {1, (byte)0xD0, (byte)0xC0}, // invalid second byte + {1, (byte)0xDF, (byte)0x00}, // invalid second byte + {1, (byte)0xDF, (byte)0xC0}, // invalid second byte + + // Three-byte sequences + {1, (byte)0xE0, (byte)0x80, (byte)0x80}, // 111x first byte first nibble + {1, (byte)0xE0, (byte)0x80, (byte)0x80 }, // U+0000 zero-padded + {1, (byte)0xE0, (byte)0x81, (byte)0xBF }, // U+007F zero-padded + {1, (byte)0xE0, (byte)0x9F, (byte)0xBF }, // U+07FF zero-padded + + {1, (byte)0xE0, (byte)0xC0, (byte)0xBF }, // invalid second byte + {2, (byte)0xE0, (byte)0xA0, (byte)0x7F }, // invalid third byte + {2, (byte)0xE0, (byte)0xA0, (byte)0xC0 }, // invalid third byte + {1, (byte)0xFF, (byte)0xFF, (byte)0xFF }, // all ones + {1, (byte)0xE0, (byte)0xC0, (byte)0x80 }, // invalid second byte + {1, (byte)0xE0, (byte)0x80, (byte)0xC0 }, // invalid first byte + + // Four-byte sequences + {1, (byte)0xF0, (byte)0x80, (byte)0x80, (byte)0x80 }, // U+0000 zero-padded + {1, (byte)0xF0, (byte)0x80, (byte)0x81, (byte)0xBF }, // U+007F zero-padded + {1, (byte)0xF0, (byte)0x80, (byte)0x9F, (byte)0xBF }, // U+007F zero-padded + {1, (byte)0xF0, (byte)0x8F, (byte)0xBF, (byte)0xBF }, // U+07FF zero-padded + + {1, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF }, // all ones + {1, (byte)0xF0, (byte)0x80, (byte)0x80, (byte)0x80}, // invalid second byte + {1, (byte)0xF0, (byte)0xC0, (byte)0x80, (byte)0x80 }, // invalid second byte + {2, (byte)0xF0, (byte)0x90, (byte)0xC0, (byte)0x80 }, // invalid third byte + {3, (byte)0xF0, (byte)0x90, (byte)0x80, (byte)0xC0 }, // invalid third byte + + {1, (byte)0xF1, (byte)0xC0, (byte)0x80, (byte)0x80 }, // invalid second byte + {2, (byte)0xF1, (byte)0x80, (byte)0xC0, (byte)0x80 }, // invalid third byte + {3, (byte)0xF1, (byte)0x80, (byte)0x80, (byte)0xC0 }, // invalid forth byte + {1, (byte)0xF4, (byte)0x90, (byte)0x80, (byte)0xC0 }, // out-range 4-byte + {1, (byte)0xF4, (byte)0xC0, (byte)0x80, (byte)0xC0 }, // out-range 4-byte + {1, (byte)0xF5, (byte)0x80, (byte)0x80, (byte)0xC0 }, // out-range 4-byte + + // Five-byte sequences + {5, (byte)0xF8, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x80}, // invalid first byte + {5, (byte)0xF8, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x80 }, // U+0000 zero-padded + {5, (byte)0xF8, (byte)0x80, (byte)0x80, (byte)0x81, (byte)0xBF }, // U+007F zero-padded + {5, (byte)0xF8, (byte)0x80, (byte)0x80, (byte)0x9F, (byte)0xBF }, // U+07FF zero-padded + {5, (byte)0xF8, (byte)0x80, (byte)0x8F, (byte)0xBF, (byte)0xBF }, // U+FFFF zero-padded + + {1, (byte)0xF8, (byte)0xC0, (byte)0x80, (byte)0x80, (byte)0x80}, + {2, (byte)0xF8, (byte)0x80, (byte)0xC0, (byte)0x80, (byte)0x80 }, + {3, (byte)0xF8, (byte)0x80, (byte)0x80, (byte)0xC1, (byte)0xBF }, + {4, (byte)0xF8, (byte)0x80, (byte)0x80, (byte)0x9F, (byte)0xC0 }, + + // Six-byte sequences + {6, (byte)0xFC, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x80 }, // U+0000 zero-padded + {6, (byte)0xFC, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x81, (byte)0xBF }, // U+007F zero-padded + {6, (byte)0xFC, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x9F, (byte)0xBF }, // U+07FF zero-padded + {6, (byte)0xFC, (byte)0x80, (byte)0x80, (byte)0x8F, (byte)0xBF, (byte)0xBF }, // U+FFFF zero-padded + {1, (byte)0xF8, (byte)0xC0, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x80 }, + {2, (byte)0xF8, (byte)0x80, (byte)0xC0, (byte)0x80, (byte)0x80, (byte)0x80 }, + {3, (byte)0xF8, (byte)0x80, (byte)0x80, (byte)0xC1, (byte)0xBF, (byte)0x80 }, + {4, (byte)0xF8, (byte)0x80, (byte)0x80, (byte)0x9F, (byte)0xC0, (byte)0x80 }, + {5, (byte)0xF8, (byte)0x80, (byte)0x80, (byte)0x9F, (byte)0x80, (byte)0xC0 }, + }; + + static void checkMalformed(String csn) throws Exception { + boolean failed = false; + System.out.printf(" Check malformed <%s>...%n", csn); + for (boolean direct: new boolean[] {false, true}) { + for (byte[] bins : malformed) { + int mlen = bins[0]; + byte[] bin = Arrays.copyOfRange(bins, 1, bins.length); + CoderResult cr = decodeCR(bin, csn, direct); + String ashex = ""; + for (int i = 0; i < bin.length; i++) { + if (i > 0) ashex += " "; + ashex += Integer.toBinaryString((int)bin[i] & 0xff); + } + if (!cr.isMalformed()) { + System.out.printf(" FAIL(direct=%b): [%s] not malformed.\n", direct, ashex); + failed = true; + } else if (cr.length() != mlen) { + System.out.printf(" FAIL(direct=%b): [%s] malformed[len=%d].\n", direct, ashex, cr.length()); + failed = true; + } + } + } + if (failed) + throw new RuntimeException("Check malformed failed " + csn); + } + + static boolean check(CharsetDecoder dec, byte[] utf8s, boolean direct, int[] flow) { + int inPos = flow[0]; + int inLen = flow[1]; + int outPos = flow[2]; + int outLen = flow[3]; + int expedInPos = flow[4]; + int expedOutPos = flow[5]; + CoderResult expedCR = (flow[6]==0)?CoderResult.UNDERFLOW + :CoderResult.OVERFLOW; + ByteBuffer bbf; + CharBuffer cbf; + if (direct) { + bbf = ByteBuffer.allocateDirect(inPos + utf8s.length); + cbf = ByteBuffer.allocateDirect((outPos + outLen)*2).asCharBuffer(); + } else { + bbf = ByteBuffer.allocate(inPos + utf8s.length); + cbf = CharBuffer.allocate(outPos + outLen); + } + bbf.position(inPos); + bbf.put(utf8s).flip().position(inPos).limit(inPos + inLen); + cbf.position(outPos); + dec.reset(); + CoderResult cr = dec.decode(bbf, cbf, false); + if (cr != expedCR || + bbf.position() != expedInPos || + cbf.position() != expedOutPos) { + System.out.printf("Expected(direct=%5b): [", direct); + for (int i:flow) System.out.print(" " + i); + System.out.println("] CR=" + cr + + ", inPos=" + bbf.position() + + ", outPos=" + cbf.position()); + return false; + } + return true; + } + + static void checkUnderOverflow(String csn) throws Exception { + System.out.printf(" Check under/overflow <%s>...%n", csn); + CharsetDecoder dec = Charset.forName(csn).newDecoder(); + boolean failed = false; + byte[] utf8s = new String("\u007f\u07ff\ue000\ud800\udc00").getBytes("UTF-8"); + int inlen = utf8s.length; + + for (int inoff = 0; inoff < 20; inoff++) { + for (int outoff = 0; outoff < 20; outoff++) { + int[][] Flows = { + //inpos, inLen, outPos, outLen, inPosEP, outposEP, under(0)/over(1) + {inoff, inlen, outoff, 1, inoff + 1, outoff + 1, 1}, + {inoff, inlen, outoff, 2, inoff + 3, outoff + 2, 1}, + {inoff, inlen, outoff, 3, inoff + 6, outoff + 3, 1}, + {inoff, inlen, outoff, 4, inoff + 6, outoff + 3, 1}, + {inoff, inlen, outoff, 5, inoff + 10,outoff + 5, 0}, + // underflow + {inoff, 1, outoff, 5, inoff + 1, outoff + 1, 0}, + {inoff, 2, outoff, 5, inoff + 1, outoff + 1, 0}, + {inoff, 3, outoff, 5, inoff + 3, outoff + 2, 0}, + {inoff, 4, outoff, 5, inoff + 3, outoff + 2, 0}, + {inoff, 5, outoff, 5, inoff + 3, outoff + 2, 0}, + {inoff, 6, outoff, 5, inoff + 6, outoff + 3, 0}, + {inoff, 7, outoff, 5, inoff + 6, outoff + 3, 0}, + {inoff, 8, outoff, 5, inoff + 6, outoff + 3, 0}, + {inoff, 9, outoff, 5, inoff + 6, outoff + 3, 0}, + {inoff, 10, outoff, 5, inoff + 10,outoff + 5, 0}, + // 2-byte underflow/overflow + {inoff, 2, outoff, 1, inoff + 1, outoff + 1, 0}, + {inoff, 3, outoff, 1, inoff + 1, outoff + 1, 1}, + // 3-byte underflow/overflow + {inoff, 4, outoff, 2, inoff + 3, outoff + 2, 0}, + {inoff, 5, outoff, 2, inoff + 3, outoff + 2, 0}, + {inoff, 6, outoff, 2, inoff + 3, outoff + 2, 1}, + // 4-byte underflow/overflow + {inoff, 7, outoff, 4, inoff + 6, outoff + 3, 0}, + {inoff, 8, outoff, 4, inoff + 6, outoff + 3, 0}, + {inoff, 9, outoff, 4, inoff + 6, outoff + 3, 0}, + {inoff, 10, outoff, 4, inoff + 6, outoff + 3, 1}, + }; + for (boolean direct: new boolean[] {false, true}) { + for (int[] flow: Flows) { + if (!check(dec, utf8s, direct, flow)) + failed = true; + } + }}} + if (failed) + throw new RuntimeException("Check under/overflow failed " + csn); + } + + public static void main(String[] args) throws Exception { + checkRoundtrip("UTF-8"); + check6ByteSurrs("UTF-8"); + //compare("UTF-8", "UTF-8-OLD"); + checkMalformed("UTF-8"); + checkUnderOverflow("UTF-8"); + } +} -- GitLab From 7181e3a9645a8ceb0fb3bbf0f1dbdc36199bd7f9 Mon Sep 17 00:00:00 2001 From: sherman Date: Fri, 22 Aug 2008 22:54:20 -0700 Subject: [PATCH 062/139] 6740702: Comment tag update Summary: tag update Reviewed-by: mr --- src/share/classes/sun/nio/cs/UTF_8.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/share/classes/sun/nio/cs/UTF_8.java b/src/share/classes/sun/nio/cs/UTF_8.java index e5eef85bf..589e36795 100644 --- a/src/share/classes/sun/nio/cs/UTF_8.java +++ b/src/share/classes/sun/nio/cs/UTF_8.java @@ -51,9 +51,6 @@ import java.nio.charset.CoderResult; * U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF * U+100000..U10FFFF F4 80..8F 80..BF 80..BF * - * @author Xueming Shen - * @author Martin Buchholz - * */ class UTF_8 extends Unicode -- GitLab From ea0c561afac4f28ccf5030321fb3d3e9f4e3e21f Mon Sep 17 00:00:00 2001 From: jccollet Date: Mon, 25 Aug 2008 14:38:20 +0200 Subject: [PATCH 063/139] 6717876: Make java.net.NetworkInterface.getIndex() public Summary: Make getIndex() and getByIndex() public. Required a name change in native code Reviewed-by: alanb, chegar, michaelm --- make/java/net/mapfile-vers | 2 +- .../classes/java/net/NetworkInterface.java | 30 +++++++--- .../native/java/net/NetworkInterface.c | 4 +- .../native/java/net/PlainDatagramSocketImpl.c | 2 +- .../native/java/net/NetworkInterface.c | 6 +- .../native/java/net/NetworkInterface_winXP.c | 4 +- .../net/TwoStacksPlainDatagramSocketImpl.c | 2 +- src/windows/native/java/net/net_util_md.h | 2 +- test/java/net/NetworkInterface/IndexTest.java | 58 +++++++++++++++++++ 9 files changed, 92 insertions(+), 18 deletions(-) create mode 100644 test/java/net/NetworkInterface/IndexTest.java diff --git a/make/java/net/mapfile-vers b/make/java/net/mapfile-vers index 86cb351c9..e7da6186e 100644 --- a/make/java/net/mapfile-vers +++ b/make/java/net/mapfile-vers @@ -57,7 +57,7 @@ SUNWprivate_1.1 { Java_java_net_Inet6AddressImpl_isReachable0; Java_java_net_NetworkInterface_init; Java_java_net_NetworkInterface_getByName0; - Java_java_net_NetworkInterface_getByIndex; + Java_java_net_NetworkInterface_getByIndex0; Java_java_net_NetworkInterface_getByInetAddress0; Java_java_net_NetworkInterface_getAll; Java_java_net_NetworkInterface_isUp0; diff --git a/src/share/classes/java/net/NetworkInterface.java b/src/share/classes/java/net/NetworkInterface.java index 7852fa363..a4f4bc9c6 100644 --- a/src/share/classes/java/net/NetworkInterface.java +++ b/src/share/classes/java/net/NetworkInterface.java @@ -203,11 +203,17 @@ public final class NetworkInterface { } /** - * Get the index of this network interface. + * Returns the index of this network interface. The index is an integer greater + * or equal to zero, or {@code -1} for unknown. This is a system specific value + * and interfaces with the same name can have different indexes on different + * machines. * - * @return the index of this network interface + * @return the index of this network interface or {@code -1} if the index is + * unknown + * @see #getByIndex(int) + * @since 1.7 */ - int getIndex() { + public int getIndex() { return index; } @@ -249,11 +255,18 @@ public final class NetworkInterface { * Get a network interface given its index. * * @param index an integer, the index of the interface - * @return the NetworkInterface obtained from its index - * @exception SocketException if an I/O error occurs. + * @return the NetworkInterface obtained from its index, or {@code null} if + * there is no interface with such an index on the system + * @throws SocketException if an I/O error occurs. + * @throws IllegalArgumentException if index has a negative value + * @see #getIndex() + * @since 1.7 */ - native static NetworkInterface getByIndex(int index) - throws SocketException; + public static NetworkInterface getByIndex(int index) throws SocketException { + if (index < 0) + throw new IllegalArgumentException("Interface index can't be negative"); + return getByIndex0(index); + } /** * Convenience method to search for a network interface that @@ -325,6 +338,9 @@ public final class NetworkInterface { private native static NetworkInterface getByName0(String name) throws SocketException; + private native static NetworkInterface getByIndex0(int index) + throws SocketException; + private native static NetworkInterface getByInetAddress0(InetAddress addr) throws SocketException; diff --git a/src/solaris/native/java/net/NetworkInterface.c b/src/solaris/native/java/net/NetworkInterface.c index a54174483..14273f5c1 100644 --- a/src/solaris/native/java/net/NetworkInterface.c +++ b/src/solaris/native/java/net/NetworkInterface.c @@ -206,10 +206,10 @@ JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByName0 /* * Class: java_net_NetworkInterface - * Method: getByIndex + * Method: getByIndex0 * Signature: (Ljava/lang/String;)Ljava/net/NetworkInterface; */ -JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByIndex +JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByIndex0 (JNIEnv *env, jclass cls, jint index) { netif *ifs, *curr; diff --git a/src/solaris/native/java/net/PlainDatagramSocketImpl.c b/src/solaris/native/java/net/PlainDatagramSocketImpl.c index 7c68f7205..dd3895cdb 100644 --- a/src/solaris/native/java/net/PlainDatagramSocketImpl.c +++ b/src/solaris/native/java/net/PlainDatagramSocketImpl.c @@ -1741,7 +1741,7 @@ jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, jint opt) { * (for IF). */ if (index > 0) { - ni = Java_java_net_NetworkInterface_getByIndex(env, ni_class, + ni = Java_java_net_NetworkInterface_getByIndex0(env, ni_class, index); if (ni == NULL) { char errmsg[255]; diff --git a/src/windows/native/java/net/NetworkInterface.c b/src/windows/native/java/net/NetworkInterface.c index cccf7d440..b59595c7e 100644 --- a/src/windows/native/java/net/NetworkInterface.c +++ b/src/windows/native/java/net/NetworkInterface.c @@ -762,17 +762,17 @@ JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByName0 /* * Class: NetworkInterface - * Method: getByIndex + * Method: getByIndex0 * Signature: (I)LNetworkInterface; */ -JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByIndex +JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByIndex0 (JNIEnv *env, jclass cls, jint index) { netif *ifList, *curr; jobject netifObj = NULL; if (os_supports_ipv6 && ipv6_available()) { - return Java_java_net_NetworkInterface_getByIndex_XP (env, cls, index); + return Java_java_net_NetworkInterface_getByIndex0_XP (env, cls, index); } /* get the list of interfaces */ diff --git a/src/windows/native/java/net/NetworkInterface_winXP.c b/src/windows/native/java/net/NetworkInterface_winXP.c index 87eb2eb9a..e2d878d78 100644 --- a/src/windows/native/java/net/NetworkInterface_winXP.c +++ b/src/windows/native/java/net/NetworkInterface_winXP.c @@ -576,10 +576,10 @@ JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByName0_XP /* * Class: NetworkInterface - * Method: getByIndex + * Method: getByIndex0_XP * Signature: (I)LNetworkInterface; */ -JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByIndex_XP +JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByIndex0_XP (JNIEnv *env, jclass cls, jint index) { netif *ifList, *curr; diff --git a/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c b/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c index 4ada9f964..ddb7a477e 100644 --- a/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c +++ b/src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c @@ -2090,7 +2090,7 @@ jobject getMulticastInterface(JNIEnv *env, jobject this, int fd, int fd1, jint o * (for IF). */ if (index > 0) { - ni = Java_java_net_NetworkInterface_getByIndex(env, ni_class, + ni = Java_java_net_NetworkInterface_getByIndex0(env, ni_class, index); if (ni == NULL) { char errmsg[255]; diff --git a/src/windows/native/java/net/net_util_md.h b/src/windows/native/java/net/net_util_md.h index 9b237c4dc..5b3ae84e4 100644 --- a/src/windows/native/java/net/net_util_md.h +++ b/src/windows/native/java/net/net_util_md.h @@ -329,7 +329,7 @@ extern jint NET_Wait(JNIEnv *env, jint fd, jint flags, jint timeout); JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByName0_XP (JNIEnv *env, jclass cls, jstring name); -JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByIndex_XP +JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByIndex0_XP (JNIEnv *env, jclass cls, jint index); JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByInetAddress0_XP diff --git a/test/java/net/NetworkInterface/IndexTest.java b/test/java/net/NetworkInterface/IndexTest.java new file mode 100644 index 000000000..fd6e4780d --- /dev/null +++ b/test/java/net/NetworkInterface/IndexTest.java @@ -0,0 +1,58 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 6717876 + * @summary Make java.net.NetworkInterface.getIndex() public + */ + +import java.net.*; +import java.util.Enumeration; + +public class IndexTest { + public static void main(String[] args) throws Exception { + Enumeration netifs = NetworkInterface.getNetworkInterfaces(); + NetworkInterface nif = null; + while (netifs.hasMoreElements()) { + nif = netifs.nextElement(); + int index = nif.getIndex(); + if (index >= 0) { + NetworkInterface nif2 = NetworkInterface.getByIndex(index); + if (! nif.equals(nif2)) { + throw new RuntimeException("both interfaces should be equal"); + } + } + } + try { + nif = NetworkInterface.getByIndex(-1); + throw new RuntimeException("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // OK + } + // In all likelyhood, this interface should not exist. + nif = NetworkInterface.getByIndex(Integer.MAX_VALUE - 1); + if (nif != null) { + throw new RuntimeException("getByIndex() should have returned null"); + } + } +} -- GitLab From a90d474b40bcb60da15d2464ea6e9e0b5b28cbd4 Mon Sep 17 00:00:00 2001 From: mlapshin Date: Tue, 26 Aug 2008 12:16:23 +0400 Subject: [PATCH 064/139] 6736649: test/closed/javax/swing/JMenuItem/6458123/ManualBug6458123.java fails on Linux Summary: Now text bearings are taken into account when labelRect width is calculated Reviewed-by: alexp --- .../plaf/synth/SynthMenuItemLayoutHelper.java | 3 +- .../sun/swing/MenuItemLayoutHelper.java | 38 ++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java b/src/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java index 6b890c786..4ca139a70 100644 --- a/src/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java +++ b/src/share/classes/javax/swing/plaf/synth/SynthMenuItemLayoutHelper.java @@ -136,7 +136,7 @@ class SynthMenuItemLayoutHelper extends MenuItemLayoutHelper { // accRect if (!getAccText().equals("")) { - getAccSize().setWidth(accGu.computeStringWidth(getAccContext(), + getAccSize().setWidth(accGu.computeStringWidth(getAccContext(), getAccFontMetrics().getFont(), getAccFontMetrics(), getAccText())); getAccSize().setHeight(getAccFontMetrics().getHeight()); @@ -195,6 +195,7 @@ class SynthMenuItemLayoutHelper extends MenuItemLayoutHelper { getHorizontalAlignment(), getVerticalAlignment(), getHorizontalTextPosition(), getVerticalTextPosition(), getViewRect(), iconRect, textRect, getGap()); + textRect.width += getLeftTextExtraWidth() + getRightTextExtraWidth(); Rectangle labelRect = iconRect.union(textRect); getLabelSize().setHeight(labelRect.height); getLabelSize().setWidth(labelRect.width); diff --git a/src/share/classes/sun/swing/MenuItemLayoutHelper.java b/src/share/classes/sun/swing/MenuItemLayoutHelper.java index b1313e431..109d0c402 100644 --- a/src/share/classes/sun/swing/MenuItemLayoutHelper.java +++ b/src/share/classes/sun/swing/MenuItemLayoutHelper.java @@ -83,6 +83,9 @@ public class MenuItemLayoutHelper { private int afterCheckIconGap; private int minTextOffset; + private int leftTextExtraWidth; + private int rightTextExtraWidth; + private Rectangle viewRect; private RectSize iconSize; @@ -143,6 +146,7 @@ public class MenuItemLayoutHelper { this.checkSize = new RectSize(); this.arrowSize = new RectSize(); this.labelSize = new RectSize(); + calcExtraWidths(); calcWidthsAndHeights(); setOriginalWidths(); calcMaxWidths(); @@ -151,6 +155,29 @@ public class MenuItemLayoutHelper { calcMaxTextOffset(viewRect); } + private void calcExtraWidths() { + leftTextExtraWidth = getLeftExtraWidth(text); + rightTextExtraWidth = getRightExtraWidth(text); + } + + private int getLeftExtraWidth(String str) { + int lsb = SwingUtilities2.getLeftSideBearing(mi, fm, str); + if (lsb < 0) { + return -lsb; + } else { + return 0; + } + } + + private int getRightExtraWidth(String str) { + int rsb = SwingUtilities2.getRightSideBearing(mi, fm, str); + if (rsb > 0) { + return rsb; + } else { + return 0; + } + } + private void setOriginalWidths() { iconSize.origWidth = iconSize.width; textSize.origWidth = textSize.width; @@ -286,6 +313,7 @@ public class MenuItemLayoutHelper { verticalAlignment, horizontalAlignment, verticalTextPosition, horizontalTextPosition, viewRect, iconRect, textRect, gap); + textRect.width += leftTextExtraWidth + rightTextExtraWidth; Rectangle labelRect = iconRect.union(textRect); labelSize.height = labelRect.height; labelSize.width = labelRect.width; @@ -727,7 +755,7 @@ public class MenuItemLayoutHelper { } } - /** + /** * Sets Y coordinates of text and icon * taking into account the vertical alignment */ @@ -1089,6 +1117,14 @@ public class MenuItemLayoutHelper { this.labelSize = labelSize; } + public int getLeftTextExtraWidth() { + return leftTextExtraWidth; + } + + public int getRightTextExtraWidth() { + return rightTextExtraWidth; + } + /** * Returns false if the component is a JMenu and it is a top * level menu (on the menubar). -- GitLab From d7586529e5309a09b697a679fa53165c55a0d17b Mon Sep 17 00:00:00 2001 From: alanb Date: Tue, 26 Aug 2008 09:23:12 +0100 Subject: [PATCH 065/139] 6728542: (se) epoll based SelectorProvider should be portable to platforms other than x86 and x64 Reviewed-by: sherman --- make/java/nio/mapfile-linux | 2 ++ .../classes/sun/nio/ch/EPollArrayWrapper.java | 12 +++++----- .../native/sun/nio/ch/EPollArrayWrapper.c | 22 ++++++++++++++++++- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/make/java/nio/mapfile-linux b/make/java/nio/mapfile-linux index dc2044c9d..2eba69d17 100644 --- a/make/java/nio/mapfile-linux +++ b/make/java/nio/mapfile-linux @@ -18,6 +18,8 @@ SUNWprivate_1.1 { Java_sun_nio_ch_EPollArrayWrapper_fdLimit; Java_sun_nio_ch_EPollArrayWrapper_init; Java_sun_nio_ch_EPollArrayWrapper_interrupt; + Java_sun_nio_ch_EPollArrayWrapper_offsetofData; + Java_sun_nio_ch_EPollArrayWrapper_sizeofEPollEvent; Java_sun_nio_ch_FileChannelImpl_close0; Java_sun_nio_ch_FileChannelImpl_force0; Java_sun_nio_ch_FileChannelImpl_initIDs; diff --git a/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java b/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java index 4ee5e0617..8ebb0c223 100644 --- a/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java +++ b/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java @@ -69,11 +69,11 @@ class EPollArrayWrapper { static final int EPOLL_CTL_MOD = 3; // Miscellaneous constants - static final short SIZE_EPOLLEVENT = 12; - static final short EVENT_OFFSET = 0; - static final short DATA_OFFSET = 4; - static final short FD_OFFSET = 4; - static final int NUM_EPOLLEVENTS = Math.min(fdLimit(), 8192); + static final int SIZE_EPOLLEVENT = sizeofEPollEvent(); + static final int EVENT_OFFSET = 0; + static final int DATA_OFFSET = offsetofData(); + static final int FD_OFFSET = DATA_OFFSET; + static final int NUM_EPOLLEVENTS = Math.min(fdLimit(), 8192); // Base address of the native pollArray private final long pollArrayAddress; @@ -280,6 +280,8 @@ class EPollArrayWrapper { private native void epollCtl(int epfd, int opcode, int fd, int events); private native int epollWait(long pollAddress, int numfds, long timeout, int epfd) throws IOException; + private static native int sizeofEPollEvent(); + private static native int offsetofData(); private static native int fdLimit(); private static native void interrupt(int fd); private static native void init(); diff --git a/src/solaris/native/sun/nio/ch/EPollArrayWrapper.c b/src/solaris/native/sun/nio/ch/EPollArrayWrapper.c index ad9237b2e..7b4a4e738 100644 --- a/src/solaris/native/sun/nio/ch/EPollArrayWrapper.c +++ b/src/solaris/native/sun/nio/ch/EPollArrayWrapper.c @@ -48,10 +48,18 @@ typedef union epoll_data { __uint64_t u64; } epoll_data_t; + +/* x86-64 has same alignment as 32-bit */ +#ifdef __x86_64__ +#define EPOLL_PACKED __attribute__((packed)) +#else +#define EPOLL_PACKED +#endif + struct epoll_event { __uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ -} __attribute__ ((__packed__)); +} EPOLL_PACKED; #ifdef __cplusplus } @@ -143,6 +151,18 @@ Java_sun_nio_ch_EPollArrayWrapper_fdLimit(JNIEnv *env, jclass this) return (jint)rlp.rlim_max; } +JNIEXPORT jint JNICALL +Java_sun_nio_ch_EPollArrayWrapper_sizeofEPollEvent(JNIEnv* env, jclass this) +{ + return sizeof(struct epoll_event); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_EPollArrayWrapper_offsetofData(JNIEnv* env, jclass this) +{ + return offsetof(struct epoll_event, data); +} + JNIEXPORT void JNICALL Java_sun_nio_ch_EPollArrayWrapper_epollCtl(JNIEnv *env, jobject this, jint epfd, jint opcode, jint fd, jint events) -- GitLab From 0c812b3150a95155b54b4ca6ec3ee44aab52046b Mon Sep 17 00:00:00 2001 From: alanb Date: Tue, 26 Aug 2008 10:21:22 +0100 Subject: [PATCH 066/139] 6682020: (bf) Support monitoring of direct and mapped buffer usage Reviewed-by: mchung, iris --- make/java/java/FILES_java.gmk | 3 +- make/java/nio/FILES_java.gmk | 1 + .../lang/management/PlatformComponent.java | 18 +++ src/share/classes/java/nio/Bits.java | 66 ++++++++++- .../classes/java/nio/BufferPoolMXBean.java | 94 ++++++++++++++++ .../classes/java/nio/Direct-X-Buffer.java | 18 +-- src/share/classes/sun/misc/JavaNioAccess.java | 32 ++++++ src/share/classes/sun/misc/SharedSecrets.java | 15 +++ .../classes/sun/nio/ch/FileChannelImpl.java | 71 +++++++++++- test/java/nio/BufferPoolMXBean/Basic.java | 106 ++++++++++++++++++ 10 files changed, 409 insertions(+), 15 deletions(-) create mode 100644 src/share/classes/java/nio/BufferPoolMXBean.java create mode 100644 src/share/classes/sun/misc/JavaNioAccess.java create mode 100644 test/java/nio/BufferPoolMXBean/Basic.java diff --git a/make/java/java/FILES_java.gmk b/make/java/java/FILES_java.gmk index b4a07fc35..2531ce723 100644 --- a/make/java/java/FILES_java.gmk +++ b/make/java/java/FILES_java.gmk @@ -449,6 +449,7 @@ JAVA_JAVA_java = \ sun/misc/JavaLangAccess.java \ sun/misc/JavaIOAccess.java \ sun/misc/JavaIODeleteOnExitAccess.java \ - sun/misc/JavaIOFileDescriptorAccess.java + sun/misc/JavaIOFileDescriptorAccess.java \ + sun/misc/JavaNioAccess.java FILES_java = $(JAVA_JAVA_java) diff --git a/make/java/nio/FILES_java.gmk b/make/java/nio/FILES_java.gmk index 5958812ad..c99f55204 100644 --- a/make/java/nio/FILES_java.gmk +++ b/make/java/nio/FILES_java.gmk @@ -26,6 +26,7 @@ FILES_src = \ java/nio/Bits.java \ java/nio/Buffer.java \ + java/nio/BufferPoolMXBean.java \ java/nio/ByteOrder.java \ java/nio/MappedByteBuffer.java \ java/nio/StringCharBuffer.java \ diff --git a/src/share/classes/java/lang/management/PlatformComponent.java b/src/share/classes/java/lang/management/PlatformComponent.java index 3cd5d369d..a90d0c7c5 100644 --- a/src/share/classes/java/lang/management/PlatformComponent.java +++ b/src/share/classes/java/lang/management/PlatformComponent.java @@ -32,6 +32,7 @@ import java.util.HashSet; import java.util.Set; import java.util.logging.LoggingMXBean; import java.util.logging.LogManager; +import java.nio.BufferPoolMXBean; import javax.management.MBeanServerConnection; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; @@ -188,6 +189,23 @@ enum PlatformComponent { } }), + + /** + * Buffer pools. + */ + BUFFER_POOL( + "java.nio.BufferPoolMXBean", + "java.nio", "BufferPool", keyProperties("name"), + new MXBeanFetcher() { + public List getMXBeans() { + List pools = new ArrayList(2); + pools.add( sun.misc.SharedSecrets.getJavaNioAccess().getDirectBufferPoolMXBean() ); + pools.add( sun.nio.ch.FileChannelImpl.getMappedBufferPoolMXBean() ); + return pools; + } + }), + + // Sun Platform Extension /** diff --git a/src/share/classes/java/nio/Bits.java b/src/share/classes/java/nio/Bits.java index 603ee11bb..ee74686c9 100644 --- a/src/share/classes/java/nio/Bits.java +++ b/src/share/classes/java/nio/Bits.java @@ -29,6 +29,8 @@ import java.security.AccessController; import java.security.PrivilegedAction; import sun.misc.Unsafe; import sun.misc.VM; +import javax.management.ObjectName; +import javax.management.MalformedObjectNameException; /** * Access to bits, native and otherwise. @@ -625,13 +627,15 @@ class Bits { // package-private // direct buffer memory. This value may be changed during VM // initialization if it is launched with "-XX:MaxDirectMemorySize=". private static volatile long maxMemory = VM.maxDirectMemory(); - private static volatile long reservedMemory = 0; + private static volatile long reservedMemory; + private static volatile long usedMemory; + private static volatile long count; private static boolean memoryLimitSet = false; // These methods should be called whenever direct memory is allocated or // freed. They allow the user to control the amount of direct memory // which a process may access. All sizes are specified in bytes. - static void reserveMemory(long size) { + static void reserveMemory(long size, int cap) { synchronized (Bits.class) { if (!memoryLimitSet && VM.isBooted()) { @@ -640,6 +644,8 @@ class Bits { // package-private } if (size <= maxMemory - reservedMemory) { reservedMemory += size; + usedMemory += cap; + count++; return; } } @@ -655,17 +661,71 @@ class Bits { // package-private if (reservedMemory + size > maxMemory) throw new OutOfMemoryError("Direct buffer memory"); reservedMemory += size; + usedMemory += cap; + count++; } } - static synchronized void unreserveMemory(long size) { + static synchronized void unreserveMemory(long size, int cap) { if (reservedMemory > 0) { reservedMemory -= size; + usedMemory -= cap; + count--; assert (reservedMemory > -1); } } + // -- Management interface for monitoring of direct buffer usage -- + + static { + // setup access to this package in SharedSecrets + sun.misc.SharedSecrets.setJavaNioAccess( + new sun.misc.JavaNioAccess() { + @Override + public BufferPoolMXBean getDirectBufferPoolMXBean() { + return LazyInitialization.directBufferPoolMXBean; + } + } + ); + } + + // Lazy initialization of management interface + private static class LazyInitialization { + static final BufferPoolMXBean directBufferPoolMXBean = directBufferPoolMXBean(); + + private static BufferPoolMXBean directBufferPoolMXBean() { + final String pool = "direct"; + final ObjectName obj; + try { + obj = new ObjectName("java.nio:type=BufferPool,name=" + pool); + } catch (MalformedObjectNameException x) { + throw new AssertionError(x); + } + return new BufferPoolMXBean() { + @Override + public ObjectName getObjectName() { + return obj; + } + @Override + public String getName() { + return pool; + } + @Override + public long getCount() { + return Bits.count; + } + @Override + public long getTotalCapacity() { + return Bits.usedMemory; + } + @Override + public long getMemoryUsed() { + return Bits.reservedMemory; + } + }; + } + } // -- Bulk get/put acceleration -- diff --git a/src/share/classes/java/nio/BufferPoolMXBean.java b/src/share/classes/java/nio/BufferPoolMXBean.java new file mode 100644 index 000000000..0a7fe303f --- /dev/null +++ b/src/share/classes/java/nio/BufferPoolMXBean.java @@ -0,0 +1,94 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.nio; + +import java.lang.management.PlatformManagedObject; + +/** + * The management interface for a buffer pool. + * + *

      A class implementing this interface is an MXBean. A Java + * virtual machine has one or more implementations of this interface. The {@link + * java.lang.management.ManagementFactory#getPlatformMXBeans getPlatformMXBeans} + * method can be used to obtain the list of {@code BufferPoolMXBean} objects + * representing the management interfaces for pools of buffers as follows: + *

      + *     List<BufferPoolMXBean> pools = ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class);
      + * 
      + * + *

      The management interfaces are also registered with the platform {@link + * javax.management.MBeanServer MBeanServer}. The {@link + * javax.management.ObjectName ObjectName} that uniquely identifies the + * management interface within the {@code MBeanServer} takes the form: + *

      + * java.nio:type=BufferPool,name=pool name + *
      + * where pool name is the {@link #getName name} of the buffer pool. + * + * @since 1.7 + */ + +public interface BufferPoolMXBean extends PlatformManagedObject { + + /** + * Returns the name representing this buffer pool. + * + * @return The name of this buffer pool. + */ + String getName(); + + /** + * Returns an estimate of the number of buffers in the pool. + * + * @return An estimate of the number of buffers in this pool + */ + long getCount(); + + /** + * Returns an estimate of the total capacity of the buffers in this pool. + * A buffer's capacity is the number of elements it contains and the value + * returned by this method is an estimate of the total capacity of buffers + * in the pool in bytes. + * + * @return An estimate of the total capacity of the buffers in this pool + * in bytes + */ + long getTotalCapacity(); + + /** + * Returns an estimate of the memory that the Java virtual machine is using + * for this buffer pool. The value returned by this method may differ + * from the estimate of the total {@link #getTotalCapacity capacity} of + * the buffers in this pool. This difference is explained by alignment, + * memory allocator, and other implementation specific reasons. + * + * @return An estimate of the memory that the Java virtual machine is using + * for this buffer pool in bytes, or {@code -1L} if an estimate of + * the memory usage is not available + */ + long getMemoryUsed(); +} diff --git a/src/share/classes/java/nio/Direct-X-Buffer.java b/src/share/classes/java/nio/Direct-X-Buffer.java index 092ac0e83..0ad03feef 100644 --- a/src/share/classes/java/nio/Direct-X-Buffer.java +++ b/src/share/classes/java/nio/Direct-X-Buffer.java @@ -71,11 +71,13 @@ class Direct$Type$Buffer$RW$$BO$ private static Unsafe unsafe = Unsafe.getUnsafe(); private long address; + private long size; private int capacity; - private Deallocator(long address, int capacity) { + private Deallocator(long address, long size, int capacity) { assert (address != 0); this.address = address; + this.size = size; this.capacity = capacity; } @@ -86,7 +88,7 @@ class Direct$Type$Buffer$RW$$BO$ } unsafe.freeMemory(address); address = 0; - Bits.unreserveMemory(capacity); + Bits.unreserveMemory(size, capacity); } } @@ -110,23 +112,25 @@ class Direct$Type$Buffer$RW$$BO$ Direct$Type$Buffer$RW$(int cap) { // package-private #if[rw] super(-1, 0, cap, cap, false); - Bits.reserveMemory(cap); int ps = Bits.pageSize(); + int size = cap + ps; + Bits.reserveMemory(size, cap); + long base = 0; try { - base = unsafe.allocateMemory(cap + ps); + base = unsafe.allocateMemory(size); } catch (OutOfMemoryError x) { - Bits.unreserveMemory(cap); + Bits.unreserveMemory(size, cap); throw x; } - unsafe.setMemory(base, cap + ps, (byte) 0); + unsafe.setMemory(base, size, (byte) 0); if (base % ps != 0) { // Round up to page boundary address = base + ps - (base & (ps - 1)); } else { address = base; } - cleaner = Cleaner.create(this, new Deallocator(base, cap)); + cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); #else[rw] super(cap); #end[rw] diff --git a/src/share/classes/sun/misc/JavaNioAccess.java b/src/share/classes/sun/misc/JavaNioAccess.java new file mode 100644 index 000000000..4781cb7b4 --- /dev/null +++ b/src/share/classes/sun/misc/JavaNioAccess.java @@ -0,0 +1,32 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.misc; + +import java.nio.BufferPoolMXBean; + +public interface JavaNioAccess { + BufferPoolMXBean getDirectBufferPoolMXBean(); +} diff --git a/src/share/classes/sun/misc/SharedSecrets.java b/src/share/classes/sun/misc/SharedSecrets.java index 3a9db2364..b9de220bb 100644 --- a/src/share/classes/sun/misc/SharedSecrets.java +++ b/src/share/classes/sun/misc/SharedSecrets.java @@ -46,6 +46,7 @@ public class SharedSecrets { private static JavaIOAccess javaIOAccess; private static JavaIODeleteOnExitAccess javaIODeleteOnExitAccess; private static JavaNetAccess javaNetAccess; + private static JavaNioAccess javaNioAccess; private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess; public static JavaUtilJarAccess javaUtilJarAccess() { @@ -77,6 +78,20 @@ public class SharedSecrets { return javaNetAccess; } + public static void setJavaNioAccess(JavaNioAccess jna) { + javaNioAccess = jna; + } + + public static JavaNioAccess getJavaNioAccess() { + if (javaNioAccess == null) { + // Ensure java.nio.ByteOrder is initialized; we know that + // this class initializes java.nio.Bits that provides the + // shared secret. + unsafe.ensureClassInitialized(java.nio.ByteOrder.class); + } + return javaNioAccess; + } + public static void setJavaIOAccess(JavaIOAccess jia) { javaIOAccess = jia; } diff --git a/src/share/classes/sun/nio/ch/FileChannelImpl.java b/src/share/classes/sun/nio/ch/FileChannelImpl.java index c4ac22c04..894c489cd 100644 --- a/src/share/classes/sun/nio/ch/FileChannelImpl.java +++ b/src/share/classes/sun/nio/ch/FileChannelImpl.java @@ -32,6 +32,7 @@ import java.io.RandomAccessFile; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; +import java.nio.BufferPoolMXBean; import java.nio.channels.*; import java.nio.channels.spi.*; import java.util.ArrayList; @@ -43,10 +44,12 @@ import java.lang.ref.ReferenceQueue; import java.lang.reflect.Field; import java.security.AccessController; import java.security.PrivilegedAction; +import javax.management.ObjectName; +import javax.management.MalformedObjectNameException; + import sun.misc.Cleaner; import sun.security.action.GetPropertyAction; - public class FileChannelImpl extends FileChannel { @@ -681,14 +684,26 @@ public class FileChannelImpl private static class Unmapper implements Runnable { + // keep track of mapped buffer usage + static volatile int count; + static volatile long totalSize; + static volatile long totalCapacity; private long address; private long size; + private int cap; - private Unmapper(long address, long size) { + private Unmapper(long address, long size, int cap) { assert (address != 0); this.address = address; this.size = size; + this.cap = cap; + + synchronized (Unmapper.class) { + count++; + totalSize += size; + totalCapacity += cap; + } } public void run() { @@ -696,8 +711,13 @@ public class FileChannelImpl return; unmap0(address, size); address = 0; - } + synchronized (Unmapper.class) { + count--; + totalSize -= size; + totalCapacity -= cap; + } + } } private static void unmap(MappedByteBuffer bb) { @@ -786,7 +806,7 @@ public class FileChannelImpl assert (IOStatus.checkAll(addr)); assert (addr % allocationGranularity == 0); int isize = (int)size; - Unmapper um = new Unmapper(addr, size + pagePosition); + Unmapper um = new Unmapper(addr, size + pagePosition, isize); if ((!writable) || (imode == MAP_RO)) return Util.newMappedByteBufferR(isize, addr + pagePosition, um); else @@ -797,6 +817,49 @@ public class FileChannelImpl } } + /** + * Returns the management interface for mapped buffers + */ + public static BufferPoolMXBean getMappedBufferPoolMXBean() { + return LazyInitialization.mappedBufferPoolMXBean; + } + + // Lazy initialization of management interface + private static class LazyInitialization { + static final BufferPoolMXBean mappedBufferPoolMXBean = mappedBufferPoolMXBean(); + + private static BufferPoolMXBean mappedBufferPoolMXBean() { + final String pool = "mapped"; + final ObjectName obj; + try { + obj = new ObjectName("java.nio:type=BufferPool,name=" + pool); + } catch (MalformedObjectNameException x) { + throw new AssertionError(x); + } + return new BufferPoolMXBean() { + @Override + public ObjectName getObjectName() { + return obj; + } + @Override + public String getName() { + return pool; + } + @Override + public long getCount() { + return Unmapper.count; + } + @Override + public long getTotalCapacity() { + return Unmapper.totalCapacity; + } + @Override + public long getMemoryUsed() { + return Unmapper.totalSize; + } + }; + } + } // -- Locks -- diff --git a/test/java/nio/BufferPoolMXBean/Basic.java b/test/java/nio/BufferPoolMXBean/Basic.java new file mode 100644 index 000000000..98e5f0e0b --- /dev/null +++ b/test/java/nio/BufferPoolMXBean/Basic.java @@ -0,0 +1,106 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 6606598 + * @summary Unit test for java.nio.BufferPoolMXBean + */ + +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.BufferPoolMXBean; +import java.nio.channels.FileChannel; +import java.io.File; +import java.io.RandomAccessFile; +import java.lang.management.ManagementFactory; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import java.util.*; + +public class Basic { + + // static fields to ensure buffers aren't GC'ed + static List buffers; + static MappedByteBuffer mbb; + + // check counters + static void check(List pools, + int minBufferCount, + long minTotalCapacity) + { + int bufferCount = 0; + long totalCap = 0; + long totalMem = 0; + for (BufferPoolMXBean pool: pools) { + bufferCount += pool.getCount(); + totalCap += pool.getTotalCapacity(); + totalMem += pool.getMemoryUsed(); + } + if (bufferCount < minBufferCount) + throw new RuntimeException("Count less than expected"); + if (totalMem < minTotalCapacity) + throw new RuntimeException("Memory usage less than expected"); + if (totalCap < minTotalCapacity) + throw new RuntimeException("Total capacity less than expected"); + } + + public static void main(String[] args) throws Exception { + Random rand = new Random(); + + // allocate a few direct buffers + int bufferCount = 5 + rand.nextInt(20); + buffers = new ArrayList(bufferCount); + long totalCapacity = 0L; + for (int i=0; i pools = + ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class); + check(pools, bufferCount, totalCapacity); + + // using MBeanServer + MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + Set mbeans = server.queryNames( + new ObjectName("java.nio:type=BufferPool,*"), null); + pools = new ArrayList(); + for (ObjectName name: mbeans) { + BufferPoolMXBean pool = ManagementFactory + .newPlatformMXBeanProxy(server, name.toString(), BufferPoolMXBean.class); + pools.add(pool); + } + check(pools, bufferCount, totalCapacity); + } +} -- GitLab From 363ab66ca905809db7e0dc133c7d4277a729a7f5 Mon Sep 17 00:00:00 2001 From: rupashka Date: Tue, 26 Aug 2008 15:12:54 +0400 Subject: [PATCH 067/139] 6727662: Code improvement and warnings removing from swing packages Summary: Removed unnecessary castings and other warnings Reviewed-by: malenkov --- .../classes/javax/swing/AbstractButton.java | 13 +-- .../javax/swing/AbstractCellEditor.java | 3 +- .../javax/swing/AbstractListModel.java | 3 +- .../javax/swing/AbstractSpinnerModel.java | 3 +- src/share/classes/javax/swing/ActionMap.java | 2 +- .../classes/javax/swing/AncestorNotifier.java | 2 +- src/share/classes/javax/swing/ArrayTable.java | 10 +- .../classes/javax/swing/ButtonGroup.java | 2 +- .../javax/swing/DebugGraphicsInfo.java | 6 +- .../javax/swing/DefaultBoundedRangeModel.java | 3 +- .../javax/swing/DefaultButtonModel.java | 8 +- .../javax/swing/DefaultFocusManager.java | 7 +- .../swing/DefaultListSelectionModel.java | 3 +- .../swing/DefaultSingleSelectionModel.java | 3 +- .../classes/javax/swing/GroupLayout.java | 4 +- src/share/classes/javax/swing/InputMap.java | 4 +- src/share/classes/javax/swing/JComboBox.java | 12 +-- src/share/classes/javax/swing/JComponent.java | 47 +++++----- .../classes/javax/swing/JDesktopPane.java | 24 +++-- src/share/classes/javax/swing/JDialog.java | 4 +- .../classes/javax/swing/JEditorPane.java | 44 +++++---- .../classes/javax/swing/JFileChooser.java | 19 ++-- .../classes/javax/swing/JInternalFrame.java | 11 +-- .../classes/javax/swing/JLayeredPane.java | 12 +-- src/share/classes/javax/swing/JList.java | 15 ++- src/share/classes/javax/swing/JMenu.java | 28 +++--- src/share/classes/javax/swing/JMenuBar.java | 15 ++- src/share/classes/javax/swing/JMenuItem.java | 10 +- .../classes/javax/swing/JOptionPane.java | 37 ++++---- src/share/classes/javax/swing/JPopupMenu.java | 27 +++--- .../classes/javax/swing/JProgressBar.java | 3 +- src/share/classes/javax/swing/JScrollBar.java | 7 +- src/share/classes/javax/swing/JSlider.java | 7 +- src/share/classes/javax/swing/JSpinner.java | 7 +- .../classes/javax/swing/JTabbedPane.java | 5 +- src/share/classes/javax/swing/JTable.java | 40 ++++---- src/share/classes/javax/swing/JTextField.java | 3 +- src/share/classes/javax/swing/JTree.java | 86 ++++++++---------- src/share/classes/javax/swing/JViewport.java | 20 ++-- src/share/classes/javax/swing/JWindow.java | 6 +- .../classes/javax/swing/KeyboardManager.java | 22 ++--- .../classes/javax/swing/LayoutComparator.java | 21 ++--- .../swing/LayoutFocusTraversalPolicy.java | 2 +- .../swing/LegacyGlueFocusTraversalPolicy.java | 12 +-- .../javax/swing/MenuSelectionManager.java | 25 +++-- .../classes/javax/swing/MultiUIDefaults.java | 21 ++--- .../classes/javax/swing/PopupFactory.java | 91 +++++++++---------- .../classes/javax/swing/RepaintManager.java | 20 ++-- .../swing/SortingFocusTraversalPolicy.java | 18 ++-- .../classes/javax/swing/SpringLayout.java | 18 ++-- .../classes/javax/swing/SwingUtilities.java | 34 +++---- src/share/classes/javax/swing/Timer.java | 3 +- src/share/classes/javax/swing/TimerQueue.java | 4 +- src/share/classes/javax/swing/UIDefaults.java | 33 ++++--- src/share/classes/javax/swing/UIManager.java | 34 ++++--- .../swing/filechooser/FileSystemView.java | 32 +++---- .../javax/swing/table/AbstractTableModel.java | 3 +- .../javax/swing/table/DefaultTableModel.java | 12 +-- .../swing/tree/DefaultTreeCellEditor.java | 2 +- .../javax/swing/tree/DefaultTreeModel.java | 5 +- .../swing/tree/DefaultTreeSelectionModel.java | 41 ++++----- .../swing/tree/FixedHeightLayoutCache.java | 18 ++-- .../swing/tree/VariableHeightLayoutCache.java | 22 ++--- .../classes/javax/swing/undo/StateEdit.java | 6 +- .../classes/javax/swing/undo/UndoManager.java | 16 ++-- .../javax/swing/undo/UndoableEditSupport.java | 3 +- .../classes/sun/swing/AccessibleMethod.java | 2 +- src/share/classes/sun/swing/FilePane.java | 21 ++--- .../classes/sun/swing/SwingLazyValue.java | 5 +- .../classes/sun/swing/SwingUtilities2.java | 26 +++--- 70 files changed, 514 insertions(+), 623 deletions(-) diff --git a/src/share/classes/javax/swing/AbstractButton.java b/src/share/classes/javax/swing/AbstractButton.java index 4248a7144..d48108101 100644 --- a/src/share/classes/javax/swing/AbstractButton.java +++ b/src/share/classes/javax/swing/AbstractButton.java @@ -1315,8 +1315,7 @@ public abstract class AbstractButton extends JComponent implements ItemSelectabl // Make sure the change actually took effect if (!selected && isSelected()) { if (getModel() instanceof DefaultButtonModel) { - ButtonGroup group = (ButtonGroup) - ((DefaultButtonModel)getModel()).getGroup(); + ButtonGroup group = ((DefaultButtonModel)getModel()).getGroup(); if (group != null) { group.clearSelection(); } @@ -1886,8 +1885,7 @@ public abstract class AbstractButton extends JComponent implements ItemSelectabl * @since 1.4 */ public ChangeListener[] getChangeListeners() { - return (ChangeListener[])(listenerList.getListeners( - ChangeListener.class)); + return listenerList.getListeners(ChangeListener.class); } /** @@ -1944,8 +1942,7 @@ public abstract class AbstractButton extends JComponent implements ItemSelectabl * @since 1.4 */ public ActionListener[] getActionListeners() { - return (ActionListener[])(listenerList.getListeners( - ActionListener.class)); + return listenerList.getListeners(ActionListener.class); } /** @@ -2137,7 +2134,7 @@ public abstract class AbstractButton extends JComponent implements ItemSelectabl * @since 1.4 */ public ItemListener[] getItemListeners() { - return (ItemListener[])listenerList.getListeners(ItemListener.class); + return listenerList.getListeners(ItemListener.class); } /** @@ -2981,7 +2978,7 @@ public abstract class AbstractButton extends JComponent implements ItemSelectabl paintViewR.height = AbstractButton.this.getHeight() - (paintViewInsets.top + paintViewInsets.bottom); String clippedText = SwingUtilities.layoutCompoundLabel( - (JComponent)AbstractButton.this, + AbstractButton.this, getFontMetrics(getFont()), text, icon, diff --git a/src/share/classes/javax/swing/AbstractCellEditor.java b/src/share/classes/javax/swing/AbstractCellEditor.java index 5ecdc4390..90b1c8d69 100644 --- a/src/share/classes/javax/swing/AbstractCellEditor.java +++ b/src/share/classes/javax/swing/AbstractCellEditor.java @@ -118,8 +118,7 @@ public abstract class AbstractCellEditor implements CellEditor, Serializable { * @since 1.4 */ public CellEditorListener[] getCellEditorListeners() { - return (CellEditorListener[])listenerList.getListeners( - CellEditorListener.class); + return listenerList.getListeners(CellEditorListener.class); } /** diff --git a/src/share/classes/javax/swing/AbstractListModel.java b/src/share/classes/javax/swing/AbstractListModel.java index 102218bdb..00c8841a5 100644 --- a/src/share/classes/javax/swing/AbstractListModel.java +++ b/src/share/classes/javax/swing/AbstractListModel.java @@ -85,8 +85,7 @@ public abstract class AbstractListModel implements ListModel, Serializable * @since 1.4 */ public ListDataListener[] getListDataListeners() { - return (ListDataListener[])listenerList.getListeners( - ListDataListener.class); + return listenerList.getListeners(ListDataListener.class); } diff --git a/src/share/classes/javax/swing/AbstractSpinnerModel.java b/src/share/classes/javax/swing/AbstractSpinnerModel.java index 295633912..9cf16c6db 100644 --- a/src/share/classes/javax/swing/AbstractSpinnerModel.java +++ b/src/share/classes/javax/swing/AbstractSpinnerModel.java @@ -98,8 +98,7 @@ public abstract class AbstractSpinnerModel implements SpinnerModel, Serializable * @since 1.4 */ public ChangeListener[] getChangeListeners() { - return (ChangeListener[])listenerList.getListeners( - ChangeListener.class); + return listenerList.getListeners(ChangeListener.class); } diff --git a/src/share/classes/javax/swing/ActionMap.java b/src/share/classes/javax/swing/ActionMap.java index b1d4e4b20..3858eabe4 100644 --- a/src/share/classes/javax/swing/ActionMap.java +++ b/src/share/classes/javax/swing/ActionMap.java @@ -197,7 +197,7 @@ public class ActionMap implements Serializable { return pKeys; } - HashMap keyMap = new HashMap(); + HashMap keyMap = new HashMap(); int counter; for (counter = keys.length - 1; counter >= 0; counter--) { diff --git a/src/share/classes/javax/swing/AncestorNotifier.java b/src/share/classes/javax/swing/AncestorNotifier.java index 694bc5df0..eb67ed154 100644 --- a/src/share/classes/javax/swing/AncestorNotifier.java +++ b/src/share/classes/javax/swing/AncestorNotifier.java @@ -62,7 +62,7 @@ class AncestorNotifier implements ComponentListener, PropertyChangeListener, Ser } AncestorListener[] getAncestorListeners() { - return (AncestorListener[])listenerList.getListeners(AncestorListener.class); + return listenerList.getListeners(AncestorListener.class); } /** diff --git a/src/share/classes/javax/swing/ArrayTable.java b/src/share/classes/javax/swing/ArrayTable.java index b44fc436c..1ee5f07d8 100644 --- a/src/share/classes/javax/swing/ArrayTable.java +++ b/src/share/classes/javax/swing/ArrayTable.java @@ -88,10 +88,10 @@ class ArrayTable implements Cloneable { // Write ou the Serializable key/value pairs. s.writeInt(validCount); if (validCount > 0) { - for (int counter = 0; counter < keys.length; counter++) { - if (keys[counter] != null) { - s.writeObject(keys[counter]); - s.writeObject(table.get(keys[counter])); + for (Object key : keys) { + if (key != null) { + s.writeObject(key); + s.writeObject(table.get(key)); if (--validCount == 0) { break; } @@ -315,7 +315,7 @@ class ArrayTable implements Cloneable { */ private void grow() { Object[] array = (Object[])table; - Hashtable tmp = new Hashtable(array.length/2); + Hashtable tmp = new Hashtable(array.length/2); for (int i = 0; i buttons = new Vector(); + protected Vector buttons = new Vector(); /** * The current selection. diff --git a/src/share/classes/javax/swing/DebugGraphicsInfo.java b/src/share/classes/javax/swing/DebugGraphicsInfo.java index f9e6c72a9..75be3e7f8 100644 --- a/src/share/classes/javax/swing/DebugGraphicsInfo.java +++ b/src/share/classes/javax/swing/DebugGraphicsInfo.java @@ -37,7 +37,7 @@ class DebugGraphicsInfo { Color flashColor = Color.red; int flashTime = 100; int flashCount = 2; - Hashtable componentToDebug; + Hashtable componentToDebug; JFrame debugFrame = null; java.io.PrintStream stream = System.out; @@ -46,7 +46,7 @@ class DebugGraphicsInfo { return; } if (componentToDebug == null) { - componentToDebug = new Hashtable(); + componentToDebug = new Hashtable(); } if (debug > 0) { componentToDebug.put(component, Integer.valueOf(debug)); @@ -59,7 +59,7 @@ class DebugGraphicsInfo { if (componentToDebug == null) { return 0; } else { - Integer integer = (Integer)componentToDebug.get(component); + Integer integer = componentToDebug.get(component); return integer == null ? 0 : integer.intValue(); } diff --git a/src/share/classes/javax/swing/DefaultBoundedRangeModel.java b/src/share/classes/javax/swing/DefaultBoundedRangeModel.java index d8e4f0f04..c718ce602 100644 --- a/src/share/classes/javax/swing/DefaultBoundedRangeModel.java +++ b/src/share/classes/javax/swing/DefaultBoundedRangeModel.java @@ -343,8 +343,7 @@ public class DefaultBoundedRangeModel implements BoundedRangeModel, Serializable * @since 1.4 */ public ChangeListener[] getChangeListeners() { - return (ChangeListener[])listenerList.getListeners( - ChangeListener.class); + return listenerList.getListeners(ChangeListener.class); } diff --git a/src/share/classes/javax/swing/DefaultButtonModel.java b/src/share/classes/javax/swing/DefaultButtonModel.java index b9c943844..8ad0acddb 100644 --- a/src/share/classes/javax/swing/DefaultButtonModel.java +++ b/src/share/classes/javax/swing/DefaultButtonModel.java @@ -326,8 +326,7 @@ public class DefaultButtonModel implements ButtonModel, Serializable { * @since 1.4 */ public ChangeListener[] getChangeListeners() { - return (ChangeListener[])listenerList.getListeners( - ChangeListener.class); + return listenerList.getListeners(ChangeListener.class); } /** @@ -380,8 +379,7 @@ public class DefaultButtonModel implements ButtonModel, Serializable { * @since 1.4 */ public ActionListener[] getActionListeners() { - return (ActionListener[])listenerList.getListeners( - ActionListener.class); + return listenerList.getListeners(ActionListener.class); } /** @@ -434,7 +432,7 @@ public class DefaultButtonModel implements ButtonModel, Serializable { * @since 1.4 */ public ItemListener[] getItemListeners() { - return (ItemListener[])listenerList.getListeners(ItemListener.class); + return listenerList.getListeners(ItemListener.class); } /** diff --git a/src/share/classes/javax/swing/DefaultFocusManager.java b/src/share/classes/javax/swing/DefaultFocusManager.java index 417b625f9..021867a5c 100644 --- a/src/share/classes/javax/swing/DefaultFocusManager.java +++ b/src/share/classes/javax/swing/DefaultFocusManager.java @@ -156,18 +156,17 @@ final class LegacyLayoutFocusTraversalPolicy } } -final class CompareTabOrderComparator implements Comparator { +final class CompareTabOrderComparator implements Comparator { private final DefaultFocusManager defaultFocusManager; CompareTabOrderComparator(DefaultFocusManager defaultFocusManager) { this.defaultFocusManager = defaultFocusManager; } - public int compare(Object o1, Object o2) { + public int compare(Component o1, Component o2) { if (o1 == o2) { return 0; } - return (defaultFocusManager.compareTabOrder((Component)o1, - (Component)o2)) ? -1 : 1; + return (defaultFocusManager.compareTabOrder(o1, o2)) ? -1 : 1; } } diff --git a/src/share/classes/javax/swing/DefaultListSelectionModel.java b/src/share/classes/javax/swing/DefaultListSelectionModel.java index 329fa38b4..2ba0e7b9d 100644 --- a/src/share/classes/javax/swing/DefaultListSelectionModel.java +++ b/src/share/classes/javax/swing/DefaultListSelectionModel.java @@ -133,8 +133,7 @@ public class DefaultListSelectionModel implements ListSelectionModel, Cloneable, * @since 1.4 */ public ListSelectionListener[] getListSelectionListeners() { - return (ListSelectionListener[])listenerList.getListeners( - ListSelectionListener.class); + return listenerList.getListeners(ListSelectionListener.class); } /** diff --git a/src/share/classes/javax/swing/DefaultSingleSelectionModel.java b/src/share/classes/javax/swing/DefaultSingleSelectionModel.java index d0270f1c7..c03b51f16 100644 --- a/src/share/classes/javax/swing/DefaultSingleSelectionModel.java +++ b/src/share/classes/javax/swing/DefaultSingleSelectionModel.java @@ -110,8 +110,7 @@ Serializable { * @since 1.4 */ public ChangeListener[] getChangeListeners() { - return (ChangeListener[])listenerList.getListeners( - ChangeListener.class); + return listenerList.getListeners(ChangeListener.class); } /** diff --git a/src/share/classes/javax/swing/GroupLayout.java b/src/share/classes/javax/swing/GroupLayout.java index fd1fd0c65..fb59d0c85 100644 --- a/src/share/classes/javax/swing/GroupLayout.java +++ b/src/share/classes/javax/swing/GroupLayout.java @@ -1119,7 +1119,7 @@ public class GroupLayout implements LayoutManager2 { * creating one if necessary. */ private ComponentInfo getComponentInfo(Component component) { - ComponentInfo info = (ComponentInfo)componentInfos.get(component); + ComponentInfo info = componentInfos.get(component); if (info == null) { info = new ComponentInfo(component); componentInfos.put(component, info); @@ -1698,7 +1698,7 @@ public class GroupLayout implements LayoutManager2 { for (int counter = springs.size() - 1; counter >= 0; counter--) { Spring spring = springs.get(counter); if (spring instanceof AutoPreferredGapSpring) { - ((AutoPreferredGapSpring)spring).unset(); + spring.unset(); } else if (spring instanceof Group) { ((Group)spring).unsetAutopadding(); } diff --git a/src/share/classes/javax/swing/InputMap.java b/src/share/classes/javax/swing/InputMap.java index cba5a5d8b..0992b1188 100644 --- a/src/share/classes/javax/swing/InputMap.java +++ b/src/share/classes/javax/swing/InputMap.java @@ -200,7 +200,7 @@ public class InputMap implements Serializable { return pKeys; } - HashMap keyMap = new HashMap(); + HashMap keyMap = new HashMap(); int counter; for (counter = keys.length - 1; counter >= 0; counter--) { @@ -212,7 +212,7 @@ public class InputMap implements Serializable { KeyStroke[] allKeys = new KeyStroke[keyMap.size()]; - return (KeyStroke[])keyMap.keySet().toArray(allKeys); + return keyMap.keySet().toArray(allKeys); } private void writeObject(ObjectOutputStream s) throws IOException { diff --git a/src/share/classes/javax/swing/JComboBox.java b/src/share/classes/javax/swing/JComboBox.java index 2230147f4..42ef64669 100644 --- a/src/share/classes/javax/swing/JComboBox.java +++ b/src/share/classes/javax/swing/JComboBox.java @@ -859,7 +859,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * @since 1.4 */ public ItemListener[] getItemListeners() { - return (ItemListener[])listenerList.getListeners(ItemListener.class); + return listenerList.getListeners(ItemListener.class); } /** @@ -897,8 +897,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * @since 1.4 */ public ActionListener[] getActionListeners() { - return (ActionListener[])listenerList.getListeners( - ActionListener.class); + return listenerList.getListeners(ActionListener.class); } /** @@ -937,8 +936,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { * @since 1.4 */ public PopupMenuListener[] getPopupMenuListeners() { - return (PopupMenuListener[])listenerList.getListeners( - PopupMenuListener.class); + return listenerList.getListeners(PopupMenuListener.class); } /** @@ -1668,7 +1666,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { if (editor != null) { Component comp = editor.getEditorComponent(); if (comp instanceof Accessible) { - AccessibleContext ac = ((Accessible)comp).getAccessibleContext(); + AccessibleContext ac = comp.getAccessibleContext(); if (ac != null) { // may be null ac.setAccessibleName(getAccessibleName()); ac.setAccessibleDescription(getAccessibleDescription()); @@ -1741,7 +1739,7 @@ implements ItemSelectable,ListDataListener,ActionListener, Accessible { // Fire a FOCUSED lost PropertyChangeEvent for the // previously selected list item. - PropertyChangeEvent pce = null; + PropertyChangeEvent pce; if (previousSelectedAccessible != null) { pce = new PropertyChangeEvent(previousSelectedAccessible, diff --git a/src/share/classes/javax/swing/JComponent.java b/src/share/classes/javax/swing/JComponent.java index 7d3881a1c..831dc717a 100644 --- a/src/share/classes/javax/swing/JComponent.java +++ b/src/share/classes/javax/swing/JComponent.java @@ -192,7 +192,8 @@ public abstract class JComponent extends Container implements Serializable, /** * @see #readObject */ - private static final Hashtable readObjectCallbacks = new Hashtable(1); + private static final Hashtable readObjectCallbacks = + new Hashtable(1); /** * Keys to use for forward focus traversal when the JComponent is @@ -356,7 +357,7 @@ public abstract class JComponent extends Container implements Serializable, /** * Temporary rectangles. */ - private static java.util.List tempRectangles = new java.util.ArrayList(11); + private static java.util.List tempRectangles = new java.util.ArrayList(11); /** Used for WHEN_FOCUSED bindings. */ private InputMap focusInputMap; @@ -451,7 +452,7 @@ public abstract class JComponent extends Container implements Serializable, Rectangle rect; int size = tempRectangles.size(); if (size > 0) { - rect = (Rectangle)tempRectangles.remove(size - 1); + rect = tempRectangles.remove(size - 1); } else { rect = new Rectangle(0, 0, 0, 0); @@ -806,7 +807,7 @@ public abstract class JComponent extends Container implements Serializable, // its index. if (paintingChild != null && (paintingChild instanceof JComponent) && - ((JComponent)paintingChild).isOpaque()) { + paintingChild.isOpaque()) { for (; i >= 0; i--) { if (getComponent(i) == paintingChild){ break; @@ -875,7 +876,7 @@ public abstract class JComponent extends Container implements Serializable, shouldSetFlagBack = true; } if(!printing) { - ((JComponent)comp).paint(cg); + comp.paint(cg); } else { if (!getFlag(IS_PRINTING_ALL)) { @@ -1098,7 +1099,7 @@ public abstract class JComponent extends Container implements Serializable, } private void adjustPaintFlags() { - JComponent jparent = null; + JComponent jparent; Container parent; for(parent = getParent() ; parent != null ; parent = parent.getParent()) { @@ -2096,7 +2097,7 @@ public abstract class JComponent extends Container implements Serializable, private void registerWithKeyboardManager(boolean onlyIfNew) { InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW, false); KeyStroke[] strokes; - Hashtable registered = (Hashtable)getClientProperty + Hashtable registered = (Hashtable)getClientProperty (WHEN_IN_FOCUSED_WINDOW_BINDINGS); if (inputMap != null) { @@ -2120,10 +2121,10 @@ public abstract class JComponent extends Container implements Serializable, } // Remove any old ones. if (registered != null && registered.size() > 0) { - Enumeration keys = registered.keys(); + Enumeration keys = registered.keys(); while (keys.hasMoreElements()) { - KeyStroke ks = (KeyStroke)keys.nextElement(); + KeyStroke ks = keys.nextElement(); unregisterWithKeyboardManager(ks); } registered.clear(); @@ -2131,7 +2132,7 @@ public abstract class JComponent extends Container implements Serializable, // Updated the registered Hashtable. if (strokes != null && strokes.length > 0) { if (registered == null) { - registered = new Hashtable(strokes.length); + registered = new Hashtable(strokes.length); putClientProperty(WHEN_IN_FOCUSED_WINDOW_BINDINGS, registered); } for (int counter = strokes.length - 1; counter >= 0; counter--) { @@ -2174,7 +2175,7 @@ public abstract class JComponent extends Container implements Serializable, InputMap km = getInputMap(WHEN_IN_FOCUSED_WINDOW, false); while (km != inputMap && km != null) { - km = (ComponentInputMap)km.getParent(); + km = km.getParent(); } if (km != null) { registerWithKeyboardManager(false); @@ -3673,7 +3674,7 @@ public abstract class JComponent extends Container implements Serializable, if (c != null && c instanceof Accessible) { AccessibleJComponent.this.firePropertyChange( AccessibleContext.ACCESSIBLE_CHILD_PROPERTY, - null, ((Accessible) c).getAccessibleContext()); + null, c.getAccessibleContext()); } } public void componentRemoved(ContainerEvent e) { @@ -3681,7 +3682,7 @@ public abstract class JComponent extends Container implements Serializable, if (c != null && c instanceof Accessible) { AccessibleJComponent.this.firePropertyChange( AccessibleContext.ACCESSIBLE_CHILD_PROPERTY, - ((Accessible) c).getAccessibleContext(), null); + c.getAccessibleContext(), null); } } } @@ -4377,7 +4378,7 @@ public abstract class JComponent extends Container implements Serializable, // System.out.println("A) checking opaque: " + ((JComponent)child).isOpaque() + " " + child); // System.out.print("B) "); // Thread.dumpStack(); - return ((JComponent)child).isOpaque(); + return child.isOpaque(); } else { /** Sometimes a heavy weight can have a bound larger than its peer size * so we should always draw under heavy weights @@ -4693,7 +4694,7 @@ public abstract class JComponent extends Container implements Serializable, result = (T[])getPropertyChangeListeners(); } else { - result = (T[])listenerList.getListeners(listenerType); + result = listenerList.getListeners(listenerType); } if (result.length == 0) { @@ -4904,7 +4905,7 @@ public abstract class JComponent extends Container implements Serializable, if(!isShowing()) { return; } - while(!((JComponent)c).isOpaque()) { + while(!c.isOpaque()) { parent = c.getParent(); if(parent != null) { x += c.getX(); @@ -5198,7 +5199,7 @@ public abstract class JComponent extends Container implements Serializable, Rectangle siblingRect; boolean opaque; if (sibling instanceof JComponent) { - opaque = ((JComponent)sibling).isOpaque(); + opaque = sibling.isOpaque(); if (!opaque) { if (retValue == PARTIALLY_OBSCURED) { continue; @@ -5345,7 +5346,7 @@ public abstract class JComponent extends Container implements Serializable, */ private class ReadObjectCallback implements ObjectInputValidation { - private final Vector roots = new Vector(1); + private final Vector roots = new Vector(1); private final ObjectInputStream inputStream; ReadObjectCallback(ObjectInputStream s) throws Exception { @@ -5361,8 +5362,7 @@ public abstract class JComponent extends Container implements Serializable, */ public void validateObject() throws InvalidObjectException { try { - for(int i = 0; i < roots.size(); i++) { - JComponent root = (JComponent)(roots.elementAt(i)); + for (JComponent root : roots) { SwingUtilities.updateComponentTreeUI(root); } } @@ -5382,8 +5382,7 @@ public abstract class JComponent extends Container implements Serializable, /* If the Component c is a descendant of one of the * existing roots (or it IS an existing root), we're done. */ - for(int i = 0; i < roots.size(); i++) { - JComponent root = (JComponent)roots.elementAt(i); + for (JComponent root : roots) { for(Component p = c; p != null; p = p.getParent()) { if (p == root) { return; @@ -5396,7 +5395,7 @@ public abstract class JComponent extends Container implements Serializable, * to the roots vector. */ for(int i = 0; i < roots.size(); i++) { - JComponent root = (JComponent)roots.elementAt(i); + JComponent root = roots.elementAt(i); for(Component p = root.getParent(); p != null; p = p.getParent()) { if (p == c) { roots.removeElementAt(i--); // !! @@ -5428,7 +5427,7 @@ public abstract class JComponent extends Container implements Serializable, * in the readObjectCallbacks table. Note that the ReadObjectCallback * constructor takes care of calling s.registerValidation(). */ - ReadObjectCallback cb = (ReadObjectCallback)(readObjectCallbacks.get(s)); + ReadObjectCallback cb = readObjectCallbacks.get(s); if (cb == null) { try { readObjectCallbacks.put(s, cb = new ReadObjectCallback(s)); diff --git a/src/share/classes/javax/swing/JDesktopPane.java b/src/share/classes/javax/swing/JDesktopPane.java index 2c0ab3120..19cfefd65 100644 --- a/src/share/classes/javax/swing/JDesktopPane.java +++ b/src/share/classes/javax/swing/JDesktopPane.java @@ -133,8 +133,8 @@ public class JDesktopPane extends JLayeredPane implements Accessible public Component getDefaultComponent(Container c) { JInternalFrame jifArray[] = getAllFrames(); Component comp = null; - for (int i = 0; i < jifArray.length; i++) { - comp = jifArray[i].getFocusTraversalPolicy().getDefaultComponent(jifArray[i]); + for (JInternalFrame jif : jifArray) { + comp = jif.getFocusTraversalPolicy().getDefaultComponent(jif); if (comp != null) { break; } @@ -262,16 +262,15 @@ public class JDesktopPane extends JLayeredPane implements Accessible public JInternalFrame[] getAllFrames() { int i, count; JInternalFrame[] results; - Vector vResults = new Vector(10); - Object next, tmp; + Vector vResults = new Vector(10); count = getComponentCount(); for(i = 0; i < count; i++) { - next = getComponent(i); + Component next = getComponent(i); if(next instanceof JInternalFrame) - vResults.addElement(next); + vResults.addElement((JInternalFrame) next); else if(next instanceof JInternalFrame.JDesktopIcon) { - tmp = ((JInternalFrame.JDesktopIcon)next).getInternalFrame(); + JInternalFrame tmp = ((JInternalFrame.JDesktopIcon)next).getInternalFrame(); if(tmp != null) vResults.addElement(tmp); } @@ -324,18 +323,17 @@ public class JDesktopPane extends JLayeredPane implements Accessible public JInternalFrame[] getAllFramesInLayer(int layer) { int i, count; JInternalFrame[] results; - Vector vResults = new Vector(10); - Object next, tmp; + Vector vResults = new Vector(10); count = getComponentCount(); for(i = 0; i < count; i++) { - next = getComponent(i); + Component next = getComponent(i); if(next instanceof JInternalFrame) { if(((JInternalFrame)next).getLayer() == layer) - vResults.addElement(next); + vResults.addElement((JInternalFrame) next); } else if(next instanceof JInternalFrame.JDesktopIcon) { - tmp = ((JInternalFrame.JDesktopIcon)next).getInternalFrame(); - if(tmp != null && ((JInternalFrame)tmp).getLayer() == layer) + JInternalFrame tmp = ((JInternalFrame.JDesktopIcon)next).getInternalFrame(); + if(tmp != null && tmp.getLayer() == layer) vResults.addElement(tmp); } } diff --git a/src/share/classes/javax/swing/JDialog.java b/src/share/classes/javax/swing/JDialog.java index ac2a24ecc..796409490 100644 --- a/src/share/classes/javax/swing/JDialog.java +++ b/src/share/classes/javax/swing/JDialog.java @@ -277,7 +277,7 @@ public class JDialog extends Dialog implements WindowConstants, title, modal); if (owner == null) { WindowListener ownerShutdownListener = - (WindowListener)SwingUtilities.getSharedOwnerFrameShutdownListener(); + SwingUtilities.getSharedOwnerFrameShutdownListener(); addWindowListener(ownerShutdownListener); } dialogInit(); @@ -329,7 +329,7 @@ public class JDialog extends Dialog implements WindowConstants, title, modal, gc); if (owner == null) { WindowListener ownerShutdownListener = - (WindowListener)SwingUtilities.getSharedOwnerFrameShutdownListener(); + SwingUtilities.getSharedOwnerFrameShutdownListener(); addWindowListener(ownerShutdownListener); } dialogInit(); diff --git a/src/share/classes/javax/swing/JEditorPane.java b/src/share/classes/javax/swing/JEditorPane.java index 4d70cacb7..21528752f 100644 --- a/src/share/classes/javax/swing/JEditorPane.java +++ b/src/share/classes/javax/swing/JEditorPane.java @@ -319,8 +319,7 @@ public class JEditorPane extends JTextComponent { * @since 1.4 */ public synchronized HyperlinkListener[] getHyperlinkListeners() { - return (HyperlinkListener[])listenerList.getListeners( - HyperlinkListener.class); + return listenerList.getListeners(javax.swing.event.HyperlinkListener.class); } /** @@ -492,8 +491,8 @@ public class JEditorPane extends JTextComponent { if (pageProperties != null) { // transfer properties discovered in stream to the // document property collection. - for (Enumeration e = pageProperties.keys(); e.hasMoreElements() ;) { - Object key = e.nextElement(); + for (Enumeration e = pageProperties.keys(); e.hasMoreElements() ;) { + String key = e.nextElement(); doc.putProperty(key, pageProperties.get(key)); } pageProperties.clear(); @@ -775,7 +774,7 @@ public class JEditorPane extends JTextComponent { */ private void handleConnectionProperties(URLConnection conn) { if (pageProperties == null) { - pageProperties = new Hashtable(); + pageProperties = new Hashtable(); } String type = conn.getContentType(); if (type != null) { @@ -989,7 +988,7 @@ public class JEditorPane extends JTextComponent { * of the content type in the http header information. */ private void setCharsetFromContentTypeParameters(String paramlist) { - String charset = null; + String charset; try { // paramlist is handed to us with a leading ';', strip it. int semi = paramlist.indexOf(';'); @@ -1080,9 +1079,9 @@ public class JEditorPane extends JTextComponent { */ public EditorKit getEditorKitForContentType(String type) { if (typeHandlers == null) { - typeHandlers = new Hashtable(3); + typeHandlers = new Hashtable(3); } - EditorKit k = (EditorKit) typeHandlers.get(type); + EditorKit k = typeHandlers.get(type); if (k == null) { k = createEditorKitForContentType(type); if (k != null) { @@ -1106,7 +1105,7 @@ public class JEditorPane extends JTextComponent { */ public void setEditorKitForContentType(String type, EditorKit k) { if (typeHandlers == null) { - typeHandlers = new Hashtable(3); + typeHandlers = new Hashtable(3); } typeHandlers.put(type, k); } @@ -1176,13 +1175,12 @@ public class JEditorPane extends JTextComponent { * registered for the given type */ public static EditorKit createEditorKitForContentType(String type) { - EditorKit k = null; - Hashtable kitRegistry = getKitRegisty(); - k = (EditorKit) kitRegistry.get(type); + Hashtable kitRegistry = getKitRegisty(); + EditorKit k = kitRegistry.get(type); if (k == null) { // try to dynamically load the support - String classname = (String) getKitTypeRegistry().get(type); - ClassLoader loader = (ClassLoader) getKitLoaderRegistry().get(type); + String classname = getKitTypeRegistry().get(type); + ClassLoader loader = getKitLoaderRegistry().get(type); try { Class c; if (loader != null) { @@ -1252,20 +1250,20 @@ public class JEditorPane extends JTextComponent { * @since 1.3 */ public static String getEditorKitClassNameForContentType(String type) { - return (String)getKitTypeRegistry().get(type); + return getKitTypeRegistry().get(type); } - private static Hashtable getKitTypeRegistry() { + private static Hashtable getKitTypeRegistry() { loadDefaultKitsIfNecessary(); return (Hashtable)SwingUtilities.appContextGet(kitTypeRegistryKey); } - private static Hashtable getKitLoaderRegistry() { + private static Hashtable getKitLoaderRegistry() { loadDefaultKitsIfNecessary(); return (Hashtable)SwingUtilities.appContextGet(kitLoaderRegistryKey); } - private static Hashtable getKitRegisty() { + private static Hashtable getKitRegisty() { Hashtable ht = (Hashtable)SwingUtilities.appContextGet(kitRegistryKey); if (ht == null) { ht = new Hashtable(3); @@ -1512,7 +1510,7 @@ public class JEditorPane extends JTextComponent { private EditorKit kit; private boolean isUserSetEditorKit; - private Hashtable pageProperties; + private Hashtable pageProperties; /** Should be kept in sync with javax.swing.text.html.FormView counterpart. */ final static String PostDataProperty = "javax.swing.JEditorPane.postdata"; @@ -1520,7 +1518,7 @@ public class JEditorPane extends JTextComponent { /** * Table of registered type handlers for this editor. */ - private Hashtable typeHandlers; + private Hashtable typeHandlers; /* * Private AppContext keys for this class's static variables. @@ -1913,11 +1911,11 @@ public class JEditorPane extends JTextComponent { } } - private class LinkVector extends Vector { + private class LinkVector extends Vector { public int baseElementIndex(Element e) { HTMLLink l; for (int i = 0; i < elementCount; i++) { - l = (HTMLLink) elementAt(i); + l = elementAt(i); if (l.element == e) { return i; } @@ -2029,7 +2027,7 @@ public class JEditorPane extends JTextComponent { buildLinkTable(); } if (linkIndex >= 0 && linkIndex < hyperlinks.size()) { - return (AccessibleHyperlink) hyperlinks.elementAt(linkIndex); + return hyperlinks.elementAt(linkIndex); } else { return null; } diff --git a/src/share/classes/javax/swing/JFileChooser.java b/src/share/classes/javax/swing/JFileChooser.java index 4a213df62..82c85fe9f 100644 --- a/src/share/classes/javax/swing/JFileChooser.java +++ b/src/share/classes/javax/swing/JFileChooser.java @@ -248,7 +248,7 @@ public class JFileChooser extends JComponent implements Accessible { private String approveButtonToolTipText = null; private int approveButtonMnemonic = 0; - private Vector filters = new Vector(5); + private Vector filters = new Vector(5); private JDialog dialog = null; private int dialogType = OPEN_DIALOG; private int returnValue = ERROR_OPTION; @@ -503,7 +503,7 @@ public class JFileChooser extends JComponent implements Accessible { if(selectedFiles == null) { return new File[0]; } else { - return (File[]) selectedFiles.clone(); + return selectedFiles.clone(); } } @@ -1415,17 +1415,17 @@ public class JFileChooser extends JComponent implements Accessible { fileFilter = filter; if (filter != null) { if (isMultiSelectionEnabled() && selectedFiles != null && selectedFiles.length > 0) { - Vector fList = new Vector(); + Vector fList = new Vector(); boolean failed = false; - for (int i = 0; i < selectedFiles.length; i++) { - if (filter.accept(selectedFiles[i])) { - fList.add(selectedFiles[i]); + for (File file : selectedFiles) { + if (filter.accept(file)) { + fList.add(file); } else { failed = true; } } if (failed) { - setSelectedFiles((fList.size() == 0) ? null : (File[])fList.toArray(new File[fList.size()])); + setSelectedFiles((fList.size() == 0) ? null : fList.toArray(new File[fList.size()])); } } else if (selectedFile != null && !filter.accept(selectedFile)) { setSelectedFile(null); @@ -1702,8 +1702,7 @@ public class JFileChooser extends JComponent implements Accessible { * @since 1.4 */ public ActionListener[] getActionListeners() { - return (ActionListener[])listenerList.getListeners( - ActionListener.class); + return listenerList.getListeners(ActionListener.class); } /** @@ -1744,7 +1743,7 @@ public class JFileChooser extends JComponent implements Accessible { WeakReference jfcRef; public WeakPCL(JFileChooser jfc) { - jfcRef = new WeakReference(jfc); + jfcRef = new WeakReference(jfc); } public void propertyChange(PropertyChangeEvent ev) { assert ev.getPropertyName().equals(SHOW_HIDDEN_PROP); diff --git a/src/share/classes/javax/swing/JInternalFrame.java b/src/share/classes/javax/swing/JInternalFrame.java index c3e3cad1c..837ffe49e 100644 --- a/src/share/classes/javax/swing/JInternalFrame.java +++ b/src/share/classes/javax/swing/JInternalFrame.java @@ -421,8 +421,8 @@ public class JInternalFrame extends JComponent implements invalidate(); Component[] children = getComponents(); if (children != null) { - for(int i = 0; i < children.length; i++) { - SwingUtilities.updateComponentTreeUI(children[i]); + for (Component child : children) { + SwingUtilities.updateComponentTreeUI(child); } } } @@ -1535,8 +1535,7 @@ public class JInternalFrame extends JComponent implements * @see #addInternalFrameListener */ public InternalFrameListener[] getInternalFrameListeners() { - return (InternalFrameListener[])listenerList.getListeners( - InternalFrameListener.class); + return listenerList.getListeners(InternalFrameListener.class); } // remind: name ok? all one method ok? need to be synchronized? @@ -2258,8 +2257,8 @@ public class JInternalFrame extends JComponent implements invalidate(); Component[] children = getComponents(); if (children != null) { - for(int i = 0; i < children.length; i++) { - SwingUtilities.updateComponentTreeUI(children[i]); + for (Component child : children) { + SwingUtilities.updateComponentTreeUI(child); } } } diff --git a/src/share/classes/javax/swing/JLayeredPane.java b/src/share/classes/javax/swing/JLayeredPane.java index b3cedb2d3..e971fe1d8 100644 --- a/src/share/classes/javax/swing/JLayeredPane.java +++ b/src/share/classes/javax/swing/JLayeredPane.java @@ -191,7 +191,7 @@ public class JLayeredPane extends JComponent implements Accessible { private void validateOptimizedDrawing() { boolean layeredComponentFound = false; synchronized(getTreeLock()) { - Integer layer = null; + Integer layer; for (Component c : getComponents()) { layer = null; @@ -213,7 +213,7 @@ public class JLayeredPane extends JComponent implements Accessible { } protected void addImpl(Component comp, Object constraints, int index) { - int layer = DEFAULT_LAYER.intValue(); + int layer; int pos; if(constraints instanceof Integer) { @@ -364,7 +364,7 @@ public class JLayeredPane extends JComponent implements Accessible { if(c instanceof JComponent) ((JComponent)c).putClientProperty(LAYER_PROPERTY, layerObj); else - getComponentToLayer().put((Component)c, layerObj); + getComponentToLayer().put(c, layerObj); if(c.getParent() == null || c.getParent() != this) { repaint(c.getBounds()); @@ -388,7 +388,7 @@ public class JLayeredPane extends JComponent implements Accessible { if(c instanceof JComponent) i = (Integer)((JComponent)c).getClientProperty(LAYER_PROPERTY); else - i = (Integer)getComponentToLayer().get((Component)c); + i = getComponentToLayer().get(c); if(i == null) return DEFAULT_LAYER.intValue(); @@ -465,9 +465,9 @@ public class JLayeredPane extends JComponent implements Accessible { * @see #getComponentCountInLayer */ public int getPosition(Component c) { - int i, count, startLayer, curLayer, startLocation, pos = 0; + int i, startLayer, curLayer, startLocation, pos = 0; - count = getComponentCount(); + getComponentCount(); startLocation = getIndexOf(c); if(startLocation == -1) diff --git a/src/share/classes/javax/swing/JList.java b/src/share/classes/javax/swing/JList.java index a83a4c94e..772c75540 100644 --- a/src/share/classes/javax/swing/JList.java +++ b/src/share/classes/javax/swing/JList.java @@ -1848,8 +1848,7 @@ public class JList extends JComponent implements Scrollable, Accessible * @since 1.4 */ public ListSelectionListener[] getListSelectionListeners() { - return (ListSelectionListener[])listenerList.getListeners( - ListSelectionListener.class); + return listenerList.getListeners(ListSelectionListener.class); } @@ -2220,9 +2219,9 @@ public class JList extends JComponent implements Scrollable, Accessible ListSelectionModel sm = getSelectionModel(); sm.clearSelection(); int size = getModel().getSize(); - for(int i = 0; i < indices.length; i++) { - if (indices[i] < size) { - sm.addSelectionInterval(indices[i], indices[i]); + for (int i : indices) { + if (i < size) { + sm.addSelectionInterval(i, i); } } } @@ -2724,7 +2723,7 @@ public class JList extends JComponent implements Scrollable, Accessible return true; } if (getParent() instanceof JViewport) { - return (((JViewport)getParent()).getWidth() > getPreferredSize().width); + return (getParent().getWidth() > getPreferredSize().width); } return false; } @@ -2749,7 +2748,7 @@ public class JList extends JComponent implements Scrollable, Accessible return true; } if (getParent() instanceof JViewport) { - return (((JViewport)getParent()).getHeight() > getPreferredSize().height); + return (getParent().getHeight() > getPreferredSize().height); } return false; } @@ -3161,7 +3160,7 @@ public class JList extends JComponent implements Scrollable, Accessible private AccessibleContext getCurrentAccessibleContext() { Component c = getComponentAtIndex(indexInParent); if (c instanceof Accessible) { - return ((Accessible) c).getAccessibleContext(); + return c.getAccessibleContext(); } else { return null; } diff --git a/src/share/classes/javax/swing/JMenu.java b/src/share/classes/javax/swing/JMenu.java index ed90cb73e..cf069116c 100644 --- a/src/share/classes/javax/swing/JMenu.java +++ b/src/share/classes/javax/swing/JMenu.java @@ -371,8 +371,8 @@ public class JMenu extends JMenuItem implements Accessible,MenuElement * @since 1.3 */ protected Point getPopupMenuOrigin() { - int x = 0; - int y = 0; + int x; + int y; JPopupMenu pm = getPopupMenu(); // Figure out the sizes needed to caclulate the menu position Dimension s = getSize(); @@ -900,10 +900,8 @@ public class JMenu extends JMenuItem implements Accessible,MenuElement * on another menu */ public boolean isTopLevelMenu() { - if (getParent() instanceof JMenuBar) - return true; + return getParent() instanceof JMenuBar; - return false; } /** @@ -1015,7 +1013,7 @@ public class JMenu extends JMenuItem implements Accessible,MenuElement * @since 1.4 */ public MenuListener[] getMenuListeners() { - return (MenuListener[])listenerList.getListeners(MenuListener.class); + return listenerList.getListeners(MenuListener.class); } /** @@ -1305,7 +1303,7 @@ public class JMenu extends JMenuItem implements Accessible,MenuElement * @return the array of menu items */ private MenuElement[] buildMenuElementArray(JMenu leaf) { - Vector elements = new Vector(); + Vector elements = new Vector(); Component current = leaf.getPopupMenu(); JPopupMenu pop; JMenu menu; @@ -1409,8 +1407,8 @@ public class JMenu extends JMenuItem implements Accessible,MenuElement public int getAccessibleChildrenCount() { Component[] children = getMenuComponents(); int count = 0; - for (int j = 0; j < children.length; j++) { - if (children[j] instanceof Accessible) { + for (Component child : children) { + if (child instanceof Accessible) { count++; } } @@ -1426,18 +1424,18 @@ public class JMenu extends JMenuItem implements Accessible,MenuElement public Accessible getAccessibleChild(int i) { Component[] children = getMenuComponents(); int count = 0; - for (int j = 0; j < children.length; j++) { - if (children[j] instanceof Accessible) { + for (Component child : children) { + if (child instanceof Accessible) { if (count == i) { - if (children[j] instanceof JComponent) { + if (child instanceof JComponent) { // FIXME: [[[WDW - probably should set this when // the component is added to the menu. I tried // to do this in most cases, but the separators // added by addSeparator are hard to get to.]]] - AccessibleContext ac = ((Accessible) children[j]).getAccessibleContext(); + AccessibleContext ac = child.getAccessibleContext(); ac.setAccessibleParent(JMenu.this); } - return (Accessible) children[j]; + return (Accessible) child; } else { count++; } @@ -1581,7 +1579,7 @@ public class JMenu extends JMenuItem implements Accessible,MenuElement } JMenuItem mi = getItem(i); if (mi != null && mi instanceof JMenu) { - if (((JMenu) mi).isSelected()) { + if (mi.isSelected()) { MenuElement old[] = MenuSelectionManager.defaultManager().getSelectedPath(); MenuElement me[] = new MenuElement[old.length-2]; diff --git a/src/share/classes/javax/swing/JMenuBar.java b/src/share/classes/javax/swing/JMenuBar.java index d6f04fbb4..724eba6b8 100644 --- a/src/share/classes/javax/swing/JMenuBar.java +++ b/src/share/classes/javax/swing/JMenuBar.java @@ -414,7 +414,7 @@ public class JMenuBar extends JComponent implements Accessible,MenuElement */ public MenuElement[] getSubElements() { MenuElement result[]; - Vector tmp = new Vector(); + Vector tmp = new Vector(); int c = getComponentCount(); int i; Component m; @@ -422,12 +422,12 @@ public class JMenuBar extends JComponent implements Accessible,MenuElement for(i=0 ; i < c ; i++) { m = getComponent(i); if(m instanceof MenuElement) - tmp.addElement(m); + tmp.addElement((MenuElement) m); } result = new MenuElement[tmp.size()]; for(i=0,c=tmp.size() ; i < c ; i++) - result[i] = (MenuElement) tmp.elementAt(i); + result[i] = tmp.elementAt(i); return result; } @@ -664,9 +664,9 @@ public class JMenuBar extends JComponent implements Accessible,MenuElement boolean retValue = super.processKeyBinding(ks, e, condition, pressed); if (!retValue) { MenuElement[] subElements = getSubElements(); - for (int i=0; i values = new Vector(); s.defaultWriteObject(); // Save the icon, if its Serializable. @@ -2342,7 +2339,7 @@ public class JOptionPane extends JComponent implements Accessible } // Save the treeModel, if its Serializable. if(options != null) { - Vector serOptions = new Vector(); + Vector serOptions = new Vector(); for(int counter = 0, maxCounter = options.length; counter < maxCounter; counter++) @@ -2510,16 +2507,16 @@ public class JOptionPane extends JComponent implements Accessible /** * Retrieves a method from the provided class and makes it accessible. */ - private static class ModalPrivilegedAction implements PrivilegedAction { - private Class clazz; + private static class ModalPrivilegedAction implements PrivilegedAction { + private Class clazz; private String methodName; - public ModalPrivilegedAction(Class clazz, String methodName) { + public ModalPrivilegedAction(Class clazz, String methodName) { this.clazz = clazz; this.methodName = methodName; } - public Object run() { + public Method run() { Method method = null; try { method = clazz.getDeclaredMethod(methodName, (Class[])null); diff --git a/src/share/classes/javax/swing/JPopupMenu.java b/src/share/classes/javax/swing/JPopupMenu.java index 83aec9a17..8f90a02a2 100644 --- a/src/share/classes/javax/swing/JPopupMenu.java +++ b/src/share/classes/javax/swing/JPopupMenu.java @@ -584,7 +584,7 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { int nitems = getComponentCount(); // PENDING(ges): Why not use an array? - Vector tempItems = new Vector(); + Vector tempItems = new Vector(); /* Remove the item at index, nitems-index times storing them in a temporary vector in the @@ -600,8 +600,8 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { /* Add the removed items back to the menu, they are already in the correct order in the temp vector. */ - for (int i = 0; i < tempItems.size() ; i++) { - add((Component)tempItems.elementAt(i)); + for (Component tempItem : tempItems) { + add(tempItem); } } @@ -632,8 +632,7 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { * @since 1.4 */ public PopupMenuListener[] getPopupMenuListeners() { - return (PopupMenuListener[])listenerList.getListeners( - PopupMenuListener.class); + return listenerList.getListeners(PopupMenuListener.class); } /** @@ -665,8 +664,7 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { * @since 1.5 */ public MenuKeyListener[] getMenuKeyListeners() { - return (MenuKeyListener[])listenerList.getListeners( - MenuKeyListener.class); + return listenerList.getListeners(MenuKeyListener.class); } /** @@ -781,7 +779,7 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { // set selection path before popping up! if (isPopupMenu()) { MenuElement me[] = new MenuElement[1]; - me[0] = (MenuElement) this; + me[0] = this; MenuSelectionManager.defaultManager().setSelectedPath(me); } } @@ -848,10 +846,7 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { * being displayed). */ public boolean isVisible() { - if(popup != null) - return true; - else - return false; + return popup != null; } /** @@ -1311,7 +1306,7 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { // Serialization support. //////////// private void writeObject(ObjectOutputStream s) throws IOException { - Vector values = new Vector(); + Vector values = new Vector(); s.defaultWriteObject(); // Save the invoker, if its Serializable. @@ -1494,7 +1489,7 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { */ public MenuElement[] getSubElements() { MenuElement result[]; - Vector tmp = new Vector(); + Vector tmp = new Vector(); int c = getComponentCount(); int i; Component m; @@ -1502,12 +1497,12 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { for(i=0 ; i < c ; i++) { m = getComponent(i); if(m instanceof MenuElement) - tmp.addElement(m); + tmp.addElement((MenuElement) m); } result = new MenuElement[tmp.size()]; for(i=0,c=tmp.size() ; i < c ; i++) - result[i] = (MenuElement) tmp.elementAt(i); + result[i] = tmp.elementAt(i); return result; } diff --git a/src/share/classes/javax/swing/JProgressBar.java b/src/share/classes/javax/swing/JProgressBar.java index b1d27c8cd..9ba25d96c 100644 --- a/src/share/classes/javax/swing/JProgressBar.java +++ b/src/share/classes/javax/swing/JProgressBar.java @@ -699,8 +699,7 @@ public class JProgressBar extends JComponent implements SwingConstants, Accessib * @since 1.4 */ public ChangeListener[] getChangeListeners() { - return (ChangeListener[])listenerList.getListeners( - ChangeListener.class); + return listenerList.getListeners(ChangeListener.class); } /** diff --git a/src/share/classes/javax/swing/JScrollBar.java b/src/share/classes/javax/swing/JScrollBar.java index 5897fd6fa..90e2cc759 100644 --- a/src/share/classes/javax/swing/JScrollBar.java +++ b/src/share/classes/javax/swing/JScrollBar.java @@ -659,8 +659,7 @@ public class JScrollBar extends JComponent implements Adjustable, Accessible * @since 1.4 */ public AdjustmentListener[] getAdjustmentListeners() { - return (AdjustmentListener[])listenerList.getListeners( - AdjustmentListener.class); + return listenerList.getListeners(AdjustmentListener.class); } @@ -754,8 +753,8 @@ public class JScrollBar extends JComponent implements Adjustable, Accessible public void setEnabled(boolean x) { super.setEnabled(x); Component[] children = getComponents(); - for(int i = 0; i < children.length; i++) { - children[i].setEnabled(x); + for (Component child : children) { + child.setEnabled(x); } } diff --git a/src/share/classes/javax/swing/JSlider.java b/src/share/classes/javax/swing/JSlider.java index ea2b510be..c2941c642 100644 --- a/src/share/classes/javax/swing/JSlider.java +++ b/src/share/classes/javax/swing/JSlider.java @@ -930,7 +930,7 @@ public class JSlider extends JComponent implements SwingConstants, Accessible { throw new IllegalArgumentException( "Label incremement must be > 0" ); } - class SmartHashtable extends Hashtable implements PropertyChangeListener { + class SmartHashtable extends Hashtable implements PropertyChangeListener { int increment = 0; int start = 0; boolean startAtMin = false; @@ -977,9 +977,8 @@ public class JSlider extends JComponent implements SwingConstants, Accessible { if ( e.getPropertyName().equals( "minimum" ) || e.getPropertyName().equals( "maximum" ) ) { - Dictionary labelTable = getLabelTable(); - Enumeration keys = labelTable.keys(); - Hashtable hashtable = new Hashtable(); + Enumeration keys = getLabelTable().keys(); + Hashtable hashtable = new Hashtable(); // Save the labels that were added by the developer while ( keys.hasMoreElements() ) { diff --git a/src/share/classes/javax/swing/JSpinner.java b/src/share/classes/javax/swing/JSpinner.java index dd573e104..c5cebda03 100644 --- a/src/share/classes/javax/swing/JSpinner.java +++ b/src/share/classes/javax/swing/JSpinner.java @@ -433,8 +433,7 @@ public class JSpinner extends JComponent implements Accessible * @since 1.4 */ public ChangeListener[] getChangeListeners() { - return (ChangeListener[])listenerList.getListeners( - ChangeListener.class); + return listenerList.getListeners(ChangeListener.class); } @@ -1536,7 +1535,7 @@ public class JSpinner extends JComponent implements Accessible return textField.getAccessibleContext(); } } else if (editor instanceof Accessible) { - return ((Accessible)editor).getAccessibleContext(); + return editor.getAccessibleContext(); } return null; } @@ -1693,7 +1692,7 @@ public class JSpinner extends JComponent implements Accessible if (i < 0 || i > 1) { return false; } - Object o = null; + Object o; if (i == 0) { o = getNextValue(); // AccessibleAction.INCREMENT } else { diff --git a/src/share/classes/javax/swing/JTabbedPane.java b/src/share/classes/javax/swing/JTabbedPane.java index 9bcafb1ab..2c74189fd 100644 --- a/src/share/classes/javax/swing/JTabbedPane.java +++ b/src/share/classes/javax/swing/JTabbedPane.java @@ -313,8 +313,7 @@ public class JTabbedPane extends JComponent * @since 1.4 */ public ChangeListener[] getChangeListeners() { - return (ChangeListener[])listenerList.getListeners( - ChangeListener.class); + return listenerList.getListeners(ChangeListener.class); } /** @@ -2062,7 +2061,7 @@ public class JTabbedPane extends JComponent * Accessibility classes unnecessarily. */ AccessibleContext ac; - ac = ((Accessible) component).getAccessibleContext(); + ac = component.getAccessibleContext(); if (ac != null) { ac.setAccessibleParent(this); } diff --git a/src/share/classes/javax/swing/JTable.java b/src/share/classes/javax/swing/JTable.java index 976cb8130..1b770d891 100644 --- a/src/share/classes/javax/swing/JTable.java +++ b/src/share/classes/javax/swing/JTable.java @@ -1677,16 +1677,16 @@ public class JTable extends JComponent implements TableModelListener, Scrollable if (!forDrop && state != null) { clearSelection(); - int[] rows = (int[])((int[][])state)[0]; - int[] cols = (int[])((int[][])state)[1]; - int[] anchleads = (int[])((int[][])state)[2]; + int[] rows = ((int[][])state)[0]; + int[] cols = ((int[][])state)[1]; + int[] anchleads = ((int[][])state)[2]; - for (int i = 0; i < rows.length; i++) { - addRowSelectionInterval(rows[i], rows[i]); + for (int row : rows) { + addRowSelectionInterval(row, row); } - for (int i = 0; i < cols.length; i++) { - addColumnSelectionInterval(cols[i], cols[i]); + for (int col : cols) { + addColumnSelectionInterval(col, col); } SwingUtilities2.setLeadAnchorWithoutSelection( @@ -1776,7 +1776,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable boolean oldValue = this.autoCreateRowSorter; this.autoCreateRowSorter = autoCreateRowSorter; if (autoCreateRowSorter) { - setRowSorter(new TableRowSorter(getModel())); + setRowSorter(new TableRowSorter(getModel())); } firePropertyChange("autoCreateRowSorter", oldValue, autoCreateRowSorter); @@ -3198,7 +3198,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable private void accommodateDelta(int resizingColumnIndex, int delta) { int columnCount = getColumnCount(); int from = resizingColumnIndex; - int to = columnCount; + int to; // Use the mode to determine how to absorb the changes. switch(autoResizeMode) { @@ -3237,8 +3237,6 @@ public class JTable extends JComponent implements TableModelListener, Scrollable } adjustSizes(totalWidth + delta, r, false); - - return; } private interface Resizable2 { @@ -3492,7 +3490,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable * @see #editingRow */ public boolean isEditing() { - return (cellEditor == null)? false : true; + return cellEditor != null; } /** @@ -3642,7 +3640,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable firePropertyChange("model", old, dataModel); if (getAutoCreateRowSorter()) { - setRowSorter(new TableRowSorter(dataModel)); + setRowSorter(new TableRowSorter(dataModel)); } } } @@ -5160,7 +5158,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable public boolean getScrollableTracksViewportHeight() { return getFillsViewportHeight() && getParent() instanceof JViewport - && (((JViewport)getParent()).getHeight() > getPreferredSize().height); + && (getParent().getHeight() > getPreferredSize().height); } /** @@ -5214,7 +5212,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable // by setting the client property JTable.autoStartsEdit to Boolean.FALSE. if (!retValue && condition == WHEN_ANCESTOR_OF_FOCUSED_COMPONENT && isFocusOwner() && - !Boolean.FALSE.equals((Boolean)getClientProperty("JTable.autoStartsEdit"))) { + !Boolean.FALSE.equals(getClientProperty("JTable.autoStartsEdit"))) { // We do not have a binding for the event. Component editorComponent = getEditorComponent(); if (editorComponent == null) { @@ -5436,7 +5434,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable this.value = null; ((JComponent)getComponent()).setBorder(new LineBorder(Color.black)); try { - Class type = table.getColumnClass(column); + Class type = table.getColumnClass(column); // Since our obligation is to produce a value which is // assignable for the required type it is OK to use the // String constructor for columns which are declared @@ -6627,10 +6625,10 @@ public class JTable extends JComponent implements TableModelListener, Scrollable } else if (name.compareTo("tableCellEditor") == 0) { if (oldValue != null && oldValue instanceof TableCellEditor) { - ((TableCellEditor) oldValue).removeCellEditorListener((CellEditorListener) this); + ((TableCellEditor) oldValue).removeCellEditorListener(this); } if (newValue != null && newValue instanceof TableCellEditor) { - ((TableCellEditor) newValue).addCellEditorListener((CellEditorListener) this); + ((TableCellEditor) newValue).addCellEditorListener(this); } } } @@ -7045,7 +7043,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable */ public Accessible getAccessibleSelection(int i) { if (i < 0 || i > getAccessibleSelectionCount()) { - return (Accessible) null; + return null; } int rowsSel = JTable.this.getSelectedRowCount(); @@ -7158,7 +7156,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable return getAccessibleChild((r * ttlCols) + c); } } - return (Accessible) null; + return null; } /** @@ -7906,7 +7904,7 @@ public class JTable extends JComponent implements TableModelListener, Scrollable JTable.this, getValueAt(row, column), false, false, row, column); if (component instanceof Accessible) { - return ((Accessible) component).getAccessibleContext(); + return component.getAccessibleContext(); } else { return null; } diff --git a/src/share/classes/javax/swing/JTextField.java b/src/share/classes/javax/swing/JTextField.java index b6d3b47f2..56ca320a7 100644 --- a/src/share/classes/javax/swing/JTextField.java +++ b/src/share/classes/javax/swing/JTextField.java @@ -475,8 +475,7 @@ public class JTextField extends JTextComponent implements SwingConstants { * @since 1.4 */ public synchronized ActionListener[] getActionListeners() { - return (ActionListener[])listenerList.getListeners( - ActionListener.class); + return listenerList.getListeners(ActionListener.class); } /** diff --git a/src/share/classes/javax/swing/JTree.java b/src/share/classes/javax/swing/JTree.java index e6aa08c05..012db5063 100644 --- a/src/share/classes/javax/swing/JTree.java +++ b/src/share/classes/javax/swing/JTree.java @@ -187,7 +187,7 @@ public class JTree extends JComponent implements Scrollable, Accessible * information must be determined by visiting all the parent * paths and seeing if they are visible. */ - transient private Hashtable expandedState; + transient private Hashtable expandedState; /** @@ -281,7 +281,7 @@ public class JTree extends JComponent implements Scrollable, Accessible * Used when setExpandedState is invoked, * will be a Stack of Stacks. */ - transient private Stack expandedStack; + transient private Stack> expandedStack; /** * Lead selection path, may not be null. @@ -652,9 +652,9 @@ public class JTree extends JComponent implements Scrollable, Accessible @ConstructorProperties({"model"}) public JTree(TreeModel newModel) { super(); - expandedStack = new Stack(); + expandedStack = new Stack>(); toggleClickCount = 2; - expandedState = new Hashtable(); + expandedState = new Hashtable(); setLayout(null); rowHeight = 16; visibleRowCount = 20; @@ -691,7 +691,7 @@ public class JTree extends JComponent implements Scrollable, Accessible * description: The UI object that implements the Component's LookAndFeel. */ public void setUI(TreeUI ui) { - if ((TreeUI)this.ui != ui) { + if (this.ui != ui) { settingUI = true; uiTreeExpansionListener = null; try { @@ -1298,8 +1298,8 @@ public class JTree extends JComponent implements Scrollable, Accessible Object root = (model == null) ? null : model.getRoot(); TreePath rootPath = (root == null) ? null : new TreePath(root); - TreePath child = null; - TreePath parent = null; + TreePath child; + TreePath parent; boolean outside = row == -1 || p.y < bounds.y || p.y >= bounds.y + bounds.height; @@ -1940,14 +1940,14 @@ public class JTree extends JComponent implements Scrollable, Accessible if(!isExpanded(parent)) return null; - Enumeration toggledPaths = expandedState.keys(); - Vector elements = null; + Enumeration toggledPaths = expandedState.keys(); + Vector elements = null; TreePath path; Object value; if(toggledPaths != null) { while(toggledPaths.hasMoreElements()) { - path = (TreePath)toggledPaths.nextElement(); + path = toggledPaths.nextElement(); value = expandedState.get(path); // Add the path if it is expanded, a descendant of parent, // and it is visible (all parents expanded). This is rather @@ -1956,7 +1956,7 @@ public class JTree extends JComponent implements Scrollable, Accessible ((Boolean)value).booleanValue() && parent.isDescendant(path) && isVisible(path)) { if (elements == null) { - elements = new Vector(); + elements = new Vector(); } elements.addElement(path); } @@ -1990,9 +1990,9 @@ public class JTree extends JComponent implements Scrollable, Accessible return false; // Is this node expanded? - Object value = expandedState.get(path); + Boolean value = expandedState.get(path); - if(value == null || !((Boolean)value).booleanValue()) + if(value == null || !value.booleanValue()) return false; // It is, make sure its parent is also expanded. @@ -2018,7 +2018,7 @@ public class JTree extends JComponent implements Scrollable, Accessible TreePath path = tree.getPathForRow(this, row); if(path != null) { - Boolean value = (Boolean)expandedState.get(path); + Boolean value = expandedState.get(path); return (value != null && value.booleanValue()); } @@ -2704,8 +2704,7 @@ public class JTree extends JComponent implements Scrollable, Accessible * @since 1.4 */ public TreeExpansionListener[] getTreeExpansionListeners() { - return (TreeExpansionListener[])listenerList.getListeners( - TreeExpansionListener.class); + return listenerList.getListeners(TreeExpansionListener.class); } /** @@ -2737,8 +2736,7 @@ public class JTree extends JComponent implements Scrollable, Accessible * @since 1.4 */ public TreeWillExpandListener[] getTreeWillExpandListeners() { - return (TreeWillExpandListener[])listenerList.getListeners( - TreeWillExpandListener.class); + return listenerList.getListeners(TreeWillExpandListener.class); } /** @@ -2895,8 +2893,7 @@ public class JTree extends JComponent implements Scrollable, Accessible * @since 1.4 */ public TreeSelectionListener[] getTreeSelectionListeners() { - return (TreeSelectionListener[])listenerList.getListeners( - TreeSelectionListener.class); + return listenerList.getListeners(TreeSelectionListener.class); } /** @@ -3030,7 +3027,7 @@ public class JTree extends JComponent implements Scrollable, Accessible // Serialization support. private void writeObject(ObjectOutputStream s) throws IOException { - Vector values = new Vector(); + Vector values = new Vector(); s.defaultWriteObject(); // Save the cellRenderer, if its Serializable. @@ -3077,9 +3074,9 @@ public class JTree extends JComponent implements Scrollable, Accessible // Create an instance of expanded state. - expandedState = new Hashtable(); + expandedState = new Hashtable(); - expandedStack = new Stack(); + expandedStack = new Stack>(); Vector values = (Vector)s.readObject(); int indexCounter = 0; @@ -3132,13 +3129,13 @@ public class JTree extends JComponent implements Scrollable, Accessible TreeModel model = getModel(); if(model != null) { - Enumeration paths = expandedState.keys(); + Enumeration paths = expandedState.keys(); if(paths != null) { - Vector state = new Vector(); + Vector state = new Vector(); while(paths.hasMoreElements()) { - TreePath path = (TreePath)paths.nextElement(); + TreePath path = paths.nextElement(); Object archivePath; try { @@ -3502,7 +3499,7 @@ public class JTree extends JComponent implements Scrollable, Accessible */ public boolean getScrollableTracksViewportWidth() { if (getParent() instanceof JViewport) { - return (((JViewport)getParent()).getWidth() > getPreferredSize().width); + return getParent().getWidth() > getPreferredSize().width; } return false; } @@ -3518,7 +3515,7 @@ public class JTree extends JComponent implements Scrollable, Accessible */ public boolean getScrollableTracksViewportHeight() { if (getParent() instanceof JViewport) { - return (((JViewport)getParent()).getHeight() > getPreferredSize().height); + return getParent().getHeight() > getPreferredSize().height; } return false; } @@ -3535,14 +3532,14 @@ public class JTree extends JComponent implements Scrollable, Accessible protected void setExpandedState(TreePath path, boolean state) { if(path != null) { // Make sure all parents of path are expanded. - Stack stack; - TreePath parentPath = path.getParentPath(); + Stack stack; + TreePath parentPath = path.getParentPath(); if (expandedStack.size() == 0) { - stack = new Stack(); + stack = new Stack(); } else { - stack = (Stack)expandedStack.pop(); + stack = expandedStack.pop(); } try { @@ -3556,7 +3553,7 @@ public class JTree extends JComponent implements Scrollable, Accessible } } for(int counter = stack.size() - 1; counter >= 0; counter--) { - parentPath = (TreePath)stack.pop(); + parentPath = stack.pop(); if(!isExpanded(parentPath)) { try { fireTreeWillExpand(parentPath); @@ -3636,12 +3633,11 @@ public class JTree extends JComponent implements Scrollable, Accessible if(parent == null) return null; - Vector descendants = new Vector(); - Enumeration nodes = expandedState.keys(); - TreePath path; + Vector descendants = new Vector(); + Enumeration nodes = expandedState.keys(); while(nodes.hasMoreElements()) { - path = (TreePath)nodes.nextElement(); + TreePath path = nodes.nextElement(); if(parent.isDescendant(path)) descendants.addElement(path); } @@ -3664,8 +3660,8 @@ public class JTree extends JComponent implements Scrollable, Accessible { if(toRemove != null) { while(toRemove.hasMoreElements()) { - Enumeration descendants = getDescendantToggledPaths - ((TreePath)toRemove.nextElement()); + Enumeration descendants = getDescendantToggledPaths + (toRemove.nextElement()); if(descendants != null) { while(descendants.hasMoreElements()) { @@ -4250,7 +4246,7 @@ public class JTree extends JComponent implements Scrollable, Accessible private AccessibleContext getCurrentAccessibleContext() { Component c = getCurrentComponent(); if (c instanceof Accessible) { - return (((Accessible) c).getAccessibleContext()); + return c.getAccessibleContext(); } else { return null; } @@ -4573,7 +4569,7 @@ public class JTree extends JComponent implements Scrollable, Accessible private AccessibleContext getCurrentAccessibleContext() { Component c = getCurrentComponent(); if (c instanceof Accessible) { - return (((Accessible) c).getAccessibleContext()); + return c.getAccessibleContext(); } else { return null; } @@ -5117,12 +5113,8 @@ public class JTree extends JComponent implements Scrollable, Accessible public boolean isVisible() { Rectangle pathBounds = tree.getPathBounds(path); Rectangle parentBounds = tree.getVisibleRect(); - if (pathBounds != null && parentBounds != null && - parentBounds.intersects(pathBounds)) { - return true; - } else { - return false; - } + return pathBounds != null && parentBounds != null && + parentBounds.intersects(pathBounds); } public void setVisible(boolean b) { diff --git a/src/share/classes/javax/swing/JViewport.java b/src/share/classes/javax/swing/JViewport.java index f5a16bbd2..734e80a05 100644 --- a/src/share/classes/javax/swing/JViewport.java +++ b/src/share/classes/javax/swing/JViewport.java @@ -389,7 +389,7 @@ public class JViewport extends JComponent implements Accessible // could be bigger than invalid size. validateView(); } - int dx = 0, dy = 0; + int dx, dy; dx = positionAdjustment(getWidth(), contentRect.width, contentRect.x); dy = positionAdjustment(getHeight(), contentRect.height, contentRect.y); @@ -682,10 +682,7 @@ public class JViewport extends JComponent implements Accessible * @see JComponent#isPaintingOrigin() */ boolean isPaintingOrigin() { - if (scrollMode == BACKINGSTORE_SCROLL_MODE) { - return true; - } - return false; + return scrollMode == BACKINGSTORE_SCROLL_MODE; } @@ -903,11 +900,7 @@ public class JViewport extends JComponent implements Accessible */ public void setScrollMode(int mode) { scrollMode = mode; - if (mode == BACKINGSTORE_SCROLL_MODE) { - backingStore = true; - } else { - backingStore = false; - } + backingStore = mode == BACKINGSTORE_SCROLL_MODE; } /** @@ -958,10 +951,10 @@ public class JViewport extends JComponent implements Accessible } } - private final boolean isBlitting() { + private boolean isBlitting() { Component view = getView(); return (scrollMode == BLIT_SCROLL_MODE) && - (view instanceof JComponent) && ((JComponent)view).isOpaque(); + (view instanceof JComponent) && view.isOpaque(); } @@ -1380,8 +1373,7 @@ public class JViewport extends JComponent implements Accessible * @since 1.4 */ public ChangeListener[] getChangeListeners() { - return (ChangeListener[])listenerList.getListeners( - ChangeListener.class); + return listenerList.getListeners(ChangeListener.class); } /** diff --git a/src/share/classes/javax/swing/JWindow.java b/src/share/classes/javax/swing/JWindow.java index c94803c4c..d805838b0 100644 --- a/src/share/classes/javax/swing/JWindow.java +++ b/src/share/classes/javax/swing/JWindow.java @@ -185,7 +185,7 @@ public class JWindow extends Window implements Accessible, super(owner == null? SwingUtilities.getSharedOwnerFrame() : owner); if (owner == null) { WindowListener ownerShutdownListener = - (WindowListener)SwingUtilities.getSharedOwnerFrameShutdownListener(); + SwingUtilities.getSharedOwnerFrameShutdownListener(); addWindowListener(ownerShutdownListener); } windowInit(); @@ -212,7 +212,7 @@ public class JWindow extends Window implements Accessible, owner); if (owner == null) { WindowListener ownerShutdownListener = - (WindowListener)SwingUtilities.getSharedOwnerFrameShutdownListener(); + SwingUtilities.getSharedOwnerFrameShutdownListener(); addWindowListener(ownerShutdownListener); } windowInit(); @@ -250,7 +250,7 @@ public class JWindow extends Window implements Accessible, owner, gc); if (owner == null) { WindowListener ownerShutdownListener = - (WindowListener)SwingUtilities.getSharedOwnerFrameShutdownListener(); + SwingUtilities.getSharedOwnerFrameShutdownListener(); addWindowListener(ownerShutdownListener); } windowInit(); diff --git a/src/share/classes/javax/swing/KeyboardManager.java b/src/share/classes/javax/swing/KeyboardManager.java index e27a72f3d..d5ed58ba0 100644 --- a/src/share/classes/javax/swing/KeyboardManager.java +++ b/src/share/classes/javax/swing/KeyboardManager.java @@ -68,13 +68,13 @@ class KeyboardManager { /** * maps top-level containers to a sub-hashtable full of keystrokes */ - Hashtable containerMap = new Hashtable(); + Hashtable containerMap = new Hashtable(); /** * Maps component/keystroke pairs to a topLevel container * This is mainly used for fast unregister operations */ - Hashtable componentKeyStrokeMap = new Hashtable(); + Hashtable componentKeyStrokeMap = new Hashtable(); public static KeyboardManager getCurrentManager() { return currentManager; @@ -95,7 +95,7 @@ class KeyboardManager { if (topContainer == null) { return; } - Hashtable keyMap = (Hashtable)containerMap.get(topContainer); + Hashtable keyMap = containerMap.get(topContainer); if (keyMap == null) { // lazy evaluate one keyMap = registerNewTopContainer(topContainer); @@ -114,8 +114,8 @@ class KeyboardManager { // Then add the old compoennt and the new compoent to the vector // then insert the vector in the table if (tmp != c) { // this means this is already registered for this component, no need to dup - Vector v = new Vector(); - v.addElement(tmp); + Vector v = new Vector(); + v.addElement((JComponent) tmp); v.addElement(c); keyMap.put(k, v); } @@ -154,13 +154,13 @@ class KeyboardManager { ComponentKeyStrokePair ckp = new ComponentKeyStrokePair(c,ks); - Object topContainer = componentKeyStrokeMap.get(ckp); + Container topContainer = componentKeyStrokeMap.get(ckp); if (topContainer == null) { // never heard of this pairing, so bail return; } - Hashtable keyMap = (Hashtable)containerMap.get(topContainer); + Hashtable keyMap = containerMap.get(topContainer); if (keyMap == null) { // this should never happen, but I'm being safe Thread.dumpStack(); return; @@ -221,7 +221,7 @@ class KeyboardManager { ks=KeyStroke.getKeyStroke(e.getKeyCode(), e.getModifiers(), !pressed); } - Hashtable keyMap = (Hashtable)containerMap.get(topAncestor); + Hashtable keyMap = containerMap.get(topAncestor); if (keyMap != null) { // this container isn't registered, so bail Object tmp = keyMap.get(ks); @@ -293,7 +293,7 @@ class KeyboardManager { if (top == null) { return; } - Hashtable keyMap = (Hashtable)containerMap.get(top); + Hashtable keyMap = containerMap.get(top); if (keyMap == null) { // lazy evaluate one keyMap = registerNewTopContainer(top); @@ -314,11 +314,11 @@ class KeyboardManager { public void unregisterMenuBar(JMenuBar mb) { - Object topContainer = getTopAncestor(mb); + Container topContainer = getTopAncestor(mb); if (topContainer == null) { return; } - Hashtable keyMap = (Hashtable)containerMap.get(topContainer); + Hashtable keyMap = containerMap.get(topContainer); if (keyMap!=null) { Vector v = (Vector)keyMap.get(JMenuBar.class); if (v != null) { diff --git a/src/share/classes/javax/swing/LayoutComparator.java b/src/share/classes/javax/swing/LayoutComparator.java index 42d85c70c..01ba6e54d 100644 --- a/src/share/classes/javax/swing/LayoutComparator.java +++ b/src/share/classes/javax/swing/LayoutComparator.java @@ -39,7 +39,7 @@ import java.awt.Window; * * @author David Mendenhall */ -final class LayoutComparator implements Comparator, java.io.Serializable { +final class LayoutComparator implements Comparator, java.io.Serializable { private static final int ROW_TOLERANCE = 10; @@ -51,10 +51,7 @@ final class LayoutComparator implements Comparator, java.io.Serializable { leftToRight = orientation.isLeftToRight(); } - public int compare(Object o1, Object o2) { - Component a = (Component)o1; - Component b = (Component)o2; - + public int compare(Component a, Component b) { if (a == b) { return 0; } @@ -65,9 +62,9 @@ final class LayoutComparator implements Comparator, java.io.Serializable { // each Component and then search from the Window down until the // hierarchy branches. if (a.getParent() != b.getParent()) { - LinkedList aAncestory, bAncestory; + LinkedList aAncestory = new LinkedList(); - for(aAncestory = new LinkedList(); a != null; a = a.getParent()) { + for(; a != null; a = a.getParent()) { aAncestory.add(a); if (a instanceof Window) { break; @@ -78,7 +75,9 @@ final class LayoutComparator implements Comparator, java.io.Serializable { throw new ClassCastException(); } - for(bAncestory = new LinkedList(); b != null; b = b.getParent()) { + LinkedList bAncestory = new LinkedList(); + + for(; b != null; b = b.getParent()) { bAncestory.add(b); if (b instanceof Window) { break; @@ -89,18 +88,18 @@ final class LayoutComparator implements Comparator, java.io.Serializable { throw new ClassCastException(); } - for (ListIterator + for (ListIterator aIter = aAncestory.listIterator(aAncestory.size()), bIter = bAncestory.listIterator(bAncestory.size()); ;) { if (aIter.hasPrevious()) { - a = (Component)aIter.previous(); + a = aIter.previous(); } else { // a is an ancestor of b return -1; } if (bIter.hasPrevious()) { - b = (Component)bIter.previous(); + b = bIter.previous(); } else { // b is an ancestor of a return 1; diff --git a/src/share/classes/javax/swing/LayoutFocusTraversalPolicy.java b/src/share/classes/javax/swing/LayoutFocusTraversalPolicy.java index 7e200e02e..33d81a91a 100644 --- a/src/share/classes/javax/swing/LayoutFocusTraversalPolicy.java +++ b/src/share/classes/javax/swing/LayoutFocusTraversalPolicy.java @@ -65,7 +65,7 @@ public class LayoutFocusTraversalPolicy extends SortingFocusTraversalPolicy * Constructs a LayoutFocusTraversalPolicy with the passed in * Comparator. */ - LayoutFocusTraversalPolicy(Comparator c) { + LayoutFocusTraversalPolicy(Comparator c) { super(c); } diff --git a/src/share/classes/javax/swing/LegacyGlueFocusTraversalPolicy.java b/src/share/classes/javax/swing/LegacyGlueFocusTraversalPolicy.java index f8a37fe7a..fe73a0895 100644 --- a/src/share/classes/javax/swing/LegacyGlueFocusTraversalPolicy.java +++ b/src/share/classes/javax/swing/LegacyGlueFocusTraversalPolicy.java @@ -48,8 +48,8 @@ final class LegacyGlueFocusTraversalPolicy extends FocusTraversalPolicy private transient FocusTraversalPolicy delegatePolicy; private transient DefaultFocusManager delegateManager; - private HashMap forwardMap = new HashMap(), - backwardMap = new HashMap(); + private HashMap forwardMap = new HashMap(), + backwardMap = new HashMap(); LegacyGlueFocusTraversalPolicy(FocusTraversalPolicy delegatePolicy) { this.delegatePolicy = delegatePolicy; @@ -70,11 +70,11 @@ final class LegacyGlueFocusTraversalPolicy extends FocusTraversalPolicy public Component getComponentAfter(Container focusCycleRoot, Component aComponent) { Component hardCoded = aComponent, prevHardCoded; - HashSet sanity = new HashSet(); + HashSet sanity = new HashSet(); do { prevHardCoded = hardCoded; - hardCoded = (Component)forwardMap.get(hardCoded); + hardCoded = forwardMap.get(hardCoded); if (hardCoded == null) { if (delegatePolicy != null && prevHardCoded.isFocusCycleRoot(focusCycleRoot)) { @@ -99,11 +99,11 @@ final class LegacyGlueFocusTraversalPolicy extends FocusTraversalPolicy public Component getComponentBefore(Container focusCycleRoot, Component aComponent) { Component hardCoded = aComponent, prevHardCoded; - HashSet sanity = new HashSet(); + HashSet sanity = new HashSet(); do { prevHardCoded = hardCoded; - hardCoded = (Component)backwardMap.get(hardCoded); + hardCoded = backwardMap.get(hardCoded); if (hardCoded == null) { if (delegatePolicy != null && prevHardCoded.isFocusCycleRoot(focusCycleRoot)) { diff --git a/src/share/classes/javax/swing/MenuSelectionManager.java b/src/share/classes/javax/swing/MenuSelectionManager.java index 8e1f36a3a..e1234c69a 100644 --- a/src/share/classes/javax/swing/MenuSelectionManager.java +++ b/src/share/classes/javax/swing/MenuSelectionManager.java @@ -37,7 +37,7 @@ import sun.awt.AppContext; * @author Arnaud Weber */ public class MenuSelectionManager { - private Vector selection = new Vector(); + private Vector selection = new Vector(); /* diagnostic aids -- should be false for production builds. */ private static final boolean TRACE = false; // trace creates and disposes @@ -100,14 +100,14 @@ public class MenuSelectionManager { } for(i=0,c=path.length;i= firstDifference ; i--) { - MenuElement me = (MenuElement)selection.elementAt(i); + MenuElement me = selection.elementAt(i); selection.removeElementAt(i); me.menuSelectionChanged(false); } @@ -131,7 +131,7 @@ public class MenuSelectionManager { MenuElement res[] = new MenuElement[selection.size()]; int i,c; for(i=0,c=selection.size();i 0) { - MenuElement me = (MenuElement)selection.elementAt(0); + MenuElement me = selection.elementAt(0); return isComponentPartOfCurrentMenu(me,c); } else return false; diff --git a/src/share/classes/javax/swing/MultiUIDefaults.java b/src/share/classes/javax/swing/MultiUIDefaults.java index 30867e228..d7d435ef1 100644 --- a/src/share/classes/javax/swing/MultiUIDefaults.java +++ b/src/share/classes/javax/swing/MultiUIDefaults.java @@ -56,8 +56,7 @@ class MultiUIDefaults extends UIDefaults return value; } - for(int i = 0; i < tables.length; i++) { - UIDefaults table = tables[i]; + for (UIDefaults table : tables) { value = (table != null) ? table.get(key) : null; if (value != null) { return value; @@ -75,8 +74,7 @@ class MultiUIDefaults extends UIDefaults return value; } - for(int i = 0; i < tables.length; i++) { - UIDefaults table = tables[i]; + for (UIDefaults table : tables) { value = (table != null) ? table.get(key,l) : null; if (value != null) { return value; @@ -89,8 +87,7 @@ class MultiUIDefaults extends UIDefaults public int size() { int n = super.size(); - for(int i = 0; i < tables.length; i++) { - UIDefaults table = tables[i]; + for (UIDefaults table : tables) { n += (table != null) ? table.size() : 0; } return n; @@ -102,7 +99,7 @@ class MultiUIDefaults extends UIDefaults } - public Enumeration keys() + public Enumeration keys() { Enumeration[] enums = new Enumeration[1 + tables.length]; enums[0] = super.keys(); @@ -116,7 +113,7 @@ class MultiUIDefaults extends UIDefaults } - public Enumeration elements() + public Enumeration elements() { Enumeration[] enums = new Enumeration[1 + tables.length]; enums[0] = super.elements(); @@ -137,7 +134,7 @@ class MultiUIDefaults extends UIDefaults } } - private static class MultiUIDefaultsEnumerator implements Enumeration + private static class MultiUIDefaultsEnumerator implements Enumeration { Enumeration[] enums; int n = 0; @@ -175,8 +172,7 @@ class MultiUIDefaults extends UIDefaults return value; } - for(int i = 0; i < tables.length; i++) { - UIDefaults table = tables[i]; + for (UIDefaults table : tables) { value = (table != null) ? table.remove(key) : null; if (value != null) { return value; @@ -189,8 +185,7 @@ class MultiUIDefaults extends UIDefaults public void clear() { super.clear(); - for(int i = 0; i < tables.length; i++) { - UIDefaults table = tables[i]; + for (UIDefaults table : tables) { if (table != null) { table.clear(); } diff --git a/src/share/classes/javax/swing/PopupFactory.java b/src/share/classes/javax/swing/PopupFactory.java index f9c3c0dd5..753959cea 100644 --- a/src/share/classes/javax/swing/PopupFactory.java +++ b/src/share/classes/javax/swing/PopupFactory.java @@ -313,9 +313,9 @@ public class PopupFactory { if(contents instanceof JPopupMenu) { JPopupMenu jpm = (JPopupMenu) contents; Component popComps[] = jpm.getComponents(); - for(int i=0;i cache; + Map> heavyPopupCache = getHeavyWeightPopupCache(); if (heavyPopupCache.containsKey(w)) { - cache = (List)heavyPopupCache.get(w); + cache = heavyPopupCache.get(w); } else { return null; } - int c; - if ((c = cache.size()) > 0) { - HeavyWeightPopup r = (HeavyWeightPopup)cache.get(0); + if (cache.size() > 0) { + HeavyWeightPopup r = cache.get(0); cache.remove(0); return r; } @@ -380,13 +379,13 @@ public class PopupFactory { * Window to a List of * HeavyWeightPopups. */ - private static Map getHeavyWeightPopupCache() { + private static Map> getHeavyWeightPopupCache() { synchronized (HeavyWeightPopup.class) { - Map cache = (Map)SwingUtilities.appContextGet( + Map> cache = (Map>)SwingUtilities.appContextGet( heavyWeightPopupCacheKey); if (cache == null) { - cache = new HashMap(2); + cache = new HashMap>(2); SwingUtilities.appContextPut(heavyWeightPopupCacheKey, cache); } @@ -399,13 +398,13 @@ public class PopupFactory { */ private static void recycleHeavyWeightPopup(HeavyWeightPopup popup) { synchronized (HeavyWeightPopup.class) { - List cache; - Object window = SwingUtilities.getWindowAncestor( + List cache; + Window window = SwingUtilities.getWindowAncestor( popup.getComponent()); - Map heavyPopupCache = getHeavyWeightPopupCache(); + Map> heavyPopupCache = getHeavyWeightPopupCache(); if (window instanceof Popup.DefaultFrame || - !((Window)window).isVisible()) { + !window.isVisible()) { // If the Window isn't visible, we don't cache it as we // likely won't ever get a windowClosed event to clean up. // We also don't cache DefaultFrames as this indicates @@ -414,28 +413,27 @@ public class PopupFactory { popup._dispose(); return; } else if (heavyPopupCache.containsKey(window)) { - cache = (List)heavyPopupCache.get(window); + cache = heavyPopupCache.get(window); } else { - cache = new ArrayList(); + cache = new ArrayList(); heavyPopupCache.put(window, cache); // Clean up if the Window is closed - final Window w = (Window)window; + final Window w = window; w.addWindowListener(new WindowAdapter() { public void windowClosed(WindowEvent e) { - List popups; + List popups; synchronized(HeavyWeightPopup.class) { - Map heavyPopupCache2 = + Map> heavyPopupCache2 = getHeavyWeightPopupCache(); - popups = (List)heavyPopupCache2.remove(w); + popups = heavyPopupCache2.remove(w); } if (popups != null) { for (int counter = popups.size() - 1; counter >= 0; counter--) { - ((HeavyWeightPopup)popups.get(counter)). - _dispose(); + popups.get(counter)._dispose(); } } } @@ -534,10 +532,9 @@ public class PopupFactory { Window[] ownedWindows = w.getOwnedWindows(); if(ownedWindows != null) { Rectangle bnd = component.getBounds(); - for(int i=0; i getLightWeightPopupCache() { + List cache = (List)SwingUtilities.appContextGet( lightWeightPopupCacheKey); if (cache == null) { - cache = new ArrayList(); + cache = new ArrayList(); SwingUtilities.appContextPut(lightWeightPopupCacheKey, cache); } return cache; @@ -682,7 +679,7 @@ public class PopupFactory { */ private static void recycleLightWeightPopup(LightWeightPopup popup) { synchronized (LightWeightPopup.class) { - List lightPopupCache = getLightWeightPopupCache(); + List lightPopupCache = getLightWeightPopupCache(); if (lightPopupCache.size() < MAX_CACHE_SIZE) { lightPopupCache.add(popup); } @@ -695,11 +692,9 @@ public class PopupFactory { */ private static LightWeightPopup getRecycledLightWeightPopup() { synchronized (LightWeightPopup.class) { - List lightPopupCache = getLightWeightPopupCache(); - int c; - if((c = lightPopupCache.size()) > 0) { - LightWeightPopup r = (LightWeightPopup)lightPopupCache. - get(0); + List lightPopupCache = getLightWeightPopupCache(); + if (lightPopupCache.size() > 0) { + LightWeightPopup r = lightPopupCache.get(0); lightPopupCache.remove(0); return r; } @@ -755,8 +750,7 @@ public class PopupFactory { component.setLocation(p.x, p.y); if (parent instanceof JLayeredPane) { - ((JLayeredPane)parent).add(component, - JLayeredPane.POPUP_LAYER, 0); + parent.add(component, JLayeredPane.POPUP_LAYER, 0); } else { parent.add(component); } @@ -826,12 +820,12 @@ public class PopupFactory { /** * Returns the cache to use for medium weight popups. */ - private static List getMediumWeightPopupCache() { - List cache = (List)SwingUtilities.appContextGet( + private static List getMediumWeightPopupCache() { + List cache = (List)SwingUtilities.appContextGet( mediumWeightPopupCacheKey); if (cache == null) { - cache = new ArrayList(); + cache = new ArrayList(); SwingUtilities.appContextPut(mediumWeightPopupCacheKey, cache); } return cache; @@ -842,7 +836,7 @@ public class PopupFactory { */ private static void recycleMediumWeightPopup(MediumWeightPopup popup) { synchronized (MediumWeightPopup.class) { - List mediumPopupCache = getMediumWeightPopupCache(); + List mediumPopupCache = getMediumWeightPopupCache(); if (mediumPopupCache.size() < MAX_CACHE_SIZE) { mediumPopupCache.add(popup); } @@ -855,12 +849,9 @@ public class PopupFactory { */ private static MediumWeightPopup getRecycledMediumWeightPopup() { synchronized (MediumWeightPopup.class) { - java.util.List mediumPopupCache = - getMediumWeightPopupCache(); - int c; - if ((c=mediumPopupCache.size()) > 0) { - MediumWeightPopup r = (MediumWeightPopup)mediumPopupCache. - get(0); + List mediumPopupCache = getMediumWeightPopupCache(); + if (mediumPopupCache.size() > 0) { + MediumWeightPopup r = mediumPopupCache.get(0); mediumPopupCache.remove(0); return r; } @@ -904,7 +895,7 @@ public class PopupFactory { x, y); component.setVisible(false); component.setLocation(p.x, p.y); - ((JLayeredPane)parent).add(component, JLayeredPane.POPUP_LAYER, + parent.add(component, JLayeredPane.POPUP_LAYER, 0); } else { Point p = SwingUtilities.convertScreenLocationToParent(parent, diff --git a/src/share/classes/javax/swing/RepaintManager.java b/src/share/classes/javax/swing/RepaintManager.java index 81c816022..b0e6c048f 100644 --- a/src/share/classes/javax/swing/RepaintManager.java +++ b/src/share/classes/javax/swing/RepaintManager.java @@ -589,7 +589,7 @@ public class RepaintManager */ private synchronized boolean extendDirtyRegion( Component c, int x, int y, int w, int h) { - Rectangle r = (Rectangle)dirtyComponents.get(c); + Rectangle r = dirtyComponents.get(c); if (r != null) { // A non-null r implies c is already marked as dirty, // and that the parent is valid. Therefore we can @@ -609,9 +609,9 @@ public class RepaintManager if (delegate != null) { return delegate.getDirtyRegion(aComponent); } - Rectangle r = null; + Rectangle r; synchronized(this) { - r = (Rectangle)dirtyComponents.get(aComponent); + r = dirtyComponents.get(aComponent); } if(r == null) return new Rectangle(0,0,0,0); @@ -745,8 +745,8 @@ public class RepaintManager Rectangle rect; int localBoundsX = 0; int localBoundsY = 0; - int localBoundsH = 0; - int localBoundsW = 0; + int localBoundsH; + int localBoundsW; Enumeration keys; roots = new ArrayList(count); @@ -853,7 +853,7 @@ public class RepaintManager dx = rootDx = 0; dy = rootDy = 0; - tmp.setBounds((Rectangle) dirtyComponents.get(dirtyComponent)); + tmp.setBounds(dirtyComponents.get(dirtyComponent)); // System.out.println("Collect dirty component for bound " + tmp + // "component bounds is " + cBounds);; @@ -900,7 +900,7 @@ public class RepaintManager Rectangle r; tmp.setLocation(tmp.x + rootDx - dx, tmp.y + rootDy - dy); - r = (Rectangle)dirtyComponents.get(rootDirtyComponent); + r = dirtyComponents.get(rootDirtyComponent); SwingUtilities.computeUnion(tmp.x,tmp.y,tmp.width,tmp.height,r); } @@ -985,7 +985,7 @@ public class RepaintManager private Image _getOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) { Dimension maxSize = getDoubleBufferMaximumSize(); - DoubleBufferInfo doubleBuffer = null; + DoubleBufferInfo doubleBuffer; int width, height; if (standardDoubleBuffer == null) { @@ -1054,7 +1054,7 @@ public class RepaintManager Iterator gcs = volatileMap.keySet().iterator(); while (gcs.hasNext()) { GraphicsConfiguration gc = (GraphicsConfiguration)gcs.next(); - VolatileImage image = (VolatileImage)volatileMap.get(gc); + VolatileImage image = volatileMap.get(gc); if (image.getWidth() > width || image.getHeight() > height) { image.flush(); gcs.remove(); @@ -1222,7 +1222,7 @@ public class RepaintManager */ void beginPaint() { boolean multiThreadedPaint = false; - int paintDepth = 0; + int paintDepth; Thread currentThread = Thread.currentThread(); synchronized(this) { paintDepth = this.paintDepth; diff --git a/src/share/classes/javax/swing/SortingFocusTraversalPolicy.java b/src/share/classes/javax/swing/SortingFocusTraversalPolicy.java index e920acccb..85a2bb96a 100644 --- a/src/share/classes/javax/swing/SortingFocusTraversalPolicy.java +++ b/src/share/classes/javax/swing/SortingFocusTraversalPolicy.java @@ -79,7 +79,7 @@ public class SortingFocusTraversalPolicy * sorted list should be reused if possible. */ transient private Container cachedRoot; - transient private List cachedCycle; + transient private List cachedCycle; // Delegate our fitness test to ContainerOrder so that we only have to // code the algorithm once. @@ -111,7 +111,7 @@ public class SortingFocusTraversalPolicy return cycle; } private int getComponentIndex(List cycle, Component aComponent) { - int index = 0; + int index; try { index = Collections.binarySearch(cycle, aComponent, comparator); } catch (ClassCastException e) { @@ -130,14 +130,14 @@ public class SortingFocusTraversalPolicy return index; } - private void enumerateAndSortCycle(Container focusCycleRoot, List cycle) { + private void enumerateAndSortCycle(Container focusCycleRoot, List cycle) { if (focusCycleRoot.isShowing()) { enumerateCycle(focusCycleRoot, cycle); Collections.sort(cycle, comparator); } } - private void enumerateCycle(Container container, List cycle) { + private void enumerateCycle(Container container, List cycle) { if (!(container.isVisible() && container.isDisplayable())) { return; } @@ -145,8 +145,7 @@ public class SortingFocusTraversalPolicy cycle.add(container); Component[] components = container.getComponents(); - for (int i = 0; i < components.length; i++) { - Component comp = components[i]; + for (Component comp : components) { if (comp instanceof Container) { Container cont = (Container)comp; @@ -385,8 +384,8 @@ public class SortingFocusTraversalPolicy return getLastComponent(aContainer); } - Component comp = null; - Component tryComp = null; + Component comp; + Component tryComp; for (index--; index>=0; index--) { comp = cycle.get(index); @@ -442,8 +441,7 @@ public class SortingFocusTraversalPolicy } if (log.isLoggable(Level.FINE)) log.fine("### Cycle is " + cycle); - for (int i = 0; i < cycle.size(); i++) { - Component comp = cycle.get(i); + for (Component comp : cycle) { if (accept(comp)) { return comp; } else if (comp instanceof Container && comp != aContainer) { diff --git a/src/share/classes/javax/swing/SpringLayout.java b/src/share/classes/javax/swing/SpringLayout.java index 348479442..e825f4c61 100644 --- a/src/share/classes/javax/swing/SpringLayout.java +++ b/src/share/classes/javax/swing/SpringLayout.java @@ -185,11 +185,11 @@ import java.util.*; * @since 1.4 */ public class SpringLayout implements LayoutManager2 { - private Map componentConstraints = new HashMap(); + private Map componentConstraints = new HashMap(); private Spring cyclicReference = Spring.constant(Spring.UNSET); - private Set cyclicSprings; - private Set acyclicSprings; + private Set cyclicSprings; + private Set acyclicSprings; /** @@ -415,8 +415,7 @@ public class SpringLayout implements LayoutManager2 { } if (!valid) { String[] all = horizontal ? ALL_HORIZONTAL : ALL_VERTICAL; - for (int i = 0; i < all.length; i++) { - String s = all[i]; + for (String s : all) { if (!history.contains(s)) { setConstraint(s, null); } @@ -822,8 +821,7 @@ public class SpringLayout implements LayoutManager2 { /*pp*/ void reset() { Spring[] allSprings = {x, y, width, height, east, south, horizontalCenter, verticalCenter, baseline}; - for (int i = 0; i < allSprings.length; i++) { - Spring s = allSprings[i]; + for (Spring s : allSprings) { if (s != null) { s.setValue(Spring.UNSET); } @@ -881,8 +879,8 @@ public class SpringLayout implements LayoutManager2 { public SpringLayout() {} private void resetCyclicStatuses() { - cyclicSprings = new HashSet(); - acyclicSprings = new HashSet(); + cyclicSprings = new HashSet(); + acyclicSprings = new HashSet(); } private void setParent(Container p) { @@ -1145,7 +1143,7 @@ public class SpringLayout implements LayoutManager2 { * @return the constraints for the specified component */ public Constraints getConstraints(Component c) { - Constraints result = (Constraints)componentConstraints.get(c); + Constraints result = componentConstraints.get(c); if (result == null) { if (c instanceof javax.swing.JComponent) { Object cp = ((javax.swing.JComponent)c).getClientProperty(SpringLayout.class); diff --git a/src/share/classes/javax/swing/SwingUtilities.java b/src/share/classes/javax/swing/SwingUtilities.java index 38f18e781..0493510a7 100644 --- a/src/share/classes/javax/swing/SwingUtilities.java +++ b/src/share/classes/javax/swing/SwingUtilities.java @@ -108,11 +108,8 @@ public class SwingUtilities implements SwingConstants * Return true if a contains b */ public static final boolean isRectangleContainingRectangle(Rectangle a,Rectangle b) { - if (b.x >= a.x && (b.x + b.width) <= (a.x + a.width) && - b.y >= a.y && (b.y + b.height) <= (a.y + a.height)) { - return true; - } - return false; + return b.x >= a.x && (b.x + b.width) <= (a.x + a.width) && + b.y >= a.y && (b.y + b.height) <= (a.y + a.height); } /** @@ -272,8 +269,7 @@ public class SwingUtilities implements SwingConstants } if (parent instanceof Container) { Component components[] = ((Container)parent).getComponents(); - for (int i = 0 ; i < components.length ; i++) { - Component comp = components[i]; + for (Component comp : components) { if (comp != null && comp.isVisible()) { Point loc = comp.getLocation(); if (comp instanceof Container) { @@ -376,8 +372,8 @@ public class SwingUtilities implements SwingConstants do { if(c instanceof JComponent) { - x = ((JComponent)c).getX(); - y = ((JComponent)c).getY(); + x = c.getX(); + y = c.getY(); } else if(c instanceof java.applet.Applet || c instanceof java.awt.Window) { try { @@ -415,8 +411,8 @@ public class SwingUtilities implements SwingConstants do { if(c instanceof JComponent) { - x = ((JComponent)c).getX(); - y = ((JComponent)c).getY(); + x = c.getX(); + y = c.getY(); } else if(c instanceof java.applet.Applet || c instanceof java.awt.Window) { try { @@ -980,7 +976,7 @@ public class SwingUtilities implements SwingConstants */ int gap; - View v = null; + View v; if (textIsEmpty) { textR.width = textR.height = 0; text = ""; @@ -1248,8 +1244,8 @@ public class SwingUtilities implements SwingConstants children = ((Container)c).getComponents(); } if (children != null) { - for(int i = 0; i < children.length; i++) { - updateComponentTreeUI0(children[i]); + for (Component child : children) { + updateComponentTreeUI0(child); } } } @@ -1702,7 +1698,7 @@ public class SwingUtilities implements SwingConstants */ public static void replaceUIActionMap(JComponent component, ActionMap uiActionMap) { - ActionMap map = component.getActionMap((uiActionMap != null));; + ActionMap map = component.getActionMap((uiActionMap != null)); while (map != null) { ActionMap parent = map.getParent(); @@ -1770,8 +1766,7 @@ public class SwingUtilities implements SwingConstants */ void installListeners() { Window[] windows = getOwnedWindows(); - for (int ind = 0; ind < windows.length; ind++){ - Window window = windows[ind]; + for (Window window : windows) { if (window != null) { window.removeWindowListener(this); window.addWindowListener(this); @@ -1786,8 +1781,7 @@ public class SwingUtilities implements SwingConstants public void windowClosed(WindowEvent e) { synchronized(getTreeLock()) { Window[] windows = getOwnedWindows(); - for (int ind = 0; ind < windows.length; ind++) { - Window window = windows[ind]; + for (Window window : windows) { if (window != null) { if (window.isDisplayable()) { return; @@ -1875,7 +1869,7 @@ public class SwingUtilities implements SwingConstants } - static Class loadSystemClass(String className) throws ClassNotFoundException { + static Class loadSystemClass(String className) throws ClassNotFoundException { return Class.forName(className, true, Thread.currentThread(). getContextClassLoader()); } diff --git a/src/share/classes/javax/swing/Timer.java b/src/share/classes/javax/swing/Timer.java index a96d2ce8d..4f339158e 100644 --- a/src/share/classes/javax/swing/Timer.java +++ b/src/share/classes/javax/swing/Timer.java @@ -270,8 +270,7 @@ public class Timer implements Serializable * @since 1.4 */ public ActionListener[] getActionListeners() { - return (ActionListener[])listenerList.getListeners( - ActionListener.class); + return listenerList.getListeners(ActionListener.class); } diff --git a/src/share/classes/javax/swing/TimerQueue.java b/src/share/classes/javax/swing/TimerQueue.java index 1f694d1c8..f68f4e996 100644 --- a/src/share/classes/javax/swing/TimerQueue.java +++ b/src/share/classes/javax/swing/TimerQueue.java @@ -97,7 +97,7 @@ class TimerQueue implements Runnable final ThreadGroup threadGroup = AppContext.getAppContext().getThreadGroup(); java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { + new java.security.PrivilegedAction() { public Object run() { Thread timerThread = new Thread(threadGroup, TimerQueue.this, "TimerQueue"); @@ -226,7 +226,7 @@ class TimerQueue implements Runnable /** * Returns nanosecond time offset by origin */ - private final static long now() { + private static long now() { return System.nanoTime() - NANO_ORIGIN; } diff --git a/src/share/classes/javax/swing/UIDefaults.java b/src/share/classes/javax/swing/UIDefaults.java index 302dc00b0..7a372e797 100644 --- a/src/share/classes/javax/swing/UIDefaults.java +++ b/src/share/classes/javax/swing/UIDefaults.java @@ -72,11 +72,11 @@ import sun.util.CoreResourceBundleControl; */ public class UIDefaults extends Hashtable { - private static final Object PENDING = new String("Pending"); + private static final Object PENDING = "Pending"; private SwingPropertyChangeSupport changeSupport; - private Vector resourceBundles; + private Vector resourceBundles; private Locale defaultLocale = Locale.getDefault(); @@ -86,7 +86,7 @@ public class UIDefaults extends Hashtable * Access to this should be done while holding a lock on the * UIDefaults, eg synchronized(this). */ - private Map resourceCache; + private Map> resourceCache; /** * Creates an empty defaults table. @@ -106,7 +106,7 @@ public class UIDefaults extends Hashtable */ public UIDefaults(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); - resourceCache = new HashMap(); + resourceCache = new HashMap>(); } @@ -281,24 +281,24 @@ public class UIDefaults extends Hashtable if( defaultLocale == null ) return null; else - l = (Locale)defaultLocale; + l = defaultLocale; } synchronized(this) { - return getResourceCache(l).get((String)key); + return getResourceCache(l).get(key); } } /** * Returns a Map of the known resources for the given locale. */ - private Map getResourceCache(Locale l) { - Map values = (Map)resourceCache.get(l); + private Map getResourceCache(Locale l) { + Map values = resourceCache.get(l); if (values == null) { - values = new HashMap(); + values = new HashMap(); for (int i=resourceBundles.size()-1; i >= 0; i--) { - String bundleName = (String)resourceBundles.get(i); + String bundleName = resourceBundles.get(i); try { Control c = CoreResourceBundleControl.getRBControlInstance(bundleName); ResourceBundle b; @@ -751,7 +751,7 @@ public class UIDefaults extends Hashtable Object cl = get("ClassLoader"); ClassLoader uiClassLoader = (cl != null) ? (ClassLoader)cl : target.getClass().getClassLoader(); - Class uiClass = getUIClass(target.getUIClassID(), uiClassLoader); + Class uiClass = getUIClass(target.getUIClassID(), uiClassLoader); Object uiObject = null; if (uiClass == null) { @@ -761,8 +761,7 @@ public class UIDefaults extends Hashtable try { Method m = (Method)get(uiClass); if (m == null) { - Class acClass = javax.swing.JComponent.class; - m = uiClass.getMethod("createUI", new Class[]{acClass}); + m = uiClass.getMethod("createUI", new Class[]{JComponent.class}); put(uiClass, m); } uiObject = MethodUtil.invoke(m, null, new Object[]{target}); @@ -862,7 +861,7 @@ public class UIDefaults extends Hashtable return; } if( resourceBundles == null ) { - resourceBundles = new Vector(5); + resourceBundles = new Vector(5); } if (!resourceBundles.contains(bundleName)) { resourceBundles.add( bundleName ); @@ -1064,7 +1063,7 @@ public class UIDefaults extends Hashtable className = c; methodName = m; if (o != null) { - args = (Object[])o.clone(); + args = o.clone(); } } @@ -1079,10 +1078,10 @@ public class UIDefaults extends Hashtable // In order to pick up the security policy in effect at the // time of creation we use a doPrivileged with the // AccessControlContext that was in place when this was created. - return AccessController.doPrivileged(new PrivilegedAction() { + return AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { - Class c; + Class c; Object cl; // See if we should use a separate ClassLoader if (table == null || !((cl = table.get("ClassLoader")) diff --git a/src/share/classes/javax/swing/UIManager.java b/src/share/classes/javax/swing/UIManager.java index 6fb543d33..a4a62f009 100644 --- a/src/share/classes/javax/swing/UIManager.java +++ b/src/share/classes/javax/swing/UIManager.java @@ -191,7 +191,7 @@ public class UIManager implements Serializable MultiUIDefaults multiUIDefaults = new MultiUIDefaults(tables); LookAndFeel lookAndFeel; LookAndFeel multiLookAndFeel = null; - Vector auxLookAndFeels = null; + Vector auxLookAndFeels = null; SwingPropertyChangeSupport changeSupport; UIDefaults getLookAndFeelDefaults() { return tables[0]; } @@ -378,7 +378,7 @@ public class UIManager implements Serializable private static LookAndFeelInfo[] installedLAFs; static { - ArrayList iLAFs = new ArrayList(4); + ArrayList iLAFs = new ArrayList(4); iLAFs.add(new LookAndFeelInfo( "Metal", "javax.swing.plaf.metal.MetalLookAndFeel")); iLAFs.add(new LookAndFeelInfo("CDE/Motif", @@ -400,8 +400,7 @@ public class UIManager implements Serializable iLAFs.add(new LookAndFeelInfo("GTK+", "com.sun.java.swing.plaf.gtk.GTKLookAndFeel")); } - installedLAFs = (LookAndFeelInfo[])iLAFs.toArray( - new LookAndFeelInfo[iLAFs.size()]); + installedLAFs = iLAFs.toArray(new LookAndFeelInfo[iLAFs.size()]); } @@ -640,7 +639,7 @@ public class UIManager implements Serializable * @see #getSystemLookAndFeelClassName */ public static String getCrossPlatformLookAndFeelClassName() { - String laf = (String)AccessController.doPrivileged( + String laf = AccessController.doPrivileged( new GetPropertyAction("swing.crossplatformlaf")); if (laf != null) { return laf; @@ -1079,9 +1078,9 @@ public class UIManager implements Serializable // for that. return; } - Vector v = getLAFState().auxLookAndFeels; + Vector v = getLAFState().auxLookAndFeels; if (v == null) { - v = new Vector(); + v = new Vector(); } if (!v.contains(laf)) { @@ -1115,7 +1114,7 @@ public class UIManager implements Serializable boolean result; - Vector v = getLAFState().auxLookAndFeels; + Vector v = getLAFState().auxLookAndFeels; if ((v == null) || (v.size() == 0)) { return false; } @@ -1151,14 +1150,14 @@ public class UIManager implements Serializable static public LookAndFeel[] getAuxiliaryLookAndFeels() { maybeInitialize(); - Vector v = getLAFState().auxLookAndFeels; + Vector v = getLAFState().auxLookAndFeels; if ((v == null) || (v.size() == 0)) { return null; } else { LookAndFeel[] rv = new LookAndFeel[v.size()]; for (int i = 0; i < rv.length; i++) { - rv[i] = (LookAndFeel)v.elementAt(i); + rv[i] = v.elementAt(i); } return rv; } @@ -1225,7 +1224,7 @@ public class UIManager implements Serializable final Properties props = new Properties(); java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { + new java.security.PrivilegedAction() { public Object run() { try { File file = new File(makeSwingPropertiesFilename()); @@ -1284,7 +1283,7 @@ public class UIManager implements Serializable * property. For example given "swing.installedlafs=motif,windows" * lafs = {"motif", "windows"}. */ - Vector lafs = new Vector(); + Vector lafs = new Vector(); StringTokenizer st = new StringTokenizer(ilafsString, ",", false); while (st.hasMoreTokens()) { lafs.addElement(st.nextToken()); @@ -1294,9 +1293,8 @@ public class UIManager implements Serializable * list. If they both exist then add a LookAndFeelInfo to * the installedLafs array. */ - Vector ilafs = new Vector(lafs.size()); - for(int i = 0; i < lafs.size(); i++) { - String laf = (String)lafs.elementAt(i); + Vector ilafs = new Vector(lafs.size()); + for (String laf : lafs) { String name = swingProps.getProperty(makeInstalledLAFKey(laf, "name"), laf); String cls = swingProps.getProperty(makeInstalledLAFKey(laf, "class")); if (cls != null) { @@ -1306,7 +1304,7 @@ public class UIManager implements Serializable installedLAFs = new LookAndFeelInfo[ilafs.size()]; for(int i = 0; i < ilafs.size(); i++) { - installedLAFs[i] = (LookAndFeelInfo)(ilafs.elementAt(i)); + installedLAFs[i] = ilafs.elementAt(i); } } @@ -1350,7 +1348,7 @@ public class UIManager implements Serializable return; } - Vector auxLookAndFeels = new Vector(); + Vector auxLookAndFeels = new Vector(); StringTokenizer p = new StringTokenizer(auxLookAndFeelNames,","); String factoryName; @@ -1451,7 +1449,7 @@ public class UIManager implements Serializable Component c = e.getComponent(); if ((!(c instanceof JComponent) || - (c != null && !((JComponent)c).isEnabled())) && + (c != null && !c.isEnabled())) && JComponent.KeyboardState.shouldProcess(e) && SwingUtilities.processKeyBindings(e)) { e.consume(); diff --git a/src/share/classes/javax/swing/filechooser/FileSystemView.java b/src/share/classes/javax/swing/filechooser/FileSystemView.java index 855a35757..c2ff7bb8d 100644 --- a/src/share/classes/javax/swing/filechooser/FileSystemView.java +++ b/src/share/classes/javax/swing/filechooser/FileSystemView.java @@ -136,8 +136,8 @@ public abstract class FileSystemView { } File[] roots = getRoots(); - for (int i = 0; i < roots.length; i++) { - if (roots[i].equals(f)) { + for (File root : roots) { + if (root.equals(f)) { return true; } } @@ -252,8 +252,8 @@ public abstract class FileSystemView { return true; } File[] children = getFiles(folder, false); - for (int i = 0; i < children.length; i++) { - if (file.equals(children[i])) { + for (File child : children) { + if (file.equals(child)) { return true; } } @@ -276,9 +276,9 @@ public abstract class FileSystemView { public File getChild(File parent, String fileName) { if (parent instanceof ShellFolder) { File[] children = getFiles(parent, false); - for (int i = 0; i < children.length; i++) { - if (children[i].getName().equals(fileName)) { - return children[i]; + for (File child : children) { + if (child.getName().equals(fileName)) { + return child; } } } @@ -444,7 +444,7 @@ public abstract class FileSystemView { * Gets the list of shown (i.e. not hidden) files. */ public File[] getFiles(File dir, boolean useFileHiding) { - Vector files = new Vector(); + Vector files = new Vector(); // add all files in dir @@ -483,7 +483,7 @@ public abstract class FileSystemView { } } - return (File[])files.toArray(new File[files.size()]); + return files.toArray(new File[files.size()]); } @@ -590,7 +590,7 @@ class UnixFileSystemView extends FileSystemView { if(containingDir == null) { throw new IOException("Containing directory is null:"); } - File newFolder = null; + File newFolder; // Unix - using OpenWindows' default folder name. Can't find one for Motif/CDE. newFolder = createFileObject(containingDir, newFolderString); int i = 1; @@ -614,11 +614,7 @@ class UnixFileSystemView extends FileSystemView { } public boolean isDrive(File dir) { - if (isFloppyDrive(dir)) { - return true; - } else { - return false; - } + return isFloppyDrive(dir); } public boolean isFloppyDrive(File dir) { @@ -700,9 +696,8 @@ class WindowsFileSystemView extends FileSystemView { if(containingDir == null) { throw new IOException("Containing directory is null:"); } - File newFolder = null; // Using NT's default folder name - newFolder = createFileObject(containingDir, newFolderString); + File newFolder = createFileObject(containingDir, newFolderString); int i = 2; while (newFolder.exists() && (i < 100)) { newFolder = createFileObject(containingDir, MessageFormat.format( @@ -770,9 +765,8 @@ class GenericFileSystemView extends FileSystemView { if(containingDir == null) { throw new IOException("Containing directory is null:"); } - File newFolder = null; // Using NT's default folder name - newFolder = createFileObject(containingDir, newFolderString); + File newFolder = createFileObject(containingDir, newFolderString); if(newFolder.exists()) { throw new IOException("Directory already exists:" + newFolder.getAbsolutePath()); diff --git a/src/share/classes/javax/swing/table/AbstractTableModel.java b/src/share/classes/javax/swing/table/AbstractTableModel.java index 4a9474cb2..9d6f4cafd 100644 --- a/src/share/classes/javax/swing/table/AbstractTableModel.java +++ b/src/share/classes/javax/swing/table/AbstractTableModel.java @@ -176,8 +176,7 @@ public abstract class AbstractTableModel implements TableModel, Serializable * @since 1.4 */ public TableModelListener[] getTableModelListeners() { - return (TableModelListener[])listenerList.getListeners( - TableModelListener.class); + return listenerList.getListeners(TableModelListener.class); } // diff --git a/src/share/classes/javax/swing/table/DefaultTableModel.java b/src/share/classes/javax/swing/table/DefaultTableModel.java index c2e97a9a2..0a63bfa73 100644 --- a/src/share/classes/javax/swing/table/DefaultTableModel.java +++ b/src/share/classes/javax/swing/table/DefaultTableModel.java @@ -681,9 +681,9 @@ public class DefaultTableModel extends AbstractTableModel implements Serializabl if (anArray == null) { return null; } - Vector v = new Vector(anArray.length); - for (int i=0; i < anArray.length; i++) { - v.addElement(anArray[i]); + Vector v = new Vector(anArray.length); + for (Object o : anArray) { + v.addElement(o); } return v; } @@ -698,9 +698,9 @@ public class DefaultTableModel extends AbstractTableModel implements Serializabl if (anArray == null) { return null; } - Vector v = new Vector(anArray.length); - for (int i=0; i < anArray.length; i++) { - v.addElement(convertToVector(anArray[i])); + Vector v = new Vector(anArray.length); + for (Object[] o : anArray) { + v.addElement(convertToVector(o)); } return v; } diff --git a/src/share/classes/javax/swing/tree/DefaultTreeCellEditor.java b/src/share/classes/javax/swing/tree/DefaultTreeCellEditor.java index de8df158f..b0acd9be7 100644 --- a/src/share/classes/javax/swing/tree/DefaultTreeCellEditor.java +++ b/src/share/classes/javax/swing/tree/DefaultTreeCellEditor.java @@ -551,7 +551,7 @@ public class DefaultTreeCellEditor implements ActionListener, TreeCellEditor, // Serialization support. private void writeObject(ObjectOutputStream s) throws IOException { - Vector values = new Vector(); + Vector values = new Vector(); s.defaultWriteObject(); // Save the realEditor, if its Serializable. diff --git a/src/share/classes/javax/swing/tree/DefaultTreeModel.java b/src/share/classes/javax/swing/tree/DefaultTreeModel.java index a74444677..f4e8b14f2 100644 --- a/src/share/classes/javax/swing/tree/DefaultTreeModel.java +++ b/src/share/classes/javax/swing/tree/DefaultTreeModel.java @@ -453,8 +453,7 @@ public class DefaultTreeModel implements Serializable, TreeModel { * @since 1.4 */ public TreeModelListener[] getTreeModelListeners() { - return (TreeModelListener[])listenerList.getListeners( - TreeModelListener.class); + return listenerList.getListeners(TreeModelListener.class); } /** @@ -652,7 +651,7 @@ public class DefaultTreeModel implements Serializable, TreeModel { // Serialization support. private void writeObject(ObjectOutputStream s) throws IOException { - Vector values = new Vector(); + Vector values = new Vector(); s.defaultWriteObject(); // Save the root, if its Serializable. diff --git a/src/share/classes/javax/swing/tree/DefaultTreeSelectionModel.java b/src/share/classes/javax/swing/tree/DefaultTreeSelectionModel.java index 6f84e169f..2eb24e846 100644 --- a/src/share/classes/javax/swing/tree/DefaultTreeSelectionModel.java +++ b/src/share/classes/javax/swing/tree/DefaultTreeSelectionModel.java @@ -61,7 +61,7 @@ import javax.swing.DefaultListSelectionModel; * * @author Scott Violet */ -public class DefaultTreeSelectionModel extends Object implements Cloneable, Serializable, TreeSelectionModel +public class DefaultTreeSelectionModel implements Cloneable, Serializable, TreeSelectionModel { /** Property name for selectionMode. */ public static final String SELECTION_MODE_PROPERTY = "selectionMode"; @@ -98,8 +98,8 @@ public class DefaultTreeSelectionModel extends Object implements Cloneable, Seri /** Used to make sure the paths are unique, will contain all the paths * in selection. */ - private Hashtable uniquePaths; - private Hashtable lastPaths; + private Hashtable uniquePaths; + private Hashtable lastPaths; private TreePath[] tempPaths; @@ -111,8 +111,8 @@ public class DefaultTreeSelectionModel extends Object implements Cloneable, Seri listSelectionModel = new DefaultListSelectionModel(); selectionMode = DISCONTIGUOUS_TREE_SELECTION; leadIndex = leadRow = -1; - uniquePaths = new Hashtable(); - lastPaths = new Hashtable(); + uniquePaths = new Hashtable(); + lastPaths = new Hashtable(); tempPaths = new TreePath[1]; } @@ -245,7 +245,7 @@ public class DefaultTreeSelectionModel extends Object implements Cloneable, Seri } TreePath beginLeadPath = leadPath; - Vector cPaths = new Vector(newCount + oldCount); + Vector cPaths = new Vector(newCount + oldCount); List newSelectionAsList = new ArrayList(newCount); @@ -276,7 +276,7 @@ public class DefaultTreeSelectionModel extends Object implements Cloneable, Seri selection = newSelection; - Hashtable tempHT = uniquePaths; + Hashtable tempHT = uniquePaths; uniquePaths = lastPaths; lastPaths = tempHT; @@ -348,7 +348,7 @@ public class DefaultTreeSelectionModel extends Object implements Cloneable, Seri int counter, validCount; int oldCount; TreePath beginLeadPath = leadPath; - Vector cPaths = null; + Vector cPaths = null; if(selection == null) oldCount = 0; @@ -363,7 +363,7 @@ public class DefaultTreeSelectionModel extends Object implements Cloneable, Seri if (uniquePaths.get(paths[counter]) == null) { validCount++; if(cPaths == null) - cPaths = new Vector(); + cPaths = new Vector(); cPaths.addElement(new PathPlaceHolder (paths[counter], true)); uniquePaths.put(paths[counter], Boolean.TRUE); @@ -388,12 +388,11 @@ public class DefaultTreeSelectionModel extends Object implements Cloneable, Seri if(validCount != paths.length) { /* Some of the paths in paths are already in the selection. */ - Enumeration newPaths = lastPaths.keys(); + Enumeration newPaths = lastPaths.keys(); counter = oldCount; while (newPaths.hasMoreElements()) { - newSelection[counter++] = (TreePath)newPaths. - nextElement(); + newSelection[counter++] = newPaths.nextElement(); } } else { @@ -448,7 +447,7 @@ public class DefaultTreeSelectionModel extends Object implements Cloneable, Seri clearSelection(); } else { - Vector pathsToRemove = null; + Vector pathsToRemove = null; /* Find the paths that can be removed. */ for (int removeCounter = paths.length - 1; removeCounter >= 0; @@ -456,7 +455,7 @@ public class DefaultTreeSelectionModel extends Object implements Cloneable, Seri if(paths[removeCounter] != null) { if (uniquePaths.get(paths[removeCounter]) != null) { if(pathsToRemove == null) - pathsToRemove = new Vector(paths.length); + pathsToRemove = new Vector(paths.length); uniquePaths.remove(paths[removeCounter]); pathsToRemove.addElement(new PathPlaceHolder (paths[removeCounter], false)); @@ -471,14 +470,13 @@ public class DefaultTreeSelectionModel extends Object implements Cloneable, Seri selection = null; } else { - Enumeration pEnum = uniquePaths.keys(); + Enumeration pEnum = uniquePaths.keys(); int validCount = 0; selection = new TreePath[selection.length - removeCount]; while (pEnum.hasMoreElements()) { - selection[validCount++] = (TreePath)pEnum. - nextElement(); + selection[validCount++] = pEnum.nextElement(); } } if (leadPath != null && @@ -613,8 +611,7 @@ public class DefaultTreeSelectionModel extends Object implements Cloneable, Seri * @since 1.4 */ public TreeSelectionListener[] getTreeSelectionListeners() { - return (TreeSelectionListener[])listenerList.getListeners( - TreeSelectionListener.class); + return listenerList.getListeners(TreeSelectionListener.class); } /** @@ -1081,7 +1078,7 @@ public class DefaultTreeSelectionModel extends Object implements Cloneable, Seri PathPlaceHolder placeholder; for(int counter = 0; counter < cPathCount; counter++) { - placeholder = (PathPlaceHolder)changedPaths.elementAt(counter); + placeholder = changedPaths.elementAt(counter); newness[counter] = placeholder.isNew; paths[counter] = placeholder.path; } @@ -1177,8 +1174,8 @@ public class DefaultTreeSelectionModel extends Object implements Cloneable, Seri clone.listenerList = new EventListenerList(); clone.listSelectionModel = (DefaultListSelectionModel) listSelectionModel.clone(); - clone.uniquePaths = new Hashtable(); - clone.lastPaths = new Hashtable(); + clone.uniquePaths = new Hashtable(); + clone.lastPaths = new Hashtable(); clone.tempPaths = new TreePath[1]; return clone; } diff --git a/src/share/classes/javax/swing/tree/FixedHeightLayoutCache.java b/src/share/classes/javax/swing/tree/FixedHeightLayoutCache.java index 37f805ae0..57b813bc7 100644 --- a/src/share/classes/javax/swing/tree/FixedHeightLayoutCache.java +++ b/src/share/classes/javax/swing/tree/FixedHeightLayoutCache.java @@ -64,21 +64,21 @@ public class FixedHeightLayoutCache extends AbstractLayoutCache { /** * Maps from TreePath to a FHTreeStateNode. */ - private Hashtable treePathMapping; + private Hashtable treePathMapping; /** * Used for getting path/row information. */ private SearchInfo info; - private Stack tempStacks; + private Stack> tempStacks; public FixedHeightLayoutCache() { super(); - tempStacks = new Stack(); + tempStacks = new Stack>(); boundsBuffer = new Rectangle(); - treePathMapping = new Hashtable(); + treePathMapping = new Hashtable(); info = new SearchInfo(); setRowHeight(1); } @@ -592,7 +592,7 @@ public class FixedHeightLayoutCache extends AbstractLayoutCache { * return null, if you to create a node use getNodeForPath. */ private FHTreeStateNode getMapping(TreePath path) { - return (FHTreeStateNode)treePathMapping.get(path); + return treePathMapping.get(path); } /** @@ -695,13 +695,13 @@ public class FixedHeightLayoutCache extends AbstractLayoutCache { return null; // Check all the parent paths, until a match is found. - Stack paths; + Stack paths; if(tempStacks.size() == 0) { - paths = new Stack(); + paths = new Stack(); } else { - paths = (Stack)tempStacks.pop(); + paths = tempStacks.pop(); } try { @@ -714,7 +714,7 @@ public class FixedHeightLayoutCache extends AbstractLayoutCache { // Found a match, create entries for all paths in // paths. while(node != null && paths.size() > 0) { - path = (TreePath)paths.pop(); + path = paths.pop(); node = node.createChildFor(path. getLastPathComponent()); } diff --git a/src/share/classes/javax/swing/tree/VariableHeightLayoutCache.java b/src/share/classes/javax/swing/tree/VariableHeightLayoutCache.java index 5861dd95b..cdf0a1a73 100644 --- a/src/share/classes/javax/swing/tree/VariableHeightLayoutCache.java +++ b/src/share/classes/javax/swing/tree/VariableHeightLayoutCache.java @@ -56,7 +56,7 @@ public class VariableHeightLayoutCache extends AbstractLayoutCache { * The array of nodes that are currently visible, in the order they * are displayed. */ - private Vector visibleNodes; + private Vector visibleNodes; /** * This is set to true if one of the entries has an invalid size. @@ -79,20 +79,20 @@ public class VariableHeightLayoutCache extends AbstractLayoutCache { /** * Maps from TreePath to a TreeStateNode. */ - private Hashtable treePathMapping; + private Hashtable treePathMapping; /** * A stack of stacks. */ - private Stack tempStacks; + private Stack> tempStacks; public VariableHeightLayoutCache() { super(); - tempStacks = new Stack(); - visibleNodes = new Vector(); + tempStacks = new Stack>(); + visibleNodes = new Vector(); boundsBuffer = new Rectangle(); - treePathMapping = new Hashtable(); + treePathMapping = new Hashtable(); } /** @@ -704,7 +704,7 @@ public class VariableHeightLayoutCache extends AbstractLayoutCache { * return null, if you to create a node use getNodeForPath. */ private TreeStateNode getMapping(TreePath path) { - return (TreeStateNode)treePathMapping.get(path); + return treePathMapping.get(path); } /** @@ -824,13 +824,13 @@ public class VariableHeightLayoutCache extends AbstractLayoutCache { } // Check all the parent paths, until a match is found. - Stack paths; + Stack paths; if(tempStacks.size() == 0) { - paths = new Stack(); + paths = new Stack(); } else { - paths = (Stack)tempStacks.pop(); + paths = tempStacks.pop(); } try { @@ -843,7 +843,7 @@ public class VariableHeightLayoutCache extends AbstractLayoutCache { // Found a match, create entries for all paths in // paths. while(node != null && paths.size() > 0) { - path = (TreePath)paths.pop(); + path = paths.pop(); node.getLoadedChildren(shouldCreate); int childIndex = treeModel. diff --git a/src/share/classes/javax/swing/undo/StateEdit.java b/src/share/classes/javax/swing/undo/StateEdit.java index 675afb397..554ed0537 100644 --- a/src/share/classes/javax/swing/undo/StateEdit.java +++ b/src/share/classes/javax/swing/undo/StateEdit.java @@ -116,7 +116,7 @@ public class StateEdit protected void init (StateEditable anObject, String name) { this.object = anObject; - this.preState = new Hashtable(11); + this.preState = new Hashtable(11); this.object.storeState(this.preState); this.postState = null; this.undoRedoName = name; @@ -133,7 +133,7 @@ public class StateEdit * ends the edit. */ public void end() { - this.postState = new Hashtable(11); + this.postState = new Hashtable(11); this.object.storeState(this.postState); this.removeRedundantState(); } @@ -170,7 +170,7 @@ public class StateEdit * Remove redundant key/values in state hashtables. */ protected void removeRedundantState() { - Vector uselessKeys = new Vector(); + Vector uselessKeys = new Vector(); Enumeration myKeys = preState.keys(); // Locate redundant state diff --git a/src/share/classes/javax/swing/undo/UndoManager.java b/src/share/classes/javax/swing/undo/UndoManager.java index 88c300c94..fb1dcaf8e 100644 --- a/src/share/classes/javax/swing/undo/UndoManager.java +++ b/src/share/classes/javax/swing/undo/UndoManager.java @@ -166,12 +166,10 @@ public class UndoManager extends CompoundEdit implements UndoableEditListener { * @see AbstractUndoableEdit#die */ public synchronized void discardAllEdits() { - Enumeration cursor = edits.elements(); - while (cursor.hasMoreElements()) { - UndoableEdit e = (UndoableEdit)cursor.nextElement(); + for (UndoableEdit e : edits) { e.die(); } - edits = new Vector(); + edits = new Vector(); indexOfNextAdd = 0; // PENDING(rjrjr) when vector grows a removeRange() method // (expected in JDK 1.2), trimEdits() will be nice and @@ -240,7 +238,7 @@ public class UndoManager extends CompoundEdit implements UndoableEditListener { // System.out.println("Trimming " + from + " " + to + " with index " + // indexOfNextAdd); for (int i = to; from <= i; i--) { - UndoableEdit e = (UndoableEdit)edits.elementAt(i); + UndoableEdit e = edits.elementAt(i); // System.out.println("JUM: Discarding " + // e.getUndoPresentationName()); e.die(); @@ -293,7 +291,7 @@ public class UndoManager extends CompoundEdit implements UndoableEditListener { protected UndoableEdit editToBeUndone() { int i = indexOfNextAdd; while (i > 0) { - UndoableEdit edit = (UndoableEdit)edits.elementAt(--i); + UndoableEdit edit = edits.elementAt(--i); if (edit.isSignificant()) { return edit; } @@ -314,7 +312,7 @@ public class UndoManager extends CompoundEdit implements UndoableEditListener { int i = indexOfNextAdd; while (i < count) { - UndoableEdit edit = (UndoableEdit)edits.elementAt(i++); + UndoableEdit edit = edits.elementAt(i++); if (edit.isSignificant()) { return edit; } @@ -333,7 +331,7 @@ public class UndoManager extends CompoundEdit implements UndoableEditListener { protected void undoTo(UndoableEdit edit) throws CannotUndoException { boolean done = false; while (!done) { - UndoableEdit next = (UndoableEdit)edits.elementAt(--indexOfNextAdd); + UndoableEdit next = edits.elementAt(--indexOfNextAdd); next.undo(); done = next == edit; } @@ -349,7 +347,7 @@ public class UndoManager extends CompoundEdit implements UndoableEditListener { protected void redoTo(UndoableEdit edit) throws CannotRedoException { boolean done = false; while (!done) { - UndoableEdit next = (UndoableEdit)edits.elementAt(indexOfNextAdd++); + UndoableEdit next = edits.elementAt(indexOfNextAdd++); next.redo(); done = next == edit; } diff --git a/src/share/classes/javax/swing/undo/UndoableEditSupport.java b/src/share/classes/javax/swing/undo/UndoableEditSupport.java index 0d1d7b9f0..7b791bf67 100644 --- a/src/share/classes/javax/swing/undo/UndoableEditSupport.java +++ b/src/share/classes/javax/swing/undo/UndoableEditSupport.java @@ -89,8 +89,7 @@ public class UndoableEditSupport { * @since 1.4 */ public synchronized UndoableEditListener[] getUndoableEditListeners() { - return (UndoableEditListener[])(listeners.toArray( - new UndoableEditListener[0])); + return listeners.toArray(new UndoableEditListener[0]); } /** diff --git a/src/share/classes/sun/swing/AccessibleMethod.java b/src/share/classes/sun/swing/AccessibleMethod.java index 420ca321f..97b02ea7a 100644 --- a/src/share/classes/sun/swing/AccessibleMethod.java +++ b/src/share/classes/sun/swing/AccessibleMethod.java @@ -114,7 +114,7 @@ public class AccessibleMethod { /** The action used to fetch the method and make it accessible */ private static class AccessMethodAction implements PrivilegedExceptionAction { - private final Class klass; + private final Class klass; private final String methodName; private final Class[] paramTypes; diff --git a/src/share/classes/sun/swing/FilePane.java b/src/share/classes/sun/swing/FilePane.java index 7c862cb78..fd5a43086 100644 --- a/src/share/classes/sun/swing/FilePane.java +++ b/src/share/classes/sun/swing/FilePane.java @@ -546,8 +546,7 @@ public class FilePane extends JPanel implements PropertyChangeListener { public static void addActionsToMap(ActionMap map, Action[] actions) { if (map != null && actions != null) { - for (int i = 0; i < actions.length; i++) { - Action a = actions[i]; + for (Action a : actions) { String cmd = (String)a.getValue(Action.ACTION_COMMAND_KEY); if (cmd == null) { cmd = (String)a.getValue(Action.NAME); @@ -715,13 +714,13 @@ public class FilePane extends JPanel implements PropertyChangeListener { visibleColumns.toArray(columns); columnMap = Arrays.copyOf(columnMap, columns.length); - List sortKeys = + List sortKeys = (rowSorter == null) ? null : rowSorter.getSortKeys(); fireTableStructureChanged(); restoreSortKeys(sortKeys); } - private void restoreSortKeys(List sortKeys) { + private void restoreSortKeys(List sortKeys) { if (sortKeys != null) { // check if preserved sortKeys are valid for this folder for (int i = 0; i < sortKeys.size(); i++) { @@ -886,7 +885,7 @@ public class FilePane extends JPanel implements PropertyChangeListener { return rowSorter; } - private class DetailsTableRowSorter extends TableRowSorter { + private class DetailsTableRowSorter extends TableRowSorter { public DetailsTableRowSorter() { setModelWrapper(new SorterModelWrapper()); } @@ -906,8 +905,8 @@ public class FilePane extends JPanel implements PropertyChangeListener { updateComparators(detailsTableModel.getColumns()); } - private class SorterModelWrapper extends ModelWrapper { - public Object getModel() { + private class SorterModelWrapper extends ModelWrapper { + public TableModel getModel() { return getDetailsTableModel(); } @@ -923,7 +922,7 @@ public class FilePane extends JPanel implements PropertyChangeListener { return FilePane.this.getModel().getElementAt(row); } - public Object getIdentifier(int row) { + public Integer getIdentifier(int row) { return row; } } @@ -1718,9 +1717,9 @@ public class FilePane extends JPanel implements PropertyChangeListener { private void updateViewMenu() { if (viewMenu != null) { Component[] comps = viewMenu.getMenuComponents(); - for (int i = 0; i < comps.length; i++) { - if (comps[i] instanceof JRadioButtonMenuItem) { - JRadioButtonMenuItem mi = (JRadioButtonMenuItem)comps[i]; + for (Component comp : comps) { + if (comp instanceof JRadioButtonMenuItem) { + JRadioButtonMenuItem mi = (JRadioButtonMenuItem) comp; if (((ViewTypeAction)mi.getAction()).viewType == viewType) { mi.setSelected(true); } diff --git a/src/share/classes/sun/swing/SwingLazyValue.java b/src/share/classes/sun/swing/SwingLazyValue.java index 6aabcf66e..f3b6b9e88 100644 --- a/src/share/classes/sun/swing/SwingLazyValue.java +++ b/src/share/classes/sun/swing/SwingLazyValue.java @@ -54,15 +54,14 @@ public class SwingLazyValue implements UIDefaults.LazyValue { className = c; methodName = m; if (o != null) { - args = (Object[])o.clone(); + args = o.clone(); } } public Object createValue(final UIDefaults table) { try { - Class c; Object cl; - c = Class.forName(className, true, null); + Class c = Class.forName(className, true, null); if (methodName != null) { Class[] types = getClassArray(args); Method m = c.getMethod(methodName, types); diff --git a/src/share/classes/sun/swing/SwingUtilities2.java b/src/share/classes/sun/swing/SwingUtilities2.java index f0165be3e..0603e55b5 100644 --- a/src/share/classes/sun/swing/SwingUtilities2.java +++ b/src/share/classes/sun/swing/SwingUtilities2.java @@ -460,7 +460,7 @@ public class SwingUtilities2 { return clipString; } - boolean needsTextLayout = false; + boolean needsTextLayout; synchronized (charsBufferLock) { if (charsBuffer == null || charsBuffer.length < stringLength) { @@ -715,11 +715,8 @@ public class SwingUtilities2 { // See if coords are inside // ASSUME: mouse x,y will never be < cell's x,y assert (p.x >= cellBounds.x && p.y >= cellBounds.y); - if (p.x > cellBounds.x + cellBounds.width || - p.y > cellBounds.y + cellBounds.height) { - return true; - } - return false; + return p.x > cellBounds.x + cellBounds.width || + p.y > cellBounds.y + cellBounds.height; } /** @@ -1239,12 +1236,11 @@ public class SwingUtilities2 { private static synchronized boolean inputEvent_canAccessSystemClipboard(InputEvent ie) { if (inputEvent_CanAccessSystemClipboard_Field == null) { inputEvent_CanAccessSystemClipboard_Field = - (Field)AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public Object run() { - Field field = null; + AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Field run() { try { - field = InputEvent.class. + Field field = InputEvent.class. getDeclaredField("canAccessSystemClipboard"); field.setAccessible(true); return field; @@ -1419,10 +1415,10 @@ public class SwingUtilities2 { * Class.getResourceAsStream just returns raw * bytes, which we can convert to an image. */ - byte[] buffer = (byte[]) + byte[] buffer = java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public Object run() { + new java.security.PrivilegedAction() { + public byte[] run() { try { InputStream resource = null; Class srchClass = baseClass; @@ -1489,7 +1485,7 @@ public class SwingUtilities2 { return true; } // Else probably Solaris or Linux in which case may be remote X11 - Class x11Class = Class.forName("sun.awt.X11GraphicsEnvironment"); + Class x11Class = Class.forName("sun.awt.X11GraphicsEnvironment"); Method isDisplayLocalMethod = x11Class.getMethod( "isDisplayLocal", new Class[0]); return (Boolean)isDisplayLocalMethod.invoke(null, (Object[])null); -- GitLab From 0b15e286b34af6af065e49ccd2eb8f95055d7c95 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Wed, 27 Aug 2008 11:03:59 +0200 Subject: [PATCH 068/139] 5041784: (reflect) generic signature methods needlessly return generic arrays Reviewed-by: darcy --- .../factory/CoreReflectionFactory.java | 6 +- .../Generics/TestPlainArrayNotGeneric.java | 156 ++++++++++++++++++ 2 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 test/java/lang/reflect/Generics/TestPlainArrayNotGeneric.java diff --git a/src/share/classes/sun/reflect/generics/factory/CoreReflectionFactory.java b/src/share/classes/sun/reflect/generics/factory/CoreReflectionFactory.java index e3972325d..54c0908ca 100644 --- a/src/share/classes/sun/reflect/generics/factory/CoreReflectionFactory.java +++ b/src/share/classes/sun/reflect/generics/factory/CoreReflectionFactory.java @@ -25,6 +25,7 @@ package sun.reflect.generics.factory; +import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.GenericDeclaration; import java.lang.reflect.Method; @@ -118,7 +119,10 @@ public class CoreReflectionFactory implements GenericsFactory { } public Type makeArrayType(Type componentType){ - return GenericArrayTypeImpl.make(componentType); + if (componentType instanceof Class) + return Array.newInstance((Class) componentType, 0).getClass(); + else + return GenericArrayTypeImpl.make(componentType); } public Type makeByte(){return byte.class;} diff --git a/test/java/lang/reflect/Generics/TestPlainArrayNotGeneric.java b/test/java/lang/reflect/Generics/TestPlainArrayNotGeneric.java new file mode 100644 index 000000000..63cb15a0c --- /dev/null +++ b/test/java/lang/reflect/Generics/TestPlainArrayNotGeneric.java @@ -0,0 +1,156 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 5041784 + * @summary Check that plain arrays like String[] are never represented as + * GenericArrayType. + * @author Eamonn McManus + */ + +import java.lang.reflect.Constructor; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class TestPlainArrayNotGeneric { + public String[] m1(List p1) {return null;} + public List m2(String[] p1) {return null;} + public void m3(List p1, String[] p2) {} + public void m4(List p1) {} + public TestPlainArrayNotGeneric(List p1) {} + public TestPlainArrayNotGeneric(List p1, String[] p2) {} + + public > T m5(T p1) {return null;} + public T[] m6(T[] p1, List p2) {return null;} + + public List m6(List p1) {return null;} + public > T m7(T[] p1) {return null;} + public List m8(List p1) {return null;} + public > T[] m9(T[] p1) {return null;} + + public static interface XMap extends Map, String[]> {} + public static interface YMap, V> + extends Map {} + + + private static String lastFailure; + private static int failureCount; + + public static void main(String[] args) throws Exception { + checkClass(TestPlainArrayNotGeneric.class); + + if (failureCount == 0) + System.out.println("TEST PASSED"); + else + throw new Exception("TEST FAILED: Last failure: " + lastFailure); + } + + private static void checkClass(Class c) throws Exception { + Method[] methods = c.getMethods(); + for (Method m : methods) { + check(m.getGenericReturnType(), "return type of method " + m); + check(m.getGenericParameterTypes(), "parameter", "method " + m); + check(m.getTypeParameters(), "type parameter", "method " + m); + } + + Constructor[] constructors = c.getConstructors(); + for (Constructor constr : constructors) { + check(constr.getGenericParameterTypes(), "parameter", + "constructor " + constr); + check(constr.getTypeParameters(), "type parameter", + "constructor " + constr); + } + + Class[] inners = c.getDeclaredClasses(); + for (Class inner : inners) + checkClass(inner); + } + + private static void check(Type[] types, String elementKind, String what) { + for (int i = 0; i < types.length; i++) { + Type t = types[i]; + check(t, elementKind + " " + (i+1) + " of " + what); + } + } + + private static final Set checking = new HashSet(); + + private static void check(Type t, String what) { + if (t == null || !checking.add(t)) + return; + // Avoid infinite recursion. t can be null e.g. for superclass of Object. + try { + check2(t, what); + } finally { + checking.remove(t); + } + } + + private static void check2(Type t, String what) { + if (t instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) t; + check(pt.getActualTypeArguments(), "type argument", what); + } else if (t instanceof TypeVariable) { + TypeVariable tv = (TypeVariable) t; + check(tv.getBounds(), "bound", what); + GenericDeclaration gd = tv.getGenericDeclaration(); + if (gd instanceof Type) + check((Type) gd, "declaration containing " + what); + } else if (t instanceof WildcardType) { + WildcardType wt = (WildcardType) t; + check(wt.getLowerBounds(), "lower bound", "wildcard type in " + what); + check(wt.getUpperBounds(), "upper bound", "wildcard type in " + what); + } else if (t instanceof Class) { + Class c = (Class) t; + check(c.getGenericInterfaces(), "superinterface", c.toString()); + check(c.getGenericSuperclass(), "superclass of " + c); + check(c.getTypeParameters(), "type parameter", c.toString()); + } else if (t instanceof GenericArrayType) { + GenericArrayType gat = (GenericArrayType) t; + Type comp = gat.getGenericComponentType(); + if (comp instanceof Class) { + fail("Type " + t + " uses GenericArrayType when plain " + + "array would do, in " + what); + } else + check(comp, "component type of " + what); + } else { + fail("TEST BUG: mutant Type " + t + " (a " + t.getClass().getName() + ")"); + } + } + + private static void fail(String why) { + System.out.println("FAIL: " + why); + lastFailure = why; + failureCount++; + } +} -- GitLab From 94036782003800a7a513ad46ddc1189f8fd345da Mon Sep 17 00:00:00 2001 From: ksrini Date: Tue, 26 Aug 2008 10:21:20 -0700 Subject: [PATCH 069/139] 6685121: (launcher) make ReportErrorMessages accessible by other launcher subsystems Summary: provided error reporting interfaces to other java subsystems that the launcher uses. Reviewed-by: darcy --- make/java/jli/Makefile | 6 ++- make/java/jli/mapfile-vers | 6 ++- src/share/bin/emessages.h | 4 +- src/share/bin/java.c | 102 ++++++++++++++++++------------------- src/share/bin/java.h | 20 +++----- src/solaris/bin/java_md.c | 42 +++++++-------- src/windows/bin/java_md.c | 42 +++++++-------- 7 files changed, 113 insertions(+), 109 deletions(-) diff --git a/make/java/jli/Makefile b/make/java/jli/Makefile index 5a1c312d7..7a0c53f1c 100644 --- a/make/java/jli/Makefile +++ b/make/java/jli/Makefile @@ -113,7 +113,11 @@ ifeq ($(PLATFORM), windows) JAVALIB = OTHER_LCF = -export:JLI_Launch \ -export:JLI_ManifestIterate \ - -export:JLI_SetTraceLauncher + -export:JLI_SetTraceLauncher \ + -export:JLI_ReportErrorMessage \ + -export:JLI_ReportErrorMessageSys \ + -export:JLI_ReportMessage \ + -export:JLI_ReportExceptionDescription endif diff --git a/make/java/jli/mapfile-vers b/make/java/jli/mapfile-vers index a9e8198de..e04e4f13d 100644 --- a/make/java/jli/mapfile-vers +++ b/make/java/jli/mapfile-vers @@ -1,5 +1,5 @@ # -# Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,10 @@ SUNWprivate_1.1 { JLI_Launch; JLI_ManifestIterate; JLI_SetTraceLauncher; + JLI_ReportErrorMessage; + JLI_ReportErrorMessageSys; + JLI_ReportMessage; + JLI_ReportExceptionDescription; local: *; }; diff --git a/src/share/bin/emessages.h b/src/share/bin/emessages.h index 03824bba5..008cc1f90 100644 --- a/src/share/bin/emessages.h +++ b/src/share/bin/emessages.h @@ -25,8 +25,8 @@ /* * This file primarily consists of all the error and warning messages, that - * are used in ReportErrorMessage. All message must be defined here, in order - * to help in I18N/L10N the messages. + * are used in JLI_ReportErrorMessage. All message must be defined here, in + * order to help with localizing the messages. */ #ifndef _EMESSAGES_H diff --git a/src/share/bin/java.c b/src/share/bin/java.c index f7cbcdc95..89ae857b1 100644 --- a/src/share/bin/java.c +++ b/src/share/bin/java.c @@ -148,7 +148,7 @@ static void ShowSplashScreen(); static jboolean IsWildCardEnabled(); #define ARG_CHECK(n, f, a) if (n < 1) { \ - ReportErrorMessage(f, a); \ + JLI_ReportErrorMessage(f, a); \ printUsage = JNI_TRUE; \ *pret = 1; \ return JNI_TRUE; \ @@ -326,15 +326,15 @@ JavaMain(void * _args) start = CounterGet(); if (!InitializeJVM(&vm, &env, &ifn)) { - ReportErrorMessage(JVM_ERROR1); + JLI_ReportErrorMessage(JVM_ERROR1); exit(1); } if (printVersion || showVersion) { PrintJavaVersion(env, showVersion); if ((*env)->ExceptionOccurred(env)) { - ReportExceptionDescription(env); - ReportErrorMessage(JNI_ERROR); + JLI_ReportExceptionDescription(env); + JLI_ReportErrorMessage(JNI_ERROR); goto leave; } if (printVersion) { @@ -347,8 +347,8 @@ JavaMain(void * _args) if (printXUsage || printUsage || (jarfile == 0 && classname == 0)) { PrintUsage(env, printXUsage); if ((*env)->ExceptionOccurred(env)) { - ReportExceptionDescription(env); - ReportErrorMessage(JNI_ERROR); + JLI_ReportExceptionDescription(env); + JLI_ReportErrorMessage(JNI_ERROR); ret=1; } goto leave; @@ -397,43 +397,43 @@ JavaMain(void * _args) if (jarfile != 0) { mainClassName = GetMainClassName(env, jarfile); if ((*env)->ExceptionOccurred(env)) { - ReportExceptionDescription(env); - ReportErrorMessage(JNI_ERROR); + JLI_ReportExceptionDescription(env); + JLI_ReportErrorMessage(JNI_ERROR); goto leave; } if (mainClassName == NULL) { - ReportErrorMessage(JAR_ERROR1,jarfile, GEN_ERROR); + JLI_ReportErrorMessage(JAR_ERROR1,jarfile, GEN_ERROR); goto leave; } classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0); if (classname == NULL) { - ReportExceptionDescription(env); - ReportErrorMessage(JNI_ERROR); + JLI_ReportExceptionDescription(env); + JLI_ReportErrorMessage(JNI_ERROR); goto leave; } mainClass = LoadClass(env, classname); if(mainClass == NULL) { /* exception occured */ - ReportExceptionDescription(env); - ReportErrorMessage(CLS_ERROR1, classname); + JLI_ReportExceptionDescription(env); + JLI_ReportErrorMessage(CLS_ERROR1, classname); goto leave; } (*env)->ReleaseStringUTFChars(env, mainClassName, classname); } else { mainClassName = NewPlatformString(env, classname); if (mainClassName == NULL) { - ReportErrorMessage(CLS_ERROR2, classname, GEN_ERROR); + JLI_ReportErrorMessage(CLS_ERROR2, classname, GEN_ERROR); goto leave; } classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0); if (classname == NULL) { - ReportExceptionDescription(env); - ReportErrorMessage(JNI_ERROR); + JLI_ReportExceptionDescription(env); + JLI_ReportErrorMessage(JNI_ERROR); goto leave; } mainClass = LoadClass(env, classname); if(mainClass == NULL) { /* exception occured */ - ReportExceptionDescription(env); - ReportErrorMessage(CLS_ERROR1, classname); + JLI_ReportExceptionDescription(env); + JLI_ReportErrorMessage(CLS_ERROR1, classname); goto leave; } (*env)->ReleaseStringUTFChars(env, mainClassName, classname); @@ -444,10 +444,10 @@ JavaMain(void * _args) "([Ljava/lang/String;)V"); if (mainID == NULL) { if ((*env)->ExceptionOccurred(env)) { - ReportExceptionDescription(env); - ReportErrorMessage(JNI_ERROR); + JLI_ReportExceptionDescription(env); + JLI_ReportErrorMessage(JNI_ERROR); } else { - ReportErrorMessage(CLS_ERROR3); + JLI_ReportErrorMessage(CLS_ERROR3); } goto leave; } @@ -459,8 +459,8 @@ JavaMain(void * _args) mainID, JNI_TRUE); if( obj == NULL) { /* exception occurred */ - ReportExceptionDescription(env); - ReportErrorMessage(JNI_ERROR); + JLI_ReportExceptionDescription(env); + JLI_ReportErrorMessage(JNI_ERROR); goto leave; } @@ -469,14 +469,14 @@ JavaMain(void * _args) (*env)->GetObjectClass(env, obj), "getModifiers", "()I"); if ((*env)->ExceptionOccurred(env)) { - ReportExceptionDescription(env); - ReportErrorMessage(JNI_ERROR); + JLI_ReportExceptionDescription(env); + JLI_ReportErrorMessage(JNI_ERROR); goto leave; } mods = (*env)->CallIntMethod(env, obj, mid); if ((mods & 1) == 0) { /* if (!Modifier.isPublic(mods)) ... */ - ReportErrorMessage(CLS_ERROR4); + JLI_ReportErrorMessage(CLS_ERROR4); goto leave; } } @@ -484,8 +484,8 @@ JavaMain(void * _args) /* Build argument array */ mainArgs = NewPlatformStringArray(env, argv, argc); if (mainArgs == NULL) { - ReportExceptionDescription(env); - ReportErrorMessage(JNI_ERROR); + JLI_ReportExceptionDescription(env); + JLI_ReportErrorMessage(JNI_ERROR); goto leave; } @@ -506,7 +506,7 @@ JavaMain(void * _args) * launcher's return code except by calling System.exit. */ if ((*vm)->DetachCurrentThread(vm) != 0) { - ReportErrorMessage(JVM_ERROR2); + JLI_ReportErrorMessage(JVM_ERROR2); ret = 1; goto leave; } @@ -635,7 +635,7 @@ CheckJvmType(int *pargc, char ***argv, jboolean speculative) { if (loopCount > knownVMsCount) { if (!speculative) { - ReportErrorMessage(CFG_ERROR1); + JLI_ReportErrorMessage(CFG_ERROR1); exit(1); } else { return "ERROR"; @@ -645,7 +645,7 @@ CheckJvmType(int *pargc, char ***argv, jboolean speculative) { if (nextIdx < 0) { if (!speculative) { - ReportErrorMessage(CFG_ERROR2, knownVMs[jvmidx].alias); + JLI_ReportErrorMessage(CFG_ERROR2, knownVMs[jvmidx].alias); exit(1); } else { return "ERROR"; @@ -660,7 +660,7 @@ CheckJvmType(int *pargc, char ***argv, jboolean speculative) { switch (knownVMs[jvmidx].flag) { case VM_WARN: if (!speculative) { - ReportErrorMessage(CFG_WARN1, jvmtype, knownVMs[0].name + 1); + JLI_ReportErrorMessage(CFG_WARN1, jvmtype, knownVMs[0].name + 1); } /* fall through */ case VM_IGNORE: @@ -670,7 +670,7 @@ CheckJvmType(int *pargc, char ***argv, jboolean speculative) { break; case VM_ERROR: if (!speculative) { - ReportErrorMessage(CFG_ERROR3, jvmtype); + JLI_ReportErrorMessage(CFG_ERROR3, jvmtype); exit(1); } else { return "ERROR"; @@ -879,9 +879,9 @@ SelectVersion(int argc, char **argv, char **main_class) if (jarflag && operand) { if ((res = JLI_ParseManifest(operand, &info)) != 0) { if (res == -1) - ReportErrorMessage(JAR_ERROR2, operand); + JLI_ReportErrorMessage(JAR_ERROR2, operand); else - ReportErrorMessage(JAR_ERROR3, operand); + JLI_ReportErrorMessage(JAR_ERROR3, operand); exit(1); } @@ -948,7 +948,7 @@ SelectVersion(int argc, char **argv, char **main_class) * Check for correct syntax of the version specification (JSR 56). */ if (!JLI_ValidVersionString(info.jre_version)) { - ReportErrorMessage(SPC_ERROR1, info.jre_version); + JLI_ReportErrorMessage(SPC_ERROR1, info.jre_version); exit(1); } @@ -970,7 +970,7 @@ SelectVersion(int argc, char **argv, char **main_class) JLI_MemFree(new_argv); return; } else { - ReportErrorMessage(CFG_ERROR4, info.jre_version); + JLI_ReportErrorMessage(CFG_ERROR4, info.jre_version); exit(1); } } @@ -1040,7 +1040,7 @@ ParseArguments(int *pargc, char ***pargv, char **pjarfile, * command line options. */ } else if (JLI_StrCmp(arg, "-fullversion") == 0) { - ReportMessage("%s full version \"%s\"", _launcher_name, GetFullVersion()); + JLI_ReportMessage("%s full version \"%s\"", _launcher_name, GetFullVersion()); return JNI_FALSE; } else if (JLI_StrCmp(arg, "-verbosegc") == 0) { AddOption("-verbose:gc", NULL); @@ -1080,7 +1080,7 @@ ParseArguments(int *pargc, char ***pargv, char **pjarfile, JLI_StrCmp(arg, "-cs") == 0 || JLI_StrCmp(arg, "-noasyncgc") == 0) { /* No longer supported */ - ReportErrorMessage(ARG_WARN, arg); + JLI_ReportErrorMessage(ARG_WARN, arg); } else if (JLI_StrCCmp(arg, "-version:") == 0 || JLI_StrCmp(arg, "-no-jre-restrict-search") == 0 || JLI_StrCmp(arg, "-jre-restrict-search") == 0 || @@ -1143,12 +1143,12 @@ InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn) #define NULL_CHECK0(e) if ((e) == 0) { \ - ReportErrorMessage(JNI_ERROR); \ + JLI_ReportErrorMessage(JNI_ERROR); \ return 0; \ } #define NULL_CHECK(e) if ((e) == 0) { \ - ReportErrorMessage(JNI_ERROR); \ + JLI_ReportErrorMessage(JNI_ERROR); \ return; \ } @@ -1351,7 +1351,7 @@ TranslateApplicationArgs(int jargc, const char **jargv, int *pargc, char ***parg char *arg = argv[i]; if (arg[0] == '-' && arg[1] == 'J') { if (arg[2] == '\0') { - ReportErrorMessage(ARG_ERROR3); + JLI_ReportErrorMessage(ARG_ERROR3); exit(1); } *nargv++ = arg + 2; @@ -1418,7 +1418,7 @@ AddApplicationOptions(int cpathc, const char **cpathv) } if (!GetApplicationHome(home, sizeof(home))) { - ReportErrorMessage(CFG_ERROR5); + JLI_ReportErrorMessage(CFG_ERROR5); return JNI_FALSE; } @@ -1691,7 +1691,7 @@ ReadKnownVMs(const char *jrepath, const char * arch, jboolean speculative) jvmCfg = fopen(jvmCfgName, "r"); if (jvmCfg == NULL) { if (!speculative) { - ReportErrorMessage(CFG_ERROR6, jvmCfgName); + JLI_ReportErrorMessage(CFG_ERROR6, jvmCfgName); exit(1); } else { return -1; @@ -1703,7 +1703,7 @@ ReadKnownVMs(const char *jrepath, const char * arch, jboolean speculative) if (line[0] == '#') continue; if (line[0] != '-') { - ReportErrorMessage(CFG_WARN2, lineno, jvmCfgName); + JLI_ReportErrorMessage(CFG_WARN2, lineno, jvmCfgName); } if (cnt >= knownVMsLimit) { GrowKnownVMs(cnt); @@ -1711,13 +1711,13 @@ ReadKnownVMs(const char *jrepath, const char * arch, jboolean speculative) line[JLI_StrLen(line)-1] = '\0'; /* remove trailing newline */ tmpPtr = line + JLI_StrCSpn(line, whiteSpace); if (*tmpPtr == 0) { - ReportErrorMessage(CFG_WARN3, lineno, jvmCfgName); + JLI_ReportErrorMessage(CFG_WARN3, lineno, jvmCfgName); } else { /* Null-terminate this string for JLI_StringDup below */ *tmpPtr++ = 0; tmpPtr += JLI_StrSpn(tmpPtr, whiteSpace); if (*tmpPtr == 0) { - ReportErrorMessage(CFG_WARN3, lineno, jvmCfgName); + JLI_ReportErrorMessage(CFG_WARN3, lineno, jvmCfgName); } else { if (!JLI_StrCCmp(tmpPtr, "KNOWN")) { vmType = VM_KNOWN; @@ -1727,7 +1727,7 @@ ReadKnownVMs(const char *jrepath, const char * arch, jboolean speculative) tmpPtr += JLI_StrSpn(tmpPtr, whiteSpace); } if (*tmpPtr == 0) { - ReportErrorMessage(CFG_WARN3, lineno, jvmCfgName); + JLI_ReportErrorMessage(CFG_WARN3, lineno, jvmCfgName); } else { /* Null terminate altVMName */ altVMName = tmpPtr; @@ -1747,7 +1747,7 @@ ReadKnownVMs(const char *jrepath, const char * arch, jboolean speculative) tmpPtr += JLI_StrSpn(tmpPtr, whiteSpace); } if (*tmpPtr == 0) { - ReportErrorMessage(CFG_WARN4, lineno, jvmCfgName); + JLI_ReportErrorMessage(CFG_WARN4, lineno, jvmCfgName); } else { /* Null terminate server class VM name */ serverClassVMName = tmpPtr; @@ -1756,7 +1756,7 @@ ReadKnownVMs(const char *jrepath, const char * arch, jboolean speculative) vmType = VM_IF_SERVER_CLASS; } } else { - ReportErrorMessage(CFG_WARN5, lineno, &jvmCfgName[0]); + JLI_ReportErrorMessage(CFG_WARN5, lineno, &jvmCfgName[0]); vmType = VM_KNOWN; } } @@ -2019,7 +2019,7 @@ RemovableOption(char * option) * A utility procedure to always print to stderr */ void -ReportMessage(const char* fmt, ...) +JLI_ReportMessage(const char* fmt, ...) { va_list vl; va_start(vl, fmt); diff --git a/src/share/bin/java.h b/src/share/bin/java.h index e6a08f3cf..23871b5d2 100644 --- a/src/share/bin/java.h +++ b/src/share/bin/java.h @@ -121,24 +121,20 @@ void CreateExecutionEnvironment(int *_argc, char jvmpath[], jint so_jvmpath, char **original_argv); +/* Reports an error message to stderr or a window as appropriate. */ +void JLI_ReportErrorMessage(const char * message, ...); -/* - * Report an error message to stderr or a window as appropriate. - */ -void ReportErrorMessage(const char * message, ...); -void ReportErrorMessageSys(const char * format, ...); +/* Reports a system error message to stderr or a window */ +void JLI_ReportErrorMessageSys(const char * message, ...); -/* - * Report an error message only to stderr. - */ -void ReportMessage(const char * message, ...); +/* Reports an error message only to stderr. */ +void JLI_ReportMessage(const char * message, ...); /* - * Report an exception which terminates the vm to stderr or a window + * Reports an exception which terminates the vm to stderr or a window * as appropriate. */ -void ReportExceptionDescription(JNIEnv * env); - +void JLI_ReportExceptionDescription(JNIEnv * env); void PrintMachineDependentOptions(); const char *jlong_format_specifier(); diff --git a/src/solaris/bin/java_md.c b/src/solaris/bin/java_md.c index c7c0da6d7..6ecd9681c 100644 --- a/src/solaris/bin/java_md.c +++ b/src/solaris/bin/java_md.c @@ -289,13 +289,13 @@ CreateExecutionEnvironment(int *_argcp, if (wanted == running) { /* Find out where the JRE is that we will be using. */ if (!GetJREPath(jrepath, so_jrepath, arch, JNI_FALSE) ) { - ReportErrorMessage(JRE_ERROR1); + JLI_ReportErrorMessage(JRE_ERROR1); exit(2); } /* Find the specified JVM type */ if (ReadKnownVMs(jrepath, arch, JNI_FALSE) < 1) { - ReportErrorMessage(CFG_ERROR7); + JLI_ReportErrorMessage(CFG_ERROR7); exit(1); } @@ -303,7 +303,7 @@ CreateExecutionEnvironment(int *_argcp, jvmtype = CheckJvmType(_argcp, _argvp, JNI_FALSE); if (!GetJVMPath(jrepath, jvmtype, jvmpath, so_jvmpath, arch )) { - ReportErrorMessage(CFG_ERROR8, jvmtype, jvmpath); + JLI_ReportErrorMessage(CFG_ERROR8, jvmtype, jvmpath); exit(4); } } else { /* do the same speculatively or exit */ @@ -330,7 +330,7 @@ CreateExecutionEnvironment(int *_argcp, EndDataModelSpeculate: /* give up and let other code report error message */ ; #else - ReportErrorMessage(JRE_ERROR2, wanted); + JLI_ReportErrorMessage(JRE_ERROR2, wanted); exit(1); #endif } @@ -391,7 +391,7 @@ CreateExecutionEnvironment(int *_argcp, break; default: - ReportErrorMessage(JRE_ERROR3, __LINE__); + JLI_ReportErrorMessage(JRE_ERROR3, __LINE__); exit(1); /* unknown value in wanted */ break; } @@ -553,17 +553,17 @@ CreateExecutionEnvironment(int *_argcp, (void)fflush(stdout); (void)fflush(stderr); execve(newexec, argv, newenvp); - ReportErrorMessageSys(JRE_ERROR4, newexec); + JLI_ReportErrorMessageSys(JRE_ERROR4, newexec); #ifdef DUAL_MODE if (running != wanted) { - ReportErrorMessage(JRE_ERROR5, wanted, running); + JLI_ReportErrorMessage(JRE_ERROR5, wanted, running); # ifdef __solaris__ # ifdef __sparc - ReportErrorMessage(JRE_ERROR6); + JLI_ReportErrorMessage(JRE_ERROR6); # else - ReportErrorMessage(JRE_ERROR7); + JLI_ReportErrorMessage(JRE_ERROR7); # endif } # endif @@ -627,7 +627,7 @@ GetJREPath(char *path, jint pathsize, const char * arch, jboolean speculative) } if (!speculative) - ReportErrorMessage(JRE_ERROR8 JAVA_DLL); + JLI_ReportErrorMessage(JRE_ERROR8 JAVA_DLL); return JNI_FALSE; found: @@ -680,13 +680,13 @@ LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn) if(length > 0) { location = JLI_StrStr(buf, "sparcv8plus "); if(location == NULL) { - ReportErrorMessage(JVM_ERROR3); + JLI_ReportErrorMessage(JVM_ERROR3); return JNI_FALSE; } } } #endif - ReportErrorMessage(DLL_ERROR1, __LINE__); + JLI_ReportErrorMessage(DLL_ERROR1, __LINE__); goto error; } @@ -703,7 +703,7 @@ LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn) return JNI_TRUE; error: - ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); + JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); return JNI_FALSE; } @@ -848,7 +848,7 @@ SetExecname(char **argv) fptr = (int (*)())dlsym(RTLD_DEFAULT, "main"); if (fptr == NULL) { - ReportErrorMessage(DLL_ERROR3, dlerror()); + JLI_ReportErrorMessage(DLL_ERROR3, dlerror()); return JNI_FALSE; } @@ -885,7 +885,7 @@ SetExecname(char **argv) return exec_path; } -void ReportErrorMessage(const char* fmt, ...) { +void JLI_ReportErrorMessage(const char* fmt, ...) { va_list vl; va_start(vl, fmt); vfprintf(stderr, fmt, vl); @@ -893,7 +893,7 @@ void ReportErrorMessage(const char* fmt, ...) { va_end(vl); } -void ReportErrorMessageSys(const char* fmt, ...) { +void JLI_ReportErrorMessageSys(const char* fmt, ...) { va_list vl; char *emsg; @@ -912,7 +912,7 @@ void ReportErrorMessageSys(const char* fmt, ...) { va_end(vl); } -void ReportExceptionDescription(JNIEnv * env) { +void JLI_ReportExceptionDescription(JNIEnv * env) { (*env)->ExceptionDescribe(env); } @@ -1078,7 +1078,7 @@ ExecJRE(char *jre, char **argv) * Resolve the real path to the directory containing the selected JRE. */ if (realpath(jre, wanted) == NULL) { - ReportErrorMessage(JRE_ERROR9, jre); + JLI_ReportErrorMessage(JRE_ERROR9, jre); exit(1); } @@ -1087,7 +1087,7 @@ ExecJRE(char *jre, char **argv) */ SetExecname(argv); if (execname == NULL) { - ReportErrorMessage(JRE_ERROR10); + JLI_ReportErrorMessage(JRE_ERROR10); exit(1); } @@ -1106,7 +1106,7 @@ ExecJRE(char *jre, char **argv) * can be so deadly. */ if (JLI_StrLen(wanted) + JLI_StrLen(progname) + 6 > PATH_MAX) { - ReportErrorMessage(JRE_ERROR11); + JLI_ReportErrorMessage(JRE_ERROR11); exit(1); } @@ -1126,7 +1126,7 @@ ExecJRE(char *jre, char **argv) (void)fflush(stdout); (void)fflush(stderr); execv(wanted, argv); - ReportErrorMessageSys(JRE_ERROR12, wanted); + JLI_ReportErrorMessageSys(JRE_ERROR12, wanted); exit(1); } diff --git a/src/windows/bin/java_md.c b/src/windows/bin/java_md.c index 53e1e9a0a..2db1f25e2 100644 --- a/src/windows/bin/java_md.c +++ b/src/windows/bin/java_md.c @@ -105,26 +105,26 @@ CreateExecutionEnvironment(int *_argc, } } if (running != wanted) { - ReportErrorMessage(JRE_ERROR2, wanted); + JLI_ReportErrorMessage(JRE_ERROR2, wanted); exit(1); } /* Find out where the JRE is that we will be using. */ if (!GetJREPath(jrepath, so_jrepath)) { - ReportErrorMessage(JRE_ERROR1); + JLI_ReportErrorMessage(JRE_ERROR1); exit(2); } /* Find the specified JVM type */ if (ReadKnownVMs(jrepath, (char*)GetArch(), JNI_FALSE) < 1) { - ReportErrorMessage(CFG_ERROR7); + JLI_ReportErrorMessage(CFG_ERROR7); exit(1); } jvmtype = CheckJvmType(_argc, _argv, JNI_FALSE); jvmpath[0] = '\0'; if (!GetJVMPath(jrepath, jvmtype, jvmpath, so_jvmpath)) { - ReportErrorMessage(CFG_ERROR8, jvmtype, jvmpath); + JLI_ReportErrorMessage(CFG_ERROR8, jvmtype, jvmpath); exit(4); } /* If we got here, jvmpath has been correctly initialized. */ @@ -160,7 +160,7 @@ GetJREPath(char *path, jint pathsize) goto found; } - ReportErrorMessage(JRE_ERROR8 JAVA_DLL); + JLI_ReportErrorMessage(JRE_ERROR8 JAVA_DLL); return JNI_FALSE; found: @@ -212,7 +212,7 @@ LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn) JLI_TraceLauncher("CRT path is %s\n", crtpath); if (_access(crtpath, 0) == 0) { if (LoadLibrary(crtpath) == 0) { - ReportErrorMessage(DLL_ERROR4, crtpath); + JLI_ReportErrorMessage(DLL_ERROR4, crtpath); return JNI_FALSE; } } @@ -220,7 +220,7 @@ LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn) /* Load the Java VM DLL */ if ((handle = LoadLibrary(jvmpath)) == 0) { - ReportErrorMessage(DLL_ERROR4, (char *)jvmpath); + JLI_ReportErrorMessage(DLL_ERROR4, (char *)jvmpath); return JNI_FALSE; } @@ -230,7 +230,7 @@ LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn) ifn->GetDefaultJavaVMInitArgs = (void *)GetProcAddress(handle, "JNI_GetDefaultJavaVMInitArgs"); if (ifn->CreateJavaVM == 0 || ifn->GetDefaultJavaVMInitArgs == 0) { - ReportErrorMessage(JNI_ERROR1, (char *)jvmpath); + JLI_ReportErrorMessage(JNI_ERROR1, (char *)jvmpath); return JNI_FALSE; } @@ -292,19 +292,19 @@ GetPublicJREHome(char *buf, jint bufsize) /* Find the current version of the JRE */ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, JRE_KEY, 0, KEY_READ, &key) != 0) { - ReportErrorMessage(REG_ERROR1, JRE_KEY); + JLI_ReportErrorMessage(REG_ERROR1, JRE_KEY); return JNI_FALSE; } if (!GetStringFromRegistry(key, "CurrentVersion", version, sizeof(version))) { - ReportErrorMessage(REG_ERROR2, JRE_KEY); + JLI_ReportErrorMessage(REG_ERROR2, JRE_KEY); RegCloseKey(key); return JNI_FALSE; } if (JLI_StrCmp(version, GetDotVersion()) != 0) { - ReportErrorMessage(REG_ERROR3, JRE_KEY, version, GetDotVersion() + JLI_ReportErrorMessage(REG_ERROR3, JRE_KEY, version, GetDotVersion() ); RegCloseKey(key); return JNI_FALSE; @@ -312,13 +312,13 @@ GetPublicJREHome(char *buf, jint bufsize) /* Find directory where the current version is installed. */ if (RegOpenKeyEx(key, version, 0, KEY_READ, &subkey) != 0) { - ReportErrorMessage(REG_ERROR1, JRE_KEY, version); + JLI_ReportErrorMessage(REG_ERROR1, JRE_KEY, version); RegCloseKey(key); return JNI_FALSE; } if (!GetStringFromRegistry(subkey, "JavaHome", buf, bufsize)) { - ReportErrorMessage(REG_ERROR4, JRE_KEY, version); + JLI_ReportErrorMessage(REG_ERROR4, JRE_KEY, version); RegCloseKey(key); RegCloseKey(subkey); return JNI_FALSE; @@ -370,7 +370,7 @@ jlong Counter2Micros(jlong counts) } void -ReportErrorMessage(const char* fmt, ...) { +JLI_ReportErrorMessage(const char* fmt, ...) { va_list vl; va_start(vl,fmt); @@ -394,12 +394,12 @@ ReportErrorMessage(const char* fmt, ...) { } /* - * Just like ReportErrorMessage, except that it concatenates the system + * Just like JLI_ReportErrorMessage, except that it concatenates the system * error message if any, its upto the calling routine to correctly * format the separation of the messages. */ void -ReportErrorMessageSys(const char *fmt, ...) +JLI_ReportErrorMessageSys(const char *fmt, ...) { va_list vl; @@ -462,7 +462,7 @@ ReportErrorMessageSys(const char *fmt, ...) va_end(vl); } -void ReportExceptionDescription(JNIEnv * env) { +void JLI_ReportExceptionDescription(JNIEnv * env) { if (IsJavaw()) { /* * This code should be replaced by code which opens a window with @@ -733,7 +733,7 @@ ExecJRE(char *jre, char **argv) { */ len = GetModuleFileName(NULL, path, MAXPATHLEN + 1); if (len == 0 || len > MAXPATHLEN) { - ReportErrorMessageSys(JRE_ERROR9, progname); + JLI_ReportErrorMessageSys(JRE_ERROR9, progname); exit(1); } @@ -766,7 +766,7 @@ ExecJRE(char *jre, char **argv) { * If it weren't for this semantic flaw, the code below would be ... * * execv(path, argv); - * ReportErrorMessage("Error: Exec of %s failed\n", path); + * JLI_ReportErrorMessage("Error: Exec of %s failed\n", path); * exit(1); * * The incorrect exec semantics could be addressed by: @@ -876,7 +876,7 @@ ExecJRE(char *jre, char **argv) { (LPCTSTR)NULL, /* current directory */ (LPSTARTUPINFO)&si, /* (in) startup information */ (LPPROCESS_INFORMATION)&pi)) { /* (out) process information */ - ReportErrorMessageSys(SYS_ERROR1, path); + JLI_ReportErrorMessageSys(SYS_ERROR1, path); exit(1); } @@ -884,7 +884,7 @@ ExecJRE(char *jre, char **argv) { if (GetExitCodeProcess(pi.hProcess, &exitCode) == FALSE) exitCode = 1; } else { - ReportErrorMessage(SYS_ERROR2); + JLI_ReportErrorMessage(SYS_ERROR2); exitCode = 1; } -- GitLab From bafc6e8bf018017d4c3893f4ebf31c36749e335c Mon Sep 17 00:00:00 2001 From: rupashka Date: Wed, 27 Aug 2008 20:49:35 +0400 Subject: [PATCH 070/139] 6351074: JFileChooser removes leading space in filename Summary: Removed trimming of leading spaces in filename Reviewed-by: alexp --- .../swing/plaf/basic/BasicFileChooserUI.java | 164 +++++++++--------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/src/share/classes/javax/swing/plaf/basic/BasicFileChooserUI.java b/src/share/classes/javax/swing/plaf/basic/BasicFileChooserUI.java index 4016435c7..13d13cdce 100644 --- a/src/share/classes/javax/swing/plaf/basic/BasicFileChooserUI.java +++ b/src/share/classes/javax/swing/plaf/basic/BasicFileChooserUI.java @@ -36,6 +36,7 @@ import java.awt.datatransfer.*; import java.beans.*; import java.io.*; import java.util.*; +import java.util.List; import java.util.regex.*; import sun.awt.shell.ShellFolder; import sun.swing.*; @@ -829,11 +830,17 @@ public class BasicFileChooserUI extends FileChooserUI { File dir = chooser.getCurrentDirectory(); if (filename != null) { - // Remove whitespace from beginning and end of filename - filename = filename.trim(); + // Remove whitespaces from end of filename + int i = filename.length() - 1; + + while (i >=0 && filename.charAt(i) <= ' ') { + i--; + } + + filename = filename.substring(0, i + 1); } - if (filename == null || filename.equals("")) { + if (filename == null || filename.length() == 0) { // no file selected, multiple selection off, therefore cancel the approve action resetGlobFilter(); return; @@ -842,100 +849,93 @@ public class BasicFileChooserUI extends FileChooserUI { File selectedFile = null; File[] selectedFiles = null; - if (filename != null && !filename.equals("")) { - // Unix: Resolve '~' to user's home directory - if (File.separatorChar == '/') { - if (filename.startsWith("~/")) { - filename = System.getProperty("user.home") + filename.substring(1); - } else if (filename.equals("~")) { - filename = System.getProperty("user.home"); - } + // Unix: Resolve '~' to user's home directory + if (File.separatorChar == '/') { + if (filename.startsWith("~/")) { + filename = System.getProperty("user.home") + filename.substring(1); + } else if (filename.equals("~")) { + filename = System.getProperty("user.home"); } + } - if (chooser.isMultiSelectionEnabled() && filename.startsWith("\"")) { - ArrayList fList = new ArrayList(); + if (chooser.isMultiSelectionEnabled() && filename.length() > 1 && + filename.charAt(0) == '"' && filename.charAt(filename.length() - 1) == '"') { + List fList = new ArrayList(); - filename = filename.substring(1); - if (filename.endsWith("\"")) { - filename = filename.substring(0, filename.length()-1); - } - File[] children = null; - int childIndex = 0; - do { - String str; - int i = filename.indexOf("\" \""); - if (i > 0) { - str = filename.substring(0, i); - filename = filename.substring(i+3); - } else { - str = filename; - filename = ""; + String[] files = filename.substring(1, filename.length() - 1).split("\" \""); + // Optimize searching files by names in "children" array + Arrays.sort(files); + + File[] children = null; + int childIndex = 0; + + for (String str : files) { + File file = fs.createFileObject(str); + if (!file.isAbsolute()) { + if (children == null) { + children = fs.getFiles(dir, false); + Arrays.sort(children); } - File file = fs.createFileObject(str); - if (!file.isAbsolute()) { - if (children == null) { - children = fs.getFiles(dir, false); - Arrays.sort(children); - } - for (int k = 0; k < children.length; k++) { - int l = (childIndex + k) % children.length; - if (children[l].getName().equals(str)) { - file = children[l]; - childIndex = l + 1; - break; - } + for (int k = 0; k < children.length; k++) { + int l = (childIndex + k) % children.length; + if (children[l].getName().equals(str)) { + file = children[l]; + childIndex = l + 1; + break; } } - fList.add(file); - } while (filename.length() > 0); - if (fList.size() > 0) { - selectedFiles = fList.toArray(new File[fList.size()]); } - resetGlobFilter(); - } else { - selectedFile = fs.createFileObject(filename); - if(!selectedFile.isAbsolute()) { - selectedFile = fs.getChild(dir, filename); + fList.add(file); + } + + if (!fList.isEmpty()) { + selectedFiles = fList.toArray(new File[fList.size()]); + } + resetGlobFilter(); + } else { + selectedFile = fs.createFileObject(filename); + if (!selectedFile.isAbsolute()) { + selectedFile = fs.getChild(dir, filename); + } + // check for wildcard pattern + FileFilter currentFilter = chooser.getFileFilter(); + if (!selectedFile.exists() && isGlobPattern(filename)) { + changeDirectory(selectedFile.getParentFile()); + if (globFilter == null) { + globFilter = new GlobFilter(); } - // check for wildcard pattern - FileFilter currentFilter = chooser.getFileFilter(); - if (!selectedFile.exists() && isGlobPattern(filename)) { - changeDirectory(selectedFile.getParentFile()); - if (globFilter == null) { - globFilter = new GlobFilter(); - } - try { - globFilter.setPattern(selectedFile.getName()); - if (!(currentFilter instanceof GlobFilter)) { - actualFileFilter = currentFilter; - } - chooser.setFileFilter(null); - chooser.setFileFilter(globFilter); - return; - } catch (PatternSyntaxException pse) { - // Not a valid glob pattern. Abandon filter. + try { + globFilter.setPattern(selectedFile.getName()); + if (!(currentFilter instanceof GlobFilter)) { + actualFileFilter = currentFilter; } + chooser.setFileFilter(null); + chooser.setFileFilter(globFilter); + return; + } catch (PatternSyntaxException pse) { + // Not a valid glob pattern. Abandon filter. } + } - resetGlobFilter(); + resetGlobFilter(); - // Check for directory change action - boolean isDir = (selectedFile != null && selectedFile.isDirectory()); - boolean isTrav = (selectedFile != null && chooser.isTraversable(selectedFile)); - boolean isDirSelEnabled = chooser.isDirectorySelectionEnabled(); - boolean isFileSelEnabled = chooser.isFileSelectionEnabled(); - boolean isCtrl = (e != null && (e.getModifiers() & ActionEvent.CTRL_MASK) != 0); + // Check for directory change action + boolean isDir = (selectedFile != null && selectedFile.isDirectory()); + boolean isTrav = (selectedFile != null && chooser.isTraversable(selectedFile)); + boolean isDirSelEnabled = chooser.isDirectorySelectionEnabled(); + boolean isFileSelEnabled = chooser.isFileSelectionEnabled(); + boolean isCtrl = (e != null && (e.getModifiers() & ActionEvent.CTRL_MASK) != 0); - if (isDir && isTrav && (isCtrl || !isDirSelEnabled)) { - changeDirectory(selectedFile); - return; - } else if ((isDir || !isFileSelEnabled) - && (!isDir || !isDirSelEnabled) - && (!isDirSelEnabled || selectedFile.exists())) { - selectedFile = null; - } + if (isDir && isTrav && (isCtrl || !isDirSelEnabled)) { + changeDirectory(selectedFile); + return; + } else if ((isDir || !isFileSelEnabled) + && (!isDir || !isDirSelEnabled) + && (!isDirSelEnabled || selectedFile.exists())) { + selectedFile = null; } } + if (selectedFiles != null || selectedFile != null) { if (selectedFiles != null || chooser.isMultiSelectionEnabled()) { if (selectedFiles == null) { -- GitLab From 2a6ead186548aa789d0846506ae812783e422410 Mon Sep 17 00:00:00 2001 From: sherman Date: Wed, 27 Aug 2008 10:12:22 -0700 Subject: [PATCH 071/139] 4849617: (cs)Revise Charset spec to allow '+' in names Summary: Update the spec and code to accept '+' as a charset name character Reviewed-by: alanb --- .../classes/java/nio/charset/Charset.java | 4 ++ .../sun/nio/cs/ext/ExtendedCharsets.java | 24 ++++---- .../classes/sun/nio/cs/standard-charsets | 1 + test/sun/nio/cs/CheckICNE.java | 58 +++++++++++++++++++ 4 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 test/sun/nio/cs/CheckICNE.java diff --git a/src/share/classes/java/nio/charset/Charset.java b/src/share/classes/java/nio/charset/Charset.java index 4908c77d1..804d0091c 100644 --- a/src/share/classes/java/nio/charset/Charset.java +++ b/src/share/classes/java/nio/charset/Charset.java @@ -85,6 +85,9 @@ import sun.security.action.GetPropertyAction; *
    • The dash character '-' * ('\u002d'HYPHEN-MINUS), * + *
    • The plus character '+' + * ('\u002b'PLUS SIGN), + * *
    • The period character '.' * ('\u002e'FULL STOP), * @@ -307,6 +310,7 @@ public abstract class Charset if (c >= 'a' && c <= 'z') continue; if (c >= '0' && c <= '9') continue; if (c == '-' && i != 0) continue; + if (c == '+' && i != 0) continue; if (c == ':' && i != 0) continue; if (c == '_' && i != 0) continue; if (c == '.' && i != 0) continue; diff --git a/src/share/classes/sun/nio/cs/ext/ExtendedCharsets.java b/src/share/classes/sun/nio/cs/ext/ExtendedCharsets.java index 9c7ae0910..04434f1f5 100644 --- a/src/share/classes/sun/nio/cs/ext/ExtendedCharsets.java +++ b/src/share/classes/sun/nio/cs/ext/ExtendedCharsets.java @@ -916,7 +916,7 @@ public class ExtendedCharsets "ccsid01140", "cp01140", "1140", - // "ebcdic-us-037+euro" + "ebcdic-us-037+euro" }); charset("IBM01141", "IBM1141", @@ -925,7 +925,7 @@ public class ExtendedCharsets "ccsid01141", "cp01141", "1141", - // "ebcdic-de-273+euro" + "ebcdic-de-273+euro" }); charset("IBM01142", "IBM1142", @@ -934,8 +934,8 @@ public class ExtendedCharsets "ccsid01142", "cp01142", "1142", - // "ebcdic-no-277+euro", - // "ebcdic-dk-277+euro" + "ebcdic-no-277+euro", + "ebcdic-dk-277+euro" }); charset("IBM01143", "IBM1143", @@ -944,8 +944,8 @@ public class ExtendedCharsets "ccsid01143", "cp01143", "1143", - // "ebcdic-fi-278+euro", - // "ebcdic-se-278+euro" + "ebcdic-fi-278+euro", + "ebcdic-se-278+euro" }); charset("IBM01144", "IBM1144", @@ -954,7 +954,7 @@ public class ExtendedCharsets "ccsid01144", "cp01144", "1144", - // "ebcdic-it-280+euro" + "ebcdic-it-280+euro" }); charset("IBM01145", "IBM1145", @@ -963,7 +963,7 @@ public class ExtendedCharsets "ccsid01145", "cp01145", "1145", - // "ebcdic-es-284+euro" + "ebcdic-es-284+euro" }); charset("IBM01146", "IBM1146", @@ -972,7 +972,7 @@ public class ExtendedCharsets "ccsid01146", "cp01146", "1146", - // "ebcdic-gb-285+euro" + "ebcdic-gb-285+euro" }); charset("IBM01147", "IBM1147", @@ -981,7 +981,7 @@ public class ExtendedCharsets "ccsid01147", "cp01147", "1147", - // "ebcdic-fr-277+euro" + "ebcdic-fr-277+euro" }); charset("IBM01148", "IBM1148", @@ -990,7 +990,7 @@ public class ExtendedCharsets "ccsid01148", "cp01148", "1148", - // "ebcdic-international-500+euro" + "ebcdic-international-500+euro" }); charset("IBM01149", "IBM1149", @@ -999,7 +999,7 @@ public class ExtendedCharsets "ccsid01149", "cp01149", "1149", - // "ebcdic-s-871+euro" + "ebcdic-s-871+euro" }); // Macintosh MacOS/Apple char encodingd diff --git a/src/share/classes/sun/nio/cs/standard-charsets b/src/share/classes/sun/nio/cs/standard-charsets index a3cad5fd9..da0b26f55 100644 --- a/src/share/classes/sun/nio/cs/standard-charsets +++ b/src/share/classes/sun/nio/cs/standard-charsets @@ -314,6 +314,7 @@ charset IBM00858 IBM858 alias ccsid00858 alias cp00858 alias 858 + alias PC-Multilingual-850+euro charset IBM862 IBM862 alias cp862 #JDK historical diff --git a/test/sun/nio/cs/CheckICNE.java b/test/sun/nio/cs/CheckICNE.java new file mode 100644 index 000000000..4b3e905f1 --- /dev/null +++ b/test/sun/nio/cs/CheckICNE.java @@ -0,0 +1,58 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + @bug 4849617 + @summary Checks "+" is a legal character for charset name + */ +import java.nio.charset.*; + +public class CheckICNE { + static int failed = 0; + public static void main (String[] args) throws Exception { + try { + Charset.forName("abc+"); + } catch (UnsupportedCharsetException uce) {} + + try { + java.nio.charset.Charset.forName("+abc"); + } catch (IllegalCharsetNameException icne) {} + + String[] euros = {"PC-Multilingual-850+euro", + "ebcdic-us-037+euro", + "ebcdic-de-273+euro", + "ebcdic-no-277+euro", + "ebcdic-dk-277+euro", + "ebcdic-fi-278+euro", + "ebcdic-se-278+euro", + "ebcdic-it-280+euro", + "ebcdic-es-284+euro", + "ebcdic-gb-285+euro", + "ebcdic-fr-277+euro", + "ebcdic-international-500+euro", + "ebcdic-s-871+euro" + }; + + System.out.println("Test Passed!"); + } +} -- GitLab From 052050c6477d4190abe8815b9af830ee1ac41e9e Mon Sep 17 00:00:00 2001 From: xdono Date: Thu, 28 Aug 2008 11:05:27 -0700 Subject: [PATCH 072/139] Added tag jdk7-b34 for changeset 434055a0716e --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 75a9825e9..88db46731 100644 --- a/.hgtags +++ b/.hgtags @@ -8,3 +8,4 @@ b6d6877c1155621a175dccd12dc14c54f938fb8b jdk7-b30 b7474b739d13bacd9972f88ac91f6350b7b0be12 jdk7-b31 c51121419e30eac5f0fbbce45ff1711c4ce0de28 jdk7-b32 fa4c0a6cdd25d97d4e6f5d7aa180bcbb0e0d56af jdk7-b33 +434055a0716ee44bca712ebca02fc04b20e6e288 jdk7-b34 -- GitLab From c016254a6c06ade89b2e664a5495a703ee5c782e Mon Sep 17 00:00:00 2001 From: rupashka Date: Fri, 29 Aug 2008 13:23:55 +0400 Subject: [PATCH 073/139] 6742490: JSlider tests are located in JFileChooser directory Summary: Tests were moved to appropriate folder Reviewed-by: peterz --- .../javax/swing/{JFileChooser => JSlider}/4252173/bug4252173.java | 0 .../javax/swing/{JFileChooser => JSlider}/6524424/bug6524424.html | 0 .../javax/swing/{JFileChooser => JSlider}/6524424/bug6524424.java | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename test/javax/swing/{JFileChooser => JSlider}/4252173/bug4252173.java (100%) rename test/javax/swing/{JFileChooser => JSlider}/6524424/bug6524424.html (100%) rename test/javax/swing/{JFileChooser => JSlider}/6524424/bug6524424.java (100%) diff --git a/test/javax/swing/JFileChooser/4252173/bug4252173.java b/test/javax/swing/JSlider/4252173/bug4252173.java similarity index 100% rename from test/javax/swing/JFileChooser/4252173/bug4252173.java rename to test/javax/swing/JSlider/4252173/bug4252173.java diff --git a/test/javax/swing/JFileChooser/6524424/bug6524424.html b/test/javax/swing/JSlider/6524424/bug6524424.html similarity index 100% rename from test/javax/swing/JFileChooser/6524424/bug6524424.html rename to test/javax/swing/JSlider/6524424/bug6524424.html diff --git a/test/javax/swing/JFileChooser/6524424/bug6524424.java b/test/javax/swing/JSlider/6524424/bug6524424.java similarity index 100% rename from test/javax/swing/JFileChooser/6524424/bug6524424.java rename to test/javax/swing/JSlider/6524424/bug6524424.java -- GitLab From cc745e0bf0660084f89a72c9c35a0057fff87630 Mon Sep 17 00:00:00 2001 From: rupashka Date: Fri, 29 Aug 2008 18:58:17 +0400 Subject: [PATCH 074/139] 6742358: MetalSliderUI paint wrong vertical disabled filled JSlider for DefaultMetalTheme Summary: Corrected the method MetalSliderUI.paintTrack Reviewed-by: malenkov --- .../javax/swing/plaf/metal/MetalSliderUI.java | 3 +- .../swing/JSlider/6742358/bug6742358.html | 6 ++ .../swing/JSlider/6742358/bug6742358.java | 92 +++++++++++++++++++ 3 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 test/javax/swing/JSlider/6742358/bug6742358.html create mode 100644 test/javax/swing/JSlider/6742358/bug6742358.java diff --git a/src/share/classes/javax/swing/plaf/metal/MetalSliderUI.java b/src/share/classes/javax/swing/plaf/metal/MetalSliderUI.java index 13563b80e..ce9462737 100644 --- a/src/share/classes/javax/swing/plaf/metal/MetalSliderUI.java +++ b/src/share/classes/javax/swing/plaf/metal/MetalSliderUI.java @@ -314,8 +314,7 @@ public class MetalSliderUI extends BasicSliderUI { } else { g.setColor( MetalLookAndFeel.getControlShadow() ); - g.fillRect( fillLeft, fillTop, - fillRight - fillLeft, trackBottom - trackTop ); + g.fillRect(fillLeft, fillTop, fillRight - fillLeft, fillBottom - fillTop); } } diff --git a/test/javax/swing/JSlider/6742358/bug6742358.html b/test/javax/swing/JSlider/6742358/bug6742358.html new file mode 100644 index 000000000..d004e116c --- /dev/null +++ b/test/javax/swing/JSlider/6742358/bug6742358.html @@ -0,0 +1,6 @@ + + + +Check that all sliders look good. + + diff --git a/test/javax/swing/JSlider/6742358/bug6742358.java b/test/javax/swing/JSlider/6742358/bug6742358.java new file mode 100644 index 000000000..a50f50cd7 --- /dev/null +++ b/test/javax/swing/JSlider/6742358/bug6742358.java @@ -0,0 +1,92 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 6742358 + * @summary MetalSliderUI paint wrong vertical disabled filled JSlider for DefaultMetalTheme + * @author Pavel Porvatov + * @run applet/manual=done bug6742358.html + */ + +import javax.swing.*; +import javax.swing.plaf.metal.DefaultMetalTheme; +import javax.swing.plaf.metal.MetalLookAndFeel; + +public class bug6742358 extends JApplet { + public static void main(String[] args) { + MetalLookAndFeel.setCurrentTheme(new DefaultMetalTheme()); + + JFrame frame = new JFrame(); + + frame.setContentPane(new TestPanel()); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.pack(); + frame.setLocationRelativeTo(null); + + frame.setVisible(true); + } + + public void init() { + MetalLookAndFeel.setCurrentTheme(new DefaultMetalTheme()); + + TestPanel panel = new TestPanel(); + + setContentPane(panel); + } + + private static class TestPanel extends JPanel { + + private TestPanel() { + JPanel pnVertical = new JPanel(); + + pnVertical.setLayout(new BoxLayout(pnVertical, BoxLayout.Y_AXIS)); + + for (int i = 0; i < 8; i++) { + pnVertical.add(createSlider(false, (i & 4) == 0, (i & 2) == 0, (i & 1) == 0)); + } + + JPanel pnHorizontal = new JPanel(); + + pnHorizontal.setLayout(new BoxLayout(pnHorizontal, BoxLayout.X_AXIS)); + + for (int i = 0; i < 8; i++) { + pnHorizontal.add(createSlider(true, (i & 4) == 0, (i & 2) == 0, (i & 1) == 0)); + } + + add(pnHorizontal); + add(pnVertical); + } + } + + private static JSlider createSlider(boolean vertical, boolean enabled, boolean filled, boolean inverted) { + JSlider result = new JSlider(vertical ? SwingConstants.VERTICAL : SwingConstants.HORIZONTAL, 0, 10, 5); + + result.setEnabled(enabled); + result.putClientProperty("JSlider.isFilled", filled); + result.setInverted(inverted); + result.setToolTipText("vertical = " + vertical + "
      enabled = " + enabled + "
      filled = " + filled + + "
      inverted = " + inverted + ""); + + return result; + } +} -- GitLab From 800b483c182dfc8c8af522d7eff2c6300ad33baf Mon Sep 17 00:00:00 2001 From: chegar Date: Fri, 29 Aug 2008 17:46:45 +0100 Subject: [PATCH 075/139] 6576763: Thread constructors throw undocumented NPE for null name Summary: update javadoc to specify NPE as well as fix minor bug in implementation. Reviewed-by: alanb --- src/share/classes/java/lang/Thread.java | 329 ++++++++++-------- .../java/lang/ThreadGroup/NullThreadName.java | 85 +++++ 2 files changed, 268 insertions(+), 146 deletions(-) create mode 100644 test/java/lang/ThreadGroup/NullThreadName.java diff --git a/src/share/classes/java/lang/Thread.java b/src/share/classes/java/lang/Thread.java index e6dd655b3..60300ebc9 100644 --- a/src/share/classes/java/lang/Thread.java +++ b/src/share/classes/java/lang/Thread.java @@ -1,5 +1,5 @@ /* - * Copyright 1994-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ import java.security.AccessControlContext; import java.security.PrivilegedAction; import java.util.Map; import java.util.HashMap; -import java.util.Collections; import java.util.concurrent.locks.LockSupport; import sun.misc.SoftCache; import sun.nio.ch.Interruptible; @@ -120,6 +119,10 @@ import sun.security.util.SecurityConstants; * Every thread has a name for identification purposes. More than * one thread may have the same name. If a name is not specified when * a thread is created, a new name is generated for it. + *

      + * Unless otherwise noted, passing a {@code null} argument to a constructor + * or method in this class will cause a {@link NullPointerException} to be + * thrown. * * @author unascribed * @see Runnable @@ -348,6 +351,10 @@ class Thread implements Runnable { */ private void init(ThreadGroup g, Runnable target, String name, long stackSize) { + if (name == null) { + throw new NullPointerException("name cannot be null"); + } + Thread parent = currentThread(); SecurityManager security = System.getSecurityManager(); if (g == null) { @@ -379,7 +386,6 @@ class Thread implements Runnable { } } - g.addUnstarted(); this.group = g; @@ -403,160 +409,175 @@ class Thread implements Runnable { tid = nextThreadID(); } - /** - * Allocates a new Thread object. This constructor has - * the same effect as Thread(null, null, - * gname), where gname is - * a newly generated name. Automatically generated names are of the - * form "Thread-"+n, where n is an integer. - * - * @see #Thread(ThreadGroup, Runnable, String) + /** + * Allocates a new {@code Thread} object. This constructor has the same + * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread} + * {@code (null, null, gname)}, where {@code gname} is a newly generated + * name. Automatically generated names are of the form + * {@code "Thread-"+}n, where n is an integer. */ public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } /** - * Allocates a new Thread object. This constructor has - * the same effect as Thread(null, target, - * gname), where gname is - * a newly generated name. Automatically generated names are of the - * form "Thread-"+n, where n is an integer. + * Allocates a new {@code Thread} object. This constructor has the same + * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread} + * {@code (null, target, gname)}, where {@code gname} is a newly generated + * name. Automatically generated names are of the form + * {@code "Thread-"+}n, where n is an integer. * - * @param target the object whose run method is called. - * @see #Thread(ThreadGroup, Runnable, String) + * @param target + * the object whose {@code run} method is invoked when this thread + * is started. If {@code null}, this classes {@code run} method does + * nothing. */ public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } /** - * Allocates a new Thread object. This constructor has - * the same effect as Thread(group, target, - * gname), where gname is - * a newly generated name. Automatically generated names are of the - * form "Thread-"+n, where n is an integer. - * - * @param group the thread group. - * @param target the object whose run method is called. - * @exception SecurityException if the current thread cannot create a - * thread in the specified thread group. - * @see #Thread(ThreadGroup, Runnable, String) + * Allocates a new {@code Thread} object. This constructor has the same + * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread} + * {@code (group, target, gname)} ,where {@code gname} is a newly generated + * name. Automatically generated names are of the form + * {@code "Thread-"+}n, where n is an integer. + * + * @param group + * the thread group. If {@code null} and there is a security + * manager, the group is determined by {@linkplain + * SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}. + * If there is not a security manager or {@code + * SecurityManager.getThreadGroup()} returns {@code null}, the group + * is set to the current thread's thread group. + * + * @param target + * the object whose {@code run} method is invoked when this thread + * is started. If {@code null}, this thread's run method is invoked. + * + * @throws SecurityException + * if the current thread cannot create a thread in the specified + * thread group */ public Thread(ThreadGroup group, Runnable target) { init(group, target, "Thread-" + nextThreadNum(), 0); } /** - * Allocates a new Thread object. This constructor has - * the same effect as Thread(null, null, name). + * Allocates a new {@code Thread} object. This constructor has the same + * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread} + * {@code (null, null, name)}. * - * @param name the name of the new thread. - * @see #Thread(ThreadGroup, Runnable, String) + * @param name + * the name of the new thread */ public Thread(String name) { init(null, null, name, 0); } /** - * Allocates a new Thread object. This constructor has - * the same effect as Thread(group, null, name) + * Allocates a new {@code Thread} object. This constructor has the same + * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread} + * {@code (group, null, name)}. + * + * @param group + * the thread group. If {@code null} and there is a security + * manager, the group is determined by {@linkplain + * SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}. + * If there is not a security manager or {@code + * SecurityManager.getThreadGroup()} returns {@code null}, the group + * is set to the current thread's thread group. * - * @param group the thread group. - * @param name the name of the new thread. - * @exception SecurityException if the current thread cannot create a - * thread in the specified thread group. - * @see #Thread(ThreadGroup, Runnable, String) + * @param name + * the name of the new thread + * + * @throws SecurityException + * if the current thread cannot create a thread in the specified + * thread group */ public Thread(ThreadGroup group, String name) { init(group, null, name, 0); } /** - * Allocates a new Thread object. This constructor has - * the same effect as Thread(null, target, name). + * Allocates a new {@code Thread} object. This constructor has the same + * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread} + * {@code (null, target, name)}. * - * @param target the object whose run method is called. - * @param name the name of the new thread. - * @see #Thread(ThreadGroup, Runnable, String) + * @param target + * the object whose {@code run} method is invoked when this thread + * is started. If {@code null}, this thread's run method is invoked. + * + * @param name + * the name of the new thread */ public Thread(Runnable target, String name) { init(null, target, name, 0); } /** - * Allocates a new Thread object so that it has - * target as its run object, has the specified - * name as its name, and belongs to the thread group - * referred to by group. - *

      - * If group is null and there is a - * security manager, the group is determined by the security manager's - * getThreadGroup method. If group is - * null and there is not a security manager, or the - * security manager's getThreadGroup method returns - * null, the group is set to be the same ThreadGroup - * as the thread that is creating the new thread. - * - *

      If there is a security manager, its checkAccess - * method is called with the ThreadGroup as its argument. - *

      In addition, its checkPermission - * method is called with the - * RuntimePermission("enableContextClassLoaderOverride") + * Allocates a new {@code Thread} object so that it has {@code target} + * as its run object, has the specified {@code name} as its name, + * and belongs to the thread group referred to by {@code group}. + * + *

      If there is a security manager, its + * {@link SecurityManager#checkAccess(ThreadGroup) checkAccess} + * method is invoked with the ThreadGroup as its argument. + * + *

      In addition, its {@code checkPermission} method is invoked with + * the {@code RuntimePermission("enableContextClassLoaderOverride")} * permission when invoked directly or indirectly by the constructor - * of a subclass which overrides the getContextClassLoader - * or setContextClassLoader methods. - * This may result in a SecurityException. - - *

      - * If the target argument is not null, the - * run method of the target is called when - * this thread is started. If the target argument is - * null, this thread's run method is called - * when this thread is started. - *

      - * The priority of the newly created thread is set equal to the + * of a subclass which overrides the {@code getContextClassLoader} + * or {@code setContextClassLoader} methods. + * + *

      The priority of the newly created thread is set equal to the * priority of the thread creating it, that is, the currently running - * thread. The method setPriority may be used to - * change the priority to a new value. - *

      - * The newly created thread is initially marked as being a daemon + * thread. The method {@linkplain #setPriority setPriority} may be + * used to change the priority to a new value. + * + *

      The newly created thread is initially marked as being a daemon * thread if and only if the thread creating it is currently marked - * as a daemon thread. The method setDaemon may be used - * to change whether or not a thread is a daemon. - * - * @param group the thread group. - * @param target the object whose run method is called. - * @param name the name of the new thread. - * @exception SecurityException if the current thread cannot create a - * thread in the specified thread group or cannot - * override the context class loader methods. - * @see Runnable#run() - * @see #run() - * @see #setDaemon(boolean) - * @see #setPriority(int) - * @see ThreadGroup#checkAccess() - * @see SecurityManager#checkAccess + * as a daemon thread. The method {@linkplain #setDaemon setDaemon} + * may be used to change whether or not a thread is a daemon. + * + * @param group + * the thread group. If {@code null} and there is a security + * manager, the group is determined by {@linkplain + * SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}. + * If there is not a security manager or {@code + * SecurityManager.getThreadGroup()} returns {@code null}, the group + * is set to the current thread's thread group. + * + * @param target + * the object whose {@code run} method is invoked when this thread + * is started. If {@code null}, this thread's run method is invoked. + * + * @param name + * the name of the new thread + * + * @throws SecurityException + * if the current thread cannot create a thread in the specified + * thread group or cannot override the context class loader methods. */ public Thread(ThreadGroup group, Runnable target, String name) { init(group, target, name, 0); } /** - * Allocates a new Thread object so that it has - * target as its run object, has the specified - * name as its name, belongs to the thread group referred to - * by group, and has the specified stack size. + * Allocates a new {@code Thread} object so that it has {@code target} + * as its run object, has the specified {@code name} as its name, + * and belongs to the thread group referred to by {@code group}, and has + * the specified stack size. * *

      This constructor is identical to {@link * #Thread(ThreadGroup,Runnable,String)} with the exception of the fact * that it allows the thread stack size to be specified. The stack size * is the approximate number of bytes of address space that the virtual * machine is to allocate for this thread's stack. The effect of the - * stackSize parameter, if any, is highly platform dependent. + * {@code stackSize} parameter, if any, is highly platform dependent. * *

      On some platforms, specifying a higher value for the - * stackSize parameter may allow a thread to achieve greater + * {@code stackSize} parameter may allow a thread to achieve greater * recursion depth before throwing a {@link StackOverflowError}. * Similarly, specifying a lower value may allow a greater number of * threads to exist concurrently without throwing an {@link @@ -564,9 +585,9 @@ class Thread implements Runnable { * the relationship between the value of the stackSize parameter * and the maximum recursion depth and concurrency level are * platform-dependent. On some platforms, the value of the - * stackSize parameter may have no effect whatsoever. + * {@code stackSize} parameter may have no effect whatsoever. * - *

      The virtual machine is free to treat the stackSize + *

      The virtual machine is free to treat the {@code stackSize} * parameter as a suggestion. If the specified value is unreasonably low * for the platform, the virtual machine may instead use some * platform-specific minimum value; if the specified value is unreasonably @@ -574,9 +595,9 @@ class Thread implements Runnable { * maximum. Likewise, the virtual machine is free to round the specified * value up or down as it sees fit (or to ignore it completely). * - *

      Specifying a value of zero for the stackSize parameter will + *

      Specifying a value of zero for the {@code stackSize} parameter will * cause this constructor to behave exactly like the - * Thread(ThreadGroup, Runnable, String) constructor. + * {@code Thread(ThreadGroup, Runnable, String)} constructor. * *

      Due to the platform-dependent nature of the behavior of this * constructor, extreme care should be exercised in its use. @@ -588,15 +609,32 @@ class Thread implements Runnable { * *

      Implementation note: Java platform implementers are encouraged to * document their implementation's behavior with respect to the - * stackSize parameter. - * - * @param group the thread group. - * @param target the object whose run method is called. - * @param name the name of the new thread. - * @param stackSize the desired stack size for the new thread, or - * zero to indicate that this parameter is to be ignored. - * @exception SecurityException if the current thread cannot create a - * thread in the specified thread group. + * {@code stackSize} parameter. + * + * + * @param group + * the thread group. If {@code null} and there is a security + * manager, the group is determined by {@linkplain + * SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}. + * If there is not a security manager or {@code + * SecurityManager.getThreadGroup()} returns {@code null}, the group + * is set to the current thread's thread group. + * + * @param target + * the object whose {@code run} method is invoked when this thread + * is started. If {@code null}, this thread's run method is invoked. + * + * @param name + * the name of the new thread + * + * @param stackSize + * the desired stack size for the new thread, or zero to indicate + * that this parameter is to be ignored. + * + * @throws SecurityException + * if the current thread cannot create a thread in the specified + * thread group + * * @since 1.4 */ public Thread(ThreadGroup group, Runnable target, String name, @@ -669,6 +707,7 @@ class Thread implements Runnable { * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ + @Override public void run() { if (target != null) { target.run(); @@ -1386,28 +1425,25 @@ class Thread implements Runnable { * Returns the context ClassLoader for this Thread. The context * ClassLoader is provided by the creator of the thread for use * by code running in this thread when loading classes and resources. - * If not set, the default is the ClassLoader context of the parent - * Thread. The context ClassLoader of the primordial thread is - * typically set to the class loader used to load the application. - * - *

      First, if there is a security manager, and the caller's class - * loader is not null and the caller's class loader is not the same as or - * an ancestor of the context class loader for the thread whose - * context class loader is being requested, then the security manager's - * checkPermission - * method is called with a - * RuntimePermission("getClassLoader") permission - * to see if it's ok to get the context ClassLoader.. + * If not {@linkplain #setContextClassLoader set}, the default is the + * ClassLoader context of the parent Thread. The context ClassLoader of the + * primordial thread is typically set to the class loader used to load the + * application. + * + *

      If a security manager is present, and the invoker's class loader is not + * {@code null} and is not the same as or an ancestor of the context class + * loader, then this method invokes the security manager's {@link + * SecurityManager#checkPermission(java.security.Permission) checkPermission} + * method with a {@link RuntimePermission RuntimePermission}{@code + * ("getClassLoader")} permission to verify that retrieval of the context + * class loader is permitted. + * + * @return the context ClassLoader for this Thread, or {@code null} + * indicating the system class loader (or, failing that, the + * bootstrap class loader) * - * @return the context ClassLoader for this Thread - * - * @throws SecurityException - * if a security manager exists and its - * checkPermission method doesn't allow - * getting the context ClassLoader. - * @see #setContextClassLoader - * @see SecurityManager#checkPermission - * @see RuntimePermission + * @throws SecurityException + * if the current thread cannot get the context ClassLoader * * @since 1.2 */ @@ -1428,21 +1464,22 @@ class Thread implements Runnable { /** * Sets the context ClassLoader for this Thread. The context * ClassLoader can be set when a thread is created, and allows - * the creator of the thread to provide the appropriate class loader - * to code running in the thread when loading classes and resources. + * the creator of the thread to provide the appropriate class loader, + * through {@code getContextClassLoader}, to code running in the thread + * when loading classes and resources. * - *

      First, if there is a security manager, its checkPermission - * method is called with a - * RuntimePermission("setContextClassLoader") permission - * to see if it's ok to set the context ClassLoader.. + *

      If a security manager is present, its {@link + * SecurityManager#checkPermission(java.security.Permission) checkPermission} + * method is invoked with a {@link RuntimePermission RuntimePermission}{@code + * ("setContextClassLoader")} permission to see if setting the context + * ClassLoader is permitted. * - * @param cl the context ClassLoader for this Thread + * @param cl + * the context ClassLoader for this Thread, or null indicating the + * system class loader (or, failing that, the bootstrap class loader) * - * @exception SecurityException if the current thread cannot set the - * context ClassLoader. - * @see #getContextClassLoader - * @see SecurityManager#checkPermission - * @see RuntimePermission + * @throws SecurityException + * if the current thread cannot set the context ClassLoader * * @since 1.2 */ diff --git a/test/java/lang/ThreadGroup/NullThreadName.java b/test/java/lang/ThreadGroup/NullThreadName.java new file mode 100644 index 000000000..844fd93e2 --- /dev/null +++ b/test/java/lang/ThreadGroup/NullThreadName.java @@ -0,0 +1,85 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6576763 + * @summary (thread) Thread constructors throw undocumented NPE for null name + */ + +/* + * Verify that threads constructed with a null thread name do not get added + * to the list of unstarted thread for a thread group. We can do this by + * checking that a daemon threadGroup is desroyed after its final valid thread + * has completed. + */ + +import java.util.concurrent.CountDownLatch; +import static java.lang.System.out; + +public class NullThreadName +{ + static CountDownLatch done = new CountDownLatch(1); + + public static void main(String args[]) throws Exception { + ThreadGroup tg = new ThreadGroup("chegar-threads"); + Thread goodThread = new Thread(tg, new GoodThread(), "goodThread"); + try { + Thread badThread = new Thread(tg, new Runnable(){ + @Override + public void run() {} }, null); + } catch (NullPointerException npe) { + out.println("OK, caught expected " + npe); + } + tg.setDaemon(true); + goodThread.start(); + + done.await(); + + int count = 0; + while (goodThread.isAlive()) { + /* Hold off a little to allow the thread to complete */ + out.println("GoodThread still alive, sleeping..."); + try { Thread.sleep(2000); } + catch (InterruptedException unused) {} + + /* do not wait forever */ + if (count++ > 5) + throw new AssertionError("GoodThread is still alive!"); + } + + if (!tg.isDestroyed()) { + throw new AssertionError("Failed: Thread group is not destroyed."); + } + } + + static class GoodThread implements Runnable + { + @Override + public void run() { + out.println("Good Thread started..."); + out.println("Good Thread finishing"); + done.countDown(); + } + } +} -- GitLab From 1b95ba6d97ad0dd9f5a6b63ba4fbbcb69270d148 Mon Sep 17 00:00:00 2001 From: swamyv Date: Fri, 29 Aug 2008 14:33:05 -0700 Subject: [PATCH 076/139] 6614052: jhat fails to read heap dump > 2GB. Summary: Modified the jhat code to use long for unsigned int. This is a forward port of changes from Kevin Walls. Reviewed-by: jjh --- .../sun/tools/hat/internal/parser/HprofReader.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/share/classes/com/sun/tools/hat/internal/parser/HprofReader.java b/src/share/classes/com/sun/tools/hat/internal/parser/HprofReader.java index df09dd5c5..1bbac940f 100644 --- a/src/share/classes/com/sun/tools/hat/internal/parser/HprofReader.java +++ b/src/share/classes/com/sun/tools/hat/internal/parser/HprofReader.java @@ -120,7 +120,7 @@ public class HprofReader extends Reader /* imports */ implements ArrayTypeCodes private int version; // The version of .hprof being read private int debugLevel; - private int currPos; // Current position in the file + private long currPos; // Current position in the file private int dumpsToSkip; private boolean callStack; // If true, read the call stack of objects @@ -196,7 +196,9 @@ public class HprofReader extends Reader /* imports */ implements ArrayTypeCodes break; } in.readInt(); // Timestamp of this record - int length = in.readInt(); + // Length of record: readInt() will return negative value for record + // length >2GB. so store 32bit value in long to keep it unsigned. + long length = in.readInt() & 0xffffffffL; if (debugLevel > 0) { System.out.println("Read record type " + type + ", length " + length @@ -211,7 +213,7 @@ public class HprofReader extends Reader /* imports */ implements ArrayTypeCodes switch (type) { case HPROF_UTF8: { long id = readID(); - byte[] chars = new byte[length - identifierSize]; + byte[] chars = new byte[(int)length - identifierSize]; in.readFully(chars); names.put(new Long(id), new String(chars)); break; @@ -351,8 +353,8 @@ public class HprofReader extends Reader /* imports */ implements ArrayTypeCodes return snapshot; } - private void skipBytes(int length) throws IOException { - in.skipBytes(length); + private void skipBytes(long length) throws IOException { + in.skipBytes((int)length); } private int readVersionHeader() throws IOException { @@ -381,7 +383,7 @@ public class HprofReader extends Reader /* imports */ implements ArrayTypeCodes throw new IOException("Version string not recognized at byte " + (pos+3)); } - private void readHeapDump(int bytesLeft, int posAtEnd) throws IOException { + private void readHeapDump(long bytesLeft, long posAtEnd) throws IOException { while (bytesLeft > 0) { int type = in.readUnsignedByte(); if (debugLevel > 0) { -- GitLab From ebf5be442168297864df19dda942d7589a87780b Mon Sep 17 00:00:00 2001 From: rupashka Date: Sat, 30 Aug 2008 17:29:59 +0400 Subject: [PATCH 077/139] 6554743: JFileChooser dn't close after pressing escape key after changing the views Summary: Restore focus after changing the views in JFileChooser Reviewed-by: loneid --- src/share/classes/sun/swing/FilePane.java | 108 +++++++++++----------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/src/share/classes/sun/swing/FilePane.java b/src/share/classes/sun/swing/FilePane.java index fd5a43086..298fd42b1 100644 --- a/src/share/classes/sun/swing/FilePane.java +++ b/src/share/classes/sun/swing/FilePane.java @@ -308,44 +308,80 @@ public class FilePane extends JPanel implements PropertyChangeListener { } public void setViewType(int viewType) { - int oldValue = this.viewType; - if (viewType == oldValue) { + if (viewType == this.viewType) { return; } + + int oldValue = this.viewType; this.viewType = viewType; + JPanel createdViewPanel = null; + Component newFocusOwner = null; + switch (viewType) { case VIEWTYPE_LIST: if (viewPanels[viewType] == null) { - JPanel p = fileChooserUIAccessor.createList(); - if (p == null) { - p = createList(); + createdViewPanel = fileChooserUIAccessor.createList(); + if (createdViewPanel == null) { + createdViewPanel = createList(); + } + + list = (JList) findChildComponent(createdViewPanel, JList.class); + if (listSelectionModel == null) { + listSelectionModel = list.getSelectionModel(); + if (detailsTable != null) { + detailsTable.setSelectionModel(listSelectionModel); + } + } else { + list.setSelectionModel(listSelectionModel); } - setViewPanel(viewType, p); } list.setLayoutOrientation(JList.VERTICAL_WRAP); + newFocusOwner = list; break; case VIEWTYPE_DETAILS: if (viewPanels[viewType] == null) { - JPanel p = fileChooserUIAccessor.createDetailsView(); - if (p == null) { - p = createDetailsView(); + createdViewPanel = fileChooserUIAccessor.createDetailsView(); + if (createdViewPanel == null) { + createdViewPanel = createDetailsView(); + } + + detailsTable = (JTable) findChildComponent(createdViewPanel, JTable.class); + detailsTable.setRowHeight(Math.max(detailsTable.getFont().getSize() + 4, 16 + 1)); + if (listSelectionModel != null) { + detailsTable.setSelectionModel(listSelectionModel); } - setViewPanel(viewType, p); } + newFocusOwner = detailsTable; break; } - JPanel oldViewPanel = currentViewPanel; + + if (createdViewPanel != null) { + viewPanels[viewType] = createdViewPanel; + recursivelySetInheritsPopupMenu(createdViewPanel, true); + } + + boolean isFocusOwner = false; + + if (currentViewPanel != null) { + Component owner = DefaultKeyboardFocusManager. + getCurrentKeyboardFocusManager().getPermanentFocusOwner(); + + isFocusOwner = owner == detailsTable || owner == list; + + remove(currentViewPanel); + } + currentViewPanel = viewPanels[viewType]; - if (currentViewPanel != oldViewPanel) { - if (oldViewPanel != null) { - remove(oldViewPanel); - } - add(currentViewPanel, BorderLayout.CENTER); - revalidate(); - repaint(); + add(currentViewPanel, BorderLayout.CENTER); + + if (isFocusOwner && newFocusOwner != null) { + newFocusOwner.requestFocusInWindow(); } + + revalidate(); + repaint(); updateViewMenu(); firePropertyChange("viewType", oldValue, viewType); } @@ -385,42 +421,6 @@ public class FilePane extends JPanel implements PropertyChangeListener { } } - public void setViewPanel(int viewType, JPanel viewPanel) { - viewPanels[viewType] = viewPanel; - recursivelySetInheritsPopupMenu(viewPanel, true); - - switch (viewType) { - case VIEWTYPE_LIST: - list = (JList)findChildComponent(viewPanels[viewType], JList.class); - if (listSelectionModel == null) { - listSelectionModel = list.getSelectionModel(); - if (detailsTable != null) { - detailsTable.setSelectionModel(listSelectionModel); - } - } else { - list.setSelectionModel(listSelectionModel); - } - break; - - case VIEWTYPE_DETAILS: - detailsTable = (JTable)findChildComponent(viewPanels[viewType], JTable.class); - detailsTable.setRowHeight(Math.max(detailsTable.getFont().getSize() + 4, 16+1)); - if (listSelectionModel != null) { - detailsTable.setSelectionModel(listSelectionModel); - } - break; - } - if (this.viewType == viewType) { - if (currentViewPanel != null) { - remove(currentViewPanel); - } - currentViewPanel = viewPanel; - add(currentViewPanel, BorderLayout.CENTER); - revalidate(); - repaint(); - } - } - protected void installDefaults() { Locale l = getFileChooser().getLocale(); -- GitLab From 70763cd4017a941320478ae0a6acee8dad1934fc Mon Sep 17 00:00:00 2001 From: alanb Date: Sun, 31 Aug 2008 18:32:59 +0100 Subject: [PATCH 078/139] 6570619: (bf) DirectByteBuffer.get/put(byte[]) does not scale well Reviewed-by: iris --- make/java/java/mapfile-vers | 2 - src/share/classes/java/nio/Bits.java | 62 +++++++++++++++++-- .../classes/java/nio/Direct-X-Buffer.java | 17 +++-- src/share/native/java/nio/Bits.c | 40 ------------ 4 files changed, 70 insertions(+), 51 deletions(-) diff --git a/make/java/java/mapfile-vers b/make/java/java/mapfile-vers index ad0380979..77a78301a 100644 --- a/make/java/java/mapfile-vers +++ b/make/java/java/mapfile-vers @@ -222,8 +222,6 @@ SUNWprivate_1.1 { Java_java_lang_UNIXProcess_waitForProcessExit; Java_java_lang_UNIXProcess_forkAndExec; Java_java_lang_UNIXProcess_destroyProcess; - Java_java_nio_Bits_copyFromByteArray; - Java_java_nio_Bits_copyToByteArray; Java_java_nio_Bits_copyFromShortArray; Java_java_nio_Bits_copyToShortArray; Java_java_nio_Bits_copyFromIntArray; diff --git a/src/share/classes/java/nio/Bits.java b/src/share/classes/java/nio/Bits.java index ee74686c9..277fb4226 100644 --- a/src/share/classes/java/nio/Bits.java +++ b/src/share/classes/java/nio/Bits.java @@ -735,14 +735,68 @@ class Bits { // package-private static final int JNI_COPY_TO_ARRAY_THRESHOLD = 6; static final int JNI_COPY_FROM_ARRAY_THRESHOLD = 6; + // This number limits the number of bytes to copy per call to Unsafe's + // copyMemory method. A limit is imposed to allow for safepoint polling + // during a large copy + static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L; + // These methods do no bounds checking. Verification that the copy will not // result in memory corruption should be done prior to invocation. // All positions and lengths are specified in bytes. - static native void copyFromByteArray(Object src, long srcPos, long dstAddr, - long length); - static native void copyToByteArray(long srcAddr, Object dst, long dstPos, - long length); + /** + * Copy from given source array to destination address. + * + * @param src + * source array + * @param srcBaseOffset + * offset of first element of storage in source array + * @param srcPos + * offset within source array of the first element to read + * @param dstAddr + * destination address + * @param length + * number of bytes to copy + */ + static void copyFromArray(Object src, long srcBaseOffset, long srcPos, + long dstAddr, long length) + { + long offset = srcBaseOffset + srcPos; + while (length > 0) { + long size = (length > UNSAFE_COPY_THRESHOLD) ? UNSAFE_COPY_THRESHOLD : length; + unsafe.copyMemory(src, offset, null, dstAddr, size); + length -= size; + offset += size; + dstAddr += size; + } + } + + /** + * Copy from source address into given destination array. + * + * @param srcAddr + * source address + * @param dst + * destination array + * @param dstBaseOffset + * offset of first element of storage in destination array + * @param dstPos + * offset within destination array of the first element to write + * @param length + * number of bytes to copy + */ + static void copyToArray(long srcAddr, Object dst, long dstBaseOffset, long dstPos, + long length) + { + long offset = dstBaseOffset + dstPos; + while (length > 0) { + long size = (length > UNSAFE_COPY_THRESHOLD) ? UNSAFE_COPY_THRESHOLD : length; + unsafe.copyMemory(null, srcAddr, dst, offset, size); + length -= size; + srcAddr += size; + offset += size; + } + } static void copyFromCharArray(Object src, long srcPos, long dstAddr, long length) diff --git a/src/share/classes/java/nio/Direct-X-Buffer.java b/src/share/classes/java/nio/Direct-X-Buffer.java index 0ad03feef..d1cfb6b5c 100644 --- a/src/share/classes/java/nio/Direct-X-Buffer.java +++ b/src/share/classes/java/nio/Direct-X-Buffer.java @@ -47,6 +47,9 @@ class Direct$Type$Buffer$RW$$BO$ // Cached unsafe-access object protected static final Unsafe unsafe = Bits.unsafe(); + // Cached array base offset + private static final long arrayBaseOffset = (long)unsafe.arrayBaseOffset($type$[].class); + // Cached unaligned-access capability protected static final boolean unaligned = Bits.unaligned(); @@ -242,14 +245,16 @@ class Direct$Type$Buffer$RW$$BO$ if (length > rem) throw new BufferUnderflowException(); +#if[!byte] if (order() != ByteOrder.nativeOrder()) Bits.copyTo$Memtype$Array(ix(pos), dst, offset << $LG_BYTES_PER_VALUE$, length << $LG_BYTES_PER_VALUE$); else - Bits.copyToByteArray(ix(pos), dst, - offset << $LG_BYTES_PER_VALUE$, - length << $LG_BYTES_PER_VALUE$); +#end[!byte] + Bits.copyToArray(ix(pos), dst, arrayBaseOffset, + offset << $LG_BYTES_PER_VALUE$, + length << $LG_BYTES_PER_VALUE$); position(pos + length); } else { super.get(dst, offset, length); @@ -332,12 +337,14 @@ class Direct$Type$Buffer$RW$$BO$ if (length > rem) throw new BufferOverflowException(); +#if[!byte] if (order() != ByteOrder.nativeOrder()) Bits.copyFrom$Memtype$Array(src, offset << $LG_BYTES_PER_VALUE$, ix(pos), length << $LG_BYTES_PER_VALUE$); else - Bits.copyFromByteArray(src, offset << $LG_BYTES_PER_VALUE$, - ix(pos), length << $LG_BYTES_PER_VALUE$); +#end[!byte] + Bits.copyFromArray(src, arrayBaseOffset, offset << $LG_BYTES_PER_VALUE$, + ix(pos), length << $LG_BYTES_PER_VALUE$); position(pos + length); } else { super.put(src, offset, length); diff --git a/src/share/native/java/nio/Bits.c b/src/share/native/java/nio/Bits.c index 0227d6de8..6c87d37fc 100644 --- a/src/share/native/java/nio/Bits.c +++ b/src/share/native/java/nio/Bits.c @@ -67,46 +67,6 @@ #define SWAPLONG(x) ((jlong)(((jlong)SWAPINT((jint)(x)) << 32) | \ ((jlong)SWAPINT((jint)((x) >> 32)) & 0xffffffff))) -JNIEXPORT void JNICALL -Java_java_nio_Bits_copyFromByteArray(JNIEnv *env, jobject this, jobject src, - jlong srcPos, jlong dstAddr, jlong length) -{ - jbyte *bytes; - size_t size; - - while (length > 0) { - size = (length > MBYTE ? MBYTE : length); - - GETCRITICAL(bytes, env, src); - memcpy((void *)dstAddr, bytes + srcPos, size); - RELEASECRITICAL(bytes, env, src, JNI_ABORT); - - length -= size; - dstAddr += size; - srcPos += size; - } -} - -JNIEXPORT void JNICALL -Java_java_nio_Bits_copyToByteArray(JNIEnv *env, jobject this, jlong srcAddr, - jobject dst, jlong dstPos, jlong length) -{ - jbyte *bytes; - size_t size; - - while (length > 0) { - size = (length > MBYTE ? MBYTE : length); - - GETCRITICAL(bytes, env, dst); - memcpy(bytes + dstPos, (void *)srcAddr, size); - RELEASECRITICAL(bytes, env, dst, 0); - - length -= size; - srcAddr += size; - dstPos += size; - } -} - JNIEXPORT void JNICALL Java_java_nio_Bits_copyFromShortArray(JNIEnv *env, jobject this, jobject src, jlong srcPos, jlong dstAddr, jlong length) -- GitLab From ec347b32b9e9564748de9b56b043d76e4d07322b Mon Sep 17 00:00:00 2001 From: alanb Date: Sun, 31 Aug 2008 18:39:01 +0100 Subject: [PATCH 079/139] 4640544: New I/O: Complete socket-channel functionality Reviewed-by: iris, sherman, chegar --- make/java/nio/FILES_java.gmk | 19 +- make/java/nio/Makefile | 45 +- make/java/nio/mapfile-linux | 41 +- make/java/nio/mapfile-solaris | 41 +- make/mksample/nio/Makefile | 2 +- .../mksample/nio/multicast/Makefile | 35 +- .../classes/java/net/NetworkInterface.java | 1 - .../classes/java/net/ProtocolFamily.java | 39 ++ src/share/classes/java/net/SocketOption.java | 55 ++ .../java/net/StandardProtocolFamily.java | 45 ++ .../java/net/StandardSocketOption.java | 352 +++++++++++++ .../java/nio/channels/DatagramChannel.java | 157 +++++- .../java/nio/channels/MembershipKey.java | 183 +++++++ .../java/nio/channels/MulticastChannel.java | 211 ++++++++ .../java/nio/channels/NetworkChannel.java | 158 ++++++ .../nio/channels/ServerSocketChannel.java | 127 ++++- .../java/nio/channels/SocketChannel.java | 150 +++++- .../classes/java/nio/channels/exceptions | 11 + .../java/nio/channels/package-info.java | 231 ++++++++ .../classes/java/nio/channels/package.html | 222 -------- .../nio/channels/spi/SelectorProvider.java | 24 +- .../sun/nio/ch/DatagramChannelImpl.java | 497 +++++++++++++++--- .../sun/nio/ch/DatagramSocketAdaptor.java | 108 ++-- .../sun/nio/ch/ExtendedSocketOption.java | 44 ++ .../classes/sun/nio/ch/MembershipKeyImpl.java | 222 ++++++++ .../sun/nio/ch/MembershipRegistry.java | 129 +++++ src/share/classes/sun/nio/ch/Net.java | 370 +++++++++++-- .../classes/sun/nio/ch/OptionAdaptor.java | 229 -------- src/share/classes/sun/nio/ch/OptionKey.java | 48 ++ .../sun/nio/ch/SelectorProviderImpl.java | 6 +- .../sun/nio/ch/ServerSocketAdaptor.java | 38 +- .../sun/nio/ch/ServerSocketChannelImpl.java | 102 ++-- .../classes/sun/nio/ch/SocketAdaptor.java | 126 +++-- .../classes/sun/nio/ch/SocketChannelImpl.java | 205 +++++--- src/share/classes/sun/nio/ch/SocketOpts.java | 115 ---- .../classes/sun/nio/ch/SocketOptsImpl.java | 318 ----------- src/share/native/java/net/net_util.c | 4 +- src/share/native/java/net/net_util.h | 6 +- .../sun/nio/ch/genSocketOptionRegistry.c | 129 +++++ .../nio/multicast/MulticastAddress.java | 127 +++++ src/share/sample/nio/multicast/Reader.java | 142 +++++ src/share/sample/nio/multicast/Sender.java | 71 +++ src/solaris/native/java/net/net_util_md.c | 2 +- .../native/sun/nio/ch/DatagramChannelImpl.c | 4 +- src/solaris/native/sun/nio/ch/FileKey.c | 6 - src/solaris/native/sun/nio/ch/Net.c | 492 ++++++++++++----- .../sun/nio/ch/ServerSocketChannelImpl.c | 8 - .../native/sun/nio/ch/SocketChannelImpl.c | 13 - src/solaris/native/sun/nio/ch/nio_util.h | 7 + src/windows/native/java/net/net_util_md.c | 2 +- .../native/sun/nio/ch/DatagramChannelImpl.c | 148 ++---- src/windows/native/sun/nio/ch/Net.c | 394 +++++++++++--- .../sun/nio/ch/ServerSocketChannelImpl.c | 26 +- .../native/sun/nio/ch/SocketChannelImpl.c | 9 - .../DatagramChannel/BasicMulticastTests.java | 220 ++++++++ .../MulticastSendReceiveTests.java | 220 ++++++++ .../DatagramChannel/NetworkConfiguration.java | 97 ++++ .../DatagramChannel/SocketOptionTests.java | 115 ++++ .../SocketOptionTests.java | 84 +++ .../SocketChannel/SocketOptionTests.java | 129 +++++ test/java/nio/channels/TestUtil.java | 3 +- .../nio/channels/etc/NetworkChannelTests.java | 187 +++++++ 62 files changed, 5728 insertions(+), 1623 deletions(-) rename src/share/classes/sun/nio/ch/exceptions => make/mksample/nio/multicast/Makefile (65%) create mode 100644 src/share/classes/java/net/ProtocolFamily.java create mode 100644 src/share/classes/java/net/SocketOption.java create mode 100644 src/share/classes/java/net/StandardProtocolFamily.java create mode 100644 src/share/classes/java/net/StandardSocketOption.java create mode 100644 src/share/classes/java/nio/channels/MembershipKey.java create mode 100644 src/share/classes/java/nio/channels/MulticastChannel.java create mode 100644 src/share/classes/java/nio/channels/NetworkChannel.java create mode 100644 src/share/classes/java/nio/channels/package-info.java delete mode 100644 src/share/classes/java/nio/channels/package.html create mode 100644 src/share/classes/sun/nio/ch/ExtendedSocketOption.java create mode 100644 src/share/classes/sun/nio/ch/MembershipKeyImpl.java create mode 100644 src/share/classes/sun/nio/ch/MembershipRegistry.java delete mode 100644 src/share/classes/sun/nio/ch/OptionAdaptor.java create mode 100644 src/share/classes/sun/nio/ch/OptionKey.java delete mode 100644 src/share/classes/sun/nio/ch/SocketOpts.java delete mode 100644 src/share/classes/sun/nio/ch/SocketOptsImpl.java create mode 100644 src/share/native/sun/nio/ch/genSocketOptionRegistry.c create mode 100644 src/share/sample/nio/multicast/MulticastAddress.java create mode 100644 src/share/sample/nio/multicast/Reader.java create mode 100644 src/share/sample/nio/multicast/Sender.java create mode 100644 test/java/nio/channels/DatagramChannel/BasicMulticastTests.java create mode 100644 test/java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java create mode 100644 test/java/nio/channels/DatagramChannel/NetworkConfiguration.java create mode 100644 test/java/nio/channels/DatagramChannel/SocketOptionTests.java create mode 100644 test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java create mode 100644 test/java/nio/channels/SocketChannel/SocketOptionTests.java create mode 100644 test/java/nio/channels/etc/NetworkChannelTests.java diff --git a/make/java/nio/FILES_java.gmk b/make/java/nio/FILES_java.gmk index c99f55204..8a6177b0a 100644 --- a/make/java/nio/FILES_java.gmk +++ b/make/java/nio/FILES_java.gmk @@ -39,6 +39,9 @@ FILES_src = \ java/nio/channels/FileLock.java \ java/nio/channels/GatheringByteChannel.java \ java/nio/channels/InterruptibleChannel.java \ + java/nio/channels/MembershipKey.java \ + java/nio/channels/MulticastChannel.java \ + java/nio/channels/NetworkChannel.java \ java/nio/channels/ReadableByteChannel.java \ java/nio/channels/ScatteringByteChannel.java \ java/nio/channels/SelectableChannel.java \ @@ -73,6 +76,7 @@ FILES_src = \ sun/nio/ch/DatagramSocketAdaptor.java \ sun/nio/ch/DefaultSelectorProvider.java \ sun/nio/ch/DirectBuffer.java \ + sun/nio/ch/ExtendedSocketOption.java \ sun/nio/ch/FileChannelImpl.java \ sun/nio/ch/FileDispatcher.java \ sun/nio/ch/FileKey.java \ @@ -80,12 +84,14 @@ FILES_src = \ sun/nio/ch/IOUtil.java \ sun/nio/ch/IOStatus.java \ sun/nio/ch/IOVecWrapper.java \ + sun/nio/ch/MembershipKeyImpl.java \ + sun/nio/ch/MembershipRegistry.java \ sun/nio/ch/NativeDispatcher.java \ sun/nio/ch/NativeObject.java \ sun/nio/ch/NativeThread.java \ sun/nio/ch/NativeThreadSet.java \ sun/nio/ch/Net.java \ - sun/nio/ch/OptionAdaptor.java \ + sun/nio/ch/OptionKey.java \ sun/nio/ch/PipeImpl.java \ sun/nio/ch/PollArrayWrapper.java \ sun/nio/ch/Reflect.java \ @@ -99,8 +105,7 @@ FILES_src = \ sun/nio/ch/SocketAdaptor.java \ sun/nio/ch/SocketChannelImpl.java \ sun/nio/ch/SocketDispatcher.java \ - sun/nio/ch/SocketOpts.java \ - sun/nio/ch/SocketOptsImpl.java \ + sun/nio/ch/SocketOptionRegistry.java \ sun/nio/ch/SourceChannelImpl.java \ sun/nio/ch/Util.java \ \ @@ -240,6 +245,7 @@ FILES_gen_ex = \ java/nio/InvalidMarkException.java \ java/nio/ReadOnlyBufferException.java \ \ + java/nio/channels/AlreadyBoundException.java \ java/nio/channels/AlreadyConnectedException.java \ java/nio/channels/AsynchronousCloseException.java \ java/nio/channels/ClosedByInterruptException.java \ @@ -258,14 +264,15 @@ FILES_gen_ex = \ java/nio/channels/UnresolvedAddressException.java \ java/nio/channels/UnsupportedAddressTypeException.java \ \ - sun/nio/ch/AlreadyBoundException.java \ - \ java/nio/charset/CharacterCodingException.java \ java/nio/charset/IllegalCharsetNameException.java \ java/nio/charset/UnsupportedCharsetException.java FILES_gen_csp = sun/nio/cs/StandardCharsets.java -FILES_gen = $(FILES_gen_coder) $(FILES_gen_buffer) $(FILES_gen_ex) $(FILES_gen_csp) +FILES_gen_sor = sun/nio/ch/SocketOptionRegistry.java + +FILES_gen = $(FILES_gen_coder) $(FILES_gen_buffer) $(FILES_gen_ex) \ + $(FILES_gen_csp) $(FILES_gen_sor) FILES_java = $(FILES_src) $(FILES_gen) diff --git a/make/java/nio/Makefile b/make/java/nio/Makefile index cff9ce5ce..26c2f2fc6 100644 --- a/make/java/nio/Makefile +++ b/make/java/nio/Makefile @@ -56,18 +56,18 @@ FILES_java += \ sun/nio/ch/DevPollSelectorProvider.java \ sun/nio/ch/InheritedChannel.java \ sun/nio/ch/PollSelectorProvider.java \ - sun/nio/ch/PollSelectorImpl.java + sun/nio/ch/PollSelectorImpl.java FILES_c += \ DevPollArrayWrapper.c \ InheritedChannel.c \ - PollArrayWrapper.c \ - NativeThread.c + NativeThread.c \ + PollArrayWrapper.c FILES_export += \ sun/nio/ch/DevPollArrayWrapper.java \ sun/nio/ch/InheritedChannel.java \ - sun/nio/ch/NativeThread.java + sun/nio/ch/NativeThread.java endif # PLATFORM = solaris ifeq ($(PLATFORM), windows) @@ -94,14 +94,14 @@ FILES_java += \ FILES_c += \ EPollArrayWrapper.c \ - PollArrayWrapper.c \ InheritedChannel.c \ - NativeThread.c + NativeThread.c \ + PollArrayWrapper.c FILES_export += \ sun/nio/ch/EPollArrayWrapper.java \ sun/nio/ch/InheritedChannel.java \ - sun/nio/ch/NativeThread.java + sun/nio/ch/NativeThread.java endif # PLATFORM = linux # Find platform-specific C source files @@ -618,12 +618,6 @@ $(BUF_GEN)/%Exception.java: genExceptions.sh $(BUF_SRC)/exceptions @$(RM) $@.temp $(GEN_EX_CMD) $(BUF_SRC)/exceptions $(BUF_GEN) -$(SCH_GEN)/%Exception.java: genExceptions.sh $(SCH_SRC)/exceptions - $(prep-target) - @$(RM) $@.temp - $(GEN_EX_CMD) $(SCH_SRC)/exceptions $(SCH_GEN) - - # # Generated charset-provider classes # @@ -638,4 +632,29 @@ $(SCS_GEN)/StandardCharsets.java: genCharsetProvider.sh \ HASHER="$(BOOT_JAVA_CMD) -jar $(HASHER_JARFILE)" \ $(SH) -e genCharsetProvider.sh $(SCS_SRC)/standard-charsets $(SCS_GEN) +# +# Generated channel implementation classes. +# C source is compiled in TEMPDIR to avoid turds left by Windows compilers. +# + +GENSOR_SRC = $(SHARE_SRC)/native/sun/nio/ch/genSocketOptionRegistry.c + +GENSOR_EXE = $(TEMPDIR)/genSocketOptionRegistry$(EXE_SUFFIX) + +SOR_COPYRIGHT_YEARS = $(shell $(CAT) $(GENSOR_SRC) | \ + $(NAWK) '/^.*Copyright.*Sun/ { print $$3 }') + +$(TEMPDIR)/$(GENSOR_SRC) : $(GENSOR_SRC) + $(install-file) + +$(GENSOR_EXE) : $(TEMPDIR)/$(GENSOR_SRC) + $(prep-target) + ($(CD) $(TEMPDIR); $(CC) $(CPPFLAGS) $(LDDFLAGS) \ + -o genSocketOptionRegistry$(EXE_SUFFIX) $(GENSOR_SRC)) + +$(SCH_GEN)/SocketOptionRegistry.java: $(GENSOR_EXE) + $(prep-target) + NAWK="$(NAWK)" SH="$(SH)" $(SH) -e addNotices.sh $(SOR_COPYRIGHT_YEARS) > $@ + $(GENSOR_EXE) >> $@ + .PHONY: sources diff --git a/make/java/nio/mapfile-linux b/make/java/nio/mapfile-linux index 2eba69d17..3fb47b0eb 100644 --- a/make/java/nio/mapfile-linux +++ b/make/java/nio/mapfile-linux @@ -1,3 +1,27 @@ +# +# Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code 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 General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# SUNWprivate_1.1 { global: @@ -61,20 +85,29 @@ SUNWprivate_1.1 { Java_sun_nio_ch_NativeThread_init; Java_sun_nio_ch_NativeThread_signal; Java_sun_nio_ch_Net_socket0; - Java_sun_nio_ch_Net_bind; - Java_sun_nio_ch_Net_connect; + Java_sun_nio_ch_Net_bind0; + Java_sun_nio_ch_Net_connect0; + Java_sun_nio_ch_Net_listen; Java_sun_nio_ch_Net_localPort; Java_sun_nio_ch_Net_localInetAddress; Java_sun_nio_ch_Net_getIntOption0; Java_sun_nio_ch_Net_setIntOption0; Java_sun_nio_ch_Net_initIDs; + Java_sun_nio_ch_Net_isIPv6Available0; + Java_sun_nio_ch_Net_joinOrDrop4; + Java_sun_nio_ch_Net_blockOrUnblock4; + Java_sun_nio_ch_Net_joinOrDrop6; + Java_sun_nio_ch_Net_blockOrUnblock6; + Java_sun_nio_ch_Net_setInterface4; + Java_sun_nio_ch_Net_getInterface4; + Java_sun_nio_ch_Net_setInterface6; + Java_sun_nio_ch_Net_getInterface6; + Java_sun_nio_ch_Net_shutdown; Java_sun_nio_ch_PollArrayWrapper_interrupt; Java_sun_nio_ch_PollArrayWrapper_poll0; Java_sun_nio_ch_ServerSocketChannelImpl_accept0; Java_sun_nio_ch_ServerSocketChannelImpl_initIDs; - Java_sun_nio_ch_ServerSocketChannelImpl_listen; Java_sun_nio_ch_SocketChannelImpl_checkConnect; - Java_sun_nio_ch_SocketChannelImpl_shutdown; local: *; diff --git a/make/java/nio/mapfile-solaris b/make/java/nio/mapfile-solaris index 77ef8ea2a..6e109e2fa 100644 --- a/make/java/nio/mapfile-solaris +++ b/make/java/nio/mapfile-solaris @@ -1,3 +1,27 @@ +# +# Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code 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 General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# SUNWprivate_1.1 { global: @@ -59,20 +83,29 @@ SUNWprivate_1.1 { Java_sun_nio_ch_NativeThread_init; Java_sun_nio_ch_NativeThread_signal; Java_sun_nio_ch_Net_socket0; - Java_sun_nio_ch_Net_bind; - Java_sun_nio_ch_Net_connect; + Java_sun_nio_ch_Net_bind0; + Java_sun_nio_ch_Net_connect0; + Java_sun_nio_ch_Net_listen; Java_sun_nio_ch_Net_localPort; Java_sun_nio_ch_Net_localInetAddress; Java_sun_nio_ch_Net_getIntOption0; Java_sun_nio_ch_Net_setIntOption0; Java_sun_nio_ch_Net_initIDs; + Java_sun_nio_ch_Net_isIPv6Available0; + Java_sun_nio_ch_Net_joinOrDrop4; + Java_sun_nio_ch_Net_blockOrUnblock4; + Java_sun_nio_ch_Net_joinOrDrop6; + Java_sun_nio_ch_Net_blockOrUnblock6; + Java_sun_nio_ch_Net_setInterface4; + Java_sun_nio_ch_Net_getInterface4; + Java_sun_nio_ch_Net_setInterface6; + Java_sun_nio_ch_Net_getInterface6; + Java_sun_nio_ch_Net_shutdown; Java_sun_nio_ch_PollArrayWrapper_interrupt; Java_sun_nio_ch_PollArrayWrapper_poll0; Java_sun_nio_ch_ServerSocketChannelImpl_accept0; Java_sun_nio_ch_ServerSocketChannelImpl_initIDs; - Java_sun_nio_ch_ServerSocketChannelImpl_listen; Java_sun_nio_ch_SocketChannelImpl_checkConnect; - Java_sun_nio_ch_SocketChannelImpl_shutdown; local: *; diff --git a/make/mksample/nio/Makefile b/make/mksample/nio/Makefile index a6e8fc46d..1f17a4cce 100644 --- a/make/mksample/nio/Makefile +++ b/make/mksample/nio/Makefile @@ -31,7 +31,7 @@ BUILDDIR = ../.. PRODUCT = java include $(BUILDDIR)/common/Defs.gmk -SUBDIRS = server +SUBDIRS = multicast server all build clean clobber:: $(SUBDIRS-loop) diff --git a/src/share/classes/sun/nio/ch/exceptions b/make/mksample/nio/multicast/Makefile similarity index 65% rename from src/share/classes/sun/nio/ch/exceptions rename to make/mksample/nio/multicast/Makefile index 55d295f87..05a153a13 100644 --- a/src/share/classes/sun/nio/ch/exceptions +++ b/make/mksample/nio/multicast/Makefile @@ -1,5 +1,5 @@ # -# Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -23,17 +23,30 @@ # have any questions. # -# Generated exception classes for sun.nio.ch +# +# Makefile for the nio/multicast sample code +# + +BUILDDIR = ../../.. + +PRODUCT = java + +include $(BUILDDIR)/common/Defs.gmk + +SAMPLE_SRC_DIR = $(SHARE_SRC)/sample/nio/multicast +SAMPLE_DST_DIR = $(SAMPLEDIR)/nio/multicast + +SAMPLE_FILES = \ + $(SAMPLE_DST_DIR)/Reader.java \ + $(SAMPLE_DST_DIR)/Sender.java \ + $(SAMPLE_DST_DIR)/MulticastAddress.java -SINCE=1.4 -PACKAGE=sun.nio.ch -# This year should only change if the generated source is modified. -COPYRIGHT_YEARS=2000-2007 +all build: $(SAMPLE_FILES) +$(SAMPLE_DST_DIR)/%: $(SAMPLE_SRC_DIR)/% + $(install-file) -SUPER=IllegalStateException +clean clobber: + $(RM) -r $(SAMPLE_DST_DIR) -gen AlreadyBoundException " - * Unchecked exception thrown when an attempt is made to bind a {@link - * SocketChannel} that is already bound." \ - 9002280723481772026L +.PHONY: all build clean clobber diff --git a/src/share/classes/java/net/NetworkInterface.java b/src/share/classes/java/net/NetworkInterface.java index a4f4bc9c6..da9503d57 100644 --- a/src/share/classes/java/net/NetworkInterface.java +++ b/src/share/classes/java/net/NetworkInterface.java @@ -25,7 +25,6 @@ package java.net; -import java.net.SocketException; import java.util.Enumeration; import java.util.NoSuchElementException; import sun.security.action.*; diff --git a/src/share/classes/java/net/ProtocolFamily.java b/src/share/classes/java/net/ProtocolFamily.java new file mode 100644 index 000000000..b997c7bb0 --- /dev/null +++ b/src/share/classes/java/net/ProtocolFamily.java @@ -0,0 +1,39 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.net; + +/** + * Represents a family of communication protocols. + * + * @since 1.7 + */ + +public interface ProtocolFamily { + /** + * Returns the name of the protocol family. + */ + String name(); +} diff --git a/src/share/classes/java/net/SocketOption.java b/src/share/classes/java/net/SocketOption.java new file mode 100644 index 000000000..afc9714b5 --- /dev/null +++ b/src/share/classes/java/net/SocketOption.java @@ -0,0 +1,55 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.net; + +/** + * A socket option associated with a socket. + * + *

      In the {@link java.nio.channels channels} package, the {@link + * java.nio.channels.NetworkChannel} interface defines the {@link + * java.nio.channels.NetworkChannel#setOption(SocketOption,Object) setOption} + * and {@link java.nio.channels.NetworkChannel#getOption(SocketOption) getOption} + * methods to set and query the channel's socket options. + * + * @param The type of the socket option value. + * + * @since 1.7 + * + * @see StandardSocketOption + */ + +public interface SocketOption { + + /** + * Returns the name of the socket option. + */ + String name(); + + /** + * Returns the type of the socket option value. + */ + Class type(); +} diff --git a/src/share/classes/java/net/StandardProtocolFamily.java b/src/share/classes/java/net/StandardProtocolFamily.java new file mode 100644 index 000000000..7c11b32f5 --- /dev/null +++ b/src/share/classes/java/net/StandardProtocolFamily.java @@ -0,0 +1,45 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.net; + +/** + * Defines the standard family of communication protocols. + * + * @since 1.7 + */ + +public enum StandardProtocolFamily implements ProtocolFamily { + + /** + * Internet Protocol Version 4 (IPv4) + */ + INET, + + /** + * Internet Protocol Version 6 (IPv6) + */ + INET6 +} diff --git a/src/share/classes/java/net/StandardSocketOption.java b/src/share/classes/java/net/StandardSocketOption.java new file mode 100644 index 000000000..405038cc1 --- /dev/null +++ b/src/share/classes/java/net/StandardSocketOption.java @@ -0,0 +1,352 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.net; + +/** + * Defines the standard socket options. + * + *

      The {@link SocketOption#name name} of each socket option defined by this + * class is its field name. + * + *

      In this release, the socket options defined here are used by {@link + * java.nio.channels.NetworkChannel network} channels in the {@link + * java.nio.channels channels} package. + * + * @since 1.7 + */ + +public final class StandardSocketOption { + private StandardSocketOption() { } + + // -- SOL_SOCKET -- + + /** + * Allow transmission of broadcast datagrams. + * + *

      The value of this socket option is a {@code Boolean} that represents + * whether the option is enabled or disabled. The option is specific to + * datagram-oriented sockets sending to {@link java.net.Inet4Address IPv4} + * broadcast addresses. When the socket option is enabled then the socket + * can be used to send broadcast datagrams. + * + *

      The initial value of this socket option is {@code FALSE}. The socket + * option may be enabled or disabled at any time. Some operating systems may + * require that the Java virtual machine be started with implementation + * specific privileges to enable this option or send broadcast datagrams. + * + * @see RFC 929: + * Broadcasting Internet Datagrams + */ + public static final SocketOption SO_BROADCAST = + new StdSocketOption("SO_BROADCAST", Boolean.class); + + /** + * Keep connection alive. + * + *

      The value of this socket option is a {@code Boolean} that represents + * whether the option is enabled or disabled. When the {@code SO_KEEPALIVE} + * option is enabled the operating system may use a keep-alive + * mechanism to periodically probe the other end of a connection when the + * connection is otherwise idle. The exact semantics of the keep alive + * mechanism is system dependent and therefore unspecified. + * + *

      The initial value of this socket option is {@code FALSE}. The socket + * option may be enabled or disabled at any time. + * + * @see RFC 1122 + * Requirements for Internet Hosts -- Communication Layers + */ + public static final SocketOption SO_KEEPALIVE = + new StdSocketOption("SO_KEEPALIVE", Boolean.class); + + /** + * The size of the socket send buffer. + * + *

      The value of this socket option is an {@code Integer} that is the + * size of the socket send buffer in bytes. The socket send buffer is an + * output buffer used by the networking implementation. It may need to be + * increased for high-volume connections. The value of the socket option is + * a hint to the implementation to size the buffer and the actual + * size may differ. The socket option can be queried to retrieve the actual + * size. + * + *

      For datagram-oriented sockets, the size of the send buffer may limit + * the size of the datagrams that may be sent by the socket. Whether + * datagrams larger than the buffer size are sent or discarded is system + * dependent. + * + *

      The initial/default size of the socket send buffer and the range of + * allowable values is system dependent although a negative size is not + * allowed. An attempt to set the socket send buffer to larger than its + * maximum size causes it to be set to its maximum size. + * + *

      An implementation allows this socket option to be set before the + * socket is bound or connected. Whether an implementation allows the + * socket send buffer to be changed after the socket is bound is system + * dependent. + */ + public static final SocketOption SO_SNDBUF = + new StdSocketOption("SO_SNDBUF", Integer.class); + + + /** + * The size of the socket receive buffer. + * + *

      The value of this socket option is an {@code Integer} that is the + * size of the socket receive buffer in bytes. The socket receive buffer is + * an input buffer used by the networking implementation. It may need to be + * increased for high-volume connections or decreased to limit the possible + * backlog of incoming data. The value of the socket option is a + * hint to the implementation to size the buffer and the actual + * size may differ. + * + *

      For datagram-oriented sockets, the size of the receive buffer may + * limit the size of the datagrams that can be received. Whether datagrams + * larger than the buffer size can be received is system dependent. + * Increasing the socket receive buffer may be important for cases where + * datagrams arrive in bursts faster than they can be processed. + * + *

      In the case of stream-oriented sockets and the TCP/IP protocol, the + * size of the socket receive buffer may be used when advertising the size + * of the TCP receive window to the remote peer. + * + *

      The initial/default size of the socket receive buffer and the range + * of allowable values is system dependent although a negative size is not + * allowed. An attempt to set the socket receive buffer to larger than its + * maximum size causes it to be set to its maximum size. + * + *

      An implementation allows this socket option to be set before the + * socket is bound or connected. Whether an implementation allows the + * socket receive buffer to be changed after the socket is bound is system + * dependent. + * + * @see RFC 1323: TCP + * Extensions for High Performance + */ + public static final SocketOption SO_RCVBUF = + new StdSocketOption("SO_RCVBUF", Integer.class); + + /** + * Re-use address. + * + *

      The value of this socket option is a {@code Boolean} that represents + * whether the option is enabled or disabled. The exact semantics of this + * socket option are socket type and system dependent. + * + *

      In the case of stream-oriented sockets, this socket option will + * usually determine whether the socket can be bound to a socket address + * when a previous connection involving that socket address is in the + * TIME_WAIT state. On implementations where the semantics differ, + * and the socket option is not required to be enabled in order to bind the + * socket when a previous connection is in this state, then the + * implementation may choose to ignore this option. + * + *

      For datagram-oriented sockets the socket option is used to allow + * multiple programs bind to the same address. This option should be enabled + * when the socket is to be used for Internet Protocol (IP) multicasting. + * + *

      An implementation allows this socket option to be set before the + * socket is bound or connected. Changing the value of this socket option + * after the socket is bound has no effect. The default value of this + * socket option is system dependent. + * + * @see RFC 793: Transmission + * Control Protocol + */ + public static final SocketOption SO_REUSEADDR = + new StdSocketOption("SO_REUSEADDR", Boolean.class); + + /** + * Linger on close if data is present. + * + *

      The value of this socket option is an {@code Integer} that controls + * the action taken when unsent data is queued on the socket and a method + * to close the socket is invoked. If the value of the socket option is zero + * or greater, then it represents a timeout value, in seconds, known as the + * linger interval. The linger interval is the timeout for the + * {@code close} method to block while the operating system attempts to + * transmit the unsent data or it decides that it is unable to transmit the + * data. If the value of the socket option is less than zero then the option + * is disabled. In that case the {@code close} method does not wait until + * unsent data is transmitted; if possible the operating system will transmit + * any unsent data before the connection is closed. + * + *

      This socket option is intended for use with sockets that are configured + * in {@link java.nio.channels.SelectableChannel#isBlocking() blocking} mode + * only. The behavior of the {@code close} method when this option is + * enabled on a non-blocking socket is not defined. + * + *

      The initial value of this socket option is a negative value, meaning + * that the option is disabled. The option may be enabled, or the linger + * interval changed, at any time. The maximum value of the linger interval + * is system dependent. Setting the linger interval to a value that is + * greater than its maximum value causes the linger interval to be set to + * its maximum value. + */ + public static final SocketOption SO_LINGER = + new StdSocketOption("SO_LINGER", Integer.class); + + + // -- IPPROTO_IP -- + + /** + * The Type of Service (ToS) octet in the Internet Protocol (IP) header. + * + *

      The value of this socket option is an {@code Integer}, the least + * significant 8 bits of which represents the value of the ToS octet in IP + * packets sent by sockets to an {@link StandardProtocolFamily#INET IPv4} + * socket. The interpretation of the ToS octet is network specific and + * is not defined by this class. Further information on the ToS octet can be + * found in RFC 1349 + * and RFC 2474. The + * value of the socket option is a hint. An implementation may + * ignore the value, or ignore specific values. + * + *

      The initial/default value of the TOS field in the ToS octet is + * implementation specific but will typically be {@code 0}. For + * datagram-oriented sockets the option may be configured at any time after + * the socket has been bound. The new value of the octet is used when sending + * subsequent datagrams. It is system dependent whether this option can be + * queried or changed prior to binding the socket. + * + *

      The behavior of this socket option on a stream-oriented socket, or an + * {@link StandardProtocolFamily#INET6 IPv6} socket, is not defined in this + * release. + */ + public static final SocketOption IP_TOS = + new StdSocketOption("IP_TOS", Integer.class); + + /** + * The network interface for Internet Protocol (IP) multicast datagrams. + * + *

      The value of this socket option is a {@link NetworkInterface} that + * represents the outgoing interface for multicast datagrams sent by the + * datagram-oriented socket. For {@link StandardProtocolFamily#INET6 IPv6} + * sockets then it is system dependent whether setting this option also + * sets the outgoing interface for multlicast datagrams sent to IPv4 + * addresses. + * + *

      The initial/default value of this socket option may be {@code null} + * to indicate that outgoing interface will be selected by the operating + * system, typically based on the network routing tables. An implementation + * allows this socket option to be set after the socket is bound. Whether + * the socket option can be queried or changed prior to binding the socket + * is system dependent. + * + * @see java.nio.channels.MulticastChannel + */ + public static final SocketOption IP_MULTICAST_IF = + new StdSocketOption("IP_MULTICAST_IF", NetworkInterface.class); + + /** + * The time-to-live for Internet Protocol (IP) multicast datagrams. + * + *

      The value of this socket option is an {@code Integer} in the range + * 0 <= value <= 255. It is used to control + * the scope of multicast datagrams sent by the datagram-oriented socket. + * In the case of an {@link StandardProtocolFamily#INET IPv4} socket + * the option is the time-to-live (TTL) on multicast datagrams sent by the + * socket. Datagrams with a TTL of zero are not transmitted on the network + * but may be delivered locally. In the case of an {@link + * StandardProtocolFamily#INET6 IPv6} socket the option is the + * hop limit which is number of hops that the datagram can + * pass through before expiring on the network. For IPv6 sockets it is + * system dependent whether the option also sets the time-to-live + * on multicast datagrams sent to IPv4 addresses. + * + *

      The initial/default value of the time-to-live setting is typically + * {@code 1}. An implementation allows this socket option to be set after + * the socket is bound. Whether the socket option can be queried or changed + * prior to binding the socket is system dependent. + * + * @see java.nio.channels.MulticastChannel + */ + public static final SocketOption IP_MULTICAST_TTL = + new StdSocketOption("IP_MULTICAST_TTL", Integer.class); + + /** + * Loopback for Internet Protocol (IP) multicast datagrams. + * + *

      The value of this socket option is a {@code Boolean} that controls + * the loopback of multicast datagrams. The value of the socket + * option represents if the option is enabled or disabled. + * + *

      The exact semantics of this socket options are system dependent. + * In particular, it is system dependent whether the loopback applies to + * multicast datagrams sent from the socket or received by the socket. + * For {@link StandardProtocolFamily#INET6 IPv6} sockets then it is + * system dependent whether the option also applies to multicast datagrams + * sent to IPv4 addresses. + * + *

      The initial/default value of this socket option is {@code TRUE}. An + * implementation allows this socket option to be set after the socket is + * bound. Whether the socket option can be queried or changed prior to + * binding the socket is system dependent. + * + * @see java.nio.channels.MulticastChannel + */ + public static final SocketOption IP_MULTICAST_LOOP = + new StdSocketOption("IP_MULTICAST_LOOP", Boolean.class); + + + // -- IPPROTO_TCP -- + + /** + * Disable the Nagle algorithm. + * + *

      The value of this socket option is a {@code Boolean} that represents + * whether the option is enabled or disabled. The socket option is specific to + * stream-oriented sockets using the TCP/IP protocol. TCP/IP uses an algorithm + * known as The Nagle Algorithm to coalesce short segments and + * improve network efficiency. + * + *

      The default value of this socket option is {@code FALSE}. The + * socket option should only be enabled in cases where it is known that the + * coalescing impacts performance. The socket option may be enabled at any + * time. In other words, the Nagle Algorithm can be disabled. Once the option + * is enabled, it is system dependent whether it can be subsequently + * disabled. In that case, invoking the {@code setOption} method to disable + * the option has no effect. + * + * @see RFC 1122: + * Requirements for Internet Hosts -- Communication Layers + */ + public static final SocketOption TCP_NODELAY = + new StdSocketOption("TCP_NODELAY", Boolean.class); + + + private static class StdSocketOption implements SocketOption { + private final String name; + private final Class type; + StdSocketOption(String name, Class type) { + this.name = name; + this.type = type; + } + @Override public String name() { return name; } + @Override public Class type() { return type; } + @Override public String toString() { return name; } + } +} diff --git a/src/share/classes/java/nio/channels/DatagramChannel.java b/src/share/classes/java/nio/channels/DatagramChannel.java index 744495c55..f04b1b000 100644 --- a/src/share/classes/java/nio/channels/DatagramChannel.java +++ b/src/share/classes/java/nio/channels/DatagramChannel.java @@ -26,28 +26,21 @@ package java.nio.channels; import java.io.IOException; +import java.net.ProtocolFamily; import java.net.DatagramSocket; +import java.net.SocketOption; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.spi.*; - /** * A selectable channel for datagram-oriented sockets. * - * - *

      Datagram channels are not a complete abstraction of network datagram - * sockets. Binding and the manipulation of socket options must be done - * through an associated {@link java.net.DatagramSocket} object obtained by - * invoking the {@link #socket() socket} method. It is not possible to create - * a channel for an arbitrary, pre-existing datagram socket, nor is it possible - * to specify the {@link java.net.DatagramSocketImpl} object to be used by a - * datagram socket associated with a datagram channel. - * - *

      A datagram channel is created by invoking the {@link #open open} method - * of this class. A newly-created datagram channel is open but not connected. - * A datagram channel need not be connected in order for the {@link #send send} - * and {@link #receive receive} methods to be used. A datagram channel may be + *

      A datagram channel is created by invoking one of the {@link #open open} methods + * of this class. It is not possible to create a channel for an arbitrary, + * pre-existing datagram socket. A newly-created datagram channel is open but not + * connected. A datagram channel need not be connected in order for the {@link #send + * send} and {@link #receive receive} methods to be used. A datagram channel may be * connected, by invoking its {@link #connect connect} method, in order to * avoid the overhead of the security checks are otherwise performed as part of * every send and receive operation. A datagram channel must be connected in @@ -59,11 +52,57 @@ import java.nio.channels.spi.*; * disconnected or closed. Whether or not a datagram channel is connected may * be determined by invoking its {@link #isConnected isConnected} method. * + *

      Socket options are configured using the {@link #setOption(SocketOption,Object) + * setOption} method. Datagram channels support the following options: + *

      + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
      Option NameDescription
      {@link java.net.StandardSocketOption#SO_SNDBUF SO_SNDBUF} The size of the socket send buffer
      {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} The size of the socket receive buffer
      {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} Re-use address
      {@link java.net.StandardSocketOption#SO_BROADCAST SO_BROADCAST} Allow transmission of broadcast datagrams
      {@link java.net.StandardSocketOption#IP_TOS IP_TOS} The Type of Service (ToS) octet in the Internet Protocol (IP) header
      {@link java.net.StandardSocketOption#IP_MULTICAST_IF IP_MULTICAST_IF} The network interface for Internet Protocol (IP) multicast datagrams
      {@link java.net.StandardSocketOption#IP_MULTICAST_TTL + * IP_MULTICAST_TTL} The time-to-live for Internet Protocol (IP) multicast + * datagrams
      {@link java.net.StandardSocketOption#IP_MULTICAST_LOOP + * IP_MULTICAST_LOOP} Loopback for Internet Protocol (IP) multicast datagrams
      + *
      + * Additional (implementation specific) options may also be supported. + * *

      Datagram channels are safe for use by multiple concurrent threads. They * support concurrent reading and writing, though at most one thread may be * reading and at most one thread may be writing at any given time.

      * - * * @author Mark Reinhold * @author JSR-51 Expert Group * @since 1.4 @@ -71,7 +110,7 @@ import java.nio.channels.spi.*; public abstract class DatagramChannel extends AbstractSelectableChannel - implements ByteChannel, ScatteringByteChannel, GatheringByteChannel + implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, MulticastChannel { /** @@ -88,7 +127,13 @@ public abstract class DatagramChannel * java.nio.channels.spi.SelectorProvider#openDatagramChannel() * openDatagramChannel} method of the system-wide default {@link * java.nio.channels.spi.SelectorProvider} object. The channel will not be - * connected.

      + * connected. + * + *

      The {@link ProtocolFamily ProtocolFamily} of the channel's socket + * is platform (and possibly configuration) dependent and therefore unspecified. + * The {@link #open(ProtocolFamily) open} allows the protocol family to be + * selected when opening a datagram channel, and should be used to open + * datagram channels that are intended for Internet Protocol multicasting. * * @return A new datagram channel * @@ -99,6 +144,39 @@ public abstract class DatagramChannel return SelectorProvider.provider().openDatagramChannel(); } + /** + * Opens a datagram channel. + * + *

      The {@code family} parameter is used to specify the {@link + * ProtocolFamily}. If the datagram channel is to be used for IP multicasing + * then this should correspond to the address type of the multicast groups + * that this channel will join. + * + *

      The new channel is created by invoking the {@link + * java.nio.channels.spi.SelectorProvider#openDatagramChannel(ProtocolFamily) + * openDatagramChannel} method of the system-wide default {@link + * java.nio.channels.spi.SelectorProvider} object. The channel will not be + * connected. + * + * @param family + * The protocol family + * + * @return A new datagram channel + * + * @throws UnsupportedOperationException + * If the specified protocol family is not supported. For example, + * suppose the parameter is specified as {@link + * java.net.StandardProtocolFamily#INET6 StandardProtocolFamily.INET6} + * but IPv6 is not enabled on the platform. + * @throws IOException + * If an I/O error occurs + * + * @since 1.7 + */ + public static DatagramChannel open(ProtocolFamily family) throws IOException { + return SelectorProvider.provider().openDatagramChannel(family); + } + /** * Returns an operation set identifying this channel's supported * operations. @@ -117,6 +195,32 @@ public abstract class DatagramChannel // -- Socket-specific operations -- + /** + * @throws AlreadyBoundException {@inheritDoc} + * @throws UnsupportedAddressTypeException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws SecurityException + * If a security manager has been installed and its {@link + * SecurityManager#checkListen checkListen} method denies the + * operation + * + * @since 1.7 + */ + public abstract DatagramChannel bind(SocketAddress local) + throws IOException; + + /** + * @throws IllegalArgumentException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + * + * @since 1.7 + */ + public abstract DatagramChannel setOption(SocketOption name, T value) + throws IOException; + + /** * Retrieves a datagram socket associated with this channel. * @@ -128,10 +232,10 @@ public abstract class DatagramChannel public abstract DatagramSocket socket(); /** - * Tells whether or not this channel's socket is connected.

      + * Tells whether or not this channel's socket is connected. * - * @return true if, and only if, this channel's socket - * is connected + * @return {@code true} if, and only if, this channel's socket + * is {@link #isOpen open} and connected */ public abstract boolean isConnected(); @@ -206,6 +310,19 @@ public abstract class DatagramChannel */ public abstract DatagramChannel disconnect() throws IOException; + /** + * Returns the remote address to which this channel's socket is connected. + * + * @return The remote address; {@code null} if the channel is not {@link + * #isOpen open} or the channel's socket is not connected + * + * @throws IOException + * If an I/O error occurs + * + * @since 1.7 + */ + public abstract SocketAddress getConnectedAddress() throws IOException; + /** * Receives a datagram via this channel. * diff --git a/src/share/classes/java/nio/channels/MembershipKey.java b/src/share/classes/java/nio/channels/MembershipKey.java new file mode 100644 index 000000000..0d2fc52bc --- /dev/null +++ b/src/share/classes/java/nio/channels/MembershipKey.java @@ -0,0 +1,183 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.nio.channels; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.io.IOException; +import java.util.List; + +/** + * A token representing the membership of an Internet Protocol (IP) multicast + * group. + * + *

      A membership key may represent a membership to receive all datagrams sent + * to the group, or it may be source-specific, meaning that it + * represents a membership that receives only datagrams from a specific source + * address. Whether or not a membership key is source-specific may be determined + * by invoking its {@link #getSourceAddress() getSourceAddress} method. + * + *

      A membership key is valid upon creation and remains valid until the + * membership is dropped by invoking the {@link #drop() drop} method, or + * the channel is closed. The validity of the membership key may be tested + * by invoking its {@link #isValid() isValid} method. + * + *

      Where a membership key is not source-specific and the underlying operation + * system supports source filtering, then the {@link #block block} and {@link + * #unblock unblock} methods can be used to block or unblock multicast datagrams + * from particular source addresses. + * + * @see MulticastChannel + * + * @since 1.7 + */ +public abstract class MembershipKey { + + /** + * Initializes a new instance of this class. + */ + protected MembershipKey() { + } + + /** + * Tells whether or not this membership is valid. + * + *

      A multicast group membership is valid upon creation and remains + * valid until the membership is dropped by invoking the {@link #drop() drop} + * method, or the channel is closed. + * + * @return {@code true} if this membership key is valid, {@code false} + * otherwise + */ + public abstract boolean isValid(); + + /** + * Drop membership. + * + *

      If the membership key represents a membership to receive all datagrams + * then the membership is dropped and the channel will no longer receive any + * datagrams sent to the group. If the membership key is source-specific + * then the channel will no longer receive datagrams sent to the group from + * that source address. + * + *

      After membership is dropped it may still be possible to receive + * datagrams sent to the group. This can arise when datagrams are waiting to + * be received in the socket's receive buffer. After membership is dropped + * then the channel may {@link MulticastChannel#join join} the group again + * in which case a new membership key is returned. + * + *

      Upon return, this membership object will be {@link #isValid() invalid}. + * If the multicast group membership is already invalid then invoking this + * method has no effect. Once a multicast group membership is invalid, + * it remains invalid forever. + * + * @throws IOException + * If an I/O error occurs + */ + public abstract void drop() throws IOException; + + /** + * Block multicast datagrams from the given source address. + * + *

      If this membership key is not source-specific, and the underlying + * operating system supports source filtering, then this method blocks + * multicast datagrams from the given source address. If the given source + * address is already blocked then this method has no effect. + * After a source address is blocked it may still be possible to receive + * datagams from that source. This can arise when datagrams are waiting to + * be received in the socket's receive buffer. + * + * @param source + * The source address to block + * + * @return This membership key + * + * @throws IllegalArgumentException + * If the {@code source} parameter is not a unicast address or + * is not the same address type as the multicast group + * @throws IllegalStateException + * If this membership key is source-specific or is no longer valid + * @throws UnsupportedOperationException + * If the underlying operating system does not support source + * filtering + * @throws IOException + * If an I/O error occurs + */ + public abstract MembershipKey block(InetAddress source) throws IOException; + + /** + * Unblock multicast datagrams from the given source address that was + * previously blocked using the {@link #block(InetAddress) block} method. + * + * @param source + * The source address to unblock + * + * @return This membership key + * + * @throws IllegalStateException + * If the given source address is not currently blocked or the + * membership key is no longer valid + * @throws IOException + * If an I/O error occurs + */ + public abstract MembershipKey unblock(InetAddress source) throws IOException; + + /** + * Returns the channel for which this membership key was created. This + * method will continue to return the channel even after the membership + * becomes {@link #isValid invalid}. + * + * @return the channel + */ + public abstract MulticastChannel getChannel(); + + /** + * Returns the multicast group for which this membership key was created. + * This method will continue to return the group even after the membership + * becomes {@link #isValid invalid}. + * + * @return the multicast group + */ + public abstract InetAddress getGroup(); + + /** + * Returns the network interface for which this membership key was created. + * This method will continue to return the network interface even after the + * membership becomes {@link #isValid invalid}. + * + * @return the network interface + */ + public abstract NetworkInterface getNetworkInterface(); + + /** + * Returns the source address if this membership key is source-specific, + * or {@code null} if this membership is not source-specific. + * + * @return The source address if this membership key is source-specific, + * otherwise {@code null} + */ + public abstract InetAddress getSourceAddress(); +} diff --git a/src/share/classes/java/nio/channels/MulticastChannel.java b/src/share/classes/java/nio/channels/MulticastChannel.java new file mode 100644 index 000000000..440dd2a8b --- /dev/null +++ b/src/share/classes/java/nio/channels/MulticastChannel.java @@ -0,0 +1,211 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.nio.channels; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.io.IOException; +import java.net.ProtocolFamily; // javadoc +import java.net.StandardProtocolFamily; // javadoc +import java.net.StandardSocketOption; // javadoc + +/** + * A network channel that supports Internet Protocol (IP) multicasting. + * + *

      IP multicasting is the transmission of IP datagrams to members of + * a group that is zero or more hosts identified by a single destination + * address. + * + *

      In the case of a channel to an {@link StandardProtocolFamily#INET IPv4} socket, + * the underlying operating system supports + * RFC 2236: Internet Group Management Protocol, Version 2 (IGMPv2). + * It may optionally support source filtering as specified by RFC 3376: Internet Group + * Management Protocol, Version 3 (IGMPv3). + * For channels to an {@link StandardProtocolFamily#INET6 IPv6} socket, the equivalent + * standards are RFC 2710: + * Multicast Listener Discovery (MLD) for IPv6 and RFC 3810: Multicast Listener + * Discovery Version 2 (MLDv2) for IPv6. + * + *

      The {@link #join(InetAddress,NetworkInterface)} method is used to + * join a group and receive all multicast datagrams sent to the group. A channel + * may join several multicast groups and may join the same group on several + * {@link NetworkInterface interfaces}. Membership is dropped by invoking the {@link + * MembershipKey#drop drop} method on the returned {@link MembershipKey}. If the + * underlying platform supports source filtering then the {@link MembershipKey#block + * block} and {@link MembershipKey#unblock unblock} methods can be used to block or + * unblock multicast datagrams from particular source addresses. + * + *

      The {@link #join(InetAddress,NetworkInterface,InetAddress)} method + * is used to begin receiving datagrams sent to a group whose source address matches + * a given source address. This method throws {@link UnsupportedOperationException} + * if the underlying platform does not support source filtering. Membership is + * cumulative and this method may be invoked again with the same group + * and interface to allow receiving datagrams from other source addresses. The + * method returns a {@link MembershipKey} that represents membership to receive + * datagrams from the given source address. Invoking the key's {@link + * MembershipKey#drop drop} method drops membership so that datagrams from the + * source address can no longer be received. + * + *

      Platform dependencies

      + * + * The multicast implementation is intended to map directly to the native + * multicasting facility. Consequently, the following items should be considered + * when developing an application that receives IP multicast datagrams: + * + *
        + * + *
      1. The creation of the channel should specify the {@link ProtocolFamily} + * that corresponds to the address type of the multicast groups that the channel + * will join. There is no guarantee that a channel to a socket in one protocol + * family can join and receive multicast datagrams when the address of the + * multicast group corresponds to another protocol family. For example, it is + * implementation specific if a channel to an {@link StandardProtocolFamily#INET6 IPv6} + * socket can join an {@link StandardProtocolFamily#INET IPv4} multicast group and receive + * multicast datagrams sent to the group.

      2. + * + *
      3. The channel's socket should be bound to the {@link + * InetAddress#isAnyLocalAddress wildcard} address. If the socket is bound to + * a specific address, rather than the wildcard address then it is implementation + * specific if multicast datagrams are received by the socket.

      4. + * + *
      5. The {@link StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} option should be + * enabled prior to {@link NetworkChannel#bind binding} the socket. This is + * required to allow multiple members of the group to bind to the same + * address.

      6. + * + *
      + * + *

      Usage Example: + *

      + *     // join multicast group on this interface, and also use this
      + *     // interface for outgoing multicast datagrams
      + *     NetworkInterface ni = NetworkInterface.getByName("hme0");
      + *
      + *     DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)
      + *         .setOption(StandardSocketOption.SO_REUSEADDR, true)
      + *         .bind(new InetSocketAddress(5000))
      + *         .setOption(StandardSocketOption.IP_MULTICAST_IF, ni);
      + *
      + *     InetAddress group = InetAddress.getByName("225.4.5.6");
      + *
      + *     MembershipKey key = dc.join(group, ni);
      + * 
      + * + * @since 1.7 + */ + +public interface MulticastChannel + extends NetworkChannel +{ + /** + * Joins a multicast group to begin receiving all datagrams sent to the group, + * returning a membership key. + * + *

      If this channel is currently a member of the group on the given + * interface to receive all datagrams then the membership key, representing + * that membership, is returned. Otherwise this channel joins the group and + * the resulting new membership key is returned. The resulting membership key + * is not {@link MembershipKey#getSourceAddress source-specific}. + * + *

      A multicast channel may join several multicast groups, including + * the same group on more than one interface. An implementation may impose a + * limit on the number of groups that may be joined at the same time. + * + * @param group + * The multicast address to join + * @param interf + * The network interface on which to join the group + * + * @return The membership key + * + * @throws IllegalArgumentException + * If the group parameter is not a {@link InetAddress#isMulticastAddress + * multicast} address, or the group parameter is an address type + * that is not supported by this channel + * @throws IllegalStateException + * If the channel already has source-specific membership of the + * group on the interface + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If an I/O error occurs + * @throws SecurityException + * If a security manager is set, and its + * {@link SecurityManager#checkMulticast(InetAddress) checkMulticast} + * method denies access to the multiast group + */ + MembershipKey join(InetAddress group, NetworkInterface interf) + throws IOException; + + /** + * Joins a multicast group to begin receiving datagrams sent to the group + * from a given source address. + * + *

      If this channel is currently a member of the group on the given + * interface to receive datagrams from the given source address then the + * membership key, representing that membership, is returned. Otherwise this + * channel joins the group and the resulting new membership key is returned. + * The resulting membership key is {@link MembershipKey#getSourceAddress + * source-specific}. + * + *

      Membership is cumulative and this method may be invoked + * again with the same group and interface to allow receiving datagrams sent + * by other source addresses to the group. + * + * @param group + * The multicast address to join + * @param interf + * The network interface on which to join the group + * @param source + * The source address + * + * @return The membership key + * + * @throws IllegalArgumentException + * If the group parameter is not a {@link + * InetAddress#isMulticastAddress multicast} address, the + * source parameter is not a unicast address, the group + * parameter is an address type that is not supported by this channel, + * or the source parameter is not the same address type as the group + * @throws IllegalStateException + * If the channel is currently a member of the group on the given + * interface to receive all datagrams + * @throws UnsupportedOperationException + * If the underlying operation system does not support source filtering + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If an I/O error occurs + * @throws SecurityException + * If a security manager is set, and its + * {@link SecurityManager#checkMulticast(InetAddress) checkMulticast} + * method denies access to the multiast group + */ + MembershipKey join(InetAddress group, NetworkInterface interf, InetAddress source) + throws IOException; +} diff --git a/src/share/classes/java/nio/channels/NetworkChannel.java b/src/share/classes/java/nio/channels/NetworkChannel.java new file mode 100644 index 000000000..fae642fcb --- /dev/null +++ b/src/share/classes/java/nio/channels/NetworkChannel.java @@ -0,0 +1,158 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.nio.channels; + +import java.net.SocketOption; +import java.net.SocketAddress; +import java.util.Set; +import java.io.IOException; + +/** + * A channel to a network socket. + * + *

      A channel that implements this interface is a channel to a network + * socket. The {@link #bind(SocketAddress) bind} method is used to bind the + * socket to a local {@link SocketAddress address}, the {@link #getLocalAddress() + * getLocalAddress} method returns the address that the socket is bound to, and + * the {@link #setOption(SocketOption,Object) setOption} and {@link + * #getOption(SocketOption) getOption} methods are used to set and query socket + * options. An implementation of this interface should specify the socket options + * that it supports. + * + *

      The {@link #bind bind} and {@link #setOption setOption} methods that do + * not otherwise have a value to return are specified to return the network + * channel upon which they are invoked. This allows method invocations to be + * chained. Implementations of this interface should specialize the return type + * so that method invocations on the implementation class can be chained. + * + * @since 1.7 + */ + +public interface NetworkChannel + extends Channel +{ + /** + * Binds the channel's socket to a local address. + * + *

      This method is used to establish an association between the socket and + * a local address. Once an association is established then the socket remains + * bound until the channel is closed. If the {@code local} parameter has the + * value {@code null} then the socket will be bound to an address that is + * assigned automatically. + * + * @param local + * The address to bind the socket, or {@code null} to bind the socket + * to an automatically assigned socket address + * + * @return This channel + * + * @throws AlreadyBoundException + * If the socket is already bound + * @throws UnsupportedAddressTypeException + * If the type of the given address is not supported + * @throws ClosedChannelException + * If the channel is closed + * @throws IOException + * If some other I/O error occurs + * @throws SecurityException + * If a security manager is installed and it denies an unspecified + * permission. An implementation of this interface should specify + * any required permissions. + * + * @see #getLocalAddress + */ + NetworkChannel bind(SocketAddress local) throws IOException; + + /** + * Returns the socket address that this channel's socket is bound to, or + * {@code null} if the socket is not bound. + * + *

      Where the channel is {@link #bind bound} to an Internet Protocol + * socket address then the return value from this method is of type {@link + * java.net.InetSocketAddress}. + * + * @return The socket address that the socket is bound to, or {@code null} + * if the channel is not {@link #isOpen open} or the channel's socket + * is not bound + * + * @throws IOException + * If an I/O error occurs + */ + SocketAddress getLocalAddress() throws IOException; + + /** + * Sets the value of a socket option. + * + * @param name + * The socket option + * @param value + * The value of the socket option. A value of {@code null} may be + * a valid value for some socket options. + * + * @return This channel + * + * @throws IllegalArgumentException + * If the socket option is not supported by this channel, or + * the value is not a valid value for this socket option + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If an I/O error occurs + * + * @see java.net.StandardSocketOption + */ + NetworkChannel setOption(SocketOption name, T value) throws IOException; + + /** + * Returns the value of a socket option. + * + * @param name + * The socket option + * + * @return The value of the socket option. A value of {@code null} may be + * a valid value for some socket options. + * + * @throws IllegalArgumentException + * If the socket option is not supported by this channel + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If an I/O error occurs + * + * @see java.net.StandardSocketOption + */ + T getOption(SocketOption name) throws IOException; + + /** + * Returns a set of the socket options supported by this channel. + * + *

      This method will continue to return the set of options even after the + * channel has been closed. + * + * @return A set of the socket options supported by this channel + */ + Set> options(); +} diff --git a/src/share/classes/java/nio/channels/ServerSocketChannel.java b/src/share/classes/java/nio/channels/ServerSocketChannel.java index 0ffed003a..578646f30 100644 --- a/src/share/classes/java/nio/channels/ServerSocketChannel.java +++ b/src/share/classes/java/nio/channels/ServerSocketChannel.java @@ -27,33 +27,44 @@ package java.nio.channels; import java.io.IOException; import java.net.ServerSocket; +import java.net.SocketOption; import java.net.SocketAddress; import java.nio.channels.spi.*; - /** * A selectable channel for stream-oriented listening sockets. * - *

      Server-socket channels are not a complete abstraction of listening - * network sockets. Binding and the manipulation of socket options must be - * done through an associated {@link java.net.ServerSocket} object obtained by - * invoking the {@link #socket() socket} method. It is not possible to create - * a channel for an arbitrary, pre-existing server socket, nor is it possible - * to specify the {@link java.net.SocketImpl} object to be used by a server - * socket associated with a server-socket channel. - * *

      A server-socket channel is created by invoking the {@link #open() open} - * method of this class. A newly-created server-socket channel is open but not - * yet bound. An attempt to invoke the {@link #accept() accept} method of an - * unbound server-socket channel will cause a {@link NotYetBoundException} to - * be thrown. A server-socket channel can be bound by invoking one of the - * {@link java.net.ServerSocket#bind(java.net.SocketAddress,int) bind} methods - * of an associated server socket. + * method of this class. It is not possible to create a channel for an arbitrary, + * pre-existing {@link ServerSocket}. A newly-created server-socket channel is + * open but not yet bound. An attempt to invoke the {@link #accept() accept} + * method of an unbound server-socket channel will cause a {@link NotYetBoundException} + * to be thrown. A server-socket channel can be bound by invoking one of the + * {@link #bind(java.net.SocketAddress,int) bind} methods defined by this class. + * + *

      Socket options are configured using the {@link #setOption(SocketOption,Object) + * setOption} method. Server-socket channels support the following options: + *

      + * + * + * + * + * + * + * + * + * + * + * + * + * + *
      Option NameDescription
      {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} The size of the socket receive buffer
      {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} Re-use address
      + *
      + * Additional (implementation specific) options may also be supported. * *

      Server-socket channels are safe for use by multiple concurrent threads. *

      * - * * @author Mark Reinhold * @author JSR-51 Expert Group * @since 1.4 @@ -61,6 +72,7 @@ import java.nio.channels.spi.*; public abstract class ServerSocketChannel extends AbstractSelectableChannel + implements NetworkChannel { /** @@ -109,6 +121,89 @@ public abstract class ServerSocketChannel // -- ServerSocket-specific operations -- + /** + * Binds the channel's socket to a local address and configures the socket + * to listen for connections. + * + *

      An invocation of this method is equivalent to the following: + *

      +     * bind(local, 0);
      +     * 
      + * + * @param local + * The local address to bind the socket, or {@code null} to bind + * to an automatically assigned socket address + * + * @return This channel + * + * @throws AlreadyBoundException {@inheritDoc} + * @throws UnsupportedAddressTypeException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws SecurityException + * If a security manager has been installed and its {@link + * SecurityManager#checkListen checkListen} method denies the + * operation + * + * @since 1.7 + */ + public final ServerSocketChannel bind(SocketAddress local) + throws IOException + { + return bind(local, 0); + } + + /** + * Binds the channel's socket to a local address and configures the socket to + * listen for connections. + * + *

      This method is used to establish an association between the socket and + * a local address. Once an association is established then the socket remains + * bound until the channel is closed. + * + *

      The {@code backlog} parameter is the maximum number of pending + * connections on the socket. Its exact semantics are implementation specific. + * In particular, an implementation may impose a maximum length or may choose + * to ignore the parameter altogther. If the {@code backlog} parameter has + * the value {@code 0}, or a negative value, then an implementation specific + * default is used. + * + * @param local + * The address to bind the socket, or {@code null} to bind to an + * automatically assigned socket address + * @param backlog + * The maximum number of pending connections + * + * @return This channel + * + * @throws AlreadyBoundException + * If the socket is already bound + * @throws UnsupportedAddressTypeException + * If the type of the given address is not supported + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If some other I/O error occurs + * @throws SecurityException + * If a security manager has been installed and its {@link + * SecurityManager#checkListen checkListen} method denies the + * operation + * + * @since 1.7 + */ + public abstract ServerSocketChannel bind(SocketAddress local, int backlog) + throws IOException; + + /** + * @throws IllegalArgumentException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + * + * @since 1.7 + */ + public abstract ServerSocketChannel setOption(SocketOption name, T value) + throws IOException; + /** * Retrieves a server socket associated with this channel. * diff --git a/src/share/classes/java/nio/channels/SocketChannel.java b/src/share/classes/java/nio/channels/SocketChannel.java index 826699422..ccc6342a7 100644 --- a/src/share/classes/java/nio/channels/SocketChannel.java +++ b/src/share/classes/java/nio/channels/SocketChannel.java @@ -27,24 +27,17 @@ package java.nio.channels; import java.io.IOException; import java.net.Socket; +import java.net.SocketOption; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.spi.*; - /** * A selectable channel for stream-oriented connecting sockets. * - *

      Socket channels are not a complete abstraction of connecting network - * sockets. Binding, shutdown, and the manipulation of socket options must be - * done through an associated {@link java.net.Socket} object obtained by - * invoking the {@link #socket() socket} method. It is not possible to create - * a channel for an arbitrary, pre-existing socket, nor is it possible to - * specify the {@link java.net.SocketImpl} object to be used by a socket - * associated with a socket channel. - * *

      A socket channel is created by invoking one of the {@link #open open} - * methods of this class. A newly-created socket channel is open but not yet + * methods of this class. It is not possible to create a channel for an arbitrary, + * pre-existing socket. A newly-created socket channel is open but not yet * connected. An attempt to invoke an I/O operation upon an unconnected * channel will cause a {@link NotYetConnectedException} to be thrown. A * socket channel can be connected by invoking its {@link #connect connect} @@ -59,16 +52,6 @@ import java.nio.channels.spi.*; * Whether or not a connection operation is in progress may be determined by * invoking the {@link #isConnectionPending isConnectionPending} method. * - *

      The input and output sides of a socket channel may independently be - * shut down without actually closing the channel. Shutting down the - * input side of a channel by invoking the {@link java.net.Socket#shutdownInput - * shutdownInput} method of an associated socket object will cause further - * reads on the channel to return -1, the end-of-stream indication. - * Shutting down the output side of the channel by invoking the {@link - * java.net.Socket#shutdownOutput shutdownOutput} method of an associated - * socket object will cause further writes on the channel to throw a {@link - * ClosedChannelException}. - * *

      Socket channels support asynchronous shutdown, which is similar * to the asynchronous close operation specified in the {@link Channel} class. * If the input side of a socket is shut down by one thread while another @@ -79,6 +62,43 @@ import java.nio.channels.spi.*; * channel, then the blocked thread will receive an {@link * AsynchronousCloseException}. * + *

      Socket options are configured using the {@link #setOption(SocketOption,Object) + * setOption} method. Socket channels support the following options: + *

      + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
      Option NameDescription
      {@link java.net.StandardSocketOption#SO_SNDBUF SO_SNDBUF} The size of the socket send buffer
      {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} The size of the socket receive buffer
      {@link java.net.StandardSocketOption#SO_KEEPALIVE SO_KEEPALIVE} Keep connection alive
      {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} Re-use address
      {@link java.net.StandardSocketOption#SO_LINGER SO_LINGER} Linger on close if data is present (when configured in blocking mode + * only)
      {@link java.net.StandardSocketOption#TCP_NODELAY TCP_NODELAY} Disable the Nagle algorithm
      + *
      + * Additional (implementation specific) options may also be supported. + * *

      Socket channels are safe for use by multiple concurrent threads. They * support concurrent reading and writing, though at most one thread may be * reading and at most one thread may be writing at any given time. The {@link @@ -87,7 +107,6 @@ import java.nio.channels.spi.*; * or write operation while an invocation of one of these methods is in * progress will block until that invocation is complete.

      * - * * @author Mark Reinhold * @author JSR-51 Expert Group * @since 1.4 @@ -95,7 +114,7 @@ import java.nio.channels.spi.*; public abstract class SocketChannel extends AbstractSelectableChannel - implements ByteChannel, ScatteringByteChannel, GatheringByteChannel + implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel { /** @@ -191,6 +210,73 @@ public abstract class SocketChannel // -- Socket-specific operations -- + /** + * @throws ConnectionPendingException + * If a non-blocking connection operation is already in progress on + * this channel + * @throws AlreadyBoundException {@inheritDoc} + * @throws UnsupportedAddressTypeException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + * + * @since 1.7 + */ + @Override + public abstract SocketChannel bind(SocketAddress local) + throws IOException; + + /** + * @throws IllegalArgumentException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + * + * @since 1.7 + */ + @Override + public abstract SocketChannel setOption(SocketOption name, T value) + throws IOException; + + /** + * Shutdown the connection for reading without closing the channel. + * + *

      Once shutdown for reading then further reads on the channel will + * return {@code -1}, the end-of-stream indication. If the input side of the + * connection is already shutdown then invoking this method has no effect. + * + * @return The channel + * + * @throws NotYetConnectedException + * If this channel is not yet connected + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If some other I/O error occurs + * + * @since 1.7 + */ + public abstract SocketChannel shutdownInput() throws IOException; + + /** + * Shutdown the connection for writing without closing the channel. + * + *

      Once shutdown for writing then further attempts to write to the + * channel will throw {@link ClosedChannelException}. If the output side of + * the connection is already shutdown then invoking this method has no + * effect. + * + * @return The channel + * + * @throws NotYetConnectedException + * If this channel is not yet connected + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If some other I/O error occurs + * + * @since 1.7 + */ + public abstract SocketChannel shutdownOutput() throws IOException; + /** * Retrieves a socket associated with this channel. * @@ -202,10 +288,10 @@ public abstract class SocketChannel public abstract Socket socket(); /** - * Tells whether or not this channel's network socket is connected.

      + * Tells whether or not this channel's network socket is connected. * * @return true if, and only if, this channel's network socket - * is connected + * is {@link #isOpen open} and connected */ public abstract boolean isConnected(); @@ -339,6 +425,22 @@ public abstract class SocketChannel */ public abstract boolean finishConnect() throws IOException; + /** + * Returns the remote address to which this channel's socket is connected. + * + *

      Where the channel is bound and connected to an Internet Protocol + * socket address then the return value from this method is of type {@link + * java.net.InetSocketAddress}. + * + * @return The remote address; {@code null} if the channel is not {@link + * #isOpen open} or the channel's socket is not connected + * + * @throws IOException + * If an I/O error occurs + * + * @since 1.7 + */ + public abstract SocketAddress getConnectedAddress() throws IOException; // -- ByteChannel operations -- diff --git a/src/share/classes/java/nio/channels/exceptions b/src/share/classes/java/nio/channels/exceptions index ae4d4c0d9..dd6e4357f 100644 --- a/src/share/classes/java/nio/channels/exceptions +++ b/src/share/classes/java/nio/channels/exceptions @@ -146,3 +146,14 @@ gen OverlappingFileLockException " * virtual machine, or when another thread is already waiting to lock an * overlapping region of the same file." \ 2047812138163068433L + + +SINCE=1.7 + +SUPER=IllegalStateException + +gen AlreadyBoundException " + * Unchecked exception thrown when an attempt is made to bind the socket a + * network oriented channel that is already bound." \ + 6796072983322737592L + diff --git a/src/share/classes/java/nio/channels/package-info.java b/src/share/classes/java/nio/channels/package-info.java new file mode 100644 index 000000000..fd4d50369 --- /dev/null +++ b/src/share/classes/java/nio/channels/package-info.java @@ -0,0 +1,231 @@ +/* + * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * Defines channels, which represent connections to entities that are capable of + * performing I/O operations, such as files and sockets; defines selectors, for + * multiplexed, non-blocking I/O operations. + * + * + * + *

      + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *

      Channels

      Description

      {@link java.nio.channels.Channel}A nexus for I/O operations
        {@link java.nio.channels.ReadableByteChannel}Can read into a buffer
          {@link java.nio.channels.ScatteringByteChannel}  Can read into a sequence of buffers
        {@link java.nio.channels.WritableByteChannel}Can write from a buffer
          {@link java.nio.channels.GatheringByteChannel}Can write from a sequence of buffers
        {@link java.nio.channels.ByteChannel}Can read/write to/from a buffer
          {@link java.nio.channels.SeekableByteChannel}A {@code ByteChannel} connected to an entity that contains a variable-length sequence of bytes
        {@link java.nio.channels.NetworkChannel}A channel to a network socket
          {@link java.nio.channels.MulticastChannel}Can join Internet Protocol (IP) multicast groups
      {@link java.nio.channels.Channels}Utility methods for channel/stream interoperation
      + * + *

      A channel represents an open connection to an entity such as a + * hardware device, a file, a network socket, or a program component that is + * capable of performing one or more distinct I/O operations, for example reading + * or writing. As specified in the {@link java.nio.channels.Channel} interface, + * channels are either open or closed, and they are both asynchronously + * closeable and interruptible. + * + *

      The {@link java.nio.channels.Channel} interface is extended by several + * other interfaces. + * + *

      The {@link java.nio.channels.ReadableByteChannel} interface specifies a + * {@link java.nio.channels.ReadableByteChannel#read read} method that reads bytes + * from the channel into a buffer; similarly, the {@link + * java.nio.channels.WritableByteChannel} interface specifies a {@link + * java.nio.channels.WritableByteChannel#write write} method that writes bytes + * from a buffer to the channel. The {@link java.nio.channels.ByteChannel} + * interface unifies these two interfaces for the common case of channels that can + * both read and write bytes. The {@link java.nio.channels.SeekableByteChannel} + * interface extends the {@code ByteChannel} interface with methods to {@link + * java.nio.channels.SeekableByteChannel#position() query} and {@link + * java.nio.channels.SeekableByteChannel#position(long) modify} the channel's + * current position, and its {@link java.nio.channels.SeekableByteChannel#size + * size}. + * + *

      The {@link java.nio.channels.ScatteringByteChannel} and {@link + * java.nio.channels.GatheringByteChannel} interfaces extend the {@link + * java.nio.channels.ReadableByteChannel} and {@link + * java.nio.channels.WritableByteChannel} interfaces, respectively, adding {@link + * java.nio.channels.ScatteringByteChannel#read read} and {@link + * java.nio.channels.GatheringByteChannel#write write} methods that take a + * sequence of buffers rather than a single buffer. + * + *

      The {@link java.nio.channels.NetworkChannel} interface specifies methods + * to {@link java.nio.channels.NetworkChannel#bind bind} the channel's socket, + * obtain the address to which the socket is bound, and methods to {@link + * java.nio.channels.NetworkChannel#getOption get} and {@link + * java.nio.channels.NetworkChannel#setOption set} socket options. The {@link + * java.nio.channels.MulticastChannel} interface specifies methods to join + * Internet Protocol (IP) multicast groups. + * + *

      The {@link java.nio.channels.Channels} utility class defines static methods + * that support the interoperation of the stream classes of the {@link + * java.io} package with the channel classes of this package. An appropriate + * channel can be constructed from an {@link java.io.InputStream} or an {@link + * java.io.OutputStream}, and conversely an {@link java.io.InputStream} or an + * {@link java.io.OutputStream} can be constructed from a channel. A {@link + * java.io.Reader} can be constructed that uses a given charset to decode bytes + * from a given readable byte channel, and conversely a {@link java.io.Writer} can + * be constructed that uses a given charset to encode characters into bytes and + * write them to a given writable byte channel. + * + *

      + * + * + * + * + * + * + * + *

      File channels

      Description

      {@link java.nio.channels.FileChannel}Reads, writes, maps, and manipulates files
      {@link java.nio.channels.FileLock}A lock on a (region of a) file
      {@link java.nio.MappedByteBuffer}/{@link java.nio.MappedBigByteBuffer}  A direct byte buffer or big byte buffer mapped to a region of a file
      + * + *

      The {@link java.nio.channels.FileChannel} class supports the usual + * operations of reading bytes from, and writing bytes to, a channel connected to + * a file, as well as those of querying and modifying the current file position + * and truncating the file to a specific size. It defines methods for acquiring + * locks on the whole file or on a specific region of a file; these methods return + * instances of the {@link java.nio.channels.FileLock} class. Finally, it defines + * methods for forcing updates to the file to be written to the storage device that + * contains it, for efficiently transferring bytes between the file and other + * channels, and for mapping a region of the file directly into memory. + * + *

      A {@code FileChannel} is created by invoking one of its static {@link + * java.nio.channels.FileChannel#open open} methods, or by invoking the {@code + * getChannel} method of a {@link java.io.FileInputStream}, {@link + * java.io.FileOutputStream}, or {@link java.io.RandomAccessFile} to return a + * file channel connected to the same underlying file as the {@link java.io} + * class. + * + * + *

      + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *

      Multiplexed, non-blocking I/O

      Description

      {@link java.nio.channels.SelectableChannel}A channel that can be multiplexed
        {@link java.nio.channels.DatagramChannel}A channel to a datagram-oriented socket
        {@link java.nio.channels.Pipe.SinkChannel}The write end of a pipe
        {@link java.nio.channels.Pipe.SourceChannel}The read end of a pipe
        {@link java.nio.channels.ServerSocketChannel}  A channel to a stream-oriented listening socket
        {@link java.nio.channels.SocketChannel}A channel for a stream-oriented connecting socket
      {@link java.nio.channels.Selector}A multiplexor of selectable channels
      {@link java.nio.channels.SelectionKey}A token representing the registration
      of a channel + * with a selector
      {@link java.nio.channels.Pipe}Two channels that form a unidirectional pipe
      + * + *

      Multiplexed, non-blocking I/O, which is much more scalable than + * thread-oriented, blocking I/O, is provided by selectors, selectable + * channels, and selection keys. + * + *

      A selector is a multiplexor of selectable channels, which in turn are + * a special type of channel that can be put into non-blocking mode. To perform + * multiplexed I/O operations, one or more selectable channels are first created, + * put into non-blocking mode, and {@link + * java.nio.channels.SelectableChannel#register registered} + * with a selector. Registering a channel specifies the set of I/O operations + * that will be tested for readiness by the selector, and returns a selection key that represents the + * registration. + * + *

      Once some channels have been registered with a selector, a selection operation can be performed in + * order to discover which channels, if any, have become ready to perform one or + * more of the operations in which interest was previously declared. If a channel + * is ready then the key returned when it was registered will be added to the + * selector's selected-key set. The key set, and the keys within it, can + * be examined in order to determine the operations for which each channel is + * ready. From each key one can retrieve the corresponding channel in order to + * perform whatever I/O operations are required. + * + *

      That a selection key indicates that its channel is ready for some operation + * is a hint, but not a guarantee, that such an operation can be performed by a + * thread without causing the thread to block. It is imperative that code that + * performs multiplexed I/O be written so as to ignore these hints when they prove + * to be incorrect. + * + *

      This package defines selectable-channel classes corresponding to the {@link + * java.net.DatagramSocket}, {@link java.net.ServerSocket}, and {@link + * java.net.Socket} classes defined in the {@link java.net} package. + * Minor changes to these classes have been made in order to support sockets that + * are associated with channels. This package also defines a simple class that + * implements unidirectional pipes. In all cases, a new selectable channel is + * created by invoking the static open method of the corresponding class. + * If a channel needs an associated socket then a socket will be created as a side + * effect of this operation. + * + *

      The implementation of selectors, selectable channels, and selection keys + * can be replaced by "plugging in" an alternative definition or instance of the + * {@link java.nio.channels.spi.SelectorProvider} class defined in the {@link + * java.nio.channels.spi} package. It is not expected that many developers + * will actually make use of this facility; it is provided primarily so that + * sophisticated users can take advantage of operating-system-specific + * I/O-multiplexing mechanisms when very high performance is required. + * + *

      Much of the bookkeeping and synchronization required to implement the + * multiplexed-I/O abstractions is performed by the {@link + * java.nio.channels.spi.AbstractInterruptibleChannel}, {@link + * java.nio.channels.spi.AbstractSelectableChannel}, {@link + * java.nio.channels.spi.AbstractSelectionKey}, and {@link + * java.nio.channels.spi.AbstractSelector} classes in the {@link + * java.nio.channels.spi} package. When defining a custom selector provider, + * only the {@link java.nio.channels.spi.AbstractSelector} and {@link + * java.nio.channels.spi.AbstractSelectionKey} classes should be subclassed + * directly; custom channel classes should extend the appropriate {@link + * java.nio.channels.SelectableChannel} subclasses defined in this package. + * + *


      + *

      Unless otherwise noted, passing a null argument to a constructor + * or method in any class or interface in this package will cause a {@link + * java.lang.NullPointerException NullPointerException} to be thrown. + * + * @since 1.4 + * @author Mark Reinhold + * @author JSR-51 Expert Group + */ + +package java.nio.channels; diff --git a/src/share/classes/java/nio/channels/package.html b/src/share/classes/java/nio/channels/package.html deleted file mode 100644 index 151378f78..000000000 --- a/src/share/classes/java/nio/channels/package.html +++ /dev/null @@ -1,222 +0,0 @@ - - - - - - -Defines channels, which represent connections to entities that are capable of -performing I/O operations, such as files and sockets; defines selectors, for -multiplexed, non-blocking I/O operations. - - - - -

      - - - - - - - - - - - - - - - -

      Channels

      Description

      {@link java.nio.channels.Channel}A nexus for I/O operations
        {@link java.nio.channels.ReadableByteChannel}Can read into a buffer
          {@link java.nio.channels.ScatteringByteChannel}  Can read into a sequence of buffers
        {@link java.nio.channels.WritableByteChannel}Can write from a buffer
          {@link java.nio.channels.GatheringByteChannel}Can write from a sequence of buffers
        {@link java.nio.channels.ByteChannel}Can read/write to/from a buffer
      {@link java.nio.channels.Channels}Utility methods for channel/stream interoperation
      - -

      A channel represents an open connection to an entity such as a -hardware device, a file, a network socket, or a program component that is -capable of performing one or more distinct I/O operations, for example reading -or writing. As specified in the {@link java.nio.channels.Channel} interface, -channels are either open or closed, and they are both asynchronously -closeable and interruptible. - -

      The {@link java.nio.channels.Channel} interface is extended by several -other interfaces, each of which specifies a new I/O operation. - -

      The {@link java.nio.channels.ReadableByteChannel} interface specifies a -{@link java.nio.channels.ReadableByteChannel#read read} method that reads bytes -from the channel into a buffer; similarly, the {@link -java.nio.channels.WritableByteChannel} interface specifies a {@link -java.nio.channels.WritableByteChannel#write write} method that writes bytes -from a buffer to the channel. The {@link java.nio.channels.ByteChannel} -interface unifies these two interfaces for the common case of channels that can -both read and write bytes. - -

      The {@link java.nio.channels.ScatteringByteChannel} and {@link -java.nio.channels.GatheringByteChannel} interfaces extend the {@link -java.nio.channels.ReadableByteChannel} and {@link -java.nio.channels.WritableByteChannel} interfaces, respectively, adding {@link -java.nio.channels.ScatteringByteChannel#read read} and {@link -java.nio.channels.GatheringByteChannel#write write} methods that take a -sequence of buffers rather than a single buffer. - -

      The {@link java.nio.channels.Channels} utility class defines static methods -that support the interoperation of the stream classes of the {@link -java.io} package with the channel classes of this package. An appropriate -channel can be constructed from an {@link java.io.InputStream} or an {@link -java.io.OutputStream}, and conversely an {@link java.io.InputStream} or an -{@link java.io.OutputStream} can be constructed from a channel. A {@link -java.io.Reader} can be constructed that uses a given charset to decode bytes -from a given readable byte channel, and conversely a {@link java.io.Writer} can -be constructed that uses a given charset to encode characters into bytes and -write them to a given writable byte channel. - - -

      - - - - - - - -

      File channels

      Description

      {@link java.nio.channels.FileChannel}Reads, writes, maps, and manipulates files
      {@link java.nio.channels.FileLock}A lock on a (region of a) file
      {@link java.nio.MappedByteBuffer}  A direct byte buffer mapped to a region of a file
      - -

      The {@link java.nio.channels.FileChannel} class supports the usual -operations of reading bytes from, and writing bytes to, a channel connected to -a file, as well as those of querying and modifying the current file position -and truncating the file to a specific size. It defines methods for acquiring -locks on the whole file or on a specific region of a file; these methods return -instances of the {@link java.nio.channels.FileLock} class. Finally, it defines -methods for forcing updates to the file to be written to the storage device that -contains it, for efficiently transferring bytes between the file and other -channels, and for mapping a region of the file directly into memory. This last -operation creates an instance of the {@link java.nio.MappedByteBuffer} -class, which extends the {@link java.nio.ByteBuffer} class with several -file-related operations. - -

      A getChannel method has been added to each of the {@link -java.io.FileInputStream#getChannel FileInputStream}, {@link -java.io.FileOutputStream#getChannel FileOutputStream}, and {@link -java.io.RandomAccessFile#getChannel RandomAccessFile} classes of the {@link -java.io java.io} package. Invoking this method upon an instance of one of -these classes will return a file channel connected to the underlying file. - - - - -

      - - - - - - - - - - - - - - - - - - - -

      Multiplexed, non-blocking I/O

      Description

      {@link java.nio.channels.SelectableChannel}A channel that can be multiplexed
        {@link java.nio.channels.DatagramChannel}A channel for a {@link java.net.DatagramSocket java.net.DatagramSocket}
        {@link java.nio.channels.Pipe.SinkChannel}The write end of a pipe
        {@link java.nio.channels.Pipe.SourceChannel}The read end of a pipe
        {@link java.nio.channels.ServerSocketChannel}  A channel for a {@link java.net.ServerSocket java.net.ServerSocket}
        {@link java.nio.channels.SocketChannel}A channel for a {@link java.net.Socket java.net.Socket}
      {@link java.nio.channels.Selector}A multiplexor of selectable channels
      {@link java.nio.channels.SelectionKey}A token representing the registration
      of a channel - with a selector
      {@link java.nio.channels.Pipe}Two channels that form a unidirectional pipe
      - -

      Multiplexed, non-blocking I/O, which is much more scalable than -thread-oriented, blocking I/O, is provided by selectors, selectable -channels, and selection keys. - -

      A selector is a multiplexor of selectable channels, which in turn are -a special type of channel that can be put into non-blocking mode. To perform -multiplexed I/O operations, one or more selectable channels are first created, -put into non-blocking mode, and {@link -java.nio.channels.SelectableChannel#register registered} -with a selector. Registering a channel specifies the set of I/O operations -that will be tested for readiness by the selector, and returns a selection key that represents the -registration. - -

      Once some channels have been registered with a selector, a selection operation can be performed in -order to discover which channels, if any, have become ready to perform one or -more of the operations in which interest was previously declared. If a channel -is ready then the key returned when it was registered will be added to the -selector's selected-key set. The key set, and the keys within it, can -be examined in order to determine the operations for which each channel is -ready. From each key one can retrieve the corresponding channel in order to -perform whatever I/O operations are required. - -

      That a selection key indicates that its channel is ready for some operation -is a hint, but not a guarantee, that such an operation can be performed by a -thread without causing the thread to block. It is imperative that code that -performs multiplexed I/O be written so as to ignore these hints when they prove -to be incorrect. - -

      This package defines selectable-channel classes corresponding to the {@link -java.net.DatagramSocket}, {@link java.net.ServerSocket}, and {@link -java.net.Socket} classes defined in the {@link java.net} package. -Minor changes to these classes have been made in order to support sockets that -are associated with channels. This package also defines a simple class that -implements unidirectional pipes. In all cases, a new selectable channel is -created by invoking the static open method of the corresponding class. -If a channel needs an associated socket then a socket will be created as a side -effect of this operation. - -

      The implementation of selectors, selectable channels, and selection keys -can be replaced by "plugging in" an alternative definition or instance of the -{@link java.nio.channels.spi.SelectorProvider} class defined in the {@link -java.nio.channels.spi} package. It is not expected that many developers -will actually make use of this facility; it is provided primarily so that -sophisticated users can take advantage of operating-system-specific -I/O-multiplexing mechanisms when very high performance is required. - -

      Much of the bookkeeping and synchronization required to implement the -multiplexed-I/O abstractions is performed by the {@link -java.nio.channels.spi.AbstractInterruptibleChannel}, {@link -java.nio.channels.spi.AbstractSelectableChannel}, {@link -java.nio.channels.spi.AbstractSelectionKey}, and {@link -java.nio.channels.spi.AbstractSelector} classes in the {@link -java.nio.channels.spi} package. When defining a custom selector provider, -only the {@link java.nio.channels.spi.AbstractSelector} and {@link -java.nio.channels.spi.AbstractSelectionKey} classes should be subclassed -directly; custom channel classes should extend the appropriate {@link -java.nio.channels.SelectableChannel} subclasses defined in this package. - -

      Unless otherwise noted, passing a null argument to a constructor -or method in any class or interface in this package will cause a {@link -java.lang.NullPointerException NullPointerException} to be thrown. - - -@since 1.4 -@author Mark Reinhold -@author JSR-51 Expert Group - - - diff --git a/src/share/classes/java/nio/channels/spi/SelectorProvider.java b/src/share/classes/java/nio/channels/spi/SelectorProvider.java index 9a8a3cc70..dc61c9c27 100644 --- a/src/share/classes/java/nio/channels/spi/SelectorProvider.java +++ b/src/share/classes/java/nio/channels/spi/SelectorProvider.java @@ -25,10 +25,8 @@ package java.nio.channels.spi; -import java.io.FileDescriptor; import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; +import java.net.ProtocolFamily; import java.nio.channels.*; import java.security.AccessController; import java.security.PrivilegedAction; @@ -190,7 +188,25 @@ public abstract class SelectorProvider { throws IOException; /** - * Opens a pipe.

      + * Opens a datagram channel. + * + * @param family + * The protocol family + * + * @return A new datagram channel + * + * @throws UnsupportedOperationException + * If the specified protocol family is not supported + * @throws IOException + * If an I/O error occurs + * + * @since 1.7 + */ + public abstract DatagramChannel openDatagramChannel(ProtocolFamily family) + throws IOException; + + /** + * Opens a pipe.

      * * @return The new pipe */ diff --git a/src/share/classes/sun/nio/ch/DatagramChannelImpl.java b/src/share/classes/sun/nio/ch/DatagramChannelImpl.java index 029ab3e11..a7939c0ce 100644 --- a/src/share/classes/sun/nio/ch/DatagramChannelImpl.java +++ b/src/share/classes/sun/nio/ch/DatagramChannelImpl.java @@ -31,7 +31,7 @@ import java.net.*; import java.nio.ByteBuffer; import java.nio.channels.*; import java.nio.channels.spi.*; -import java.lang.ref.SoftReference; +import java.util.*; /** @@ -47,11 +47,14 @@ class DatagramChannelImpl private static NativeDispatcher nd = new DatagramDispatcher(); // Our file descriptor - FileDescriptor fd = null; + private final FileDescriptor fd; // fd value needed for dev/poll. This value will remain valid // even after the value in the file descriptor object has been set to -1 - int fdVal; + private final int fdVal; + + // The protocol family of the socket + private final ProtocolFamily family; // IDs of native threads doing reads and writes, for signalling private volatile long readerThread = 0; @@ -59,8 +62,8 @@ class DatagramChannelImpl // Cached InetAddress and port for unconnected DatagramChannels // used by receive0 - private InetAddress cachedSenderInetAddress = null; - private int cachedSenderPort = 0; + private InetAddress cachedSenderInetAddress; + private int cachedSenderPort; // Lock held by current reading or connecting thread private final Object readLock = new Object(); @@ -76,20 +79,20 @@ class DatagramChannelImpl // State (does not necessarily increase monotonically) private static final int ST_UNINITIALIZED = -1; - private static int ST_UNCONNECTED = 0; - private static int ST_CONNECTED = 1; + private static final int ST_UNCONNECTED = 0; + private static final int ST_CONNECTED = 1; private static final int ST_KILLED = 2; private int state = ST_UNINITIALIZED; // Binding - private SocketAddress localAddress = null; - SocketAddress remoteAddress = null; - - // Options - private SocketOpts.IP options = null; + private SocketAddress localAddress; + private SocketAddress remoteAddress; // Our socket adaptor, if any - private DatagramSocket socket = null; + private DatagramSocket socket; + + // Multicast support + private MembershipRegistry registry; // -- End of fields protected by stateLock @@ -98,7 +101,26 @@ class DatagramChannelImpl throws IOException { super(sp); - this.fd = Net.socket(false); + this.family = Net.isIPv6Available() ? + StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; + this.fd = Net.socket(family, false); + this.fdVal = IOUtil.fdVal(fd); + this.state = ST_UNCONNECTED; + } + + public DatagramChannelImpl(SelectorProvider sp, ProtocolFamily family) { + super(sp); + if ((family != StandardProtocolFamily.INET) && + (family != StandardProtocolFamily.INET6)) { + throw new UnsupportedOperationException("Protocol family not supported"); + } + if (family == StandardProtocolFamily.INET6) { + if (!Net.isIPv6Available()) { + throw new UnsupportedOperationException("IPv6 not available"); + } + } + this.family = family; + this.fd = Net.socket(family, false); this.fdVal = IOUtil.fdVal(fd); this.state = ST_UNCONNECTED; } @@ -107,9 +129,12 @@ class DatagramChannelImpl throws IOException { super(sp); + this.family = Net.isIPv6Available() ? + StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; this.fd = fd; this.fdVal = IOUtil.fdVal(fd); this.state = ST_UNCONNECTED; + this.localAddress = Net.localAddress(fd); } public DatagramSocket socket() { @@ -120,6 +145,156 @@ class DatagramChannelImpl } } + @Override + public SocketAddress getLocalAddress() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + return null; + return localAddress; + } + } + + @Override + public SocketAddress getConnectedAddress() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + return null; + return remoteAddress; + } + } + + @Override + public DatagramChannel setOption(SocketOption name, Object value) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!options().contains(name)) + throw new IllegalArgumentException("Invalid option name"); + + synchronized (stateLock) { + ensureOpen(); + + if (name == StandardSocketOption.IP_TOS) { + // IPv4 only; no-op for IPv6 + if (family == StandardProtocolFamily.INET) { + Net.setSocketOption(fd, family, name, value); + } + return this; + } + + if (name == StandardSocketOption.IP_MULTICAST_TTL || + name == StandardSocketOption.IP_MULTICAST_LOOP) + { + // options are protocol dependent + Net.setSocketOption(fd, family, name, value); + return this; + } + + if (name == StandardSocketOption.IP_MULTICAST_IF) { + if (value == null) + throw new IllegalArgumentException("Cannot set IP_MULTICAST_IF to 'null'"); + NetworkInterface interf = (NetworkInterface)value; + if (family == StandardProtocolFamily.INET6) { + int index = interf.getIndex(); + if (index == -1) + throw new IOException("Network interface cannot be identified"); + Net.setInterface6(fd, index); + } else { + // need IPv4 address to identify interface + Inet4Address target = Net.anyInet4Address(interf); + if (target == null) + throw new IOException("Network interface not configured for IPv4"); + int targetAddress = Net.inet4AsInt(target); + Net.setInterface4(fd, targetAddress); + } + return this; + } + + // remaining options don't need any special handling + Net.setSocketOption(fd, Net.UNSPEC, name, value); + return this; + } + } + + @Override + @SuppressWarnings("unchecked") + public T getOption(SocketOption name) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!options().contains(name)) + throw new IllegalArgumentException("Invalid option name"); + + synchronized (stateLock) { + ensureOpen(); + + if (name == StandardSocketOption.IP_TOS) { + // IPv4 only; always return 0 on IPv6 + if (family == StandardProtocolFamily.INET) { + return (T) Net.getSocketOption(fd, family, name); + } else { + return (T) Integer.valueOf(0); + } + } + + if (name == StandardSocketOption.IP_MULTICAST_TTL || + name == StandardSocketOption.IP_MULTICAST_LOOP) + { + return (T) Net.getSocketOption(fd, family, name); + } + + if (name == StandardSocketOption.IP_MULTICAST_IF) { + if (family == StandardProtocolFamily.INET) { + int address = Net.getInterface4(fd); + if (address == 0) + return null; // default interface + + InetAddress ia = Net.inet4FromInt(address); + NetworkInterface ni = NetworkInterface.getByInetAddress(ia); + if (ni == null) + throw new IOException("Unable to map address to interface"); + return (T) ni; + } else { + int index = Net.getInterface6(fd); + if (index == 0) + return null; // default interface + + NetworkInterface ni = NetworkInterface.getByIndex(index); + if (ni == null) + throw new IOException("Unable to map index to interface"); + return (T) ni; + } + } + + // no special handling + return (T) Net.getSocketOption(fd, Net.UNSPEC, name); + } + } + + private static class LazyInitialization { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet>(8); + set.add(StandardSocketOption.SO_SNDBUF); + set.add(StandardSocketOption.SO_RCVBUF); + set.add(StandardSocketOption.SO_REUSEADDR); + set.add(StandardSocketOption.SO_BROADCAST); + set.add(StandardSocketOption.IP_TOS); + set.add(StandardSocketOption.IP_MULTICAST_IF); + set.add(StandardSocketOption.IP_MULTICAST_TTL); + set.add(StandardSocketOption.IP_MULTICAST_LOOP); + return Collections.unmodifiableSet(set); + } + } + + @Override + public final Set> options() { + return LazyInitialization.defaultOptions; + } + private void ensureOpen() throws ClosedChannelException { if (!isOpen()) throw new ClosedChannelException(); @@ -135,8 +310,10 @@ class DatagramChannelImpl synchronized (readLock) { ensureOpen(); // If socket is not bound then behave as if nothing received - if (!isBound()) // ## NotYetBoundException ?? + // Will be fixed by 6621699 + if (localAddress() == null) { return null; + } int n = 0; ByteBuffer bb = null; try { @@ -267,6 +444,12 @@ class DatagramChannelImpl do { n = send(fd, src, target); } while ((n == IOStatus.INTERRUPTED) && isOpen()); + + synchronized (stateLock) { + if (isOpen() && (localAddress == null)) { + localAddress = Net.localAddress(fd); + } + } return IOStatus.normalize(n); } finally { writerThread = 0; @@ -316,7 +499,8 @@ class DatagramChannelImpl assert (pos <= lim); int rem = (pos <= lim ? lim - pos : 0); - int written = send0(fd, ((DirectBuffer)bb).address() + pos, + boolean preferIPv6 = (family != StandardProtocolFamily.INET); + int written = send0(preferIPv6, fd, ((DirectBuffer)bb).address() + pos, rem, target); if (written > 0) bb.position(pos + written); @@ -453,42 +637,8 @@ class DatagramChannelImpl IOUtil.configureBlocking(fd, block); } - public SocketOpts options() { - synchronized (stateLock) { - if (options == null) { - SocketOptsImpl.Dispatcher d - = new SocketOptsImpl.Dispatcher() { - int getInt(int opt) throws IOException { - return Net.getIntOption(fd, opt); - } - void setInt(int opt, int arg) - throws IOException - { - Net.setIntOption(fd, opt, arg); - } - }; - options = new SocketOptsImpl.IP(d); - } - return options; - } - } - - public boolean isBound() { - return Net.localPortNumber(fd) != 0; - } - public SocketAddress localAddress() { synchronized (stateLock) { - if (isConnected() && (localAddress == null)) { - // Socket was not bound before connecting, - // so ask what the address turned out to be - localAddress = Net.localAddress(fd); - } - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - InetSocketAddress isa = (InetSocketAddress)localAddress; - sm.checkConnect(isa.getAddress().getHostAddress(), -1); - } return localAddress; } } @@ -499,22 +649,37 @@ class DatagramChannelImpl } } - public void bind(SocketAddress local) throws IOException { + @Override + public DatagramChannel bind(SocketAddress local) throws IOException { synchronized (readLock) { synchronized (writeLock) { synchronized (stateLock) { ensureOpen(); - if (isBound()) + if (localAddress != null) throw new AlreadyBoundException(); - InetSocketAddress isa = Net.checkAddress(local); + InetSocketAddress isa; + if (local == null) { + isa = new InetSocketAddress(0); + } else { + isa = Net.checkAddress(local); + + // only Inet4Address allowed with IPv4 socket + if (family == StandardProtocolFamily.INET) { + InetAddress addr = isa.getAddress(); + if (!(addr instanceof Inet4Address)) + throw new UnsupportedAddressTypeException(); + } + } SecurityManager sm = System.getSecurityManager(); - if (sm != null) + if (sm != null) { sm.checkListen(isa.getPort()); - Net.bind(fd, isa.getAddress(), isa.getPort()); + } + Net.bind(family, fd, isa.getAddress(), isa.getPort()); localAddress = Net.localAddress(fd); } } } + return this; } public boolean isConnected() { @@ -533,7 +698,6 @@ class DatagramChannelImpl } public DatagramChannel connect(SocketAddress sa) throws IOException { - int trafficClass = 0; int localPort = 0; synchronized(readLock) { @@ -545,10 +709,10 @@ class DatagramChannelImpl if (sm != null) sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort()); - int n = Net.connect(fd, + int n = Net.connect(family, + fd, isa.getAddress(), - isa.getPort(), - trafficClass); + isa.getPort()); if (n <= 0) throw new Error(); // Can't happen @@ -558,6 +722,11 @@ class DatagramChannelImpl sender = isa; cachedSenderInetAddress = isa.getAddress(); cachedSenderPort = isa.getPort(); + + // Socket was not bound before connecting, + if (localAddress == null) { + localAddress = Net.localAddress(fd); + } } } } @@ -584,9 +753,215 @@ class DatagramChannelImpl return this; } + /** + * Joins channel's socket to the given group/interface and + * optional source address. + */ + private MembershipKey innerJoin(InetAddress group, + NetworkInterface interf, + InetAddress source) + throws IOException + { + if (!group.isMulticastAddress()) + throw new IllegalArgumentException("Group not a multicast address"); + + // check multicast address is compatible with this socket + if (!(group instanceof Inet4Address)) { + if (family == StandardProtocolFamily.INET) + throw new IllegalArgumentException("Group is not IPv4 address"); + if (!(group instanceof Inet6Address)) + throw new IllegalArgumentException("Address type not supported"); + } + + // check source address + if (source != null) { + if (source.isAnyLocalAddress()) + throw new IllegalArgumentException("Source address is a wildcard address"); + if (source.isMulticastAddress()) + throw new IllegalArgumentException("Source address is multicast address"); + if (source.getClass() != group.getClass()) + throw new IllegalArgumentException("Source address is different type to group"); + } + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkMulticast(group); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + // check the registry to see if we are already a member of the group + if (registry == null) { + registry = new MembershipRegistry(); + } else { + // return existing membership key + MembershipKey key = registry.checkMembership(group, interf, source); + if (key != null) + return key; + } + + MembershipKeyImpl key; + if (family == StandardProtocolFamily.INET6) { + int index = interf.getIndex(); + if (index == -1) + throw new IOException("Network interface cannot be identified"); + + // need multicast and source address as byte arrays + byte[] groupAddress = Net.inet6AsByteArray(group); + byte[] sourceAddress = (source == null) ? null : + Net.inet6AsByteArray(source); + + // join the group + int n = Net.join6(fd, groupAddress, index, sourceAddress); + if (n == IOStatus.UNAVAILABLE) + throw new UnsupportedOperationException(); + + key = new MembershipKeyImpl.Type6(this, group, interf, source, + groupAddress, index, sourceAddress); + + } else { + // need IPv4 address to identify interface + Inet4Address target = Net.anyInet4Address(interf); + if (target == null) + throw new IOException("Network interface not configured for IPv4"); + + int groupAddress = Net.inet4AsInt(group); + int targetAddress = Net.inet4AsInt(target); + int sourceAddress = (source == null) ? 0 : Net.inet4AsInt(source); + + // join the group + int n = Net.join4(fd, groupAddress, targetAddress, sourceAddress); + if (n == IOStatus.UNAVAILABLE) + throw new UnsupportedOperationException(); + + key = new MembershipKeyImpl.Type4(this, group, interf, source, + groupAddress, targetAddress, sourceAddress); + } + + registry.add(key); + return key; + } + } + + @Override + public MembershipKey join(InetAddress group, + NetworkInterface interf) + throws IOException + { + return innerJoin(group, interf, null); + } + + @Override + public MembershipKey join(InetAddress group, + NetworkInterface interf, + InetAddress source) + throws IOException + { + if (source == null) + throw new NullPointerException("source address is null"); + return innerJoin(group, interf, source); + } + + // package-private + void drop(MembershipKeyImpl key) + throws IOException + { + assert key.getChannel() == this; + + synchronized (stateLock) { + if (!key.isValid()) + return; + + if (family == StandardProtocolFamily.INET6) { + MembershipKeyImpl.Type6 key6 = + (MembershipKeyImpl.Type6)key; + Net.drop6(fd, key6.group(), key6.index(), key6.source()); + } else { + MembershipKeyImpl.Type4 key4 = + (MembershipKeyImpl.Type4)key; + Net.drop4(fd, key4.group(), key4.interfaceAddress(), key4.source()); + } + + key.invalidate(); + registry.remove(key); + } + } + + /** + * Block datagrams from given source if a memory to receive all + * datagrams. + */ + void block(MembershipKeyImpl key, InetAddress source) + throws IOException + { + assert key.getChannel() == this; + assert key.getSourceAddress() == null; + + synchronized (stateLock) { + if (!key.isValid()) + throw new IllegalStateException("key is no longer valid"); + if (source.isAnyLocalAddress()) + throw new IllegalArgumentException("Source address is a wildcard address"); + if (source.isMulticastAddress()) + throw new IllegalArgumentException("Source address is multicast address"); + if (source.getClass() != key.getGroup().getClass()) + throw new IllegalArgumentException("Source address is different type to group"); + + int n; + if (family == StandardProtocolFamily.INET6) { + MembershipKeyImpl.Type6 key6 = + (MembershipKeyImpl.Type6)key; + n = Net.block6(fd, key6.group(), key6.index(), + Net.inet6AsByteArray(source)); + } else { + MembershipKeyImpl.Type4 key4 = + (MembershipKeyImpl.Type4)key; + n = Net.block4(fd, key4.group(), key4.interfaceAddress(), + Net.inet4AsInt(source)); + } + if (n == IOStatus.UNAVAILABLE) { + // ancient kernel + throw new UnsupportedOperationException(); + } + } + } + + /** + * Unblock given source. + */ + void unblock(MembershipKeyImpl key, InetAddress source) + throws IOException + { + assert key.getChannel() == this; + assert key.getSourceAddress() == null; + + synchronized (stateLock) { + if (!key.isValid()) + throw new IllegalStateException("key is no longer valid"); + + if (family == StandardProtocolFamily.INET6) { + MembershipKeyImpl.Type6 key6 = + (MembershipKeyImpl.Type6)key; + Net.unblock6(fd, key6.group(), key6.index(), + Net.inet6AsByteArray(source)); + } else { + MembershipKeyImpl.Type4 key4 = + (MembershipKeyImpl.Type4)key; + Net.unblock4(fd, key4.group(), key4.interfaceAddress(), + Net.inet4AsInt(source)); + } + } + } + protected void implCloseSelectableChannel() throws IOException { synchronized (stateLock) { nd.preClose(fd); + + // if member of mulitcast group then invalidate all keys + if (registry != null) + registry.invalidateAll(); + long th; if ((th = readerThread) != 0) NativeThread.signal(th); @@ -695,8 +1070,8 @@ class DatagramChannelImpl boolean connected) throws IOException; - private native int send0(FileDescriptor fd, long address, int len, - SocketAddress sa) + private native int send0(boolean preferIPv6, FileDescriptor fd, long address, int len, + SocketAddress sa) throws IOException; static { diff --git a/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java b/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java index 79a0d41b4..34b24225a 100644 --- a/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java +++ b/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java @@ -45,16 +45,9 @@ public class DatagramSocketAdaptor // The channel being adapted private final DatagramChannelImpl dc; - // Option adaptor object, created on demand - private volatile OptionAdaptor opts = null; - // Timeout "option" value for receives private volatile int timeout = 0; - // Traffic-class/Type-of-service - private volatile int trafficClass = 0; - - // ## super will create a useless impl private DatagramSocketAdaptor(DatagramChannelImpl dc) throws IOException { // Invoke the DatagramSocketAdaptor(SocketAddress) constructor, @@ -82,7 +75,7 @@ public class DatagramSocketAdaptor throw new IllegalArgumentException("connect: " + port); if (remote == null) throw new IllegalArgumentException("connect: null address"); - if (!isClosed()) + if (isClosed()) return; try { dc.connect(remote); @@ -124,11 +117,11 @@ public class DatagramSocketAdaptor } public boolean isBound() { - return dc.isBound(); + return dc.localAddress() != null; } public boolean isConnected() { - return dc.isConnected(); + return dc.remoteAddress() != null; } public InetAddress getInetAddress() { @@ -157,7 +150,7 @@ public class DatagramSocketAdaptor // Legacy DatagramSocket will send in this case // and set address and port of the packet InetSocketAddress isa = (InetSocketAddress) - dc.remoteAddress; + dc.remoteAddress(); p.setPort(isa.getPort()); p.setAddress(isa.getAddress()); dc.write(bb); @@ -241,21 +234,32 @@ public class DatagramSocketAdaptor public InetAddress getLocalAddress() { if (isClosed()) return null; - try { - return Net.asInetSocketAddress(dc.localAddress()).getAddress(); - } catch (Exception x) { - return new InetSocketAddress(0).getAddress(); + SocketAddress local = dc.localAddress(); + if (local == null) + local = new InetSocketAddress(0); + InetAddress result = ((InetSocketAddress)local).getAddress(); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + sm.checkConnect(result.getHostAddress(), -1); + } catch (SecurityException x) { + return new InetSocketAddress(0).getAddress(); + } } + return result; } public int getLocalPort() { if (isClosed()) return -1; try { - return Net.asInetSocketAddress(dc.localAddress()).getPort(); + SocketAddress local = dc.getLocalAddress(); + if (local != null) { + return ((InetSocketAddress)local).getPort(); + } } catch (Exception x) { - return 0; } + return 0; } public void setSoTimeout(int timeout) throws SocketException { @@ -266,55 +270,87 @@ public class DatagramSocketAdaptor return timeout; } - private OptionAdaptor opts() { - if (opts == null) - opts = new OptionAdaptor(dc); - return opts; + private void setBooleanOption(SocketOption name, boolean value) + throws SocketException + { + try { + dc.setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private void setIntOption(SocketOption name, int value) + throws SocketException + { + try { + dc.setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private boolean getBooleanOption(SocketOption name) throws SocketException { + try { + return dc.getOption(name).booleanValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return false; // keep compiler happy + } + } + + private int getIntOption(SocketOption name) throws SocketException { + try { + return dc.getOption(name).intValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return -1; // keep compiler happy + } } public void setSendBufferSize(int size) throws SocketException { - opts().setSendBufferSize(size); + if (size <= 0) + throw new IllegalArgumentException("Invalid send size"); + setIntOption(StandardSocketOption.SO_SNDBUF, size); } public int getSendBufferSize() throws SocketException { - return opts().getSendBufferSize(); + return getIntOption(StandardSocketOption.SO_SNDBUF); } public void setReceiveBufferSize(int size) throws SocketException { - opts().setReceiveBufferSize(size); + if (size <= 0) + throw new IllegalArgumentException("Invalid receive size"); + setIntOption(StandardSocketOption.SO_RCVBUF, size); } public int getReceiveBufferSize() throws SocketException { - return opts().getReceiveBufferSize(); + return getIntOption(StandardSocketOption.SO_RCVBUF); } public void setReuseAddress(boolean on) throws SocketException { - opts().setReuseAddress(on); + setBooleanOption(StandardSocketOption.SO_REUSEADDR, on); } public boolean getReuseAddress() throws SocketException { - return opts().getReuseAddress(); + return getBooleanOption(StandardSocketOption.SO_REUSEADDR); + } public void setBroadcast(boolean on) throws SocketException { - opts().setBroadcast(on); + setBooleanOption(StandardSocketOption.SO_BROADCAST, on); } public boolean getBroadcast() throws SocketException { - return opts().getBroadcast(); + return getBooleanOption(StandardSocketOption.SO_BROADCAST); } public void setTrafficClass(int tc) throws SocketException { - opts().setTrafficClass(tc); - trafficClass = tc; + setIntOption(StandardSocketOption.IP_TOS, tc); } public int getTrafficClass() throws SocketException { - int tc = opts().getTrafficClass(); - if (tc < 0) { - tc = trafficClass; - } - return tc; + return getIntOption(StandardSocketOption.IP_TOS); } public void close() { diff --git a/src/share/classes/sun/nio/ch/ExtendedSocketOption.java b/src/share/classes/sun/nio/ch/ExtendedSocketOption.java new file mode 100644 index 000000000..86e787ef8 --- /dev/null +++ b/src/share/classes/sun/nio/ch/ExtendedSocketOption.java @@ -0,0 +1,44 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.nio.ch; + +import java.net.SocketOption; + +/** + * Defines socket options that are supported by the implementation + * but not defined in StandardSocketOption. + */ + +class ExtendedSocketOption { + private ExtendedSocketOption() { } + + static final SocketOption SO_OOBINLINE = + new SocketOption() { + public String name() { return "SO_OOBINLINE"; } + public Class type() { return Boolean.class; } + public String toString() { return name(); } + }; +} diff --git a/src/share/classes/sun/nio/ch/MembershipKeyImpl.java b/src/share/classes/sun/nio/ch/MembershipKeyImpl.java new file mode 100644 index 000000000..687f79c00 --- /dev/null +++ b/src/share/classes/sun/nio/ch/MembershipKeyImpl.java @@ -0,0 +1,222 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.nio.ch; + +import java.nio.channels.*; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.io.IOException; +import java.util.HashSet; + +/** + * MembershipKey implementation. + */ + +class MembershipKeyImpl + extends MembershipKey +{ + private final MulticastChannel ch; + private final InetAddress group; + private final NetworkInterface interf; + private final InetAddress source; + + // true when key is valid + private volatile boolean valid = true; + + // lock used when creating or accessing blockedSet + private Object stateLock = new Object(); + + // set of source addresses that are blocked + private HashSet blockedSet; + + private MembershipKeyImpl(MulticastChannel ch, + InetAddress group, + NetworkInterface interf, + InetAddress source) + { + this.ch = ch; + this.group = group; + this.interf = interf; + this.source = source; + } + + /** + * MembershipKey will additional context for IPv4 membership + */ + static class Type4 extends MembershipKeyImpl { + private final int groupAddress; + private final int interfAddress; + private final int sourceAddress; + + Type4(MulticastChannel ch, + InetAddress group, + NetworkInterface interf, + InetAddress source, + int groupAddress, + int interfAddress, + int sourceAddress) + { + super(ch, group, interf, source); + this.groupAddress = groupAddress; + this.interfAddress = interfAddress; + this.sourceAddress = sourceAddress; + } + + int group() { + return groupAddress; + } + + int interfaceAddress() { + return interfAddress; + } + + int source() { + return sourceAddress; + } + } + + /** + * MembershipKey will additional context for IPv6 membership + */ + static class Type6 extends MembershipKeyImpl { + private final byte[] groupAddress; + private final int index; + private final byte[] sourceAddress; + + Type6(MulticastChannel ch, + InetAddress group, + NetworkInterface interf, + InetAddress source, + byte[] groupAddress, + int index, + byte[] sourceAddress) + { + super(ch, group, interf, source); + this.groupAddress = groupAddress; + this.index = index; + this.sourceAddress = sourceAddress; + } + + byte[] group() { + return groupAddress; + } + + int index() { + return index; + } + + byte[] source() { + return sourceAddress; + } + } + + public boolean isValid() { + return valid; + } + + // package-private + void invalidate() { + valid = false; + } + + public void drop() throws IOException { + // delegate to channel + ((DatagramChannelImpl)ch).drop(this); + } + + @Override + public MulticastChannel getChannel() { + return ch; + } + + @Override + public InetAddress getGroup() { + return group; + } + + @Override + public NetworkInterface getNetworkInterface() { + return interf; + } + + @Override + public InetAddress getSourceAddress() { + return source; + } + + @Override + public MembershipKey block(InetAddress toBlock) + throws IOException + { + if (source != null) + throw new IllegalStateException("key is source-specific"); + + synchronized (stateLock) { + if ((blockedSet != null) && blockedSet.contains(toBlock)) { + // already blocked, nothing to do + return this; + } + + ((DatagramChannelImpl)ch).block(this, toBlock); + + // created blocked set if required and add source address + if (blockedSet == null) + blockedSet = new HashSet(); + blockedSet.add(toBlock); + } + return this; + } + + @Override + public MembershipKey unblock(InetAddress toUnblock) + throws IOException + { + synchronized (stateLock) { + if ((blockedSet == null) || !blockedSet.contains(toUnblock)) + throw new IllegalStateException("not blocked"); + + ((DatagramChannelImpl)ch).unblock(this, toUnblock); + + blockedSet.remove(toUnblock); + } + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(64); + sb.append('<'); + sb.append(group.getHostAddress()); + sb.append(','); + sb.append(interf.getName()); + if (source != null) { + sb.append(','); + sb.append(source.getHostAddress()); + } + sb.append('>'); + return sb.toString(); + } +} diff --git a/src/share/classes/sun/nio/ch/MembershipRegistry.java b/src/share/classes/sun/nio/ch/MembershipRegistry.java new file mode 100644 index 000000000..5a2a8ef74 --- /dev/null +++ b/src/share/classes/sun/nio/ch/MembershipRegistry.java @@ -0,0 +1,129 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.nio.ch; + +import java.nio.channels.*; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.*; + +/** + * Simple registry of membership keys for a MulticastChannel. + * + * Instances of this object are not safe by multiple concurrent threads. + */ + +class MembershipRegistry { + + // map multicast group to keys + private Map> groups = null; + + MembershipRegistry() { + } + + /** + * Checks registry for membership of the group on the given + * network interface. + */ + MembershipKey checkMembership(InetAddress group, NetworkInterface interf, + InetAddress source) + { + if (groups != null) { + List keys = groups.get(group); + if (keys != null) { + for (MembershipKeyImpl key: keys) { + if (key.getNetworkInterface().equals(interf)) { + // already a member to receive all packets so return + // existing key or detect conflict + if (source == null) { + if (key.getSourceAddress() == null) + return key; + throw new IllegalStateException("Already a member to receive all packets"); + } + + // already have source-specific membership so return key + // or detect conflict + if (key.getSourceAddress() == null) + throw new IllegalStateException("Already have source-specific membership"); + if (source.equals(key.getSourceAddress())) + return key; + } + } + } + } + return null; + } + + /** + * Add membership to the registry, returning a new membership key. + */ + void add(MembershipKeyImpl key) { + InetAddress group = key.getGroup(); + List keys; + if (groups == null) { + groups = new HashMap>(); + keys = null; + } else { + keys = groups.get(group); + } + if (keys == null) { + keys = new LinkedList(); + groups.put(group, keys); + } + keys.add(key); + } + + /** + * Remove a key from the registry + */ + void remove(MembershipKeyImpl key) { + InetAddress group = key.getGroup(); + List keys = groups.get(group); + if (keys != null) { + Iterator i = keys.iterator(); + while (i.hasNext()) { + if (i.next() == key) { + i.remove(); + break; + } + } + if (keys.isEmpty()) { + groups.remove(group); + } + } + } + + /** + * Invalidate all keys in the registry + */ + void invalidateAll() { + for (InetAddress group: groups.keySet()) { + for (MembershipKeyImpl key: groups.get(group)) { + key.invalidate(); + } + } + } +} diff --git a/src/share/classes/sun/nio/ch/Net.java b/src/share/classes/sun/nio/ch/Net.java index 295e4f5c4..98b4615ab 100644 --- a/src/share/classes/sun/nio/ch/Net.java +++ b/src/share/classes/sun/nio/ch/Net.java @@ -26,21 +26,43 @@ package sun.nio.ch; import java.io.*; -import java.lang.reflect.*; import java.net.*; import java.nio.channels.*; +import java.util.*; +import java.security.AccessController; +import java.security.PrivilegedAction; class Net { // package-private private Net() { } + // unspecified protocol family + static final ProtocolFamily UNSPEC = new ProtocolFamily() { + public String name() { + return "UNSPEC"; + } + }; // -- Miscellaneous utilities -- + private static volatile boolean checkedIPv6 = false; + private static volatile boolean isIPv6Available; + + /** + * Tells whether dual-IPv4/IPv6 sockets should be used. + */ + static boolean isIPv6Available() { + if (!checkedIPv6) { + isIPv6Available = isIPv6Available0(); + checkedIPv6 = true; + } + return isIPv6Available; + } + static InetSocketAddress checkAddress(SocketAddress sa) { if (sa == null) - throw new IllegalArgumentException(); + throw new NullPointerException(); if (!(sa instanceof InetSocketAddress)) throw new UnsupportedAddressTypeException(); // ## needs arg InetSocketAddress isa = (InetSocketAddress)sa; @@ -63,6 +85,8 @@ class Net { // package-private Exception nx = x; if (x instanceof ClosedChannelException) nx = new SocketException("Socket is closed"); + else if (x instanceof NotYetConnectedException) + nx = new SocketException("Socket is not connected"); else if (x instanceof AlreadyBoundException) nx = new SocketException("Already bound"); else if (x instanceof NotYetBoundException) @@ -105,73 +129,359 @@ class Net { // package-private translateException(x, false); } + /** + * Returns any IPv4 address of the given network interface, or + * null if the interface does not have any IPv4 addresses. + */ + static Inet4Address anyInet4Address(final NetworkInterface interf) { + return AccessController.doPrivileged(new PrivilegedAction() { + public Inet4Address run() { + Enumeration addrs = interf.getInetAddresses(); + while (addrs.hasMoreElements()) { + InetAddress addr = addrs.nextElement(); + if (addr instanceof Inet4Address) { + return (Inet4Address)addr; + } + } + return null; + } + }); + } + + /** + * Returns an IPv4 address as an int. + */ + static int inet4AsInt(InetAddress ia) { + if (ia instanceof Inet4Address) { + byte[] addr = ia.getAddress(); + int address = addr[3] & 0xFF; + address |= ((addr[2] << 8) & 0xFF00); + address |= ((addr[1] << 16) & 0xFF0000); + address |= ((addr[0] << 24) & 0xFF000000); + return address; + } + throw new AssertionError("Should not reach here"); + } + + /** + * Returns an InetAddress from the given IPv4 address + * represented as an int. + */ + static InetAddress inet4FromInt(int address) { + byte[] addr = new byte[4]; + addr[0] = (byte) ((address >>> 24) & 0xFF); + addr[1] = (byte) ((address >>> 16) & 0xFF); + addr[2] = (byte) ((address >>> 8) & 0xFF); + addr[3] = (byte) (address & 0xFF); + try { + return InetAddress.getByAddress(addr); + } catch (UnknownHostException uhe) { + throw new AssertionError("Should not reach here"); + } + } + + /** + * Returns an IPv6 address as a byte array + */ + static byte[] inet6AsByteArray(InetAddress ia) { + if (ia instanceof Inet6Address) { + return ia.getAddress(); + } + + // need to construct IPv4-mapped address + if (ia instanceof Inet4Address) { + byte[] ip4address = ia.getAddress(); + byte[] address = new byte[16]; + address[10] = (byte)0xff; + address[11] = (byte)0xff; + address[12] = ip4address[0]; + address[13] = ip4address[1]; + address[14] = ip4address[2]; + address[15] = ip4address[3]; + return address; + } + + throw new AssertionError("Should not reach here"); + } + + // -- Socket options + + static void setSocketOption(FileDescriptor fd, ProtocolFamily family, + SocketOption name, Object value) + throws IOException + { + if (value == null) + throw new IllegalArgumentException("Invalid option value"); + + // only simple values supported by this method + Class type = name.type(); + if (type != Integer.class && type != Boolean.class) + throw new AssertionError("Should not reach here"); + + // special handling + if (name == StandardSocketOption.SO_RCVBUF || + name == StandardSocketOption.SO_SNDBUF) + { + int i = ((Integer)value).intValue(); + if (i < 0) + throw new IllegalArgumentException("Invalid send/receive buffer size"); + } + if (name == StandardSocketOption.SO_LINGER) { + int i = ((Integer)value).intValue(); + if (i < 0) + value = Integer.valueOf(-1); + if (i > 65535) + value = Integer.valueOf(65535); + } + if (name == StandardSocketOption.IP_TOS) { + int i = ((Integer)value).intValue(); + if (i < 0 || i > 255) + throw new IllegalArgumentException("Invalid IP_TOS value"); + } + if (name == StandardSocketOption.IP_MULTICAST_TTL) { + int i = ((Integer)value).intValue(); + if (i < 0 || i > 255) + throw new IllegalArgumentException("Invalid TTL/hop value"); + } + + // map option name to platform level/name + OptionKey key = SocketOptionRegistry.findOption(name, family); + if (key == null) + throw new AssertionError("Option not found"); + + int arg; + if (type == Integer.class) { + arg = ((Integer)value).intValue(); + } else { + boolean b = ((Boolean)value).booleanValue(); + arg = (b) ? 1 : 0; + } + + boolean mayNeedConversion = (family == UNSPEC); + setIntOption0(fd, mayNeedConversion, key.level(), key.name(), arg); + } + + static Object getSocketOption(FileDescriptor fd, ProtocolFamily family, + SocketOption name) + throws IOException + { + Class type = name.type(); + + // only simple values supported by this method + if (type != Integer.class && type != Boolean.class) + throw new AssertionError("Should not reach here"); + + // map option name to platform level/name + OptionKey key = SocketOptionRegistry.findOption(name, family); + if (key == null) + throw new AssertionError("Option not found"); + + boolean mayNeedConversion = (family == UNSPEC); + int value = getIntOption0(fd, mayNeedConversion, key.level(), key.name()); + + if (type == Integer.class) { + return Integer.valueOf(value); + } else { + return (value == 0) ? Boolean.FALSE : Boolean.TRUE; + } + } // -- Socket operations -- + static native boolean isIPv6Available0(); + static FileDescriptor socket(boolean stream) { - return IOUtil.newFD(socket0(stream, false)); + return socket(UNSPEC, stream); + } + + static FileDescriptor socket(ProtocolFamily family, boolean stream) { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + return IOUtil.newFD(socket0(preferIPv6, stream, false)); } static FileDescriptor serverSocket(boolean stream) { - return IOUtil.newFD(socket0(stream, true)); + return IOUtil.newFD(socket0(isIPv6Available(), stream, true)); } // Due to oddities SO_REUSEADDR on windows reuse is ignored - private static native int socket0(boolean stream, boolean reuse); + private static native int socket0(boolean preferIPv6, boolean stream, boolean reuse); + + static void bind(FileDescriptor fd, InetAddress addr, int port) + throws IOException + { + bind(UNSPEC, fd, addr, port); + } - static native void bind(FileDescriptor fd, InetAddress addr, int port) + static void bind(ProtocolFamily family, FileDescriptor fd, + InetAddress addr, int port) throws IOException + { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + bind0(preferIPv6, fd, addr, port); + } + + private static native void bind0(boolean preferIPv6, FileDescriptor fd, + InetAddress addr, int port) throws IOException; - static native int connect(FileDescriptor fd, - InetAddress remote, - int remotePort, - int trafficClass) + static native void listen(FileDescriptor fd, int backlog) throws IOException; + + static int connect(FileDescriptor fd, InetAddress remote, int remotePort) + throws IOException + { + return connect(UNSPEC, fd, remote, remotePort); + } + + static int connect(ProtocolFamily family, FileDescriptor fd, InetAddress remote, int remotePort) + throws IOException + { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + return connect0(preferIPv6, fd, remote, remotePort); + } + + private static native int connect0(boolean preferIPv6, + FileDescriptor fd, + InetAddress remote, + int remotePort) throws IOException; + public final static int SHUT_RD = 0; + public final static int SHUT_WR = 1; + public final static int SHUT_RDWR = 2; + + static native void shutdown(FileDescriptor fd, int how) throws IOException; + private static native int localPort(FileDescriptor fd) throws IOException; private static native InetAddress localInetAddress(FileDescriptor fd) throws IOException; - static InetSocketAddress localAddress(FileDescriptor fd) { - try { - return new InetSocketAddress(localInetAddress(fd), - localPort(fd)); - } catch (IOException x) { - throw new Error(x); // Can't happen - } + static InetSocketAddress localAddress(FileDescriptor fd) + throws IOException + { + return new InetSocketAddress(localInetAddress(fd), localPort(fd)); } - static int localPortNumber(FileDescriptor fd) { - try { - return localPort(fd); - } catch (IOException x) { - throw new Error(x); // Can't happen - } + private static native int remotePort(FileDescriptor fd) + throws IOException; + + private static native InetAddress remoteInetAddress(FileDescriptor fd) + throws IOException; + + static InetSocketAddress remoteAddress(FileDescriptor fd) + throws IOException + { + return new InetSocketAddress(remoteInetAddress(fd), remotePort(fd)); + } + + private static native int getIntOption0(FileDescriptor fd, boolean mayNeedConversion, + int level, int opt) + throws IOException; + + private static native void setIntOption0(FileDescriptor fd, boolean mayNeedConversion, + int level, int opt, int arg) + throws IOException; + + // -- Multicast support -- + + + /** + * Join IPv4 multicast group + */ + static int join4(FileDescriptor fd, int group, int interf, int source) + throws IOException + { + return joinOrDrop4(true, fd, group, interf, source); + } + + /** + * Drop membership of IPv4 multicast group + */ + static void drop4(FileDescriptor fd, int group, int interf, int source) + throws IOException + { + joinOrDrop4(false, fd, group, interf, source); + } + + private static native int joinOrDrop4(boolean join, FileDescriptor fd, int group, int interf, int source) + throws IOException; + + /** + * Block IPv4 source + */ + static int block4(FileDescriptor fd, int group, int interf, int source) + throws IOException + { + return blockOrUnblock4(true, fd, group, interf, source); + } + + /** + * Unblock IPv6 source + */ + static void unblock4(FileDescriptor fd, int group, int interf, int source) + throws IOException + { + blockOrUnblock4(false, fd, group, interf, source); } - private static native int getIntOption0(FileDescriptor fd, int opt) + private static native int blockOrUnblock4(boolean block, FileDescriptor fd, int group, + int interf, int source) throws IOException; - static int getIntOption(FileDescriptor fd, int opt) + /** + * Join IPv6 multicast group + */ + static int join6(FileDescriptor fd, byte[] group, int index, byte[] source) throws IOException { - return getIntOption0(fd, opt); + return joinOrDrop6(true, fd, group, index, source); } + /** + * Drop membership of IPv6 multicast group + */ + static void drop6(FileDescriptor fd, byte[] group, int index, byte[] source) + throws IOException + { + joinOrDrop6(false, fd, group, index, source); + } - private static native void setIntOption0(FileDescriptor fd, - int opt, int arg) + private static native int joinOrDrop6(boolean join, FileDescriptor fd, byte[] group, int index, byte[] source) throws IOException; - static void setIntOption(FileDescriptor fd, int opt, int arg) + /** + * Block IPv6 source + */ + static int block6(FileDescriptor fd, byte[] group, int index, byte[] source) + throws IOException + { + return blockOrUnblock6(true, fd, group, index, source); + } + + /** + * Unblock IPv6 source + */ + static void unblock6(FileDescriptor fd, byte[] group, int index, byte[] source) throws IOException { - setIntOption0(fd, opt, arg); + blockOrUnblock6(false, fd, group, index, source); } + static native int blockOrUnblock6(boolean block, FileDescriptor fd, byte[] group, int index, byte[] source) + throws IOException; + + static native void setInterface4(FileDescriptor fd, int interf) throws IOException; + + static native int getInterface4(FileDescriptor fd) throws IOException; + + static native void setInterface6(FileDescriptor fd, int index) throws IOException; + + static native int getInterface6(FileDescriptor fd) throws IOException; + private static native void initIDs(); static { diff --git a/src/share/classes/sun/nio/ch/OptionAdaptor.java b/src/share/classes/sun/nio/ch/OptionAdaptor.java deleted file mode 100644 index 95a271176..000000000 --- a/src/share/classes/sun/nio/ch/OptionAdaptor.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -package sun.nio.ch; - -import java.io.*; -import java.net.*; -import java.nio.*; -import java.nio.channels.*; - - -// Adaptor class for java.net-style options -// -// The option get/set methods in the socket, server-socket, and datagram-socket -// adaptors delegate to an instance of this class. -// - -class OptionAdaptor { // package-private - - private final SocketOpts.IP opts; - - OptionAdaptor(SocketChannelImpl sc) { - opts = (SocketOpts.IP)sc.options(); - } - - OptionAdaptor(ServerSocketChannelImpl ssc) { - opts = (SocketOpts.IP)ssc.options(); - } - - OptionAdaptor(DatagramChannelImpl dc) { - opts = (SocketOpts.IP)dc.options(); - } - - private SocketOpts.IP opts() { - return opts; - } - - private SocketOpts.IP.TCP tcpOpts() { - return (SocketOpts.IP.TCP)opts; - } - - public void setTcpNoDelay(boolean on) throws SocketException { - try { - tcpOpts().noDelay(on); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public boolean getTcpNoDelay() throws SocketException { - try { - return tcpOpts().noDelay(); - } catch (Exception x) { - Net.translateToSocketException(x); - return false; // Never happens - } - } - - public void setSoLinger(boolean on, int linger) throws SocketException { - try { - if (linger > 65535) - linger = 65535; - opts().linger(on ? linger : -1); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public int getSoLinger() throws SocketException { - try { - return opts().linger(); - } catch (Exception x) { - Net.translateToSocketException(x); - return 0; // Never happens - } - } - - public void setOOBInline(boolean on) throws SocketException { - try { - opts().outOfBandInline(on); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public boolean getOOBInline() throws SocketException { - try { - return opts().outOfBandInline(); - } catch (Exception x) { - Net.translateToSocketException(x); - return false; // Never happens - } - } - - public void setSendBufferSize(int size) - throws SocketException - { - try { - opts().sendBufferSize(size); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public int getSendBufferSize() throws SocketException { - try { - return opts().sendBufferSize(); - } catch (Exception x) { - Net.translateToSocketException(x); - return 0; // Never happens - } - } - - public void setReceiveBufferSize(int size) - throws SocketException - { - try { - opts().receiveBufferSize(size); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public int getReceiveBufferSize() throws SocketException { - try { - return opts().receiveBufferSize(); - } catch (Exception x) { - Net.translateToSocketException(x); - return 0; // Never happens - } - } - - public void setKeepAlive(boolean on) throws SocketException { - try { - opts().keepAlive(on); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public boolean getKeepAlive() throws SocketException { - try { - return opts().keepAlive(); - } catch (Exception x) { - Net.translateToSocketException(x); - return false; // Never happens - } - } - - public void setTrafficClass(int tc) throws SocketException { - if (tc < 0 || tc > 255) - throw new IllegalArgumentException("tc is not in range 0 -- 255"); - try { - opts().typeOfService(tc); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public int getTrafficClass() throws SocketException { - try { - return opts().typeOfService(); - } catch (Exception x) { - Net.translateToSocketException(x); - return 0; // Never happens - } - } - - public void setReuseAddress(boolean on) - throws SocketException - { - try { - opts().reuseAddress(on); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public boolean getReuseAddress() throws SocketException { - try { - return opts().reuseAddress(); - } catch (Exception x) { - Net.translateToSocketException(x); - return false; // Never happens - } - } - - public void setBroadcast(boolean on) - throws SocketException - { - try { - opts().broadcast(on); - } catch (Exception x) { - Net.translateToSocketException(x); - } - } - - public boolean getBroadcast() throws SocketException { - try { - return opts().broadcast(); - } catch (Exception x) { - Net.translateToSocketException(x); - return false; // Never happens - } - } - -} diff --git a/src/share/classes/sun/nio/ch/OptionKey.java b/src/share/classes/sun/nio/ch/OptionKey.java new file mode 100644 index 000000000..70ba8a6fa --- /dev/null +++ b/src/share/classes/sun/nio/ch/OptionKey.java @@ -0,0 +1,48 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.nio.ch; + +/** + * Represents the level/name of a socket option + */ + +class OptionKey { + private int level; + private int name; + + OptionKey(int level, int name) { + this.level = level; + this.name = name; + } + + int level() { + return level; + } + + int name() { + return name; + } +} diff --git a/src/share/classes/sun/nio/ch/SelectorProviderImpl.java b/src/share/classes/sun/nio/ch/SelectorProviderImpl.java index 367785564..7a8771361 100644 --- a/src/share/classes/sun/nio/ch/SelectorProviderImpl.java +++ b/src/share/classes/sun/nio/ch/SelectorProviderImpl.java @@ -29,6 +29,7 @@ import java.io.FileDescriptor; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; +import java.net.ProtocolFamily; import java.nio.channels.*; import java.nio.channels.spi.*; @@ -41,6 +42,10 @@ public abstract class SelectorProviderImpl return new DatagramChannelImpl(this); } + public DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException { + return new DatagramChannelImpl(this, family); + } + public Pipe openPipe() throws IOException { return new PipeImpl(this); } @@ -54,5 +59,4 @@ public abstract class SelectorProviderImpl public SocketChannel openSocketChannel() throws IOException { return new SocketChannelImpl(this); } - } diff --git a/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java b/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java index 1077ae4bd..ccf8e03d5 100644 --- a/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java +++ b/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java @@ -44,9 +44,6 @@ public class ServerSocketAdaptor // package-private // The channel being adapted private final ServerSocketChannelImpl ssc; - // Option adaptor object, created on demand - private volatile OptionAdaptor opts = null; - // Timeout "option" value for accepts private volatile int timeout = 0; @@ -174,18 +171,21 @@ public class ServerSocketAdaptor // package-private return timeout; } - private OptionAdaptor opts() { - if (opts == null) - opts = new OptionAdaptor(ssc); - return opts; - } - public void setReuseAddress(boolean on) throws SocketException { - opts().setReuseAddress(on); + try { + ssc.setOption(StandardSocketOption.SO_REUSEADDR, on); + } catch (IOException x) { + Net.translateToSocketException(x); + } } public boolean getReuseAddress() throws SocketException { - return opts().getReuseAddress(); + try { + return ssc.getOption(StandardSocketOption.SO_REUSEADDR).booleanValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return false; // Never happens + } } public String toString() { @@ -197,11 +197,23 @@ public class ServerSocketAdaptor // package-private } public void setReceiveBufferSize(int size) throws SocketException { - opts().setReceiveBufferSize(size); + // size 0 valid for ServerSocketChannel, invalid for ServerSocket + if (size <= 0) + throw new IllegalArgumentException("size cannot be 0 or negative"); + try { + ssc.setOption(StandardSocketOption.SO_RCVBUF, size); + } catch (IOException x) { + Net.translateToSocketException(x); + } } public int getReceiveBufferSize() throws SocketException { - return opts().getReceiveBufferSize(); + try { + return ssc.getOption(StandardSocketOption.SO_RCVBUF).intValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return -1; // Never happens + } } } diff --git a/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java b/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java index 7f91cf3b8..cf4e11dde 100644 --- a/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java +++ b/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java @@ -33,8 +33,7 @@ import java.nio.channels.*; import java.nio.channels.spi.*; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.HashSet; -import java.util.Iterator; +import java.util.*; /** @@ -75,10 +74,7 @@ class ServerSocketChannelImpl private int state = ST_UNINITIALIZED; // Binding - private SocketAddress localAddress = null; // null => unbound - - // Options, created on demand - private SocketOpts.IP.TCP options = null; + private SocketAddress localAddress; // null => unbound // Our socket adaptor, if any ServerSocket socket; @@ -103,7 +99,6 @@ class ServerSocketChannelImpl localAddress = Net.localAddress(fd); } - public ServerSocket socket() { synchronized (stateLock) { if (socket == null) @@ -112,6 +107,69 @@ class ServerSocketChannelImpl } } + @Override + public SocketAddress getLocalAddress() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + return null; + return localAddress; + } + } + + @Override + public ServerSocketChannel setOption(SocketOption name, Object value) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!options().contains(name)) + throw new IllegalArgumentException("invalid option name"); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + // no options that require special handling + Net.setSocketOption(fd, Net.UNSPEC, name, value); + return this; + } + } + + @Override + @SuppressWarnings("unchecked") + public T getOption(SocketOption name) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!options().contains(name)) + throw new IllegalArgumentException("invalid option name"); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + // no options that require special handling + return (T) Net.getSocketOption(fd, Net.UNSPEC, name); + } + } + + private static class LazyInitialization { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet>(2); + set.add(StandardSocketOption.SO_RCVBUF); + set.add(StandardSocketOption.SO_REUSEADDR); + return Collections.unmodifiableSet(set); + } + } + + @Override + public final Set> options() { + return LazyInitialization.defaultOptions; + } + public boolean isBound() { synchronized (stateLock) { return localAddress != null; @@ -124,22 +182,25 @@ class ServerSocketChannelImpl } } - public void bind(SocketAddress local, int backlog) throws IOException { + @Override + public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException { synchronized (lock) { if (!isOpen()) throw new ClosedChannelException(); if (isBound()) throw new AlreadyBoundException(); - InetSocketAddress isa = Net.checkAddress(local); + InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) : + Net.checkAddress(local); SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkListen(isa.getPort()); Net.bind(fd, isa.getAddress(), isa.getPort()); - listen(fd, backlog < 1 ? 50 : backlog); + Net.listen(fd, backlog < 1 ? 50 : backlog); synchronized (stateLock) { localAddress = Net.localAddress(fd); } } + return this; } public SocketChannel accept() throws IOException { @@ -196,24 +257,6 @@ class ServerSocketChannelImpl IOUtil.configureBlocking(fd, block); } - public SocketOpts options() { - synchronized (stateLock) { - if (options == null) { - SocketOptsImpl.Dispatcher d - = new SocketOptsImpl.Dispatcher() { - int getInt(int opt) throws IOException { - return Net.getIntOption(fd, opt); - } - void setInt(int opt, int arg) throws IOException { - Net.setIntOption(fd, opt, arg); - } - }; - options = new SocketOptsImpl.IP.TCP(d); - } - return options; - } - } - protected void implCloseSelectableChannel() throws IOException { synchronized (stateLock) { nd.preClose(fd); @@ -320,9 +363,6 @@ class ServerSocketChannelImpl // -- Native methods -- - private static native void listen(FileDescriptor fd, int backlog) - throws IOException; - // Accepts a new connection, setting the given file descriptor to refer to // the new socket and setting isaa[0] to the socket's remote address. // Returns 1 on success, or IOStatus.UNAVAILABLE (if non-blocking and no diff --git a/src/share/classes/sun/nio/ch/SocketAdaptor.java b/src/share/classes/sun/nio/ch/SocketAdaptor.java index dbf796203..8d74663bf 100644 --- a/src/share/classes/sun/nio/ch/SocketAdaptor.java +++ b/src/share/classes/sun/nio/ch/SocketAdaptor.java @@ -54,16 +54,9 @@ public class SocketAdaptor // The channel being adapted private final SocketChannelImpl sc; - // Option adaptor object, created on demand - private volatile OptionAdaptor opts = null; - // Timeout "option" value for reads private volatile int timeout = 0; - // Traffic-class/Type-of-service - private volatile int trafficClass = 0; - - // ## super will create a useless impl private SocketAdaptor(SocketChannelImpl sc) { this.sc = sc; @@ -145,8 +138,6 @@ public class SocketAdaptor public void bind(SocketAddress local) throws IOException { try { - if (local == null) - local = new InetSocketAddress(0); sc.bind(local); } catch (Exception x) { Net.translateException(x); @@ -154,27 +145,39 @@ public class SocketAdaptor } public InetAddress getInetAddress() { - if (!sc.isConnected()) + SocketAddress remote = sc.remoteAddress(); + if (remote == null) { return null; - return Net.asInetSocketAddress(sc.remoteAddress()).getAddress(); + } else { + return ((InetSocketAddress)remote).getAddress(); + } } public InetAddress getLocalAddress() { - if (!sc.isBound()) - return new InetSocketAddress(0).getAddress(); - return Net.asInetSocketAddress(sc.localAddress()).getAddress(); + if (sc.isOpen()) { + SocketAddress local = sc.localAddress(); + if (local != null) + return ((InetSocketAddress)local).getAddress(); + } + return new InetSocketAddress(0).getAddress(); } public int getPort() { - if (!sc.isConnected()) + SocketAddress remote = sc.remoteAddress(); + if (remote == null) { return 0; - return Net.asInetSocketAddress(sc.remoteAddress()).getPort(); + } else { + return ((InetSocketAddress)remote).getPort(); + } } public int getLocalPort() { - if (!sc.isBound()) + SocketAddress local = sc.localAddress(); + if (local == null) { return -1; - return Net.asInetSocketAddress(sc.localAddress()).getPort(); + } else { + return ((InetSocketAddress)local).getPort(); + } } private class SocketInputStream @@ -276,26 +279,60 @@ public class SocketAdaptor return os; } - private OptionAdaptor opts() { - if (opts == null) - opts = new OptionAdaptor(sc); - return opts; + private void setBooleanOption(SocketOption name, boolean value) + throws SocketException + { + try { + sc.setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private void setIntOption(SocketOption name, int value) + throws SocketException + { + try { + sc.setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private boolean getBooleanOption(SocketOption name) throws SocketException { + try { + return sc.getOption(name).booleanValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return false; // keep compiler happy + } + } + + private int getIntOption(SocketOption name) throws SocketException { + try { + return sc.getOption(name).intValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return -1; // keep compiler happy + } } public void setTcpNoDelay(boolean on) throws SocketException { - opts().setTcpNoDelay(on); + setBooleanOption(StandardSocketOption.TCP_NODELAY, on); } public boolean getTcpNoDelay() throws SocketException { - return opts().getTcpNoDelay(); + return getBooleanOption(StandardSocketOption.TCP_NODELAY); } public void setSoLinger(boolean on, int linger) throws SocketException { - opts().setSoLinger(on, linger); + if (!on) + linger = -1; + setIntOption(StandardSocketOption.SO_LINGER, linger); } public int getSoLinger() throws SocketException { - return opts().getSoLinger(); + return getIntOption(StandardSocketOption.SO_LINGER); } public void sendUrgentData(int data) throws IOException { @@ -303,11 +340,11 @@ public class SocketAdaptor } public void setOOBInline(boolean on) throws SocketException { - opts().setOOBInline(on); + setBooleanOption(ExtendedSocketOption.SO_OOBINLINE, on); } public boolean getOOBInline() throws SocketException { - return opts().getOOBInline(); + return getBooleanOption(ExtendedSocketOption.SO_OOBINLINE); } public void setSoTimeout(int timeout) throws SocketException { @@ -321,48 +358,49 @@ public class SocketAdaptor } public void setSendBufferSize(int size) throws SocketException { - opts().setSendBufferSize(size); + // size 0 valid for SocketChannel, invalid for Socket + if (size <= 0) + throw new IllegalArgumentException("Invalid send size"); + setIntOption(StandardSocketOption.SO_SNDBUF, size); } public int getSendBufferSize() throws SocketException { - return opts().getSendBufferSize(); + return getIntOption(StandardSocketOption.SO_SNDBUF); } public void setReceiveBufferSize(int size) throws SocketException { - opts().setReceiveBufferSize(size); + // size 0 valid for SocketChannel, invalid for Socket + if (size <= 0) + throw new IllegalArgumentException("Invalid receive size"); + setIntOption(StandardSocketOption.SO_RCVBUF, size); } public int getReceiveBufferSize() throws SocketException { - return opts().getReceiveBufferSize(); + return getIntOption(StandardSocketOption.SO_RCVBUF); } public void setKeepAlive(boolean on) throws SocketException { - opts().setKeepAlive(on); + setBooleanOption(StandardSocketOption.SO_KEEPALIVE, on); } public boolean getKeepAlive() throws SocketException { - return opts().getKeepAlive(); + return getBooleanOption(StandardSocketOption.SO_KEEPALIVE); } public void setTrafficClass(int tc) throws SocketException { - opts().setTrafficClass(tc); - trafficClass = tc; + setIntOption(StandardSocketOption.IP_TOS, tc); } public int getTrafficClass() throws SocketException { - int tc = opts().getTrafficClass(); - if (tc < 0) { - tc = trafficClass; - } - return tc; + return getIntOption(StandardSocketOption.IP_TOS); } public void setReuseAddress(boolean on) throws SocketException { - opts().setReuseAddress(on); + setBooleanOption(StandardSocketOption.SO_REUSEADDR, on); } public boolean getReuseAddress() throws SocketException { - return opts().getReuseAddress(); + return getBooleanOption(StandardSocketOption.SO_REUSEADDR); } public void close() throws IOException { @@ -402,7 +440,7 @@ public class SocketAdaptor } public boolean isBound() { - return sc.isBound(); + return sc.localAddress() != null; } public boolean isClosed() { diff --git a/src/share/classes/sun/nio/ch/SocketChannelImpl.java b/src/share/classes/sun/nio/ch/SocketChannelImpl.java index 2c67270aa..4549eacfd 100644 --- a/src/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/src/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -31,6 +31,7 @@ import java.net.*; import java.nio.ByteBuffer; import java.nio.channels.*; import java.nio.channels.spi.*; +import java.util.*; /** @@ -78,19 +79,16 @@ class SocketChannelImpl private int state = ST_UNINITIALIZED; // Binding - private SocketAddress localAddress = null; - private SocketAddress remoteAddress = null; + private SocketAddress localAddress; + private SocketAddress remoteAddress; // Input/Output open private boolean isInputOpen = true; private boolean isOutputOpen = true; private boolean readyToConnect = false; - // Options, created on demand - private SocketOpts.IP.TCP options = null; - // Socket adaptor, created on demand - private Socket socket = null; + private Socket socket; // -- End of fields protected by stateLock @@ -114,6 +112,7 @@ class SocketChannelImpl this.fd = fd; this.fdVal = IOUtil.fdVal(fd); this.state = ST_CONNECTED; + this.localAddress = Net.localAddress(fd); this.remoteAddress = remote; } @@ -125,6 +124,98 @@ class SocketChannelImpl } } + @Override + public SocketAddress getLocalAddress() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + return null; + return localAddress; + } + } + + @Override + public SocketAddress getConnectedAddress() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + return null; + return remoteAddress; + } + } + + @Override + public SocketChannel setOption(SocketOption name, Object value) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!options().contains(name)) + throw new IllegalArgumentException("Invalid option name"); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + // special handling for IP_TOS: no-op when IPv6 + if (name == StandardSocketOption.IP_TOS) { + if (!Net.isIPv6Available()) + Net.setSocketOption(fd, StandardProtocolFamily.INET, name, value); + return this; + } + + // no options that require special handling + Net.setSocketOption(fd, Net.UNSPEC, name, value); + return this; + } + } + + @Override + @SuppressWarnings("unchecked") + public T getOption(SocketOption name) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!options().contains(name)) + throw new IllegalArgumentException("Invalid option name"); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + // special handling for IP_TOS: always return 0 when IPv6 + if (name == StandardSocketOption.IP_TOS) { + return (Net.isIPv6Available()) ? (T) Integer.valueOf(0) : + (T) Net.getSocketOption(fd, StandardProtocolFamily.INET, name); + } + + // no options that require special handling + return (T) Net.getSocketOption(fd, Net.UNSPEC, name); + } + } + + private static class LazyInitialization { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet>(8); + set.add(StandardSocketOption.SO_SNDBUF); + set.add(StandardSocketOption.SO_RCVBUF); + set.add(StandardSocketOption.SO_KEEPALIVE); + set.add(StandardSocketOption.SO_REUSEADDR); + set.add(StandardSocketOption.SO_LINGER); + set.add(StandardSocketOption.TCP_NODELAY); + // additional options required by socket adaptor + set.add(StandardSocketOption.IP_TOS); + set.add(ExtendedSocketOption.SO_OOBINLINE); + return Collections.unmodifiableSet(set); + } + } + + @Override + public final Set> options() { + return LazyInitialization.defaultOptions; + } + private boolean ensureReadOpen() throws ClosedChannelException { synchronized (stateLock) { if (!isOpen()) @@ -410,43 +501,8 @@ class SocketChannelImpl IOUtil.configureBlocking(fd, block); } - public SocketOpts options() { - synchronized (stateLock) { - if (options == null) { - SocketOptsImpl.Dispatcher d - = new SocketOptsImpl.Dispatcher() { - int getInt(int opt) throws IOException { - return Net.getIntOption(fd, opt); - } - void setInt(int opt, int arg) - throws IOException - { - Net.setIntOption(fd, opt, arg); - } - }; - options = new SocketOptsImpl.IP.TCP(d); - } - return options; - } - } - - public boolean isBound() { - synchronized (stateLock) { - if (state == ST_CONNECTED) - return true; - return localAddress != null; - } - } - public SocketAddress localAddress() { synchronized (stateLock) { - if (state == ST_CONNECTED && - (localAddress == null || - ((InetSocketAddress)localAddress).getAddress().isAnyLocalAddress())) { - // Socket was not bound before connecting or - // Socket was bound with an "anyLocalAddress" - localAddress = Net.localAddress(fd); - } return localAddress; } } @@ -457,19 +513,25 @@ class SocketChannelImpl } } - public void bind(SocketAddress local) throws IOException { + @Override + public SocketChannel bind(SocketAddress local) throws IOException { synchronized (readLock) { synchronized (writeLock) { synchronized (stateLock) { - ensureOpenAndUnconnected(); + if (!isOpen()) + throw new ClosedChannelException(); + if (state == ST_PENDING) + throw new ConnectionPendingException(); if (localAddress != null) throw new AlreadyBoundException(); - InetSocketAddress isa = Net.checkAddress(local); + InetSocketAddress isa = (local == null) ? + new InetSocketAddress(0) : Net.checkAddress(local); Net.bind(fd, isa.getAddress(), isa.getPort()); localAddress = Net.localAddress(fd); } } } + return this; } public boolean isConnected() { @@ -496,7 +558,6 @@ class SocketChannelImpl } public boolean connect(SocketAddress sa) throws IOException { - int trafficClass = 0; // ## Pick up from options int localPort = 0; synchronized (readLock) { @@ -524,13 +585,24 @@ class SocketChannelImpl ia = InetAddress.getLocalHost(); n = Net.connect(fd, ia, - isa.getPort(), - trafficClass); + isa.getPort()); if ( (n == IOStatus.INTERRUPTED) && isOpen()) continue; break; } + + synchronized (stateLock) { + if (isOpen() && (localAddress == null) || + ((InetSocketAddress)localAddress) + .getAddress().isAnyLocalAddress()) + { + // Socket was not bound before connecting or + // Socket was bound with an "anyLocalAddress" + localAddress = Net.localAddress(fd); + } + } + } finally { readerCleanup(); end((n > 0) || (n == IOStatus.UNAVAILABLE)); @@ -646,29 +718,37 @@ class SocketChannelImpl } } - public final static int SHUT_RD = 0; - public final static int SHUT_WR = 1; - public final static int SHUT_RDWR = 2; - - public void shutdownInput() throws IOException { + @Override + public SocketChannel shutdownInput() throws IOException { synchronized (stateLock) { if (!isOpen()) throw new ClosedChannelException(); - isInputOpen = false; - shutdown(fd, SHUT_RD); - if (readerThread != 0) - NativeThread.signal(readerThread); + if (!isConnected()) + throw new NotYetConnectedException(); + if (isInputOpen) { + Net.shutdown(fd, Net.SHUT_RD); + if (readerThread != 0) + NativeThread.signal(readerThread); + isInputOpen = false; + } + return this; } } - public void shutdownOutput() throws IOException { + @Override + public SocketChannel shutdownOutput() throws IOException { synchronized (stateLock) { if (!isOpen()) throw new ClosedChannelException(); - isOutputOpen = false; - shutdown(fd, SHUT_WR); - if (writerThread != 0) - NativeThread.signal(writerThread); + if (!isConnected()) + throw new NotYetConnectedException(); + if (isOutputOpen) { + Net.shutdown(fd, Net.SHUT_WR); + if (writerThread != 0) + NativeThread.signal(writerThread); + isOutputOpen = false; + } + return this; } } @@ -869,9 +949,6 @@ class SocketChannelImpl boolean block, boolean ready) throws IOException; - private static native void shutdown(FileDescriptor fd, int how) - throws IOException; - static { Util.load(); nd = new SocketDispatcher(); diff --git a/src/share/classes/sun/nio/ch/SocketOpts.java b/src/share/classes/sun/nio/ch/SocketOpts.java deleted file mode 100644 index 52ef58d8d..000000000 --- a/src/share/classes/sun/nio/ch/SocketOpts.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -package sun.nio.ch; - -import java.io.IOException; -import java.nio.*; -import java.net.NetworkInterface; - - -// Typical use: -// -// sc.options() -// .noDelay(true) -// .typeOfService(SocketOpts.IP.TOS_RELIABILITY) -// .sendBufferSize(1024) -// .receiveBufferSize(1024) -// .keepAlive(true); -// - - -public interface SocketOpts { // SocketOptions already used in java.net - - // Options that apply to all kinds of sockets - - // SO_BROADCAST - public abstract boolean broadcast() throws IOException; - public abstract SocketOpts broadcast(boolean b) throws IOException; - - // SO_KEEPALIVE - public abstract boolean keepAlive() throws IOException; - public abstract SocketOpts keepAlive(boolean b) throws IOException; - - // SO_LINGER - public abstract int linger() throws IOException; - public abstract SocketOpts linger(int n) throws IOException; - - // SO_OOBINLINE - public abstract boolean outOfBandInline() throws IOException; - public abstract SocketOpts outOfBandInline(boolean b) throws IOException; - - // SO_RCVBUF - public abstract int receiveBufferSize() throws IOException; - public abstract SocketOpts receiveBufferSize(int n) throws IOException; - - // SO_SNDBUF - public abstract int sendBufferSize() throws IOException; - public abstract SocketOpts sendBufferSize(int n) throws IOException; - - // SO_REUSEADDR - public abstract boolean reuseAddress() throws IOException; - public abstract SocketOpts reuseAddress(boolean b) throws IOException; - - - // IP-specific options - - public static interface IP - extends SocketOpts - { - - // IP_MULTICAST_IF2 - public abstract NetworkInterface multicastInterface() - throws IOException; - public abstract IP multicastInterface(NetworkInterface ni) - throws IOException; - - // IP_MULTICAST_LOOP - public abstract boolean multicastLoop() throws IOException; - public abstract IP multicastLoop(boolean b) throws IOException; - - // IP_TOS - public static final int TOS_LOWDELAY = 0x10; - public static final int TOS_THROUGHPUT = 0x08; - public static final int TOS_RELIABILITY = 0x04; - public static final int TOS_MINCOST = 0x02; - public abstract int typeOfService() throws IOException; - public abstract IP typeOfService(int tos) throws IOException; - - - // TCP-specific options - - public static interface TCP - extends IP - { - // TCP_NODELAY - public abstract boolean noDelay() throws IOException; - public abstract TCP noDelay(boolean b) throws IOException; - - } - - } - -} diff --git a/src/share/classes/sun/nio/ch/SocketOptsImpl.java b/src/share/classes/sun/nio/ch/SocketOptsImpl.java deleted file mode 100644 index 183747de1..000000000 --- a/src/share/classes/sun/nio/ch/SocketOptsImpl.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - */ - -package sun.nio.ch; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.NetworkInterface; -import java.net.SocketOptions; -import java.nio.channels.*; - - -class SocketOptsImpl - implements SocketOpts -{ - - static abstract class Dispatcher { - abstract int getInt(int opt) throws IOException; - abstract void setInt(int opt, int arg) throws IOException; - // Others that pass addresses, etc., will come later - } - - private final Dispatcher d; - - SocketOptsImpl(Dispatcher d) { - this.d = d; - } - - protected boolean getBoolean(int opt) throws IOException { - return d.getInt(opt) > 0; - } - - protected void setBoolean(int opt, boolean b) throws IOException { - d.setInt(opt, b ? 1 : 0); - } - - protected int getInt(int opt) throws IOException { - return d.getInt(opt); - } - - protected void setInt(int opt, int n) throws IOException { - d.setInt(opt, n); - } - - protected NetworkInterface getNetworkInterface(int opt) - throws IOException - { - throw new UnsupportedOperationException("NYI"); - } - - protected void setNetworkInterface(int opt, NetworkInterface ni) - throws IOException - { - throw new UnsupportedOperationException("NYI"); - } - - protected void addToString(StringBuffer sb, String s) { - char c = sb.charAt(sb.length() - 1); - if ((c != '[') && (c != '=')) - sb.append(' '); - sb.append(s); - } - - protected void addToString(StringBuffer sb, int n) { - addToString(sb, Integer.toString(n)); - } - - - // SO_BROADCAST - - public boolean broadcast() throws IOException { - return getBoolean(SocketOptions.SO_BROADCAST); - } - - public SocketOpts broadcast(boolean b) throws IOException { - setBoolean(SocketOptions.SO_BROADCAST, b); - return this; - } - - - // SO_KEEPALIVE - - public boolean keepAlive() throws IOException { - return getBoolean(SocketOptions.SO_KEEPALIVE); - } - - public SocketOpts keepAlive(boolean b) throws IOException { - setBoolean(SocketOptions.SO_KEEPALIVE, b); - return this; - } - - - // SO_LINGER - - public int linger() throws IOException { - return getInt(SocketOptions.SO_LINGER); - } - - public SocketOpts linger(int n) throws IOException { - setInt(SocketOptions.SO_LINGER, n); - return this; - } - - - // SO_OOBINLINE - - public boolean outOfBandInline() throws IOException { - return getBoolean(SocketOptions.SO_OOBINLINE); - } - - public SocketOpts outOfBandInline(boolean b) throws IOException { - setBoolean(SocketOptions.SO_OOBINLINE, b); - return this; - } - - - // SO_RCVBUF - - public int receiveBufferSize() throws IOException { - return getInt(SocketOptions.SO_RCVBUF); - } - - public SocketOpts receiveBufferSize(int n) throws IOException { - if (n <= 0) - throw new IllegalArgumentException("Invalid receive size"); - setInt(SocketOptions.SO_RCVBUF, n); - return this; - } - - - // SO_SNDBUF - - public int sendBufferSize() throws IOException { - return getInt(SocketOptions.SO_SNDBUF); - } - - public SocketOpts sendBufferSize(int n) throws IOException { - if (n <= 0) - throw new IllegalArgumentException("Invalid send size"); - setInt(SocketOptions.SO_SNDBUF, n); - return this; - } - - - // SO_REUSEADDR - - public boolean reuseAddress() throws IOException { - return getBoolean(SocketOptions.SO_REUSEADDR); - } - - public SocketOpts reuseAddress(boolean b) throws IOException { - setBoolean(SocketOptions.SO_REUSEADDR, b); - return this; - } - - - // toString - - protected void toString(StringBuffer sb) throws IOException { - int n; - if (broadcast()) - addToString(sb, "broadcast"); - if (keepAlive()) - addToString(sb, "keepalive"); - if ((n = linger()) > 0) { - addToString(sb, "linger="); - addToString(sb, n); - } - if (outOfBandInline()) - addToString(sb, "oobinline"); - if ((n = receiveBufferSize()) > 0) { - addToString(sb, "rcvbuf="); - addToString(sb, n); - } - if ((n = sendBufferSize()) > 0) { - addToString(sb, "sndbuf="); - addToString(sb, n); - } - if (reuseAddress()) - addToString(sb, "reuseaddr"); - } - - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append(this.getClass().getInterfaces()[0].getName()); - sb.append('['); - int i = sb.length(); - try { - toString(sb); - } catch (IOException x) { - sb.setLength(i); - sb.append("closed"); - } - sb.append(']'); - return sb.toString(); - } - - - // IP-specific socket options - - static class IP - extends SocketOptsImpl - implements SocketOpts.IP - { - - IP(Dispatcher d) { - super(d); - } - - - // IP_MULTICAST_IF2 - // ## Do we need IP_MULTICAST_IF also? - - public NetworkInterface multicastInterface() throws IOException { - return getNetworkInterface(SocketOptions.IP_MULTICAST_IF2); - } - - public SocketOpts.IP multicastInterface(NetworkInterface ni) - throws IOException - { - setNetworkInterface(SocketOptions.IP_MULTICAST_IF2, ni); - return this; - } - - - // IP_MULTICAST_LOOP - - public boolean multicastLoop() throws IOException { - return getBoolean(SocketOptions.IP_MULTICAST_LOOP); - } - - public SocketOpts.IP multicastLoop(boolean b) throws IOException { - setBoolean(SocketOptions.IP_MULTICAST_LOOP, b); - return this; - } - - - // IP_TOS - - public int typeOfService() throws IOException { - return getInt(SocketOptions.IP_TOS); - } - - public SocketOpts.IP typeOfService(int tos) throws IOException { - setInt(SocketOptions.IP_TOS, tos); - return this; - } - - - // toString - - protected void toString(StringBuffer sb) throws IOException { - super.toString(sb); - int n; - if ((n = typeOfService()) > 0) { - addToString(sb, "tos="); - addToString(sb, n); - } - } - - - // TCP-specific IP options - - public static class TCP - extends SocketOptsImpl.IP - implements SocketOpts.IP.TCP - { - - TCP(Dispatcher d) { - super(d); - } - - // TCP_NODELAY - - public boolean noDelay() throws IOException { - return getBoolean(SocketOptions.TCP_NODELAY); - } - - public SocketOpts.IP.TCP noDelay(boolean b) throws IOException { - setBoolean(SocketOptions.TCP_NODELAY, b); - return this; - } - - - // toString - - protected void toString(StringBuffer sb) throws IOException { - super.toString(sb); - if (noDelay()) - addToString(sb, "nodelay"); - } - - } - } - -} diff --git a/src/share/native/java/net/net_util.c b/src/share/native/java/net/net_util.c index 2559a0edb..5634e736a 100644 --- a/src/share/native/java/net/net_util.c +++ b/src/share/native/java/net/net_util.c @@ -82,7 +82,7 @@ void init(JNIEnv *env) { } } -jobject +JNIEXPORT jobject JNICALL NET_SockaddrToInetAddress(JNIEnv *env, struct sockaddr *him, int *port) { jobject iaObj; init(env); @@ -159,7 +159,7 @@ NET_SockaddrToInetAddress(JNIEnv *env, struct sockaddr *him, int *port) { return iaObj; } -jint +JNIEXPORT jint JNICALL NET_SockaddrEqualsInetAddress(JNIEnv *env, struct sockaddr *him, jobject iaObj) { jint family = (*env)->GetIntField(env, iaObj, ia_familyID) == IPv4? diff --git a/src/share/native/java/net/net_util.h b/src/share/native/java/net/net_util.h index d00a1c0be..9fded2f9c 100644 --- a/src/share/native/java/net/net_util.h +++ b/src/share/native/java/net/net_util.h @@ -116,7 +116,7 @@ NET_AllocSockaddr(struct sockaddr **him, int *len); JNIEXPORT int JNICALL NET_InetAddressToSockaddr(JNIEnv *env, jobject iaObj, int port, struct sockaddr *him, int *len, jboolean v4MappedAddress); -jobject +JNIEXPORT jobject JNICALL NET_SockaddrToInetAddress(JNIEnv *env, struct sockaddr *him, int *port); void initLocalAddrTable (); @@ -124,10 +124,10 @@ void initLocalAddrTable (); void NET_SetTrafficClass(struct sockaddr *him, int trafficClass); -jint +JNIEXPORT jint JNICALL NET_GetPortFromSockaddr(struct sockaddr *him); -jint +JNIEXPORT jint JNICALL NET_SockaddrEqualsInetAddress(JNIEnv *env,struct sockaddr *him, jobject iaObj); int diff --git a/src/share/native/sun/nio/ch/genSocketOptionRegistry.c b/src/share/native/sun/nio/ch/genSocketOptionRegistry.c new file mode 100644 index 000000000..85088ace8 --- /dev/null +++ b/src/share/native/sun/nio/ch/genSocketOptionRegistry.c @@ -0,0 +1,129 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +#include +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#endif + +/** + * Generates sun.nio.ch.SocketOptionRegistry, a class that maps Java-level + * socket options to the platform specific level and option. + */ + +static void out(char* s) { + printf("%s\n", s); +} + +static void emit(const char *name, char * family, int level, int optname) { + printf(" map.put(new RegistryKey(%s, %s),", name, family); + printf(" new OptionKey(%d, %d));\n", level, optname); +} + +static void emit_unspec(const char *name, int level, int optname) { + emit(name, "Net.UNSPEC", level, optname); +} + +static void emit_inet(const char *name, int level, int optname) { + emit(name, "StandardProtocolFamily.INET", level, optname); +} + +static void emit_inet6(const char *name, int level, int optname) { + emit(name, "StandardProtocolFamily.INET6", level, optname); +} + +int main(int argc, const char* argv[]) { + out("// AUTOMATICALLY GENERATED FILE - DO NOT EDIT "); + out("package sun.nio.ch; "); + out("import java.net.SocketOption; "); + out("import java.net.StandardSocketOption; "); + out("import java.net.ProtocolFamily; "); + out("import java.net.StandardProtocolFamily; "); + out("import java.util.Map; "); + out("import java.util.HashMap; "); + out("class SocketOptionRegistry { "); + out(" private SocketOptionRegistry() { } "); + out(" private static class RegistryKey { "); + out(" private final SocketOption name; "); + out(" private final ProtocolFamily family; "); + out(" RegistryKey(SocketOption name, ProtocolFamily family) { "); + out(" this.name = name; "); + out(" this.family = family; "); + out(" } "); + out(" public int hashCode() { "); + out(" return name.hashCode() + family.hashCode(); "); + out(" } "); + out(" public boolean equals(Object ob) { "); + out(" if (ob == null) return false; "); + out(" if (!(ob instanceof RegistryKey)) return false; "); + out(" RegistryKey other = (RegistryKey)ob; "); + out(" if (this.name != other.name) return false; "); + out(" if (this.family != other.family) return false; "); + out(" return true; "); + out(" } "); + out(" } "); + out(" private static class LazyInitialization { "); + out(" static final Map options = options(); "); + out(" private static Map options() { "); + out(" Map map = "); + out(" new HashMap(); "); + + emit_unspec("StandardSocketOption.SO_BROADCAST", SOL_SOCKET, SO_BROADCAST); + emit_unspec("StandardSocketOption.SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE); + emit_unspec("StandardSocketOption.SO_LINGER", SOL_SOCKET, SO_LINGER); + emit_unspec("StandardSocketOption.SO_SNDBUF", SOL_SOCKET, SO_SNDBUF); + emit_unspec("StandardSocketOption.SO_RCVBUF", SOL_SOCKET, SO_RCVBUF); + emit_unspec("StandardSocketOption.SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR); + emit_unspec("StandardSocketOption.TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY); + + emit_inet("StandardSocketOption.IP_TOS", IPPROTO_IP, IP_TOS); + emit_inet("StandardSocketOption.IP_MULTICAST_IF", IPPROTO_IP, IP_MULTICAST_IF); + emit_inet("StandardSocketOption.IP_MULTICAST_TTL", IPPROTO_IP, IP_MULTICAST_TTL); + emit_inet("StandardSocketOption.IP_MULTICAST_LOOP", IPPROTO_IP, IP_MULTICAST_LOOP); + +#ifdef AF_INET6 + emit_inet6("StandardSocketOption.IP_MULTICAST_IF", IPPROTO_IPV6, IPV6_MULTICAST_IF); + emit_inet6("StandardSocketOption.IP_MULTICAST_TTL", IPPROTO_IPV6, IPV6_MULTICAST_HOPS); + emit_inet6("StandardSocketOption.IP_MULTICAST_LOOP", IPPROTO_IPV6, IPV6_MULTICAST_LOOP); +#endif + + emit_unspec("ExtendedSocketOption.SO_OOBINLINE", SOL_SOCKET, SO_OOBINLINE); + + out(" return map; "); + out(" } "); + out(" } "); + out(" public static OptionKey findOption(SocketOption name, ProtocolFamily family) { "); + out(" RegistryKey key = new RegistryKey(name, family); "); + out(" return LazyInitialization.options.get(key); "); + out(" } "); + out("} "); + + return 0; +} diff --git a/src/share/sample/nio/multicast/MulticastAddress.java b/src/share/sample/nio/multicast/MulticastAddress.java new file mode 100644 index 000000000..e1a3bf3eb --- /dev/null +++ b/src/share/sample/nio/multicast/MulticastAddress.java @@ -0,0 +1,127 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Sun Microsystems nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.UnknownHostException; +import java.net.SocketException; + +/** + * Parses and represents a multicast address. + */ + +class MulticastAddress { + private final InetAddress group; + private final int port; + private final NetworkInterface interf; + + private MulticastAddress(InetAddress group, int port, NetworkInterface interf) { + this.group = group; + this.port = port; + this.interf = interf; + } + + InetAddress group() { + return group; + } + + int port() { + return port; + } + + /** + * @return The network interface, may be {@code null} + */ + NetworkInterface interf() { + return interf; + } + + /** + * Parses a string of the form "group:port[@interface]", returning + * a MulticastAddress representing the address + */ + static MulticastAddress parse(String s) { + String[] components = s.split("@"); + if (components.length > 2) + throw new IllegalArgumentException("At most one '@' expected"); + + // get group and port + String target = components[0]; + int len = components[0].length(); + int colon = components[0].lastIndexOf(':'); + if ((colon < 1) || (colon > (len-2))) + throw new IllegalArgumentException("group:port expected"); + String groupString = target.substring(0, colon); + int port = -1; + try { + port = Integer.parseInt(target.substring(colon+1, len)); + } catch (NumberFormatException x) { + throw new IllegalArgumentException(x); + } + + // handle IPv6 literal address + if (groupString.charAt(0) == '[') { + len = groupString.length(); + if (groupString.charAt(len-1) != ']') + throw new IllegalArgumentException("missing ']'"); + groupString = groupString.substring(1,len-1); + if (groupString.length() == 0) + throw new IllegalArgumentException("missing IPv6 address"); + } + + // get group address + InetAddress group = null; + try { + group = InetAddress.getByName(groupString); + } catch (UnknownHostException x) { + throw new IllegalArgumentException(x); + } + if (!group.isMulticastAddress()) { + throw new IllegalArgumentException("'" + group.getHostAddress() + + "' is not multicast address"); + } + + // optional interface + NetworkInterface interf = null; + if (components.length == 2) { + try { + interf = NetworkInterface.getByName(components[1]); + } catch (SocketException x) { + throw new IllegalArgumentException(x); + } + if (interf == null) { + throw new IllegalArgumentException("'" + components[1] + + "' is not valid interface"); + } + } + return new MulticastAddress(group, port, interf); + } +} diff --git a/src/share/sample/nio/multicast/Reader.java b/src/share/sample/nio/multicast/Reader.java new file mode 100644 index 000000000..0d1bd8672 --- /dev/null +++ b/src/share/sample/nio/multicast/Reader.java @@ -0,0 +1,142 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Sun Microsystems nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import java.nio.channels.*; +import java.nio.charset.*; +import java.nio.ByteBuffer; +import java.net.*; +import java.io.IOException; +import java.util.*; + +public class Reader { + + static void usage() { + System.err.println("usage: java Reader group:port@interf [-only source...] [-block source...]"); + System.exit(-1); + } + + static void printDatagram(SocketAddress sa, ByteBuffer buf) { + System.out.format("-- datagram from %s --\n", + ((InetSocketAddress)sa).getAddress().getHostAddress()); + System.out.println(Charset.defaultCharset().decode(buf)); + } + + static void parseAddessList(String s, List list) + throws UnknownHostException + { + String[] sources = s.split(","); + for (int i=0; i includeList = new ArrayList(); + List excludeList = new ArrayList(); + int argc = 1; + while (argc < args.length) { + String option = args[argc++]; + if (argc >= args.length) + usage(); + String value = args[argc++]; + if (option.equals("-only")) { + parseAddessList(value, includeList); + continue; + } + if (option.equals("-block")) { + parseAddessList(value, excludeList); + continue; + } + usage(); + } + if (!includeList.isEmpty() && !excludeList.isEmpty()) { + usage(); + } + + // create and bind socket + ProtocolFamily family = StandardProtocolFamily.INET; + if (target.group() instanceof Inet6Address) { + family = StandardProtocolFamily.INET6; + } + DatagramChannel dc = DatagramChannel.open(family) + .setOption(StandardSocketOption.SO_REUSEADDR, true) + .bind(new InetSocketAddress(target.port())); + + if (includeList.isEmpty()) { + // join group and block addresses on the exclude list + MembershipKey key = dc.join(target.group(), target.interf()); + for (InetAddress source: excludeList) { + key.block(source); + } + } else { + // join with source-specific membership for each source + for (InetAddress source: includeList) { + dc.join(target.group(), target.interf(), source); + } + } + + // register socket with Selector + Selector sel = Selector.open(); + dc.configureBlocking(false); + dc.register(sel, SelectionKey.OP_READ); + + // print out each datagram that we receive + ByteBuffer buf = ByteBuffer.allocateDirect(4096); + for (;;) { + int updated = sel.select(); + if (updated > 0) { + Iterator iter = sel.selectedKeys().iterator(); + while (iter.hasNext()) { + SelectionKey sk = iter.next(); + iter.remove(); + + DatagramChannel ch = (DatagramChannel)sk.channel(); + SocketAddress sa = ch.receive(buf); + if (sa != null) { + buf.flip(); + printDatagram(sa, buf); + buf.rewind(); + buf.limit(buf.capacity()); + } + } + } + } + } +} diff --git a/src/share/sample/nio/multicast/Sender.java b/src/share/sample/nio/multicast/Sender.java new file mode 100644 index 000000000..d72a24d5a --- /dev/null +++ b/src/share/sample/nio/multicast/Sender.java @@ -0,0 +1,71 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Sun Microsystems nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import java.nio.channels.*; +import java.nio.charset.Charset; +import java.net.*; +import java.io.IOException; +import java.util.*; + +/** + * Sample multicast sender to send a message in a multicast datagram + * to a given group. + */ + +public class Sender { + + private static void usage() { + System.err.println("usage: java Sender group:port[@interface] message"); + System.exit(-1); + } + + public static void main(String[] args) throws IOException { + if (args.length < 2) + usage(); + + MulticastAddress target = MulticastAddress.parse(args[0]); + + // create socket + ProtocolFamily family = StandardProtocolFamily.INET; + if (target.group() instanceof Inet6Address) + family = StandardProtocolFamily.INET6; + DatagramChannel dc = DatagramChannel.open(family).bind(new InetSocketAddress(0)); + if (target.interf() != null) { + dc.setOption(StandardSocketOption.IP_MULTICAST_IF, target.interf()); + } + + // send multicast packet + dc.send(Charset.defaultCharset().encode(args[1]), + new InetSocketAddress(target.group(), target.port())); + dc.close(); + } + +} diff --git a/src/solaris/native/java/net/net_util_md.c b/src/solaris/native/java/net/net_util_md.c index 1ff6c48db..460ce1049 100644 --- a/src/solaris/native/java/net/net_util_md.c +++ b/src/solaris/native/java/net/net_util_md.c @@ -791,7 +791,7 @@ NET_SetTrafficClass(struct sockaddr *him, int trafficClass) { #endif /* AF_INET6 */ } -jint +JNIEXPORT jint JNICALL NET_GetPortFromSockaddr(struct sockaddr *him) { #ifdef AF_INET6 if (him->sa_family == AF_INET6) { diff --git a/src/solaris/native/sun/nio/ch/DatagramChannelImpl.c b/src/solaris/native/sun/nio/ch/DatagramChannelImpl.c index b1f9f680b..bbbccd725 100644 --- a/src/solaris/native/sun/nio/ch/DatagramChannelImpl.c +++ b/src/solaris/native/sun/nio/ch/DatagramChannelImpl.c @@ -198,7 +198,7 @@ Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv *env, jobject this, JNIEXPORT jint JNICALL Java_sun_nio_ch_DatagramChannelImpl_send0(JNIEnv *env, jobject this, - jobject fdo, jlong address, + jboolean preferIPv6, jobject fdo, jlong address, jint len, jobject dest) { jint fd = fdval(env, fdo); @@ -215,7 +215,7 @@ Java_sun_nio_ch_DatagramChannelImpl_send0(JNIEnv *env, jobject this, if (NET_InetAddressToSockaddr(env, destAddress, destPort, (struct sockaddr *)&sa, - &sa_len, JNI_TRUE) != 0) { + &sa_len, preferIPv6) != 0) { return IOS_THROWN; } diff --git a/src/solaris/native/sun/nio/ch/FileKey.c b/src/solaris/native/sun/nio/ch/FileKey.c index 511b27f87..fc5618883 100644 --- a/src/solaris/native/sun/nio/ch/FileKey.c +++ b/src/solaris/native/sun/nio/ch/FileKey.c @@ -33,12 +33,6 @@ static jfieldID key_st_dev; /* id for FileKey.st_dev */ static jfieldID key_st_ino; /* id for FileKey.st_ino */ -#define RESTARTABLE(_cmd, _result) do { \ - do { \ - _result = _cmd; \ - } while ((_result == -1) && (errno == EINTR)); \ -} while(0) - JNIEXPORT void JNICALL Java_sun_nio_ch_FileKey_initIDs(JNIEnv *env, jclass clazz) diff --git a/src/solaris/native/sun/nio/ch/Net.c b/src/solaris/native/sun/nio/ch/Net.c index 9eb31c892..d9bbb6e78 100644 --- a/src/solaris/native/sun/nio/ch/Net.c +++ b/src/solaris/native/sun/nio/ch/Net.c @@ -37,61 +37,171 @@ #include "net_util.h" #include "net_util_md.h" #include "nio_util.h" -#include "java_net_SocketOptions.h" #include "nio.h" -#ifdef __linux__ -#include +/** + * Definitions for source-specific multicast to allow for building + * with older header files. + */ + +#ifdef __solaris__ + +#ifndef IP_BLOCK_SOURCE + +#define IP_BLOCK_SOURCE 0x15 +#define IP_UNBLOCK_SOURCE 0x16 +#define IP_ADD_SOURCE_MEMBERSHIP 0x17 +#define IP_DROP_SOURCE_MEMBERSHIP 0x18 + +#define MCAST_BLOCK_SOURCE 0x2b +#define MCAST_UNBLOCK_SOURCE 0x2c +#define MCAST_JOIN_SOURCE_GROUP 0x2d +#define MCAST_LEAVE_SOURCE_GROUP 0x2e -#define IPV6_MULTICAST_IF 17 -#ifndef SO_BSDCOMPAT -#define SO_BSDCOMPAT 14 +#endif /* IP_BLOCK_SOURCE */ + +struct my_ip_mreq_source { + struct in_addr imr_multiaddr; + struct in_addr imr_sourceaddr; + struct in_addr imr_interface; +}; + +/* + * Use #pragma pack() construct to force 32-bit alignment on amd64. + */ +#if defined(amd64) +#pragma pack(4) #endif + +struct my_group_source_req { + uint32_t gsr_interface; /* interface index */ + struct sockaddr_storage gsr_group; /* group address */ + struct sockaddr_storage gsr_source; /* source address */ +}; + +#if defined(amd64) +#pragma pack() #endif +#endif /* __solaris__ */ + + +#ifdef __linux__ + +#ifndef IP_BLOCK_SOURCE + +#define IP_BLOCK_SOURCE 38 +#define IP_UNBLOCK_SOURCE 37 +#define IP_ADD_SOURCE_MEMBERSHIP 39 +#define IP_DROP_SOURCE_MEMBERSHIP 40 + +#define MCAST_BLOCK_SOURCE 43 +#define MCAST_UNBLOCK_SOURCE 44 +#define MCAST_JOIN_SOURCE_GROUP 42 +#define MCAST_LEAVE_SOURCE_GROUP 45 + +#endif /* IP_BLOCK_SOURCE */ + +struct my_ip_mreq_source { + struct in_addr imr_multiaddr; + struct in_addr imr_interface; + struct in_addr imr_sourceaddr; +}; + +struct my_group_source_req { + uint32_t gsr_interface; /* interface index */ + struct sockaddr_storage gsr_group; /* group address */ + struct sockaddr_storage gsr_source; /* source address */ +}; + +#endif /* __linux__ */ + + +#define COPY_INET6_ADDRESS(env, source, target) \ + (*env)->GetByteArrayRegion(env, source, 0, 16, target) + +/* + * Copy IPv6 group, interface index, and IPv6 source address + * into group_source_req structure. + */ +static void initGroupSourceReq(JNIEnv* env, jbyteArray group, jint index, + jbyteArray source, struct my_group_source_req* req) +{ + struct sockaddr_in6* sin6; + + req->gsr_interface = (uint32_t)index; + + sin6 = (struct sockaddr_in6*)&(req->gsr_group); + sin6->sin6_family = AF_INET6; + COPY_INET6_ADDRESS(env, group, (jbyte*)&(sin6->sin6_addr)); + + sin6 = (struct sockaddr_in6*)&(req->gsr_source); + sin6->sin6_family = AF_INET6; + COPY_INET6_ADDRESS(env, source, (jbyte*)&(sin6->sin6_addr)); +} + + JNIEXPORT void JNICALL Java_sun_nio_ch_Net_initIDs(JNIEnv *env, jclass clazz) { /* Here because Windows native code does need to init IDs */ } +JNIEXPORT jboolean JNICALL +Java_sun_nio_ch_Net_isIPv6Available0(JNIEnv* env, jclass cl) +{ + return (ipv6_available()) ? JNI_TRUE : JNI_FALSE; +} + JNIEXPORT int JNICALL -Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean stream, - jboolean reuse) +Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, + jboolean stream, jboolean reuse) { int fd; + int type = (stream ? SOCK_STREAM : SOCK_DGRAM); + int domain = (ipv6_available() && preferIPv6) ? AF_INET6 : AF_INET; -#ifdef AF_INET6 - if (ipv6_available()) - fd = socket(AF_INET6, (stream ? SOCK_STREAM : SOCK_DGRAM), 0); - else -#endif /* AF_INET6 */ - fd = socket(AF_INET, (stream ? SOCK_STREAM : SOCK_DGRAM), 0); - + fd = socket(domain, type, 0); if (fd < 0) { return handleSocketError(env, errno); } if (reuse) { int arg = 1; - if (NET_SetSockOpt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, - sizeof(arg)) < 0) { + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, + sizeof(arg)) < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "sun.nio.ch.Net.setIntOption"); + close(fd); + return -1; + } + } +#ifdef __linux__ + /* By default, Linux uses the route default */ + if (domain == AF_INET6 && type == SOCK_DGRAM) { + int arg = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &arg, + sizeof(arg)) < 0) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "sun.nio.ch.Net.setIntOption"); + close(fd); + return -1; } } +#endif return fd; } JNIEXPORT void JNICALL -Java_sun_nio_ch_Net_bind(JNIEnv *env, jclass clazz, /* ## Needs rest of PSI gunk */ - jobject fdo, jobject ia, int port) +Java_sun_nio_ch_Net_bind0(JNIEnv *env, jclass clazz, jboolean preferIPv6, + jobject fdo, jobject iao, int port) { SOCKADDR sa; int sa_len = SOCKADDR_LEN; int rv = 0; - if (NET_InetAddressToSockaddr(env, ia, port, (struct sockaddr *)&sa, &sa_len, JNI_TRUE) != 0) { + if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *)&sa, &sa_len, preferIPv6) != 0) { return; } @@ -101,27 +211,27 @@ Java_sun_nio_ch_Net_bind(JNIEnv *env, jclass clazz, /* ## Needs rest of PSI gunk } } +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_listen(JNIEnv *env, jclass cl, jobject fdo, jint backlog) +{ + if (listen(fdval(env, fdo), backlog) < 0) + handleSocketError(env, errno); +} + JNIEXPORT jint JNICALL -Java_sun_nio_ch_Net_connect(JNIEnv *env, jclass clazz, - jobject fdo, jobject iao, jint port, - jint trafficClass) +Java_sun_nio_ch_Net_connect0(JNIEnv *env, jclass clazz, jboolean preferIPv6, + jobject fdo, jobject iao, jint port) { SOCKADDR sa; int sa_len = SOCKADDR_LEN; int rv; - if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *) &sa, &sa_len, JNI_TRUE) != 0) { + if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *) &sa, + &sa_len, preferIPv6) != 0) + { return IOS_THROWN; } -#ifdef AF_INET6 -#if 0 - if (trafficClass != 0 && ipv6_available()) { /* ## FIX */ - NET_SetTrafficClass((struct sockaddr *)&sa, trafficClass); - } -#endif -#endif - rv = connect(fdval(env, fdo), (struct sockaddr *)&sa, sa_len); if (rv != 0) { if (errno == EINPROGRESS) { @@ -159,119 +269,79 @@ Java_sun_nio_ch_Net_localInetAddress(JNIEnv *env, jclass clazz, jobject fdo) return NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port); } - -#ifdef NEEDED - -/* ## This is gross. We should generate platform-specific constant - * ## definitions into a .java file and use those directly. - */ - -static int -mapOption(JNIEnv *env, int opt, int *klevel, int *kopt) -{ - - switch (opt) { - - case java_net_SocketOptions_IP_TOS: - *klevel = IPPROTO_IP; - *kopt = IP_TOS; - break; - - case java_net_SocketOptions_SO_BROADCAST: - case java_net_SocketOptions_SO_KEEPALIVE: - case java_net_SocketOptions_SO_LINGER: - case java_net_SocketOptions_SO_OOBINLINE: - case java_net_SocketOptions_SO_RCVBUF: - case java_net_SocketOptions_SO_REUSEADDR: - case java_net_SocketOptions_SO_SNDBUF: - *klevel = SOL_SOCKET; - break; - - case java_net_SocketOptions_TCP_NODELAY: - *klevel = IPPROTO_IP; - *kopt = TCP_NODELAY; - return 0; - - default: - JNU_ThrowByName(env, "java/lang/IllegalArgumentException", NULL); - return -1; - } - - switch (opt) { - - case java_net_SocketOptions_SO_BROADCAST: *kopt = SO_BROADCAST; break; - case java_net_SocketOptions_SO_KEEPALIVE: *kopt = SO_KEEPALIVE; break; - case java_net_SocketOptions_SO_LINGER: *kopt = SO_LINGER; break; - case java_net_SocketOptions_SO_OOBINLINE: *kopt = SO_OOBINLINE; break; - case java_net_SocketOptions_SO_RCVBUF: *kopt = SO_RCVBUF; break; - case java_net_SocketOptions_SO_REUSEADDR: *kopt = SO_REUSEADDR; break; - case java_net_SocketOptions_SO_SNDBUF: *kopt = SO_SNDBUF; break; - - default: - return -1; - } - - return 0; -} -#endif - - JNIEXPORT jint JNICALL -Java_sun_nio_ch_Net_getIntOption0(JNIEnv *env, jclass clazz, - jobject fdo, jint opt) +Java_sun_nio_ch_Net_getIntOption0(JNIEnv *env, jclass clazz, jobject fdo, + jboolean mayNeedConversion, jint level, jint opt) { - int klevel, kopt; int result; struct linger linger; + u_char carg; void *arg; - int arglen; + int arglen, n; - if (NET_MapSocketOption(opt, &klevel, &kopt) < 0) { - JNU_ThrowByNameWithLastError(env, - JNU_JAVANETPKG "SocketException", - "Unsupported socket option"); - return -1; + /* Option value is an int except for a few specific cases */ + + arg = (void *)&result; + arglen = sizeof(result); + + if (level == IPPROTO_IP && + (opt == IP_MULTICAST_TTL || opt == IP_MULTICAST_LOOP)) { + arg = (void*)&carg; + arglen = sizeof(carg); } - if (opt == java_net_SocketOptions_SO_LINGER) { + if (level == SOL_SOCKET && opt == SO_LINGER) { arg = (void *)&linger; arglen = sizeof(linger); - } else { - arg = (void *)&result; - arglen = sizeof(result); } - if (NET_GetSockOpt(fdval(env, fdo), klevel, kopt, arg, &arglen) < 0) { + if (mayNeedConversion) { + n = NET_GetSockOpt(fdval(env, fdo), level, opt, arg, &arglen); + } else { + n = getsockopt(fdval(env, fdo), level, opt, arg, &arglen); + } + if (n < 0) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "sun.nio.ch.Net.getIntOption"); return -1; } - if (opt == java_net_SocketOptions_SO_LINGER) - return linger.l_onoff ? linger.l_linger : -1; - else - return result; + if (level == IPPROTO_IP && + (opt == IP_MULTICAST_TTL || opt == IP_MULTICAST_LOOP)) + { + return (jint)carg; + } + + if (level == SOL_SOCKET && opt == SO_LINGER) + return linger.l_onoff ? (jint)linger.l_linger : (jint)-1; + + return (jint)result; } JNIEXPORT void JNICALL -Java_sun_nio_ch_Net_setIntOption0(JNIEnv *env, jclass clazz, - jobject fdo, jint opt, jint arg) +Java_sun_nio_ch_Net_setIntOption0(JNIEnv *env, jclass clazz, jobject fdo, + jboolean mayNeedConversion, jint level, jint opt, jint arg) { - int klevel, kopt; int result; struct linger linger; + u_char carg; void *parg; - int arglen; + int arglen, n; - if (NET_MapSocketOption(opt, &klevel, &kopt) < 0) { - JNU_ThrowByNameWithLastError(env, - JNU_JAVANETPKG "SocketException", - "Unsupported socket option"); - return; + /* Option value is an int except for a few specific cases */ + + parg = (void*)&arg; + arglen = sizeof(arg); + + if (level == IPPROTO_IP && + (opt == IP_MULTICAST_TTL || opt == IP_MULTICAST_LOOP)) { + parg = (void*)&carg; + arglen = sizeof(carg); + carg = (u_char)arg; } - if (opt == java_net_SocketOptions_SO_LINGER) { + if (level == SOL_SOCKET && opt == SO_LINGER) { parg = (void *)&linger; arglen = sizeof(linger); if (arg >= 0) { @@ -281,19 +351,199 @@ Java_sun_nio_ch_Net_setIntOption0(JNIEnv *env, jclass clazz, linger.l_onoff = 0; linger.l_linger = 0; } - } else { - parg = (void *)&arg; - arglen = sizeof(arg); } - if (NET_SetSockOpt(fdval(env, fdo), klevel, kopt, parg, arglen) < 0) { + if (mayNeedConversion) { + n = NET_SetSockOpt(fdval(env, fdo), level, opt, parg, arglen); + } else { + n = setsockopt(fdval(env, fdo), level, opt, parg, arglen); + } + if (n < 0) { JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "sun.nio.ch.Net.setIntOption"); } } +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_joinOrDrop4(JNIEnv *env, jobject this, jboolean join, jobject fdo, + jint group, jint interf, jint source) +{ + struct ip_mreq mreq; + struct my_ip_mreq_source mreq_source; + int opt, n, optlen; + void* optval; + + if (source == 0) { + mreq.imr_multiaddr.s_addr = htonl(group); + mreq.imr_interface.s_addr = htonl(interf); + opt = (join) ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; + optval = (void*)&mreq; + optlen = sizeof(mreq); + } else { + mreq_source.imr_multiaddr.s_addr = htonl(group); + mreq_source.imr_sourceaddr.s_addr = htonl(source); + mreq_source.imr_interface.s_addr = htonl(interf); + opt = (join) ? IP_ADD_SOURCE_MEMBERSHIP : IP_DROP_SOURCE_MEMBERSHIP; + optval = (void*)&mreq_source; + optlen = sizeof(mreq_source); + } + + n = setsockopt(fdval(env,fdo), IPPROTO_IP, opt, optval, optlen); + if (n < 0) { + if (join && (errno == ENOPROTOOPT)) + return IOS_UNAVAILABLE; + handleSocketError(env, errno); + } + return 0; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_blockOrUnblock4(JNIEnv *env, jobject this, jboolean block, jobject fdo, + jint group, jint interf, jint source) +{ + struct my_ip_mreq_source mreq_source; + int n; + int opt = (block) ? IP_BLOCK_SOURCE : IP_UNBLOCK_SOURCE; + + mreq_source.imr_multiaddr.s_addr = htonl(group); + mreq_source.imr_sourceaddr.s_addr = htonl(source); + mreq_source.imr_interface.s_addr = htonl(interf); + + n = setsockopt(fdval(env,fdo), IPPROTO_IP, opt, + (void*)&mreq_source, sizeof(mreq_source)); + if (n < 0) { + if (block && (errno == ENOPROTOOPT)) + return IOS_UNAVAILABLE; + handleSocketError(env, errno); + } + return 0; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_joinOrDrop6(JNIEnv *env, jobject this, jboolean join, jobject fdo, + jbyteArray group, jint index, jbyteArray source) +{ + struct ipv6_mreq mreq6; + struct my_group_source_req req; + int opt, n, optlen; + void* optval; + + if (source == NULL) { + COPY_INET6_ADDRESS(env, group, (jbyte*)&(mreq6.ipv6mr_multiaddr)); + mreq6.ipv6mr_interface = (int)index; + opt = (join) ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP; + optval = (void*)&mreq6; + optlen = sizeof(mreq6); + } else { +#ifdef __linux__ + /* Include-mode filtering broken on Linux at least to 2.6.24 */ + return IOS_UNAVAILABLE; +#else + initGroupSourceReq(env, group, index, source, &req); + opt = (join) ? MCAST_JOIN_SOURCE_GROUP : MCAST_LEAVE_SOURCE_GROUP; + optval = (void*)&req; + optlen = sizeof(req); +#endif + } + + n = setsockopt(fdval(env,fdo), IPPROTO_IPV6, opt, optval, optlen); + if (n < 0) { + if (join && (errno == ENOPROTOOPT)) + return IOS_UNAVAILABLE; + handleSocketError(env, errno); + } + return 0; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_blockOrUnblock6(JNIEnv *env, jobject this, jboolean block, jobject fdo, + jbyteArray group, jint index, jbyteArray source) +{ + struct my_group_source_req req; + int n; + int opt = (block) ? MCAST_BLOCK_SOURCE : MCAST_UNBLOCK_SOURCE; + + initGroupSourceReq(env, group, index, source, &req); + + n = setsockopt(fdval(env,fdo), IPPROTO_IPV6, opt, + (void*)&req, sizeof(req)); + if (n < 0) { + if (block && (errno == ENOPROTOOPT)) + return IOS_UNAVAILABLE; + handleSocketError(env, errno); + } + return 0; +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_setInterface4(JNIEnv* env, jobject this, jobject fdo, jint interf) +{ + struct in_addr in; + int arglen = sizeof(struct in_addr); + int n; + + in.s_addr = htonl(interf); + + n = setsockopt(fdval(env, fdo), IPPROTO_IP, IP_MULTICAST_IF, + (void*)&(in.s_addr), arglen); + if (n < 0) { + handleSocketError(env, errno); + } +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_getInterface4(JNIEnv* env, jobject this, jobject fdo) +{ + struct in_addr in; + int arglen = sizeof(struct in_addr); + int n; + + n = getsockopt(fdval(env, fdo), IPPROTO_IP, IP_MULTICAST_IF, (void*)&in, &arglen); + if (n < 0) { + handleSocketError(env, errno); + return -1; + } + return ntohl(in.s_addr); +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_setInterface6(JNIEnv* env, jobject this, jobject fdo, jint index) +{ + int value = (jint)index; + int arglen = sizeof(value); + int n; + n = setsockopt(fdval(env, fdo), IPPROTO_IPV6, IPV6_MULTICAST_IF, + (void*)&(index), arglen); + if (n < 0) { + handleSocketError(env, errno); + } +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_getInterface6(JNIEnv* env, jobject this, jobject fdo) +{ + int index; + int arglen = sizeof(index); + int n; + + n = getsockopt(fdval(env, fdo), IPPROTO_IPV6, IPV6_MULTICAST_IF, (void*)&index, &arglen); + if (n < 0) { + handleSocketError(env, errno); + return -1; + } + return (jint)index; +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_shutdown(JNIEnv *env, jclass cl, jobject fdo, jint jhow) +{ + int how = (jhow == sun_nio_ch_Net_SHUT_RD) ? SHUT_RD : + (jhow == sun_nio_ch_Net_SHUT_WR) ? SHUT_WR : SHUT_RDWR; + if (shutdown(fdval(env, fdo), how) < 0) + handleSocketError(env, errno); +} /* Declared in nio_util.h */ diff --git a/src/solaris/native/sun/nio/ch/ServerSocketChannelImpl.c b/src/solaris/native/sun/nio/ch/ServerSocketChannelImpl.c index da92ed73d..c0019d5ad 100644 --- a/src/solaris/native/sun/nio/ch/ServerSocketChannelImpl.c +++ b/src/solaris/native/sun/nio/ch/ServerSocketChannelImpl.c @@ -65,14 +65,6 @@ Java_sun_nio_ch_ServerSocketChannelImpl_initIDs(JNIEnv *env, jclass c) "(Ljava/net/InetAddress;I)V"); } -JNIEXPORT void JNICALL -Java_sun_nio_ch_ServerSocketChannelImpl_listen(JNIEnv *env, jclass cl, - jobject fdo, jint backlog) -{ - if (listen(fdval(env, fdo), backlog) < 0) - handleSocketError(env, errno); -} - JNIEXPORT jint JNICALL Java_sun_nio_ch_ServerSocketChannelImpl_accept0(JNIEnv *env, jobject this, jobject ssfdo, jobject newfdo, diff --git a/src/solaris/native/sun/nio/ch/SocketChannelImpl.c b/src/solaris/native/sun/nio/ch/SocketChannelImpl.c index 94c8467cf..7190b1b12 100644 --- a/src/solaris/native/sun/nio/ch/SocketChannelImpl.c +++ b/src/solaris/native/sun/nio/ch/SocketChannelImpl.c @@ -35,10 +35,6 @@ #include #endif -#if defined(__solaris__) && !defined(_SOCKLEN_T) -typedef size_t socklen_t; /* New in SunOS 5.7, so need this for 5.6 */ -#endif - #include "jni.h" #include "jni_util.h" #include "net_util.h" @@ -88,12 +84,3 @@ Java_sun_nio_ch_SocketChannelImpl_checkConnect(JNIEnv *env, jobject this, } return 0; } - - -JNIEXPORT void JNICALL -Java_sun_nio_ch_SocketChannelImpl_shutdown(JNIEnv *env, jclass cl, - jobject fdo, jint how) -{ - if (shutdown(fdval(env, fdo), how) < 0) - handleSocketError(env, errno); -} diff --git a/src/solaris/native/sun/nio/ch/nio_util.h b/src/solaris/native/sun/nio/ch/nio_util.h index 608b0cc89..02c15ed47 100644 --- a/src/solaris/native/sun/nio/ch/nio_util.h +++ b/src/solaris/native/sun/nio/ch/nio_util.h @@ -27,8 +27,15 @@ #include "jni_util.h" #include "jvm.h" #include "jlong.h" +#include #include +#define RESTARTABLE(_cmd, _result) do { \ + do { \ + _result = _cmd; \ + } while((_result == -1) && (errno == EINTR)); \ +} while(0) + /* NIO utility procedures */ diff --git a/src/windows/native/java/net/net_util_md.c b/src/windows/native/java/net/net_util_md.c index 5fe1dc5d9..ddf149684 100644 --- a/src/windows/native/java/net/net_util_md.c +++ b/src/windows/native/java/net/net_util_md.c @@ -889,7 +889,7 @@ NET_InetAddressToSockaddr(JNIEnv *env, jobject iaObj, int port, struct sockaddr return 0; } -jint +JNIEXPORT jint JNICALL NET_GetPortFromSockaddr(struct sockaddr *him) { if (him->sa_family == AF_INET6) { return ntohs(((struct sockaddr_in6*)him)->sin6_port); diff --git a/src/windows/native/sun/nio/ch/DatagramChannelImpl.c b/src/windows/native/sun/nio/ch/DatagramChannelImpl.c index 6d400c213..99cc716eb 100644 --- a/src/windows/native/sun/nio/ch/DatagramChannelImpl.c +++ b/src/windows/native/sun/nio/ch/DatagramChannelImpl.c @@ -39,46 +39,9 @@ static jfieldID isa_portID; /* port in java.net.InetSocketAddress */ static jfieldID dci_senderID; /* sender in sun.nio.ch.DatagramChannelImpl */ static jfieldID dci_senderAddrID; /* sender InetAddress in sun.nio.ch.DatagramChannelImpl */ static jfieldID dci_senderPortID; /* sender port in sun.nio.ch.DatagramChannelImpl */ -static jfieldID ia_addrID; -static jfieldID ia_famID; static jclass isa_class; /* java.net.InetSocketAddress */ -static jclass ia_class; -static jmethodID isa_ctorID; /* .InetSocketAddress(InetAddress, int) */ -static jmethodID ia_ctorID; +static jmethodID isa_ctorID; /* java.net.InetSocketAddress(InetAddress, int) */ -/* - * Returns JNI_TRUE if DatagramChannelImpl has already cached an - * InetAddress/port corresponding to the socket address. - */ -static jboolean isSenderCached(JNIEnv *env, jobject this, struct sockaddr_in *sa) { - jobject senderAddr; - - /* shouldn't happen until we have dual IPv4/IPv6 stack (post-XP ?) */ - if (sa->sin_family != AF_INET) { - return JNI_FALSE; - } - - /* - * Compare source address to cached InetAddress - */ - senderAddr = (*env)->GetObjectField(env, this, dci_senderAddrID); - if (senderAddr == NULL) { - return JNI_FALSE; - } - if ((jint)ntohl(sa->sin_addr.s_addr) != - (*env)->GetIntField(env, senderAddr, ia_addrID)) { - return JNI_FALSE; - } - - /* - * Compare source port to cached port - */ - if ((jint)ntohs(sa->sin_port) != - (*env)->GetIntField(env, this, dci_senderPortID)) { - return JNI_FALSE; - } - return JNI_TRUE; -} JNIEXPORT void JNICALL Java_sun_nio_ch_DatagramChannelImpl_initIDs(JNIEnv *env, jclass clazz) @@ -99,32 +62,6 @@ Java_sun_nio_ch_DatagramChannelImpl_initIDs(JNIEnv *env, jclass clazz) "Ljava/net/InetAddress;"); dci_senderPortID = (*env)->GetFieldID(env, clazz, "cachedSenderPort", "I"); - clazz = (*env)->FindClass(env, "java/net/Inet4Address"); - ia_class = (*env)->NewGlobalRef(env, clazz); - ia_addrID = (*env)->GetFieldID(env, clazz, "address", "I"); - ia_famID = (*env)->GetFieldID(env, clazz, "family", "I"); - ia_ctorID = (*env)->GetMethodID(env, clazz, "", "()V"); -} - -/* - * Return JNI_TRUE if this Windows edition supports ICMP Port Unreachable - */ -__inline static jboolean supportPortUnreachable() { - static jboolean initDone; - static jboolean portUnreachableSupported; - - if (!initDone) { - OSVERSIONINFO ver; - ver.dwOSVersionInfoSize = sizeof(ver); - GetVersionEx(&ver); - if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT && ver.dwMajorVersion >= 5) { - portUnreachableSupported = JNI_TRUE; - } else { - portUnreachableSupported = JNI_FALSE; - } - initDone = JNI_TRUE; - } - return portUnreachableSupported; } /* @@ -140,15 +77,8 @@ jboolean purgeOutstandingICMP(JNIEnv *env, jclass clazz, jint fd) char buf[1]; fd_set tbl; struct timeval t = { 0, 0 }; - struct sockaddr_in rmtaddr; - int addrlen = sizeof(rmtaddr); - - /* - * A no-op if this OS doesn't support it. - */ - if (!supportPortUnreachable()) { - return JNI_FALSE; - } + SOCKETADDRESS sa; + int addrlen = sizeof(sa); /* * Peek at the queue to see if there is an ICMP port unreachable. If there @@ -161,7 +91,7 @@ jboolean purgeOutstandingICMP(JNIEnv *env, jclass clazz, jint fd) break; } if (recvfrom(fd, buf, 1, MSG_PEEK, - (struct sockaddr *)&rmtaddr, &addrlen) != SOCKET_ERROR) { + (struct sockaddr *)&sa, &addrlen) != SOCKET_ERROR) { break; } if (WSAGetLastError() != WSAECONNRESET) { @@ -169,7 +99,7 @@ jboolean purgeOutstandingICMP(JNIEnv *env, jclass clazz, jint fd) break; } - recvfrom(fd, buf, 1, 0, (struct sockaddr *)&rmtaddr, &addrlen); + recvfrom(fd, buf, 1, 0, (struct sockaddr *)&sa, &addrlen); got_icmp = JNI_TRUE; } @@ -182,12 +112,12 @@ Java_sun_nio_ch_DatagramChannelImpl_disconnect0(JNIEnv *env, jobject this, { jint fd = fdval(env, fdo); int rv = 0; - struct sockaddr_in psa; - int sa_len = sizeof(psa); + SOCKETADDRESS sa; + int sa_len = sizeof(sa); - memset(&psa, 0, sa_len); + memset(&sa, 0, sa_len); - rv = connect((SOCKET)fd, (struct sockaddr *)&psa, sa_len); + rv = connect((SOCKET)fd, (struct sockaddr *)&sa, sa_len); if (rv == SOCKET_ERROR) { handleSocketError(env, WSAGetLastError()); } @@ -200,10 +130,11 @@ Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv *env, jobject this, { jint fd = fdval(env, fdo); void *buf = (void *)jlong_to_ptr(address); - struct sockaddr_in psa; - int sa_len = sizeof(psa); + SOCKETADDRESS sa; + int sa_len = sizeof(sa); BOOL retry = FALSE; jint n; + jobject senderAddr; do { retry = FALSE; @@ -211,7 +142,7 @@ Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv *env, jobject this, (char *)buf, len, 0, - (struct sockaddr *)&psa, + (struct sockaddr *)&sa, &sa_len); if (n == SOCKET_ERROR) { @@ -233,21 +164,30 @@ Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv *env, jobject this, } } while (retry); - if (!isSenderCached(env, this, &psa)) { - int port = ntohs(psa.sin_port); - jobject ia = (*env)->NewObject(env, ia_class, ia_ctorID); - jobject isa = NULL; - - if (psa.sin_family != AF_INET) { - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", - "Protocol family unavailable"); + /* + * If the source address and port match the cached address + * and port in DatagramChannelImpl then we don't need to + * create InetAddress and InetSocketAddress objects. + */ + senderAddr = (*env)->GetObjectField(env, this, dci_senderAddrID); + if (senderAddr != NULL) { + if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&sa, + senderAddr)) { + senderAddr = NULL; + } else { + jint port = (*env)->GetIntField(env, this, dci_senderPortID); + if (port != NET_GetPortFromSockaddr((struct sockaddr *)&sa)) { + senderAddr = NULL; + } } + } + if (senderAddr == NULL) { + jobject isa = NULL; + int port; + jobject ia = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, + &port); if (ia != NULL) { - // populate InetAddress (assumes AF_INET) - (*env)->SetIntField(env, ia, ia_addrID, ntohl(psa.sin_addr.s_addr)); - - // create InetSocketAddress isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port); } @@ -258,9 +198,8 @@ Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv *env, jobject this, // update cachedSenderInetAddress/cachedSenderPort (*env)->SetObjectField(env, this, dci_senderAddrID, ia); - (*env)->SetIntField(env, this, dci_senderPortID, port); - - // update sender + (*env)->SetIntField(env, this, dci_senderPortID, + NET_GetPortFromSockaddr((struct sockaddr *)&sa)); (*env)->SetObjectField(env, this, dci_senderID, isa); } return n; @@ -268,21 +207,20 @@ Java_sun_nio_ch_DatagramChannelImpl_receive0(JNIEnv *env, jobject this, JNIEXPORT jint JNICALL Java_sun_nio_ch_DatagramChannelImpl_send0(JNIEnv *env, jobject this, - jobject fdo, jlong address, - jint len, jobject dest) + jboolean preferIPv6, jobject fdo, + jlong address, jint len, jobject dest) { jint fd = fdval(env, fdo); void *buf = (void *)jlong_to_ptr(address); - SOCKETADDRESS psa; - int sa_len = sizeof(psa); + SOCKETADDRESS sa; + int sa_len; jint rv = 0; jobject destAddress = (*env)->GetObjectField(env, dest, isa_addrID); jint destPort = (*env)->GetIntField(env, dest, isa_portID); - if (NET_InetAddressToSockaddr(env, destAddress, destPort, - (struct sockaddr *)&psa, - &sa_len, JNI_FALSE) != 0) { + (struct sockaddr *)&sa, + &sa_len, preferIPv6) != 0) { return IOS_THROWN; } @@ -290,7 +228,7 @@ Java_sun_nio_ch_DatagramChannelImpl_send0(JNIEnv *env, jobject this, buf, len, 0, - (struct sockaddr *)&psa, + (struct sockaddr *)&sa, sa_len); if (rv == SOCKET_ERROR) { int theErr = (jint)WSAGetLastError(); diff --git a/src/windows/native/sun/nio/ch/Net.c b/src/windows/native/sun/nio/ch/Net.c index b9fac794e..c89745a21 100644 --- a/src/windows/native/sun/nio/ch/Net.c +++ b/src/windows/native/sun/nio/ch/Net.c @@ -36,51 +36,95 @@ #include "sun_nio_ch_Net.h" +/** + * Definitions to allow for building with older SDK include files. + */ + +#ifndef MCAST_BLOCK_SOURCE + +#define MCAST_BLOCK_SOURCE 43 +#define MCAST_UNBLOCK_SOURCE 44 +#define MCAST_JOIN_SOURCE_GROUP 45 +#define MCAST_LEAVE_SOURCE_GROUP 46 + +#endif /* MCAST_BLOCK_SOURCE */ -static jfieldID ia_addrID; -static jclass ia_class; -static jmethodID ia_ctorID; -static jfieldID ia_famID; +typedef struct my_ip_mreq_source { + IN_ADDR imr_multiaddr; + IN_ADDR imr_sourceaddr; + IN_ADDR imr_interface; +}; -/************************************************************** - * static method to store field IDs in initializers +typedef struct my_group_source_req { + ULONG gsr_interface; + SOCKADDR_STORAGE gsr_group; + SOCKADDR_STORAGE gsr_source; +}; + +/** + * Copy IPv6 address as jbytearray to target */ +#define COPY_INET6_ADDRESS(env, source, target) \ + (*env)->GetByteArrayRegion(env, source, 0, 16, target) + + JNIEXPORT void JNICALL Java_sun_nio_ch_Net_initIDs(JNIEnv *env, jclass clazz) { - clazz = (*env)->FindClass(env, "java/net/Inet4Address"); - ia_class = (*env)->NewGlobalRef(env, clazz); - ia_addrID = (*env)->GetFieldID(env, clazz, "address", "I"); - ia_famID = (*env)->GetFieldID(env, clazz, "family", "I"); - ia_ctorID = (*env)->GetMethodID(env, clazz, "", "()V"); + /* nothing to do */ } +JNIEXPORT jboolean JNICALL +Java_sun_nio_ch_Net_isIPv6Available0(JNIEnv* env, jclass cl) +{ + /* + * Return true if Windows Vista or newer, and IPv6 is configured + */ + OSVERSIONINFO ver; + ver.dwOSVersionInfoSize = sizeof(ver); + GetVersionEx(&ver); + if ((ver.dwPlatformId == VER_PLATFORM_WIN32_NT) && + (ver.dwMajorVersion >= 6) && ipv6_available()) + { + return JNI_TRUE; + } + return JNI_FALSE; +} JNIEXPORT jint JNICALL -Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean stream, - jboolean reuse) +Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, + jboolean stream, jboolean reuse) { SOCKET s; + int domain = (preferIPv6) ? AF_INET6 : AF_INET; - s = socket(AF_INET, (stream ? SOCK_STREAM : SOCK_DGRAM), 0); + s = socket(domain, (stream ? SOCK_STREAM : SOCK_DGRAM), 0); if (s != INVALID_SOCKET) { SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0); + + /* IPV6_V6ONLY is true by default */ + if (domain == AF_INET6) { + int opt = 0; + setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + (const char *)&opt, sizeof(opt)); + } } else { NET_ThrowNew(env, WSAGetLastError(), "socket"); } + return (jint)s; } JNIEXPORT void JNICALL -Java_sun_nio_ch_Net_bind(JNIEnv *env, jclass clazz, - jobject fdo, jobject iao, jint port) +Java_sun_nio_ch_Net_bind0(JNIEnv *env, jclass clazz, jboolean preferIPv6, + jobject fdo, jobject iao, jint port) { SOCKETADDRESS sa; int rv; - int sa_len = sizeof(sa); + int sa_len; - if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *)&sa, &sa_len, JNI_FALSE) != 0) { + if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *)&sa, &sa_len, preferIPv6) != 0) { return; } @@ -89,16 +133,25 @@ Java_sun_nio_ch_Net_bind(JNIEnv *env, jclass clazz, NET_ThrowNew(env, WSAGetLastError(), "bind"); } +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_listen(JNIEnv *env, jclass cl, jobject fdo, jint backlog) +{ + if (listen(fdval(env,fdo), backlog) == SOCKET_ERROR) { + NET_ThrowNew(env, WSAGetLastError(), "listen"); + } +} + + JNIEXPORT jint JNICALL -Java_sun_nio_ch_Net_connect(JNIEnv *env, jclass clazz, jobject fdo, jobject iao, - jint port, jint trafficClass) +Java_sun_nio_ch_Net_connect0(JNIEnv *env, jclass clazz, jboolean preferIPv6, jobject fdo, + jobject iao, jint port) { SOCKETADDRESS sa; int rv; - int sa_len = sizeof(sa); + int sa_len; - if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *)&sa, &sa_len, JNI_FALSE) != 0) { - return IOS_THROWN; + if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *)&sa, &sa_len, preferIPv6) != 0) { + return IOS_THROWN; } rv = connect(fdval(env, fdo), (struct sockaddr *)&sa, sa_len); @@ -116,7 +169,7 @@ Java_sun_nio_ch_Net_connect(JNIEnv *env, jclass clazz, jobject fdo, jobject iao, JNIEXPORT jint JNICALL Java_sun_nio_ch_Net_localPort(JNIEnv *env, jclass clazz, jobject fdo) { - struct sockaddr_in sa; + SOCKETADDRESS sa; int sa_len = sizeof(sa); if (getsockname(fdval(env, fdo), (struct sockaddr *)&sa, &sa_len) < 0) { @@ -127,50 +180,64 @@ Java_sun_nio_ch_Net_localPort(JNIEnv *env, jclass clazz, jobject fdo) NET_ThrowNew(env, error, "getsockname"); return IOS_THROWN; } - return (jint)ntohs(sa.sin_port); + return NET_GetPortFromSockaddr((struct sockaddr *)&sa); } JNIEXPORT jobject JNICALL Java_sun_nio_ch_Net_localInetAddress(JNIEnv *env, jclass clazz, jobject fdo) { - struct sockaddr_in sa; + SOCKETADDRESS sa; int sa_len = sizeof(sa); - jobject iao; + int port; if (getsockname(fdval(env, fdo), (struct sockaddr *)&sa, &sa_len) < 0) { NET_ThrowNew(env, WSAGetLastError(), "getsockname"); return NULL; } + return NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port); +} - iao = (*env)->NewObject(env, ia_class, ia_ctorID); - if (iao == NULL) { - JNU_ThrowOutOfMemoryError(env, "heap allocation failure"); - } else { - (*env)->SetIntField(env, iao, ia_addrID, ntohl(sa.sin_addr.s_addr)); - } +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_remotePort(JNIEnv *env, jclass clazz, jobject fdo) +{ + SOCKETADDRESS sa; + int sa_len = sizeof(sa); - return iao; + if (getpeername(fdval(env, fdo), (struct sockaddr *)&sa, &sa_len) < 0) { + int error = WSAGetLastError(); + if (error == WSAEINVAL) { + return 0; + } + NET_ThrowNew(env, error, "getsockname"); + return IOS_THROWN; + } + return NET_GetPortFromSockaddr((struct sockaddr *)&sa); } +JNIEXPORT jobject JNICALL +Java_sun_nio_ch_Net_remoteInetAddress(JNIEnv *env, jclass clazz, jobject fdo) +{ + SOCKETADDRESS sa; + int sa_len = sizeof(sa); + int port; + + if (getpeername(fdval(env, fdo), (struct sockaddr *)&sa, &sa_len) < 0) { + NET_ThrowNew(env, WSAGetLastError(), "getsockname"); + return NULL; + } + return NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port); +} JNIEXPORT jint JNICALL -Java_sun_nio_ch_Net_getIntOption0(JNIEnv *env, jclass clazz, - jobject fdo, jint opt) +Java_sun_nio_ch_Net_getIntOption0(JNIEnv *env, jclass clazz, jobject fdo, + jboolean mayNeedConversion, jint level, jint opt) { - int klevel, kopt; - int result; + int result = 0; struct linger linger; char *arg; - int arglen; - - if (NET_MapSocketOption(opt, &klevel, &kopt) < 0) { - JNU_ThrowByNameWithLastError(env, - JNU_JAVANETPKG "SocketException", - "Unsupported socket option"); - return IOS_THROWN; - } + int arglen, n; - if (opt == java_net_SocketOptions_SO_LINGER) { + if (level == SOL_SOCKET && opt == SO_LINGER) { arg = (char *)&linger; arglen = sizeof(linger); } else { @@ -178,34 +245,40 @@ Java_sun_nio_ch_Net_getIntOption0(JNIEnv *env, jclass clazz, arglen = sizeof(result); } - if (NET_GetSockOpt(fdval(env, fdo), klevel, kopt, arg, &arglen) < 0) { - NET_ThrowNew(env, WSAGetLastError(), "sun.nio.ch.Net.setIntOption"); + /** + * HACK: IP_TOS is deprecated on Windows and querying the option + * returns a protocol error. NET_GetSockOpt handles this and uses + * a fallback mechanism. + */ + if (level == IPPROTO_IP && opt == IP_TOS) { + mayNeedConversion = JNI_TRUE; + } + + if (mayNeedConversion) { + n = NET_GetSockOpt(fdval(env, fdo), level, opt, arg, &arglen); + } else { + n = getsockopt(fdval(env, fdo), level, opt, arg, &arglen); + } + if (n < 0) { + handleSocketError(env, WSAGetLastError()); return IOS_THROWN; } - if (opt == java_net_SocketOptions_SO_LINGER) + if (level == SOL_SOCKET && opt == SO_LINGER) return linger.l_onoff ? linger.l_linger : -1; else return result; } JNIEXPORT void JNICALL -Java_sun_nio_ch_Net_setIntOption0(JNIEnv *env, jclass clazz, - jobject fdo, jint opt, jint arg) +Java_sun_nio_ch_Net_setIntOption0(JNIEnv *env, jclass clazz, jobject fdo, + jboolean mayNeedConversion, jint level, jint opt, jint arg) { - int klevel, kopt; struct linger linger; char *parg; - int arglen; + int arglen, n; - if (NET_MapSocketOption(opt, &klevel, &kopt) < 0) { - JNU_ThrowByNameWithLastError(env, - JNU_JAVANETPKG "SocketException", - "Unsupported socket option"); - return; - } - - if (opt == java_net_SocketOptions_SO_LINGER) { + if (level == SOL_SOCKET && opt == SO_LINGER) { parg = (char *)&linger; arglen = sizeof(linger); if (arg >= 0) { @@ -220,7 +293,200 @@ Java_sun_nio_ch_Net_setIntOption0(JNIEnv *env, jclass clazz, arglen = sizeof(arg); } - if (NET_SetSockOpt(fdval(env, fdo), klevel, kopt, parg, arglen) < 0) { - NET_ThrowNew(env, WSAGetLastError(), "sun.nio.ch.Net.setIntOption"); + if (mayNeedConversion) { + n = NET_SetSockOpt(fdval(env, fdo), level, opt, parg, arglen); + } else { + n = setsockopt(fdval(env, fdo), level, opt, parg, arglen); + } + if (n < 0) + handleSocketError(env, WSAGetLastError()); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_joinOrDrop4(JNIEnv *env, jobject this, jboolean join, jobject fdo, + jint group, jint interf, jint source) +{ + struct ip_mreq mreq; + struct my_ip_mreq_source mreq_source; + int opt, n, optlen; + void* optval; + + if (source == 0) { + mreq.imr_multiaddr.s_addr = htonl(group); + mreq.imr_interface.s_addr = htonl(interf); + opt = (join) ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; + optval = (void*)&mreq; + optlen = sizeof(mreq); + } else { + mreq_source.imr_multiaddr.s_addr = htonl(group); + mreq_source.imr_sourceaddr.s_addr = htonl(source); + mreq_source.imr_interface.s_addr = htonl(interf); + opt = (join) ? IP_ADD_SOURCE_MEMBERSHIP : IP_DROP_SOURCE_MEMBERSHIP; + optval = (void*)&mreq_source; + optlen = sizeof(mreq_source); + } + + n = setsockopt(fdval(env,fdo), IPPROTO_IP, opt, optval, optlen); + if (n < 0) { + if (join && (WSAGetLastError() == WSAENOPROTOOPT)) + return IOS_UNAVAILABLE; + handleSocketError(env, WSAGetLastError()); + } + return 0; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_blockOrUnblock4(JNIEnv *env, jobject this, jboolean block, jobject fdo, + jint group, jint interf, jint source) +{ + struct my_ip_mreq_source mreq_source; + int n; + int opt = (block) ? IP_BLOCK_SOURCE : IP_UNBLOCK_SOURCE; + + mreq_source.imr_multiaddr.s_addr = htonl(group); + mreq_source.imr_sourceaddr.s_addr = htonl(source); + mreq_source.imr_interface.s_addr = htonl(interf); + + n = setsockopt(fdval(env,fdo), IPPROTO_IP, opt, + (void*)&mreq_source, sizeof(mreq_source)); + if (n < 0) { + if (block && (WSAGetLastError() == WSAENOPROTOOPT)) + return IOS_UNAVAILABLE; + handleSocketError(env, WSAGetLastError()); + } + return 0; +} + +/** + * Call setsockopt with a IPPROTO_IPV6 level socket option + * and a group_source_req structure as the option value. The + * given IPv6 group, interface index, and IPv6 source address + * are copied into the structure. + */ +static int setGroupSourceReqOption(JNIEnv* env, + jobject fdo, + int opt, + jbyteArray group, + jint index, + jbyteArray source) +{ + struct my_group_source_req req; + struct sockaddr_in6* sin6; + + req.gsr_interface = (ULONG)index; + + sin6 = (struct sockaddr_in6*)&(req.gsr_group); + sin6->sin6_family = AF_INET6; + COPY_INET6_ADDRESS(env, group, (jbyte*)&(sin6->sin6_addr)); + + sin6 = (struct sockaddr_in6*)&(req.gsr_source); + sin6->sin6_family = AF_INET6; + COPY_INET6_ADDRESS(env, source, (jbyte*)&(sin6->sin6_addr)); + + return setsockopt(fdval(env,fdo), IPPROTO_IPV6, opt, (void*)&req, sizeof(req)); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_joinOrDrop6(JNIEnv *env, jobject this, jboolean join, jobject fdo, + jbyteArray group, jint index, jbyteArray source) +{ + struct ipv6_mreq mreq6; + int n; + + if (source == NULL) { + int opt = (join) ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP; + COPY_INET6_ADDRESS(env, group, (jbyte*)&(mreq6.ipv6mr_multiaddr)); + mreq6.ipv6mr_interface = (int)index; + n = setsockopt(fdval(env,fdo), IPPROTO_IPV6, opt, + (void*)&mreq6, sizeof(mreq6)); + } else { + int opt = (join) ? MCAST_JOIN_SOURCE_GROUP : MCAST_LEAVE_SOURCE_GROUP; + n = setGroupSourceReqOption(env, fdo, opt, group, index, source); + } + + if (n < 0) { + handleSocketError(env, errno); + } + return 0; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_blockOrUnblock6(JNIEnv *env, jobject this, jboolean block, jobject fdo, + jbyteArray group, jint index, jbyteArray source) +{ + int opt = (block) ? MCAST_BLOCK_SOURCE : MCAST_UNBLOCK_SOURCE; + int n = setGroupSourceReqOption(env, fdo, opt, group, index, source); + if (n < 0) { + handleSocketError(env, errno); + } + return 0; +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_setInterface4(JNIEnv* env, jobject this, jobject fdo, jint interf) +{ + struct in_addr in; + int arglen = sizeof(struct in_addr); + int n; + + in.s_addr = htonl(interf); + + n = setsockopt(fdval(env, fdo), IPPROTO_IP, IP_MULTICAST_IF, + (void*)&(in.s_addr), arglen); + if (n < 0) { + handleSocketError(env, WSAGetLastError()); + } +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_getInterface4(JNIEnv* env, jobject this, jobject fdo) +{ + struct in_addr in; + int arglen = sizeof(struct in_addr); + int n; + + n = getsockopt(fdval(env, fdo), IPPROTO_IP, IP_MULTICAST_IF, (void*)&in, &arglen); + if (n < 0) { + handleSocketError(env, WSAGetLastError()); + return IOS_THROWN; + } + return ntohl(in.s_addr); +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_setInterface6(JNIEnv* env, jobject this, jobject fdo, jint index) +{ + int value = (jint)index; + int arglen = sizeof(value); + int n; + + n = setsockopt(fdval(env, fdo), IPPROTO_IPV6, IPV6_MULTICAST_IF, + (void*)&(index), arglen); + if (n < 0) { + handleSocketError(env, errno); + } +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_getInterface6(JNIEnv* env, jobject this, jobject fdo) +{ + int index; + int arglen = sizeof(index); + int n; + + n = getsockopt(fdval(env, fdo), IPPROTO_IPV6, IPV6_MULTICAST_IF, (void*)&index, &arglen); + if (n < 0) { + handleSocketError(env, errno); + return -1; + } + return (jint)index; +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Net_shutdown(JNIEnv *env, jclass cl, jobject fdo, jint jhow) { + int how = (jhow == sun_nio_ch_Net_SHUT_RD) ? SD_RECEIVE : + (jhow == sun_nio_ch_Net_SHUT_WR) ? SD_SEND : SD_BOTH; + if (shutdown(fdval(env, fdo), how) == SOCKET_ERROR) { + NET_ThrowNew(env, WSAGetLastError(), "shutdown"); } } diff --git a/src/windows/native/sun/nio/ch/ServerSocketChannelImpl.c b/src/windows/native/sun/nio/ch/ServerSocketChannelImpl.c index 36b6006e0..a597d2525 100644 --- a/src/windows/native/sun/nio/ch/ServerSocketChannelImpl.c +++ b/src/windows/native/sun/nio/ch/ServerSocketChannelImpl.c @@ -46,10 +46,6 @@ static jfieldID fd_fdID; /* java.io.FileDescriptor.fd */ static jclass isa_class; /* java.net.InetSocketAddress */ static jmethodID isa_ctorID; /* InetSocketAddress(InetAddress, int) */ -static jclass ia_class; /* java.net.InetAddress */ -static jmethodID ia_ctorID; /* InetAddress() */ -static jfieldID ia_addrID; /* java.net.InetAddress.address */ -static jfieldID ia_famID; /* java.net.InetAddress.family */ /************************************************************** @@ -66,12 +62,6 @@ Java_sun_nio_ch_ServerSocketChannelImpl_initIDs(JNIEnv *env, jclass cls) isa_class = (*env)->NewGlobalRef(env, cls); isa_ctorID = (*env)->GetMethodID(env, cls, "", "(Ljava/net/InetAddress;I)V"); - - cls = (*env)->FindClass(env, "java/net/Inet4Address"); - ia_class = (*env)->NewGlobalRef(env, cls); - ia_ctorID = (*env)->GetMethodID(env, cls, "","()V"); - ia_addrID = (*env)->GetFieldID(env, cls, "address", "I"); - ia_famID = (*env)->GetFieldID(env, cls, "family", "I"); } JNIEXPORT void JNICALL @@ -90,8 +80,9 @@ Java_sun_nio_ch_ServerSocketChannelImpl_accept0(JNIEnv *env, jobject this, { jint ssfd = (*env)->GetIntField(env, ssfdo, fd_fdID); jint newfd; - struct sockaddr_in sa; - jobject remote_ia = 0; + SOCKETADDRESS sa; + jobject remote_ia; + int remote_port; jobject isa; jobject ia; int addrlen = sizeof(sa); @@ -106,14 +97,13 @@ Java_sun_nio_ch_ServerSocketChannelImpl_accept0(JNIEnv *env, jobject this, JNU_ThrowIOExceptionWithLastError(env, "Accept failed"); return IOS_THROWN; } - (*env)->SetIntField(env, newfdo, fd_fdID, newfd); - ia = (*env)->NewObject(env, ia_class, ia_ctorID); - (*env)->SetIntField(env, ia, ia_addrID, ntohl(sa.sin_addr.s_addr)); - (*env)->SetIntField(env, ia, ia_famID, sa.sin_family); + (*env)->SetIntField(env, newfdo, fd_fdID, newfd); + remote_ia = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, (int *)&remote_port); - isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, - ntohs(sa.sin_port)); + isa = (*env)->NewObject(env, isa_class, isa_ctorID, + remote_ia, remote_port); (*env)->SetObjectArrayElement(env, isaa, 0, isa); + return 1; } diff --git a/src/windows/native/sun/nio/ch/SocketChannelImpl.c b/src/windows/native/sun/nio/ch/SocketChannelImpl.c index d49d9bf29..1c8992568 100644 --- a/src/windows/native/sun/nio/ch/SocketChannelImpl.c +++ b/src/windows/native/sun/nio/ch/SocketChannelImpl.c @@ -139,12 +139,3 @@ Java_sun_nio_ch_SocketChannelImpl_checkConnect(JNIEnv *env, jobject this, return 0; } - -JNIEXPORT void JNICALL -Java_sun_nio_ch_SocketChannelImpl_shutdown(JNIEnv *env, jclass cl, - jobject fdo, jint how) -{ - if (shutdown(fdval(env, fdo), how) == SOCKET_ERROR) { - NET_ThrowNew(env, WSAGetLastError(), "shutdown"); - } -} diff --git a/test/java/nio/channels/DatagramChannel/BasicMulticastTests.java b/test/java/nio/channels/DatagramChannel/BasicMulticastTests.java new file mode 100644 index 000000000..03b5daa68 --- /dev/null +++ b/test/java/nio/channels/DatagramChannel/BasicMulticastTests.java @@ -0,0 +1,220 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4527345 + * @summary Unit test for DatagramChannel's multicast support + * @build BasicMulticastTests NetworkConfiguration + */ + +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.net.*; +import java.util.*; +import java.io.IOException; + +public class BasicMulticastTests { + + /** + * Tests that existing membership key is returned by join methods and that + * membership key methods return the expected results + */ + static void membershipKeyTests(NetworkInterface nif, + InetAddress group, + InetAddress source) + throws IOException + { + System.out.format("MembershipKey test using %s @ %s\n", + group.getHostAddress(), nif.getName()); + + ProtocolFamily family = (group instanceof Inet4Address) ? + StandardProtocolFamily.INET : StandardProtocolFamily.INET6; + + DatagramChannel dc = DatagramChannel.open(family) + .setOption(StandardSocketOption.SO_REUSEADDR, true) + .bind(new InetSocketAddress(source, 0)); + + // check existing key is returned + MembershipKey key = dc.join(group, nif); + MembershipKey other = dc.join(group, nif); + if (other != key) { + throw new RuntimeException("existing key not returned"); + } + + // check key + if (!key.isValid()) + throw new RuntimeException("key is not valid"); + if (!key.getGroup().equals(group)) + throw new RuntimeException("group is incorrect"); + if (!key.getNetworkInterface().equals(nif)) + throw new RuntimeException("network interface is incorrect"); + if (key.getSourceAddress() != null) + throw new RuntimeException("key is source specific"); + + // drop membership + key.drop(); + if (key.isValid()) { + throw new RuntimeException("key is still valid"); + } + + // source-specific + try { + key = dc.join(group, nif, source); + other = dc.join(group, nif, source); + if (other != key) { + throw new RuntimeException("existing key not returned"); + } + if (!key.isValid()) + throw new RuntimeException("key is not valid"); + if (!key.getGroup().equals(group)) + throw new RuntimeException("group is incorrect"); + if (!key.getNetworkInterface().equals(nif)) + throw new RuntimeException("network interface is incorrect"); + if (!key.getSourceAddress().equals(source)) + throw new RuntimeException("key's source address incorrect"); + + // drop membership + key.drop(); + if (key.isValid()) { + throw new RuntimeException("key is still valid"); + } + } catch (UnsupportedOperationException x) { + } + + // done + dc.close(); + } + + /** + * Tests exceptions for invalid arguments or scenarios + */ + static void exceptionTests(NetworkInterface nif) + throws IOException + { + System.out.println("Exception Tests"); + + DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET) + .setOption(StandardSocketOption.SO_REUSEADDR, true) + .bind(new InetSocketAddress(0)); + + InetAddress group = InetAddress.getByName("225.4.5.6"); + InetAddress notGroup = InetAddress.getByName("1.2.3.4"); + InetAddress thisHost = InetAddress.getLocalHost(); + + // IllegalStateException + MembershipKey key; + key = dc.join(group, nif); + try { + dc.join(group, nif, thisHost); + throw new RuntimeException("IllegalStateException not thrown"); + } catch (IllegalStateException x) { + } catch (UnsupportedOperationException x) { + } + key.drop(); + try { + key = dc.join(group, nif, thisHost); + try { + dc.join(group, nif); + throw new RuntimeException("IllegalStateException not thrown"); + } catch (IllegalStateException x) { + } + key.drop(); + } catch (UnsupportedOperationException x) { + } + + // IllegalArgumentException + try { + dc.join(notGroup, nif); + throw new RuntimeException("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException x) { + } + try { + dc.join(notGroup, nif, thisHost); + throw new RuntimeException("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException x) { + } catch (UnsupportedOperationException x) { + } + + // NullPointerException + try { + dc.join(null, nif); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + dc.join(group, null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + dc.join(group, nif, null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } catch (UnsupportedOperationException x) { + } + + dc.close(); + + // ClosedChannelException + try { + dc.join(group, nif); + throw new RuntimeException("ClosedChannelException not thrown"); + } catch (ClosedChannelException x) { + } + try { + dc.join(group, nif, thisHost); + throw new RuntimeException("ClosedChannelException not thrown"); + } catch (ClosedChannelException x) { + } catch (UnsupportedOperationException x) { + } + } + + + /** + * Probe interfaces to get interfaces that support IPv4 or IPv6 multicasting + * and invoke tests. + */ + public static void main(String[] args) throws IOException { + + // multicast groups used for the test + InetAddress ip4Group = InetAddress.getByName("225.4.5.6"); + InetAddress ip6Group = InetAddress.getByName("ff02::a"); + + + NetworkConfiguration config = NetworkConfiguration.probe(); + + NetworkInterface nif = config.ip4Interfaces().iterator().next(); + InetAddress anySource = config.ip4Addresses(nif).iterator().next(); + membershipKeyTests(nif, ip4Group, anySource); + exceptionTests(nif); + + // re-run the membership key tests with IPv6 if available + + Iterator iter = config.ip6Interfaces().iterator(); + if (iter.hasNext()) { + nif = iter.next(); + anySource = config.ip6Addresses(nif).iterator().next(); + membershipKeyTests(nif, ip6Group, anySource); + } + } +} diff --git a/test/java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java b/test/java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java new file mode 100644 index 000000000..11e3b0be0 --- /dev/null +++ b/test/java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java @@ -0,0 +1,220 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4527345 + * @summary Unit test for DatagramChannel's multicast support + * @build MulticastSendReceiveTests NetworkConfiguration + */ + +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.net.*; +import java.util.*; +import java.io.IOException; + +public class MulticastSendReceiveTests { + + static Random rand = new Random(); + + /** + * Send datagram from given local address to given multicast + * group. + */ + static int sendDatagram(InetAddress local, + NetworkInterface nif, + InetAddress group, + int port) + throws IOException + { + ProtocolFamily family = (group instanceof Inet6Address) ? + StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; + DatagramChannel dc = DatagramChannel.open(family) + .bind(new InetSocketAddress(local, 0)) + .setOption(StandardSocketOption.IP_MULTICAST_IF, nif); + int id = rand.nextInt(); + byte[] msg = Integer.toString(id).getBytes("UTF-8"); + ByteBuffer buf = ByteBuffer.wrap(msg); + System.out.format("Send message from %s -> group %s (id=0x%x)\n", + local.getHostAddress(), group.getHostAddress(), id); + dc.send(buf, new InetSocketAddress(group, port)); + dc.close(); + return id; + } + + /** + * Wait (with timeout) for datagram. + * + * @param expectedSender - expected sender address, or + * null if no datagram expected + * @param id - expected id of datagram + */ + static void receiveDatagram(DatagramChannel dc, + InetAddress expectedSender, + int id) + throws IOException + { + Selector sel = Selector.open(); + dc.configureBlocking(false); + dc.register(sel, SelectionKey.OP_READ); + ByteBuffer buf = ByteBuffer.allocateDirect(100); + + try { + for (;;) { + System.out.println("Waiting to receive message"); + sel.select(5*1000); + SocketAddress sa = dc.receive(buf); + + // no datagram received + if (sa == null) { + if (expectedSender != null) { + throw new RuntimeException("Expected message not recieved"); + } + System.out.println("No message received (correct)"); + return; + } + + // datagram received + + InetAddress sender = ((InetSocketAddress)sa).getAddress(); + buf.flip(); + byte[] bytes = new byte[buf.remaining()]; + buf.get(bytes); + int receivedId = Integer.parseInt(new String(bytes)); + + System.out.format("Received message from %s (id=0x%x)\n", + sender, receivedId); + + if (expectedSender == null) { + if (receivedId == id) + throw new RuntimeException("Message not expected"); + System.out.println("Message ignored (has wrong id)"); + } else { + if (sender.equals(expectedSender)) { + System.out.println("Message expected"); + return; + } + System.out.println("Message ignored (wrong sender)"); + } + + sel.selectedKeys().clear(); + buf.rewind(); + } + } finally { + sel.close(); + } + } + + + /** + * Exercise multicast send/receive on given group/interface + */ + static void test(NetworkInterface nif, InetAddress group, InetAddress source) + throws IOException + { + ProtocolFamily family = (group instanceof Inet6Address) ? + StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; + System.out.format("create channel to %s socket\n", family.name()); + DatagramChannel dc = DatagramChannel.open(family) + .setOption(StandardSocketOption.SO_REUSEADDR, true) + .bind(new InetSocketAddress(0)); + + // join group + System.out.format("join %s @ %s\n", group.getHostAddress(), + nif.getName()); + MembershipKey key = dc.join(group, nif); + + // send message to group + int port = ((InetSocketAddress)dc.getLocalAddress()).getPort(); + int id = sendDatagram(source, nif, group, port); + + // receive message and check id matches + receiveDatagram(dc, source, id); + + // exclude-mode filtering + + try { + System.out.format("block %s\n", source.getHostAddress()); + + // may throw UOE + key.block(source); + id = sendDatagram(source, nif, group, port); + receiveDatagram(dc, null, id); + + // unblock source, send message, message should be received + System.out.format("unblock %s\n", source.getHostAddress()); + key.unblock(source); + id = sendDatagram(source, nif, group, port); + receiveDatagram(dc, source, id); + } catch (UnsupportedOperationException x) { + System.out.println("Exclude-mode filtering not supported!"); + } + + key.drop(); + + // include-mode filtering + + InetAddress bogus = (group instanceof Inet6Address) ? + InetAddress.getByName("fe80::1234") : + InetAddress.getByName("1.2.3.4"); + System.out.format("join %s @ %s only-source %s\n", group.getHostAddress(), + nif.getName(), bogus.getHostAddress()); + try { + // may throw UOE + key = dc.join(group, nif, bogus); + + id = sendDatagram(source, nif, group, port); + receiveDatagram(dc, null, id); + + System.out.format("join %s @ %s only-source %s\n", group.getHostAddress(), + nif.getName(), source.getHostAddress()); + key = dc.join(group, nif, source); + + id = sendDatagram(source, nif, group, port); + receiveDatagram(dc, source, id); + } catch (UnsupportedOperationException x) { + System.out.println("Include-mode filtering not supported!"); + } + + // done + dc.close(); + } + + public static void main(String[] args) throws IOException { + NetworkConfiguration config = NetworkConfiguration.probe(); + + // multicast groups used for the test + InetAddress ip4Group = InetAddress.getByName("225.4.5.6"); + InetAddress ip6Group = InetAddress.getByName("ff02::a"); + + for (NetworkInterface nif: config.ip4Interfaces()) { + InetAddress source = config.ip4Addresses(nif).iterator().next(); + test(nif, ip4Group, source); + } + + for (NetworkInterface nif: config.ip6Interfaces()) { + InetAddress source = config.ip6Addresses(nif).iterator().next(); + test(nif, ip6Group, source); + } + } +} diff --git a/test/java/nio/channels/DatagramChannel/NetworkConfiguration.java b/test/java/nio/channels/DatagramChannel/NetworkConfiguration.java new file mode 100644 index 000000000..f1d7d5deb --- /dev/null +++ b/test/java/nio/channels/DatagramChannel/NetworkConfiguration.java @@ -0,0 +1,97 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.net.*; +import java.util.*; +import java.io.IOException; + +/** + * Helper class for multicasting tests. + */ + +class NetworkConfiguration { + + private Map> ip4Interfaces; + private Map> ip6Interfaces; + + private NetworkConfiguration(Map> ip4Interfaces, + Map> ip6Interfaces) + { + this.ip4Interfaces = ip4Interfaces; + this.ip6Interfaces = ip6Interfaces; + } + + Iterable ip4Interfaces() { + return ip4Interfaces.keySet(); + } + + Iterable ip6Interfaces() { + return ip6Interfaces.keySet(); + } + + Iterable ip4Addresses(NetworkInterface nif) { + return ip4Interfaces.get(nif); + } + + Iterable ip6Addresses(NetworkInterface nif) { + return ip6Interfaces.get(nif); + } + + static NetworkConfiguration probe() throws IOException { + Map> ip4Interfaces = + new HashMap>(); + Map> ip6Interfaces = + new HashMap>(); + + // find the interfaces that support IPv4 and IPv6 + List nifs = Collections + .list(NetworkInterface.getNetworkInterfaces()); + for (NetworkInterface nif: nifs) { + // ignore intertaces that are down or don't support multicast + if (!nif.isUp() || !nif.supportsMulticast() || nif.isLoopback()) + continue; + + List addrs = Collections.list(nif.getInetAddresses()); + for (InetAddress addr: addrs) { + if (addr instanceof Inet4Address) { + List list = ip4Interfaces.get(nif); + if (list == null) { + list = new LinkedList(); + } + list.add(addr); + ip4Interfaces.put(nif, list); + } + if (addr instanceof Inet6Address) { + List list = ip6Interfaces.get(nif); + if (list == null) { + list = new LinkedList(); + } + list.add(addr); + ip6Interfaces.put(nif, list); + + } + } + } + return new NetworkConfiguration(ip4Interfaces, ip6Interfaces); + } +} diff --git a/test/java/nio/channels/DatagramChannel/SocketOptionTests.java b/test/java/nio/channels/DatagramChannel/SocketOptionTests.java new file mode 100644 index 000000000..e4e85b11f --- /dev/null +++ b/test/java/nio/channels/DatagramChannel/SocketOptionTests.java @@ -0,0 +1,115 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4640544 + * @summary Unit test for setOption/getOption/options methods + */ + +import java.nio.*; +import java.nio.channels.*; +import java.net.*; +import java.io.IOException; +import java.util.*; +import static java.net.StandardSocketOption.*; + +public class SocketOptionTests { + + static void checkOption(DatagramChannel dc, + SocketOption name, + T expectedValue) + throws IOException + { + T value = dc.getOption(name); + if (!value.equals(expectedValue)) + throw new RuntimeException("value not as expected"); + } + + public static void main(String[] args) throws IOException { + DatagramChannel dc = DatagramChannel.open(); + + // check supported options + Set> options = dc.options(); + List> expected = Arrays.asList(SO_SNDBUF, SO_RCVBUF, + SO_REUSEADDR, SO_BROADCAST, IP_TOS, IP_MULTICAST_IF, IP_MULTICAST_TTL, + IP_MULTICAST_LOOP); + for (SocketOption opt: expected) { + if (!options.contains(opt)) + throw new RuntimeException(opt.name() + " should be supported"); + } + + // check specified defaults + checkOption(dc, SO_BROADCAST, false); + checkOption(dc, IP_MULTICAST_TTL, 1); // true on supported platforms + checkOption(dc, IP_MULTICAST_LOOP, true); // true on supported platforms + + // allowed to change when not bound + dc.setOption(SO_BROADCAST, true); + checkOption(dc, SO_BROADCAST, true); + dc.setOption(SO_BROADCAST, false); + checkOption(dc, SO_BROADCAST, false); + dc.setOption(SO_SNDBUF, 16*1024); // can't check + dc.setOption(SO_RCVBUF, 16*1024); // can't check + dc.setOption(SO_REUSEADDR, true); + checkOption(dc, SO_REUSEADDR, true); + dc.setOption(SO_REUSEADDR, false); + checkOption(dc, SO_REUSEADDR, false); + + // bind socket + dc.bind(new InetSocketAddress(0)); + + // allow to change when bound + dc.setOption(SO_BROADCAST, true); + checkOption(dc, SO_BROADCAST, true); + dc.setOption(SO_BROADCAST, false); + checkOption(dc, SO_BROADCAST, false); + dc.setOption(IP_TOS, 0x08); // can't check + dc.setOption(IP_MULTICAST_TTL, 2); + checkOption(dc, IP_MULTICAST_TTL, 2); + dc.setOption(IP_MULTICAST_LOOP, false); + checkOption(dc, IP_MULTICAST_LOOP, false); + dc.setOption(IP_MULTICAST_LOOP, true); + checkOption(dc, IP_MULTICAST_LOOP, true); + + + // NullPointerException + try { + dc.setOption(null, "value"); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + dc.getOption(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + + // ClosedChannelException + dc.close(); + try { + dc.setOption(IP_MULTICAST_LOOP, true); + throw new RuntimeException("ClosedChannelException not thrown"); + } catch (ClosedChannelException x) { + } + } +} diff --git a/test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java b/test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java new file mode 100644 index 000000000..6c4a443ed --- /dev/null +++ b/test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java @@ -0,0 +1,84 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4640544 + * @summary Unit test for ServerSocketChannel setOption/getOption/options + * methods. + */ + +import java.nio.*; +import java.nio.channels.*; +import java.net.*; +import java.io.IOException; +import java.util.*; +import static java.net.StandardSocketOption.*; + +public class SocketOptionTests { + + static void checkOption(ServerSocketChannel ssc, SocketOption name, Object expectedValue) + throws IOException + { + Object value = ssc.getOption(name); + if (!value.equals(expectedValue)) + throw new RuntimeException("value not as expected"); + } + + public static void main(String[] args) throws IOException { + ServerSocketChannel ssc = ServerSocketChannel.open(); + + // check supported options + Set> options = ssc.options(); + if (!options.contains(SO_REUSEADDR)) + throw new RuntimeException("SO_REUSEADDR should be supported"); + if (!options.contains(SO_RCVBUF)) + throw new RuntimeException("SO_RCVBUF should be supported"); + + // allowed to change when not bound + ssc.setOption(SO_RCVBUF, 256*1024); // can't check + ssc.setOption(SO_REUSEADDR, true); + checkOption(ssc, SO_REUSEADDR, true); + ssc.setOption(SO_REUSEADDR, false); + checkOption(ssc, SO_REUSEADDR, false); + + // NullPointerException + try { + ssc.setOption(null, "value"); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + ssc.getOption(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + + // ClosedChannelException + ssc.close(); + try { + ssc.setOption(SO_REUSEADDR, true); + throw new RuntimeException("ClosedChannelException not thrown"); + } catch (ClosedChannelException x) { + } + } +} diff --git a/test/java/nio/channels/SocketChannel/SocketOptionTests.java b/test/java/nio/channels/SocketChannel/SocketOptionTests.java new file mode 100644 index 000000000..b6fadced8 --- /dev/null +++ b/test/java/nio/channels/SocketChannel/SocketOptionTests.java @@ -0,0 +1,129 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4640544 + * @summary Unit test to check SocketChannel setOption/getOption/options + * methods. + */ + +import java.nio.*; +import java.nio.channels.*; +import java.net.*; +import java.io.IOException; +import java.util.*; +import static java.net.StandardSocketOption.*; + +public class SocketOptionTests { + + static void checkOption(SocketChannel sc, SocketOption name, Object expectedValue) + throws IOException + { + Object value = sc.getOption(name); + if (!value.equals(expectedValue)) + throw new RuntimeException("value not as expected"); + } + + public static void main(String[] args) throws IOException { + SocketChannel sc = SocketChannel.open(); + + // check supported options + Set> options = sc.options(); + List expected = Arrays.asList(SO_SNDBUF, SO_RCVBUF, + SO_KEEPALIVE, SO_REUSEADDR, SO_LINGER, TCP_NODELAY); + for (SocketOption opt: expected) { + if (!options.contains(opt)) + throw new RuntimeException(opt.name() + " should be supported"); + } + + // check specified defaults + int linger = sc.getOption(SO_LINGER); + if (linger >= 0) + throw new RuntimeException("initial value of SO_LINGER should be < 0"); + checkOption(sc, SO_KEEPALIVE, false); + checkOption(sc, TCP_NODELAY, false); + + // allowed to change when not bound + sc.setOption(SO_KEEPALIVE, true); + checkOption(sc, SO_KEEPALIVE, true); + sc.setOption(SO_KEEPALIVE, false); + checkOption(sc, SO_KEEPALIVE, false); + sc.setOption(SO_SNDBUF, 128*1024); // can't check + sc.setOption(SO_RCVBUF, 256*1024); // can't check + sc.setOption(SO_REUSEADDR, true); + checkOption(sc, SO_REUSEADDR, true); + sc.setOption(SO_REUSEADDR, false); + checkOption(sc, SO_REUSEADDR, false); + sc.setOption(SO_LINGER, 10); + linger = sc.getOption(SO_LINGER); + if (linger < 1) + throw new RuntimeException("expected linger to be enabled"); + sc.setOption(SO_LINGER, -1); + linger = sc.getOption(SO_LINGER); + if (linger >= 0) + throw new RuntimeException("expected linger to be disabled"); + sc.setOption(TCP_NODELAY, true); + checkOption(sc, TCP_NODELAY, true); + sc.setOption(TCP_NODELAY, false); // can't check + + // bind socket + sc.bind(new InetSocketAddress(0)); + + // allow to change when bound + sc.setOption(SO_KEEPALIVE, true); + checkOption(sc, SO_KEEPALIVE, true); + sc.setOption(SO_KEEPALIVE, false); + checkOption(sc, SO_KEEPALIVE, false); + + sc.setOption(SO_LINGER, 10); + linger = sc.getOption(SO_LINGER); + if (linger < 1) + throw new RuntimeException("expected linger to be enabled"); + sc.setOption(SO_LINGER, -1); + linger = sc.getOption(SO_LINGER); + if (linger >= 0) + throw new RuntimeException("expected linger to be disabled"); + sc.setOption(TCP_NODELAY, true); // can't check + sc.setOption(TCP_NODELAY, false); // can't check + + // NullPointerException + try { + sc.setOption(null, "value"); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + sc.getOption(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + + // ClosedChannelException + sc.close(); + try { + sc.setOption(TCP_NODELAY, true); + throw new RuntimeException("ClosedChannelException not thrown"); + } catch (ClosedChannelException x) { + } + } +} diff --git a/test/java/nio/channels/TestUtil.java b/test/java/nio/channels/TestUtil.java index 63e8537ea..8a8449e00 100644 --- a/test/java/nio/channels/TestUtil.java +++ b/test/java/nio/channels/TestUtil.java @@ -38,7 +38,7 @@ public class TestUtil { // executing in a different network. public static final String HOST = "javaweb.sfbay.sun.com"; public static final String REFUSING_HOST = "jano1.sfbay.sun.com"; - public static final String FAR_HOST = "theclub.ireland.sun.com"; + public static final String FAR_HOST = "irejano.ireland.sun.com"; public static final String UNRESOLVABLE_HOST = "blah-blah.blah-blah.blah"; private TestUtil() { } @@ -102,5 +102,4 @@ public class TestUtil { static boolean onWindows() { return osName.startsWith("Windows"); } - } diff --git a/test/java/nio/channels/etc/NetworkChannelTests.java b/test/java/nio/channels/etc/NetworkChannelTests.java new file mode 100644 index 000000000..5f03453ca --- /dev/null +++ b/test/java/nio/channels/etc/NetworkChannelTests.java @@ -0,0 +1,187 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4640544 + * @summary Unit test for channels that implement NetworkChannel + */ + +import java.nio.*; +import java.nio.channels.*; +import java.net.*; +import java.io.IOException; +import java.util.*; + +public class NetworkChannelTests { + + static interface ChannelFactory { + NetworkChannel open() throws IOException; + } + + static class BogusSocketAddress extends SocketAddress { + } + + /** + * Exercise bind method. + */ + static void bindTests(ChannelFactory factory) throws IOException { + NetworkChannel ch; + + // AlreadyBoundException + ch = factory.open().bind(new InetSocketAddress(0)); + try { + ch.bind(new InetSocketAddress(0)); + throw new RuntimeException("AlreadyBoundException not thrown"); + } catch (AlreadyBoundException x) { + } + ch.close(); + + // bind(null) + ch = factory.open().bind(null); + if (ch.getLocalAddress() == null) + throw new RuntimeException("socket not found"); + ch.close(); + + // UnsupportedAddressTypeException + ch = factory.open(); + try { + ch.bind(new BogusSocketAddress()); + throw new RuntimeException("UnsupportedAddressTypeException not thrown"); + } catch (UnsupportedAddressTypeException x) { + } + ch.close(); + + // ClosedChannelException + try { + ch.bind(new InetSocketAddress(0)); + throw new RuntimeException("ClosedChannelException not thrown"); + } catch (ClosedChannelException x) { + } + } + + /** + * Exercise getLocalAddress method. + */ + static void localAddressTests(ChannelFactory factory) throws IOException { + NetworkChannel ch; + + // not bound + ch = factory.open(); + if (ch.getLocalAddress() != null) { + throw new RuntimeException("Local address returned when not bound"); + } + + // bound + InetSocketAddress local = + (InetSocketAddress)(ch.bind(new InetSocketAddress(0)).getLocalAddress()); + if (!local.getAddress().isAnyLocalAddress()) { + if (NetworkInterface.getByInetAddress(local.getAddress()) == null) + throw new RuntimeException("not bound to local address"); + } + if (local.getPort() <= 0) + throw new RuntimeException("not bound to local port"); + + // closed + ch.close(); + if (ch.getLocalAddress() != null) { + throw new RuntimeException("Local address return when closed"); + } + } + + /** + * Exercise getConnectedAddress method (SocketChannel only) + */ + static void connectedAddressTests() throws IOException { + ServerSocketChannel ssc = ServerSocketChannel.open() + .bind(new InetSocketAddress(0)); + InetSocketAddress local = (InetSocketAddress)(ssc.getLocalAddress()); + int port = local.getPort(); + InetSocketAddress server = new InetSocketAddress(InetAddress.getLocalHost(), port); + + SocketChannel sc = SocketChannel.open(); + + // not connected + if (sc.getConnectedAddress() != null) + throw new RuntimeException("getConnectedAddress returned address when not connected"); + + // connected + sc.connect(server); + SocketAddress remote = sc.getConnectedAddress(); + if (!remote.equals(server)) + throw new RuntimeException("getConnectedAddress returned incorrect address"); + + // closed + sc.close(); + if (sc.getConnectedAddress() != null) + throw new RuntimeException("getConnectedAddress returned address when closed"); + + ssc.close(); + } + + public static void main(String[] args) throws IOException { + ChannelFactory factory; + + // -- SocketChannel -- + + factory = new ChannelFactory() { + public NetworkChannel open() throws IOException { + return SocketChannel.open(); + } + }; + + bindTests(factory); + localAddressTests(factory); + connectedAddressTests(); + + // -- ServerSocketChannel -- + + factory = new ChannelFactory() { + public NetworkChannel open() throws IOException { + return ServerSocketChannel.open(); + } + }; + + bindTests(factory); + localAddressTests(factory); + + // backlog values + ServerSocketChannel.open() + .bind(new InetSocketAddress(0), 100).close(); + ServerSocketChannel.open() + .bind(new InetSocketAddress(0), 0).close(); + ServerSocketChannel.open() + .bind(new InetSocketAddress(0), -1).close(); + + // -- DatagramChannel -- + + factory = new ChannelFactory() { + public NetworkChannel open() throws IOException { + return DatagramChannel.open(); + } + }; + + bindTests(factory); + localAddressTests(factory); + } + +} -- GitLab From 8345e0723908933a8460f3981ff3ad4a033e491f Mon Sep 17 00:00:00 2001 From: peterz Date: Mon, 1 Sep 2008 15:21:46 +0400 Subject: [PATCH 080/139] 5062055: JEditorPane HTML: HR-tag with attribute size=1px causes NumberFormatException Summary: Wrapped parseInt() with try/catch Reviewed-by: gsm --- .../javax/swing/text/html/HRuleView.java | 11 ++-- .../text/html/HRuleView/Test5062055.java | 53 +++++++++++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 test/javax/swing/text/html/HRuleView/Test5062055.java diff --git a/src/share/classes/javax/swing/text/html/HRuleView.java b/src/share/classes/javax/swing/text/html/HRuleView.java index d345b348d..26303a596 100644 --- a/src/share/classes/javax/swing/text/html/HRuleView.java +++ b/src/share/classes/javax/swing/text/html/HRuleView.java @@ -67,13 +67,18 @@ class HRuleView extends View { // use ALIGN_CENTER by default, so we check if the alignment // attribute is actually defined if (attr.getAttribute(StyleConstants.Alignment) != null) { - alignment = StyleConstants.getAlignment(attr); + alignment = StyleConstants.getAlignment(attr); } noshade = (String)eAttr.getAttribute(HTML.Attribute.NOSHADE); Object value = eAttr.getAttribute(HTML.Attribute.SIZE); - if (value != null && (value instanceof String)) - size = Integer.parseInt((String)value); + if (value != null && (value instanceof String)) { + try { + size = Integer.parseInt((String)value); + } catch (NumberFormatException e) { + size = 1; + } + } value = attr.getAttribute(CSS.Attribute.WIDTH); if (value != null && (value instanceof CSS.LengthValue)) { widthValue = (CSS.LengthValue)value; diff --git a/test/javax/swing/text/html/HRuleView/Test5062055.java b/test/javax/swing/text/html/HRuleView/Test5062055.java new file mode 100644 index 000000000..4fe2dbbcc --- /dev/null +++ b/test/javax/swing/text/html/HRuleView/Test5062055.java @@ -0,0 +1,53 @@ +/* + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + @bug 5062055 + @summary Tests parsing of incorrect HR attributes + @author Peter Zhelezniakov + @run main Test5062055 +*/ + +import java.awt.Dimension; +import javax.swing.*; + +public class Test5062055 implements Runnable +{ + public static void main(String argv[]) { + SwingUtilities.invokeLater(new Test5062055()); + // give HTML time to be parsed + try { + Thread.sleep(5000); + } catch (InterruptedException ex) { + throw new Error("Wait interrupted"); + } + } + + public void run() { + JEditorPane jep = new JEditorPane(); + jep.setContentType("text/html"); + jep.setEditable(false); + jep.setText("
      "); + jep.setPreferredSize(new Dimension(640,480)); + } +} -- GitLab From 7f86cd1d24418d1244ed4e9b45d1f2c9eebd8914 Mon Sep 17 00:00:00 2001 From: malenkov Date: Mon, 1 Sep 2008 17:36:57 +0400 Subject: [PATCH 081/139] 5026703: RFE: DOC: Are PropertyChangeSupport & VetoableChangeSupport Thread-Safe? --Docs Should Say Reviewed-by: peterz, rupashka --- .../java/beans/PropertyChangeSupport.java | 41 ++++++++++++++++++- .../java/beans/VetoableChangeSupport.java | 41 ++++++++++++++++++- 2 files changed, 78 insertions(+), 4 deletions(-) diff --git a/src/share/classes/java/beans/PropertyChangeSupport.java b/src/share/classes/java/beans/PropertyChangeSupport.java index 2a3f79ff3..04b510ae2 100644 --- a/src/share/classes/java/beans/PropertyChangeSupport.java +++ b/src/share/classes/java/beans/PropertyChangeSupport.java @@ -34,12 +34,49 @@ import java.util.Map.Entry; /** * This is a utility class that can be used by beans that support bound - * properties. You can use an instance of this class as a member field - * of your bean and delegate various work to it. + * properties. It manages a list of listeners and dispatches + * {@link PropertyChangeEvent}s to them. You can use an instance of this class + * as a member field of your bean and delegate these types of work to it. + * The {@link PropertyChangeListener} can be registered for all properties + * or for a property specified by name. + *

      + * Here is an example of {@code PropertyChangeSupport} usage that follows + * the rules and recommendations laid out in the JavaBeans™ specification: + *

      + * public class MyBean {
      + *     private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
        *
      + *     public void addPropertyChangeListener(PropertyChangeListener listener) {
      + *         this.pcs.addPropertyChangeListener(listener);
      + *     }
      + *
      + *     public void removePropertyChangeListener(PropertyChangeListener listener) {
      + *         this.pcs.removePropertyChangeListener(listener);
      + *     }
      + *
      + *     private String value;
      + *
      + *     public String getValue() {
      + *         return this.value;
      + *     }
      + *
      + *     public void setValue(String newValue) {
      + *         String oldValue = this.value;
      + *         this.value = newValue;
      + *         this.pcs.firePropertyChange("value", oldValue, newValue);
      + *     }
      + *
      + *     [...]
      + * }
      + * 
      + *

      + * A {@code PropertyChangeSupport} instance is thread-safe. + *

      * This class is serializable. When it is serialized it will save * (and restore) any listeners that are themselves serializable. Any * non-serializable listeners will be skipped during serialization. + * + * @see VetoableChangeSupport */ public class PropertyChangeSupport implements Serializable { private PropertyChangeListenerMap map = new PropertyChangeListenerMap(); diff --git a/src/share/classes/java/beans/VetoableChangeSupport.java b/src/share/classes/java/beans/VetoableChangeSupport.java index f6707b49f..addf68b36 100644 --- a/src/share/classes/java/beans/VetoableChangeSupport.java +++ b/src/share/classes/java/beans/VetoableChangeSupport.java @@ -34,12 +34,49 @@ import java.util.Map.Entry; /** * This is a utility class that can be used by beans that support constrained - * properties. You can use an instance of this class as a member field - * of your bean and delegate various work to it. + * properties. It manages a list of listeners and dispatches + * {@link PropertyChangeEvent}s to them. You can use an instance of this class + * as a member field of your bean and delegate these types of work to it. + * The {@link VetoableChangeListener} can be registered for all properties + * or for a property specified by name. + *

      + * Here is an example of {@code VetoableChangeSupport} usage that follows + * the rules and recommendations laid out in the JavaBeans™ specification: + *

      + * public class MyBean {
      + *     private final VetoableChangeSupport vcs = new VetoableChangeSupport(this);
        *
      + *     public void addVetoableChangeListener(VetoableChangeListener listener) {
      + *         this.vcs.addVetoableChangeListener(listener);
      + *     }
      + *
      + *     public void removeVetoableChangeListener(VetoableChangeListener listener) {
      + *         this.vcs.removeVetoableChangeListener(listener);
      + *     }
      + *
      + *     private String value;
      + *
      + *     public String getValue() {
      + *         return this.value;
      + *     }
      + *
      + *     public void setValue(String newValue) throws PropertyVetoException {
      + *         String oldValue = this.value;
      + *         this.vcs.fireVetoableChange("value", oldValue, newValue);
      + *         this.value = newValue;
      + *     }
      + *
      + *     [...]
      + * }
      + * 
      + *

      + * A {@code VetoableChangeSupport} instance is thread-safe. + *

      * This class is serializable. When it is serialized it will save * (and restore) any listeners that are themselves serializable. Any * non-serializable listeners will be skipped during serialization. + * + * @see PropertyChangeSupport */ public class VetoableChangeSupport implements Serializable { private VetoableChangeListenerMap map = new VetoableChangeListenerMap(); -- GitLab From a41e60db7a84d1ff78baf0749dcd8feee3a4aef9 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Mon, 1 Sep 2008 17:11:58 +0200 Subject: [PATCH 082/139] 6731410: JMXServiceURL cannot use @ConstructorProperties for compatibility reasons Reviewed-by: dfuchs --- .../classes/javax/management/MXBean.java | 4 +- .../management/remote/JMXServiceURL.java | 48 +++++++- .../management/mxbean/JMXServiceURLTest.java | 115 +++++++++++++++++- 3 files changed, 162 insertions(+), 5 deletions(-) diff --git a/src/share/classes/javax/management/MXBean.java b/src/share/classes/javax/management/MXBean.java index 1773328dd..f6691ccae 100644 --- a/src/share/classes/javax/management/MXBean.java +++ b/src/share/classes/javax/management/MXBean.java @@ -469,8 +469,8 @@ public class MemoryPool J, then J cannot be the type of a method parameter or return value in an MXBean interface.

      -

      If there is a way to convert opendata(J) back to - J then we say that J is +

      If there is a way to convert + opendata(J) back to J then we say that J is reconstructible. All method parameters in an MXBean interface must be reconstructible, because when the MXBean framework is invoking a method it will need to convert those diff --git a/src/share/classes/javax/management/remote/JMXServiceURL.java b/src/share/classes/javax/management/remote/JMXServiceURL.java index f2b19c5ef..06b3b92f9 100644 --- a/src/share/classes/javax/management/remote/JMXServiceURL.java +++ b/src/share/classes/javax/management/remote/JMXServiceURL.java @@ -30,13 +30,14 @@ package javax.management.remote; import com.sun.jmx.remote.util.ClassLogger; import com.sun.jmx.remote.util.EnvHelp; -import java.beans.ConstructorProperties; import java.io.Serializable; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.UnknownHostException; import java.util.BitSet; import java.util.StringTokenizer; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.InvalidKeyException; /** *

      The address of a JMX API connector server. Instances of this class @@ -273,7 +274,6 @@ public class JMXServiceURL implements Serializable { * is not possible to find the local host name, or if * port is negative. */ - @ConstructorProperties({"protocol", "host", "port", "URLPath"}) public JMXServiceURL(String protocol, String host, int port, String urlPath) throws MalformedURLException { @@ -338,6 +338,50 @@ public class JMXServiceURL implements Serializable { validate(); } + /** + *

      Construct a {@code JMXServiceURL} instance from the given + * {@code CompositeData}. The presence of this method means that + * {@code JMXServiceURL} is + * reconstructible in MXBeans.

      + * + *

      (The effect of this method could have been obtained more simply + * with a @{@link java.beans.ConstructorProperties ConstructorProperties} + * annotation on the four-parameter {@linkplain #JMXServiceURL( + * String, String, int, String) constructor}, but that would have meant + * that this API could not be implemented on versions of the Java platform + * that predated the introduction of that annotation.)

      + * + * @param cd a {@code CompositeData} object that must contain items called + * {@code protocol}, {@code host}, and {@code URLPath} of type {@code String} + * and {@code port} of type {@code Integer}. Such an object will be produced + * by the MXBean framework when mapping a {@code JMXServiceURL} + * instance to an Open Data value. + * + * @return a {@code JMXServiceURL} constructed with the protocol, host, + * port, and URL path extracted from the given {@code CompositeData}. + * + * @throws MalformedURLException if the given {@code CompositeData} does + * not contain all the required items with the required types or if the + * resultant URL is syntactically incorrect. + */ + public static JMXServiceURL from(CompositeData cd) + throws MalformedURLException { + try { + String proto = (String) cd.get("protocol"); + String host = (String) cd.get("host"); + int port = (Integer) cd.get("port"); + String urlPath = (String) cd.get("URLPath"); + return new JMXServiceURL(proto, host, port, urlPath); + } catch (RuntimeException e) { + // Could be InvalidKeyException if the item is missing, + // or ClassCastException if it is present but with the wrong type. + MalformedURLException x = new MalformedURLException(e.getMessage()); + x.initCause(e); + throw x; + } + } + private void validate() throws MalformedURLException { // Check protocol diff --git a/test/javax/management/mxbean/JMXServiceURLTest.java b/test/javax/management/mxbean/JMXServiceURLTest.java index 12646cf7c..a688ee701 100644 --- a/test/javax/management/mxbean/JMXServiceURLTest.java +++ b/test/javax/management/mxbean/JMXServiceURLTest.java @@ -23,16 +23,24 @@ /* * @test JMXServiceURLTest - * @bug 6607114 6670375 + * @bug 6607114 6670375 6731410 * @summary Test that JMXServiceURL works correctly in MXBeans * @author Eamonn McManus */ +import java.io.InvalidObjectException; import java.lang.management.ManagementFactory; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.management.Attribute; import javax.management.JMX; +import javax.management.MBeanException; import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; import javax.management.openmbean.CompositeType; import javax.management.openmbean.OpenType; import javax.management.openmbean.SimpleType; @@ -56,6 +64,25 @@ public class JMXServiceURLTest { } } + private static enum Part { + PROTOCOL("protocol", SimpleType.STRING, "rmi", 25, "", "a:b", "/", "?", "#"), + HOST("host", SimpleType.STRING, "a.b.c", 25, "a..b", ".a.b", "a.b."), + PORT("port", SimpleType.INTEGER, 25, "25", -25), + PATH("URLPath", SimpleType.STRING, "/tiddly", 25, "tiddly"); + + Part(String name, OpenType openType, Object validValue, Object... bogusValues) { + this.name = name; + this.openType = openType; + this.validValue = validValue; + this.bogusValues = bogusValues; + } + + final String name; + final OpenType openType; + final Object validValue; + final Object[] bogusValues; + } + public static void main(String[] args) throws Exception { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); ObjectName name = new ObjectName("a:b=c"); @@ -91,6 +118,92 @@ public class JMXServiceURLTest { Object actualValue = cd.get(itemName); assertEquals(expectedValue, actualValue); } + + // Now make sure we reject any bogus-looking CompositeData items. + // We first try every combination of omitted items (items can be + // null but cannot be omitted), then we try every combination of + // valid and bogus items. + final Part[] parts = Part.values(); + final int nParts = parts.length; + final int maxPartMask = (1 << nParts) - 1; + // Iterate over all possibilities of included and omitted, except + // 0, because a CompositeDataSupport must have at least one element, + // and maxPartMask, where all items are included and the result is valid. + for (int mask = 1; mask < maxPartMask; mask++) { + Map cdMap = new HashMap(); + List names = new ArrayList(); + List types = new ArrayList(); + for (int i = 0; i < nParts; i++) { + if ((mask & (1 << i)) != 0) { + Part part = parts[i]; + cdMap.put(part.name, part.validValue); + names.add(part.name); + types.add(openTypeForValue(part.validValue)); + } + } + String[] nameArray = names.toArray(new String[0]); + OpenType[] typeArray = types.toArray(new OpenType[0]); + CompositeType badct = new CompositeType( + "bad", "descr", nameArray, nameArray, typeArray); + CompositeData badcd = new CompositeDataSupport(badct, cdMap); + checkBad(mbs, name, badcd); + } + + int nBogus = 1; + for (Part part : parts) + nBogus *= (part.bogusValues.length + 1); + // Iterate over all combinations of bogus values. We are basically + // treating each Part as a digit while counting up from 1. A digit + // value of 0 stands for the valid value of that Part, and 1 on + // stand for the bogus values. Hence an integer where all the digits + // are 0 would represent a valid CompositeData, which is why we + // start from 1. + for (int bogusCount = 1; bogusCount < nBogus; bogusCount++) { + List names = new ArrayList(); + List types = new ArrayList(); + int x = bogusCount; + Map cdMap = new HashMap(); + for (Part part : parts) { + int digitMax = part.bogusValues.length + 1; + int digit = x % digitMax; + Object value = (digit == 0) ? + part.validValue : part.bogusValues[digit - 1]; + cdMap.put(part.name, value); + names.add(part.name); + types.add(openTypeForValue(value)); + x /= digitMax; + } + String[] nameArray = names.toArray(new String[0]); + OpenType[] typeArray = types.toArray(new OpenType[0]); + CompositeType badct = new CompositeType( + "bad", "descr", nameArray, nameArray, typeArray); + CompositeData badcd = new CompositeDataSupport(badct, cdMap); + checkBad(mbs, name, badcd); + } + } + + private static OpenType openTypeForValue(Object value) { + if (value instanceof String) + return SimpleType.STRING; + else if (value instanceof Integer) + return SimpleType.INTEGER; + else + throw new AssertionError("Value has invalid type: " + value); + } + + private static void checkBad( + MBeanServer mbs, ObjectName name, CompositeData badcd) + throws Exception { + try { + mbs.setAttribute(name, new Attribute("Url", badcd)); + throw new Exception("Expected exception for: " + badcd); + } catch (MBeanException e) { + if (!(e.getCause() instanceof InvalidObjectException)) { + throw new Exception( + "Wrapped exception should be InvalidObjectException", e); + } + System.out.println("OK: rejected " + badcd); + } } private static void assertEquals(Object expect, Object actual) -- GitLab From bcaacfd462545da75b5534cd9b6438d604ebf8b6 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Tue, 2 Sep 2008 14:14:05 +0200 Subject: [PATCH 083/139] 6405862: Allow CompositeType to have zero items 6737133: Compilation failure of test/javax/management/eventService/LeaseManagerDeadlockTest.java 6737140: Javadoc of some throw clauses of MBeanServer and MBeanServerConnection is garbled 6737143: createMBean of MBeanServer should acquire 2 extra throw clauses present in MBeanServerConnection Reviewed-by: dfuchs --- .../classes/javax/management/MBeanServer.java | 8 ++++ .../management/MBeanServerConnection.java | 40 +++++++++---------- .../management/openmbean/CompositeType.java | 6 +-- .../LeaseManagerDeadlockTest.java | 1 + 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/share/classes/javax/management/MBeanServer.java b/src/share/classes/javax/management/MBeanServer.java index a08f64011..613cf09cf 100644 --- a/src/share/classes/javax/management/MBeanServer.java +++ b/src/share/classes/javax/management/MBeanServer.java @@ -264,6 +264,8 @@ public interface MBeanServer extends MBeanServerConnection { * is sent as described above.

      * * @throws RuntimeOperationsException {@inheritDoc} + * @throws RuntimeMBeanException {@inheritDoc} + * @throws RuntimeErrorException {@inheritDoc} */ public ObjectInstance createMBean(String className, ObjectName name) throws ReflectionException, InstanceAlreadyExistsException, @@ -276,6 +278,8 @@ public interface MBeanServer extends MBeanServerConnection { * is sent as described above.

      * * @throws RuntimeOperationsException {@inheritDoc} + * @throws RuntimeMBeanException {@inheritDoc} + * @throws RuntimeErrorException {@inheritDoc} */ public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName) @@ -289,6 +293,8 @@ public interface MBeanServer extends MBeanServerConnection { * is sent as described above.

      * * @throws RuntimeOperationsException {@inheritDoc} + * @throws RuntimeMBeanException {@inheritDoc} + * @throws RuntimeErrorException {@inheritDoc} */ public ObjectInstance createMBean(String className, ObjectName name, Object params[], String signature[]) @@ -302,6 +308,8 @@ public interface MBeanServer extends MBeanServerConnection { * is sent as described above.

      * * @throws RuntimeOperationsException {@inheritDoc} + * @throws RuntimeMBeanException {@inheritDoc} + * @throws RuntimeErrorException {@inheritDoc} */ public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, Object params[], diff --git a/src/share/classes/javax/management/MBeanServerConnection.java b/src/share/classes/javax/management/MBeanServerConnection.java index 779301491..8b0dd6646 100644 --- a/src/share/classes/javax/management/MBeanServerConnection.java +++ b/src/share/classes/javax/management/MBeanServerConnection.java @@ -78,19 +78,19 @@ public interface MBeanServerConnection extends NotificationManager { * MBean will not be registered. * @exception RuntimeMBeanException If the postRegister * (MBeanRegistration interface) method of the MBean throws a - * RuntimeException, the createMBean method will + * RuntimeException, the createMBean method will * throw a RuntimeMBeanException, although the MBean creation * and registration succeeded. In such a case, the MBean will be actually - * registered even though the createMBean method + * registered even though the createMBean method * threw an exception. Note that RuntimeMBeanException can * also be thrown by preRegister, in which case the MBean * will not be registered. * @exception RuntimeErrorException If the postRegister * (MBeanRegistration interface) method of the MBean throws an - * Error, the createMBean method will + * Error, the createMBean method will * throw a RuntimeErrorException, although the MBean creation * and registration succeeded. In such a case, the MBean will be actually - * registered even though the createMBean method + * registered even though the createMBean method * threw an exception. Note that RuntimeErrorException can * also be thrown by preRegister, in which case the MBean * will not be registered. @@ -150,19 +150,19 @@ public interface MBeanServerConnection extends NotificationManager { * MBean will not be registered. * @exception RuntimeMBeanException If the postRegister * (MBeanRegistration interface) method of the MBean throws a - * RuntimeException, the createMBean method will + * RuntimeException, the createMBean method will * throw a RuntimeMBeanException, although the MBean creation * and registration succeeded. In such a case, the MBean will be actually - * registered even though the createMBean method + * registered even though the createMBean method * threw an exception. Note that RuntimeMBeanException can * also be thrown by preRegister, in which case the MBean * will not be registered. * @exception RuntimeErrorException If the postRegister * (MBeanRegistration interface) method of the MBean throws an - * Error, the createMBean method will + * Error, the createMBean method will * throw a RuntimeErrorException, although the MBean creation * and registration succeeded. In such a case, the MBean will be actually - * registered even though the createMBean method + * registered even though the createMBean method * threw an exception. Note that RuntimeErrorException can * also be thrown by preRegister, in which case the MBean * will not be registered. @@ -225,19 +225,19 @@ public interface MBeanServerConnection extends NotificationManager { * MBean will not be registered. * @exception RuntimeMBeanException If the postRegister * (MBeanRegistration interface) method of the MBean throws a - * RuntimeException, the createMBean method will + * RuntimeException, the createMBean method will * throw a RuntimeMBeanException, although the MBean creation * and registration succeeded. In such a case, the MBean will be actually - * registered even though the createMBean method + * registered even though the createMBean method * threw an exception. Note that RuntimeMBeanException can * also be thrown by preRegister, in which case the MBean * will not be registered. * @exception RuntimeErrorException If the postRegister * (MBeanRegistration interface) method of the MBean throws an - * Error, the createMBean method will + * Error, the createMBean method will * throw a RuntimeErrorException, although the MBean creation * and registration succeeded. In such a case, the MBean will be actually - * registered even though the createMBean method + * registered even though the createMBean method * threw an exception. Note that RuntimeErrorException can * also be thrown by preRegister, in which case the MBean * will not be registered. @@ -297,19 +297,19 @@ public interface MBeanServerConnection extends NotificationManager { * MBean will not be registered. * @exception RuntimeMBeanException If the postRegister * (MBeanRegistration interface) method of the MBean throws a - * RuntimeException, the createMBean method will + * RuntimeException, the createMBean method will * throw a RuntimeMBeanException, although the MBean creation * and registration succeeded. In such a case, the MBean will be actually - * registered even though the createMBean method + * registered even though the createMBean method * threw an exception. Note that RuntimeMBeanException can * also be thrown by preRegister, in which case the MBean * will not be registered. * @exception RuntimeErrorException If the postRegister method * (MBeanRegistration interface) method of the MBean throws an - * Error, the createMBean method will + * Error, the createMBean method will * throw a RuntimeErrorException, although the MBean creation * and registration succeeded. In such a case, the MBean will be actually - * registered even though the createMBean method + * registered even though the createMBean method * threw an exception. Note that RuntimeErrorException can * also be thrown by preRegister, in which case the MBean * will not be registered. @@ -351,19 +351,19 @@ public interface MBeanServerConnection extends NotificationManager { * has thrown an exception. * @exception RuntimeMBeanException If the postDeregister * (MBeanRegistration interface) method of the MBean throws a - * RuntimeException, the unregisterMBean method + * RuntimeException, the unregisterMBean method * will throw a RuntimeMBeanException, although the MBean * unregistration succeeded. In such a case, the MBean will be actually - * unregistered even though the unregisterMBean method + * unregistered even though the unregisterMBean method * threw an exception. Note that RuntimeMBeanException can * also be thrown by preDeregister, in which case the MBean * will remain registered. * @exception RuntimeErrorException If the postDeregister * (MBeanRegistration interface) method of the MBean throws an - * Error, the unregisterMBean method will + * Error, the unregisterMBean method will * throw a RuntimeErrorException, although the MBean * unregistration succeeded. In such a case, the MBean will be actually - * unregistered even though the unregisterMBean method + * unregistered even though the unregisterMBean method * threw an exception. Note that RuntimeMBeanException can * also be thrown by preDeregister, in which case the MBean * will remain registered. diff --git a/src/share/classes/javax/management/openmbean/CompositeType.java b/src/share/classes/javax/management/openmbean/CompositeType.java index b513b0faa..b4c662ec9 100644 --- a/src/share/classes/javax/management/openmbean/CompositeType.java +++ b/src/share/classes/javax/management/openmbean/CompositeType.java @@ -89,7 +89,7 @@ public class CompositeType extends OpenType { *
        * @param itemNames The names of the items contained in the * composite data values described by this CompositeType instance; - * cannot be null and should contain at least one element; no element can be a null or empty string. + * cannot be null; no element can be null or an empty string. * Note that the order in which the item names are given is not important to differentiate a * CompositeType instance from another; * the item names are internally stored sorted in ascending alphanumeric order. @@ -97,7 +97,7 @@ public class CompositeType extends OpenType { * @param itemDescriptions The descriptions, in the same order as itemNames, of the items contained in the * composite data values described by this CompositeType instance; * should be of the same size as itemNames; - * no element can be a null or empty string. + * no element can be null or an empty string. *
        * @param itemTypes The open type instances, in the same order as itemNames, describing the items contained * in the composite data values described by this CompositeType instance; @@ -125,7 +125,7 @@ public class CompositeType extends OpenType { // super(CompositeData.class.getName(), typeName, description, false); - // Check the 3 arrays are not null or empty (ie length==0) and that there is no null element or empty string in them + // Check the 3 arrays are not null and that there is no null element or empty string in them // checkForNullElement(itemNames, "itemNames"); checkForNullElement(itemDescriptions, "itemDescriptions"); diff --git a/test/javax/management/eventService/LeaseManagerDeadlockTest.java b/test/javax/management/eventService/LeaseManagerDeadlockTest.java index 586b24402..453aafe4b 100644 --- a/test/javax/management/eventService/LeaseManagerDeadlockTest.java +++ b/test/javax/management/eventService/LeaseManagerDeadlockTest.java @@ -26,6 +26,7 @@ * @bug 6717789 * @summary Check that a lock is not held when a LeaseManager expires. * @author Eamonn McManus + * @compile -XDignore.symbol.file=true LeaseManagerDeadlockTest.java */ import com.sun.jmx.event.LeaseManager; -- GitLab From e1a8df2f2e0c5cb3c7630675d0480cb9ec1fee38 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Wed, 3 Sep 2008 14:31:17 +0200 Subject: [PATCH 084/139] 6744132: Spurious failures from test/javax/management/MBeanInfo/NotificationInfoTest.java Reviewed-by: dfuchs --- .../MBeanInfo/NotificationInfoTest.java | 102 ++++++++---------- 1 file changed, 42 insertions(+), 60 deletions(-) diff --git a/test/javax/management/MBeanInfo/NotificationInfoTest.java b/test/javax/management/MBeanInfo/NotificationInfoTest.java index 2a78e0941..af1cac4e6 100644 --- a/test/javax/management/MBeanInfo/NotificationInfoTest.java +++ b/test/javax/management/MBeanInfo/NotificationInfoTest.java @@ -33,14 +33,16 @@ */ import java.io.*; +import java.lang.management.*; import java.lang.reflect.*; import java.net.*; import java.security.CodeSource; import java.util.*; import java.util.jar.*; import javax.management.*; -import javax.management.modelmbean.*; import javax.management.relation.*; +import javax.management.remote.*; +import javax.management.remote.rmi.*; /* * This test finds all classes in the same code-base as the JMX @@ -68,10 +70,10 @@ import javax.management.relation.*; */ public class NotificationInfoTest { // class or object names where the test failed - private static final Set/**/ failed = new TreeSet(); + private static final Set failed = new TreeSet(); // class or object names where there were no MBeanNotificationInfo entries - private static final Set/**/ suspicious = new TreeSet(); + private static final Set suspicious = new TreeSet(); public static void main(String[] args) throws Exception { System.out.println("Checking that all known MBeans that are " + @@ -86,8 +88,20 @@ public class NotificationInfoTest { .getCodeSource(); URL codeBase; if (cs == null) { - codeBase = new URL("file:" + System.getProperty("java.home") + - "/lib/rt.jar"); + String javaHome = System.getProperty("java.home"); + String[] candidates = {"/lib/rt.jar", "/classes/"}; + codeBase = null; + for (String candidate : candidates) { + File file = new File(javaHome + candidate); + if (file.exists()) { + codeBase = file.toURI().toURL(); + break; + } + } + if (codeBase == null) { + throw new Exception( + "Could not determine codeBase for java.home=" + javaHome); + } } else codeBase = cs.getLocation(); @@ -98,7 +112,7 @@ public class NotificationInfoTest { System.out.println("Testing standard MBeans..."); for (int i = 0; i < classes.length; i++) { String name = classes[i]; - Class c; + Class c; try { c = Class.forName(name); } catch (Throwable e) { @@ -109,18 +123,22 @@ public class NotificationInfoTest { System.out.println(name + ": not a NotificationBroadcaster"); continue; } + if (Modifier.isAbstract(c.getModifiers())) { + System.out.println(name + ": abstract class"); + continue; + } NotificationBroadcaster mbean; - Constructor constr; + Constructor constr; try { - constr = c.getConstructor(null); + constr = c.getConstructor(); } catch (Exception e) { System.out.println(name + ": no public no-arg constructor: " + e); continue; } try { - mbean = (NotificationBroadcaster) constr.newInstance(null); + mbean = (NotificationBroadcaster) constr.newInstance(); } catch (Exception e) { System.out.println(name + ": no-arg constructor failed: " + e); continue; @@ -161,22 +179,9 @@ public class NotificationInfoTest { } private static void checkPlatformMBeans() throws Exception { - Class managementFactory; - try { - managementFactory = - Class.forName("java.lang.management.ManagementFactory"); - } catch (Exception e) { - System.out.println("...no ManagementFactory, assuming pre-Tiger: " - + e); - return; - } - Method getPlatformMBeanServer = - managementFactory.getMethod("getPlatformMBeanServer", null); - MBeanServer mbs = (MBeanServer) - getPlatformMBeanServer.invoke(null, null); - Set mbeanNames = mbs.queryNames(null, null); - for (Iterator it = mbeanNames.iterator(); it.hasNext(); ) { - ObjectName name = (ObjectName) it.next(); + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + Set mbeanNames = mbs.queryNames(null, null); + for (ObjectName name : mbeanNames) { if (!mbs.isInstanceOf(name, NotificationBroadcaster.class.getName())) { System.out.println(name + ": not a NotificationBroadcaster"); @@ -188,31 +193,9 @@ public class NotificationInfoTest { } private static void checkRMIConnectorServer() throws Exception { - Class rmiConnectorServer; - try { - rmiConnectorServer = - Class.forName("javax.management.remote.rmi.RMIConnectorServer"); - } catch (Exception e) { - System.out.println("No RMIConnectorServer class, skipping: " + e); - return; - } - Class jmxServiceURL = - Class.forName("javax.management.remote.JMXServiceURL"); - Constructor jmxServiceURLConstructor = - jmxServiceURL.getConstructor(new Class[] {String.class}); - Object url = - jmxServiceURLConstructor.newInstance(new Object[] { - "service:jmx:rmi://" - }); - Constructor rmiConnectorServerConstructor = - rmiConnectorServer.getConstructor(new Class[] { - jmxServiceURL, Map.class - }); - Object connector = - rmiConnectorServerConstructor.newInstance(new Object[] { - url, null - }); - check((NotificationBroadcaster) connector); + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://"); + RMIConnectorServer connector = new RMIConnectorServer(url, null); + check(connector); } private static void check(String what, MBeanNotificationInfo[] mbnis) { @@ -250,30 +233,29 @@ public class NotificationInfoTest { private static String[] findStandardMBeans(URL codeBase) throws Exception { - Set names; + Set names; if (codeBase.getProtocol().equalsIgnoreCase("file") && codeBase.toString().endsWith("/")) names = findStandardMBeansFromDir(codeBase); else names = findStandardMBeansFromJar(codeBase); - Set standardMBeanNames = new TreeSet(); - for (Iterator it = names.iterator(); it.hasNext(); ) { - String name = (String) it.next(); + Set standardMBeanNames = new TreeSet(); + for (String name : names) { if (name.endsWith("MBean")) { String prefix = name.substring(0, name.length() - 5); if (names.contains(prefix)) standardMBeanNames.add(prefix); } } - return (String[]) standardMBeanNames.toArray(new String[0]); + return standardMBeanNames.toArray(new String[0]); } - private static Set findStandardMBeansFromJar(URL codeBase) + private static Set findStandardMBeansFromJar(URL codeBase) throws Exception { InputStream is = codeBase.openStream(); JarInputStream jis = new JarInputStream(is); - Set names = new TreeSet(); + Set names = new TreeSet(); JarEntry entry; while ((entry = jis.getNextJarEntry()) != null) { String name = entry.getName(); @@ -286,15 +268,15 @@ public class NotificationInfoTest { return names; } - private static Set findStandardMBeansFromDir(URL codeBase) + private static Set findStandardMBeansFromDir(URL codeBase) throws Exception { File dir = new File(new URI(codeBase.toString())); - Set names = new TreeSet(); + Set names = new TreeSet(); scanDir(dir, "", names); return names; } - private static void scanDir(File dir, String prefix, Set names) + private static void scanDir(File dir, String prefix, Set names) throws Exception { File[] files = dir.listFiles(); if (files == null) -- GitLab From 03d6b6f96c4af6dceea6c2bce2b54a12ed979e93 Mon Sep 17 00:00:00 2001 From: malenkov Date: Wed, 3 Sep 2008 21:00:04 +0400 Subject: [PATCH 085/139] 6397609: DOC: De-register API required for PropertyEditorManager and/or doc change Reviewed-by: peterz, rupashka --- .../classes/com/sun/beans/WeakCache.java | 84 ++++++++++ .../java/beans/PropertyEditorManager.java | 70 ++++----- .../PropertyEditor/MemoryClassLoader.java | 143 ++++++++++++++++++ .../beans/PropertyEditor/Test6397609.java | 59 ++++++++ .../java/beans/PropertyEditor/TestEditor.java | 122 ++------------- 5 files changed, 332 insertions(+), 146 deletions(-) create mode 100644 src/share/classes/com/sun/beans/WeakCache.java create mode 100644 test/java/beans/PropertyEditor/MemoryClassLoader.java create mode 100644 test/java/beans/PropertyEditor/Test6397609.java diff --git a/src/share/classes/com/sun/beans/WeakCache.java b/src/share/classes/com/sun/beans/WeakCache.java new file mode 100644 index 000000000..461c48e1f --- /dev/null +++ b/src/share/classes/com/sun/beans/WeakCache.java @@ -0,0 +1,84 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.beans; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; + +import java.util.Map; +import java.util.WeakHashMap; + +/** + * A hashtable-based cache with weak keys and weak values. + * An entry in the map will be automatically removed + * when its key is no longer in the ordinary use. + * A value will be automatically removed as well + * when it is no longer in the ordinary use. + * + * @since 1.7 + * + * @author Sergey A. Malenkov + */ +public final class WeakCache { + private final Map> map = new WeakHashMap>(); + + /** + * Returns a value to which the specified {@code key} is mapped, + * or {@code null} if this map contains no mapping for the {@code key}. + * + * @param key the key whose associated value is returned + * @return a value to which the specified {@code key} is mapped + */ + public V get(K key) { + Reference reference = this.map.get(key); + if (reference == null) { + return null; + } + V value = reference.get(); + if (value == null) { + this.map.remove(key); + } + return value; + } + + /** + * Associates the specified {@code value} with the specified {@code key}. + * Removes the mapping for the specified {@code key} from this cache + * if it is present and the specified {@code value} is {@code null}. + * If the cache previously contained a mapping for the {@code key}, + * the old value is replaced by the specified {@code value}. + * + * @param key the key with which the specified value is associated + * @param value the value to be associated with the specified key + */ + public void put(K key, V value) { + if (value != null) { + this.map.put(key, new WeakReference(value)); + } + else { + this.map.remove(key); + } + } +} diff --git a/src/share/classes/java/beans/PropertyEditorManager.java b/src/share/classes/java/beans/PropertyEditorManager.java index 055df1ad9..a456c2bff 100644 --- a/src/share/classes/java/beans/PropertyEditorManager.java +++ b/src/share/classes/java/beans/PropertyEditorManager.java @@ -1,5 +1,5 @@ /* - * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ package java.beans; +import com.sun.beans.WeakCache; import sun.beans.editors.*; /** @@ -55,32 +56,30 @@ import sun.beans.editors.*; public class PropertyEditorManager { /** - * Register an editor class to be used to edit values of - * a given target class. + * Registers an editor class to edit values of the given target class. + * If the editor class is {@code null}, + * then any existing definition will be removed. + * Thus this method can be used to cancel the registration. + * The registration is canceled automatically + * if either the target or editor class is unloaded. + *

      + * If there is a security manager, its {@code checkPropertiesAccess} + * method is called. This could result in a {@linkplain SecurityException}. * - *

      First, if there is a security manager, its checkPropertiesAccess - * method is called. This could result in a SecurityException. + * @param targetType the class object of the type to be edited + * @param editorClass the class object of the editor class + * @throws SecurityException if a security manager exists and + * its {@code checkPropertiesAccess} method + * doesn't allow setting of system properties * - * @param targetType the Class object of the type to be edited - * @param editorClass the Class object of the editor class. If - * this is null, then any existing definition will be removed. - * @exception SecurityException if a security manager exists and its - * checkPropertiesAccess method doesn't allow setting - * of system properties. * @see SecurityManager#checkPropertiesAccess */ - - public static void registerEditor(Class targetType, Class editorClass) { + public static synchronized void registerEditor(Class targetType, Class editorClass) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPropertiesAccess(); } - initialize(); - if (editorClass == null) { - registry.remove(targetType); - } else { - registry.put(targetType, editorClass); - } + registry.put(targetType, editorClass); } /** @@ -90,10 +89,8 @@ public class PropertyEditorManager { * @return An editor object for the given target class. * The result is null if no suitable editor can be found. */ - public static synchronized PropertyEditor findEditor(Class targetType) { - initialize(); - Class editorClass = (Class)registry.get(targetType); + Class editorClass = registry.get(targetType); if (editorClass != null) { try { Object o = editorClass.newInstance(); @@ -143,10 +140,7 @@ public class PropertyEditorManager { * e.g. Sun implementation initially sets to {"sun.beans.editors"}. */ public static synchronized String[] getEditorSearchPath() { - // Return a copy of the searchPath. - String result[] = new String[searchPath.length]; - System.arraycopy(searchPath, 0, result, 0, searchPath.length); - return result; + return searchPath.clone(); } /** @@ -162,23 +156,22 @@ public class PropertyEditorManager { * of system properties. * @see SecurityManager#checkPropertiesAccess */ - - public static synchronized void setEditorSearchPath(String path[]) { + public static synchronized void setEditorSearchPath(String[] path) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPropertiesAccess(); } - if (path == null) { - path = new String[0]; - } - searchPath = path; + searchPath = (path != null) + ? path.clone() + : EMPTY; } - private static synchronized void initialize() { - if (registry != null) { - return; - } - registry = new java.util.Hashtable(); + private static String[] searchPath = { "sun.beans.editors" }; + private static final String[] EMPTY = {}; + private static final WeakCache, Class> registry; + + static { + registry = new WeakCache, Class>(); registry.put(Byte.TYPE, ByteEditor.class); registry.put(Short.TYPE, ShortEditor.class); registry.put(Integer.TYPE, IntegerEditor.class); @@ -187,7 +180,4 @@ public class PropertyEditorManager { registry.put(Float.TYPE, FloatEditor.class); registry.put(Double.TYPE, DoubleEditor.class); } - - private static String[] searchPath = { "sun.beans.editors" }; - private static java.util.Hashtable registry; } diff --git a/test/java/beans/PropertyEditor/MemoryClassLoader.java b/test/java/beans/PropertyEditor/MemoryClassLoader.java new file mode 100644 index 000000000..cd462f883 --- /dev/null +++ b/test/java/beans/PropertyEditor/MemoryClassLoader.java @@ -0,0 +1,143 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.io.ByteArrayOutputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject.Kind; +import javax.tools.SimpleJavaFileObject; +import javax.tools.ToolProvider; + +public final class MemoryClassLoader extends ClassLoader { + private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + private final MemoryFileManager manager = new MemoryFileManager(this.compiler); + + public Class compile(String name, String content) { + compile(new Source(name, content)); + try { + return findClass(name); + } + catch (ClassNotFoundException exception) { + throw new Error(exception); + } + } + + public void compile(Source... sources) { + List list = new ArrayList(); + if (sources != null) { + for (Source source : sources) { + if (source != null) { + list.add(source); + } + } + } + synchronized (this.manager) { + this.compiler.getTask(null, this.manager, null, null, null, list).call(); + } + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + synchronized (this.manager) { + Output mc = this.manager.map.remove(name); + if (mc != null) { + byte[] array = mc.toByteArray(); + return defineClass(name, array, 0, array.length); + } + } + return super.findClass(name); + } + + private static final class MemoryFileManager extends ForwardingJavaFileManager { + private final Map map = new HashMap(); + + MemoryFileManager(JavaCompiler compiler) { + super(compiler.getStandardFileManager(null, null, null)); + } + + @Override + public Output getJavaFileForOutput(Location location, String name, Kind kind, FileObject source) { + Output mc = this.map.get(name); + if (mc == null) { + mc = new Output(name); + this.map.put(name, mc); + } + return mc; + } + } + + private static class MemoryFileObject extends SimpleJavaFileObject { + MemoryFileObject(String name, Kind kind) { + super(toURI(name, kind.extension), kind); + } + + private static URI toURI(String name, String extension) { + try { + return new URI("mfm:///" + name.replace('.', '/') + extension); + } + catch (URISyntaxException exception) { + throw new Error(exception); + } + } + } + + private static final class Output extends MemoryFileObject { + private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + Output(String name) { + super(name, Kind.CLASS); + } + + byte[] toByteArray() { + return this.baos.toByteArray(); + } + + @Override + public ByteArrayOutputStream openOutputStream() { + this.baos.reset(); + return this.baos; + } + } + + public static final class Source extends MemoryFileObject { + private final String content; + + Source(String name, String content) { + super(name, Kind.SOURCE); + this.content = content; + } + + @Override + public CharSequence getCharContent(boolean ignore) { + return this.content; + } + } +} diff --git a/test/java/beans/PropertyEditor/Test6397609.java b/test/java/beans/PropertyEditor/Test6397609.java new file mode 100644 index 000000000..ac7065e0b --- /dev/null +++ b/test/java/beans/PropertyEditor/Test6397609.java @@ -0,0 +1,59 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6397609 + * @summary Tests autocleaning + * @author Sergey Malenkov + */ + +import java.beans.PropertyEditorManager; + +public class Test6397609 { + public static void main(String[] args) throws Exception { + MemoryClassLoader loader = new MemoryClassLoader(); + PropertyEditorManager.registerEditor( + Object.class, + loader.compile("Editor", + "public class Editor extends java.beans.PropertyEditorSupport {}")); + + if (!isEditorExist(Object.class)) { + throw new Error("the editor is lost"); + } + loader = null; // clean the reference + if (isEditorExist(Object.class)) { + throw new Error("unexpected editor is found"); + } + } + + private static boolean isEditorExist(Class type) { + for (int i = 0; i < 10; i++) { + System.gc(); // clean all weak references + if (null == PropertyEditorManager.findEditor(type)) { + return false; + } + } + return true; + } +} diff --git a/test/java/beans/PropertyEditor/TestEditor.java b/test/java/beans/PropertyEditor/TestEditor.java index 85a9ec338..25c7acf60 100644 --- a/test/java/beans/PropertyEditor/TestEditor.java +++ b/test/java/beans/PropertyEditor/TestEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2006-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,18 +23,6 @@ import java.beans.PropertyEditor; import java.beans.PropertyEditorManager; -import java.io.ByteArrayOutputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import javax.tools.FileObject; -import javax.tools.ForwardingJavaFileManager; -import javax.tools.JavaCompiler; -import javax.tools.JavaFileObject.Kind; -import javax.tools.SimpleJavaFileObject; -import javax.tools.ToolProvider; final class TestEditor { private final PropertyEditor editor; @@ -53,8 +41,7 @@ final class TestEditor { void testJava(Object value) { this.editor.setValue(value); - MemoryFileManager manager = new MemoryFileManager(); - Object object = manager.invoke(this.editor.getJavaInitializationString()); + Object object = execute("Executor", "execute", this.editor.getJavaInitializationString()); System.out.println("Property value before: " + value); System.out.println("Property value after: " + object); @@ -87,98 +74,21 @@ final class TestEditor { : object1.equals(object2); } - private static final class MemoryFileManager extends ForwardingJavaFileManager { - private static final String CLASS = "Executor"; - private static final String METHOD = "execute"; - private static final JavaCompiler COMPILER = ToolProvider.getSystemJavaCompiler(); - private final Map map = new HashMap(); - private final MemoryClassLoader loader = new MemoryClassLoader(); - - MemoryFileManager() { - super(COMPILER.getStandardFileManager(null, null, null)); - } - - public Object invoke(String expression) { - MemorySource file = new MemorySource(CLASS, METHOD, expression); - if (!COMPILER.getTask(null, this, null, null, null, Arrays.asList(file)).call()) - throw new Error("compilation failed"); - - MemoryClass mc = this.map.get(CLASS); - if (mc == null) - throw new Error("class not found: " + CLASS); - - Class c = this.loader.loadClass(CLASS, mc.toByteArray()); - try { - return c.getMethod(METHOD).invoke(null); - } - catch (Exception exception) { - throw new Error(exception); - } - } - - public MemoryClass getJavaFileForOutput(Location location, String name, Kind kind, FileObject source) { - MemoryClass type = this.map.get(name); - if (type == null) { - type = new MemoryClass(name); - this.map.put(name, type); - } - return type; - } - } - - private static final class MemoryClassLoader extends ClassLoader { - public Class loadClass(String name, byte[] array) { - return defineClass(name, array, 0, array.length); - } - } - - private static class MemoryObject extends SimpleJavaFileObject { - protected MemoryObject(String name, Kind kind) { - super(toURI(name, kind.extension), kind); - } - - private static URI toURI(String name, String extension) { - try { - return new URI("mfm:///" + name.replace('.', '/') + extension); - } - catch (URISyntaxException exception) { - throw new Error(exception); - } + private static Object execute(String classname, String methodname, String value) { + String content + = "public class " + classname + " {" + + " public static Object " + methodname + "() throws Exception {" + + " return " + value + ";" + + " }" + + "}"; + + try { + MemoryClassLoader loader = new MemoryClassLoader(); + Class type = loader.compile(classname, content); + return type.getMethod(methodname).invoke(null); } - } - - private static final class MemoryClass extends MemoryObject { - private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - MemoryClass(String className) { - super(className, Kind.CLASS); - } - - public ByteArrayOutputStream openOutputStream() { - this.baos.reset(); - return this.baos; - } - - public byte[] toByteArray() { - return this.baos.toByteArray(); - } - } - - private static final class MemorySource extends MemoryObject { - private final String value; - - MemorySource(String className, String methodName, String expression) { - super(className, Kind.SOURCE); - this.value - = "public class " + className + " {\n" - + " public static Object " + methodName + "() throws Exception {\n" - + " return " + expression + ";\n" - + " }\n" - + "}\n"; - } - - public CharSequence getCharContent(boolean ignore) { - return this.value; + catch (Exception exception) { + throw new Error(exception); } } } -- GitLab From 1b3f183ac94a09bcb8f09a9b5f284c4697864fd4 Mon Sep 17 00:00:00 2001 From: rupashka Date: Thu, 4 Sep 2008 15:15:24 +0400 Subject: [PATCH 086/139] 6278700: JSlider created with BoundedRangeModel fires twice when changed Summary: Removed second registration of listener Reviewed-by: peterz --- src/share/classes/javax/swing/JSlider.java | 20 +++-- .../swing/JSlider/6278700/bug6278700.java | 73 +++++++++++++++++++ 2 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 test/javax/swing/JSlider/6278700/bug6278700.java diff --git a/src/share/classes/javax/swing/JSlider.java b/src/share/classes/javax/swing/JSlider.java index c2941c642..c014f170b 100644 --- a/src/share/classes/javax/swing/JSlider.java +++ b/src/share/classes/javax/swing/JSlider.java @@ -270,8 +270,7 @@ public class JSlider extends JComponent implements SwingConstants, Accessible { { checkOrientation(orientation); this.orientation = orientation; - sliderModel = new DefaultBoundedRangeModel(value, 0, min, max); - sliderModel.addChangeListener(changeListener); + setModel(new DefaultBoundedRangeModel(value, 0, min, max)); updateUI(); } @@ -284,7 +283,6 @@ public class JSlider extends JComponent implements SwingConstants, Accessible { { this.orientation = JSlider.HORIZONTAL; setModel(brm); - sliderModel.addChangeListener(changeListener); updateUI(); } @@ -476,15 +474,15 @@ public class JSlider extends JComponent implements SwingConstants, Accessible { if (newModel != null) { newModel.addChangeListener(changeListener); + } - if (accessibleContext != null) { - accessibleContext.firePropertyChange( - AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, - (oldModel == null - ? null : Integer.valueOf(oldModel.getValue())), - (newModel == null - ? null : Integer.valueOf(newModel.getValue()))); - } + if (accessibleContext != null) { + accessibleContext.firePropertyChange( + AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, + (oldModel == null + ? null : Integer.valueOf(oldModel.getValue())), + (newModel == null + ? null : Integer.valueOf(newModel.getValue()))); } firePropertyChange("model", oldModel, sliderModel); diff --git a/test/javax/swing/JSlider/6278700/bug6278700.java b/test/javax/swing/JSlider/6278700/bug6278700.java new file mode 100644 index 000000000..56d70d4cf --- /dev/null +++ b/test/javax/swing/JSlider/6278700/bug6278700.java @@ -0,0 +1,73 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 6278700 + * @summary JSlider created with BoundedRangeModel fires twice when changed + * @author Pavel Porvatov + @run main bug6278700 + */ + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +public class bug6278700 { + private int changeCount; + + private final ChangeListener listener = new ChangeListener() { + public void stateChanged(ChangeEvent e) { + changeCount++; + } + }; + + public static void main(String[] args) { + new bug6278700(); + } + + public bug6278700() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + JSlider slider = new JSlider(new DefaultBoundedRangeModel(5, 0, 0, 10)); + + slider.addChangeListener(listener); + slider.setValue(0); + + if (changeCount != 1) { + throw new RuntimeException("Incorrect stateChanged count: " + Integer.toString(changeCount)); + } + + changeCount = 0; + + slider = new JSlider(); + + slider.addChangeListener(listener); + slider.setValue(0); + + if (changeCount != 1) { + throw new RuntimeException("Incorrect stateChanged count: " + Integer.toString(changeCount)); + } + } + }); + } +} -- GitLab From ba8c47f68a0f555ec86b5f0b136e70cd5ff2fcb2 Mon Sep 17 00:00:00 2001 From: dfuchs Date: Thu, 4 Sep 2008 14:46:36 +0200 Subject: [PATCH 087/139] 5072476: RFE: support cascaded (federated) MBean Servers 6299231: Add support for named MBean Servers Summary: New javax.management.namespace package. Reviewed-by: emcmanus --- make/docs/CORE_PKGS.gmk | 1 + .../com/sun/jmx/defaults/JmxProperties.java | 12 + .../DefaultMBeanServerInterceptor.java | 319 ++-- .../jmx/interceptor/DispatchInterceptor.java | 547 ++++++ .../DomainDispatchInterceptor.java | 322 ++++ .../interceptor/MBeanServerInterceptor.java | 666 +------- .../MBeanServerInterceptorSupport.java | 127 ++ .../NamespaceDispatchInterceptor.java | 236 +++ .../jmx/interceptor/SingleMBeanForwarder.java | 18 +- .../sun/jmx/mbeanserver/JmxMBeanServer.java | 91 +- .../com/sun/jmx/mbeanserver/MXBeanLookup.java | 12 +- .../com/sun/jmx/mbeanserver/Repository.java | 98 +- .../jmx/mbeanserver/SunJmxMBeanServer.java | 9 +- .../classes/com/sun/jmx/mbeanserver/Util.java | 472 +++++- .../sun/jmx/namespace/DomainInterceptor.java | 475 ++++++ .../sun/jmx/namespace/HandlerInterceptor.java | 577 +++++++ .../sun/jmx/namespace/JMXNamespaceUtils.java | 369 +++++ .../jmx/namespace/NamespaceInterceptor.java | 449 +++++ .../sun/jmx/namespace/ObjectNameRouter.java | 191 +++ .../jmx/namespace/RoutingConnectionProxy.java | 132 ++ .../RoutingMBeanServerConnection.java | 671 ++++++++ .../com/sun/jmx/namespace/RoutingProxy.java | 282 ++++ .../sun/jmx/namespace/RoutingServerProxy.java | 602 +++++++ .../com/sun/jmx/namespace/package.html | 45 + .../serial/DefaultRewritingProcessor.java | 150 ++ .../namespace/serial/IdentityProcessor.java | 74 + .../namespace/serial/JMXNamespaceContext.java | 145 ++ .../namespace/serial/RewritingProcessor.java | 362 ++++ .../serial/RoutingOnlyProcessor.java | 74 + .../serial/SerialRewritingProcessor.java | 172 ++ .../com/sun/jmx/namespace/serial/package.html | 44 + .../remote/internal/ServerNotifForwarder.java | 39 +- .../remote/util/EventClientConnection.java | 32 +- .../management/InstanceNotFoundException.java | 12 + .../javax/management/MBeanPermission.java | 183 +- .../classes/javax/management/MBeanServer.java | 133 +- .../javax/management/MBeanServerDelegate.java | 106 +- .../javax/management/MBeanServerFactory.java | 368 +++- .../classes/javax/management/ObjectName.java | 214 ++- .../javax/management/event/EventClient.java | 19 + .../management/event/EventClientDelegate.java | 20 +- .../javax/management/namespace/JMXDomain.java | 382 +++++ .../management/namespace/JMXNamespace.java | 660 ++++++++ .../namespace/JMXNamespaceMBean.java | 96 ++ .../namespace/JMXNamespacePermission.java | 1474 +++++++++++++++++ .../namespace/JMXNamespaceView.java | 300 ++++ .../management/namespace/JMXNamespaces.java | 374 +++++ .../namespace/JMXRemoteNamespace.java | 837 ++++++++++ .../namespace/JMXRemoteNamespaceMBean.java | 96 ++ .../MBeanServerConnectionWrapper.java | 703 ++++++++ .../namespace}/MBeanServerSupport.java | 14 +- .../namespace/VirtualEventManager.java | 378 +++++ .../management/namespace/package-info.java | 597 +++++++ .../remote/JMXConnectorFactory.java | 34 +- .../remote/rmi/RMIConnectionImpl.java | 47 +- .../NamedMBeanServerTest.java | 440 +++++ .../ObjectName/ApplyWildcardTest.java | 180 ++ .../namespace/DomainCreationTest.java | 329 ++++ .../EventWithNamespaceControlTest.java | 92 + .../namespace/EventWithNamespaceTest.java | 241 +++ .../namespace/ExportNamespaceTest.java | 99 ++ .../management/namespace/JMXDomainTest.java | 512 ++++++ .../namespace/JMXNamespaceSecurityTest.java | 272 +++ .../namespace/JMXNamespaceTest.java | 714 ++++++++ .../namespace/JMXNamespaceViewTest.java | 549 ++++++ .../namespace/JMXNamespacesTest.java | 647 ++++++++ .../namespace/JMXRemoteNamespaceTest.java | 189 +++ .../namespace/JMXRemoteTargetNamespace.java | 222 +++ .../management/namespace/LazyDomainTest.java | 789 +++++++++ .../management/namespace/MXBeanRefTest.java | 181 ++ .../namespace/NamespaceController.java | 405 +++++ .../namespace/NamespaceControllerMBean.java | 143 ++ .../namespace/NamespaceCreationTest.java | 262 +++ .../namespace/NamespaceNotificationsTest.java | 388 +++++ .../namespace/NullDomainObjectNameTest.java | 284 ++++ .../namespace/NullObjectNameTest.java | 251 +++ .../management/namespace/QueryNamesTest.java | 408 +++++ .../RemoveNotificationListenerTest.java | 376 +++++ .../namespace/RoutingServerProxyTest.java | 399 +++++ .../namespace/SerialParamProcessorTest.java | 571 +++++++ .../namespace/SourceNamespaceTest.java | 140 ++ .../namespace/VirtualMBeanNotifTest.java | 568 +++++++ .../namespace/VirtualMBeanTest.java | 409 +++++ .../namespace/VirtualNamespaceQueryTest.java | 127 ++ .../namespace/VirtualPropsTest.java | 179 ++ test/javax/management/namespace/Wombat.java | 254 +++ .../management/namespace/WombatMBean.java | 59 + .../management/namespace/namespace.policy | 85 + 88 files changed, 24525 insertions(+), 1101 deletions(-) create mode 100644 src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java create mode 100644 src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java create mode 100644 src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptorSupport.java create mode 100644 src/share/classes/com/sun/jmx/interceptor/NamespaceDispatchInterceptor.java create mode 100644 src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java create mode 100644 src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java create mode 100644 src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java create mode 100644 src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java create mode 100644 src/share/classes/com/sun/jmx/namespace/ObjectNameRouter.java create mode 100644 src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java create mode 100644 src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java create mode 100644 src/share/classes/com/sun/jmx/namespace/RoutingProxy.java create mode 100644 src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java create mode 100644 src/share/classes/com/sun/jmx/namespace/package.html create mode 100644 src/share/classes/com/sun/jmx/namespace/serial/DefaultRewritingProcessor.java create mode 100644 src/share/classes/com/sun/jmx/namespace/serial/IdentityProcessor.java create mode 100644 src/share/classes/com/sun/jmx/namespace/serial/JMXNamespaceContext.java create mode 100644 src/share/classes/com/sun/jmx/namespace/serial/RewritingProcessor.java create mode 100644 src/share/classes/com/sun/jmx/namespace/serial/RoutingOnlyProcessor.java create mode 100644 src/share/classes/com/sun/jmx/namespace/serial/SerialRewritingProcessor.java create mode 100644 src/share/classes/com/sun/jmx/namespace/serial/package.html create mode 100644 src/share/classes/javax/management/namespace/JMXDomain.java create mode 100644 src/share/classes/javax/management/namespace/JMXNamespace.java create mode 100644 src/share/classes/javax/management/namespace/JMXNamespaceMBean.java create mode 100644 src/share/classes/javax/management/namespace/JMXNamespacePermission.java create mode 100644 src/share/classes/javax/management/namespace/JMXNamespaceView.java create mode 100644 src/share/classes/javax/management/namespace/JMXNamespaces.java create mode 100644 src/share/classes/javax/management/namespace/JMXRemoteNamespace.java create mode 100644 src/share/classes/javax/management/namespace/JMXRemoteNamespaceMBean.java create mode 100644 src/share/classes/javax/management/namespace/MBeanServerConnectionWrapper.java rename src/share/classes/{com/sun/jmx/interceptor => javax/management/namespace}/MBeanServerSupport.java (99%) create mode 100644 src/share/classes/javax/management/namespace/VirtualEventManager.java create mode 100644 src/share/classes/javax/management/namespace/package-info.java create mode 100644 test/javax/management/MBeanServerFactory/NamedMBeanServerTest.java create mode 100644 test/javax/management/namespace/DomainCreationTest.java create mode 100644 test/javax/management/namespace/EventWithNamespaceControlTest.java create mode 100644 test/javax/management/namespace/EventWithNamespaceTest.java create mode 100644 test/javax/management/namespace/ExportNamespaceTest.java create mode 100644 test/javax/management/namespace/JMXDomainTest.java create mode 100644 test/javax/management/namespace/JMXNamespaceSecurityTest.java create mode 100644 test/javax/management/namespace/JMXNamespaceTest.java create mode 100644 test/javax/management/namespace/JMXNamespaceViewTest.java create mode 100644 test/javax/management/namespace/JMXNamespacesTest.java create mode 100644 test/javax/management/namespace/JMXRemoteNamespaceTest.java create mode 100644 test/javax/management/namespace/JMXRemoteTargetNamespace.java create mode 100644 test/javax/management/namespace/LazyDomainTest.java create mode 100644 test/javax/management/namespace/MXBeanRefTest.java create mode 100644 test/javax/management/namespace/NamespaceController.java create mode 100644 test/javax/management/namespace/NamespaceControllerMBean.java create mode 100644 test/javax/management/namespace/NamespaceCreationTest.java create mode 100644 test/javax/management/namespace/NamespaceNotificationsTest.java create mode 100644 test/javax/management/namespace/NullDomainObjectNameTest.java create mode 100644 test/javax/management/namespace/NullObjectNameTest.java create mode 100644 test/javax/management/namespace/QueryNamesTest.java create mode 100644 test/javax/management/namespace/RemoveNotificationListenerTest.java create mode 100644 test/javax/management/namespace/RoutingServerProxyTest.java create mode 100644 test/javax/management/namespace/SerialParamProcessorTest.java create mode 100644 test/javax/management/namespace/SourceNamespaceTest.java create mode 100644 test/javax/management/namespace/VirtualMBeanNotifTest.java create mode 100644 test/javax/management/namespace/VirtualMBeanTest.java create mode 100644 test/javax/management/namespace/VirtualNamespaceQueryTest.java create mode 100644 test/javax/management/namespace/VirtualPropsTest.java create mode 100644 test/javax/management/namespace/Wombat.java create mode 100644 test/javax/management/namespace/WombatMBean.java create mode 100644 test/javax/management/namespace/namespace.policy diff --git a/make/docs/CORE_PKGS.gmk b/make/docs/CORE_PKGS.gmk index dfedc7e12..4a41a2005 100644 --- a/make/docs/CORE_PKGS.gmk +++ b/make/docs/CORE_PKGS.gmk @@ -158,6 +158,7 @@ CORE_PKGS = \ javax.management.event \ javax.management.loading \ javax.management.monitor \ + javax.management.namespace \ javax.management.relation \ javax.management.openmbean \ javax.management.timer \ diff --git a/src/share/classes/com/sun/jmx/defaults/JmxProperties.java b/src/share/classes/com/sun/jmx/defaults/JmxProperties.java index 89cd5bf16..b4bbbb78d 100644 --- a/src/share/classes/com/sun/jmx/defaults/JmxProperties.java +++ b/src/share/classes/com/sun/jmx/defaults/JmxProperties.java @@ -176,6 +176,18 @@ public class JmxProperties { public static final String RELATION_LOGGER_NAME = "javax.management.relation"; + /** + * Logger name for Namespaces. + */ + public static final String NAMESPACE_LOGGER_NAME = + "javax.management.namespace"; + + /** + * Logger name for Namespaces. + */ + public static final Logger NAMESPACE_LOGGER = + Logger.getLogger(NAMESPACE_LOGGER_NAME); + /** * Logger for Relation Service. */ diff --git a/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java index 8bd6cba27..7d95a77fd 100644 --- a/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java +++ b/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java @@ -25,33 +25,49 @@ package com.sun.jmx.interceptor; -// java import + +// JMX RI +import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; +import com.sun.jmx.mbeanserver.DynamicMBean2; +import com.sun.jmx.mbeanserver.Introspector; +import com.sun.jmx.mbeanserver.MBeanInjector; +import com.sun.jmx.mbeanserver.MBeanInstantiator; +import com.sun.jmx.mbeanserver.ModifiableClassLoaderRepository; +import com.sun.jmx.mbeanserver.NamedObject; +import com.sun.jmx.mbeanserver.NotifySupport; +import com.sun.jmx.mbeanserver.Repository; +import com.sun.jmx.mbeanserver.Repository.RegistrationContext; +import com.sun.jmx.mbeanserver.Util; +import com.sun.jmx.remote.util.EnvHelp; + +import java.lang.ref.WeakReference; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.Permission; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; -import java.util.logging.Level; +import java.util.Queue; import java.util.Set; -import java.util.HashSet; import java.util.WeakHashMap; -import java.lang.ref.WeakReference; -import java.security.AccessControlContext; -import java.security.Permission; -import java.security.ProtectionDomain; -import java.security.AccessController; -import java.security.PrivilegedAction; +import java.util.logging.Level; // JMX import import javax.management.Attribute; import javax.management.AttributeList; import javax.management.AttributeNotFoundException; import javax.management.DynamicMBean; +import javax.management.DynamicWrapperMBean; import javax.management.InstanceAlreadyExistsException; import javax.management.InstanceNotFoundException; import javax.management.IntrospectionException; import javax.management.InvalidAttributeValueException; import javax.management.JMRuntimeException; import javax.management.ListenerNotFoundException; -import javax.management.MalformedObjectNameException; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanPermission; @@ -64,6 +80,7 @@ import javax.management.MBeanTrustPermission; import javax.management.NotCompliantMBeanException; import javax.management.Notification; import javax.management.NotificationBroadcaster; +import javax.management.NotificationBroadcasterSupport; import javax.management.NotificationEmitter; import javax.management.NotificationFilter; import javax.management.NotificationListener; @@ -75,22 +92,7 @@ import javax.management.ReflectionException; import javax.management.RuntimeErrorException; import javax.management.RuntimeMBeanException; import javax.management.RuntimeOperationsException; - -// JMX RI -import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; -import com.sun.jmx.mbeanserver.DynamicMBean2; -import com.sun.jmx.mbeanserver.ModifiableClassLoaderRepository; -import com.sun.jmx.mbeanserver.MBeanInstantiator; -import com.sun.jmx.mbeanserver.Repository; -import com.sun.jmx.mbeanserver.NamedObject; -import com.sun.jmx.mbeanserver.Introspector; -import com.sun.jmx.mbeanserver.MBeanInjector; -import com.sun.jmx.mbeanserver.NotifySupport; -import com.sun.jmx.mbeanserver.Repository.RegistrationContext; -import com.sun.jmx.mbeanserver.Util; -import com.sun.jmx.remote.util.EnvHelp; -import javax.management.DynamicWrapperMBean; -import javax.management.NotificationBroadcasterSupport; +import javax.management.namespace.JMXNamespace; /** * This is the default class for MBean manipulation on the agent side. It @@ -113,7 +115,8 @@ import javax.management.NotificationBroadcasterSupport; * * @since 1.5 */ -public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { +public class DefaultMBeanServerInterceptor + extends MBeanServerInterceptorSupport { /** The MBeanInstantiator object used by the * DefaultMBeanServerInterceptor */ @@ -123,7 +126,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { * DefaultMBeanServerInterceptor */ private transient MBeanServer server = null; - /** The MBean server object taht associated to the + /** The MBean server delegate object that is associated to the * DefaultMBeanServerInterceptor */ private final transient MBeanServerDelegate delegate; @@ -138,13 +141,15 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { new WeakHashMap>(); + private final NamespaceDispatchInterceptor dispatcher; + /** The default domain of the object names */ private final String domain; - /** True if the repository perform queries, false otherwise */ - private boolean queryByRepo; + /** The mbeanServerName */ + private final String mbeanServerName; - /** The sequence number identifyng the notifications sent */ + /** The sequence number identifying the notifications sent */ // Now sequence number is handled by MBeanServerDelegate. // private int sequenceNumber=0; @@ -162,11 +167,13 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { * @param instantiator The MBeanInstantiator that will be used to * instantiate MBeans and take care of class loading issues. * @param repository The repository to use for this MBeanServer. + * @param dispatcher The dispatcher used by this MBeanServer */ public DefaultMBeanServerInterceptor(MBeanServer outer, MBeanServerDelegate delegate, MBeanInstantiator instantiator, - Repository repository) { + Repository repository, + NamespaceDispatchInterceptor dispatcher) { if (outer == null) throw new IllegalArgumentException("outer MBeanServer cannot be null"); if (delegate == null) throw new @@ -181,6 +188,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { this.instantiator = instantiator; this.repository = repository; this.domain = repository.getDefaultDomain(); + this.dispatcher = dispatcher; + this.mbeanServerName = Util.getMBeanServerSecurityName(delegate); } public ObjectInstance createMBean(String className, ObjectName name) @@ -259,8 +268,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { name = nonDefaultDomain(name); } - checkMBeanPermission(className, null, null, "instantiate"); - checkMBeanPermission(className, null, name, "registerMBean"); + checkMBeanPermission(mbeanServerName,className, null, null, "instantiate"); + checkMBeanPermission(mbeanServerName,className, null, name, "registerMBean"); /* Load the appropriate class. */ if (withDefaultLoaderRepository) { @@ -324,7 +333,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { final String infoClassName = getNewMBeanClassName(object); - checkMBeanPermission(infoClassName, null, name, "registerMBean"); + checkMBeanPermission(mbeanServerName,infoClassName, null, name, "registerMBean"); checkMBeanTrustPermission(theClass); return registerObject(infoClassName, object, name); @@ -433,7 +442,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { DynamicMBean instance = getMBean(name); // may throw InstanceNotFoundException - checkMBeanPermission(instance, null, name, "unregisterMBean"); + checkMBeanPermission(mbeanServerName, instance, null, name, + "unregisterMBean"); if (instance instanceof MBeanRegistration) preDeregisterInvoke((MBeanRegistration) instance); @@ -467,7 +477,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { name = nonDefaultDomain(name); DynamicMBean instance = getMBean(name); - checkMBeanPermission(instance, null, name, "getObjectInstance"); + checkMBeanPermission(mbeanServerName, + instance, null, name, "getObjectInstance"); final String className = getClassName(instance); @@ -479,7 +490,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { if (sm != null) { // Check if the caller has the right to invoke 'queryMBeans' // - checkMBeanPermission((String) null, null, null, "queryMBeans"); + checkMBeanPermission(mbeanServerName,(String) null, null, null, "queryMBeans"); // Perform query without "query". // @@ -492,7 +503,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { new HashSet(list.size()); for (ObjectInstance oi : list) { try { - checkMBeanPermission(oi.getClassName(), null, + checkMBeanPermission(mbeanServerName,oi.getClassName(), null, oi.getObjectName(), "queryMBeans"); allowedList.add(oi); } catch (SecurityException e) { @@ -516,11 +527,6 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { // Set list = repository.query(name, query); - if (queryByRepo) { - // The repository performs the filtering - query = null; - } - return (objectInstancesFromFilteredNamedObjects(list, query)); } @@ -530,7 +536,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { if (sm != null) { // Check if the caller has the right to invoke 'queryNames' // - checkMBeanPermission((String) null, null, null, "queryNames"); + checkMBeanPermission(mbeanServerName,(String) null, null, null, "queryNames"); // Perform query without "query". // @@ -543,7 +549,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { new HashSet(list.size()); for (ObjectInstance oi : list) { try { - checkMBeanPermission(oi.getClassName(), null, + checkMBeanPermission(mbeanServerName, oi.getClassName(), null, oi.getObjectName(), "queryNames"); allowedList.add(oi); } catch (SecurityException e) { @@ -572,11 +578,6 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { // Set list = repository.query(name, query); - if (queryByRepo) { - // The repository performs the filtering - query = null; - } - return (objectNamesFromFilteredNamedObjects(list, query)); } @@ -589,8 +590,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { name = nonDefaultDomain(name); -// /* Permission check */ -// checkMBeanPermission(null, null, name, "isRegistered"); + /* No Permission check */ + // isRegistered is always unchecked as per JMX spec. return (repository.contains(name)); } @@ -600,7 +601,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { if (sm != null) { // Check if the caller has the right to invoke 'getDomains' // - checkMBeanPermission((String) null, null, null, "getDomains"); + checkMBeanPermission(mbeanServerName, (String) null, null, null, "getDomains"); // Return domains // @@ -612,8 +613,9 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { List result = new ArrayList(domains.length); for (int i = 0; i < domains.length; i++) { try { - ObjectName domain = Util.newObjectName(domains[i] + ":x=x"); - checkMBeanPermission((String) null, null, domain, "getDomains"); + ObjectName dom = + Util.newObjectName(domains[i] + ":x=x"); + checkMBeanPermission(mbeanServerName, (String) null, null, dom, "getDomains"); result.add(domains[i]); } catch (SecurityException e) { // OK: Do not add this domain to the list @@ -657,7 +659,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } final DynamicMBean instance = getMBean(name); - checkMBeanPermission(instance, attribute, name, "getAttribute"); + checkMBeanPermission(mbeanServerName, instance, attribute, + name, "getAttribute"); try { return instance.getAttribute(attribute); @@ -702,7 +705,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { // Check if the caller has the right to invoke 'getAttribute' // - checkMBeanPermission(classname, null, name, "getAttribute"); + checkMBeanPermission(mbeanServerName, classname, null, name, "getAttribute"); // Check if the caller has the right to invoke 'getAttribute' // on each specific attribute @@ -711,14 +714,15 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { new ArrayList(attributes.length); for (String attr : attributes) { try { - checkMBeanPermission(classname, attr, + checkMBeanPermission(mbeanServerName, classname, attr, name, "getAttribute"); allowedList.add(attr); } catch (SecurityException e) { // OK: Do not add this attribute to the list } } - allowedAttributes = allowedList.toArray(new String[0]); + allowedAttributes = + allowedList.toArray(new String[allowedList.size()]); } try { @@ -756,7 +760,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } DynamicMBean instance = getMBean(name); - checkMBeanPermission(instance, attribute.getName(), + checkMBeanPermission(mbeanServerName, instance, attribute.getName(), name, "setAttribute"); try { @@ -799,7 +803,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { // Check if the caller has the right to invoke 'setAttribute' // - checkMBeanPermission(classname, null, name, "setAttribute"); + checkMBeanPermission(mbeanServerName, classname, null, name, "setAttribute"); // Check if the caller has the right to invoke 'setAttribute' // on each specific attribute @@ -808,7 +812,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { for (Iterator i = attributes.iterator(); i.hasNext();) { try { Attribute attribute = (Attribute) i.next(); - checkMBeanPermission(classname, attribute.getName(), + checkMBeanPermission(mbeanServerName, classname, attribute.getName(), name, "setAttribute"); allowedAttributes.add(attribute); } catch (SecurityException e) { @@ -832,7 +836,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { name = nonDefaultDomain(name); DynamicMBean instance = getMBean(name); - checkMBeanPermission(instance, operationName, name, "invoke"); + checkMBeanPermission(mbeanServerName, instance, operationName, + name, "invoke"); try { return instance.invoke(operationName, params, signature); } catch (Throwable t) { @@ -934,8 +939,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { "registerMBean", "ObjectName = " + name); } - ObjectName logicalName = name; - logicalName = preRegister(mbean, server, name); + ObjectName logicalName = preRegister(mbean, server, name); // preRegister returned successfully, so from this point on we // must call postRegister(false) if there is any problem. @@ -961,16 +965,17 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { if (logicalName != name && logicalName != null) { logicalName = - ObjectName.getInstance(nonDefaultDomain(logicalName)); + ObjectName.getInstance(nonDefaultDomain(logicalName)); } - checkMBeanPermission(classname, null, logicalName, "registerMBean"); + checkMBeanPermission(mbeanServerName, classname, null, logicalName, + "registerMBean"); if (logicalName == null) { final RuntimeException wrapped = - new IllegalArgumentException("No object name specified"); + new IllegalArgumentException("No object name specified"); throw new RuntimeOperationsException(wrapped, - "Exception occurred trying to register the MBean"); + "Exception occurred trying to register the MBean"); } final Object resource = getResource(mbean); @@ -987,13 +992,15 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { // context = registerWithRepository(resource, mbean, logicalName); + registerFailed = false; registered = true; + } finally { try { postRegister(logicalName, mbean, registered, registerFailed); } finally { - if (registered) context.done(); + if (registered && context!=null) context.done(); } } return new ObjectInstance(logicalName, classname); @@ -1001,20 +1008,19 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { private static void throwMBeanRegistrationException(Throwable t, String where) throws MBeanRegistrationException { - try { - throw t; - } catch (RuntimeException e) { - throw new RuntimeMBeanException( - e, "RuntimeException thrown " + where); - } catch (Error er) { - throw new RuntimeErrorException(er, "Error thrown " + where); - } catch (MBeanRegistrationException r) { - throw r; - } catch (Exception ex) { - throw new MBeanRegistrationException(ex, "Exception thrown " + where); - } catch (Throwable t1) { - throw new RuntimeException(t); // neither Error nor Exception?? - } + if (t instanceof RuntimeException) { + throw new RuntimeMBeanException((RuntimeException)t, + "RuntimeException thrown " + where); + } else if (t instanceof Error) { + throw new RuntimeErrorException((Error)t, + "Error thrown " + where); + } else if (t instanceof MBeanRegistrationException) { + throw (MBeanRegistrationException)t; + } else if (t instanceof Exception) { + throw new MBeanRegistrationException((Exception)t, + "Exception thrown " + where); + } else // neither Error nor Exception?? + throw new RuntimeException(t); } private static ObjectName preRegister( @@ -1230,7 +1236,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } DynamicMBean instance = getMBean(name); - checkMBeanPermission(instance, null, name, "addNotificationListener"); + checkMBeanPermission(mbeanServerName, instance, null, + name, "addNotificationListener"); NotificationBroadcaster broadcaster = getNotificationBroadcaster(name, instance, @@ -1367,7 +1374,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } DynamicMBean instance = getMBean(name); - checkMBeanPermission(instance, null, name, + checkMBeanPermission(mbeanServerName, instance, null, name, "removeNotificationListener"); /* We could simplify the code by assigning broadcaster after @@ -1438,7 +1445,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { throw new JMRuntimeException("MBean " + name + "has no MBeanInfo"); - checkMBeanPermission(mbi.getClassName(), null, name, "getMBeanInfo"); + checkMBeanPermission(mbeanServerName, mbi.getClassName(), null, name, "getMBeanInfo"); return mbi; } @@ -1446,8 +1453,9 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { public boolean isInstanceOf(ObjectName name, String className) throws InstanceNotFoundException { - DynamicMBean instance = getMBean(name); - checkMBeanPermission(instance, null, name, "isInstanceOf"); + final DynamicMBean instance = getMBean(name); + checkMBeanPermission(mbeanServerName, + instance, null, name, "isInstanceOf"); try { Object resource = getResource(instance); @@ -1498,7 +1506,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { throws InstanceNotFoundException { DynamicMBean instance = getMBean(mbeanName); - checkMBeanPermission(instance, null, mbeanName, "getClassLoaderFor"); + checkMBeanPermission(mbeanServerName, instance, null, mbeanName, + "getClassLoaderFor"); return getResourceLoader(instance); } @@ -1513,12 +1522,13 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { throws InstanceNotFoundException { if (loaderName == null) { - checkMBeanPermission((String) null, null, null, "getClassLoader"); + checkMBeanPermission(mbeanServerName, (String) null, null, null, "getClassLoader"); return server.getClass().getClassLoader(); } DynamicMBean instance = getMBean(loaderName); - checkMBeanPermission(instance, null, loaderName, "getClassLoader"); + checkMBeanPermission(mbeanServerName, instance, null, loaderName, + "getClassLoader"); Object resource = getResource(instance); @@ -1568,7 +1578,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } } else { // Access the filter - MBeanServer oldServer = QueryEval.getMBeanServer(); + final MBeanServer oldServer = QueryEval.getMBeanServer(); query.setMBeanServer(server); try { for (NamedObject no : list) { @@ -1817,26 +1827,30 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { return mbean.getMBeanInfo().getClassName(); } - private static void checkMBeanPermission(DynamicMBean mbean, + private static void checkMBeanPermission(String mbeanServerName, + DynamicMBean mbean, String member, ObjectName objectName, String actions) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { - checkMBeanPermission(safeGetClassName(mbean), + checkMBeanPermission(mbeanServerName, + safeGetClassName(mbean), member, objectName, actions); } } - private static void checkMBeanPermission(String classname, + private static void checkMBeanPermission(String mbeanServerName, + String classname, String member, ObjectName objectName, String actions) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { - Permission perm = new MBeanPermission(classname, + Permission perm = new MBeanPermission(mbeanServerName, + classname, member, objectName, actions); @@ -1902,6 +1916,12 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { throws InstanceAlreadyExistsException, MBeanRegistrationException { + // this will throw an exception if the pair (resource, logicalName) + // violates namespace conventions - for instance, if logicalName + // ends with // but resource is not a JMXNamespace. + // + checkResourceObjectNameConstraints(resource, logicalName); + // Creates a registration context, if needed. // final ResourceContext context = @@ -1967,6 +1987,57 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { return context; } + + /** + * Checks that the ObjectName is legal with regards to the + * type of the MBean resource. + * If the MBean name is domain:type=JMXDomain, the + * MBean must be a JMXDomain. + * If the MBean name is namespace//:type=JMXNamespace, the + * MBean must be a JMXNamespace. + * If the MBean is a JMXDomain, its name + * must be domain:type=JMXDomain. + * If the MBean is a JMXNamespace, its name + * must be namespace//:type=JMXNamespace. + */ + private void checkResourceObjectNameConstraints(Object resource, + ObjectName logicalName) + throws MBeanRegistrationException { + try { + dispatcher.checkLocallyRegistrable(resource, logicalName); + } catch (Throwable x) { + DefaultMBeanServerInterceptor.throwMBeanRegistrationException(x, "validating ObjectName"); + } + } + + /** + * Registers a JMXNamespace with the dispatcher. + * This method is called by the ResourceContext from within the + * repository lock. + * @param namespace The JMXNamespace + * @param logicalName The JMXNamespaceMBean ObjectName + * @param postQueue A queue that will be processed after postRegister. + */ + private void addJMXNamespace(JMXNamespace namespace, + final ObjectName logicalName, + final Queue postQueue) { + dispatcher.addNamespace(logicalName, namespace, postQueue); + } + + /** + * Unregisters a JMXNamespace from the dispatcher. + * This method is called by the ResourceContext from within the + * repository lock. + * @param namespace The JMXNamespace + * @param logicalName The JMXNamespaceMBean ObjectName + * @param postQueue A queue that will be processed after postDeregister. + */ + private void removeJMXNamespace(JMXNamespace namespace, + final ObjectName logicalName, + final Queue postQueue) { + dispatcher.removeNamespace(logicalName, namespace, postQueue); + } + /** * Registers a ClassLoader with the CLR. * This method is called by the ResourceContext from within the @@ -2020,6 +2091,52 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } } + + /** + * Creates a ResourceContext for a JMXNamespace MBean. + * The resource context makes it possible to add the JMXNamespace to + * (ResourceContext.registering) or resp. remove the JMXNamespace from + * (ResourceContext.unregistered) the NamespaceDispatchInterceptor + * when the associated MBean is added to or resp. removed from the + * repository. + * Note: JMXDomains are special sub classes of JMXNamespaces and + * are also handled by this object. + * + * @param namespace The JMXNamespace MBean being registered or + * unregistered. + * @param logicalName The name of the JMXNamespace MBean. + * @return a ResourceContext that takes in charge the addition or removal + * of the namespace to or from the NamespaceDispatchInterceptor. + */ + private ResourceContext createJMXNamespaceContext( + final JMXNamespace namespace, + final ObjectName logicalName) { + final Queue doneTaskQueue = new LinkedList(); + return new ResourceContext() { + + public void registering() { + addJMXNamespace(namespace, logicalName, doneTaskQueue); + } + + public void unregistered() { + removeJMXNamespace(namespace, logicalName, + doneTaskQueue); + } + + public void done() { + for (Runnable r : doneTaskQueue) { + try { + r.run(); + } catch (RuntimeException x) { + MBEANSERVER_LOGGER.log(Level.FINE, + "Failed to process post queue for "+ + logicalName, x); + } + } + } + }; + } + /** * Creates a ResourceContext for a ClassLoader MBean. * The resource context makes it possible to add the ClassLoader to @@ -2065,10 +2182,16 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { */ private ResourceContext makeResourceContextFor(Object resource, ObjectName logicalName) { + if (resource instanceof JMXNamespace) { + return createJMXNamespaceContext((JMXNamespace) resource, + logicalName); + } if (resource instanceof ClassLoader) { return createClassLoaderContext((ClassLoader) resource, logicalName); } return ResourceContext.NONE; } + + } diff --git a/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java new file mode 100644 index 000000000..9e8625d16 --- /dev/null +++ b/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java @@ -0,0 +1,547 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.interceptor; + + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.QueryExp; +import javax.management.ReflectionException; +import javax.management.namespace.JMXNamespace; + +/** + * A dispatcher that dispatches to MBeanServers. + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +// +// This is the base class for implementing dispatchers. We have two concrete +// dispatcher implementations: +// +// * A NamespaceDispatchInterceptor, which dispatch calls to existing +// namespace interceptors +// * A DomainDispatchInterceptor, which dispatch calls to existing domain +// interceptors. +// +// With the JMX Namespaces feature, the JMX MBeanServer is now structured +// as follows: +// +// The JMX MBeanServer delegates to a NamespaceDispatchInterceptor, +// which either dispatches to a namespace, or delegates to the +// DomainDispatchInterceptor (if the object name contained no namespace). +// The DomainDispatchInterceptor in turn either dispatches to a domain (if +// there is a JMXDomain for that domain) or delegates to the +// DefaultMBeanServerInterceptor (if there is no JMXDomain for that +// domain). This makes the following picture: +// +// JMX MBeanServer (outer shell) +// | +// | +// NamespaceDispatchInterceptor +// / \ +// no namespace in object name? \ +// / \ +// / dispatch to namespace +// DomainDispatchInterceptor +// / \ +// no JMXDomain for domain? \ +// / \ +// / dispatch to domain +// DefaultMBeanServerInterceptor +// / +// invoke locally registered MBean +// +// The logic for maintaining a map of interceptors +// and dispatching to impacted interceptor, is implemented in this +// base class, which both NamespaceDispatchInterceptor and +// DomainDispatchInterceptor extend. +// +public abstract class DispatchInterceptor + + extends MBeanServerInterceptorSupport { + + /** + * This is an abstraction which allows us to handle queryNames + * and queryMBeans with the same algorithm. There are some subclasses + * where we need to override both queryNames & queryMBeans to apply + * the same transformation (usually aggregation of results when + * several namespaces/domains are impacted) to both algorithms. + * Usually the only thing that varies between the algorithm of + * queryNames & the algorithm of queryMBean is the type of objects + * in the returned Set. By using a QueryInvoker we can implement the + * transformation only once and apply it to both queryNames & + * queryMBeans. + * @see QueryInterceptor below, and its subclass in + * {@link DomainDispatcher}. + **/ + static abstract class QueryInvoker { + abstract Set query(MBeanServer mbs, + ObjectName pattern, QueryExp query); + } + + /** + * Used to perform queryNames. A QueryInvoker that invokes + * queryNames on an MBeanServer. + **/ + final static QueryInvoker queryNamesInvoker = + new QueryInvoker() { + Set query(MBeanServer mbs, + ObjectName pattern, QueryExp query) { + return mbs.queryNames(pattern,query); + } + }; + + /** + * Used to perform queryMBeans. A QueryInvoker that invokes + * queryMBeans on an MBeanServer. + **/ + final static QueryInvoker queryMBeansInvoker = + new QueryInvoker() { + Set query(MBeanServer mbs, + ObjectName pattern, QueryExp query) { + return mbs.queryMBeans(pattern,query); + } + }; + + /** + * We use this class to intercept queries. + * There's a special case for JMXNamespace MBeans, because + * "namespace//*:*" matches both "namespace//domain:k=v" and + * "namespace//:type=JMXNamespace". + * Therefore, queries may need to be forwarded to more than + * on interceptor and the results aggregated... + */ + static class QueryInterceptor { + final MBeanServer wrapped; + QueryInterceptor(MBeanServer mbs) { + wrapped = mbs; + } + Set query(ObjectName pattern, QueryExp query, + QueryInvoker invoker, MBeanServer server) { + return invoker.query(server, pattern, query); + } + + public Set queryNames(ObjectName pattern, QueryExp query) { + return query(pattern,query,queryNamesInvoker,wrapped); + } + + public Set queryMBeans(ObjectName pattern, + QueryExp query) { + return query(pattern,query,queryMBeansInvoker,wrapped); + } + } + + // We don't need a ConcurrentHashMap here because getkeys() returns + // an array of keys. Therefore there's no risk to have a + // ConcurrentModificationException. We must however take into + // account the fact that there can be no interceptor for + // some of the returned keys if the map is being modified by + // another thread, or by a callback within the same thread... + // See getKeys() in this class and query() in DomainDispatcher. + // + private final Map handlerMap = + Collections.synchronizedMap( + new HashMap()); + + // The key at which an interceptor for accessing the named MBean can be + // found in the handlerMap. Note: there doesn't need to be an interceptor + // for that key in the Map. + // + public abstract String getHandlerKey(ObjectName name); + + // Returns an interceptor for that name, or null if there's no interceptor + // for that name. + abstract MBeanServer getInterceptorOrNullFor(ObjectName name); + + // Returns a QueryInterceptor for that pattern. + abstract QueryInterceptor getInterceptorForQuery(ObjectName pattern); + + // Returns the ObjectName of the JMXNamespace (or JMXDomain) for that + // key (a namespace or a domain name). + abstract ObjectName getHandlerNameFor(String key) + throws MalformedObjectNameException; + + // Creates an interceptor for the given key, name, JMXNamespace (or + // JMXDomain). Note: this will be either a NamespaceInterceptor + // wrapping a JMXNamespace, if this object is an instance of + // NamespaceDispatchInterceptor, or a DomainInterceptor wrapping a + // JMXDomain, if this object is an instance of DomainDispatchInterceptor. + abstract T createInterceptorFor(String key, ObjectName name, + N jmxNamespace, Queue postRegisterQueue); + // + // The next interceptor in the chain. + // + // For the NamespaceDispatchInterceptor, this the DomainDispatchInterceptor. + // For the DomainDispatchInterceptor, this is the + // DefaultMBeanServerInterceptor. + // + // The logic of when to invoke the next interceptor in the chain depends + // on the logic of the concrete dispatcher class. + // + // For instance, the NamespaceDispatchInterceptor invokes the next + // interceptor when the object name doesn't contain any namespace. + // + // On the other hand, the DomainDispatchInterceptor invokes the + // next interceptor when there's no interceptor for the accessed domain. + // + abstract MBeanServer getNextInterceptor(); + + // hook for cleanup in subclasses. + void interceptorReleased(T interceptor, + Queue postDeregisterQueue) { + // hook + } + + // Hook for subclasses. + MBeanServer getInterceptorForCreate(ObjectName name) + throws MBeanRegistrationException { + final MBeanServer ns = getInterceptorOrNullFor(name); + if (ns == null) // name cannot be null here. + throw new MBeanRegistrationException( + new IllegalArgumentException("No such MBean handler: " + + getHandlerKey(name) + " for " +name)); + return ns; + } + + // Hook for subclasses. + MBeanServer getInterceptorForInstance(ObjectName name) + throws InstanceNotFoundException { + final MBeanServer ns = getInterceptorOrNullFor(name); + if (ns == null) // name cannot be null here. + throw new InstanceNotFoundException(String.valueOf(name)); + return ns; + } + + // sanity checks + void validateHandlerNameFor(String key, ObjectName name) { + if (key == null || key.equals("")) + throw new IllegalArgumentException("invalid key for "+name+": "+key); + try { + final ObjectName handlerName = getHandlerNameFor(key); + if (!name.equals(handlerName)) + throw new IllegalArgumentException("bad handler name: "+name+ + ". Should be: "+handlerName); + } catch (MalformedObjectNameException x) { + throw new IllegalArgumentException(name.toString(),x); + } + } + + // Called by the DefaultMBeanServerInterceptor when an instance + // of JMXNamespace (or a subclass of it) is registered as an MBean. + // This method is usually invoked from within the repository lock, + // hence the necessity of the postRegisterQueue. + public void addNamespace(ObjectName name, N jmxNamespace, + Queue postRegisterQueue) { + final String key = getHandlerKey(name); + validateHandlerNameFor(key,name); + synchronized (handlerMap) { + final T exists = + handlerMap.get(key); + if (exists != null) + throw new IllegalArgumentException(key+ + ": handler already exists"); + + final T ns = createInterceptorFor(key,name,jmxNamespace, + postRegisterQueue); + handlerMap.put(key,ns); + } + } + + // Called by the DefaultMBeanServerInterceptor when an instance + // of JMXNamespace (or a subclass of it) is deregistered. + // This method is usually invoked from within the repository lock, + // hence the necessity of the postDeregisterQueue. + public void removeNamespace(ObjectName name, N jmxNamespace, + Queue postDeregisterQueue) { + final String key = getHandlerKey(name); + final T ns; + synchronized(handlerMap) { + ns = handlerMap.remove(key); + } + interceptorReleased(ns,postDeregisterQueue); + } + + // Get the interceptor for that key. + T getInterceptor(String key) { + synchronized (handlerMap) { + return handlerMap.get(key); + } + } + + // We return an array of keys, which makes it possible to make + // concurrent modifications of the handlerMap, provided that + // the code which loops over the keys is prepared to handle null + // interceptors. + // See declaration of handlerMap above, and see also query() in + // DomainDispatcher + // + public String[] getKeys() { + synchronized (handlerMap) { + final int size = handlerMap.size(); + return handlerMap.keySet().toArray(new String[size]); + } + } + + // From MBeanServer + public ObjectInstance createMBean(String className, ObjectName name) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException { + return getInterceptorForCreate(name).createMBean(className,name); + } + + // From MBeanServer + public ObjectInstance createMBean(String className, ObjectName name, + ObjectName loaderName) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException{ + return getInterceptorForCreate(name).createMBean(className,name,loaderName); + } + + // From MBeanServer + public ObjectInstance createMBean(String className, ObjectName name, + Object params[], String signature[]) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException{ + return getInterceptorForCreate(name). + createMBean(className,name,params,signature); + } + + // From MBeanServer + public ObjectInstance createMBean(String className, ObjectName name, + ObjectName loaderName, Object params[], + String signature[]) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException{ + return getInterceptorForCreate(name).createMBean(className,name,loaderName, + params,signature); + } + + // From MBeanServer + public ObjectInstance registerMBean(Object object, ObjectName name) + throws InstanceAlreadyExistsException, MBeanRegistrationException, + NotCompliantMBeanException { + return getInterceptorForCreate(name).registerMBean(object,name); + } + + // From MBeanServer + public void unregisterMBean(ObjectName name) + throws InstanceNotFoundException, MBeanRegistrationException { + getInterceptorForInstance(name).unregisterMBean(name); + } + + // From MBeanServer + public ObjectInstance getObjectInstance(ObjectName name) + throws InstanceNotFoundException { + return getInterceptorForInstance(name).getObjectInstance(name); + } + + // From MBeanServer + public Set queryMBeans(ObjectName name, QueryExp query) { + final QueryInterceptor mbs = + getInterceptorForQuery(name); + if (mbs == null) return Collections.emptySet(); + else return mbs.queryMBeans(name,query); + } + + // From MBeanServer + public Set queryNames(ObjectName name, QueryExp query) { + final QueryInterceptor mbs = + getInterceptorForQuery(name); + if (mbs == null) return Collections.emptySet(); + else return mbs.queryNames(name,query); + } + + // From MBeanServer + public boolean isRegistered(ObjectName name) { + final MBeanServer mbs = getInterceptorOrNullFor(name); + if (mbs == null) return false; + else return mbs.isRegistered(name); + } + + // From MBeanServer + public Integer getMBeanCount() { + return getNextInterceptor().getMBeanCount(); + } + + // From MBeanServer + public Object getAttribute(ObjectName name, String attribute) + throws MBeanException, AttributeNotFoundException, + InstanceNotFoundException, ReflectionException { + return getInterceptorForInstance(name).getAttribute(name,attribute); + } + + // From MBeanServer + public AttributeList getAttributes(ObjectName name, String[] attributes) + throws InstanceNotFoundException, ReflectionException { + return getInterceptorForInstance(name).getAttributes(name,attributes); + } + + // From MBeanServer + public void setAttribute(ObjectName name, Attribute attribute) + throws InstanceNotFoundException, AttributeNotFoundException, + InvalidAttributeValueException, MBeanException, + ReflectionException { + getInterceptorForInstance(name).setAttribute(name,attribute); + } + + // From MBeanServer + public AttributeList setAttributes(ObjectName name, + AttributeList attributes) + throws InstanceNotFoundException, ReflectionException { + return getInterceptorForInstance(name).setAttributes(name,attributes); + } + + // From MBeanServer + public Object invoke(ObjectName name, String operationName, + Object params[], String signature[]) + throws InstanceNotFoundException, MBeanException, + ReflectionException { + return getInterceptorForInstance(name).invoke(name,operationName,params, + signature); + } + + // From MBeanServer + public String getDefaultDomain() { + return getNextInterceptor().getDefaultDomain(); + } + + /** + * Returns the list of domains in which any MBean is currently + * registered. + */ + public abstract String[] getDomains(); + + // From MBeanServer + public void addNotificationListener(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException { + getInterceptorForInstance(name).addNotificationListener(name,listener,filter, + handback); + } + + + // From MBeanServer + public void addNotificationListener(ObjectName name, + ObjectName listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException { + getInterceptorForInstance(name).addNotificationListener(name,listener,filter, + handback); + } + + // From MBeanServer + public void removeNotificationListener(ObjectName name, + ObjectName listener) + throws InstanceNotFoundException, ListenerNotFoundException { + getInterceptorForInstance(name).removeNotificationListener(name,listener); + } + + // From MBeanServer + public void removeNotificationListener(ObjectName name, + ObjectName listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, ListenerNotFoundException { + getInterceptorForInstance(name).removeNotificationListener(name,listener,filter, + handback); + } + + + // From MBeanServer + public void removeNotificationListener(ObjectName name, + NotificationListener listener) + throws InstanceNotFoundException, ListenerNotFoundException { + getInterceptorForInstance(name).removeNotificationListener(name,listener); + } + + // From MBeanServer + public void removeNotificationListener(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, ListenerNotFoundException { + getInterceptorForInstance(name).removeNotificationListener(name,listener,filter, + handback); + } + + // From MBeanServer + public MBeanInfo getMBeanInfo(ObjectName name) + throws InstanceNotFoundException, IntrospectionException, + ReflectionException { + return getInterceptorForInstance(name).getMBeanInfo(name); + } + + + // From MBeanServer + public boolean isInstanceOf(ObjectName name, String className) + throws InstanceNotFoundException { + return getInterceptorForInstance(name).isInstanceOf(name,className); + } + + // From MBeanServer + public ClassLoader getClassLoaderFor(ObjectName mbeanName) + throws InstanceNotFoundException { + return getInterceptorForInstance(mbeanName).getClassLoaderFor(mbeanName); + } + + // From MBeanServer + public ClassLoader getClassLoader(ObjectName loaderName) + throws InstanceNotFoundException { + return getInterceptorForInstance(loaderName).getClassLoader(loaderName); + } + +} diff --git a/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java new file mode 100644 index 000000000..cb1489ee5 --- /dev/null +++ b/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java @@ -0,0 +1,322 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.interceptor; + +import com.sun.jmx.defaults.JmxProperties; +import com.sun.jmx.mbeanserver.MBeanInstantiator; +import com.sun.jmx.mbeanserver.Repository; +import com.sun.jmx.mbeanserver.Util; +import com.sun.jmx.namespace.DomainInterceptor; +import java.util.Queue; +import java.util.Set; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.management.MBeanServer; +import javax.management.MBeanServerDelegate; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.QueryExp; +import javax.management.namespace.JMXDomain; +import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR; + +/** + * A dispatcher that dispatch incoming MBeanServer requests to + * DomainInterceptors. + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +// +// See comments in DispatchInterceptor. +// +class DomainDispatchInterceptor + extends DispatchInterceptor { + + /** + * A logger for this class. + **/ + private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; + + private static final ObjectName ALL_DOMAINS = + JMXDomain.getDomainObjectName("*"); + + + /** + * A QueryInterceptor that perform & aggregates queries spanning several + * domains. + */ + final static class AggregatingQueryInterceptor extends QueryInterceptor { + + private final DomainDispatchInterceptor parent; + AggregatingQueryInterceptor(DomainDispatchInterceptor dispatcher) { + super(dispatcher.localNamespace); + parent = dispatcher; + } + + /** + * Perform queryNames or queryMBeans, depending on which QueryInvoker + * is passed as argument. This is closures without closures. + **/ + @Override + Set query(ObjectName pattern, QueryExp query, + QueryInvoker invoker, MBeanServer localNamespace) { + final Set local = invoker.query(localNamespace, pattern, query); + + // Add all matching MBeans from local namespace. + final Set res = Util.cloneSet(local); + + final boolean all = (pattern == null || + pattern.getDomain().equals("*")); + if (pattern == null) pattern = ObjectName.WILDCARD; + + final String domain = pattern.getDomain(); + + // If there's no domain pattern, just include the pattern's domain. + // Otherwiae, loop over all virtual domains (parent.getKeys()). + final String[] keys = + (pattern.isDomainPattern() ? + parent.getKeys() : new String[]{domain}); + + // Add all matching MBeans from each virtual domain + // + for (String key : keys) { + // Only invoke those virtual domain which are selected + // by the domain pattern + // + if (!all && !Util.isDomainSelected(key, domain)) + continue; + + try { + final MBeanServer mbs = parent.getInterceptor(key); + + // mbs can be null if the interceptor was removed + // concurrently... + // See handlerMap and getKeys() in DispatchInterceptor + // + if (mbs == null) continue; + + // If the domain is selected, we can replace the pattern + // by the actual domain. This is safer if we want to avoid + // a domain (which could be backed up by an MBeanServer) to + // return names from outside the domain. + // So instead of asking the domain handler for "foo" to + // return all names which match "?o*:type=Bla,*" we're + // going to ask it to return all names which match + // "foo:type=Bla,*" + // + final ObjectName subPattern = pattern.withDomain(key); + res.addAll(invoker.query(mbs, subPattern, query)); + } catch (Exception x) { + LOG.finest("Ignoring exception " + + "when attempting to query namespace "+key+": "+x); + continue; + } + } + return res; + } + } + + private final DefaultMBeanServerInterceptor localNamespace; + private final String mbeanServerName; + private final MBeanServerDelegate delegate; + + /** + * Creates a DomainDispatchInterceptor with the specified + * repository instance. + * + * @param outer A pointer to the MBeanServer object that must be + * passed to the MBeans when invoking their + * {@link javax.management.MBeanRegistration} interface. + * @param delegate A pointer to the MBeanServerDelegate associated + * with the new MBeanServer. The new MBeanServer must register + * this MBean in its MBean repository. + * @param instantiator The MBeanInstantiator that will be used to + * instantiate MBeans and take care of class loading issues. + * @param repository The repository to use for this MBeanServer + */ + public DomainDispatchInterceptor(MBeanServer outer, + MBeanServerDelegate delegate, + MBeanInstantiator instantiator, + Repository repository, + NamespaceDispatchInterceptor namespaces) { + localNamespace = new DefaultMBeanServerInterceptor(outer, + delegate, instantiator,repository,namespaces); + mbeanServerName = Util.getMBeanServerSecurityName(delegate); + this.delegate = delegate; + } + + final boolean isLocalHandlerNameFor(String domain, + ObjectName handlerName) { + if (domain == null) return true; + return handlerName.getDomain().equals(domain) && + JMXDomain.TYPE_ASSIGNMENT.equals( + handlerName.getKeyPropertyListString()); + } + + @Override + void validateHandlerNameFor(String key, ObjectName name) { + super.validateHandlerNameFor(key,name); + final String[] domains = localNamespace.getDomains(); + for (int i=0;i postRegisterQueue) { + final DomainInterceptor ns = + new DomainInterceptor(mbeanServerName,handler,key); + ns.addPostRegisterTask(postRegisterQueue, delegate); + if (LOG.isLoggable(Level.FINER)) { + LOG.finer("DomainInterceptor created: "+ns); + } + return ns; + } + + @Override + final void interceptorReleased(DomainInterceptor interceptor, + Queue postDeregisterQueue) { + interceptor.addPostDeregisterTask(postDeregisterQueue, delegate); + } + + @Override + final DefaultMBeanServerInterceptor getNextInterceptor() { + return localNamespace; + } + + /** + * Returns the list of domains in which any MBean is currently + * registered. + */ + @Override + public String[] getDomains() { + // A JMXDomain is registered in its own domain. + // Therefore, localNamespace.getDomains() contains all domains. + // In addition, localNamespace will perform the necessary + // MBeanPermission checks for getDomains(). + // + return localNamespace.getDomains(); + } + + /** + * Returns the number of MBeans registered in the MBean server. + */ + @Override + public Integer getMBeanCount() { + int count = getNextInterceptor().getMBeanCount().intValue(); + final String[] keys = getKeys(); + for (String key:keys) { + final MBeanServer mbs = getInterceptor(key); + if (mbs == null) continue; + count += mbs.getMBeanCount().intValue(); + } + return Integer.valueOf(count); + } +} diff --git a/src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptor.java index c0a1844f0..7b2d836a6 100644 --- a/src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptor.java +++ b/src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,35 +25,14 @@ package com.sun.jmx.interceptor; -import java.util.Set; -// RI import -import javax.management.DynamicMBean; -import javax.management.AttributeNotFoundException; -import javax.management.MBeanException; -import javax.management.ReflectionException; -import javax.management.MBeanAttributeInfo; -import javax.management.MBeanInfo; -import javax.management.QueryExp; -import javax.management.NotificationListener; -import javax.management.NotificationFilter; -import javax.management.ListenerNotFoundException; -import javax.management.IntrospectionException; -import javax.management.OperationsException; -import javax.management.MBeanNotificationInfo; -import javax.management.JMRuntimeException; +import java.io.ObjectInputStream; import javax.management.InstanceNotFoundException; -import javax.management.NotCompliantMBeanException; -import javax.management.MBeanRegistrationException; -import javax.management.InstanceAlreadyExistsException; -import javax.management.InvalidAttributeValueException; +import javax.management.MBeanException; +import javax.management.MBeanServer; import javax.management.ObjectName; -import javax.management.ObjectInstance; -import javax.management.Attribute; -import javax.management.AttributeList; -import javax.management.RuntimeOperationsException; -import javax.management.MBeanServerConnection; -import javax.management.MBeanServerDelegate; +import javax.management.OperationsException; +import javax.management.ReflectionException; import javax.management.loading.ClassLoaderRepository; /** @@ -85,618 +64,67 @@ import javax.management.loading.ClassLoaderRepository; * * @since 1.5 */ -public interface MBeanServerInterceptor extends MBeanServerConnection { - /** - * Instantiates and registers an MBean in the MBean server. The - * MBean server will use its {@link - * javax.management.loading.ClassLoaderRepository Default Loader - * Repository} to load the class of the MBean. An object name is - * associated to the MBean. If the object name given is null, the - * MBean must provide its own name by implementing the {@link - * javax.management.MBeanRegistration MBeanRegistration} interface - * and returning the name from the {@link - * javax.management.MBeanRegistration#preRegister preRegister} method. - * - * @param className The class name of the MBean to be instantiated. - * @param name The object name of the MBean. May be null. - * @param params An array containing the parameters of the - * constructor to be invoked. - * @param signature An array containing the signature of the - * constructor to be invoked. - * - * @return An ObjectInstance, containing the - * ObjectName and the Java class name of the newly - * instantiated MBean. - * - * @exception ReflectionException Wraps a - * java.lang.ClassNotFoundException or a - * java.lang.Exception that occurred when trying to - * invoke the MBean's constructor. - * @exception InstanceAlreadyExistsException The MBean is already - * under the control of the MBean server. - * @exception MBeanRegistrationException The - * preRegister (MBeanRegistration - * interface) method of the MBean has thrown an exception. The - * MBean will not be registered. - * @exception MBeanException The constructor of the MBean has - * thrown an exception - * @exception RuntimeOperationsException Wraps a - * java.lang.IllegalArgumentException: The className - * passed in parameter is null, the ObjectName passed - * in parameter contains a pattern or no ObjectName - * is specified for the MBean. - */ - public ObjectInstance createMBean(String className, ObjectName name, - Object params[], String signature[]) - throws ReflectionException, InstanceAlreadyExistsException, - MBeanRegistrationException, MBeanException, - NotCompliantMBeanException; - - /** - * Instantiates and registers an MBean in the MBean server. The - * class loader to be used is identified by its object name. An - * object name is associated to the MBean. If the object name of - * the loader is not specified, the ClassLoader that loaded the - * MBean server will be used. If the MBean object name given is - * null, the MBean must provide its own name by implementing the - * {@link javax.management.MBeanRegistration MBeanRegistration} - * interface and returning the name from the {@link - * javax.management.MBeanRegistration#preRegister preRegister} method. - * - * @param className The class name of the MBean to be instantiated. - * @param name The object name of the MBean. May be null. - * @param params An array containing the parameters of the - * constructor to be invoked. - * @param signature An array containing the signature of the - * constructor to be invoked. - * @param loaderName The object name of the class loader to be used. - * - * @return An ObjectInstance, containing the - * ObjectName and the Java class name of the newly - * instantiated MBean. - * - * @exception ReflectionException Wraps a - * java.lang.ClassNotFoundException or a - * java.lang.Exception that occurred when trying to - * invoke the MBean's constructor. - * @exception InstanceAlreadyExistsException The MBean is already - * under the control of the MBean server. - * @exception MBeanRegistrationException The - * preRegister (MBeanRegistration - * interface) method of the MBean has thrown an exception. The - * MBean will not be registered. - * @exception MBeanException The constructor of the MBean has - * thrown an exception - * @exception InstanceNotFoundException The specified class loader - * is not registered in the MBean server. - * @exception RuntimeOperationsException Wraps a - * java.lang.IllegalArgumentException: The className - * passed in parameter is null, the ObjectName passed - * in parameter contains a pattern or no ObjectName - * is specified for the MBean. - * - */ - public ObjectInstance createMBean(String className, ObjectName name, - ObjectName loaderName, Object params[], - String signature[]) - throws ReflectionException, InstanceAlreadyExistsException, - MBeanRegistrationException, MBeanException, - NotCompliantMBeanException, InstanceNotFoundException; - - /** - * Registers a pre-existing object as an MBean with the MBean - * server. If the object name given is null, the MBean must - * provide its own name by implementing the {@link - * javax.management.MBeanRegistration MBeanRegistration} interface - * and returning the name from the {@link - * javax.management.MBeanRegistration#preRegister preRegister} method. - * - * @param object The MBean to be registered as an MBean. - * @param name The object name of the MBean. May be null. - * - * @return The ObjectInstance for the MBean that has - * been registered. - * - * @exception InstanceAlreadyExistsException The MBean is already - * under the control of the MBean server. - * @exception MBeanRegistrationException The - * preRegister (MBeanRegistration - * interface) method of the MBean has thrown an exception. The - * MBean will not be registered. - * @exception NotCompliantMBeanException This object is not a JMX - * compliant MBean - * @exception RuntimeOperationsException Wraps a - * java.lang.IllegalArgumentException: The object - * passed in parameter is null or no object name is specified. - */ - public ObjectInstance registerMBean(Object object, ObjectName name) - throws InstanceAlreadyExistsException, MBeanRegistrationException, - NotCompliantMBeanException; - - /** - * Unregisters an MBean from the MBean server. The MBean is - * identified by its object name. Once the method has been - * invoked, the MBean may no longer be accessed by its object - * name. - * - * @param name The object name of the MBean to be unregistered. - * - * @exception InstanceNotFoundException The MBean specified is not - * registered in the MBean server. - * @exception MBeanRegistrationException The preDeregister - * ((MBeanRegistration interface) method of the MBean - * has thrown an exception. - * @exception RuntimeOperationsException Wraps a - * java.lang.IllegalArgumentException: The object - * name in parameter is null or the MBean you are when trying to - * unregister is the {@link javax.management.MBeanServerDelegate - * MBeanServerDelegate} MBean. - * - */ - public void unregisterMBean(ObjectName name) - throws InstanceNotFoundException, MBeanRegistrationException; - - /** - * Gets the ObjectInstance for a given MBean - * registered with the MBean server. - * - * @param name The object name of the MBean. - * - * @return The ObjectInstance associated to the MBean - * specified by name. - * - * @exception InstanceNotFoundException The MBean specified is not - * registered in the MBean server. - */ - public ObjectInstance getObjectInstance(ObjectName name) - throws InstanceNotFoundException; - - /** - * Gets MBeans controlled by the MBean server. This method allows - * any of the following to be obtained: All MBeans, a set of - * MBeans specified by pattern matching on the - * ObjectName and/or a Query expression, a specific - * MBean. When the object name is null or no domain and key - * properties are specified, all objects are to be selected (and - * filtered if a query is specified). It returns the set of - * ObjectInstance objects (containing the - * ObjectName and the Java Class name) for the - * selected MBeans. - * - * @param name The object name pattern identifying the MBeans to - * be retrieved. If null or no domain and key properties are - * specified, all the MBeans registered will be retrieved. - * @param query The query expression to be applied for selecting - * MBeans. If null no query expression will be applied for - * selecting MBeans. - * - * @return A set containing the ObjectInstance - * objects for the selected MBeans. If no MBean satisfies the - * query an empty list is returned. - */ - public Set queryMBeans(ObjectName name, QueryExp query); - - /** - * Gets the names of MBeans controlled by the MBean server. This - * method enables any of the following to be obtained: The names - * of all MBeans, the names of a set of MBeans specified by - * pattern matching on the ObjectName and/or a Query - * expression, a specific MBean name (equivalent to testing - * whether an MBean is registered). When the object name is null - * or no domain and key properties are specified, all objects are - * selected (and filtered if a query is specified). It returns the - * set of ObjectNames for the MBeans selected. - * - * @param name The object name pattern identifying the MBean names - * to be retrieved. If null oror no domain and key properties are - * specified, the name of all registered MBeans will be retrieved. - * @param query The query expression to be applied for selecting - * MBeans. If null no query expression will be applied for - * selecting MBeans. - * - * @return A set containing the ObjectNames for the MBeans - * selected. If no MBean satisfies the query, an empty list is - * returned. - */ - public Set queryNames(ObjectName name, QueryExp query); - - /** - * Checks whether an MBean, identified by its object name, is - * already registered with the MBean server. - * - * @param name The object name of the MBean to be checked. - * - * @return True if the MBean is already registered in the MBean - * server, false otherwise. - * - * @exception RuntimeOperationsException Wraps a - * java.lang.IllegalArgumentException: The object - * name in parameter is null. - */ - public boolean isRegistered(ObjectName name); - - /** - * Returns the number of MBeans registered in the MBean server. - */ - public Integer getMBeanCount(); - - /** - * Gets the value of a specific attribute of a named MBean. The MBean - * is identified by its object name. - * - * @param name The object name of the MBean from which the - * attribute is to be retrieved. - * @param attribute A String specifying the name of the attribute - * to be retrieved. - * - * @return The value of the retrieved attribute. - * - * @exception AttributeNotFoundException The attribute specified - * is not accessible in the MBean. - * @exception MBeanException Wraps an exception thrown by the - * MBean's getter. - * @exception InstanceNotFoundException The MBean specified is not - * registered in the MBean server. - * @exception ReflectionException Wraps a - * java.lang.Exception thrown when trying to invoke - * the setter. - * @exception RuntimeOperationsException Wraps a - * java.lang.IllegalArgumentException: The object - * name in parameter is null or the attribute in parameter is - * null. - */ - public Object getAttribute(ObjectName name, String attribute) - throws MBeanException, AttributeNotFoundException, - InstanceNotFoundException, ReflectionException; - - /** - * Enables the values of several attributes of a named MBean. The MBean - * is identified by its object name. - * - * @param name The object name of the MBean from which the - * attributes are retrieved. - * @param attributes A list of the attributes to be retrieved. - * - * @return The list of the retrieved attributes. - * - * @exception InstanceNotFoundException The MBean specified is not - * registered in the MBean server. - * @exception ReflectionException An exception occurred when - * trying to invoke the getAttributes method of a Dynamic MBean. - * @exception RuntimeOperationsException Wrap a - * java.lang.IllegalArgumentException: The object - * name in parameter is null or attributes in parameter is null. - */ - public AttributeList getAttributes(ObjectName name, String[] attributes) - throws InstanceNotFoundException, ReflectionException; - - /** - * Sets the value of a specific attribute of a named MBean. The MBean - * is identified by its object name. - * - * @param name The name of the MBean within which the attribute is - * to be set. - * @param attribute The identification of the attribute to be set - * and the value it is to be set to. - * - * @exception InstanceNotFoundException The MBean specified is not - * registered in the MBean server. - * @exception AttributeNotFoundException The attribute specified - * is not accessible in the MBean. - * @exception InvalidAttributeValueException The value specified - * for the attribute is not valid. - * @exception MBeanException Wraps an exception thrown by the - * MBean's setter. - * @exception ReflectionException Wraps a - * java.lang.Exception thrown when trying to invoke - * the setter. - * @exception RuntimeOperationsException Wraps a - * java.lang.IllegalArgumentException: The object - * name in parameter is null or the attribute in parameter is - * null. - */ - public void setAttribute(ObjectName name, Attribute attribute) - throws InstanceNotFoundException, AttributeNotFoundException, - InvalidAttributeValueException, MBeanException, - ReflectionException; - - - - /** - * Sets the values of several attributes of a named MBean. The MBean is - * identified by its object name. - * - * @param name The object name of the MBean within which the - * attributes are to be set. - * @param attributes A list of attributes: The identification of - * the attributes to be set and the values they are to be set to. - * - * @return The list of attributes that were set, with their new - * values. - * - * @exception InstanceNotFoundException The MBean specified is not - * registered in the MBean server. - * @exception ReflectionException An exception occurred when - * trying to invoke the getAttributes method of a Dynamic MBean. - * @exception RuntimeOperationsException Wraps a - * java.lang.IllegalArgumentException: The object - * name in parameter is null or attributes in parameter is null. - */ - public AttributeList setAttributes(ObjectName name, - AttributeList attributes) - throws InstanceNotFoundException, ReflectionException; - - /** - * Invokes an operation on an MBean. - * - * @param name The object name of the MBean on which the method is - * to be invoked. - * @param operationName The name of the operation to be invoked. - * @param params An array containing the parameters to be set when - * the operation is invoked - * @param signature An array containing the signature of the - * operation. The class objects will be loaded using the same - * class loader as the one used for loading the MBean on which the - * operation was invoked. - * - * @return The object returned by the operation, which represents - * the result ofinvoking the operation on the MBean specified. - * - * @exception InstanceNotFoundException The MBean specified is not - * registered in the MBean server. - * @exception MBeanException Wraps an exception thrown by the - * MBean's invoked method. - * @exception ReflectionException Wraps a - * java.lang.Exception thrown while trying to invoke - * the method. - */ - public Object invoke(ObjectName name, String operationName, - Object params[], String signature[]) - throws InstanceNotFoundException, MBeanException, - ReflectionException; - - /** - * Returns the default domain used for naming the MBean. - * The default domain name is used as the domain part in the ObjectName - * of MBeans if no domain is specified by the user. - */ - public String getDefaultDomain(); - +public interface MBeanServerInterceptor extends MBeanServer { /** - * Returns the list of domains in which any MBean is currently - * registered. + * This method should never be called. + * Usually hrows UnsupportedOperationException. */ - public String[] getDomains(); - + public Object instantiate(String className) + throws ReflectionException, MBeanException; /** - *

      Adds a listener to a registered MBean.

      - * - *

      A notification emitted by an MBean will be forwarded by the - * MBeanServer to the listener. If the source of the notification - * is a reference to an MBean object, the MBean server will replace it - * by that MBean's ObjectName. Otherwise the source is unchanged. - * - * @param name The name of the MBean on which the listener should - * be added. - * @param listener The listener object which will handle the - * notifications emitted by the registered MBean. - * @param filter The filter object. If filter is null, no - * filtering will be performed before handling notifications. - * @param handback The context to be sent to the listener when a - * notification is emitted. - * - * @exception InstanceNotFoundException The MBean name provided - * does not match any of the registered MBeans. + * This method should never be called. + * Usually throws UnsupportedOperationException. */ - public void addNotificationListener(ObjectName name, - NotificationListener listener, - NotificationFilter filter, - Object handback) - throws InstanceNotFoundException; - - + public Object instantiate(String className, ObjectName loaderName) + throws ReflectionException, MBeanException, + InstanceNotFoundException; /** - *

      Adds a listener to a registered MBean.

      - * - *

      A notification emitted by an MBean will be forwarded by the - * MBeanServer to the listener. If the source of the notification - * is a reference to an MBean object, the MBean server will - * replace it by that MBean's ObjectName. Otherwise the source is - * unchanged.

      - * - *

      The listener object that receives notifications is the one - * that is registered with the given name at the time this method - * is called. Even if it is subsequently unregistered, it will - * continue to receive notifications.

      - * - * @param name The name of the MBean on which the listener should - * be added. - * @param listener The object name of the listener which will - * handle the notifications emitted by the registered MBean. - * @param filter The filter object. If filter is null, no - * filtering will be performed before handling notifications. - * @param handback The context to be sent to the listener when a - * notification is emitted. - * - * @exception InstanceNotFoundException The MBean name of the - * notification listener or of the notification broadcaster does - * not match any of the registered MBeans. - * @exception RuntimeOperationsException Wraps an {@link - * IllegalArgumentException}. The MBean named by - * listener exists but does not implement the {@link - * NotificationListener} interface. - * @exception IOException A communication problem occurred when - * talking to the MBean server. + * This method should never be called. + * Usually throws UnsupportedOperationException. */ - public void addNotificationListener(ObjectName name, - ObjectName listener, - NotificationFilter filter, - Object handback) - throws InstanceNotFoundException; + public Object instantiate(String className, Object[] params, + String[] signature) throws ReflectionException, MBeanException; /** - * Removes a listener from a registered MBean. - * - *

      If the listener is registered more than once, perhaps with - * different filters or callbacks, this method will remove all - * those registrations. - * - * @param name The name of the MBean on which the listener should - * be removed. - * @param listener The object name of the listener to be removed. - * - * @exception InstanceNotFoundException The MBean name provided - * does not match any of the registered MBeans. - * @exception ListenerNotFoundException The listener is not - * registered in the MBean. + * This method should never be called. + * Usually throws UnsupportedOperationException. */ - public void removeNotificationListener(ObjectName name, - ObjectName listener) - throws InstanceNotFoundException, ListenerNotFoundException; + public Object instantiate(String className, ObjectName loaderName, + Object[] params, String[] signature) + throws ReflectionException, MBeanException, + InstanceNotFoundException; /** - *

      Removes a listener from a registered MBean.

      - * - *

      The MBean must have a listener that exactly matches the - * given listener, filter, and - * handback parameters. If there is more than one - * such listener, only one is removed.

      - * - *

      The filter and handback parameters - * may be null if and only if they are null in a listener to be - * removed.

      - * - * @param name The name of the MBean on which the listener should - * be removed. - * @param listener A listener that was previously added to this - * MBean. - * @param filter The filter that was specified when the listener - * was added. - * @param handback The handback that was specified when the - * listener was added. - * - * @exception InstanceNotFoundException The MBean name provided - * does not match any of the registered MBeans. - * @exception ListenerNotFoundException The listener is not - * registered in the MBean, or it is not registered with the given - * filter and handback. + * This method should never be called. + * Usually throws UnsupportedOperationException. */ - public void removeNotificationListener(ObjectName name, - ObjectName listener, - NotificationFilter filter, - Object handback) - throws InstanceNotFoundException, ListenerNotFoundException; - + @Deprecated + public ObjectInputStream deserialize(ObjectName name, byte[] data) + throws InstanceNotFoundException, OperationsException; /** - *

      Removes a listener from a registered MBean.

      - * - *

      If the listener is registered more than once, perhaps with - * different filters or callbacks, this method will remove all - * those registrations. - * - * @param name The name of the MBean on which the listener should - * be removed. - * @param listener The listener object which will handle the - * notifications emitted by the registered MBean. - * - * @exception InstanceNotFoundException The MBean name provided - * does not match any of the registered MBeans. - * @exception ListenerNotFoundException The listener is not - * registered in the MBean. + * This method should never be called. + * Usually throws UnsupportedOperationException. */ - public void removeNotificationListener(ObjectName name, - NotificationListener listener) - throws InstanceNotFoundException, ListenerNotFoundException; + @Deprecated + public ObjectInputStream deserialize(String className, byte[] data) + throws OperationsException, ReflectionException; /** - *

      Removes a listener from a registered MBean.

      - * - *

      The MBean must have a listener that exactly matches the - * given listener, filter, and - * handback parameters. If there is more than one - * such listener, only one is removed.

      - * - *

      The filter and handback parameters - * may be null if and only if they are null in a listener to be - * removed.

      - * - * @param name The name of the MBean on which the listener should - * be removed. - * @param listener A listener that was previously added to this - * MBean. - * @param filter The filter that was specified when the listener - * was added. - * @param handback The handback that was specified when the - * listener was added. - * - * @exception InstanceNotFoundException The MBean name provided - * does not match any of the registered MBeans. - * @exception ListenerNotFoundException The listener is not - * registered in the MBean, or it is not registered with the given - * filter and handback. + * This method should never be called. + * Usually hrows UnsupportedOperationException. */ - public void removeNotificationListener(ObjectName name, - NotificationListener listener, - NotificationFilter filter, - Object handback) - throws InstanceNotFoundException, ListenerNotFoundException; + @Deprecated + public ObjectInputStream deserialize(String className, + ObjectName loaderName, byte[] data) + throws InstanceNotFoundException, OperationsException, + ReflectionException; /** - * This method discovers the attributes and operations that an - * MBean exposes for management. - * - * @param name The name of the MBean to analyze - * - * @return An instance of MBeanInfo allowing the - * retrieval of all attributes and operations of this MBean. - * - * @exception IntrospectionException An exception occurred during - * introspection. - * @exception InstanceNotFoundException The MBean specified was - * not found. - * @exception ReflectionException An exception occurred when - * trying to invoke the getMBeanInfo of a Dynamic MBean. + * This method should never be called. + * Usually throws UnsupportedOperationException. */ - public MBeanInfo getMBeanInfo(ObjectName name) - throws InstanceNotFoundException, IntrospectionException, - ReflectionException; - - - /** - * Returns true if the MBean specified is an instance of the - * specified class, false otherwise. - * - * @param name The ObjectName of the MBean. - * @param className The name of the class. - * - * @return true if the MBean specified is an instance of the - * specified class, false otherwise. - * - * @exception InstanceNotFoundException The MBean specified is not - * registered in the MBean server. - */ - public boolean isInstanceOf(ObjectName name, String className) - throws InstanceNotFoundException; - - /** - *

      Return the {@link java.lang.ClassLoader} that was used for - * loading the class of the named MBean. - * @param mbeanName The ObjectName of the MBean. - * @return The ClassLoader used for that MBean. - * @exception InstanceNotFoundException if the named MBean is not found. - */ - public ClassLoader getClassLoaderFor(ObjectName mbeanName) - throws InstanceNotFoundException; - - /** - *

      Return the named {@link java.lang.ClassLoader}. - * @param loaderName The ObjectName of the ClassLoader. - * @return The named ClassLoader. - * @exception InstanceNotFoundException if the named ClassLoader is - * not found. - */ - public ClassLoader getClassLoader(ObjectName loaderName) - throws InstanceNotFoundException; + public ClassLoaderRepository getClassLoaderRepository(); } + diff --git a/src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptorSupport.java b/src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptorSupport.java new file mode 100644 index 000000000..8e1cf681e --- /dev/null +++ b/src/share/classes/com/sun/jmx/interceptor/MBeanServerInterceptorSupport.java @@ -0,0 +1,127 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.interceptor; + +import java.io.ObjectInputStream; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.ObjectName; +import javax.management.OperationsException; +import javax.management.ReflectionException; +import javax.management.loading.ClassLoaderRepository; + +/** + * An abstract class for MBeanServerInterceptorSupport. + * Some methods in MBeanServerInterceptor should never be called. + * This base class provides an implementation of these methods that simply + * throw an {@link UnsupportedOperationException}. + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +public abstract class MBeanServerInterceptorSupport + implements MBeanServerInterceptor { + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + public Object instantiate(String className) + throws ReflectionException, MBeanException { + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + public Object instantiate(String className, ObjectName loaderName) + throws ReflectionException, MBeanException, + InstanceNotFoundException { + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + public Object instantiate(String className, Object[] params, + String[] signature) throws ReflectionException, MBeanException { + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + public Object instantiate(String className, ObjectName loaderName, + Object[] params, String[] signature) + throws ReflectionException, MBeanException, + InstanceNotFoundException { + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + @Deprecated + public ObjectInputStream deserialize(ObjectName name, byte[] data) + throws InstanceNotFoundException, OperationsException { + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + @Deprecated + public ObjectInputStream deserialize(String className, byte[] data) + throws OperationsException, ReflectionException { + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + @Deprecated + public ObjectInputStream deserialize(String className, + ObjectName loaderName, byte[] data) + throws InstanceNotFoundException, OperationsException, + ReflectionException { + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + public ClassLoaderRepository getClassLoaderRepository() { + throw new UnsupportedOperationException("Not applicable."); + } + +} diff --git a/src/share/classes/com/sun/jmx/interceptor/NamespaceDispatchInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/NamespaceDispatchInterceptor.java new file mode 100644 index 000000000..dfd4e659f --- /dev/null +++ b/src/share/classes/com/sun/jmx/interceptor/NamespaceDispatchInterceptor.java @@ -0,0 +1,236 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.interceptor; + +import com.sun.jmx.defaults.JmxProperties; +import com.sun.jmx.mbeanserver.MBeanInstantiator; +import com.sun.jmx.mbeanserver.Repository; +import com.sun.jmx.mbeanserver.Util; +import com.sun.jmx.namespace.NamespaceInterceptor; + +import java.util.Queue; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.management.MBeanServer; +import javax.management.MBeanServerDelegate; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.namespace.JMXDomain; +import javax.management.namespace.JMXNamespace; +import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR; + +/** + * A dispatcher that dispatches to NamespaceInterceptors. + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +public class NamespaceDispatchInterceptor + extends DispatchInterceptor { + + /** + * A logger for this class. + **/ + private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; + + private static final int NAMESPACE_SEPARATOR_LENGTH = + NAMESPACE_SEPARATOR.length(); + + private final DomainDispatchInterceptor localNamespace; + private final String serverName; + + /** + * Creates a NamespaceDispatchInterceptor with the specified + * repository instance. + *

      Do not forget to call initialize(outer,delegate) + * before using this object. + * + * @param outer A pointer to the MBeanServer object that must be + * passed to the MBeans when invoking their + * {@link javax.management.MBeanRegistration} interface. + * @param delegate A pointer to the MBeanServerDelegate associated + * with the new MBeanServer. The new MBeanServer must register + * this MBean in its MBean repository. + * @param instantiator The MBeanInstantiator that will be used to + * instantiate MBeans and take care of class loading issues. + * @param repository The repository to use for this MBeanServer + */ + public NamespaceDispatchInterceptor(MBeanServer outer, + MBeanServerDelegate delegate, + MBeanInstantiator instantiator, + Repository repository) { + localNamespace = new DomainDispatchInterceptor(outer,delegate, + instantiator,repository,this); + serverName = Util.getMBeanServerSecurityName(delegate); + } + + // TODO: Should move that to JMXNamespace? or to ObjectName? + /** + * Get first name space in ObjectName path. Ignore leading namespace + * separators. + **/ + public static String getFirstNamespace(ObjectName name) { + if (name == null) return ""; + final String domain = name.getDomain(); + if (domain.equals("")) return ""; + + int first = 0; + int end = domain.indexOf(NAMESPACE_SEPARATOR,first); + while (end == first) { + first = end+NAMESPACE_SEPARATOR_LENGTH; + end = domain.indexOf(NAMESPACE_SEPARATOR,first); + if (end == -1) break; + } + + if (end == -1) return ""; + + final String namespace = domain.substring(first,end); + + return namespace; + } + + /** + * Called by the DefaultMBeanServerInterceptor, just before adding an + * MBean to the repository. + * + * @param resource the MBean to be registered. + * @param logicalName the name of the MBean to be registered. + */ + final void checkLocallyRegistrable(Object resource, + ObjectName logicalName) { + if (!(resource instanceof JMXNamespace) && + logicalName.getDomain().contains(NAMESPACE_SEPARATOR)) + throw new IllegalArgumentException(String.valueOf(logicalName)+ + ": Invalid ObjectName for an instance of " + + resource.getClass().getName()); + } + + final boolean isLocalHandlerNameFor(String namespace, + ObjectName handlerName) { + return handlerName.getDomain().equals(namespace+NAMESPACE_SEPARATOR) && + JMXNamespace.TYPE_ASSIGNMENT.equals( + handlerName.getKeyPropertyListString()); + } + + @Override + final MBeanServer getInterceptorOrNullFor(ObjectName name) { + final String namespace = getFirstNamespace(name); + if (namespace.equals("") || isLocalHandlerNameFor(namespace,name) || + name.getDomain().equals(namespace+NAMESPACE_SEPARATOR)) { + LOG.finer("dispatching to local name space"); + return localNamespace; + } + final NamespaceInterceptor ns = getInterceptor(namespace); + if (LOG.isLoggable(Level.FINER)) { + if (ns != null) { + LOG.finer("dispatching to name space: " + namespace); + } else { + LOG.finer("no handler for: " + namespace); + } + } + return ns; + } + + @Override + final QueryInterceptor getInterceptorForQuery(ObjectName pattern) { + final String namespace = getFirstNamespace(pattern); + if (namespace.equals("") || isLocalHandlerNameFor(namespace,pattern) || + pattern.getDomain().equals(namespace+NAMESPACE_SEPARATOR)) { + LOG.finer("dispatching to local name space"); + return new QueryInterceptor(localNamespace); + } + final NamespaceInterceptor ns = getInterceptor(namespace); + if (LOG.isLoggable(Level.FINER)) { + if (ns != null) { + LOG.finer("dispatching to name space: " + namespace); + } else { + LOG.finer("no handler for: " + namespace); + } + } + if (ns == null) return null; + return new QueryInterceptor(ns); + } + + @Override + final ObjectName getHandlerNameFor(String key) + throws MalformedObjectNameException { + return ObjectName.getInstance(key+NAMESPACE_SEPARATOR, + "type", JMXNamespace.TYPE); + } + + @Override + final public String getHandlerKey(ObjectName name) { + return getFirstNamespace(name); + } + + @Override + final NamespaceInterceptor createInterceptorFor(String key, + ObjectName name, JMXNamespace handler, + Queue postRegisterQueue) { + final NamespaceInterceptor ns = + new NamespaceInterceptor(serverName,handler,key); + if (LOG.isLoggable(Level.FINER)) { + LOG.finer("NamespaceInterceptor created: "+ns); + } + return ns; + } + + @Override + final DomainDispatchInterceptor getNextInterceptor() { + return localNamespace; + } + + /** + * Returns the list of domains in which any MBean is currently + * registered. + */ + @Override + public String[] getDomains() { + return localNamespace.getDomains(); + } + + @Override + public void addNamespace(ObjectName name, JMXNamespace handler, + Queue postRegisterQueue) { + if (handler instanceof JMXDomain) + localNamespace.addNamespace(name, + (JMXDomain)handler,postRegisterQueue); + else super.addNamespace(name,handler,postRegisterQueue); + } + + @Override + public void removeNamespace(ObjectName name, JMXNamespace handler, + Queue postDeregisterQueue) { + if (handler instanceof JMXDomain) + localNamespace.removeNamespace(name,(JMXDomain)handler, + postDeregisterQueue); + else super.removeNamespace(name,handler,postDeregisterQueue); + } + + +} diff --git a/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java b/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java index ec53998f5..4a47d4ce3 100644 --- a/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java +++ b/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java @@ -51,6 +51,8 @@ import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.management.QueryExp; import javax.management.ReflectionException; +import javax.management.namespace.JMXNamespaces; +import javax.management.namespace.MBeanServerSupport; import javax.management.remote.IdentityMBeanServerForwarder; public class SingleMBeanForwarder extends IdentityMBeanServerForwarder { @@ -285,14 +287,14 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder { if (!pattern.apply(mbeanName)) return false; -// final String dompat = pattern.getDomain(); -// if (!dompat.contains(JMXNamespaces.NAMESPACE_SEPARATOR)) -// return true; // We already checked that patterns apply. -// -// if (mbeanName.getDomain().endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) { -// // only matches if pattern ends with // -// return dompat.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR); -// } + final String dompat = pattern.getDomain(); + if (!dompat.contains(JMXNamespaces.NAMESPACE_SEPARATOR)) + return true; // We already checked that patterns apply. + + if (mbeanName.getDomain().endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) { + // only matches if pattern ends with // + return dompat.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR); + } // should not come here, unless mbeanName contains a // in the // middle of its domain, which would be weird. diff --git a/src/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java b/src/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java index 1095897d9..777cd1cce 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/JmxMBeanServer.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,44 +25,42 @@ package com.sun.jmx.mbeanserver; -import java.util.Iterator; -import java.util.logging.Level; -import java.util.Set; +import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; +import com.sun.jmx.interceptor.NamespaceDispatchInterceptor; + import java.io.ObjectInputStream; import java.security.AccessController; import java.security.Permission; import java.security.PrivilegedExceptionAction; +import java.util.Iterator; +import java.util.Set; +import java.util.logging.Level; -// RI import -import javax.management.MBeanPermission; +import javax.management.Attribute; +import javax.management.AttributeList; import javax.management.AttributeNotFoundException; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.ListenerNotFoundException; import javax.management.MBeanException; -import javax.management.ReflectionException; import javax.management.MBeanInfo; -import javax.management.QueryExp; -import javax.management.NotificationListener; -import javax.management.NotificationFilter; -import javax.management.ListenerNotFoundException; -import javax.management.IntrospectionException; -import javax.management.OperationsException; -import javax.management.InstanceNotFoundException; -import javax.management.NotCompliantMBeanException; +import javax.management.MBeanPermission; import javax.management.MBeanRegistrationException; -import javax.management.InstanceAlreadyExistsException; -import javax.management.InvalidAttributeValueException; -import javax.management.ObjectName; -import javax.management.ObjectInstance; -import javax.management.Attribute; -import javax.management.AttributeList; -import javax.management.RuntimeOperationsException; import javax.management.MBeanServer; import javax.management.MBeanServerDelegate; +import javax.management.NotCompliantMBeanException; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.OperationsException; +import javax.management.QueryExp; +import javax.management.ReflectionException; +import javax.management.RuntimeOperationsException; import javax.management.loading.ClassLoaderRepository; -import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; -import com.sun.jmx.interceptor.DefaultMBeanServerInterceptor; -import com.sun.jmx.interceptor.MBeanServerInterceptor; - /** * This is the base class for MBean manipulation on the agent side. It * contains the methods necessary for the creation, registration, and @@ -102,15 +100,14 @@ public final class JmxMBeanServer /** true if interceptors are enabled **/ private final boolean interceptorsEnabled; - /** Revisit: transient ??? **/ - private final transient MBeanServer outerShell; + private final MBeanServer outerShell; - /** Revisit: transient ??? **/ - private transient MBeanServerInterceptor mbsInterceptor = null; + private volatile MBeanServer mbsInterceptor = null; - /** Revisit: transient ??? **/ /** The MBeanServerDelegate object representing the MBean Server */ - private final transient MBeanServerDelegate mBeanServerDelegateObject; + private final MBeanServerDelegate mBeanServerDelegateObject; + + private final String mbeanServerName; /** * Package: Creates an MBeanServer with the @@ -243,9 +240,10 @@ public final class JmxMBeanServer final Repository repository = new Repository(domain,fairLock); this.mbsInterceptor = - new DefaultMBeanServerInterceptor(outer, delegate, instantiator, + new NamespaceDispatchInterceptor(outer, delegate, instantiator, repository); this.interceptorsEnabled = interceptors; + this.mbeanServerName = Util.getMBeanServerSecurityName(delegate); initialize(); } @@ -941,7 +939,8 @@ public final class JmxMBeanServer throws ReflectionException, MBeanException { /* Permission check */ - checkMBeanPermission(className, null, null, "instantiate"); + checkMBeanPermission(mbeanServerName, className, null, null, + "instantiate"); return instantiator.instantiate(className); } @@ -978,7 +977,8 @@ public final class JmxMBeanServer InstanceNotFoundException { /* Permission check */ - checkMBeanPermission(className, null, null, "instantiate"); + checkMBeanPermission(mbeanServerName, className, null, + null, "instantiate"); ClassLoader myLoader = outerShell.getClass().getClassLoader(); return instantiator.instantiate(className, loaderName, myLoader); @@ -1016,7 +1016,8 @@ public final class JmxMBeanServer throws ReflectionException, MBeanException { /* Permission check */ - checkMBeanPermission(className, null, null, "instantiate"); + checkMBeanPermission(mbeanServerName, className, null, null, + "instantiate"); ClassLoader myLoader = outerShell.getClass().getClassLoader(); return instantiator.instantiate(className, params, signature, @@ -1059,7 +1060,8 @@ public final class JmxMBeanServer InstanceNotFoundException { /* Permission check */ - checkMBeanPermission(className, null, null, "instantiate"); + checkMBeanPermission(mbeanServerName, className, null, + null, "instantiate"); ClassLoader myLoader = outerShell.getClass().getClassLoader(); return instantiator.instantiate(className,loaderName,params,signature, @@ -1236,7 +1238,7 @@ public final class JmxMBeanServer "Unexpected exception occurred", e); } throw new - IllegalStateException("Can't register delegate."); + IllegalStateException("Can't register delegate.",e); } @@ -1278,7 +1280,7 @@ public final class JmxMBeanServer * are not enabled on this object. * @see #interceptorsEnabled **/ - public synchronized MBeanServerInterceptor getMBeanServerInterceptor() { + public synchronized MBeanServer getMBeanServerInterceptor() { if (interceptorsEnabled) return mbsInterceptor; else throw new UnsupportedOperationException( "MBeanServerInterceptors are disabled."); @@ -1292,7 +1294,7 @@ public final class JmxMBeanServer * @see #interceptorsEnabled **/ public synchronized void - setMBeanServerInterceptor(MBeanServerInterceptor interceptor) { + setMBeanServerInterceptor(MBeanServer interceptor) { if (!interceptorsEnabled) throw new UnsupportedOperationException( "MBeanServerInterceptors are disabled."); if (interceptor == null) throw new @@ -1330,7 +1332,8 @@ public final class JmxMBeanServer **/ public ClassLoaderRepository getClassLoaderRepository() { /* Permission check */ - checkMBeanPermission(null, null, null, "getClassLoaderRepository"); + checkMBeanPermission(mbeanServerName, null, null, + null, "getClassLoaderRepository"); return secureClr; } @@ -1484,14 +1487,16 @@ public final class JmxMBeanServer // SECURITY CHECKS //---------------- - private static void checkMBeanPermission(String classname, + private static void checkMBeanPermission(String serverName, + String classname, String member, ObjectName objectName, String actions) throws SecurityException { SecurityManager sm = System.getSecurityManager(); if (sm != null) { - Permission perm = new MBeanPermission(classname, + Permission perm = new MBeanPermission(serverName, + classname, member, objectName, actions); diff --git a/src/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java b/src/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java index 41b3b9891..227c1eec9 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/MXBeanLookup.java @@ -224,7 +224,7 @@ public abstract class MXBeanLookup { throws InvalidObjectException { String domain = prefix + name.getDomain(); try { - name = switchDomain(domain, name); + name = name.withDomain(domain); } catch (MalformedObjectNameException e) { throw EnvHelp.initCause( new InvalidObjectException(e.getMessage()), e); @@ -242,7 +242,7 @@ public abstract class MXBeanLookup { "Proxy's name does not start with " + prefix + ": " + name); } try { - name = switchDomain(domain.substring(prefix.length()), name); + name = name.withDomain(domain.substring(prefix.length())); } catch (MalformedObjectNameException e) { throw EnvHelp.initCause(new OpenDataException(e.getMessage()), e); } @@ -269,14 +269,6 @@ public abstract class MXBeanLookup { currentLookup.set(lookup); } - // Method temporarily added until we have ObjectName.switchDomain in the - // public API. Note that this method DOES NOT PRESERVE the order of - // keys in the ObjectName so it must not be used in the final release. - static ObjectName switchDomain(String domain, ObjectName name) - throws MalformedObjectNameException { - return new ObjectName(domain, name.getKeyPropertyList()); - } - private static final ThreadLocal currentLookup = new ThreadLocal(); diff --git a/src/share/classes/com/sun/jmx/mbeanserver/Repository.java b/src/share/classes/com/sun/jmx/mbeanserver/Repository.java index f41079912..5c3339833 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/Repository.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/Repository.java @@ -45,7 +45,6 @@ import javax.management.QueryExp; import javax.management.RuntimeOperationsException; /** - * The RepositorySupport implements the Repository interface. * This repository does not support persistency. * * @since 1.5 @@ -197,9 +196,9 @@ public class Repository { if (isPropertyValuePattern && pattern.isPropertyValuePattern(keys[i])) { // wildmatch key property values - final char[] val_pattern = values[i].toCharArray(); - final char[] val_string = v.toCharArray(); - if (wildmatch(val_string,val_pattern)) + // values[i] is the pattern; + // v is the string + if (Util.wildmatch(v,values[i])) continue; else return false; @@ -236,86 +235,6 @@ public class Repository { } } - /** Match a string against a shell-style pattern. The only pattern - characters recognised are ?, standing for any one - character, and *, standing for any string of - characters, including the empty string. - - @param str the string to match, as a character array. - @param pat the pattern to match the string against, as a - character array. - - @return true if and only if the string matches the pattern. - */ - /* The algorithm is a classical one. We advance pointers in - parallel through str and pat. If we encounter a star in pat, - we remember its position and continue advancing. If at any - stage we get a mismatch between str and pat, we look to see if - there is a remembered star. If not, we fail. If so, we - retreat pat to just past that star and str to the position - after the last one we tried, and we let the match advance - again. - - Even though there is only one remembered star position, the - algorithm works when there are several stars in the pattern. - When we encounter the second star, we forget the first one. - This is OK, because if we get to the second star in A*B*C - (where A etc are arbitrary strings), we have already seen AXB. - We're therefore setting up a match of *C against the remainder - of the string, which will match if that remainder looks like - YC, so the whole string looks like AXBYC. - */ - public static boolean wildmatch(char[] str, char[] pat) { - int stri; // index in str - int pati; // index in pat - int starstri; // index for backtrack if "*" attempt fails - int starpati; // index for backtrack if "*" attempt fails, +1 - final int strlen = str.length; - final int patlen = pat.length; - - stri = pati = 0; - starstri = starpati = -1; - - /* On each pass through this loop, we either advance pati, - or we backtrack pati and advance starstri. Since starstri - is only ever assigned from pati, the loop must terminate. */ - while (true) { - if (pati < patlen) { - final char patc = pat[pati]; - switch (patc) { - case '?': - if (stri == strlen) - break; - stri++; - pati++; - continue; - case '*': - pati++; - starpati = pati; - starstri = stri; - continue; - default: - if (stri < strlen && str[stri] == patc) { - stri++; - pati++; - continue; - } - break; - } - } else if (stri == strlen) - return true; - - // Mismatched, can we backtrack to a "*"? - if (starpati < 0 || starstri == strlen) - return false; - - // Retry the match one position later in str - pati = starpati; - starstri++; - stri = starstri; - } - } - private void addNewDomMoi(final DynamicMBean object, final String dom, final ObjectName name, @@ -370,7 +289,7 @@ public class Repository { if (name.isPattern()) return null; // Extract the domain name. - String dom= name.getDomain().intern(); + String dom = name.getDomain().intern(); // Default domain case if (dom.length() == 0) { @@ -480,7 +399,7 @@ public class Repository { name = Util.newObjectName(domain + name.toString()); // Do we have default domain ? - if (dom == domain) { + if (dom == domain) { // ES: OK (dom & domain are interned) to_default_domain = true; dom = domain; } else { @@ -652,10 +571,9 @@ public class Repository { } // Pattern matching in the domain name (*, ?) - char[] dom2Match = name.getDomain().toCharArray(); + final String dom2Match = name.getDomain(); for (String dom : domainTb.keySet()) { - char[] theDom = dom.toCharArray(); - if (wildmatch(theDom, dom2Match)) { + if (Util.wildpathmatch(dom, dom2Match)) { final Map moiTb = domainTb.get(dom); if (allNames) result.addAll(moiTb.values()); @@ -726,7 +644,7 @@ public class Repository { // need to reinstantiate a hashtable because of possible // big buckets array size inside table, never cleared, // thus the new ! - if (dom == domain) + if (dom == domain) // ES: OK dom and domain are interned. domainTb.put(domain, new HashMap()); } diff --git a/src/share/classes/com/sun/jmx/mbeanserver/SunJmxMBeanServer.java b/src/share/classes/com/sun/jmx/mbeanserver/SunJmxMBeanServer.java index 6c338387d..0a1ab72ae 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/SunJmxMBeanServer.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/SunJmxMBeanServer.java @@ -28,17 +28,16 @@ package com.sun.jmx.mbeanserver; import javax.management.MBeanServer; import javax.management.MBeanServerDelegate; -import com.sun.jmx.interceptor.MBeanServerInterceptor; /** - * Extends the MBeanServer and MBeanServerInterceptor interface to + * Extends the MBeanServer interface to * provide methods for getting the MetaData and MBeanServerInstantiator * objects associated with an MBeanServer. * * @since 1.5 */ public interface SunJmxMBeanServer - extends MBeanServerInterceptor, MBeanServer { + extends MBeanServer { /** * Return the MBeanInstantiator associated to this MBeanServer. @@ -68,7 +67,7 @@ public interface SunJmxMBeanServer * are not enabled on this object. * @see #interceptorsEnabled **/ - public MBeanServerInterceptor getMBeanServerInterceptor(); + public MBeanServer getMBeanServerInterceptor(); /** * Set the MBeanServerInterceptor. @@ -77,7 +76,7 @@ public interface SunJmxMBeanServer * are not enabled on this object. * @see #interceptorsEnabled **/ - public void setMBeanServerInterceptor(MBeanServerInterceptor interceptor); + public void setMBeanServerInterceptor(MBeanServer interceptor); /** *

      Return the MBeanServerDelegate representing the MBeanServer. diff --git a/src/share/classes/com/sun/jmx/mbeanserver/Util.java b/src/share/classes/com/sun/jmx/mbeanserver/Util.java index b0f4f1dbf..fcbdd9041 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/Util.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/Util.java @@ -25,6 +25,8 @@ package com.sun.jmx.mbeanserver; +import com.sun.jmx.defaults.JmxProperties; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -42,11 +44,22 @@ import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import java.util.WeakHashMap; +import java.util.logging.Level; +import javax.management.MBeanServer; +import javax.management.MBeanServerDelegate; +import javax.management.MBeanServerFactory; import javax.management.MalformedObjectNameException; +import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.management.loading.ClassLoaderRepository; +import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR; public class Util { + private final static int NAMESPACE_SEPARATOR_LENGTH = + NAMESPACE_SEPARATOR.length(); + public final static String ILLEGAL_MBEANSERVER_NAME_CHARS=";:*?"; + + static Map newMap() { return new HashMap(); } @@ -145,6 +158,270 @@ public class Util { return hash; } + /** Match a part of a string against a shell-style pattern. + The only pattern characters recognized are ?, + standing for any one character, + and *, standing for any string of + characters, including the empty string. For instance, + {@code wildmatch("sandwich","sa?d*ch",1,4,1,4)} will match + {@code "and"} against {@code "a?d"}. + + @param str the string containing the sequence to match. + @param pat a string containing a pattern to match the sub string + against. + @param stri the index in the string at which matching should begin. + @param strend the index in the string at which the matching should + end. + @param pati the index in the pattern at which matching should begin. + @param patend the index in the pattern at which the matching should + end. + + @return true if and only if the string matches the pattern. + */ + /* The algorithm is a classical one. We advance pointers in + parallel through str and pat. If we encounter a star in pat, + we remember its position and continue advancing. If at any + stage we get a mismatch between str and pat, we look to see if + there is a remembered star. If not, we fail. If so, we + retreat pat to just past that star and str to the position + after the last one we tried, and we let the match advance + again. + + Even though there is only one remembered star position, the + algorithm works when there are several stars in the pattern. + When we encounter the second star, we forget the first one. + This is OK, because if we get to the second star in A*B*C + (where A etc are arbitrary strings), we have already seen AXB. + We're therefore setting up a match of *C against the remainder + of the string, which will match if that remainder looks like + YC, so the whole string looks like AXBYC. + */ + private static boolean wildmatch(final String str, final String pat, + int stri, final int strend, int pati, final int patend) { + + // System.out.println("matching "+pat.substring(pati,patend)+ + // " against "+str.substring(stri, strend)); + int starstri; // index for backtrack if "*" attempt fails + int starpati; // index for backtrack if "*" attempt fails, +1 + + starstri = starpati = -1; + + /* On each pass through this loop, we either advance pati, + or we backtrack pati and advance starstri. Since starstri + is only ever assigned from pati, the loop must terminate. */ + while (true) { + if (pati < patend) { + final char patc = pat.charAt(pati); + switch (patc) { + case '?': + if (stri == strend) + break; + stri++; + pati++; + continue; + case '*': + pati++; + starpati = pati; + starstri = stri; + continue; + default: + if (stri < strend && str.charAt(stri) == patc) { + stri++; + pati++; + continue; + } + break; + } + } else if (stri == strend) + return true; + + // Mismatched, can we backtrack to a "*"? + if (starpati < 0 || starstri == strend) + return false; + + // Retry the match one position later in str + pati = starpati; + starstri++; + stri = starstri; + } + } + + /** Match a string against a shell-style pattern. The only pattern + characters recognized are ?, standing for any one + character, and *, standing for any string of + characters, including the empty string. + + @param str the string to match. + @param pat the pattern to match the string against. + + @return true if and only if the string matches the pattern. + */ + public static boolean wildmatch(String str, String pat) { + return wildmatch(str,pat,0,str.length(),0,pat.length()); + } + + /** + * Matches a string against a pattern, as a name space path. + * This is a special matching where * and ?? don't match //. + * The string is split in sub-strings separated by //, and the + * pattern is split in sub-patterns separated by //. Each sub-string + * is matched against its corresponding sub-pattern. + * so ////...// matches ////...// + * only if n==q and for ( i = 1 => n) elt-i matches pat-i. + * + * In addition, if we encounter a pattern element which is exactly + * **, it can match any number of path-elements - but it must match at + * least one element. + * When we encounter such a meta-wildcard, we remember its position + * and the position in the string path, and we advance both the pattern + * and the string. Later, if we encounter a mismatch in pattern & string, + * we rewind the position in pattern to just after the meta-wildcard, + * and we backtrack the string to i+1 element after the position + * we had when we first encountered the meta-wildcard, i being the + * position when we last backtracked the string. + * + * The backtracking logic is an adaptation of the logic in wildmatch + * above. + * See test/javax/mangement/ObjectName/ApplyWildcardTest.java + * + * Note: this thing is called 'wild' - and that's for a reason ;-) + **/ + public static boolean wildpathmatch(String str, String pat) { + final int strlen = str.length(); + final int patlen = pat.length(); + int stri = 0; + int pati = 0; + + int starstri; // index for backtrack if "**" attempt fails + int starpati; // index for backtrack if "**" attempt fails + + starstri = starpati = -1; + + while (true) { + // System.out.println("pati="+pati+", stri="+stri); + final int strend = str.indexOf(NAMESPACE_SEPARATOR, stri); + final int patend = pat.indexOf(NAMESPACE_SEPARATOR, pati); + + // no // remaining in either string or pattern: simple wildmatch + // until end of string. + if (strend == -1 && patend == -1) { + // System.out.println("last sub pattern, last sub element..."); + // System.out.println("wildmatch("+str.substring(stri,strlen)+ + // ","+pat.substring(pati,patlen)+")"); + return wildmatch(str,pat,stri,strlen,pati,patlen); + } + + // no // remaining in string, but at least one remaining in + // pattern + // => no match + if (strend == -1) { + // System.out.println("pattern has more // than string..."); + return false; + } + + // strend is != -1, but patend might. + // detect wildcard ** + if (patend == pati+2 && pat.charAt(pati)=='*' && + pat.charAt(pati+1)=='*') { + // if we reach here we know that neither strend nor patend are + // equals to -1. + stri = strend + NAMESPACE_SEPARATOR_LENGTH; + pati = patend + NAMESPACE_SEPARATOR_LENGTH; + starpati = pati; // position just after **// in pattern + starstri = stri; // we eat 1 element in string, and remember + // the position for backtracking and eating + // one more element if needed. + // System.out.println("starpati="+pati); + continue; + } + + // This is a bit hacky: * can match // when // is at the end + // of the string, so we include the // delimiter in the pattern + // matching. Either we're in the middle of the path, so including + // // both at the end of the pattern and at the end of the string + // has no effect - match(*//,dfsd//) is equivalent to match(*,dfsd) + // or we're at the end of the pattern path, in which case + // including // at the end of the string will have the desired + // effect (provided that we detect the end of matching correctly, + // see further on). + // + final int endpat = + ((patend > -1)?patend+NAMESPACE_SEPARATOR_LENGTH:patlen); + final int endstr = + ((strend > -1)?strend+NAMESPACE_SEPARATOR_LENGTH:strlen); + + // if we reach the end of the pattern, or if elt-i & pat-i + // don't match, we have a mismatch. + + // Note: we know that strend != -1, therefore patend==-1 + // indicates a mismatch unless pattern can match + // a // at the end, and strend+2=strlen. + // System.out.println("wildmatch("+str.substring(stri,endstr)+","+ + // pat.substring(pati,endpat)+")"); + if (!wildmatch(str,pat,stri,endstr,pati,endpat)) { + + // System.out.println("nomatch"); + // if we have a mismatch and didn't encounter any meta-wildcard, + // we return false. String & pattern don't match. + if (starpati < 0) return false; + + // If we reach here, we had a meta-wildcard. + // We need to backtrack to the wildcard, and make it eat an + // additional string element. + // + stri = str.indexOf(NAMESPACE_SEPARATOR, starstri); + // System.out.println("eating one additional element? "+stri); + + // If there's no more elements to eat, string and pattern + // don't match => return false. + if (stri == -1) return false; + + // Backtrack to where we were when we last matched against + // the meta-wildcard, make it eat an additional path element, + // remember the new positions, and continue from there... + // + stri = stri + NAMESPACE_SEPARATOR_LENGTH; + starstri = stri; + pati = starpati; + // System.out.println("skiping to stri="+stri); + continue; + } + + // Here we know that strend > -1 but we can have patend == -1. + // + // So if we reach here, we know pat-i+//? has matched + // elt-i+// + // + // If patend==-1, we know that there was no delimiter + // at the end of the pattern, that we are at the last pattern, + // and therefore that pat-i has matched elt-i+// + // + // In that case we can consider that we have a match only if + // elt-i is also the last path element in the string, which is + // equivalent to saying that strend+2==strlen. + // + if (patend == -1 && starpati == -1) + return (strend+NAMESPACE_SEPARATOR_LENGTH==strlen); + + // patend != -1, or starpati > -1 so there remains something + // to match. + + // go to next pair: elt-(i+1) pat-(i+1); + stri = strend + NAMESPACE_SEPARATOR_LENGTH; + pati = (patend==-1)?pati:(patend + NAMESPACE_SEPARATOR_LENGTH); + } + } + + /** + * Returns true if the ObjectName's {@code domain} is selected by the + * given {@code pattern}. + */ + public static boolean isDomainSelected(String domain, String pattern) { + if (domain == null || pattern == null) + throw new IllegalArgumentException("null"); + return Util.wildpathmatch(domain,pattern); + } + /** * Filters a set of ObjectName according to a given pattern. * @@ -167,6 +444,34 @@ public class Util { return res; } + + /** + * Filters a set of ObjectInstance according to a given pattern. + * + * @param pattern the pattern that the returned names must match. + * @param all the set of instances to filter. + * @return a set of ObjectInstance from which non matching instances + * have been removed. + */ + public static Set + filterMatchingInstances(ObjectName pattern, + Set all) { + // If no pattern, just return all names + if (pattern == null + || all.isEmpty() + || ObjectName.WILDCARD.equals(pattern)) + return all; + + // If there's a pattern, do the matching. + final Set res = equivalentEmptySet(all); + for (ObjectInstance n : all) { + if (n == null) continue; + if (pattern.apply(n.getObjectName())) + res.add(n); + } + return res; + } + /** * An abstract ClassLoaderRepository that contains a single class loader. **/ @@ -216,6 +521,160 @@ public class Util { return new SingleClassLoaderRepository(loader); } + /** + * Returns the name of the given MBeanServer that should be put in a + * permission you need. + * This corresponds to the + * {@code *[;mbeanServerName=[;*]]} property + * embedded in the MBeanServerId attribute of the + * server's {@link MBeanServerDelegate}. + * + * @param server The MBean server + * @return the name of the MBeanServer, or "*" if the name couldn't be + * obtained, or {@value MBeanServerFactory#DEFAULT_MBEANSERVER_NAME} + * if there was no name. + */ + public static String getMBeanServerSecurityName(MBeanServer server) { + final String notfound = "*"; + try { + final String mbeanServerId = (String) + server.getAttribute(MBeanServerDelegate.DELEGATE_NAME, + "MBeanServerId"); + final String found = extractMBeanServerName(mbeanServerId); + if (found.length()==0) + return MBeanServerFactory.DEFAULT_MBEANSERVER_NAME; + return found; + } catch (Exception x) { + logshort("Failed to retrieve MBeanServerName for server, " + + "using \"*\"",x); + return notfound; + } + } + + /** + * Returns the name of the MBeanServer embedded in the given + * mbeanServerId. If the given mbeanServerId doesn't contain any name, + * an empty String is returned. + * The MBeanServerId is expected to be of the form: + * {@code *[;mbeanServerName=[;*]]} + * @param mbeanServerId The MBean server ID + * @return the name of the MBeanServer if found, or "" if the name was + * not present in the mbeanServerId. + */ + public static String extractMBeanServerName(String mbeanServerId) { + if (mbeanServerId==null) return ""; + final String beginMarker=";mbeanServerName="; + final String endMarker=";"; + final int found = mbeanServerId.indexOf(beginMarker); + if (found < 0) return ""; + final int start = found + beginMarker.length(); + final int stop = mbeanServerId.indexOf(endMarker, start); + return mbeanServerId.substring(start, + (stop < 0 ? mbeanServerId.length() : stop)); + } + + /** + * Insert the given mbeanServerName into the given mbeanServerId. + * If mbeanServerName is null, empty, or equals to "-", the returned + * mbeanServerId will not contain any mbeanServerName. + * @param mbeanServerId The mbeanServerId in which to insert + * mbeanServerName + * @param mbeanServerName The mbeanServerName + * @return an mbeanServerId containing the given mbeanServerName + * @throws IllegalArgumentException if mbeanServerId already contains + * a different name, or if the given mbeanServerName is not valid. + */ + public static String insertMBeanServerName(String mbeanServerId, + String mbeanServerName) { + final String found = extractMBeanServerName(mbeanServerId); + if (found.length() > 0 && + found.equals(checkServerName(mbeanServerName))) + return mbeanServerId; + if (found.length() > 0 && !isMBeanServerNameUndefined(found)) + throw new IllegalArgumentException( + "MBeanServerName already defined"); + if (isMBeanServerNameUndefined(mbeanServerName)) + return mbeanServerId; + final String beginMarker=";mbeanServerName="; + return mbeanServerId+beginMarker+checkServerName(mbeanServerName); + } + + /** + * Returns true if the given mbeanServerName corresponds to an + * undefined MBeanServerName. + * The mbeanServerName is considered undefined if it is one of: + * {@code null} or {@value MBeanServerFactory#DEFAULT_MBEANSERVER_NAME}. + * @param mbeanServerName The mbeanServerName, as returned by + * {@link #extractMBeanServerName(String)}. + * @return true if the given name corresponds to one of the forms that + * denotes an undefined MBeanServerName. + */ + public static boolean isMBeanServerNameUndefined(String mbeanServerName) { + return mbeanServerName == null || + MBeanServerFactory.DEFAULT_MBEANSERVER_NAME.equals(mbeanServerName); + } + /** + * Check that the provided mbeanServername is syntactically valid. + * @param mbeanServerName An mbeanServerName, or {@code null}. + * @return mbeanServerName, or {@value + * MBeanServerFactory#DEFAULT_MBEANSERVER_NAME} if {@code mbeanServerName} + * is {@code null}. + * @throws IllegalArgumentException if mbeanServerName contains illegal + * characters, or is empty, or is {@code "-"}. + * Illegal characters are {@value #ILLEGAL_MBEANSERVER_NAME_CHARS}. + */ + public static String checkServerName(String mbeanServerName) { + if ("".equals(mbeanServerName)) + throw new IllegalArgumentException( + "\"\" is not a valid MBean server name"); + if ("-".equals(mbeanServerName)) + throw new IllegalArgumentException( + "\"-\" is not a valid MBean server name"); + if (isMBeanServerNameUndefined(mbeanServerName)) + return MBeanServerFactory.DEFAULT_MBEANSERVER_NAME; + for (char c : ILLEGAL_MBEANSERVER_NAME_CHARS.toCharArray()) { + if (mbeanServerName.indexOf(c) >= 0) + throw new IllegalArgumentException( + "invalid character in MBeanServer name: "+c); + } + return mbeanServerName; + } + + /** + * Get the MBeanServer name that should be put in a permission you need. + * + * @param delegate The MBeanServerDelegate + * @return The MBeanServer name - or {@value + * MBeanServerFactory#DEFAULT_MBEANSERVER_NAME} if there was no name. + */ + public static String getMBeanServerSecurityName( + MBeanServerDelegate delegate) { + try { + final String serverName = delegate.getMBeanServerName(); + if (isMBeanServerNameUndefined(serverName)) + return MBeanServerFactory.DEFAULT_MBEANSERVER_NAME; + return serverName; + } catch (Exception x) { + logshort("Failed to retrieve MBeanServerName from delegate, " + + "using \"*\"",x); + return "*"; + } + } + + // Log the exception and its causes without logging the stack trace. + // Use with care - it is usally preferable to log the whole stack trace! + // We don't want to log the whole stack trace here: logshort() is + // called in those cases where the exception might not be abnormal. + private static void logshort(String msg, Throwable t) { + if (JmxProperties.MISC_LOGGER.isLoggable(Level.FINE)) { + StringBuilder toprint = new StringBuilder(msg); + toprint.append("\nCaused By: ").append(String.valueOf(t)); + while ((t=t.getCause())!=null) + toprint.append("\nCaused By: ").append(String.valueOf(t)); + JmxProperties.MISC_LOGGER.fine(toprint.toString()); + } + } + public static Set cloneSet(Set set) { if (set instanceof SortedSet) { @SuppressWarnings("unchecked") @@ -232,10 +691,19 @@ public class Util { @SuppressWarnings("unchecked") SortedSet sset = (SortedSet) set; set = new TreeSet(sset.comparator()); - } else if (set != null) { - set = new HashSet(set.size()); } else set = new HashSet(); return set; } + + // This exception is used when wrapping a class that throws IOException + // in a class that doesn't. + // The typical example for this are JMXNamespaces, when the sub + // MBeanServer can be remote. + // + public static RuntimeException newRuntimeIOException(IOException io) { + final String msg = "Communication failed with underlying resource: "+ + io.getMessage(); + return new RuntimeException(msg,io); + } } diff --git a/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java b/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java new file mode 100644 index 000000000..d7619814d --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java @@ -0,0 +1,475 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.namespace; + +import com.sun.jmx.defaults.JmxProperties; +import com.sun.jmx.mbeanserver.Util; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Queue; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.InstanceNotFoundException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanPermission; +import javax.management.MBeanServerDelegate; +import javax.management.MBeanServerNotification; +import javax.management.MalformedObjectNameException; +import javax.management.Notification; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.QueryExp; +import javax.management.namespace.JMXDomain; + +/** + * A DomainInterceptor wraps a JMXDomain. + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +public class DomainInterceptor extends HandlerInterceptor { + + // TODO: Ideally DomainInterceptor should be replaced by + // something at Repository level. + // The problem there will be that we may need to + // reinstantiate the 'queryPerformedByRepos' boolean + // [or we will need to wrap the repository in + // a 'RepositoryInterceptor'?] + // Also there's no real need for a DomainInterceptor to + // extend RewritingMBeanServerConnection. + + + /** + * A logger for this class. + **/ + private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; + + private final String domainName; + private volatile ObjectName ALL; + private final String serverName; + private volatile NotificationListener mbsListener; + + private static class PatternNotificationFilter + implements NotificationFilter { + + final ObjectName pattern; + public PatternNotificationFilter(ObjectName pattern) { + this.pattern = pattern; + } + + public boolean isNotificationEnabled(Notification notification) { + if (!(notification instanceof MBeanServerNotification)) + return false; + final MBeanServerNotification mbsn = + (MBeanServerNotification) notification; + if (pattern == null || pattern.apply(mbsn.getMBeanName())) + return true; + return false; + } + + static final long serialVersionUID = 7409950927025262111L; + } + + /** + * Creates a new instance of NamespaceInterceptor + */ + public DomainInterceptor(String serverName, + JMXDomain handler, + String domainName) { + super(handler); + this.domainName = domainName; + this.serverName = serverName; + } + + @Override + public String toString() { + return this.getClass().getName()+"(parent="+serverName+ + ", domain="+this.domainName+")"; + } + + public void connectDelegate(final MBeanServerDelegate delegate) + throws InstanceNotFoundException { + final NotificationFilter filter = + new PatternNotificationFilter(getPatternFor(null)); + synchronized (this) { + if (mbsListener == null) + mbsListener = new NotificationListener() { + + public void handleNotification(Notification notification, + Object handback) { + if (filter.isNotificationEnabled(notification)) + delegate.sendNotification(notification); + } + }; + } + + getNamespace(). + addMBeanServerNotificationListener(mbsListener, filter); + } + + public void disconnectDelegate() + throws InstanceNotFoundException, ListenerNotFoundException { + final NotificationListener l; + synchronized (this) { + l = mbsListener; + if (l == null) return; + mbsListener = null; + } + getNamespace().removeMBeanServerNotificationListener(l); + } + + public void addPostRegisterTask(Queue queue, + final MBeanServerDelegate delegate) { + if (queue == null) + throw new IllegalArgumentException("task queue must not be null"); + final Runnable task1 = new Runnable() { + public void run() { + try { + connectDelegate(delegate); + } catch (Exception x) { + throw new UnsupportedOperationException("notification forwarding",x); + } + } + }; + queue.add(task1); + } + + public void addPostDeregisterTask(Queue queue, + final MBeanServerDelegate delegate) { + if (queue == null) + throw new IllegalArgumentException("task queue must not be null"); + final Runnable task1 = new Runnable() { + public void run() { + try { + disconnectDelegate(); + } catch (Exception x) { + throw new UnsupportedOperationException("notification forwarding",x); + } + } + }; + queue.add(task1); + } + + /** + * Throws IllegalArgumentException if targetName.getDomain() is not + * in the domain handled. + **/ + @Override + protected ObjectName toSource(ObjectName targetName) { + if (targetName == null) return null; + if (targetName.isDomainPattern()) return targetName; + final String targetDomain = targetName.getDomain(); + + // TODO: revisit this. RuntimeOperationsException may be better? + // + if (!targetDomain.equals(domainName)) + throw new IllegalArgumentException(targetName.toString()); + return targetName; + } + + @Override + protected ObjectName toTarget(ObjectName sourceName) { + return sourceName; + } + + + + /** + * No rewriting: always return sources - stripping instances for which + * the caller doesn't have permissions. + **/ + @Override + Set processOutputInstances(Set sources) { + if (sources == null || sources.isEmpty() || !checkOn()) + return sources; + final Set res = Util.equivalentEmptySet(sources); + for (ObjectInstance o : sources) { + if (checkQuery(o.getObjectName(), "queryMBeans")) + res.add(o); + } + return res; + } + + + /** + * No rewriting: always return sourceNames - stripping names for which + * the caller doesn't have permissions. + **/ + @Override + Set processOutputNames(Set sourceNames) { + if (sourceNames == null || sourceNames.isEmpty() || !checkOn()) + return sourceNames; + final Set res = Util.equivalentEmptySet(sourceNames); + for (ObjectName o : sourceNames) { + if (checkQuery(o, "queryNames")) + res.add(o); + } + return res; + } + + /** No rewriting: always return source **/ + @Override + ObjectInstance processOutputInstance(ObjectInstance source) { + return source; + } + + @Override + public Set queryNames(ObjectName name, QueryExp query) { + try { + // We don't trust the wrapped JMXDomain... + final ObjectName pattern = getPatternFor(name); + final Set res = super.queryNames(pattern,query); + return Util.filterMatchingNames(pattern,res); + } catch (Exception x) { + if (LOG.isLoggable(Level.FINE)) + LOG.fine("Unexpected exception raised in queryNames: "+x); + LOG.log(Level.FINEST,"Unexpected exception raised in queryNames",x); + } + // We reach here only when an exception was raised. + // + final Set empty = Collections.emptySet(); + return empty; + } + + private ObjectName getPatternFor(final ObjectName name) { + try { + if (ALL == null) ALL = ObjectName.getInstance(domainName + ":*"); + if (name == null) return ALL; + if (name.getDomain().equals(domainName)) return name; + return name.withDomain(domainName); + } catch (MalformedObjectNameException x) { + throw new IllegalArgumentException(String.valueOf(name),x); + } + } + + @Override + public Set queryMBeans(ObjectName name, QueryExp query) { + try { + // We don't trust the wrapped JMXDomain... + final ObjectName pattern = getPatternFor(name); + final Set res = super.queryMBeans(pattern,query); + return Util.filterMatchingInstances(pattern,res); + } catch (Exception x) { + if (LOG.isLoggable(Level.FINE)) + LOG.fine("Unexpected exception raised in queryNames: "+x); + LOG.log(Level.FINEST,"Unexpected exception raised in queryNames",x); + } + // We reach here only when an exception was raised. + // + final Set empty = Collections.emptySet(); + return empty; + } + + @Override + public String getDefaultDomain() { + return domainName; + } + + @Override + public String[] getDomains() { + return new String[] {domainName}; + } + + // We call getMBeanCount() on the namespace rather than on the + // source server in order to avoid counting MBeans which are not + // in the domain. + @Override + public Integer getMBeanCount() { + return getNamespace().getMBeanCount(); + } + + private boolean checkOn() { + final SecurityManager sm = System.getSecurityManager(); + return (sm != null); + } + + // + // Implements permission checks. + // + @Override + void check(ObjectName routingName, String member, String action) { + if (!checkOn()) return; + final String act = (action==null)?"-":action.intern(); + if(act == "queryMBeans" || act == "queryNames") { // ES: OK + // This is tricky. check with 3 parameters is called + // by queryNames/queryMBeans before performing the query. + // At this point we must check with no class name. + // Therefore we pass a className of "-". + // The filtering will be done later - processOutputNames and + // processOutputInstance will call checkQuery. + // + check(routingName, "-", "-", act); + } else { + // This is also tricky: + // passing null here will cause check to retrieve the classname, + // if needed. + check(routingName, null, member, act); + } + } + + // + // Implements permission checks. + // + @Override + void checkCreate(ObjectName routingName, String className, String action) { + if (!checkOn()) return; + check(routingName,className,"-",action); + } + + // + // Implements permission checks. + // + void check(ObjectName routingName, String className, String member, + String action) { + if (!checkOn()) return; + final MBeanPermission perm; + + // action is most probably already an intern string. + // string literals are intern strings. + // we create a new intern string for 'action' - just to be on + // the safe side... + // We intern it in order to be able to use == rather than equals + // below, because if we don't, and if action is not one of the + // 4 literals below, we would have to do a full string comparison. + // + final String act = (action==null)?"-":action.intern(); + if (act == "getDomains") { // ES: OK + perm = new MBeanPermission(serverName,"-",member, + routingName,act); + } else { + final String clazz = + (className==null)?getClassName(routingName):className; + perm = new MBeanPermission(serverName,clazz,member, + routingName,act); + } + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(perm); + } + + String getClassName(ObjectName routingName) { + if (routingName == null || routingName.isPattern()) return "-"; + try { + return getNamespace().getSourceServer(). + getObjectInstance(routingName).getClassName(); + } catch (InstanceNotFoundException ex) { + LOG.finest("Can't get class name for "+routingName+ + ", using \"-\". Cause is: "+ex); + return "-"; + } + } + + // + // Implements permission filters for attributes... + // + @Override + AttributeList checkAttributes(ObjectName routingName, + AttributeList attributes, String action) { + if (!checkOn()) return attributes; + final String className = getClassName(routingName); + check(routingName,className,"-",action); + if (attributes == null || attributes.isEmpty()) return attributes; + final AttributeList res = new AttributeList(); + for (Attribute at : attributes.asList()) { + try { + check(routingName,className,at.getName(),action); + res.add(at); + } catch (SecurityException x) { // DLS: OK + continue; + } + } + return res; + } + + // + // Implements permission filters for attributes... + // + @Override + String[] checkAttributes(ObjectName routingName, String[] attributes, + String action) { + if (!checkOn()) return attributes; + final String className = getClassName(routingName); + check(routingName,className,"-",action); + if (attributes == null || attributes.length==0) return attributes; + final List res = new ArrayList(attributes.length); + for (String at : attributes) { + try { + check(routingName,className,at,action); + res.add(at); + } catch (SecurityException x) { // DLS: OK + continue; + } + } + return res.toArray(new String[res.size()]); + } + + // + // Implements permission filters for domains... + // + @Override + String[] checkDomains(String[] domains, String action) { + if (domains == null || domains.length==0 || !checkOn()) + return domains; + int count=0; + for (int i=0;i + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +public abstract class HandlerInterceptor + extends RoutingMBeanServerConnection + implements MBeanServerInterceptor { + + /** + * A logger for this class. + **/ + private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; + + // The wrapped JMXNamespace + private final T handler; + + /** + * Creates a new instance of HandlerInterceptor + */ + public HandlerInterceptor(T handler) { + if (handler == null) throw new IllegalArgumentException("null"); + this.handler = handler; + } + + @Override + protected MBeanServer source() { + return handler.getSourceServer(); + } + + // The MBeanServer on which getClassLoader / getClassLoaderFor + // will be called. + // The NamespaceInterceptor overrides this method - so that it + // getClassLoader / getClassLoaderFor don't trigger the loop + // detection mechanism. + // + MBeanServer getServerForLoading() { + return source(); + } + + T getNamespace() { + return handler; + } + + // If the underlying JMXNamespace throws an IO, the IO will be + // wrapped in a RuntimeOperationsException. + RuntimeException handleIOException(IOException x,String fromMethodName, + Object... params) { + // Must do something here? + if (LOG.isLoggable(Level.FINEST)) { + LOG.finest("IO Exception in "+fromMethodName+": "+x+ + " - "+" rethrowing as RuntimeOperationsException."); + } + throw new RuntimeOperationsException( + Util.newRuntimeIOException(x)); + } + + // From MBeanServer: catch & handles IOException + @Override + public AttributeList getAttributes(ObjectName name, String[] attributes) + throws InstanceNotFoundException, ReflectionException { + try { + return super.getAttributes(name, attributes); + } catch (IOException ex) { + throw handleIOException(ex,"getAttributes",name,attributes); + } + } + + // From MBeanServer + public ClassLoader getClassLoaderFor(ObjectName mbeanName) + throws InstanceNotFoundException { + final ObjectName sourceName = toSourceOrRuntime(mbeanName); + try { + check(mbeanName,null,"getClassLoaderFor"); + return getServerForLoading().getClassLoaderFor(sourceName); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + + // From MBeanServer + public ClassLoader getClassLoader(ObjectName loaderName) + throws InstanceNotFoundException { + final ObjectName sourceName = toSourceOrRuntime(loaderName); + try { + check(loaderName,null,"getClassLoader"); + return getServerForLoading().getClassLoader(sourceName); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // From MBeanServer + public ObjectInstance registerMBean(Object object, ObjectName name) + throws InstanceAlreadyExistsException, MBeanRegistrationException, + NotCompliantMBeanException { + final ObjectName sourceName = newSourceMBeanName(name); + try { + checkCreate(name,object.getClass().getName(),"registerMBean"); + return processOutputInstance( + source().registerMBean(object,sourceName)); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public void removeNotificationListener(ObjectName name, ObjectName listener) + throws InstanceNotFoundException, ListenerNotFoundException { + try { + super.removeNotificationListener(name, listener); + } catch (IOException ex) { + throw handleIOException(ex,"removeNotificationListener",name,listener); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public String getDefaultDomain() { + try { + return super.getDefaultDomain(); + } catch (IOException ex) { + throw handleIOException(ex,"getDefaultDomain"); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public String[] getDomains() { + try { + return super.getDomains(); + } catch (IOException ex) { + throw handleIOException(ex,"getDomains"); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public Integer getMBeanCount() { + try { + return super.getMBeanCount(); + } catch (IOException ex) { + throw handleIOException(ex,"getMBeanCount"); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public void setAttribute(ObjectName name, Attribute attribute) + throws InstanceNotFoundException, AttributeNotFoundException, + InvalidAttributeValueException, MBeanException, + ReflectionException { + try { + super.setAttribute(name, attribute); + } catch (IOException ex) { + throw handleIOException(ex,"setAttribute",name, attribute); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public Set queryNames(ObjectName name, QueryExp query) { + try { + return super.queryNames(name, query); + } catch (IOException ex) { + throw handleIOException(ex,"queryNames",name, query); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public Set queryMBeans(ObjectName name, QueryExp query) { + try { + return super.queryMBeans(name, query); + } catch (IOException ex) { + throw handleIOException(ex,"queryMBeans",name, query); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public boolean isInstanceOf(ObjectName name, String className) + throws InstanceNotFoundException { + try { + return super.isInstanceOf(name, className); + } catch (IOException ex) { + throw handleIOException(ex,"isInstanceOf",name, className); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public ObjectInstance createMBean(String className, ObjectName name) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException { + try { + return super.createMBean(className, name); + } catch (IOException ex) { + throw handleIOException(ex,"createMBean",className, name); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public ObjectInstance createMBean(String className, ObjectName name, + ObjectName loaderName) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException { + try { + return super.createMBean(className, name, loaderName); + } catch (IOException ex) { + throw handleIOException(ex,"createMBean",className, name, loaderName); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public Object getAttribute(ObjectName name, String attribute) + throws MBeanException, AttributeNotFoundException, + InstanceNotFoundException, ReflectionException { + try { + return super.getAttribute(name, attribute); + } catch (IOException ex) { + throw handleIOException(ex,"getAttribute",name, attribute); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public void removeNotificationListener(ObjectName name, ObjectName listener, + NotificationFilter filter, Object handback) + throws InstanceNotFoundException, ListenerNotFoundException { + try { + super.removeNotificationListener(name, listener, filter, handback); + } catch (IOException ex) { + throw handleIOException(ex,"removeNotificationListener",name, + listener, filter, handback); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public void removeNotificationListener(ObjectName name, + NotificationListener listener, NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, ListenerNotFoundException { + try { + super.removeNotificationListener(name, listener, filter, handback); + } catch (IOException ex) { + throw handleIOException(ex,"removeNotificationListener",name, + listener, filter, handback); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public void removeNotificationListener(ObjectName name, + NotificationListener listener) + throws InstanceNotFoundException, ListenerNotFoundException { + try { + super.removeNotificationListener(name, listener); + } catch (IOException ex) { + throw handleIOException(ex,"removeNotificationListener",name, + listener); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public void addNotificationListener(ObjectName name, + NotificationListener listener, NotificationFilter filter, + Object handback) throws InstanceNotFoundException { + try { + super.addNotificationListener(name, listener, filter, handback); + } catch (IOException ex) { + throw handleIOException(ex,"addNotificationListener",name, + listener, filter, handback); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public void addNotificationListener(ObjectName name, ObjectName listener, + NotificationFilter filter, Object handback) + throws InstanceNotFoundException { + try { + super.addNotificationListener(name, listener, filter, handback); + } catch (IOException ex) { + throw handleIOException(ex,"addNotificationListener",name, + listener, filter, handback); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public boolean isRegistered(ObjectName name) { + try { + return super.isRegistered(name); + } catch (IOException ex) { + throw handleIOException(ex,"isRegistered",name); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public void unregisterMBean(ObjectName name) + throws InstanceNotFoundException, MBeanRegistrationException { + try { + super.unregisterMBean(name); + } catch (IOException ex) { + throw handleIOException(ex,"unregisterMBean",name); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public MBeanInfo getMBeanInfo(ObjectName name) + throws InstanceNotFoundException, IntrospectionException, + ReflectionException { + try { + return super.getMBeanInfo(name); + } catch (IOException ex) { + throw handleIOException(ex,"getMBeanInfo",name); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public ObjectInstance getObjectInstance(ObjectName name) + throws InstanceNotFoundException { + try { + return super.getObjectInstance(name); + } catch (IOException ex) { + throw handleIOException(ex,"getObjectInstance",name); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public ObjectInstance createMBean(String className, ObjectName name, + Object[] params, String[] signature) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException { + try { + return super.createMBean(className, name, params, signature); + } catch (IOException ex) { + throw handleIOException(ex,"createMBean",className, name, + params, signature); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public ObjectInstance createMBean(String className, ObjectName name, + ObjectName loaderName, Object[] params, String[] signature) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException { + try { + return super.createMBean(className, name, loaderName, params, + signature); + } catch (IOException ex) { + throw handleIOException(ex,"createMBean",className, name,loaderName, + params, signature); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public AttributeList setAttributes(ObjectName name,AttributeList attributes) + throws InstanceNotFoundException, ReflectionException { + try { + return super.setAttributes(name, attributes); + } catch (IOException ex) { + throw handleIOException(ex,"setAttributes",name, attributes); + } + } + + // From MBeanServer: catch & handles IOException + @Override + public Object invoke(ObjectName name, String operationName, Object[] params, + String[] signature) + throws InstanceNotFoundException, MBeanException, ReflectionException { + try { + return super.invoke(name, operationName, params, signature); + } catch (IOException ex) { + throw handleIOException(ex,"invoke",name, operationName, + params, signature); + } + } + + // + // These methods are inherited from MBeanServer.... + // + + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + public Object instantiate(String className) + throws ReflectionException, MBeanException { + if (LOG.isLoggable(Level.FINE)) + LOG.fine("call to unsupported instantiate method: " + + "trowing UnsupportedOperationException"); + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + public Object instantiate(String className, ObjectName loaderName) + throws ReflectionException, MBeanException, + InstanceNotFoundException { + if (LOG.isLoggable(Level.FINE)) + LOG.fine("call to unsupported method: instantiate(...) -" + + "throwing UnsupportedOperationException"); + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + public Object instantiate(String className, Object[] params, + String[] signature) throws ReflectionException, MBeanException { + if (LOG.isLoggable(Level.FINE)) + LOG.fine("call to unsupported method: instantiate(...) -" + + "throwing UnsupportedOperationException"); + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + public Object instantiate(String className, ObjectName loaderName, + Object[] params, String[] signature) + throws ReflectionException, MBeanException, + InstanceNotFoundException { + if (LOG.isLoggable(Level.FINE)) + LOG.fine("call to unsupported method: instantiate(...) -" + + "throwing UnsupportedOperationException"); + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + @Deprecated + public ObjectInputStream deserialize(ObjectName name, byte[] data) + throws InstanceNotFoundException, OperationsException { + if (LOG.isLoggable(Level.FINE)) + LOG.fine("call to unsupported method: deserialize(...) -" + + "throwing UnsupportedOperationException"); + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + @Deprecated + public ObjectInputStream deserialize(String className, byte[] data) + throws OperationsException, ReflectionException { + if (LOG.isLoggable(Level.FINE)) + LOG.fine("call to unsupported method: deserialize(...) -" + + "throwing UnsupportedOperationException"); + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + @Deprecated + public ObjectInputStream deserialize(String className, + ObjectName loaderName, byte[] data) + throws InstanceNotFoundException, OperationsException, + ReflectionException { + if (LOG.isLoggable(Level.FINE)) + LOG.fine("call to unsupported method: deserialize(...) -" + + "throwing UnsupportedOperationException"); + throw new UnsupportedOperationException("Not applicable."); + } + + /** + * This method should never be called. + * Throws UnsupportedOperationException. + */ + public ClassLoaderRepository getClassLoaderRepository() { + if (LOG.isLoggable(Level.FINE)) + LOG.fine("call to unsupported method: getClassLoaderRepository() -" + + "throwing UnsupportedOperationException"); + throw new UnsupportedOperationException("Not applicable."); + } + + static RuntimeException newUnsupportedException(String namespace) { + return new RuntimeOperationsException( + new UnsupportedOperationException( + "Not supported in this namespace: "+namespace)); + } + +} diff --git a/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java b/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java new file mode 100644 index 000000000..1fccfcbc7 --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java @@ -0,0 +1,369 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.namespace; + +import com.sun.jmx.defaults.JmxProperties; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.management.ListenerNotFoundException; +import javax.management.MBeanServerConnection; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.event.EventClient; +import javax.management.namespace.JMXNamespaces; +import javax.management.remote.JMXAddressable; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXServiceURL; +import javax.security.auth.Subject; + +/** + * A collection of methods that provide JMXConnector wrappers for + * JMXRemoteNamepaces underlying connectors. + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +public final class JMXNamespaceUtils { + + /** + * A logger for this class. + **/ + private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; + + + private static Map newWeakHashMap() { + return new WeakHashMap(); + } + + /** Creates a new instance of JMXNamespaces */ + private JMXNamespaceUtils() { + } + + /** + * Returns an unmodifiable option map in which the given keys have been + * filtered out. + * @param keys keys to filter out from the map. + * @return An unmodifiable option map in which the given keys have been + * filtered out. + */ + public static Map filterMap(Map map, K... keys) { + final Map filtered; + filtered=new HashMap(map); + for (K key : keys) { + filtered.remove(key); + } + return unmodifiableMap(filtered); + } + + // returns un unmodifiable view of a map. + public static Map unmodifiableMap(Map aMap) { + if (aMap == null || aMap.isEmpty()) + return Collections.emptyMap(); + return Collections.unmodifiableMap(aMap); + } + + + /** + * A base class that helps writing JMXConnectors that return + * MBeanServerConnection wrappers. + * This base class wraps an inner JMXConnector (the source), and preserve + * its caching policy. If a connection is cached in the source, its wrapper + * will be cached in this connector too. + * Author's note: rewriting this with java.lang.reflect.Proxy could be + * envisaged. It would avoid the combinatory sub-classing introduced by + * JMXAddressable. + *

      + * Note: all the standard JMXConnector implementations are serializable. + * This implementation here is not. Should it be? + * I believe it must not be serializable unless it becomes + * part of a public API (either standard or officially exposed + * and supported in a documented com.sun package) + **/ + static class JMXCachingConnector + implements JMXConnector { + + // private static final long serialVersionUID = -2279076110599707875L; + + final JMXConnector source; + + // if this object is made serializable, then the variable below + // needs to become volatile transient and be lazyly-created... + private final + Map connectionMap; + + + public JMXCachingConnector(JMXConnector source) { + this.source = checkNonNull(source, "source"); + connectionMap = newWeakHashMap(); + } + + private MBeanServerConnection + getCached(MBeanServerConnection inner) { + return connectionMap.get(inner); + } + + private MBeanServerConnection putCached(final MBeanServerConnection inner, + final MBeanServerConnection wrapper) { + if (inner == wrapper) return wrapper; + synchronized (this) { + final MBeanServerConnection concurrent = + connectionMap.get(inner); + if (concurrent != null) return concurrent; + connectionMap.put(inner,wrapper); + } + return wrapper; + } + + public void addConnectionNotificationListener(NotificationListener + listener, NotificationFilter filter, Object handback) { + source.addConnectionNotificationListener(listener,filter,handback); + } + + public void close() throws IOException { + source.close(); + } + + public void connect() throws IOException { + source.connect(); + } + + public void connect(Map env) throws IOException { + source.connect(env); + } + + public String getConnectionId() throws IOException { + return source.getConnectionId(); + } + + /** + * Preserve caching policy of the underlying connector. + **/ + public MBeanServerConnection + getMBeanServerConnection() throws IOException { + final MBeanServerConnection inner = + source.getMBeanServerConnection(); + final MBeanServerConnection cached = getCached(inner); + if (cached != null) return cached; + final MBeanServerConnection wrapper = wrap(inner); + return putCached(inner,wrapper); + } + + public MBeanServerConnection + getMBeanServerConnection(Subject delegationSubject) + throws IOException { + final MBeanServerConnection wrapped = + source.getMBeanServerConnection(delegationSubject); + synchronized (this) { + final MBeanServerConnection cached = getCached(wrapped); + if (cached != null) return cached; + final MBeanServerConnection wrapper = + wrapWithSubject(wrapped,delegationSubject); + return putCached(wrapped,wrapper); + } + } + + public void removeConnectionNotificationListener( + NotificationListener listener) + throws ListenerNotFoundException { + source.removeConnectionNotificationListener(listener); + } + + public void removeConnectionNotificationListener( + NotificationListener l, NotificationFilter f, + Object handback) throws ListenerNotFoundException { + source.removeConnectionNotificationListener(l,f,handback); + } + + /** + * This is the method that subclass will redefine. This method + * is called by {@code this.getMBeanServerConnection()}. + * {@code inner} is the connection returned by + * {@code source.getMBeanServerConnection()}. + **/ + protected MBeanServerConnection wrap(MBeanServerConnection inner) + throws IOException { + return inner; + } + + /** + * Subclass may also want to redefine this method. + * By default it calls wrap(inner). This method + * is called by {@code this.getMBeanServerConnection(Subject)}. + * {@code inner} is the connection returned by + * {@code source.getMBeanServerConnection(Subject)}. + **/ + protected MBeanServerConnection wrapWithSubject( + MBeanServerConnection inner, Subject delegationSubject) + throws IOException { + return wrap(inner); + } + + @Override + public String toString() { + if (source instanceof JMXAddressable) { + final JMXServiceURL address = + ((JMXAddressable)source).getAddress(); + if (address != null) + return address.toString(); + } + return source.toString(); + } + + } + + + /** + * The name space connector can do 'cd' + **/ + static class JMXNamespaceConnector extends JMXCachingConnector { + + // private static final long serialVersionUID = -4813611540843020867L; + + private final String toDir; + private final boolean closeable; + + public JMXNamespaceConnector(JMXConnector source, String toDir, + boolean closeable) { + super(source); + this.toDir = toDir; + this.closeable = closeable; + } + + @Override + public void close() throws IOException { + if (!closeable) + throw new UnsupportedOperationException("close"); + else super.close(); + } + + @Override + protected MBeanServerConnection wrap(MBeanServerConnection wrapped) + throws IOException { + if (LOG.isLoggable(Level.FINER)) + LOG.finer("Creating name space proxy connection for source: "+ + "namespace="+toDir); + return JMXNamespaces.narrowToNamespace(wrapped,toDir); + } + + @Override + public String toString() { + return "JMXNamespaces.narrowToNamespace("+ + super.toString()+ + ", \""+toDir+"\")"; + } + + } + + static class JMXEventConnector extends JMXCachingConnector { + + // private static final long serialVersionUID = 4742659236340242785L; + + JMXEventConnector(JMXConnector wrapped) { + super(wrapped); + } + + @Override + protected MBeanServerConnection wrap(MBeanServerConnection inner) + throws IOException { + return EventClient.getEventClientConnection(inner); + } + + + @Override + public String toString() { + return "EventClient.withEventClient("+super.toString()+")"; + } + } + + static class JMXAddressableEventConnector extends JMXEventConnector + implements JMXAddressable { + + // private static final long serialVersionUID = -9128520234812124712L; + + JMXAddressableEventConnector(JMXConnector wrapped) { + super(wrapped); + } + + public JMXServiceURL getAddress() { + return ((JMXAddressable)source).getAddress(); + } + } + + /** + * Creates a connector whose MBeamServerConnection will point to the + * given sub name space inside the source connector. + * @see JMXNamespace + **/ + public static JMXConnector cd(final JMXConnector source, + final String toNamespace, + final boolean closeable) + throws IOException { + + checkNonNull(source, "JMXConnector"); + + if (toNamespace == null || toNamespace.equals("")) + return source; + + return new JMXNamespaceConnector(source,toNamespace,closeable); + } + + + /** + * Returns a JMX Connector that will use an {@link EventClient} + * to subscribe for notifications. If the server doesn't have + * an {@link EventClientDelegateMBean}, then the connector will + * use the legacy notification mechanism instead. + * + * @param source The underlying JMX Connector wrapped by the returned + * connector. + * @return A JMX Connector that will uses an {@link EventClient}, if + * available. + * @see EventClient#getEventClientConnection(MBeanServerConnection) + */ + public static JMXConnector withEventClient(final JMXConnector source) { + checkNonNull(source, "JMXConnector"); + if (source instanceof JMXAddressable) + return new JMXAddressableEventConnector(source); + else + return new JMXEventConnector(source); + } + + public static T checkNonNull(T parameter, String name) { + if (parameter == null) + throw new IllegalArgumentException(name+" must not be null"); + return parameter; + } + + +} diff --git a/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java b/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java new file mode 100644 index 000000000..da85fc6d4 --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java @@ -0,0 +1,449 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.jmx.namespace; + +import com.sun.jmx.defaults.JmxProperties; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.QueryExp; +import javax.management.namespace.JMXNamespaces; +import javax.management.namespace.JMXNamespace; +import javax.management.namespace.JMXNamespacePermission; + +/** + * A NamespaceInterceptor wraps a JMXNamespace, performing + * ObjectName rewriting. + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +public class NamespaceInterceptor extends HandlerInterceptor { + + /** + * A logger for this class. + **/ + private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; + private static final Logger PROBE_LOG = Logger.getLogger( + JmxProperties.NAMESPACE_LOGGER+".probe"); + + // The target name space in which the NamepsaceHandler is mounted. + private final String targetNs; + + private final String serverName; + + private final ObjectNameRouter proc; + + /** + * Internal hack. The JMXRemoteNamespace can be closed and reconnected. + * Each time the JMXRemoteNamespace connects, a probe should be sent + * to detect cycle. The MBeanServer exposed by JMXRemoteNamespace thus + * implements the DynamicProbe interface, which makes it possible for + * this handler to know that it should send a new probe. + * + * XXX: TODO this probe thing is way too complex and fragile. + * This *must* go away or be replaced by something simpler. + * ideas are welcomed. + **/ + public static interface DynamicProbe { + public boolean isProbeRequested(); + } + + /** + * Creates a new instance of NamespaceInterceptor + */ + public NamespaceInterceptor( + String serverName, + JMXNamespace handler, + String targetNamespace) { + super(handler); + this.serverName = serverName; + this.targetNs = + ObjectNameRouter.normalizeNamespacePath(targetNamespace, + true, true, false); + proc = new ObjectNameRouter(targetNamespace, ""); + } + + @Override + public String toString() { + return this.getClass().getName()+"(parent="+serverName+ + ", namespace="+this.targetNs+")"; + } + + /* + * XXX: TODO this probe thing is way too complex and fragile. + * This *must* go away or be replaced by something simpler. + * ideas are welcomed. + */ + private volatile boolean probed = false; + private volatile ObjectName probe; + + // Query Pattern that we will send through the source server in order + // to detect self-linking namespaces. + // + // XXX: TODO this probe thing is way too complex and fragile. + // This *must* go away or be replaced by something simpler. + // ideas are welcomed. + final ObjectName makeProbePattern(ObjectName probe) + throws MalformedObjectNameException { + + // we could probably link the probe pattern with the probe - e.g. + // using the UUID as key in the pattern - but is it worth it? it + // also has some side effects on the context namespace - because + // such a probe may get rejected by the jmx.context// namespace. + // + // The trick here is to devise a pattern that is not likely to + // be blocked by intermediate levels. Querying for all namespace + // handlers in the source (or source namespace) is more likely to + // achieve this goal. + // + return ObjectName.getInstance("*" + + JMXNamespaces.NAMESPACE_SEPARATOR + ":" + + JMXNamespace.TYPE_ASSIGNMENT); + } + + // tell whether the name pattern corresponds to what might have been + // sent as a probe. + // XXX: TODO this probe thing is way too complex and fragile. + // This *must* go away or be replaced by something simpler. + // ideas are welcomed. + final boolean isProbePattern(ObjectName name) { + final ObjectName p = probe; + if (p == null) return false; + try { + return String.valueOf(name).endsWith(targetNs+ + JMXNamespaces.NAMESPACE_SEPARATOR + "*" + + JMXNamespaces.NAMESPACE_SEPARATOR + ":" + + JMXNamespace.TYPE_ASSIGNMENT); + } catch (RuntimeException x) { + // should not happen. + PROBE_LOG.finest("Ignoring unexpected exception in self link detection: "+ + x); + return false; + } + } + + // The first time a request reaches this NamespaceInterceptor, the + // interceptor will send a probe to detect whether the underlying + // JMXNamespace links to itslef. + // + // One way to create such self-linking namespace would be for instance + // to create a JMXNamespace whose getSourceServer() method would return: + // JMXNamespaces.narrowToNamespace(getMBeanServer(), + // getObjectName().getDomain()) + // + // If such an MBeanServer is returned, then any call to that MBeanServer + // will trigger an infinite loop. + // There can be even trickier configurations if remote connections are + // involved. + // + // In order to prevent this from happening, the NamespaceInterceptor will + // send a probe, in an attempt to detect whether it will receive it at + // the other end. If the probe is received, an exception will be thrown + // in order to break the recursion. The probe is only sent once - when + // the first request to the namespace occurs. The DynamicProbe interface + // can also be used by a Sun JMXNamespace implementation to request the + // emission of a probe at any time (see JMXRemoteNamespace + // implementation). + // + // Probes work this way: the NamespaceInterceptor sets a flag and sends + // a queryNames() request. If a queryNames() request comes in when the flag + // is on, then it deduces that there is a self-linking loop - and instead + // of calling queryNames() on the source MBeanServer of the JMXNamespace + // handler (which would cause the loop to go on) it breaks the recursion + // by returning the probe ObjectName. + // If the NamespaceInterceptor receives the probe ObjectName as result of + // its original sendProbe() request it knows that it has been looping + // back on itslef and throws an IOException... + // + // + // XXX: TODO this probe thing is way too complex and fragile. + // This *must* go away or be replaced by something simpler. + // ideas are welcomed. + // + final void sendProbe(MBeanServerConnection msc) + throws IOException { + try { + PROBE_LOG.fine("Sending probe"); + + // This is just to prevent any other thread to modify + // the probe while the detection cycle is in progress. + // + final ObjectName probePattern; + // we don't want to synchronize on this - we use targetNs + // because it's non null and final. + synchronized (targetNs) { + probed = false; + if (probe != null) { + throw new IOException("concurent connection in progress"); + } + final String uuid = UUID.randomUUID().toString(); + final String endprobe = + JMXNamespaces.NAMESPACE_SEPARATOR + uuid + + ":type=Probe,key="+uuid; + final ObjectName newprobe = + ObjectName.getInstance(endprobe); + probePattern = makeProbePattern(newprobe); + probe = newprobe; + } + + try { + PROBE_LOG.finer("Probe query: "+probePattern+" expecting: "+probe); + final Set res = msc.queryNames(probePattern, null); + final ObjectName expected = probe; + PROBE_LOG.finer("Probe res: "+res); + if (res.contains(expected)) { + throw new IOException("namespace " + + targetNs + " is linking to itself: " + + "cycle detected by probe"); + } + } catch (SecurityException x) { + PROBE_LOG.finer("Can't check for cycles: " + x); + // can't do anything.... + } catch (RuntimeException x) { + PROBE_LOG.finer("Exception raised by queryNames: " + x); + throw x; + } finally { + probe = null; + } + } catch (MalformedObjectNameException x) { + final IOException io = + new IOException("invalid name space: probe failed"); + io.initCause(x); + throw io; + } + PROBE_LOG.fine("Probe returned - no cycles"); + probed = true; + } + + // allows a Sun implementation JMX Namespace, such as the + // JMXRemoteNamespace, to control when a probe should be sent. + // + // XXX: TODO this probe thing is way too complex and fragile. + // This *must* go away or be replaced by something simpler. + // ideas are welcomed. + private boolean isProbeRequested(Object o) { + if (o instanceof DynamicProbe) + return ((DynamicProbe)o).isProbeRequested(); + return false; + } + + /** + * This method will send a probe to detect self-linking name spaces. + * A self linking namespace is a namespace that links back directly + * on itslef. Calling a method on such a name space always results + * in an infinite loop going through: + * [1]MBeanServer -> [2]NamespaceDispatcher -> [3]NamespaceInterceptor + * [4]JMXNamespace -> { network // or cd // or ... } -> [5]MBeanServer + * with exactly the same request than [1]... + * + * The namespace interceptor [2] tries to detect such condition the + * *first time* that the connection is used. It does so by setting + * a flag, and sending a queryNames() through the name space. If the + * queryNames comes back, it knows that there's a loop. + * + * The DynamicProbe interface can also be used by a Sun JMXNamespace + * implementation to request the emission of a probe at any time + * (see JMXRemoteNamespace implementation). + */ + private MBeanServer connection() { + try { + final MBeanServer c = super.source(); + if (probe != null) // should not happen + throw new RuntimeException("connection is being probed"); + + if (probed == false || isProbeRequested(c)) { + try { + // Should not happen if class well behaved. + // Never probed. Force it. + //System.err.println("sending probe for " + + // "target="+targetNs+", source="+srcNs); + sendProbe(c); + } catch (IOException io) { + throw new RuntimeException(io.getMessage(), io); + } + } + + if (c != null) { + return c; + } + } catch (RuntimeException x) { + throw x; + } + throw new NullPointerException("getMBeanServerConnection"); + } + + + @Override + protected MBeanServer source() { + return connection(); + } + + @Override + protected MBeanServer getServerForLoading() { + // don't want to send probe on getClassLoader/getClassLoaderFor + return super.source(); + } + + /** + * Calls {@link MBeanServerConnection#queryNames queryNames} + * on the underlying + * {@link #getMBeanServerConnection MBeanServerConnection}. + **/ + @Override + public final Set queryNames(ObjectName name, QueryExp query) { + // XXX: TODO this probe thing is way too complex and fragile. + // This *must* go away or be replaced by something simpler. + // ideas are welcomed. + PROBE_LOG.finer("probe is: "+probe+" pattern is: "+name); + if (probe != null && isProbePattern(name)) { + PROBE_LOG.finer("Return probe: "+probe); + return Collections.singleton(probe); + } + return super.queryNames(name, query); + } + + @Override + protected ObjectName toSource(ObjectName targetName) + throws MalformedObjectNameException { + return proc.toSourceContext(targetName, true); + } + + @Override + protected ObjectName toTarget(ObjectName sourceName) + throws MalformedObjectNameException { + return proc.toTargetContext(sourceName, false); + } + + // + // Implements permission checks. + // + @Override + void check(ObjectName routingName, String member, String action) { + final SecurityManager sm = System.getSecurityManager(); + if (sm == null) return; + if ("getDomains".equals(action)) return; + final JMXNamespacePermission perm = + new JMXNamespacePermission(serverName,member, + routingName,action); + sm.checkPermission(perm); + } + + @Override + void checkCreate(ObjectName routingName, String className, String action) { + final SecurityManager sm = System.getSecurityManager(); + if (sm == null) return; + final JMXNamespacePermission perm = + new JMXNamespacePermission(serverName,className, + routingName,action); + sm.checkPermission(perm); + } + + // + // Implements permission filters for attributes... + // + @Override + AttributeList checkAttributes(ObjectName routingName, + AttributeList attributes, String action) { + check(routingName,null,action); + if (attributes == null || attributes.isEmpty()) return attributes; + final SecurityManager sm = System.getSecurityManager(); + if (sm == null) return attributes; + final AttributeList res = new AttributeList(); + for (Attribute at : attributes.asList()) { + try { + check(routingName,at.getName(),action); + res.add(at); + } catch (SecurityException x) { // DLS: OK + continue; + } + } + return res; + } + + // + // Implements permission filters for attributes... + // + @Override + String[] checkAttributes(ObjectName routingName, String[] attributes, + String action) { + check(routingName,null,action); + if (attributes == null || attributes.length==0) return attributes; + final SecurityManager sm = System.getSecurityManager(); + if (sm == null) return attributes; + final List res = new ArrayList(attributes.length); + for (String at : attributes) { + try { + check(routingName,at,action); + res.add(at); + } catch (SecurityException x) { // DLS: OK + continue; + } + } + return res.toArray(new String[res.size()]); + } + + // + // Implements permission filters for domains... + // + @Override + String[] checkDomains(String[] domains, String action) { + // in principle, this method is never called because + // getDomains() will never be called - since there's + // no way that MBeanServer.getDomains() can be routed + // to a NamespaceInterceptor. + // + // This is also why there's no getDomains() in a + // JMXNamespacePermission... + // + return super.checkDomains(domains, action); + } + + // + // Implements permission filters for queries... + // + @Override + boolean checkQuery(ObjectName routingName, String action) { + try { + check(routingName,null,action); + return true; + } catch (SecurityException x) { // DLS: OK + return false; + } + } + +} diff --git a/src/share/classes/com/sun/jmx/namespace/ObjectNameRouter.java b/src/share/classes/com/sun/jmx/namespace/ObjectNameRouter.java new file mode 100644 index 000000000..3c7065c47 --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/ObjectNameRouter.java @@ -0,0 +1,191 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.namespace; + +import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectInstance; +import javax.management.ObjectName; + +/** + * The ObjectNameRouter is used to rewrite routing object names. + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +public class ObjectNameRouter { + + private static final int NAMESPACE_SEPARATOR_LENGTH = + NAMESPACE_SEPARATOR.length(); + + final String targetPrefix; + final String sourcePrefix; + final int slen; + final int tlen; + final boolean identity; + + + public ObjectNameRouter(String targetDirName) { + this(targetDirName,null); + } + + /** Creates a new instance of ObjectNameRouter */ + public ObjectNameRouter(final String remove, final String add) { + this.targetPrefix = (remove==null?"":remove); + this.sourcePrefix = (add==null?"":add); + tlen = targetPrefix.length(); + slen = sourcePrefix.length(); + identity = targetPrefix.equals(sourcePrefix); + } + + public final ObjectName toTargetContext(ObjectName sourceName, + boolean removeLeadingSeparators) { + if (sourceName == null) return null; + if (identity) return sourceName; + String srcDomain = sourceName.getDomain(); + + // if the ObjectName starts with // and removeLeadingSeparators is + // true, then recursively strip leading //. + // Otherwise, do not rewrite ObjectName. + // + if (srcDomain.startsWith(NAMESPACE_SEPARATOR)) { + if (!removeLeadingSeparators) return sourceName; + else srcDomain = normalizeDomain(srcDomain,true); + } + if (slen != 0) { + if (!srcDomain.startsWith(sourcePrefix) || + !srcDomain.startsWith(NAMESPACE_SEPARATOR,slen)) + throw new IllegalArgumentException( + "ObjectName does not start with expected prefix " + + sourcePrefix + ": " + + String.valueOf(sourceName)); + srcDomain = srcDomain.substring(slen+NAMESPACE_SEPARATOR_LENGTH); + } + final String targetDomain = + (tlen>0?targetPrefix+NAMESPACE_SEPARATOR+srcDomain:srcDomain); + try { + return sourceName.withDomain(targetDomain); + } catch (MalformedObjectNameException x) { + throw new IllegalArgumentException(String.valueOf(sourceName),x); + } + } + + public final ObjectName toSourceContext(ObjectName targetName, + boolean removeLeadingSeparators) { + if (targetName == null) return null; + if (identity) return targetName; + String targetDomain = targetName.getDomain(); + if (targetDomain.startsWith(NAMESPACE_SEPARATOR)) { + if (!removeLeadingSeparators) return targetName; + else targetDomain = + normalizeDomain(targetDomain,true); + } + if (tlen != 0) { + if (!targetDomain.startsWith(targetPrefix) || + !targetDomain.startsWith(NAMESPACE_SEPARATOR,tlen)) + throw new IllegalArgumentException( + "ObjectName does not start with expected prefix " + + targetPrefix + ": " + + String.valueOf(targetName)); + targetDomain = targetDomain. + substring(tlen+NAMESPACE_SEPARATOR_LENGTH); + } + final String sourceDomain = + (slen>0?sourcePrefix+NAMESPACE_SEPARATOR+targetDomain: + targetDomain); + try { + return targetName.withDomain(sourceDomain); + } catch (MalformedObjectNameException x) { + throw new IllegalArgumentException(String.valueOf(targetName),x); + } + } + + public final ObjectInstance toTargetContext(ObjectInstance sourceMoi, + boolean removeLeadingSeparators) { + if (sourceMoi == null) return null; + if (identity) return sourceMoi; + return new ObjectInstance( + toTargetContext(sourceMoi.getObjectName(), + removeLeadingSeparators), + sourceMoi.getClassName()); + } + + /** + * Removes leading, trailing, or duplicate // in a name space path. + **/ + public static String normalizeDomain(String domain, + boolean removeLeadingSep) { + return normalizeNamespacePath(domain,removeLeadingSep,false,true); + } + + /** + * Removes leading, trailing, or duplicate // in a name space path. + **/ + public static String normalizeNamespacePath(String namespacePath, + boolean removeLeadingSep, + boolean removeTrailingSep, + boolean endsWithDomain) { + if (namespacePath.equals("")) + return ""; + final String[] components = namespacePath.split(NAMESPACE_SEPARATOR); + final StringBuilder b = + new StringBuilder(namespacePath.length()+NAMESPACE_SEPARATOR_LENGTH); + String sep = null; + if (!removeLeadingSep && namespacePath.startsWith(NAMESPACE_SEPARATOR)) + b.append(NAMESPACE_SEPARATOR); + int count = 0; + for (int i=0; i 0) + b.append(NAMESPACE_SEPARATOR); + return b.toString(); + } + + +} diff --git a/src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java b/src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java new file mode 100644 index 000000000..787343eec --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java @@ -0,0 +1,132 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.namespace; + + +import com.sun.jmx.defaults.JmxProperties; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.management.MBeanServerConnection; +import javax.management.namespace.JMXNamespaces; + + +/** + * A RoutingConnectionProxy is an MBeanServerConnection proxy that proxies a + * source name space in a source MBeanServerConnection. + * It wraps a source MBeanServerConnection, and rewrites routing + * ObjectNames. It is used to implement + * {@code JMXNamespaces.narrowToNamespace(MBeanServerConnection)}. + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +public class RoutingConnectionProxy + extends RoutingProxy { + + /** + * A logger for this class. + **/ + private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; + + + /** + * Creates a new instance of RoutingConnectionProxy + */ + public RoutingConnectionProxy(MBeanServerConnection source, + String sourceDir) { + this(source,sourceDir,"",false); + } + + /** + * Creates a new instance of RoutingConnectionProxy + */ + public RoutingConnectionProxy(MBeanServerConnection source, + String sourceDir, + String targetDir, + boolean forwardsContext) { + super(source,sourceDir,targetDir,forwardsContext); + + if (LOG.isLoggable(Level.FINER)) + LOG.finer("RoutingConnectionProxy for " + getSourceNamespace() + + " created"); + } + + @Override + public String toString() { + final String targetNs = getTargetNamespace(); + final String sourceNs = getSourceNamespace(); + String wrapped = String.valueOf(source()); + if ("".equals(targetNs)) { + if (forwardsContext) + wrapped = "ClientContext.withDynamicContext("+wrapped+")"; + return "JMXNamespaces.narrowToNamespace("+ + wrapped+", \""+ + sourceNs+"\")"; + } + return this.getClass().getSimpleName()+"("+wrapped+", \""+ + sourceNs+"\", \""+ + targetNs+"\", "+forwardsContext+")"; + } + + public static MBeanServerConnection cd(MBeanServerConnection source, + String sourcePath) { + if (source == null) throw new IllegalArgumentException("null"); + if (source.getClass().equals(RoutingConnectionProxy.class)) { + // cast is OK here, but findbugs complains unless we use class.cast + final RoutingConnectionProxy other = + RoutingConnectionProxy.class.cast(source); + final String target = other.getTargetNamespace(); + + // Avoid multiple layers of serialization. + // + // We construct a new proxy from the original source instead of + // stacking a new proxy on top of the old one. + // - that is we replace + // cd ( cd ( x, dir1), dir2); + // by + // cd (x, dir1//dir2); + // + // We can do this only when the source class is exactly + // NamespaceConnectionProxy. + // + if (target == null || target.equals("")) { + final String path = + JMXNamespaces.concat(other.getSourceNamespace(), + sourcePath); + return new RoutingConnectionProxy(other.source(),path,"", + other.forwardsContext); + } + // Note: we could do possibly something here - but it would involve + // removing part of targetDir, and possibly adding + // something to sourcePath. + // Too complex to bother! => simply default to stacking... + } + return new RoutingConnectionProxy(source,sourcePath); + } + +} diff --git a/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java b/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java new file mode 100644 index 000000000..904e5848e --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java @@ -0,0 +1,671 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.namespace; + +import com.sun.jmx.defaults.JmxProperties; +import com.sun.jmx.mbeanserver.Util; +import java.io.IOException; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.JMRuntimeException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.QueryExp; +import javax.management.ReflectionException; +import javax.management.RuntimeMBeanException; +import javax.management.RuntimeOperationsException; + +/** + * A RoutingMBeanServerConnection wraps a MBeanServerConnection, defining + * abstract methods that can be implemented by subclasses to rewrite + * routing ObjectNames. It is used to implement + * HandlerInterceptors (wrapping JMXNamespace instances) and routing + * proxies (used to implement cd operations). + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +public abstract class RoutingMBeanServerConnection + implements MBeanServerConnection { + + /** + * A logger for this class. + **/ + private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; + + /** + * Creates a new instance of RoutingMBeanServerConnection + */ + public RoutingMBeanServerConnection() { + } + + /** + * Returns the wrapped source connection. + **/ + protected abstract T source() throws IOException; + + /** + * Converts a target ObjectName to a source ObjectName. + **/ + protected abstract ObjectName toSource(ObjectName targetName) + throws MalformedObjectNameException; + + /** + * Converts a source ObjectName to a target ObjectName. + **/ + protected abstract ObjectName toTarget(ObjectName sourceName) + throws MalformedObjectNameException; + + /** + * Can be overridden by subclasses to check the validity of a new + * ObjectName used in createMBean or registerMBean. + * This method is typically used by subclasses which might require + * special handling for "null"; + **/ + protected ObjectName newSourceMBeanName(ObjectName targetName) + throws MBeanRegistrationException { + try { + return toSource(targetName); + } catch (Exception x) { + throw new MBeanRegistrationException(x,"Illegal MBean Name"); + } + } + + // Calls toSource(), Wraps MalformedObjectNameException. + ObjectName toSourceOrRuntime(ObjectName targetName) + throws RuntimeOperationsException { + try { + return toSource(targetName); + } catch (MalformedObjectNameException x) { + final IllegalArgumentException x2 = + new IllegalArgumentException(String.valueOf(targetName),x); + final RuntimeOperationsException x3 = + new RuntimeOperationsException(x2); + throw x3; + } + } + + + // Wraps given exception if needed. + RuntimeException makeCompliantRuntimeException(Exception x) { + if (x instanceof SecurityException) return (SecurityException)x; + if (x instanceof JMRuntimeException) return (JMRuntimeException)x; + if (x instanceof RuntimeException) + return new RuntimeOperationsException((RuntimeException)x); + if (x instanceof IOException) + return Util.newRuntimeIOException((IOException)x); + // shouldn't come here... + final RuntimeException x2 = new UndeclaredThrowableException(x); + return new RuntimeOperationsException(x2); + } + + /** + * This method is a hook to implement permission checking in subclasses. + * By default, this method does nothing and simply returns + * {@code attribute}. + * + * @param routingName The name of the MBean in the enclosing context. + * This is of the form {@code //}. + * @param attributes The list of attributes to check permission for. + * @param action one of "getAttribute" or "setAttribute" + * @return The list of attributes for which the callers has the + * appropriate {@link + * javax.management.namespace.JMXNamespacePermission}. + */ + String[] checkAttributes(ObjectName routingName, + String[] attributes, String action) { + check(routingName,null,action); + return attributes; + } + + /** + * This method is a hook to implement permission checking in subclasses. + * By default, this method does nothing and simply returns + * {@code attribute}. + * + * @param routingName The name of the MBean in the enclosing context. + * This is of the form {@code //}. + * @param attributes The list of attributes to check permission for. + * @param action one of "getAttribute" or "setAttribute" + * @return The list of attributes for which the callers has the + * appropriate {@link + * javax.management.namespace.JMXNamespacePermission}. + */ + AttributeList checkAttributes(ObjectName routingName, + AttributeList attributes, String action) { + check(routingName,null,action); + return attributes; + } + + // from MBeanServerConnection + public AttributeList getAttributes(ObjectName name, String[] attributes) + throws InstanceNotFoundException, ReflectionException, IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + try { + final String[] authorized = + checkAttributes(name,attributes,"getAttribute"); + final AttributeList attrList = + source().getAttributes(sourceName,authorized); + return attrList; + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + /** + * This method is a hook to implement permission checking in subclasses. + * By default, this method does nothing. + * A subclass may override this method and throw a {@link + * SecurityException} if the permission is denied. + * + * @param routingName The name of the MBean in the enclosing context. + * This is of the form {@code //}. + * @param member The {@link + * javax.management.namespace.JMXNamespacePermission#getMember member} + * name. + * @param action The {@link + * javax.management.namespace.JMXNamespacePermission#getActions action} + * name. + */ + void check(ObjectName routingName, + String member, String action) { + } + + void checkPattern(ObjectName routingPattern, + String member, String action) { + // pattern is checked only at posteriori by checkQuery. + // checking it a priori usually doesn't work, because ObjectName.apply + // does not work between two patterns. + check(null,null,action); + } + + void checkCreate(ObjectName routingName, String className, + String action) { + } + + // from MBeanServerConnection + public Object invoke(ObjectName name, String operationName, Object[] params, + String[] signature) + throws InstanceNotFoundException, MBeanException, ReflectionException, + IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + try { + check(name, operationName, "invoke"); + final Object result = + source().invoke(sourceName,operationName,params, + signature); + return result; + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public void unregisterMBean(ObjectName name) + throws InstanceNotFoundException, MBeanRegistrationException, + IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + try { + check(name, null, "unregisterMBean"); + source().unregisterMBean(sourceName); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public MBeanInfo getMBeanInfo(ObjectName name) + throws InstanceNotFoundException, IntrospectionException, + ReflectionException, IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + try { + check(name, null, "getMBeanInfo"); + return source().getMBeanInfo(sourceName); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public ObjectInstance getObjectInstance(ObjectName name) + throws InstanceNotFoundException, IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + try { + check(name, null, "getObjectInstance"); + return processOutputInstance( + source().getObjectInstance(sourceName)); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public boolean isRegistered(ObjectName name) throws IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + try { + return source().isRegistered(sourceName); + } catch (RuntimeMBeanException x) { + throw new RuntimeOperationsException(x.getTargetException()); + } catch (RuntimeException x) { + throw makeCompliantRuntimeException(x); + } + } + + // from MBeanServerConnection + public void setAttribute(ObjectName name, Attribute attribute) + throws InstanceNotFoundException, AttributeNotFoundException, + InvalidAttributeValueException, MBeanException, + ReflectionException, IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + try { + check(name, + (attribute==null?null:attribute.getName()), + "setAttribute"); + source().setAttribute(sourceName,attribute); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public ObjectInstance createMBean(String className, + ObjectName name, ObjectName loaderName, + Object[] params, String[] signature) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException, IOException { + final ObjectName sourceName = newSourceMBeanName(name); + // Loader Name is already a sourceLoaderName. + final ObjectName sourceLoaderName = loaderName; + try { + checkCreate(name, className, "instantiate"); + checkCreate(name, className, "registerMBean"); + final ObjectInstance instance = + source().createMBean(className,sourceName, + sourceLoaderName, + params,signature); + return processOutputInstance(instance); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public ObjectInstance createMBean(String className, ObjectName name, + Object[] params, String[] signature) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, IOException { + final ObjectName sourceName = newSourceMBeanName(name); + try { + checkCreate(name, className, "instantiate"); + checkCreate(name, className, "registerMBean"); + return processOutputInstance(source().createMBean(className, + sourceName,params,signature)); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public ObjectInstance createMBean(String className, ObjectName name, + ObjectName loaderName) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException, IOException { + final ObjectName sourceName = newSourceMBeanName(name); + // Loader Name is already a source Loader Name. + final ObjectName sourceLoaderName = loaderName; + try { + checkCreate(name, className, "instantiate"); + checkCreate(name, className, "registerMBean"); + return processOutputInstance(source().createMBean(className, + sourceName,sourceLoaderName)); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public ObjectInstance createMBean(String className, ObjectName name) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, IOException { + final ObjectName sourceName = newSourceMBeanName(name); + try { + checkCreate(name, className, "instantiate"); + checkCreate(name, className, "registerMBean"); + return processOutputInstance(source(). + createMBean(className,sourceName)); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public Object getAttribute(ObjectName name, String attribute) + throws MBeanException, AttributeNotFoundException, + InstanceNotFoundException, ReflectionException, IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + try { + check(name, attribute, "getAttribute"); + return source().getAttribute(sourceName,attribute); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public boolean isInstanceOf(ObjectName name, String className) + throws InstanceNotFoundException, IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + try { + check(name, null, "isInstanceOf"); + return source().isInstanceOf(sourceName,className); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public AttributeList setAttributes(ObjectName name, AttributeList attributes) + throws InstanceNotFoundException, ReflectionException, IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + try { + final AttributeList authorized = + checkAttributes(name, attributes, "setAttribute"); + return source(). + setAttributes(sourceName,authorized); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // Return names in the target's context. + Set processOutputInstances(Set sources) { + + final Set result = Util.equivalentEmptySet(sources); + for (ObjectInstance i : sources) { + try { + final ObjectInstance target = processOutputInstance(i); + if (!checkQuery(target.getObjectName(), "queryMBeans")) + continue; + result.add(target); + } catch (Exception x) { + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Skiping returned item: " + + "Unexpected exception while processing " + + "ObjectInstance: " + x); + } + continue; + } + } + return result; + } + + /** + * This is a hook to implement permission checking in subclasses. + * + * Checks that the caller has sufficient permission for returning + * information about {@code sourceName} in {@code action}. + * + * By default always return true. Subclass may override this method + * and return false if the caller doesn't have sufficient permissions. + * + * @param routingName The name of the MBean to include or exclude from + * the query, expressed in the enclosing context. + * This is of the form {@code //}. + * @param action one of "queryNames" or "queryMBeans" + * @return true if {@code sourceName} can be returned. + */ + boolean checkQuery(ObjectName routingName, String action) { + return true; + } + + // Return names in the target's context. + ObjectInstance processOutputInstance(ObjectInstance source) { + if (source == null) return null; + final ObjectName sourceName = source.getObjectName(); + try { + final ObjectName targetName = toTarget(sourceName); + return new ObjectInstance(targetName,source.getClassName()); + } catch (MalformedObjectNameException x) { + final IllegalArgumentException x2 = + new IllegalArgumentException(String.valueOf(sourceName),x); + final RuntimeOperationsException x3 = + new RuntimeOperationsException(x2); + throw x3; + } + } + + // Returns names in the target's context. + Set processOutputNames(Set sourceNames) { + + final Set names = Util.equivalentEmptySet(sourceNames); + for (ObjectName n : sourceNames) { + try { + final ObjectName targetName = toTarget(n); + if (!checkQuery(targetName, "queryNames")) continue; + names.add(targetName); + } catch (Exception x) { + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Skiping returned item: " + + "Unexpected exception while processing " + + "ObjectInstance: " + x); + } + continue; + } + } + return names; + } + + // from MBeanServerConnection + public Set queryMBeans(ObjectName name, + QueryExp query) throws IOException { + if (name == null) name=ObjectName.WILDCARD; + final ObjectName sourceName = toSourceOrRuntime(name); + try { + checkPattern(name,null,"queryMBeans"); + return processOutputInstances( + source().queryMBeans(sourceName,query)); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + + public Set queryNames(ObjectName name, QueryExp query) + throws IOException { + if (name == null) name=ObjectName.WILDCARD; + final ObjectName sourceName = toSourceOrRuntime(name); + try { + checkPattern(name,null,"queryNames"); + final Set tmp = source().queryNames(sourceName,query); + final Set out = processOutputNames(tmp); + //System.err.println("queryNames: out: "+out); + return out; + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public void removeNotificationListener(ObjectName name, + NotificationListener listener) + throws InstanceNotFoundException, + ListenerNotFoundException, IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + try { + check(name,null,"removeNotificationListener"); + source().removeNotificationListener(sourceName,listener); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public void addNotificationListener(ObjectName name, ObjectName listener, + NotificationFilter filter, Object handback) + throws InstanceNotFoundException, IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + // Listener name is already a source listener name. + try { + check(name,null,"addNotificationListener"); + source().addNotificationListener(sourceName,listener, + filter,handback); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public void addNotificationListener(ObjectName name, + NotificationListener listener, NotificationFilter filter, + Object handback) throws InstanceNotFoundException, IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + try { + check(name,null,"addNotificationListener"); + source().addNotificationListener(sourceName, listener, filter, + handback); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + + // from MBeanServerConnection + public void removeNotificationListener(ObjectName name, + NotificationListener listener, NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, ListenerNotFoundException, + IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + try { + check(name,null,"removeNotificationListener"); + source().removeNotificationListener(sourceName,listener,filter, + handback); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public void removeNotificationListener(ObjectName name, ObjectName listener, + NotificationFilter filter, Object handback) + throws InstanceNotFoundException, ListenerNotFoundException, + IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + try { + check(name,null,"removeNotificationListener"); + source().removeNotificationListener(sourceName,listener, + filter,handback); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public void removeNotificationListener(ObjectName name, ObjectName listener) + throws InstanceNotFoundException, ListenerNotFoundException, + IOException { + final ObjectName sourceName = toSourceOrRuntime(name); + // listener name is already a source name... + final ObjectName sourceListener = listener; + try { + check(name,null,"removeNotificationListener"); + source().removeNotificationListener(sourceName,sourceListener); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public Integer getMBeanCount() throws IOException { + try { + return source().getMBeanCount(); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // from MBeanServerConnection + public String[] getDomains() throws IOException { + try { + check(null,null,"getDomains"); + final String[] domains = source().getDomains(); + return checkDomains(domains,"getDomains"); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + /** + * This method is a hook to implement permission checking in subclasses. + * Checks that the caller as the necessary permissions to view the + * given domain. If not remove the domains for which the caller doesn't + * have permission from the list. + *

      + * By default, this method always returns {@code domains} + * + * @param domains The domains to return. + * @param action "getDomains" + * @return a filtered list of domains. + */ + String[] checkDomains(String[] domains, String action) { + return domains; + } + + // from MBeanServerConnection + public String getDefaultDomain() throws IOException { + try { + return source().getDefaultDomain(); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + +} diff --git a/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java b/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java new file mode 100644 index 000000000..90b3793bc --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java @@ -0,0 +1,282 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.namespace; + +import com.sun.jmx.defaults.JmxProperties; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanRegistrationException; + +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.namespace.JMXNamespaces; + + +/** + * An RoutingProxy narrows on a given name space in a + * source object implementing MBeanServerConnection. + * It is used to implement + * {@code JMXNamespaces.narrowToNamespace(...)}. + * This abstract class has two concrete subclasses: + *

      {@link RoutingConnectionProxy}: to cd in an MBeanServerConnection.

      + *

      {@link RoutingServerProxy}: to cd in an MBeanServer.

      + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +public abstract class RoutingProxy + extends RoutingMBeanServerConnection { + + /** + * A logger for this class. + **/ + private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; + + // The source MBeanServerConnection + private final T source; + + // The name space we're narrowing to (usually some name space in + // the source MBeanServerConnection + private final String sourceNs; + + // The name space we pretend to be mounted in (usually "") + private final String targetNs; + + // The name of the JMXNamespace that handles the source name space + private final ObjectName handlerName; + private final ObjectNameRouter router; + final boolean forwardsContext; + private volatile String defaultDomain = null; + + /** + * Creates a new instance of RoutingProxy + */ + protected RoutingProxy(T source, + String sourceNs, + String targetNs, + boolean forwardsContext) { + if (source == null) throw new IllegalArgumentException("null"); + this.sourceNs = JMXNamespaces.normalizeNamespaceName(sourceNs); + + // Usually sourceNs is not null, except when implementing + // Client Contexts + // + if (sourceNs.equals("")) { + this.handlerName = null; + } else { + // System.err.println("sourceNs: "+sourceNs); + this.handlerName = + JMXNamespaces.getNamespaceObjectName(this.sourceNs); + try { + // System.err.println("handlerName: "+handlerName); + if (!source.isRegistered(handlerName)) + throw new IllegalArgumentException(sourceNs + + ": no such name space"); + } catch (IOException x) { + throw new IllegalArgumentException("source stale: "+x,x); + } + } + this.source = source; + this.targetNs = (targetNs==null?"": + JMXNamespaces.normalizeNamespaceName(targetNs)); + this.router = + new ObjectNameRouter(this.targetNs,this.sourceNs); + this.forwardsContext = forwardsContext; + + if (LOG.isLoggable(Level.FINER)) + LOG.finer("RoutingProxy for " + this.sourceNs + " created"); + } + + @Override + public T source() { return source; } + + ObjectNameRouter getObjectNameRouter() { +// TODO: uncomment this when contexts are added +// if (forwardsContext) +// return ObjectNameRouter.wrapWithContext(router); +// else + return router; + } + + @Override + public ObjectName toSource(ObjectName targetName) + throws MalformedObjectNameException { + if (targetName == null) return null; + if (targetName.getDomain().equals("") && targetNs.equals("")) { + try { + if (defaultDomain == null) + defaultDomain = getDefaultDomain(); + } catch(Exception x) { + LOG.log(Level.FINEST,"Failed to get default domain",x); + } + if (defaultDomain != null) + targetName = targetName.withDomain(defaultDomain); + } + final ObjectNameRouter r = getObjectNameRouter(); + return r.toSourceContext(targetName,true); + } + + @Override + protected ObjectName newSourceMBeanName(ObjectName targetName) + throws MBeanRegistrationException { + if (targetName != null) return super.newSourceMBeanName(targetName); + + // OK => we can accept null if sourceNs is empty. + if (sourceNs.equals("")) return null; + + throw new MBeanRegistrationException( + new IllegalArgumentException( + "Can't use null ObjectName with namespaces")); + } + + @Override + public ObjectName toTarget(ObjectName sourceName) + throws MalformedObjectNameException { + if (sourceName == null) return null; + final ObjectNameRouter r = getObjectNameRouter(); + return r.toTargetContext(sourceName,false); + } + + private Object getAttributeFromHandler(String attributeName) + throws IOException { + + try { + return source().getAttribute(handlerName,attributeName); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } catch (IOException x) { + throw x; + } catch (MBeanException ex) { + throw new IOException("Failed to get "+attributeName+": "+ + ex.getMessage(), + ex.getTargetException()); + } catch (AttributeNotFoundException ex) { + throw new IOException("Failed to get "+attributeName+": "+ + ex.getMessage(),ex); + } catch (InstanceNotFoundException ex) { + throw new IOException("Failed to get "+attributeName+": "+ + ex.getMessage(),ex); + } catch (ReflectionException ex) { + throw new IOException("Failed to get "+attributeName+": "+ + ex.getMessage(),ex); + } + } + + // We cannot call getMBeanCount() on the underlying + // MBeanServerConnection, because it would return the number of + // 'top-level' MBeans, not the number of MBeans in the name space + // we are narrowing to. Instead we're calling getMBeanCount() on + // the JMXNamespace that handles the source name space. + // + // There is however one particular case when the sourceNs is empty. + // In that case, there's no handler - and the 'source' is the top + // level namespace. In that particular case, handlerName will be null, + // and we directly invoke the top level source(). + // This later complex case is only used when implementing ClientContexts. + // + @Override + public Integer getMBeanCount() throws IOException { + try { + if (handlerName == null) return source().getMBeanCount(); + return (Integer) getAttributeFromHandler("MBeanCount"); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // We cannot call getDomains() on the underlying + // MBeanServerConnection, because it would return the domains of + // 'top-level' MBeans, not the domains of MBeans in the name space + // we are narrowing to. Instead we're calling getDomains() on + // the JMXNamespace that handles the source name space. + // + // There is however one particular case when the sourceNs is empty. + // In that case, there's no handler - and the 'source' is the top + // level namespace. In that particular case, handlerName will be null, + // and we directly invoke the top level source(). + // This later complex case is only used when implementing ClientContexts. + // + @Override + public String[] getDomains() throws IOException { + try { + if (handlerName == null) return source().getDomains(); + return (String[]) getAttributeFromHandler("Domains"); + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + // We cannot call getDefaultDomain() on the underlying + // MBeanServerConnection, because it would return the default domain of + // 'top-level' namespace, not the default domain in the name space + // we are narrowing to. Instead we're calling getDefaultDomain() on + // the JMXNamespace that handles the source name space. + // + // There is however one particular case when the sourceNs is empty. + // In that case, there's no handler - and the 'source' is the top + // level namespace. In that particular case, handlerName will be null, + // and we directly invoke the top level source(). + // This later complex case is only used when implementing ClientContexts. + // + @Override + public String getDefaultDomain() throws IOException { + try { + if (handlerName == null) { + defaultDomain = source().getDefaultDomain(); + } else { + defaultDomain =(String) + getAttributeFromHandler("DefaultDomain"); + } + return defaultDomain; + } catch (RuntimeException ex) { + throw makeCompliantRuntimeException(ex); + } + } + + public String getSourceNamespace() { + return sourceNs; + } + + public String getTargetNamespace() { + return targetNs; + } + + @Override + public String toString() { + return super.toString()+", sourceNs="+ + sourceNs + (targetNs.equals("")?"": + (" mounted on targetNs="+targetNs)); + } + +} diff --git a/src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java b/src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java new file mode 100644 index 000000000..94aa139dd --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java @@ -0,0 +1,602 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.namespace; + + +import com.sun.jmx.mbeanserver.Util; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.Collections; +import java.util.Set; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.NotCompliantMBeanException; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.OperationsException; +import javax.management.QueryExp; +import javax.management.ReflectionException; +import javax.management.loading.ClassLoaderRepository; +import javax.management.namespace.JMXNamespaces; + +/** + * A RoutingServerProxy is an MBeanServer proxy that proxies a + * source name space in a source MBeanServer. + * It wraps a source MBeanServer, and rewrites routing ObjectNames. + * It is typically use for implementing 'cd' operations, and + * will add the source name space to routing ObjectNames at input, + * and remove it at output. + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * + * @since 1.7 + */ +public class RoutingServerProxy + extends RoutingProxy + implements MBeanServer { + + /** + * Creates a new instance of RoutingServerProxy + */ + public RoutingServerProxy(MBeanServer source, + String sourceNs) { + this(source,sourceNs,"",false); + } + + public RoutingServerProxy(MBeanServer source, + String sourceNs, + String targetNs, + boolean forwardsContext) { + super(source,sourceNs,targetNs,forwardsContext); + } + + /** + * This method is called each time an IOException is raised when + * trying to forward an operation to the underlying + * MBeanServerConnection, as a result of calling + * {@link #getMBeanServerConnection()} or as a result of invoking the + * operation on the returned connection. + * Subclasses may redefine this method if they need to perform any + * specific handling of IOException (logging etc...). + * @param x The raised IOException. + * @param method The name of the method in which the exception was + * raised. This is one of the methods of the MBeanServer + * interface. + * @return A RuntimeException that should be thrown by the caller. + * In this default implementation, this is an + * {@link UndeclaredThrowableException} wrapping x. + **/ + protected RuntimeException handleIOException(IOException x, + String method) { + return Util.newRuntimeIOException(x); + } + + + //-------------------------------------------- + //-------------------------------------------- + // + // Implementation of the MBeanServer interface + // + //-------------------------------------------- + //-------------------------------------------- + @Override + public void addNotificationListener(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException { + try { + super.addNotificationListener(name, listener, + filter, handback); + } catch (IOException x) { + throw handleIOException(x,"addNotificationListener"); + } + } + + @Override + public void addNotificationListener(ObjectName name, + ObjectName listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException { + try { + super.addNotificationListener(name, listener, + filter, handback); + } catch (IOException x) { + throw handleIOException(x,"addNotificationListener"); + } + } + + @Override + public ObjectInstance createMBean(String className, ObjectName name) + throws + ReflectionException, + InstanceAlreadyExistsException, + MBeanRegistrationException, + MBeanException, + NotCompliantMBeanException { + try { + return super.createMBean(className, name); + } catch (IOException x) { + throw handleIOException(x,"createMBean"); + } + } + + @Override + public ObjectInstance createMBean(String className, ObjectName name, + Object params[], String signature[]) + throws + ReflectionException, + InstanceAlreadyExistsException, + MBeanRegistrationException, + MBeanException, + NotCompliantMBeanException { + try { + return super.createMBean(className, name, + params, signature); + } catch (IOException x) { + throw handleIOException(x,"createMBean"); + } + } + + @Override + public ObjectInstance createMBean(String className, + ObjectName name, + ObjectName loaderName) + throws + ReflectionException, + InstanceAlreadyExistsException, + MBeanRegistrationException, + MBeanException, + NotCompliantMBeanException, + InstanceNotFoundException { + try { + return super.createMBean(className, name, loaderName); + } catch (IOException x) { + throw handleIOException(x,"createMBean"); + } + } + + @Override + public ObjectInstance createMBean(String className, + ObjectName name, + ObjectName loaderName, + Object params[], + String signature[]) + throws + ReflectionException, + InstanceAlreadyExistsException, + MBeanRegistrationException, + MBeanException, + NotCompliantMBeanException, + InstanceNotFoundException { + try { + return super.createMBean(className, name, loaderName, + params, signature); + } catch (IOException x) { + throw handleIOException(x,"createMBean"); + } + } + + /** + * @deprecated see {@link MBeanServer#deserialize(ObjectName,byte[]) + * MBeanServer} + **/ + @Deprecated + public ObjectInputStream deserialize(ObjectName name, byte[] data) + throws InstanceNotFoundException, OperationsException { + final ObjectName sourceName = toSourceOrRuntime(name); + try { + return source().deserialize(sourceName,data); + } catch (RuntimeException x) { + throw makeCompliantRuntimeException(x); + } + } + + /** + * @deprecated see {@link MBeanServer#deserialize(String,byte[]) + * MBeanServer} + */ + @Deprecated + public ObjectInputStream deserialize(String className, byte[] data) + throws OperationsException, ReflectionException { + try { + return source().deserialize(className,data); + } catch (RuntimeException x) { + throw makeCompliantRuntimeException(x); + } + } + + /** + * @deprecated see {@link MBeanServer#deserialize(String,ObjectName,byte[]) + * MBeanServer} + */ + @Deprecated + public ObjectInputStream deserialize(String className, + ObjectName loaderName, + byte[] data) + throws + InstanceNotFoundException, + OperationsException, + ReflectionException { + try { + return source().deserialize(className,loaderName,data); + } catch (RuntimeException x) { + throw makeCompliantRuntimeException(x); + } + } + + @Override + public Object getAttribute(ObjectName name, String attribute) + throws + MBeanException, + AttributeNotFoundException, + InstanceNotFoundException, + ReflectionException { + try { + return super.getAttribute(name, attribute); + } catch (IOException x) { + throw handleIOException(x,"getAttribute"); + } + } + + @Override + public AttributeList getAttributes(ObjectName name, String[] attributes) + throws InstanceNotFoundException, ReflectionException { + try { + return super.getAttributes(name, attributes); + } catch (IOException x) { + throw handleIOException(x,"getAttributes"); + } + } + + public ClassLoader getClassLoader(ObjectName loaderName) + throws InstanceNotFoundException { + final ObjectName sourceName = toSourceOrRuntime(loaderName); + try { + return source().getClassLoader(sourceName); + } catch (RuntimeException x) { + throw makeCompliantRuntimeException(x); + } + } + + public ClassLoader getClassLoaderFor(ObjectName mbeanName) + throws InstanceNotFoundException { + final ObjectName sourceName = toSourceOrRuntime(mbeanName); + try { + return source().getClassLoaderFor(sourceName); + } catch (RuntimeException x) { + throw makeCompliantRuntimeException(x); + } + } + + public ClassLoaderRepository getClassLoaderRepository() { + try { + return source().getClassLoaderRepository(); + } catch (RuntimeException x) { + throw makeCompliantRuntimeException(x); + } + } + + @Override + public String getDefaultDomain() { + try { + return super.getDefaultDomain(); + } catch (IOException x) { + throw handleIOException(x,"getDefaultDomain"); + } + } + + @Override + public String[] getDomains() { + try { + return super.getDomains(); + } catch (IOException x) { + throw handleIOException(x,"getDomains"); + } + } + + @Override + public Integer getMBeanCount() { + try { + return super.getMBeanCount(); + } catch (IOException x) { + throw handleIOException(x,"getMBeanCount"); + } + } + + @Override + public MBeanInfo getMBeanInfo(ObjectName name) + throws + InstanceNotFoundException, + IntrospectionException, + ReflectionException { + try { + return super.getMBeanInfo(name); + } catch (IOException x) { + throw handleIOException(x,"getMBeanInfo"); + } + } + + @Override + public ObjectInstance getObjectInstance(ObjectName name) + throws InstanceNotFoundException { + try { + return super.getObjectInstance(name); + } catch (IOException x) { + throw handleIOException(x,"getObjectInstance"); + } + } + + public Object instantiate(String className) + throws ReflectionException, MBeanException { + try { + return source().instantiate(className); + } catch (RuntimeException x) { + throw makeCompliantRuntimeException(x); + } + } + + public Object instantiate(String className, + Object params[], + String signature[]) + throws ReflectionException, MBeanException { + try { + return source().instantiate(className, + params,signature); + } catch (RuntimeException x) { + throw makeCompliantRuntimeException(x); + } + } + + public Object instantiate(String className, ObjectName loaderName) + throws ReflectionException, MBeanException, + InstanceNotFoundException { + final ObjectName srcLoaderName = toSourceOrRuntime(loaderName); + try { + return source().instantiate(className,srcLoaderName); + } catch (RuntimeException x) { + throw makeCompliantRuntimeException(x); + } + } + + public Object instantiate(String className, ObjectName loaderName, + Object params[], String signature[]) + throws ReflectionException, MBeanException, + InstanceNotFoundException { + final ObjectName srcLoaderName = toSourceOrRuntime(loaderName); + try { + return source().instantiate(className,srcLoaderName, + params,signature); + } catch (RuntimeException x) { + throw makeCompliantRuntimeException(x); + } + } + + @Override + public Object invoke(ObjectName name, String operationName, + Object params[], String signature[]) + throws + InstanceNotFoundException, + MBeanException, + ReflectionException { + try { + return super.invoke(name,operationName,params,signature); + } catch (IOException x) { + throw handleIOException(x,"invoke"); + } + } + + @Override + public boolean isInstanceOf(ObjectName name, String className) + throws InstanceNotFoundException { + try { + return super.isInstanceOf(name, className); + } catch (IOException x) { + throw handleIOException(x,"isInstanceOf"); + } + } + + @Override + public boolean isRegistered(ObjectName name) { + try { + return super.isRegistered(name); + } catch (IOException x) { + throw handleIOException(x,"isRegistered"); + } + } + + @Override + public Set queryMBeans(ObjectName name, QueryExp query) { + try { + return super.queryMBeans(name, query); + } catch (IOException x) { + handleIOException(x,"queryMBeans"); + return Collections.emptySet(); + } + } + + @Override + public Set queryNames(ObjectName name, QueryExp query) { + try { + return super.queryNames(name, query); + } catch (IOException x) { + handleIOException(x,"queryNames"); + return Collections.emptySet(); + } + } + + public ObjectInstance registerMBean(Object object, ObjectName name) + throws + InstanceAlreadyExistsException, + MBeanRegistrationException, + NotCompliantMBeanException { + final ObjectName sourceName = newSourceMBeanName(name); + try { + return processOutputInstance( + source().registerMBean(object,sourceName)); + } catch (RuntimeException x) { + throw makeCompliantRuntimeException(x); + } + } + + @Override + public void removeNotificationListener(ObjectName name, + NotificationListener listener) + throws InstanceNotFoundException, ListenerNotFoundException { + try { + super.removeNotificationListener(name, listener); + } catch (IOException x) { + throw handleIOException(x,"removeNotificationListener"); + } + } + + @Override + public void removeNotificationListener(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, ListenerNotFoundException { + try { + super.removeNotificationListener(name, listener, + filter, handback); + } catch (IOException x) { + throw handleIOException(x,"removeNotificationListener"); + } + } + + @Override + public void removeNotificationListener(ObjectName name, + ObjectName listener) + throws InstanceNotFoundException, ListenerNotFoundException { + try { + super.removeNotificationListener(name, listener); + } catch (IOException x) { + throw handleIOException(x,"removeNotificationListener"); + } + } + + @Override + public void removeNotificationListener(ObjectName name, + ObjectName listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, ListenerNotFoundException { + try { + super.removeNotificationListener(name, listener, + filter, handback); + } catch (IOException x) { + throw handleIOException(x,"removeNotificationListener"); + } + } + + @Override + public void setAttribute(ObjectName name, Attribute attribute) + throws + InstanceNotFoundException, + AttributeNotFoundException, + InvalidAttributeValueException, + MBeanException, + ReflectionException { + try { + super.setAttribute(name, attribute); + } catch (IOException x) { + throw handleIOException(x,"setAttribute"); + } + } + + @Override + public AttributeList setAttributes(ObjectName name, + AttributeList attributes) + throws InstanceNotFoundException, ReflectionException { + try { + return super.setAttributes(name, attributes); + } catch (IOException x) { + throw handleIOException(x,"setAttributes"); + } + } + + @Override + public void unregisterMBean(ObjectName name) + throws InstanceNotFoundException, MBeanRegistrationException { + try { + super.unregisterMBean(name); + } catch (IOException x) { + throw handleIOException(x,"unregisterMBean"); + } + } + + + public static MBeanServer cd(MBeanServer source, String sourcePath) { + if (source == null) throw new IllegalArgumentException("null"); + if (source.getClass().equals(RoutingServerProxy.class)) { + // cast is OK here, but findbugs complains unless we use class.cast + final RoutingServerProxy other = + RoutingServerProxy.class.cast(source); + final String target = other.getTargetNamespace(); + + // Avoid multiple layers of serialization. + // + // We construct a new proxy from the original source instead of + // stacking a new proxy on top of the old one. + // - that is we replace + // cd ( cd ( x, dir1), dir2); + // by + // cd (x, dir1//dir2); + // + // We can do this only when the source class is exactly + // NamespaceServerProxy. + // + if (target == null || target.equals("")) { + final String path = + JMXNamespaces.concat(other.getSourceNamespace(), + sourcePath); + return new RoutingServerProxy(other.source(),path,"", + other.forwardsContext); + } + // Note: we could do possibly something here - but it would involve + // removing part of targetDir, and possibly adding + // something to sourcePath. + // Too complex to bother! => simply default to stacking... + } + return new RoutingServerProxy(source,sourcePath); + } +} diff --git a/src/share/classes/com/sun/jmx/namespace/package.html b/src/share/classes/com/sun/jmx/namespace/package.html new file mode 100644 index 000000000..6677288ca --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/package.html @@ -0,0 +1,45 @@ + + + + + The <code>com.sun.jmx.namespace</code> package + + + +

      The com.sun.jmx.namespace package contains + sun specific implementation classes used to implement the + JMX namespaces. +

      +

      DO NOT USE THESE CLASSES DIRECTLY

      +

      + This API is a Sun internal API and is subject to changes without notice. +

      +

      The public API through wich these proprietary classes can be + invoked is located in javax.management.namespace + package. +

      + + diff --git a/src/share/classes/com/sun/jmx/namespace/serial/DefaultRewritingProcessor.java b/src/share/classes/com/sun/jmx/namespace/serial/DefaultRewritingProcessor.java new file mode 100644 index 000000000..f458378cb --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/serial/DefaultRewritingProcessor.java @@ -0,0 +1,150 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.namespace.serial; + + +import javax.management.ObjectInstance; +import javax.management.ObjectName; + +/** + * Class DefaultRewritingProcessor. Rewrite ObjectName in input & output + * parameters. + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +// We know that rewriting using serialization is costly. +// This object tries to determine whether an object needs rewriting prior +// to rewriting, and rewrites by creating a new object in those cases +// where we know how to recreate a new object (e.g. a Notification). +// Rewriting is however usually not used - so this object is just a +// skeleton that eventually uses serialization... +// +class DefaultRewritingProcessor extends RewritingProcessor { + + + private static enum RewriteMode { + INPUT, // Input from target to source (parameters) + OUTPUT // Output from source to target (results) + }; + + private final boolean identity; + + public DefaultRewritingProcessor(String targetDirName) { + this(targetDirName,null); + } + + /** Creates a new instance of SerialParamProcessor */ + public DefaultRewritingProcessor(final String remove, final String add) { + super(new SerialRewritingProcessor(remove, add)); + identity = remove.equals(add); + } + + private ObjectName rewriteObjectName(RewriteMode mode, + ObjectName name) { + return changeContext(mode, name); + } + + private ObjectInstance rewriteObjectInstance(RewriteMode mode, + ObjectInstance moi) { + final ObjectName srcName = moi.getObjectName(); + final ObjectName targetName = changeContext(mode,srcName); + if (targetName == srcName) return moi; + return new ObjectInstance(targetName,moi.getClassName()); + } + + + private Object processObject(RewriteMode mode, Object obj) { + if (obj == null) return null; + + // Some things which will always needs rewriting: + // ObjectName, ObjectInstance, and Notifications. + // Take care of those we can handle here... + // + if (obj instanceof ObjectName) + return rewriteObjectName(mode,(ObjectName) obj); + else if (obj instanceof ObjectInstance) + return rewriteObjectInstance(mode,(ObjectInstance) obj); + + // TODO: add other standard JMX classes - like e.g. MBeanInfo... + // + + // Well, the object may contain an ObjectName => pass it to + // our serial rewriting delegate... + // + return processAnyObject(mode,obj); + } + + + private Object processAnyObject(RewriteMode mode, Object obj) { + switch (mode) { + case INPUT: + return super.rewriteInput(obj); + case OUTPUT: + return super.rewriteOutput(obj); + default: // can't happen. + throw new AssertionError(); + } + } + + private ObjectName changeContext(RewriteMode mode, ObjectName name) { + switch (mode) { + case INPUT: + return toSourceContext(name); + case OUTPUT: + return toTargetContext(name); + default: // can't happen. + throw new AssertionError(); + } + } + + @Override + public ObjectName toTargetContext(ObjectName srcName) { + if (identity) return srcName; + return super.toTargetContext(srcName); + } + + @Override + public ObjectName toSourceContext(ObjectName targetName) { + if (identity) return targetName; + return super.toSourceContext(targetName); + } + + @SuppressWarnings("unchecked") + @Override + public T rewriteInput(T input) { + if (identity) return input; + return (T) processObject(RewriteMode.INPUT,input); + } + + @SuppressWarnings("unchecked") + @Override + public T rewriteOutput(T result) { + if (identity) return result; + return (T) processObject(RewriteMode.OUTPUT,result); + } +} diff --git a/src/share/classes/com/sun/jmx/namespace/serial/IdentityProcessor.java b/src/share/classes/com/sun/jmx/namespace/serial/IdentityProcessor.java new file mode 100644 index 000000000..32cca2d2d --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/serial/IdentityProcessor.java @@ -0,0 +1,74 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.namespace.serial; + + +import javax.management.ObjectInstance; +import javax.management.ObjectName; + +/** + * Class RoutingOnlyProcessor. A RewritingProcessor that uses + * Java Serialization to rewrite ObjectNames contained in + * input & results... + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * + * @since 1.7 + */ +class IdentityProcessor extends RewritingProcessor { + + + /** Creates a new instance of SerialRewritingProcessor */ + public IdentityProcessor() { + } + + @Override + public T rewriteOutput(T result) { + return result; + } + + @Override + public T rewriteInput(T input) { + return input; + } + + @Override + public final ObjectName toTargetContext(ObjectName sourceName) { + return sourceName; + } + + @Override + public final ObjectInstance toTargetContext(ObjectInstance sourceMoi) { + return sourceMoi; + } + + @Override + public final ObjectName toSourceContext(ObjectName targetName) { + return targetName; + } + +} diff --git a/src/share/classes/com/sun/jmx/namespace/serial/JMXNamespaceContext.java b/src/share/classes/com/sun/jmx/namespace/serial/JMXNamespaceContext.java new file mode 100644 index 000000000..6416bfb96 --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/serial/JMXNamespaceContext.java @@ -0,0 +1,145 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.namespace.serial; + +import com.sun.jmx.defaults.JmxProperties; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.logging.Level; +import java.util.logging.Logger; + + +/** + * The JMXNamespaceContext class is used to implement a thread local + * serialization / deserialization context for namespaces. + *

      + * This class is consulted by {@link javax.management.ObjectName} at + * serialization / deserialization time. + * The serialization or deserialization context is established by + * by the {@link SerialRewritingProcessor} defined in this package. + *

      + * These classes are Sun proprietary APIs, subject to change without + * notice. Do not use these classes directly. + * The public API to rewrite ObjectNames embedded in parameters is + * defined in {@link javax.management.namespace.JMXNamespaces}. + * + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +public class JMXNamespaceContext { + + private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; + + public final String prefixToRemove; + public final String prefixToAdd; + + private JMXNamespaceContext(String add, String remove) { + prefixToRemove = (remove==null?"":remove); + prefixToAdd = (add==null?"":add); + } + + private final static class SerialContext { + private JMXNamespaceContext serializationContext; + private JMXNamespaceContext deserializationContext; + public SerialContext(){ + serializationContext = new JMXNamespaceContext("",""); + deserializationContext = new JMXNamespaceContext("",""); + } + } + + private final static ThreadLocal prefix = + new ThreadLocal() { + @Override + protected SerialContext initialValue() { + return new SerialContext(); + } + }; + + public static JMXNamespaceContext getSerializationContext() { + return prefix.get().serializationContext; + } + + public static JMXNamespaceContext getDeserializationContext() { + return prefix.get().deserializationContext; + } + + private static String[] setSerializationContext(String oldPrefix, + String newPrefix) { + final SerialContext c = prefix.get(); + JMXNamespaceContext dc = c.serializationContext; + String[] old = {dc.prefixToRemove, dc.prefixToAdd}; + c.serializationContext = new JMXNamespaceContext(newPrefix,oldPrefix); + return old; + } + + private static String[] setDeserializationContext(String oldPrefix, + String newPrefix) { + final SerialContext c = prefix.get(); + JMXNamespaceContext dc = c.deserializationContext; + String[] old = {dc.prefixToRemove, dc.prefixToAdd}; + c.deserializationContext = new JMXNamespaceContext(newPrefix,oldPrefix); + return old; + } + + static void serialize(ObjectOutputStream stream, Object obj, + String prefixToRemove, String prefixToAdd) + throws IOException { + final String[] old = + setSerializationContext(prefixToRemove,prefixToAdd); + try { + stream.writeObject(obj); + } finally { + try { + setSerializationContext(old[0],old[1]); + } catch (Exception x) { + LOG.log(Level.FINEST, + "failed to restore serialization context",x); + } + } + } + + static Object deserialize(ObjectInputStream stream, + String prefixToRemove, + String prefixToAdd) + throws IOException, ClassNotFoundException { + final String[] old = + setDeserializationContext(prefixToRemove,prefixToAdd); + try { + return stream.readObject(); + } finally { + try { + setDeserializationContext(old[0],old[1]); + } catch (Exception x) { + LOG.log(Level.FINEST, + "failed to restore serialization context",x); + } + } + } + +} diff --git a/src/share/classes/com/sun/jmx/namespace/serial/RewritingProcessor.java b/src/share/classes/com/sun/jmx/namespace/serial/RewritingProcessor.java new file mode 100644 index 000000000..2c81be934 --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/serial/RewritingProcessor.java @@ -0,0 +1,362 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.namespace.serial; + + +import javax.management.ObjectInstance; +import javax.management.ObjectName; + +/** + * An object that can rewrite ObjectNames contained in input/output + * parameters when entering/leaving a {@link javax.management.namespace + * namespace}. + *

      When entering a {@link javax.management.namespace + * namespace}, the {@code namespace} prefix is stripped from + * ObjectNames contained in input parameters. When leaving a + * {@code namespace}, + * the {@code namespace} prefix is prepended to the ObjectNames contained in + * the result parameters returned from that {@code namespace}. + *

      + *

      Objects that need to perform these operations usually use a + * {@code RewritingProcessor} for that purpose.
      + * The {@code RewritingProcessor} allows a somewhat larger + * transformation in which part of a prefix {@link #newRewritingProcessor + * remove} can be replaced by another prefix {@link #newRewritingProcessor + * add}. The transformation described above correspond to the case where + * {@code remove} is the stripped {@link javax.management.namespace + * namespace} prefix (removed when entering the {@code namespace}) and + * {@code add} is the empty String {@code ""}. + *
      + * It is interesting to note that {@link + * javax.management.JMXNamespaces#narrowToNamespace narrowToNamespace} + * operations use the inverse transformation (that is, {@code remove} is + * the empty String {@code ""} and {@code add} is the {@link + * javax.management.namespace namespace} prefix). + *
      + * On a more general scale, {@link #rewriteInput rewriteInput} removes + * {@link #newRewritingProcessor remove} and the prepend {@link + * #newRewritingProcessor add}, and {@link #rewriteOutput rewriteOutput} + * does the opposite, removing {@link #newRewritingProcessor add}, and + * then adding {@link #newRewritingProcessor remove}. + *
      + * An implementation of {@code RewritingProcessor} should make sure that + * rewriteInput(rewriteOutput(x,clp),clp) and + * rewriteOutput(rewriteInput(x,clp),clp) always return + * {@code x} or an exact clone of {@code x}. + *

      + *

      A default implementation of {@code RewritingProcessor} based on + * Java Object Serialization can be + * obtained from {@link #newRewritingProcessor newRewritingProcessor}. + *

      + *

      + * By default, the instances of {@code RewritingProcessor} returned by + * {@link #newRewritingProcessor newRewritingProcessor} will rewrite + * ObjectNames contained in instances of classes they don't know about by + * serializing and then deserializing such object instances. This will + * happen even if such instances don't - or can't contain ObjectNames, + * because the default implementation of {@code RewritingProcessor} will + * not be able to determine whether instances of such classes can/do contain + * instance of ObjectNames before serializing/deserializing them. + *

      + *

      If you are using custom classes that the default implementation of + * {@code RewritingProcessor} don't know about, it can be interesting to + * prevent an instance of {@code RewritingProcessor} to serialize/deserialize + * instances of such classes for nothing. In that case, you could customize + * the behavior of such a {@code RewritingProcessor} by wrapping it in a + * custom subclass of {@code RewritingProcessor} as shown below: + *

      + * public class MyRewritingProcessor extends RewritingProcessor {
      + *      MyRewritingProcessor(String remove, String add) {
      + *          this(RewritingProcessor.newRewritingProcessor(remove,add));
      + *      }
      + *      MyRewritingProcessor(RewritingProcessor delegate) {
      + *          super(delegate);
      + *      }
      + *
      + *   T rewriteInput(T input) {
      + *          if (input == null) return null;
      + *          if (MyClass.equals(input.getClass())) {
      + *              // I know that MyClass doesn't contain any ObjectName
      + *              return (T) input;
      + *          }
      + *          return super.rewriteInput(input);
      + *      }
      + *   T rewriteOutput(T result) {
      + *          if (result == null) return null;
      + *          if (MyClass.equals(result.getClass())) {
      + *              // I know that MyClass doesn't contain any ObjectName
      + *              return (T) result;
      + *          }
      + *          return super.rewriteOutput(result);
      + *      }
      + * }
      + * 
      + *

      + *

      Such a subclass may also provide an alternate way of rewriting + * custom subclasses for which rewriting is needed - for instance: + *

      + * public class MyRewritingProcessor extends RewritingProcessor {
      + *      MyRewritingProcessor(String remove, String add) {
      + *          this(RewritingProcessor.newRewritingProcessor(remove,add));
      + *      }
      + *      MyRewritingProcessor(RewritingProcessor delegate) {
      + *          super(delegate);
      + *      }
      + *
      + *   T rewriteInput(T input) {
      + *          if (input == null) return null;
      + *          if (MyClass.equals(input.getClass())) {
      + *              // I know that MyClass doesn't contain any ObjectName
      + *              return (T) input;
      + *          } else if (MyOtherClass.equals(input.getClass())) {
      + *              // Returns a new instance in which ObjectNames have been
      + *              // replaced.
      + *              final ObjectName aname = ((MyOtherClass)input).getName();
      + *              return (T) (new MyOtherClass(super.rewriteInput(aname)));
      + *          }
      + *          return super.rewriteInput(input,clp);
      + *      }
      + *   T rewriteOutput(T result) {
      + *          if (result == null) return null;
      + *          if (MyClass.equals(result.getClass())) {
      + *              // I know that MyClass doesn't contain any ObjectName
      + *              return (T) result;
      + *          } else if (MyOtherClass.equals(result.getClass())) {
      + *              // Returns a new instance in which ObjectNames have been
      + *              // replaced.
      + *              final ObjectName aname = ((MyOtherClass)result).getName();
      + *              return (T) (new MyOtherClass(super.rewriteOutput(aname)));
      + *          }
      + *          return super.rewriteOutput(result,clp);
      + *      }
      + * }
      + * 
      + *

      + *

      If your application only uses {@link javax.management.MXBean MXBeans}, + * or MBeans using simple types, and doesn't define any custom subclass of + * {@link javax.management.Notification}, you should never write such + * such {@code RewitingProcessor} implementations. + *

      + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +public abstract class RewritingProcessor { + /** + * A logger for this class. + **/ + private final RewritingProcessor delegate; + + /** + * Creates a new instance of RewritingProcessor. + *

      This is equivalent to calling {@link + * #RewritingProcessor(RewritingProcessor) RewritingProcessor(null)}. + *

      + **/ + protected RewritingProcessor() { + this(null); + } + + /** + * Creates a new instance of RewritingProcessor, with a delegate. + * @param delegate a {@code RewritingProcessor} to which all the + * calls will be delegated. When implementing a subclass + * of {@code RewritingProcessor}, calling {@link + * #rewriteInput super.rewriteInput} will invoke + * {@code delegate.rewriteInput} and calling {@link + * #rewriteOutput super.rewriteOutput} will invoke + * {@code delegate.rewriteOutput}. + * + **/ + protected RewritingProcessor(RewritingProcessor delegate) { + this.delegate = delegate; + } + + /** + * Rewrites ObjectNames when {@link RewritingProcessor leaving} a {@link + * javax.management.namespace namespace}. + *

      + * Returns {@code obj}, if it is known that {@code obj} doesn't contain + * any ObjectName, or a new copied instance of {@code obj} in which + * ObjectNames (if any) will have been rewritten, if {@code obj} contains + * ObjectNames, or if it is not known whether {@code obj} contains + * ObjectNames or not. + *

      + *

      + * The default implementation of this method is as follows: if the + * {@link #RewritingProcessor(RewritingProcessor) delegate} is {@code + * null}, throws an {@link IllegalArgumentException}. Otherwise, + * returns {@code delegate.rewriteOutput(obj)}. + *

      + *

      This behavior can be overridden by subclasses as shown in this + * class {@link RewritingProcessor description}. + *

      + * @param obj The result to be rewritten if needed. + * + * @return {@code obj}, or a clone of {@code obj} in which ObjectNames + * have been rewritten. See this class {@link RewritingProcessor + * description} for more details. + * @throws IllegalArgumentException if this implementation does not know + * how to rewrite the object. + **/ + public T rewriteOutput(T obj) { + if (obj == null) return null; + if (delegate != null) + return delegate.rewriteOutput(obj); + throw new IllegalArgumentException("can't rewrite "+ + obj.getClass().getName()); + } + + /** + * Rewrites ObjectNames when {@link RewritingProcessor entering} a {@link + * javax.management.namespace namespace}. + *

      + * Returns {@code obj}, if it is known that {@code obj} doesn't contain + * any ObjectName, or a new copied instance of {@code obj} in which + * ObjectNames (if any) will have been rewritten, if {@code obj} contains + * ObjectNames, or if it is not known whether {@code obj} contains + * ObjectNames or not. + *

      + *

      + * The default implementation of this method is as follows: if the + * {@link #RewritingProcessor(RewritingProcessor) delegate} is {@code + * null}, throws an {@link IllegalArgumentException}. Otherwise, + * returns {@code delegate.rewriteInput(obj)}. + *

      + *

      This behavior can be overridden by subclasses as shown in this + * class {@link RewritingProcessor description}. + *

      + * @param obj The result to be rewritten if needed. + * @return {@code obj}, or a clone of {@code obj} in which ObjectNames + * have been rewritten. See this class {@link RewritingProcessor + * description} for more details. + * @throws IllegalArgumentException if this implementation does not know + * how to rewrite the object. + **/ + public T rewriteInput(T obj) { + if (obj == null) return null; + if (delegate != null) + return delegate.rewriteInput(obj); + throw new IllegalArgumentException("can't rewrite "+ + obj.getClass().getName()); + } + + /** + * Translate a routing ObjectName from the target (calling) context to + * the source (called) context when {@link RewritingProcessor entering} a + * {@link javax.management.namespace namespace}. + *

      + * The default implementation of this method is as follows: if the + * {@link #RewritingProcessor(RewritingProcessor) delegate} is {@code + * null}, throws an {@link IllegalArgumentException}. Otherwise, + * returns {@code delegate.toSourceContext(targetName)}. + *

      + *

      This behavior can be overridden by subclasses as shown in this + * class {@link RewritingProcessor description}. + *

      + * @param targetName The routing target ObjectName to translate. + * @return The ObjectName translated to the source context. + * @throws IllegalArgumentException if this implementation does not know + * how to rewrite the object. + **/ + public ObjectName toSourceContext(ObjectName targetName) { + if (delegate != null) + return delegate.toSourceContext(targetName); + throw new IllegalArgumentException("can't rewrite targetName: "+ + " no delegate."); + } + + /** + * Translate an ObjectName returned from the source context into + * the target (calling) context when {@link RewritingProcessor leaving} a + * {@link javax.management.namespace namespace}. + *

      + * The default implementation of this method is as follows: if the + * {@link #RewritingProcessor(RewritingProcessor) delegate} is {@code + * null}, throws an {@link IllegalArgumentException}. Otherwise, + * returns {@code delegate.toTargetContext(sourceName)}. + *

      + *

      This behavior can be overridden by subclasses as shown in this + * class {@link RewritingProcessor description}. + *

      + * @param sourceName The routing source ObjectName to translate to the + * target context. + * @return The ObjectName translated to the target context. + * @throws IllegalArgumentException if this implementation does not know + * how to rewrite the object. + **/ + public ObjectName toTargetContext(ObjectName sourceName) { + if (delegate != null) + return delegate.toTargetContext(sourceName); + throw new IllegalArgumentException("can't rewrite sourceName: "+ + " no delegate."); + } + + /** + * Translate an ObjectInstance returned from the source context into + * the target (calling) context when {@link RewritingProcessor leaving} a + * {@link javax.management.namespace namespace}. + *

      + * The default implementation of this method is as follows: if the + * {@link #RewritingProcessor(RewritingProcessor) delegate} is {@code + * null}, throws an {@link IllegalArgumentException}. Otherwise, + * returns {@code delegate.toTargetContext(sourceMoi)}. + *

      + *

      This behavior can be overridden by subclasses as shown in this + * class {@link RewritingProcessor description}. + *

      + * @param sourceMoi The routing source ObjectInstance to translate. + * @return The ObjectInstance translated to the target context. + * @throws IllegalArgumentException if this implementation does not know + * how to rewrite the object. + **/ + public ObjectInstance toTargetContext(ObjectInstance sourceMoi) { + if (delegate != null) + return delegate.toTargetContext(sourceMoi); + throw new IllegalArgumentException("can't rewrite sourceName: "+ + " no delegate."); + } + + /** + * Creates a new default instance of {@link RewritingProcessor}. + * @param remove The prefix to remove from {@link ObjectName ObjectNames} + * when {@link RewritingProcessor entering} the {@link + * javax.management.namespace namespace}. + * @param add The prefix to add to {@link ObjectName ObjectNames} + * when {@link RewritingProcessor entering} the {@link + * javax.management.namespace namespace} (this is performed + * after having removed the {@code remove} prefix. + * @return A new {@link RewritingProcessor} processor object that will + * perform the requested operation, using Java serialization if + * necessary. + **/ + public static RewritingProcessor newRewritingProcessor(String remove, + String add) { + return new DefaultRewritingProcessor(remove,add); + } + +} diff --git a/src/share/classes/com/sun/jmx/namespace/serial/RoutingOnlyProcessor.java b/src/share/classes/com/sun/jmx/namespace/serial/RoutingOnlyProcessor.java new file mode 100644 index 000000000..1df2e26a9 --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/serial/RoutingOnlyProcessor.java @@ -0,0 +1,74 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.namespace.serial; + +import com.sun.jmx.namespace.ObjectNameRouter; + + +import javax.management.ObjectInstance; +import javax.management.ObjectName; + +/** + * Class RoutingOnlyProcessor. A RewritingProcessor that uses + * Java Serialization to rewrite ObjectNames contained in + * input and results... + * + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +class RoutingOnlyProcessor extends RewritingProcessor { + + final ObjectNameRouter router; + + public RoutingOnlyProcessor(String targetDirName) { + this(targetDirName,null); + } + + /** Creates a new instance of RoutingOnlyProcessor */ + public RoutingOnlyProcessor(final String remove, final String add) { + super(new IdentityProcessor()); + if (remove == null || add == null) + throw new IllegalArgumentException("Null argument"); + router = new ObjectNameRouter(remove,add); + } + + @Override + public final ObjectName toTargetContext(ObjectName sourceName) { + return router.toTargetContext(sourceName,false); + } + + @Override + public final ObjectName toSourceContext(ObjectName targetName) { + return router.toSourceContext(targetName,false); + } + + @Override + public final ObjectInstance toTargetContext(ObjectInstance sourceMoi) { + return router.toTargetContext(sourceMoi,false); + } +} diff --git a/src/share/classes/com/sun/jmx/namespace/serial/SerialRewritingProcessor.java b/src/share/classes/com/sun/jmx/namespace/serial/SerialRewritingProcessor.java new file mode 100644 index 000000000..d7e879822 --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/serial/SerialRewritingProcessor.java @@ -0,0 +1,172 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.jmx.namespace.serial; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InvalidClassException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.io.OutputStream; +import java.util.LinkedList; +import java.util.Queue; + +import javax.management.ObjectName; + +/** + * Class SerialRewritingProcessor. A RewritingProcessor that uses + * Java Serialization to rewrite ObjectNames contained in + * input & results... + *

      + * This API is a Sun internal API and is subject to changes without notice. + *

      + * @since 1.7 + */ +class SerialRewritingProcessor extends RewritingProcessor { + + + private static class CloneOutput extends ObjectOutputStream { + Queue> classQueue = new LinkedList>(); + + CloneOutput(OutputStream out) throws IOException { + super(out); + } + + @Override + protected void annotateClass(Class c) { + classQueue.add(c); + } + + @Override + protected void annotateProxyClass(Class c) { + classQueue.add(c); + } + } + + private static class CloneInput extends ObjectInputStream { + private final CloneOutput output; + + CloneInput(InputStream in, CloneOutput output) throws IOException { + super(in); + this.output = output; + } + + @Override + protected Class resolveClass(ObjectStreamClass osc) + throws IOException, ClassNotFoundException { + Class c = output.classQueue.poll(); + String expected = osc.getName(); + String found = (c == null) ? null : c.getName(); + if (!expected.equals(found)) { + throw new InvalidClassException("Classes desynchronized: " + + "found " + found + " when expecting " + expected); + } + return c; + } + + @Override + protected Class resolveProxyClass(String[] interfaceNames) + throws IOException, ClassNotFoundException { + return output.classQueue.poll(); + } + } + + + final String targetPrefix; + final String sourcePrefix; + final boolean identity; + + + public SerialRewritingProcessor(String targetDirName) { + this(targetDirName,null); + } + + /** Creates a new instance of SerialRewritingProcessor */ + public SerialRewritingProcessor(final String remove, final String add) { + super(new RoutingOnlyProcessor(remove,add)); + this.targetPrefix = remove; + this.sourcePrefix = add; + identity = targetPrefix.equals(sourcePrefix); + } + + private T switchContext(T result, String from,String to) + throws IOException, ClassNotFoundException { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final CloneOutput ostream = new CloneOutput(baos); + + JMXNamespaceContext.serialize(ostream,result,from,null); + ostream.flush(); + + final byte[] bytes = baos.toByteArray(); + final ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + final CloneInput istream = new CloneInput(bais, ostream); + @SuppressWarnings("unchecked") + final T clone = (T) JMXNamespaceContext.deserialize(istream,null,to); + return clone; + } + + @Override + @SuppressWarnings("unchecked") + public T rewriteOutput(T result) { + if (identity) return result; + return (T) processOutput(result); + } + + private Object processOutput(Object result) { + try { + if (result instanceof ObjectName) + return toTargetContext((ObjectName) result); + return switchContext(result,sourcePrefix,targetPrefix); + } catch (ClassNotFoundException x) { + throw new IllegalArgumentException("Can't process result: "+x,x); + } catch (IOException x) { + throw new IllegalArgumentException("Can't process result: "+x,x); + } + } + + @Override + @SuppressWarnings("unchecked") + public T rewriteInput(T input) { + if (identity) return input; + return (T) processInput(input); + } + + private Object processInput(Object input) { + try { + if (input instanceof ObjectName) + return toSourceContext((ObjectName) input); + return switchContext(input,targetPrefix,sourcePrefix); + } catch (ClassNotFoundException x) { + throw new IllegalArgumentException("Can't process input: "+x,x); + } catch (IOException x) { + throw new IllegalArgumentException("Can't process input: "+x,x); + } + } + +} diff --git a/src/share/classes/com/sun/jmx/namespace/serial/package.html b/src/share/classes/com/sun/jmx/namespace/serial/package.html new file mode 100644 index 000000000..fe2e8c64b --- /dev/null +++ b/src/share/classes/com/sun/jmx/namespace/serial/package.html @@ -0,0 +1,44 @@ + + + + + The <code>com.sun.jmx.namespace.serial</code> package + + + +

      The com.sun.jmx.namespace.serial package contains + sun specific implementation classes used to switch namespace + prefixes in ObjectName during serialization. +

      +

      NEVER USE THESE CLASSES DIRECTLY

      +

      + This API is a Sun internal API and is subject to changes without notice. +

      +

      The public API through which these proprietary classes can be invoked is + located in javax.management.namespace.JMXNamespaces +

      + + diff --git a/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java b/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java index 2a140c2fe..c3542cc1d 100644 --- a/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java +++ b/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java @@ -57,6 +57,7 @@ import javax.security.auth.Subject; public class ServerNotifForwarder { + public ServerNotifForwarder(MBeanServer mbeanServer, Map env, NotificationBuffer notifBuffer, @@ -85,7 +86,8 @@ public class ServerNotifForwarder { // Explicitly check MBeanPermission for addNotificationListener // - checkMBeanPermission(name, "addNotificationListener"); + checkMBeanPermission(getMBeanServerName(), + mbeanServer, name, "addNotificationListener"); if (notificationAccessController != null) { notificationAccessController.addNotificationListener( connectionId, name, getSubject()); @@ -155,7 +157,8 @@ public class ServerNotifForwarder { // Explicitly check MBeanPermission for removeNotificationListener // - checkMBeanPermission(name, "removeNotificationListener"); + checkMBeanPermission(getMBeanServerName(), + mbeanServer, name, "removeNotificationListener"); if (notificationAccessController != null) { notificationAccessController.removeNotificationListener( connectionId, name, getSubject()); @@ -330,13 +333,7 @@ public class ServerNotifForwarder { * Explicitly check the MBeanPermission for * the current access control context. */ - private void checkMBeanPermission(final ObjectName name, - final String actions) - throws InstanceNotFoundException, SecurityException { - checkMBeanPermission(mbeanServer, name, actions); - } - - public static void checkMBeanPermission( + public static void checkMBeanPermission(String serverName, final MBeanServer mbs, final ObjectName name, final String actions) throws InstanceNotFoundException, SecurityException { SecurityManager sm = System.getSecurityManager(); @@ -355,7 +352,9 @@ public class ServerNotifForwarder { throw (InstanceNotFoundException) extractException(e); } String classname = oi.getClassName(); - MBeanPermission perm = new MBeanPermission(classname, + MBeanPermission perm = new MBeanPermission( + serverName, + classname, null, name, actions); @@ -370,8 +369,8 @@ public class ServerNotifForwarder { TargetedNotification tn) { try { if (checkNotificationEmission) { - checkMBeanPermission( - name, "addNotificationListener"); + checkMBeanPermission(getMBeanServerName(), + mbeanServer, name, "addNotificationListener"); } if (notificationAccessController != null) { notificationAccessController.fetchNotification( @@ -433,11 +432,27 @@ public class ServerNotifForwarder { } } + private String getMBeanServerName() { + if (mbeanServerName != null) return mbeanServerName; + else return (mbeanServerName = getMBeanServerName(mbeanServer)); + } + + private static String getMBeanServerName(final MBeanServer server) { + final PrivilegedAction action = new PrivilegedAction() { + public String run() { + return Util.getMBeanServerSecurityName(server); + } + }; + return AccessController.doPrivileged(action); + } + + //------------------ // PRIVATE VARIABLES //------------------ private MBeanServer mbeanServer; + private volatile String mbeanServerName; private final String connectionId; diff --git a/src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java b/src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java index b3520a408..d0f81e1d2 100644 --- a/src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java +++ b/src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java @@ -25,6 +25,7 @@ package com.sun.jmx.remote.util; +import com.sun.jmx.defaults.JmxProperties; import com.sun.jmx.event.EventClientFactory; import java.lang.reflect.InvocationHandler; @@ -45,6 +46,7 @@ import javax.management.NotificationListener; import javax.management.ObjectName; import javax.management.event.EventClient; import javax.management.event.EventClientDelegate; +import javax.management.namespace.JMXNamespaces; /** * Class EventClientConnection - a {@link Proxy} that wraps an @@ -63,12 +65,10 @@ public class EventClientConnection implements InvocationHandler, /** * A logger for this class. **/ - private static final Logger LOG = - Logger.getLogger(EventClientConnection.class.getName()); + private static final Logger LOG = JmxProperties.NOTIFICATION_LOGGER; - private static final String NAMESPACE_SEPARATOR = "//"; private static final int NAMESPACE_SEPARATOR_LENGTH = - NAMESPACE_SEPARATOR.length(); + JMXNamespaces.NAMESPACE_SEPARATOR.length(); /** * Creates a new {@code EventClientConnection}. @@ -212,9 +212,9 @@ public class EventClientConnection implements InvocationHandler, } final ObjectName mbean = (ObjectName) args[0]; - final EventClient client = getEventClient(); + final EventClient evtClient = getEventClient(); - // Fails if client is null AND the MBean we try to listen to is + // Fails if evtClient is null AND the MBean we try to listen to is // in a subnamespace. We fail here because we know this will not // work. // @@ -222,15 +222,15 @@ public class EventClientConnection implements InvocationHandler, // earlier agent (JDK 1.6 or earlier), then the EventClient will // be null (we can't use the event service with earlier JDKs). // - // In principle a null client indicates that the remote VM is of + // In principle a null evtClient indicates that the remote VM is of // an earlier version, in which case it shouldn't contain any namespace. // - // So having a null client AND an MBean contained in a namespace is + // So having a null evtClient AND an MBean contained in a namespace is // clearly an error case. // - if (client == null) { + if (evtClient == null) { final String domain = mbean.getDomain(); - final int index = domain.indexOf(NAMESPACE_SEPARATOR); + final int index = domain.indexOf(JMXNamespaces.NAMESPACE_SEPARATOR); if (index > -1 && index < (domain.length()-NAMESPACE_SEPARATOR_LENGTH)) { throw new UnsupportedOperationException(method.getName()+ @@ -256,9 +256,9 @@ public class EventClientConnection implements InvocationHandler, final NotificationFilter filter = (NotificationFilter) args[2]; final Object handback = args[3]; - if (client != null) { + if (evtClient != null) { // general case - client.addNotificationListener(mbean,listener,filter,handback); + evtClient.addNotificationListener(mbean,listener,filter,handback); } else { // deprecated case. Only works for mbean in local namespace. connection.addNotificationListener(mbean,listener,filter, @@ -274,9 +274,9 @@ public class EventClientConnection implements InvocationHandler, switch (nargs) { case 2: - if (client != null) { + if (evtClient != null) { // general case - client.removeNotificationListener(mbean,listener); + evtClient.removeNotificationListener(mbean,listener); } else { // deprecated case. Only works for mbean in local namespace. connection.removeNotificationListener(mbean, listener); @@ -286,8 +286,8 @@ public class EventClientConnection implements InvocationHandler, case 4: NotificationFilter filter = (NotificationFilter) args[2]; Object handback = args[3]; - if (client != null) { - client.removeNotificationListener(mbean, + if (evtClient != null) { + evtClient.removeNotificationListener(mbean, listener, filter, handback); diff --git a/src/share/classes/javax/management/InstanceNotFoundException.java b/src/share/classes/javax/management/InstanceNotFoundException.java index f572d614d..baeaed095 100644 --- a/src/share/classes/javax/management/InstanceNotFoundException.java +++ b/src/share/classes/javax/management/InstanceNotFoundException.java @@ -51,4 +51,16 @@ public class InstanceNotFoundException extends OperationsException { public InstanceNotFoundException(String message) { super(message); } + + /** + * Constructor for the frequent case where the message is the ObjectName + * of the missing MBean. + * + * @param name the ObjectName of the missing MBean. + * + * @since 1.7 + */ + public InstanceNotFoundException(ObjectName name) { + this(name.toString()); + } } diff --git a/src/share/classes/javax/management/MBeanPermission.java b/src/share/classes/javax/management/MBeanPermission.java index 762f5defc..def34bed3 100644 --- a/src/share/classes/javax/management/MBeanPermission.java +++ b/src/share/classes/javax/management/MBeanPermission.java @@ -25,6 +25,7 @@ package javax.management; +import com.sun.jmx.mbeanserver.Util; import java.io.IOException; import java.io.ObjectInputStream; import java.security.Permission; @@ -42,10 +43,10 @@ import java.security.Permission; * permission that you need. When a sensitive operation is * being checked for permission, an MBeanPermission is constructed * representing the permission you need. The operation is only - * allowed if the permissions you have {@link #implies imply} the + * allowed if the permissions you have {@linkplain #implies imply} the * permission you need.

      * - *

      An MBeanPermission contains four items of information:

      + *

      An MBeanPermission contains five items of information:

      * *
        * @@ -57,6 +58,23 @@ import java.security.Permission; * *

        The action is returned by {@link #getActions()}.

        * + *
      • The MBean Server name.

        + * + *

        For a permission you need, this is the {@linkplain + * javax.management.MBeanServerFactory#getMBeanServerName + * name of the MBeanServer} + * containing the MBean for which the MBean + * permission is checked.

        + * + *

        For a permission you have, this is either the {@linkplain + * javax.management.MBeanServerFactory#getMBeanServerName + * name of the MBeanServer} in which the MBean + * you have this permission for must be registered, + * or a pattern against which that MBean Server name will be matched.
        + * An {@code mbeanServerName} pattern can also be empty or the single + * character {@code "*"}, both of which will match any {@code MBeanServer} name. + *

        + * *
      • The class name.

        * *

        For a permission you need, this is the class name of an MBean @@ -88,7 +106,7 @@ import java.security.Permission; * or operation you can access, or it is empty or the single character * "*", both of which grant access to any member.

        * - *
      • The object name.

        + *
      • The object name.

        * *

        For a permission you need, this is the {@link ObjectName} of the * MBean you are accessing. For operations that do not reference a @@ -103,15 +121,15 @@ import java.security.Permission; *

      * *

      If you have an MBeanPermission, it allows operations only if all - * four of the items match.

      + * five of the items match.

      * - *

      The class name, member, and object name can be written together - * as a single string, which is the name of this permission. + *

      The MBean Server name, class name, member, and object name can be written + * together as a single string, which is the name of this permission. * The name of the permission is the string returned by {@link * Permission#getName() getName()}. The format of the string is:

      * *
      - * className#member[objectName] + * mbeanServerName::className#member[objectName] *
      * *

      The object name is written using the usual syntax for {@link @@ -119,15 +137,18 @@ import java.security.Permission; * ]. It is terminated by a ] character * that is the last character in the string.

      * - *

      One or more of the className, member, - * or objectName may be omitted. If the - * member is omitted, the # may be too (but + *

      One or more of the mbeanServerName, className, + * member, or objectName may be omitted. If the + * mbeanServerName is omitted, the :: may be too (but + * does not have to be). + * If the member is omitted, the # may be too (but * does not have to be). If the objectName is omitted, * the [] may be too (but does not have to be). It is - * not legal to omit all three items, that is to have a name + * not legal to omit all four items, that is to have a name * that is the empty string.

      * - *

      One or more of the className, member, + *

      One or more of the mbeanServerName, className, + * member, * or objectName may be the character "-", * which is equivalent to a null value. A null value is implied by * any value (including another null value) but does not imply any @@ -246,6 +267,13 @@ public class MBeanPermission extends Permission { */ private transient ObjectName objectName; + /** + * The name of the MBeanServer in which this permission is checked, or + * granted. If null, is implied by any MBean Server name + * but does not imply any non-null MBean Server name. + */ + private transient String mbeanServerName; + /** * Parse actions parameter. */ @@ -283,6 +311,13 @@ public class MBeanPermission extends Permission { throw new IllegalArgumentException("MBeanPermission name " + "cannot be empty"); + final int sepIndex = name.indexOf("::"); + if (sepIndex < 0) { + setMBeanServerName("*"); + } else { + setMBeanServerName(name.substring(0,sepIndex)); + } + /* The name looks like "class#member[objectname]". We subtract elements from the right as we parse, so after parsing the objectname we have "class#member" and after parsing the @@ -290,11 +325,14 @@ public class MBeanPermission extends Permission { // Parse ObjectName - int openingBracket = name.indexOf("["); + + final int start = (sepIndex<0)?0:sepIndex+2; + int openingBracket = name.indexOf("[",start); if (openingBracket == -1) { // If "[on]" missing then ObjectName("*:*") // objectName = ObjectName.WILDCARD; + name = name.substring(start); } else { if (!name.endsWith("]")) { throw new IllegalArgumentException("MBeanPermission: " + @@ -305,11 +343,11 @@ public class MBeanPermission extends Permission { } else { // Create ObjectName // + String on = name.substring(openingBracket + 1, + name.length() - 1); try { // If "[]" then ObjectName("*:*") // - String on = name.substring(openingBracket + 1, - name.length() - 1); if (on.equals("")) objectName = ObjectName.WILDCARD; else if (on.equals("-")) @@ -320,11 +358,11 @@ public class MBeanPermission extends Permission { throw new IllegalArgumentException("MBeanPermission: " + "The target name does " + "not specify a valid " + - "ObjectName"); + "ObjectName", e); } } - name = name.substring(0, openingBracket); + name = name.substring(start, openingBracket); } // Parse member @@ -348,8 +386,9 @@ public class MBeanPermission extends Permission { * Assign fields based on className, member, and objectName * parameters. */ - private void initName(String className, String member, - ObjectName objectName) { + private void initName(String mbeanServerName, String className, + String member, ObjectName objectName) { + setMBeanServerName(mbeanServerName); setClassName(className); setMember(member); this.objectName = objectName; @@ -381,19 +420,30 @@ public class MBeanPermission extends Permission { this.member = member; } + private void setMBeanServerName(String mbeanServerName) { + if (mbeanServerName == null || mbeanServerName.equals("-")) { + this.mbeanServerName = null; + } else if (mbeanServerName.equals("")) { + this.mbeanServerName = "*"; + } else { + this.mbeanServerName = mbeanServerName; + } + } + + /** *

      Create a new MBeanPermission object with the specified target name * and actions.

      * *

      The target name is of the form - * "className#member[objectName]" where each part is - * optional. It must not be empty or null.

      + * "mbeanServerName::className#member[objectName]" where + * each part is optional. It must not be empty or null.

      * *

      The actions parameter contains a comma-separated list of the * desired actions granted on the target name. It must not be * empty or null.

      * - * @param name the triplet "className#member[objectName]". + * @param name the quadruplet "mbeanServerName::className#member[objectName]". * @param actions the action string. * * @exception IllegalArgumentException if the name or @@ -418,6 +468,12 @@ public class MBeanPermission extends Permission { * optional. This will be the result of {@link #getName()} on the * resultant MBeanPermission.

      * + *

      This corresponds to a permission granted for all + * MBean servers present in the JVM and is equivalent to + * {@link #MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission("*",className,member,objectName,actions)}. + *

      + * *

      The actions parameter contains a comma-separated list of the * desired actions granted on the target name. It must not be * empty or null.

      @@ -439,17 +495,67 @@ public class MBeanPermission extends Permission { String member, ObjectName objectName, String actions) { + this("*",className,member,objectName,actions); + } + + /** + *

      Create a new MBeanPermission object with the specified target name + * (MBean Server name, class name, member, object name) and actions.

      + * + *

      The MBean Server name, class name, member and object name + * parameters define a target name of the form + * "mbeanServerName::className#member[objectName]" where each + * part is optional. This will be the result of {@link #getName()} on the + * resultant MBeanPermission. + * If the mbeanServerName is empty or exactly {@code "*"}, then + * "{@code mbeanServerName::}" is omitted in that result. + *

      + * + *

      The actions parameter contains a comma-separated list of the + * desired actions granted on the target name. It must not be + * empty or null.

      + * + * @param mbeanServerName the name of the {@code MBeanServer} to which this + * permission applies. + * May be null or "-", which represents an MBeanServer name + * that is implied by any MBeanServer name but does not imply any other + * MBeanServer name. + * @param className the class name to which this permission applies. + * May be null or "-", which represents a class name + * that is implied by any class name but does not imply any other + * class name. + * @param member the member to which this permission applies. May + * be null or "-", which represents a member that is + * implied by any member but does not imply any other member. + * @param objectName the object name to which this permission + * applies. May be null, which represents an object name that is + * implied by any object name but does not imply any other object + * name. + * @param actions the action string. + * + * @since 1.7 + */ + public MBeanPermission(String mbeanServerName, + String className, + String member, + ObjectName objectName, + String actions) { - super(makeName(className, member, objectName)); - initName(className, member, objectName); + super(makeName(mbeanServerName,className, member, objectName)); + initName(mbeanServerName,className, member, objectName); this.actions = actions; parseActions(); } - private static String makeName(String className, String member, + private static String makeName(String mbeanServerName, String className, + String member, ObjectName objectName) { final StringBuilder name = new StringBuilder(); + if (mbeanServerName == null) + mbeanServerName = "-"; + if (!mbeanServerName.equals("") && !mbeanServerName.equals("*")) + name.append(mbeanServerName).append("::"); if (className == null) className = "-"; name.append(className); @@ -991,6 +1097,9 @@ public class MBeanPermission extends Permission { * *
    • p is an instance of MBeanPermission; and
    • * + *
    • p has a null mbeanServerName or p's mbeanServerName + * matches this object's mbeanServerName; and
    • + * *
    • p has a null className or p's className * matches this object's className; and
    • * @@ -1004,6 +1113,13 @@ public class MBeanPermission extends Permission { * * * + *

      If this object's mbeanServerName is a pattern, then p's + * mbeanServerName is matched against that pattern. An empty + * mbeanServerName is equivalent to "{@code *}". A null + * mbeanServerName is equivalent to "{@code -}".

      + *

      If this object's mbeanServerName is "*" or is + * empty, p's mbeanServerName always matches it.

      + * *

      If this object's className is "*", p's * className always matches it. If it is "a.*", p's * className matches it if it begins with "a.".

      @@ -1050,6 +1166,12 @@ public class MBeanPermission extends Permission { // Target name // + // The 'mbeanServerName' check is true iff: + // 1) the mbeanServerName in 'this' permission is omitted or "*", or + // 2) the mbeanServerName in 'that' permission is omitted or "*", or + // 3) the mbeanServerName in 'this' permission does pattern + // matching with the mbeanServerName in 'that' permission. + // // The 'className' check is true iff: // 1) the className in 'this' permission is omitted or "*", or // 2) the className in 'that' permission is omitted or "*", or @@ -1076,6 +1198,17 @@ public class MBeanPermission extends Permission { expect that "that" contains a wildcard, since it is a needed permission. So we assume that.classNameExactMatch. */ + if (that.mbeanServerName == null) { + // bottom is implied + } else if (this.mbeanServerName == null) { + // bottom implies nothing but itself + return false; + } else if (that.mbeanServerName.equals(this.mbeanServerName)) { + // exact match + } else if (!Util.wildmatch(that.mbeanServerName,this.mbeanServerName)) { + return false; // no match + } + if (that.classNamePrefix == null) { // bottom is implied } else if (this.classNamePrefix == null) { diff --git a/src/share/classes/javax/management/MBeanServer.java b/src/share/classes/javax/management/MBeanServer.java index 613cf09cf..90d42d2df 100644 --- a/src/share/classes/javax/management/MBeanServer.java +++ b/src/share/classes/javax/management/MBeanServer.java @@ -42,7 +42,7 @@ import javax.management.loading.ClassLoaderRepository; * *

      User code does not usually implement this interface. Instead, * an object that implements this interface is obtained with one of - * the methods in the {@link MBeanServerFactory} class.

      + * the methods in the {@link javax.management.MBeanServerFactory} class.

      * *

      Every MBean which is added to the MBean server becomes * manageable: its attributes and operations become remotely @@ -62,8 +62,12 @@ import javax.management.loading.ClassLoaderRepository; * JMImplementation:type=MBeanServerDelegate.

      * *

      An object obtained from the {@link - * MBeanServerFactory#createMBeanServer(String) createMBeanServer} or - * {@link MBeanServerFactory#newMBeanServer(String) newMBeanServer} + * MBeanServerFactory#createMBeanServer(String) createMBeanServer}, {@link + * MBeanServerFactory#createNamedMBeanServer(String,String) createNamedMBeanServer}, + * {@link + * MBeanServerFactory#newMBeanServer(String) newMBeanServer}, or + * {@link + * MBeanServerFactory#newNamedMBeanServer(String,String) newNamedMBeanServer} * methods of the {@link MBeanServerFactory} class applies security * checks to its methods, as follows.

      * @@ -73,9 +77,26 @@ import javax.management.loading.ClassLoaderRepository; * *

      Assuming that there is a security manager, or that the * implementation chooses to make checks anyway, the checks are made - * as detailed below. In what follows, className is the + * as detailed below. + * In what follows, and unless otherwise specified: + *

      + *
      • className is the * string returned by {@link MBeanInfo#getClassName()} for the target - * MBean.

        + * MBean,
      • + *
      • {@code mbeanServerName} is the + * {@linkplain MBeanServerFactory#getMBeanServerName name of the + * MBean Server} in which the target MBean is registered. This is the + * value returned by {@link MBeanServerFactory#getMBeanServerName + * MBeanServerFactory.getMBeanServerName(MBeanServer)}, and + * is usually the {@code mbeanServerName} parameter that was supplied + * to the {@link + * MBeanServerFactory#createNamedMBeanServer(String,String) + * createNamedMBeanServer} or {@link + * MBeanServerFactory#newNamedMBeanServer(String,String) newNamedMBeanServer} + * methods of the {@link MBeanServerFactory} when the MBeanServer was created, + * or {@value javax.management.MBeanServerFactory#DEFAULT_MBEANSERVER_NAME} if + * no name was supplied. + *
      * *

      If a security check fails, the method throws {@link * SecurityException}.

      @@ -89,78 +110,86 @@ import javax.management.loading.ClassLoaderRepository; * *
    • For the {@link #invoke invoke} method, the caller's * permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, operationName, name, "invoke")}.

      + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, operationName, name, "invoke")}. + *

      * *
    • For the {@link #getAttribute getAttribute} method, the * caller's permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, attribute, name, "getAttribute")}.

      + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, attribute, name, + * "getAttribute")}.

      * *
    • For the {@link #getAttributes getAttributes} method, the * caller's permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, null, name, "getAttribute")}. + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName,className, null, name, "getAttribute")}. * Additionally, for each attribute a in the {@link * AttributeList}, if the caller's permissions do not imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, a, name, "getAttribute")}, the + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, a, name, + * "getAttribute")}, the * MBean server will behave as if that attribute had not been in the * supplied list.

      * *
    • For the {@link #setAttribute setAttribute} method, the * caller's permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, attrName, name, "setAttribute")}, where + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, attrName, name, + * "setAttribute")}, where * attrName is {@link Attribute#getName() * attribute.getName()}.

      * *
    • For the {@link #setAttributes setAttributes} method, the * caller's permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, null, name, "setAttribute")}. + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, null, name, "setAttribute")}. * Additionally, for each attribute a in the {@link * AttributeList}, if the caller's permissions do not imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, a, name, "setAttribute")}, the + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, a, name, + * "setAttribute")}, the * MBean server will behave as if that attribute had not been in the * supplied list.

      * *
    • For the addNotificationListener methods, * the caller's permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, null, name, + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, null, name, * "addNotificationListener")}.

      * *
    • For the removeNotificationListener methods, * the caller's permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, null, name, + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, null, name, * "removeNotificationListener")}.

      * *
    • For the {@link #getMBeanInfo getMBeanInfo} method, the * caller's permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, null, name, "getMBeanInfo")}.

      + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, null, name, "getMBeanInfo")}. + *

      * *
    • For the {@link #getObjectInstance getObjectInstance} method, * the caller's permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, null, name, "getObjectInstance")}.

      + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, null, name, + * "getObjectInstance")}.

      * *
    • For the {@link #isInstanceOf isInstanceOf} method, the * caller's permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, null, name, "isInstanceOf")}.

      + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, null, name, "isInstanceOf")}. + *

      * *
    • For the {@link #queryMBeans queryMBeans} method, the * caller's permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(null, null, name, "queryMBeans")}. + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, null, null, null, "queryMBeans")}. * Additionally, for each MBean that matches name, * if the caller's permissions do not imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, null, name, "queryMBeans")}, the + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, null, name, "queryMBeans")}, the * MBean server will behave as if that MBean did not exist.

      * *

      Certain query elements perform operations on the MBean server. @@ -179,10 +208,10 @@ import javax.management.loading.ClassLoaderRepository; * *

    • For the {@link #getDomains getDomains} method, the caller's * permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(null, null, name, "getDomains")}. Additionally, - * for each domain d in the returned array, if the caller's - * permissions do not imply {@link + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, null, null, null, "getDomains")}. + * Additionally, for each domain d in the returned array, if the + * caller's permissions do not imply {@link * MBeanPermission#MBeanPermission(String,String,ObjectName,String) * MBeanPermission(null, null, new ObjectName("d:x=x"), * "getDomains")}, the domain is eliminated from the array. Here, @@ -191,21 +220,22 @@ import javax.management.loading.ClassLoaderRepository; * *

    • For the {@link #getClassLoader getClassLoader} method, the * caller's permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, null, loaderName, + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, null, loaderName, * "getClassLoader")}.

      * *
    • For the {@link #getClassLoaderFor getClassLoaderFor} method, * the caller's permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, null, mbeanName, + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, null, mbeanName, * "getClassLoaderFor")}.

      * *
    • For the {@link #getClassLoaderRepository * getClassLoaderRepository} method, the caller's permissions must * imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(null, null, null, "getClassLoaderRepository")}.

      + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, null, null, null, + * "getClassLoaderRepository")}.

      * *
    • For the deprecated deserialize methods, the * required permissions are the same as for the methods that replace @@ -213,15 +243,15 @@ import javax.management.loading.ClassLoaderRepository; * *

    • For the instantiate methods, the caller's * permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, null, null, "instantiate")}.

      + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, null, null, "instantiate")}, + * where {@code className} is the name of the class which is to + * be instantiated.

      * *
    • For the {@link #registerMBean registerMBean} method, the * caller's permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, null, name, "registerMBean")}. Here - * className is the string returned by {@link - * MBeanInfo#getClassName()} for an object of this class. + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, null, name, "registerMBean")}. * *

      If the MBeanPermission check succeeds, the MBean's * class is validated by checking that its {@link @@ -241,8 +271,9 @@ import javax.management.loading.ClassLoaderRepository; * *

    • For the {@link #unregisterMBean unregisterMBean} method, * the caller's permissions must imply {@link - * MBeanPermission#MBeanPermission(String,String,ObjectName,String) - * MBeanPermission(className, null, name, "unregisterMBean")}.

      + * MBeanPermission#MBeanPermission(String,String,String,ObjectName,String) + * MBeanPermission(mbeanServerName, className, null, name, "unregisterMBean")}. + *

      * * * diff --git a/src/share/classes/javax/management/MBeanServerDelegate.java b/src/share/classes/javax/management/MBeanServerDelegate.java index 4ee976630..ea4799d3f 100644 --- a/src/share/classes/javax/management/MBeanServerDelegate.java +++ b/src/share/classes/javax/management/MBeanServerDelegate.java @@ -25,7 +25,9 @@ package javax.management; +import com.sun.jmx.defaults.JmxProperties; import com.sun.jmx.defaults.ServiceName; +import com.sun.jmx.mbeanserver.Util; /** * Represents the MBean server from the management point of view. @@ -39,6 +41,7 @@ public class MBeanServerDelegate implements MBeanServerDelegateMBean, /** The MBean server agent identification.*/ private String mbeanServerId ; + private String mbeanServerName; /** The NotificationBroadcasterSupport object that sends the notifications */ @@ -68,6 +71,7 @@ public class MBeanServerDelegate implements MBeanServerDelegateMBean, public MBeanServerDelegate () { stamp = getStamp(); broadcaster = new NotificationBroadcasterSupport() ; + mbeanServerName=null; } @@ -82,13 +86,102 @@ public class MBeanServerDelegate implements MBeanServerDelegateMBean, try { localHost = java.net.InetAddress.getLocalHost().getHostName(); } catch (java.net.UnknownHostException e) { + JmxProperties.MISC_LOGGER.finest("Can't get local host name, " + + "using \"localhost\" instead. Cause is: "+e); localHost = "localhost"; } - mbeanServerId = localHost + "_" + stamp; + mbeanServerId = + Util.insertMBeanServerName(localHost + "_" + stamp, + mbeanServerName); } return mbeanServerId; } + /** + * The name of the MBeanServer. + * @return The name of the MBeanServer, or {@value + * javax.management.MBeanServerFactory#DEFAULT_MBEANSERVER_NAME} if no + * name was specified. + * + * @since 1.7 + * @see #setMBeanServerName + */ + public synchronized String getMBeanServerName() { + if (Util.isMBeanServerNameUndefined(mbeanServerName)) + return MBeanServerFactory.DEFAULT_MBEANSERVER_NAME; + return mbeanServerName; + } + + /** + * Sets the name of the MBeanServer. The name will be embedded into the + * {@link #getMBeanServerId MBeanServerId} using the following format:
      + * {@code mbeanServerId: ;mbeanServerName=} + *

      The characters {@code ':'} (colon), {@code ';'} (semicolon ), + * {@code '*'} (star) and {@code '?'} (question mark) are not legal in an + * MBean Server name.

      + *

      For instance, if the {@code mbeanServerName} provided is + * {@code "com.mycompany.myapp.server1"}, and the original + * {@code MBeanServerId} was {@code "myhost_1213353064145"}, + * then {@code mbeanServerName} will be + * embedded in the {@code MBeanServerId} - and the new value of the + * {@code MBeanServerId} will be: + *

      + *
      +     *       "myhost_1213353064145;mbeanServerName=com.mycompany.myapp.server1"
      +     * 
      + *

      Note: The {@code mbeanServerName} is usually set by the + * {@code MBeanServerFactory}. It is set only once, before the + * MBean Server is returned by the factory. Once the MBean Server name is + * set, it is not possible to change it. + *

      + * @param mbeanServerName The MBeanServer name. + * @throws IllegalArgumentException if the MBeanServerName is already set + * to a different value, or if the provided name contains + * illegal characters, or if the provided name is {@code ""} + * (the empty string) or "-" (dash). + * @throws UnsupportedOperationException if this object is of a legacy + * subclass of MBeanServerDelegate which overrides {@link + * #getMBeanServerId()} + * in a way that doesn't support setting an MBeanServer name. + * @see MBeanServerFactory#getMBeanServerName + * @since 1.7 + */ + public synchronized void setMBeanServerName(String mbeanServerName) { + // Sets the name on the delegate. For complex backward + // compatibility reasons it is not possible to give the + // name to the MBeanServerDelegate constructor. + // + // The method setMBeanServerName() will call getMBeanServerId() + // to check that the name is accurately set in the MBeanServerId. + // If not (which could happen if a custom MBeanServerDelegate + // implementation overrides getMBeanServerId() and was not updated + // with respect to JMX 2.0 spec), this method will throw an + // IllegalStateException... + + // will fail if mbeanServerName is illegal + final String name = Util.checkServerName(mbeanServerName); + + // can only set mbeanServerDelegate once. + if (this.mbeanServerName != null && !this.mbeanServerName.equals(name)) + throw new IllegalArgumentException( + "MBeanServerName already set to a different value"); + + this.mbeanServerName = name; + + // will fail if mbeanServerId already has a different mbeanServerName + mbeanServerId = + Util.insertMBeanServerName(getMBeanServerId(),name); + + // check that we don't have a subclass which overrides + // getMBeanServerId() without setting mbeanServerName + if (!name.equals( + Util.extractMBeanServerName(getMBeanServerId()))) + throw new UnsupportedOperationException( + "Can't set MBeanServerName in MBeanServerId - " + + "unsupported by "+this.getClass().getName()+"?"); + // OK: at this point we know that we have correctly set mbeanServerName. + } + /** * Returns the full name of the JMX specification implemented * by this product. @@ -210,15 +303,8 @@ public class MBeanServerDelegate implements MBeanServerDelegateMBean, * * @since 1.6 */ - public static final ObjectName DELEGATE_NAME; - static { - try { - DELEGATE_NAME = - new ObjectName("JMImplementation:type=MBeanServerDelegate"); - } catch (MalformedObjectNameException e) { - throw new Error("Can't initialize delegate name", e); - } - } + public static final ObjectName DELEGATE_NAME = + Util.newObjectName("JMImplementation:type=MBeanServerDelegate"); /* Return a timestamp that is monotonically increasing even if System.currentTimeMillis() isn't (for example, if you call this diff --git a/src/share/classes/javax/management/MBeanServerFactory.java b/src/share/classes/javax/management/MBeanServerFactory.java index 743ca3a18..7657af108 100644 --- a/src/share/classes/javax/management/MBeanServerFactory.java +++ b/src/share/classes/javax/management/MBeanServerFactory.java @@ -25,16 +25,19 @@ package javax.management; +import com.sun.jmx.defaults.JmxProperties; import static com.sun.jmx.defaults.JmxProperties.JMX_INITIAL_BUILDER; import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; -import com.sun.jmx.interceptor.DefaultMBeanServerInterceptor; import com.sun.jmx.mbeanserver.GetPropertyAction; +import com.sun.jmx.mbeanserver.Util; import java.security.AccessController; import java.security.Permission; import java.util.ArrayList; +import java.util.List; import java.util.logging.Level; import javax.management.loading.ClassLoaderRepository; + /** *

      Provides MBean server references. There are no instances of * this class.

      @@ -80,10 +83,53 @@ import javax.management.loading.ClassLoaderRepository; * returned by the default MBeanServerBuilder implementation, for the purpose * of e.g. adding an additional security layer.

      * + *

      Since version 2.0 of the JMX API, when creating + * an MBeanServer, + * it is possible to specify an {@linkplain #getMBeanServerName + * MBean Server name}. + * To create an MBean Server with a name, the MBeanServerFactory provides two + * new methods:

      + *
      • {@link #createNamedMBeanServer + * createNamedMBeanServer(mbeanServerName, defaultDomain)}: creates a named + * MBeanServer and keeps an internal reference to the created object. The + * MBeanServer can be later retrieved using {@link #findMBeanServer + * findMBeanServer(mbeanServerId)} or + * {@link #findMBeanServerByName findMBeanServerByName(mbeanServerName)}, and + * can be released through {@link + * #releaseMBeanServer releaseMBeanServer(mbeanServer)}.
      • + *
      • {@link #newNamedMBeanServer + * newNamedMBeanServer(mbeanServerName, defaultDomain)}: + * creates a named MBeanServer without keeping any internal reference to the + * named server.
      • + *
      + *

      The name of the MBeanServer is stored in the + * {@linkplain MBeanServerDelegate MBean Server delegate MBean} + * and is embedded in its {@link MBeanServerDelegate#getMBeanServerId + * MBeanServerId} attribute.

      + *

      The name of the MBeanServer is particularly useful when + * MBean permissions are checked: + * it makes it + * possible to distinguish between an MBean named "X" in MBeanServer named + * "M1", and another MBean of the same name "X" in another MBeanServer named + * "M2".

      + *

      When naming MBean servers it is recommended to use a name that starts + * with a Java package name. It is also recommended that the default domain and + * the MBeanServer name be the same.

      + * * @since 1.5 */ public class MBeanServerFactory { + /** + * The MBean Server name that will be + * checked by a permission you need + * when checking access to an MBean registered in an MBeanServer for + * which no MBeanServer name was specified. + * + * @since 1.7 + */ + public final static String DEFAULT_MBEANSERVER_NAME = "default"; + /* * There are no instances of this class so don't generate the * default public constructor. @@ -222,13 +268,78 @@ public class MBeanServerFactory { * javax.management.builder.initial exists and can be * instantiated but is not assignment compatible with {@link * MBeanServerBuilder}. + * + * @see #createNamedMBeanServer */ public static MBeanServer createMBeanServer(String domain) { - checkPermission("createMBeanServer"); + return createMBeanServer(null,domain); + } - final MBeanServer mBeanServer = newMBeanServer(domain); - addMBeanServer(mBeanServer); - return mBeanServer; + /** + *

      Return a new object implementing the {@link MBeanServer} + * interface with the specified + * MBean Server name + * and default domain name. The given MBean server name + * is used in security checks, and + * can also be used to {@linkplain #findMBeanServerByName(java.lang.String) + * find an MBeanServer by name}. The given + * domain name is used as the domain part in the ObjectName of + * MBeans when the domain is specified by the user is null.

      + * + *

      The MBeanServer reference is internally kept. This will + * allow findMBeanServer to return a reference to + * this MBeanServer object.

      + * + * @param mbeanServerName the name for the created + * MBeanServer. This is the name that will be included in the + * {@linkplain MBeanPermission permission you need} when checking + * MBean Permissions for accessing + * an MBean registered in the returned MBeanServer. The characters + * {@code ':'} (colon), {@code ';'} (semicolon), {@code '*'} (star) + * and {@code '?'} are not legal. + * It is recommended that the {@code mbeanServerName} + * be unique in the context of a JVM, and in the form of a java package + * identifier. If {@code mbeanServerName} is {@code null} then the created + * MBean Server has no name - and {@value #DEFAULT_MBEANSERVER_NAME} is used. + * Calling {@code createNamedMBeanServer(null,domain)} is equivalent + * to calling {@link #createMBeanServer(String) createMBeanServer(domain)}. + * + * @param domain the default domain name for the created + * MBeanServer. This is the value that will be returned by {@link + * MBeanServer#getDefaultDomain}. If a non null mbeanServerName is given, + * it is recommended to pass the same value as default domain. + * + * @return the newly created MBeanServer. + * + * @exception SecurityException if there is a SecurityManager and + * the caller's permissions do not include or imply {@link + * MBeanServerPermission}("createMBeanServer"). + * + * @exception JMRuntimeException if the property + * javax.management.builder.initial exists but the + * class it names cannot be instantiated through a public + * no-argument constructor; or if the instantiated builder returns + * null from its {@link MBeanServerBuilder#newMBeanServerDelegate + * newMBeanServerDelegate} or {@link + * MBeanServerBuilder#newMBeanServer newMBeanServer} methods. + * + * @exception ClassCastException if the property + * javax.management.builder.initial exists and can be + * instantiated but is not assignment compatible with {@link + * MBeanServerBuilder}. + * + * @exception IllegalArgumentException if the specified + * {@code mbeanServerName} is empty, or is {@code "-"}, or contains a + * character which is not legal. + * + * @exception UnsupportedOperationException if the specified + * {@code mbeanServerName} cannot be set. + * + * @since 1.7 + */ + public static MBeanServer createNamedMBeanServer(String mbeanServerName, + String domain) { + return createMBeanServer(mbeanServerName, domain); } /** @@ -307,6 +418,88 @@ public class MBeanServerFactory { * MBeanServerBuilder}. */ public static MBeanServer newMBeanServer(String domain) { + return newMBeanServer(null,domain); + } + + /** + *

      Return a new object implementing the MBeanServer interface + * with the specified MBean server name + * and default domain name, without keeping an + * internal reference to this new object. The given MBean server name + * is used in security checks. + * The given domain name + * is used as the domain part in the ObjectName of MBeans when the + * domain is specified by the user is null.

      + * + *

      No reference is kept. findMBeanServer and + * findMBeanServerByName will not + * be able to return a reference to this MBeanServer object, but + * the garbage collector will be able to remove the MBeanServer + * object when it is no longer referenced.

      + * + * @param mbeanServerName the name for the created + * MBeanServer. This is the name that will be included in the + * {@linkplain MBeanPermission permission you need} when checking + * MBean Permissions for accessing + * an MBean registered in the returned MBeanServer. The characters + * {@code ':'} (colon), {@code ';'} (semicolon), {@code '*'} (star) + * and {@code '?'} are not legal. + * It is recommended that the mbeanServerName + * be unique in the context of a JVM, and in the form of a java package + * identifier. If {@code mbeanServerName} is {@code null} then the created + * MBean Server has no name - and {@value #DEFAULT_MBEANSERVER_NAME} is used. + * Calling {@code newNamedMBeanServer(null,domain)} is equivalent + * to calling {@link #newMBeanServer(String) newMBeanServer(domain)}. + * + * @param domain the default domain name for the created + * MBeanServer. This is the value that will be returned by {@link + * MBeanServer#getDefaultDomain}. + * + * @return the newly created MBeanServer. + * + * @exception SecurityException if there is a SecurityManager and the + * caller's permissions do not include or imply {@link + * MBeanServerPermission}("newMBeanServer"). + * + * @exception JMRuntimeException if the property + * javax.management.builder.initial exists but the + * class it names cannot be instantiated through a public + * no-argument constructor; or if the instantiated builder returns + * null from its {@link MBeanServerBuilder#newMBeanServerDelegate + * newMBeanServerDelegate} or {@link + * MBeanServerBuilder#newMBeanServer newMBeanServer} methods. + * + * @exception ClassCastException if the property + * javax.management.builder.initial exists and can be + * instantiated but is not assignment compatible with {@link + * MBeanServerBuilder}. + * + * @exception IllegalArgumentException if the specified + * {@code mbeanServerName} is empty, or is {@code "-"}, + * or contains a character which is not legal. + * + * @exception UnsupportedOperationException if the specified + * {@code mbeanServerName} cannot be set. + * + * @since 1.7 + */ + public static MBeanServer newNamedMBeanServer(String mbeanServerName, + String domain) { + return newMBeanServer(mbeanServerName, domain); + } + + private static MBeanServer createMBeanServer(String mbeanServerName, + String domain) { + checkPermission("createMBeanServer"); + + final MBeanServer mBeanServer = + newMBeanServer(mbeanServerName,domain); + addMBeanServer(mBeanServer); + return mBeanServer; + } + + private static MBeanServer newMBeanServer(String mbeanServerName, + String domain) { checkPermission("newMBeanServer"); // Get the builder. Creates a new one if necessary. @@ -316,20 +509,50 @@ public class MBeanServerFactory { synchronized(mbsBuilder) { final MBeanServerDelegate delegate = - mbsBuilder.newMBeanServerDelegate(); + mbsBuilder.newMBeanServerDelegate(); if (delegate == null) { final String msg = - "MBeanServerBuilder.newMBeanServerDelegate() " + - "returned null"; + "MBeanServerBuilder.newMBeanServerDelegate() " + + "returned null"; throw new JMRuntimeException(msg); } + + // Sets the name on the delegate. For complex backward + // compatibility reasons it is not possible to give the + // name to the MBeanServerDelegate constructor. + // + // The method setMBeanServerName() will call getMBeanServerId() + // to check that the name is accurately set in the MBeanServerId. + // If not (which could happen if a custom MBeanServerDelegate + // implementation overrides getMBeanServerId() and was not updated + // with respect to JMX 2.0 spec, this method will throw an + // IllegalStateException... + // + if (!Util.isMBeanServerNameUndefined(mbeanServerName)) { + delegate.setMBeanServerName(mbeanServerName); + } + final MBeanServer mbeanServer = - mbsBuilder.newMBeanServer(domain,null,delegate); + mbsBuilder.newMBeanServer(domain,null,delegate); if (mbeanServer == null) { final String msg = - "MBeanServerBuilder.newMBeanServer() returned null"; + "MBeanServerBuilder.newMBeanServer() returned null"; throw new JMRuntimeException(msg); } + + // double check that the MBeanServer name is correctly set. + // "*" might mean that the caller doesn't have the permission + // to see the MBeanServer name. + // + final String mbsName = Util.getMBeanServerSecurityName(mbeanServer); + if (!mbsName.equals(Util.checkServerName(mbeanServerName)) + && !mbsName.equals("*")) { + throw new UnsupportedOperationException( + "can't create MBeanServer with name \""+ + mbeanServerName+"\" using "+ + builder.getClass().getName()); + } + return mbeanServer; } } @@ -363,16 +586,107 @@ public class MBeanServerFactory { ArrayList result = new ArrayList(); for (MBeanServer mbs : mBeanServerList) { - String name = mBeanServerName(mbs); + String name = mBeanServerId(mbs); if (agentId.equals(name)) result.add(mbs); } return result; } + /** + *

      Returns a list of registered MBeanServer objects with the given name. A + * registered MBeanServer object is one that was created by one of + * the createMBeanServer or createNamedMBeanServer + * methods and not subsequently released with releaseMBeanServer.

      + *

      See the section about MBean Server names + * above.

      + * + * @param mbeanServerName The name of the MBeanServer to + * retrieve. If this parameter is null, all registered MBeanServers + * in this JVM are returned. + * Otherwise, only those MBeanServers that have a name + * matching mbeanServerName are returned: this + * parameter can be a pattern, where {@code '*'} matches any + * sequence of characters and {@code '?'} matches any character.
      + * The name of an MBeanServer, if specified, is embedded in the + * MBeanServerId attribute of its delegate MBean: + * this method will parse the MBeanServerId to get the + * MBeanServer name. If this parameter is equal to {@code "*"} then + * all registered MBeanServers in this JVM are returned, whether they have + * a name or not: {@code findMBeanServerByName(null)}, + * {@code findMBeanServerByName("*")} and {@code findMBeanServer(null)}, + * are equivalent. It is also possible to get all MBeanServers for which + * no name was specified by calling findMBeanServerByName({@value + * #DEFAULT_MBEANSERVER_NAME}). + * + * @return A list of MBeanServer objects. + * + * @exception SecurityException if there is a SecurityManager and the + * caller's permissions do not include or imply {@link + * MBeanServerPermission}("findMBeanServer"). + * + * @see #getMBeanServerName(MBeanServer) + * @since 1.7 + */ + public synchronized static + List findMBeanServerByName(String mbeanServerName) { + + checkPermission("findMBeanServer"); + + if (mbeanServerName==null || "*".equals(mbeanServerName)) + return new ArrayList(mBeanServerList); + + // noname=true iff we are looking for MBeanServers for which no name + // were specified. + ArrayList result = new ArrayList(); + for (MBeanServer mbs : mBeanServerList) { + final String name = Util.getMBeanServerSecurityName(mbs); + if (Util.wildmatch(name, mbeanServerName)) result.add(mbs); + } + return result; + } + + /** + * Returns the name of the MBeanServer embedded in the MBeanServerId of + * the given {@code server}. If the given MBeanServerId doesn't contain + * any name, {@value #DEFAULT_MBEANSERVER_NAME} is returned. + * The MBeanServerId is expected to be of the form: + * {@code *[;mbeanServerName=[;*]]} + *
      where {@code *} denotes any sequence of characters, and {@code [ ]} + * indicate optional parts. + *

      + *

      For instance, if an MBeanServer is created using {@link + * #createNamedMBeanServer(java.lang.String, java.lang.String) + * server = + * MBeanServerFactory.createNamedMBeanServer("com.mycompany.myapp.server1", + * null)} then {@code MBeanServerFactory.getMBeanServerName(server)} + * will return {@code "com.mycompany.myapp.server1"} and + * server.getAttribute({@link + * javax.management.MBeanServerDelegate#DELEGATE_NAME + * MBeanServerDelegate.DELEGATE_NAME}, "MBeanServerId") will return + * something like + * {@code "myhost_1213353064145;mbeanServerName=com.mycompany.myapp.server1"}. + *

      + *

      See the section about MBean Server names + * above.

      + * @param server A named (or unnamed) MBeanServer. + * @return the name of the MBeanServer if found, or + * {@value #DEFAULT_MBEANSERVER_NAME} if no name is + * present in its MBeanServerId, or "*" if its + * MBeanServerId couldn't be obtained. Returning "*" means that + * only {@link MBeanPermission}s that allow all MBean Server names + * will apply to this MBean Server. + * @see MBeanServerDelegate + * @since 1.7 + */ + public static String getMBeanServerName(MBeanServer server) { + return Util.getMBeanServerSecurityName(server); + } + /** * Return the ClassLoaderRepository used by the given MBeanServer. - * This method is equivalent to {@link MBeanServer#getClassLoaderRepository() server.getClassLoaderRepository()}. + * This method is equivalent to {@link + * MBeanServer#getClassLoaderRepository() server.getClassLoaderRepository()}. * @param server The MBeanServer under examination. Since JMX 1.2, * if server is null, the result is a * {@link NullPointerException}. This behavior differs from what @@ -387,21 +701,23 @@ public class MBeanServerFactory { * **/ public static ClassLoaderRepository getClassLoaderRepository( - MBeanServer server) { + MBeanServer server) { return server.getClassLoaderRepository(); } - private static String mBeanServerName(MBeanServer mbs) { + private static String mBeanServerId(MBeanServer mbs) { try { return (String) mbs.getAttribute(MBeanServerDelegate.DELEGATE_NAME, - "MBeanServerId"); + "MBeanServerId"); } catch (JMException e) { + JmxProperties.MISC_LOGGER.finest( + "Ignoring exception while getting MBeanServerId: "+e); return null; } } private static void checkPermission(String action) - throws SecurityException { + throws SecurityException { SecurityManager sm = System.getSecurityManager(); if (sm != null) { Permission perm = new MBeanServerPermission(action); @@ -425,16 +741,16 @@ public class MBeanServerFactory { } private static final ArrayList mBeanServerList = - new ArrayList(); + new ArrayList(); /** * Load the builder class through the context class loader. * @param builderClassName The name of the builder class. **/ private static Class loadBuilderClass(String builderClassName) - throws ClassNotFoundException { + throws ClassNotFoundException { final ClassLoader loader = - Thread.currentThread().getContextClassLoader(); + Thread.currentThread().getContextClassLoader(); if (loader != null) { // Try with context class loader @@ -453,14 +769,14 @@ public class MBeanServerFactory { **/ private static MBeanServerBuilder newBuilder(Class builderClass) { try { - final Object builder = builderClass.newInstance(); - return (MBeanServerBuilder)builder; + final Object abuilder = builderClass.newInstance(); + return (MBeanServerBuilder)abuilder; } catch (RuntimeException x) { throw x; } catch (Exception x) { final String msg = - "Failed to instantiate a MBeanServerBuilder from " + - builderClass + ": " + x; + "Failed to instantiate a MBeanServerBuilder from " + + builderClass + ": " + x; throw new JMRuntimeException(msg, x); } } @@ -472,7 +788,7 @@ public class MBeanServerFactory { private static synchronized void checkMBeanServerBuilder() { try { GetPropertyAction act = - new GetPropertyAction(JMX_INITIAL_BUILDER); + new GetPropertyAction(JMX_INITIAL_BUILDER); String builderClassName = AccessController.doPrivileged(act); try { @@ -493,8 +809,8 @@ public class MBeanServerFactory { builder = newBuilder(newBuilderClass); } catch (ClassNotFoundException x) { final String msg = - "Failed to load MBeanServerBuilder class " + - builderClassName + ": " + x; + "Failed to load MBeanServerBuilder class " + + builderClassName + ": " + x; throw new JMRuntimeException(msg, x); } } catch (RuntimeException x) { diff --git a/src/share/classes/javax/management/ObjectName.java b/src/share/classes/javax/management/ObjectName.java index d06cb35ee..20bcfce72 100644 --- a/src/share/classes/javax/management/ObjectName.java +++ b/src/share/classes/javax/management/ObjectName.java @@ -27,6 +27,8 @@ package javax.management; import com.sun.jmx.mbeanserver.GetPropertyAction; import com.sun.jmx.mbeanserver.Util; +import com.sun.jmx.namespace.serial.JMXNamespaceContext; + import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; @@ -222,6 +224,17 @@ import java.util.Map; @SuppressWarnings("serial") // don't complain serialVersionUID not constant public class ObjectName implements Comparable, QueryExp { + /** + * The sequence of characters used to separate name spaces in a name space + * path. + * + * @see javax.management.namespace + * @since 1.7 + **/ + public static final String NAMESPACE_SEPARATOR = "//"; + private static final int NAMESPACE_SEPARATOR_LENGTH = + NAMESPACE_SEPARATOR.length(); + /** * A structure recording property structure and * proposing minimal services @@ -251,16 +264,17 @@ public class ObjectName implements Comparable, QueryExp { /** * Returns a key string for receiver key */ - String getKeyString(String name) { - return name.substring(_key_index, _key_index + _key_length); + String getKeyString(String name, int offset) { + final int start = _key_index+offset; + return name.substring(start, start + _key_length); } /** * Returns a value string for receiver key */ - String getValueString(String name) { - int in_begin = _key_index + _key_length + 1; - int out_end = in_begin + _value_length; + String getValueString(String name, int offset) { + final int in_begin = _key_index + offset + _key_length + 1; + final int out_end = in_begin + _value_length; return name.substring(in_begin, out_end); } } @@ -393,6 +407,45 @@ public class ObjectName implements Comparable, QueryExp { */ private transient boolean _property_value_pattern = false; + private ObjectName(String newDomain, ObjectName aname) + throws MalformedObjectNameException{ + copyToOtherDomain(newDomain,aname); + } + + private void copyToOtherDomain(String domain, ObjectName aname) + throws MalformedObjectNameException, NullPointerException { + + // The domain cannot be null + if (domain == null) + throw new NullPointerException("domain cannot be null"); + + // The key property list cannot be null + if (aname == null) + throw new MalformedObjectNameException( + "key property list cannot be empty"); + + // checks domain validity. A side effect of this method is also to + // set the _domain_pattern flag. + if (!isDomain(domain)) + throw new MalformedObjectNameException("Invalid domain: " + domain); + + // init canonicalname + _domain_length = domain.length(); + + _canonicalName = (domain + + aname._canonicalName.substring(aname._domain_length)).intern(); + _kp_array = aname._kp_array; + _ca_array = aname._ca_array; + _propertyList = aname._propertyList; + _property_list_pattern = aname._property_list_pattern; + _property_value_pattern = aname._property_value_pattern; + // TODO remove this hack + // if (toString().endsWith("//javax.management.service:type1=event_client_delegeate_mbean,type2=default")) { + // Thread.currentThread().dumpStack(); + // throw new Error("************************ Gotcha!"); + //} + } + // Instance private fields <======================================= // Private fields <======================================== @@ -435,10 +488,10 @@ public class ObjectName implements Comparable, QueryExp { } // initialize parsing of the string - char[] name_chars = name.toCharArray(); - int len = name_chars.length; - char[] canonical_chars = new char[len]; // canonical form will be same - // length at most + final char[] name_chars = name.toCharArray(); + final int len = name_chars.length; + final char[] canonical_chars = new char[len]; // canonical form will + // be same length at most int cname_index = 0; int index = 0; char c, c1; @@ -637,10 +690,12 @@ public class ObjectName implements Comparable, QueryExp { // we got the key and value part, prepare a property for this if (!value_pattern) { - prop = new Property(key_index, key_length, value_length); + prop = new Property(key_index-_domain_length, + key_length, value_length); } else { _property_value_pattern = true; - prop = new PatternProperty(key_index, key_length, value_length); + prop = new PatternProperty(key_index-_domain_length, + key_length, value_length); } key_name = name.substring(key_index, key_index + key_length); @@ -725,12 +780,12 @@ public class ObjectName implements Comparable, QueryExp { boolean value_pattern = checkValue(value); sb.append(value); if (!value_pattern) { - prop = new Property(key_index, + prop = new Property(key_index-_domain_length, key.length(), value.length()); } else { _property_value_pattern = true; - prop = new PatternProperty(key_index, + prop = new PatternProperty(key_index-_domain_length, key.length(), value.length()); } @@ -810,9 +865,9 @@ public class ObjectName implements Comparable, QueryExp { prop = _ca_array[i]; // length of prop including '=' char prop_len = prop._key_length + prop._value_length + 1; - System.arraycopy(specified_chars, prop._key_index, + System.arraycopy(specified_chars, prop._key_index+_domain_length, canonical_chars, prop_index, prop_len); - prop.setKeyIndex(prop_index); + prop.setKeyIndex(prop_index-_domain_length); prop_index += prop_len; if (i != last_index) { canonical_chars[prop_index] = ','; @@ -1031,33 +1086,6 @@ public class ObjectName implements Comparable, QueryExp { k[endKey] + "'"); } - /* - * Tests whether string s is matched by pattern p. - * Supports "?", "*" each of which may be escaped with "\"; - * Not yet supported: internationalization; "\" inside brackets.

      - * Wildcard matching routine by Karl Heuer. Public Domain.

      - */ - private static boolean wildmatch(char[] s, char[] p, int si, int pi) { - char c; - final int slen = s.length; - final int plen = p.length; - - while (pi < plen) { // While still string - c = p[pi++]; - if (c == '?') { - if (++si > slen) return false; - } else if (c == '*') { // Wildcard - if (pi >= plen) return true; - do { - if (wildmatch(s,p,si,pi)) return true; - } while (++si < slen); - return false; - } else { - if (si >= slen || c != s[si++]) return false; - } - } - return (si == slen); - } // Category : Internal utilities <============================== @@ -1177,15 +1205,43 @@ public class ObjectName implements Comparable, QueryExp { cn = (String)in.readObject(); } + final JMXNamespaceContext ctxt = + JMXNamespaceContext.getDeserializationContext(); try { - construct(cn); + construct(changeContext(ctxt,cn)); } catch (NullPointerException e) { throw new InvalidObjectException(e.toString()); + } catch (IllegalArgumentException e) { + throw new InvalidObjectException(e.toString()); } catch (MalformedObjectNameException e) { throw new InvalidObjectException(e.toString()); } } + private String changeContext(JMXNamespaceContext context, String nameString) { + final String old = context.prefixToRemove; + final String nw = context.prefixToAdd; + final int ol = old.length(); + if (nameString.startsWith(NAMESPACE_SEPARATOR)) return nameString; + if (ol>0) { + if (!nameString.startsWith(old) || + !nameString.startsWith(NAMESPACE_SEPARATOR,ol)) + throw new IllegalArgumentException( + "Serialized ObjectName does not start with " + old + + ": " + nameString); + nameString = nameString.substring(ol+NAMESPACE_SEPARATOR_LENGTH); + } + if (!nw.equals("")) { + nameString = nw + NAMESPACE_SEPARATOR + nameString; + } + // TODO remove this hack + // if (nameString.endsWith("//javax.management.service:type1=event_client_delegeate_mbean,type2=default")) { + // System.err.println("old="+old+", nw="+nw); + // Thread.currentThread().dumpStack(); + // throw new Error("************************ Gotcha!"); + // } + return nameString; + } /** * Serializes an {@link ObjectName} to an {@link ObjectOutputStream}. @@ -1248,15 +1304,22 @@ public class ObjectName implements Comparable, QueryExp { private void writeObject(ObjectOutputStream out) throws IOException { + final JMXNamespaceContext ctxt = + JMXNamespaceContext.getSerializationContext(); + if (compat) { // Serializes this instance in the old serial form // Read CR 6441274 before making any changes to this code ObjectOutputStream.PutField fields = out.putFields(); - fields.put("domain", _canonicalName.substring(0, _domain_length)); + final String domain = + changeContext(ctxt,_canonicalName.substring(0, _domain_length)); + final String cn = + changeContext(ctxt,_canonicalName); + fields.put("domain", domain); fields.put("propertyList", getKeyPropertyList()); fields.put("propertyListString", getKeyPropertyListString()); - fields.put("canonicalName", _canonicalName); + fields.put("canonicalName", cn); fields.put("pattern", (_domain_pattern || _property_list_pattern)); fields.put("propertyPattern", _property_list_pattern); out.writeFields(); @@ -1266,7 +1329,8 @@ public class ObjectName implements Comparable, QueryExp { // Serializes this instance in the new serial form // out.defaultWriteObject(); - out.writeObject(getSerializedNameString()); + + out.writeObject(changeContext(ctxt,getSerializedNameString())); } } @@ -1396,6 +1460,27 @@ public class ObjectName implements Comparable, QueryExp { return Util.newObjectName(name.getSerializedNameString()); } + /** + * Returns an {@code ObjectName} that is the same as this one but + * with the specified domain. + * This method preserves the original key order in the new instance. + * If the provided name has a key property pattern, it will also be + * preserved in the returned instance. + * + * @param newDomain The new domain for the returned instance; + * must not be null. + * @return A new {@code ObjectName} that is the same as {@code this} + * except the domain is {@code newDomain}. + * @throws NullPointerException if {@code newDomain} is null. + * @throws MalformedObjectNameException if the new domain is syntactically + * illegal. + * @since 1.7 + **/ + public final ObjectName withDomain(String newDomain) + throws NullPointerException, MalformedObjectNameException { + return new ObjectName(newDomain, this); + } + /** * Construct an object name from the given string. * @@ -1550,7 +1635,7 @@ public class ObjectName implements Comparable, QueryExp { throw new NullPointerException("key property can't be null"); for (int i = 0; i < _ca_array.length; i++) { Property prop = _ca_array[i]; - String key = prop.getKeyString(_canonicalName); + String key = prop.getKeyString(_canonicalName,_domain_length); if (key.equals(property)) return (prop instanceof PatternProperty); } @@ -1630,8 +1715,10 @@ public class ObjectName implements Comparable, QueryExp { Property prop; for (int i = len - 1; i >= 0; i--) { prop = _ca_array[i]; - _propertyList.put(prop.getKeyString(_canonicalName), - prop.getValueString(_canonicalName)); + _propertyList.put(prop.getKeyString(_canonicalName, + _domain_length), + prop.getValueString(_canonicalName, + _domain_length)); } } } @@ -1716,7 +1803,8 @@ public class ObjectName implements Comparable, QueryExp { } } - return new String(dest_chars); + final String name = new String(dest_chars); + return name; } /** @@ -1734,7 +1822,7 @@ public class ObjectName implements Comparable, QueryExp { if (_kp_array.length == 0) return offset; final char[] dest_chars = data; - final char[] value = _canonicalName.toCharArray(); + final char[] value = canonicalChars; int index = offset; final int len = _kp_array.length; @@ -1742,7 +1830,7 @@ public class ObjectName implements Comparable, QueryExp { for (int i = 0; i < len; i++) { final Property prop = _kp_array[i]; final int prop_len = prop._key_length + prop._value_length + 1; - System.arraycopy(value, prop._key_index, dest_chars, index, + System.arraycopy(value, prop._key_index+_domain_length, dest_chars, index, prop_len); index += prop_len; if (i < last ) dest_chars[index++] = ','; @@ -1816,7 +1904,7 @@ public class ObjectName implements Comparable, QueryExp { // (because usage of intern()) ObjectName on = (ObjectName) object; String on_string = on._canonicalName; - if (_canonicalName == on_string) return true; + if (_canonicalName == on_string) return true; // ES: OK // Because we are sharing canonical form between object names, // we have finished the comparison at this stage ==> unequal @@ -1997,9 +2085,9 @@ public class ObjectName implements Comparable, QueryExp { private final boolean matchDomains(ObjectName name) { if (_domain_pattern) { // wildmatch domains - final char[] dom_pattern = getDomain().toCharArray(); - final char[] dom_string = name.getDomain().toCharArray(); - return wildmatch(dom_string,dom_pattern,0,0); + // This ObjectName is the pattern + // The other ObjectName is the string. + return Util.wildpathmatch(name.getDomain(),getDomain()); } return getDomain().equals(name.getDomain()); } @@ -2025,7 +2113,7 @@ public class ObjectName implements Comparable, QueryExp { // index in receiver // final Property p = props[i]; - final String k = p.getKeyString(cn); + final String k = p.getKeyString(cn,_domain_length); final String v = nameProps.get(k); // Did we find a value for this key ? // @@ -2034,15 +2122,13 @@ public class ObjectName implements Comparable, QueryExp { // if (_property_value_pattern && (p instanceof PatternProperty)) { // wildmatch key property values - final char[] val_pattern = - p.getValueString(cn).toCharArray(); - final char[] val_string = v.toCharArray(); - if (wildmatch(val_string,val_pattern,0,0)) + // p is the property pattern, v is the string + if (Util.wildmatch(v,p.getValueString(cn,_domain_length))) continue; else return false; } - if (v.equals(p.getValueString(cn))) continue; + if (v.equals(p.getValueString(cn,_domain_length))) continue; return false; } return true; @@ -2109,6 +2195,10 @@ public class ObjectName implements Comparable, QueryExp { * @since 1.6 */ public int compareTo(ObjectName name) { + // Quick optimization: + // + if (name == this) return 0; + // (1) Compare domains // int domainValue = this.getDomain().compareTo(name.getDomain()); diff --git a/src/share/classes/javax/management/event/EventClient.java b/src/share/classes/javax/management/event/EventClient.java index 923ef90cd..4b8101353 100644 --- a/src/share/classes/javax/management/event/EventClient.java +++ b/src/share/classes/javax/management/event/EventClient.java @@ -29,6 +29,7 @@ import com.sun.jmx.event.DaemonThreadFactory; import com.sun.jmx.event.LeaseRenewer; import com.sun.jmx.event.ReceiverBuffer; import com.sun.jmx.event.RepeatedSingletonJob; +import com.sun.jmx.namespace.JMXNamespaceUtils; import com.sun.jmx.mbeanserver.PerThreadGroupPool; import com.sun.jmx.remote.util.ClassLogger; @@ -1063,6 +1064,24 @@ public class EventClient implements EventConsumer, NotificationManager { return clientId; } + /** + * Returns a JMX Connector that will use an {@link EventClient} + * to subscribe for notifications. If the server doesn't have + * an {@link EventClientDelegateMBean}, then the connector will + * use the legacy notification mechanism instead. + * + * @param wrapped The underlying JMX Connector wrapped by the returned + * connector. + * + * @return A JMX Connector that will uses an {@link EventClient}, if + * available. + * + * @see EventClient#getEventClientConnection(MBeanServerConnection) + */ + public static JMXConnector withEventClient(final JMXConnector wrapped) { + return JMXNamespaceUtils.withEventClient(wrapped); + } + private static final PerThreadGroupPool leaseRenewerThreadPool = PerThreadGroupPool.make(); } diff --git a/src/share/classes/javax/management/event/EventClientDelegate.java b/src/share/classes/javax/management/event/EventClientDelegate.java index e5ce1f7be..cab3ff492 100644 --- a/src/share/classes/javax/management/event/EventClientDelegate.java +++ b/src/share/classes/javax/management/event/EventClientDelegate.java @@ -721,7 +721,10 @@ public class EventClientDelegate implements EventClientDelegateMBean { SecurityManager sm = System.getSecurityManager(); if (sm != null) { try { - ObjectInstance oi = (ObjectInstance) AccessController.doPrivileged( + final String serverName = getMBeanServerName(); + + ObjectInstance oi = (ObjectInstance) + AccessController.doPrivileged( new PrivilegedExceptionAction() { public Object run() throws InstanceNotFoundException { @@ -731,6 +734,7 @@ public class EventClientDelegate implements EventClientDelegateMBean { String classname = oi.getClassName(); MBeanPermission perm = new MBeanPermission( + serverName, classname, null, name, @@ -746,6 +750,20 @@ public class EventClientDelegate implements EventClientDelegateMBean { return true; } + private String getMBeanServerName() { + if (mbeanServerName != null) return mbeanServerName; + else return (mbeanServerName = getMBeanServerName(mbeanServer)); + } + + private static String getMBeanServerName(final MBeanServer server) { + final PrivilegedAction action = new PrivilegedAction() { + public String run() { + return Util.getMBeanServerSecurityName(server); + } + }; + return AccessController.doPrivileged(action); + } + // ------------------------------------ // private variables // ------------------------------------ diff --git a/src/share/classes/javax/management/namespace/JMXDomain.java b/src/share/classes/javax/management/namespace/JMXDomain.java new file mode 100644 index 000000000..a54fde7f3 --- /dev/null +++ b/src/share/classes/javax/management/namespace/JMXDomain.java @@ -0,0 +1,382 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.namespace; + +import java.io.IOException; +import javax.management.ListenerNotFoundException; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR; + + +import javax.management.InstanceNotFoundException; +import javax.management.MBeanServer; +import javax.management.MBeanServerDelegate; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +/** + * A special {@link JMXNamespace} that can handle part of + * the MBeanServer local name space. + *

      + * A {@code JMXDomain} makes a domain X of a + * {@linkplain #getSourceServer() source MBean server} appear in the same domain + * X of a containing {@code MBeanServer} in which the + * {@code JMXDomain} MBean {@linkplain #getMBeanServer() is registered}. + *

      + *

      + * The JMX infrastructure of the containing {@code MBeanServer} takes care of + * routing all calls to MBeans whose names have domain X to the + * {@linkplain #getSourceServer() source MBean server} exported by the + * {@code JMXDomain} MBean in charge of domain X. + *

      + *

      + * The {@linkplain #getSourceServer() source MBean server} of a {@code JMXDomain} + * can, but need not be a regular {@code MBeanServer} created through + * the {@link javax.management.MBeanServerFactory}. It could also be, + * for instance, an instance of a subclass of {@link MBeanServerSupport}, + * or a custom object implementing the {@link MBeanServer} interface. + *

      + * + *

      Differences between {@code JMXNamespace} and {@code JMXDomain}

      + * + *

      + * A {@code JMXDomain} is a special kind of {@code JMXNamespace}. + * A {@code JMXNamespace} such as {@code foo//} is triggered by an + * {@code ObjectName} that begins with the string {@code foo//}, for example + * {@code foo//bar:type=Baz}. A {@code JMXDomain} such as {@code foo} is + * triggered by an {@code ObjectName} with that exact domain, for example + * {@code foo:type=Baz}. A client can immediately see that an MBean is + * handled by a {@code JMXNamespace} because of the {@code //} in the name. + * A client cannot see whether a name such as {@code foo:type=Baz} is an + * ordinary MBean or is handled by a {@code JMXDomain}. + *

      + * + *

      + * A {@linkplain MBeanServer#queryNames query} on the containing {@code + * MBeanserver} will return all MBeans from the {@code JMXDomain} that match + * the query. In particular, {@code queryNames(null, null)} will return all + * MBeans including those from {@code JMXDomain} domains. On the other hand, + * a query will not include MBeans from a {@code JMXNamespace} unless the + * {@code ObjectName} pattern in the query starts with the name of that + * namespace. + *

      + * + *

      Permission checks

      + * + *

      + * When a JMXDomain MBean is registered in a containing + * MBean server created through the default {@link + * javax.management.MBeanServerBuilder}, and if a {@link + * SecurityManager SecurityManager} is + * {@linkplain System#getSecurityManager() present}, the containing MBeanServer will + * check an {@link javax.management.MBeanPermission} before invoking + * any method on the {@linkplain #getSourceServer() source MBeanServer} of the + * JMXDomain. + *

      + * + *

      First, if there is no security manager ({@link + * System#getSecurityManager()} is null), that containing + * {@code MBeanServer} is free not to make any checks. + *

      + * + *

      + * Assuming that there is a security manager, or that the + * implementation chooses to make checks anyway, the containing + * {@code MBeanServer} will perform + * {@link javax.management.MBeanPermission MBeanPermission} checks + * for access to the MBeans in domain X handled by a {@code JMXDomain} + * in the same way that it would do for MBeans registered in its own local + * repository, and as described in + * the MBeanServer interface, with the following exceptions: + *

      + * + *

      + * For those permissions that require a {@code className}, the + * className is the + * string returned by {@link #getSourceServer() getSourceServer()}. + * {@link MBeanServer#getObjectInstance(ObjectName) + * getObjectInstance(mbeanName)}. + * {@link javax.management.ObjectInstance#getClassName() getClassName()}, + * except for {@code createMBean} and {@code registerMBean} operations, + * for which the permission checks are performed as follows: + *

      + *
        + *
      • For {@code createMBean} operations, the {@code className} of the + * permission you need is the {@code className} passed as first parameter + * to {@code createMBean}.

        + * + *
      • For {@code registerMBean} operations, the {@code className} of the + * permission you need is the name of the class of the mbean object, as + * returned by {@code mbean.getClass().getClassName()}, where + * {@code mbean} is the mbean reference passed as first parameter + * to {@code registerMBean}.

        + * + *
      • In addition, for {@code createMBean} and {@code registerMBean}, the + * permission you need is checked with the {@linkplain ObjectName object name} of + * the mbean that is passed as second parameter to the {@code createMBean} or + * {@code registerMBean} operation. + *

        + * + *
      • Contrarily to what is done for regular MBeans registered in the + * MBeanServer local repository, the containing MBeanServer will not + * check the {@link javax.management.MBeanTrustPermission#MBeanTrustPermission(String) + * MBeanTrustPermission("register")} against the protection domain + * of the MBean's class. This check can be performed by the + * {@linkplain #getSourceServer source MBean server} implementation, + * if necessary. + *

        + *
      + * + *

      If a security check fails, the method throws {@link + * SecurityException}.

      + * + *

      For methods that can throw {@link InstanceNotFoundException}, + * this exception is thrown for a non-existent MBean, regardless of + * permissions. This is because a non-existent MBean has no + * className.

      + * + * All these checks are performed by the containing {@code MBeanServer}, + * before accessing the JMXDomain {@linkplain #getSourceServer source MBean server}. + * The implementation of the JMXDomain {@linkplain #getSourceServer source MBean + * server} is free to make any additional checks. In fact, if the JMXDomain + * {@linkplain #getSourceServer source MBean server} is an {@code MBeanServer} + * obtained through the {@link javax.management.MBeanServerFactory}, it will + * again make permission checks as described in the + * MBeanServer interface. + * + *

      See the MBeanServer interface + * for more details on permission checks.

      + * + * @since 1.7 + */ +public class JMXDomain extends JMXNamespace { + + + /** + * This constant contains the value of the {@code type} + * key used in defining a standard JMXDomain MBean object name. + * By definition, a standard JMXDomain MBean object name must be of + * the form: + *
      +     * {@code ":"}+{@value javax.management.namespace.JMXDomain#TYPE_ASSIGNMENT}
      +     * 
      + */ + public static final String TYPE = "JMXDomain"; + + /** + * This constant contains the value of the standard + * {@linkplain javax.management.ObjectName#getKeyPropertyListString() key + * property list string} for JMXDomain MBean object names. + * By definition, a standard JMXDomain MBean object name must be of + * the form: + *
      +     * {@code }+":"+{@value javax.management.namespace.JMXDomain#TYPE_ASSIGNMENT}
      +     * 
      + */ + public static final String TYPE_ASSIGNMENT = "type="+TYPE; + + + + /** + * Creates a new instance of JMXDomain. The MBeans contained in this + * domain are handled by the {@code virtualServer} object given to + * this constructor. Frequently, this will be an instance of + * {@link MBeanServerSupport}. + * @param virtualServer The virtual server that acts as a container for + * the MBeans handled by this JMXDomain object. Frequently, this will + * be an instance of {@link MBeanServerSupport} + * @see JMXNamespace#JMXNamespace(MBeanServer) + */ + public JMXDomain(MBeanServer virtualServer) { + super(virtualServer); + } + + /** + * Return the name of domain handled by this JMXDomain. + * @return the domain handled by this JMXDomain. + * @throws IOException - if the domain cannot be determined, + * for instance, if the MBean is not registered yet. + */ + @Override + public final String getDefaultDomain() { + final ObjectName name = getObjectName(); + if (name == null) + throw new IllegalStateException("DefaultDomain is not yet known"); + final String dom = name.getDomain(); + return dom; + } + + /** + * Returns a singleton array, containing the only domain handled by + * this JMXDomain object. This is + * {@code new String[] {getDefaultDomain()}}. + * @return the only domain handled by this JMXDomain. + * @throws IOException if the domain cannot be determined, + * for instance, if the MBean is not registered yet. + * @see #getDefaultDomain() + */ + @Override + public final String[] getDomains() { + return new String[] {getDefaultDomain()}; + } + + /** + * This method returns the number of MBeans in the domain handled + * by this JMXDomain object. The default implementation is: + *
      +     *    getSourceServer().queryNames(
      +     *        new ObjectName(getObjectName().getDomain()+":*"), null).size();
      +     * 
      + * If this JMXDomain is not yet registered, this method returns 0. + * Subclasses can override the above behavior and provide a better + * implementation. + *

      + * The getMBeanCount() method is called when computing the number + * of MBeans in the {@linkplain #getMBeanServer() containing MBeanServer}. + * @return the number of MBeans in this domain, or 0. + */ + @Override + public Integer getMBeanCount() { + final ObjectName name = getObjectName(); + if (name == null) return 0; + try { + return getSourceServer(). + queryNames(ObjectName.WILDCARD.withDomain(name.getDomain()), + null).size(); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException("Unexpected exception: "+x,x); + } + } + + + + /** + * Return a canonical handler name for the provided local + * domain name, or null if the provided domain name is + * {@code null}. + * If not null, the handler name returned will be + * {@code domain+":type="+}{@link #TYPE TYPE}, for example + * {@code foo:type=JMXDomain}. + * @param domain A domain name + * @return a canonical ObjectName for a domain handler. + * @throws IllegalArgumentException if the provided + * domain is not valid - e.g it contains "//". + */ + public static ObjectName getDomainObjectName(String domain) { + if (domain == null) return null; + if (domain.contains(NAMESPACE_SEPARATOR)) + throw new IllegalArgumentException(domain); + try { + return ObjectName.getInstance(domain, "type", TYPE); + } catch (MalformedObjectNameException x) { + throw new IllegalArgumentException(domain,x); + } + } + + + /** + * Validate the ObjectName supplied to preRegister. + * This method is introduced to allow standard subclasses to use + * an alternate naming scheme. For instance - if we want to + * reuse JMXNamespace in order to implement sessions... + * It is however only available for subclasses in this package. + **/ + @Override + ObjectName validateHandlerName(ObjectName supliedName) { + if (supliedName == null) + throw new IllegalArgumentException("Must supply a valid name"); + final String dirName = JMXNamespaces. + normalizeNamespaceName(supliedName.getDomain()); + final ObjectName handlerName = getDomainObjectName(dirName); + if (!supliedName.equals(handlerName)) + throw new IllegalArgumentException("invalid name space name: "+ + supliedName); + + return supliedName; + } + + /** + * This method is called by the JMX framework to register a + * NotificationListener that will forward {@linkplain + * javax.management.MBeanServerNotification mbean server notifications} + * through the delegate of the {@linkplain #getMBeanServer() + * containing MBeanServer}. + * The default implementation of this method is to call + *

      +     *    getSourceServer().addNotificationListener(
      +     *           MBeanServerDelegate.DELEGATE_NAME, listener, filter, null);
      +     * 
      + * Subclasses can redefine this behavior if needed. In particular, + * subclasses can send their own instances of {@link + * javax.management.MBeanServerNotification} by calling + * {@code listener.handleNotification()}. + * + * @param listener The MBeanServerNotification listener for this domain. + * @param filter A notification filter. + */ + public void addMBeanServerNotificationListener( + NotificationListener listener, NotificationFilter filter) { + try { + getSourceServer().addNotificationListener( + MBeanServerDelegate.DELEGATE_NAME, listener, filter, null); + } catch(InstanceNotFoundException x) { + throw new UnsupportedOperationException( + "Unexpected exception: " + + "Emission of MBeanServerNotification disabled.", x); + } + } + + /** + * This method is called by the JMX framework to remove the + * NotificationListener that was added with {@link + * #addMBeanServerNotificationListener addMBeanServerNotificationListener}. + * The default implementation of this method is to call + *
      +     *    getSourceServer().removeNotificationListener(
      +     *           MBeanServerDelegate.DELEGATE_NAME, listener);
      +     * 
      + * Subclasses can redefine this behavior if needed. + * + * @param listener The MBeanServerNotification listener for this domain. + * @throws ListenerNotFoundException if the listener is not found. + */ + public void removeMBeanServerNotificationListener( + NotificationListener listener) + throws ListenerNotFoundException { + try { + getSourceServer().removeNotificationListener( + MBeanServerDelegate.DELEGATE_NAME, listener); + } catch(InstanceNotFoundException x) { + throw new UnsupportedOperationException( + "Unexpected exception: " + + "Emission of MBeanServerNotification disabled.", x); + } + } + +} diff --git a/src/share/classes/javax/management/namespace/JMXNamespace.java b/src/share/classes/javax/management/namespace/JMXNamespace.java new file mode 100644 index 000000000..39cb11b02 --- /dev/null +++ b/src/share/classes/javax/management/namespace/JMXNamespace.java @@ -0,0 +1,660 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.namespace; + + +import java.io.IOException; + +import java.util.UUID; +import javax.management.MBeanRegistration; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +/** + * MBean Servers can be federated into a single hierarchical name space: + * A JMXNamespace is an MBean that handles a sub name space in that + * hierarchical name space. + *

      + * A name space is created simply by registering a {@code JMXNamespace} + * MBean in the MBean Server. The name of the created name space is defined + * by the {@linkplain JMXNamespaces#getNamespaceObjectName name of the JMXNamespace} + * that handles it. A name space is equivalent to + * an MBean Server within an MBean Server. When creating a {@code JMXNamespace}, + * the MBean Server within is passed to the constructor. + *

      + *

      + * The {@code JMXNamespace} class is the base class for implementing + * all name space handlers. All name space handlers must be instances of + * {@code JMXNamespace} or a subclass of it. + *

      + *

      + * A concrete example of a {@code JMXNamespace} MBean subclass + * is the {@link JMXRemoteNamespace JMXRemoteNamespace} MBean which + * is able to mirror all MBeans contained in a remote MBean server known by its + * {@link javax.management.remote.JMXServiceURL}. + *

      + *

      + * You can create a local namespace by supplying a newly created MBean Server + * to an instance of {@code JMXNamespace}. For instance: + *

      + * final String namespace = "foo";
      + * final ObjectName namespaceName = {@link JMXNamespaces#getNamespaceObjectName
      + *       JMXNamespaces.getNamespaceObjectName(namespace)};
      + * server.registerMBean(new JMXNamespace(MBeanServerFactory.newMBeanServer()),
      + *                      namespaceName);
      + * 
      + *

      + *

      + * Note: A JMXNamespace MBean cannot be registered + * simultaneously in two different + * MBean servers, or indeed in the same MBean Server with two + * different names. It is however possible to give the same MBeanServer + * instance to two different JMXNamespace MBeans, and thus create a graph + * rather than a tree. + *

      + * + *

      To view the content of a namespace, you will usually use an + * instance of {@link JMXNamespaceView}. For instance, given the + * namespace {@code "foo"} created above, you would do: + *

      + *
      + * final JMXNamespaceView view = new JMXNamespaceView(server);
      + * System.out.println("List of namespaces: "+Arrays.toString({@link JMXNamespaceView#list() view.list()}));
      + *
      + * final JMXNamespaceView foo  = {@link JMXNamespaceView#down view.down("foo")};
      + * System.out.println({@link JMXNamespaceView#where() foo.where()}+" contains: " +
      + *        {@link JMXNamespaceView#getMBeanServerConnection foo.getMBeanServerConnection()}.queryNames(null,null));
      + * 
      + * + *

      JMX Namespace Permission Checks

      + * + *

      A special {@link JMXNamespacePermission} is defined to check access + * to MBean within namespaces.

      + *

      When a JMXNamespace MBean is registered in an + * MBean server created through the default {@link + * javax.management.MBeanServerBuilder}, and if a {@link + * SecurityManager SecurityManager} is + * {@linkplain System#getSecurityManager() present}, the MBeanServer will + * check a {@link JMXNamespacePermission} before invoking + * any method on the {@linkplain #getSourceServer source MBeanServer} of the + * JMXNamespace. + * {@linkplain JMXNamespacePermission JMX Namespace Permissions} are similar to + * {@linkplain javax.management.MBeanPermission MBean Permissions}, except + * that you usually cannot specify an MBean class name. You can however + * specify object name patterns - which will allow you for example to only grant + * permissions for MBeans having a specific {@code type=} key + * in their object name. + *

      + * Another difference is that {@link JMXNamespacePermission + * JMXNamespacePermission} also specifies from which namespace and which + * MBean server the permission is granted. + *

      + *

      In the rest of this document, the following terms are used:

      + *
        + *
      • {@code server name} is the + * name of the + * MBeanServer in which the permission is granted. + * The name of an {@code MBeanServer} can be obtained by calling {@link + * javax.management.MBeanServerFactory#getMBeanServerName + * MBeanServerFactory.getMBeanServerName(mbeanServer)} + *

        + *
      • {@code namespace} is the name of the namespace + * in the named MBean server for which the + * permission is granted. It doesn't contain any + * {@link JMXNamespaces#NAMESPACE_SEPARATOR namespace separator}. + *

        + *
      • {@code mbean} is the name + * of the MBean in that {@code namespace}. This is the name of the MBean + * in the namespace's {@link JMXNamespace#getSourceServer() source mbean server}. + * It might contain no, one, or several {@link + * JMXNamespaces#NAMESPACE_SEPARATOR namespace separators}. + *

        + *
      + * + *

      For instance let's assume that some piece of code calls:

      + *
      + *     final MBeanServer mbeanServer = ...;
      + *     final ObjectName  name   = new ObjectName("a//b//c//D:k=v");
      + *     mbeanServer.getAttribute(name,"Foo");
      + * 
      + *

      + * Assuming that there is a security manager, or that the + * implementation chooses to make checks anyway, the checks that will + * be made in that case are: + *

      + *
        + *
      1. + * JMXNamespacePermission(mbeanServerName, "Foo", "a//b//c//D:k=v", + * "getAttribute") + * (where {@code mbeanServerName=MBeanServerFactory.getMBeanServerName(mbeanServer)}, + * namespace="a", and {@code mbean="b//c//D:k=v"}) + *
      2. + *
      3. and in addition if namespace {@code "a"} is local, + * JMXNamespacePermission(aSourceServerName,"Foo","b//c//D:k=v", + * "getAttribute")} + * (where + * {@code aSourceServerName=MBeanServerFactory.getMBeanServerName(sourceServer(a))}, + * namespace="b", and {@code mbean="c//D:k=v"}), + *
      4. + *
      5. and in addition if namespace {@code "b"} is also local, + * JMXNamespacePermission(bSourceServerName,"Foo","c//D:k=v", + * "getAttribute")} + * (where + * {@code bSourceServerName=MBeanServerFactory.getMBeanServerName(sourceServer(b))}, + * namespace="c", and {@code mbean="D:k=v"}), + *
      6. + *
      7. and in addition if the source mbean server of namespace + * {@code "c"} is a also a local MBeanServer in this JVM, + * {@code MBeanPermission(cSourceServerName,,"Foo","D:k=v","getAttrinute")}, + * (where + * {@code cSourceServerName=MBeanServerFactory.getMBeanServerName(sourceServer(c))}). + *
      8. + *
      + *

      For any of these MBean servers, if no name was supplied when + * creating that MBeanServer the {@link JMXNamespacePermission} is + * created with an {@code mbeanServerName} equal to + * {@value javax.management.MBeanServerFactory#DEFAULT_MBEANSERVER_NAME}. + *

      + *

      If the namespace {@code a} is in fact a remote {@code MBeanServer}, + * for instance because namespace {@code a} is implemented by a {@link + * JMXRemoteNamespace} pointing to a distant MBeanServer located in + * another JMX agent, then checks 2, + * 3, and 4 will not + * be performed in the local JVM. They might or might not be performed in + * the remote agent, depending on how access control and permission + * checking are configured in the remote agent, and how authentication + * is configured in the connector used by the {@link + * JMXRemoteNamespace}. + *

      + *

      In all cases, {@linkplain JMXNamespacePermission JMX Namespace Permissions} + * are checked as follows:

      + *

      First, if there is no security manager ({@link + * System#getSecurityManager()} is null), then an implementation of + * of MBeanServer that supports JMX namespaces is free not to make any + * checks.

      + * + *

      Assuming that there is a security manager, or that the + * implementation chooses to make checks anyway, the checks are made + * as detailed below.

      + * + *

      If a security check fails, the method throws {@link + * SecurityException}.

      + * + *
        + * + *
      • For the {@link MBeanServer#invoke invoke} method, the caller's + * permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, <operation name>, <namespace>//<mbean>, "invoke")}, + * where mbean server name is the name of the + * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of + * namespace is registered, and + * mbean is the name of the MBean on which the action + * is performed, in that namespace. + *

        + * + *
      • For the {@link MBeanServer#getAttribute getAttribute} method, the + * caller's permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, <attribute>, <namespace>//<mbean>, "getAttribute")}. + *

        + * + *
      • For the {@link MBeanServer#getAttributes getAttributes} method, the + * caller's permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, <null>, <namespace>//<mbean>, "getAttribute")}, + * where mbean server name is the name of the + * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of + * namespace is registered, and + * mbean is the name of the MBean on which the action + * is performed, in that namespace. + * Additionally, for each attribute att in the {@link + * javax.management.AttributeList}, if the caller's permissions do not + * imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, att, + * <namespace>//<mbean>, "getAttribute")}, the + * MBean server will behave as if that attribute had not been in the + * supplied list.

        + * + *
      • For the {@link MBeanServer#setAttribute setAttribute} method, the + * caller's permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, <attrName>, <namespace>//<mbean>, "setAttribute")}, + * where mbean server name is the name of the + * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of + * namespace is registered, and + * mbean is the name of the MBean on which the action + * is performed, in that namespace, and + * attrName is {@link javax.management.Attribute#getName() + * attribute.getName()}.

        + * + *
      • For the {@link MBeanServer#setAttributes setAttributes} method, the + * caller's permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, null, <namespace>//<mbean>, "setAttribute")}, + * where mbean server name is the name of the + * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of + * namespace is registered, and + * mbean is the name of the MBean on which the action + * is performed, in that namespace. + * Additionally, for each attribute att in the {@link + * javax.management.AttributeList}, if the caller's permissions do not + * imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, att, <namespace>//<mbean>, "setAttribute")}, + * the MBean server will behave as if that attribute had not been in the + * supplied list.

        + * + *
      • For the addNotificationListener methods, + * the caller's permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, null, <namespace>//<mbean>, + * "addNotificationListener")}, + * where mbean server name is the name of the + * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of + * namespace is registered, and + * mbean is the name of the MBean on which the action + * is performed, in that namespace. + *

        + * + *
      • For the removeNotificationListener methods, + * the caller's permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, null, <namespace>//<mbean>, + * "removeNotificationListener")}, + * where mbean server name is the name of the + * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of + * namespace is registered, and + * mbean is the name of the MBean on which the action + * is performed, in that namespace. + *

        + * + *
      • For the {@link MBeanServer#getMBeanInfo getMBeanInfo} method, the + * caller's permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, null, <namespace>//<mbean>, + * "getMBeanInfo")}, + * where mbean server name is the name of the + * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of + * namespace is registered, and + * mbean is the name of the MBean on which the action + * is performed, in that namespace. + *

        + * + *
      • For the {@link MBeanServer#getObjectInstance getObjectInstance} method, + * the caller's permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, null, <namespace>//<mbean>, + * "getObjectInstance")}, + * where mbean server name/a> is the name of the + * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of + * namespace is registered, and + * mbean is the name of the MBean on which the action + * is performed, in that namespace. + *

        + * + *
      • For the {@link MBeanServer#isInstanceOf isInstanceOf} method, the + * caller's permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, null, <namespace>//<mbean>, + * "isInstanceOf")}, + * where mbean server name is the name of the + * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of + * namespace is registered, and + * mbean is the name of the MBean on which the action + * is performed, in that namespace. + *

        + * + *
      • For the {@link MBeanServer#queryMBeans queryMBeans} method, the + * caller's permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, null, null, + * "queryMBeans")}. + * Additionally, for each MBean {@code mbean} that matches {@code pattern}, + * if the caller's permissions do not imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, null, <namespace>//<mbean>, + * "queryMBeans")}, the + * MBean server will behave as if that MBean did not exist.

        + * + *

        Certain query elements perform operations on the MBean server. + * However these operations are usually performed by the MBeanServer at the + * bottom of the namespace path, and therefore, do not involve any + * {@link JMXNamespacePermission} permission check. They might involve + * {@link javax.management.MBeanPermission} checks depending on how security + * in the JVM in which the bottom MBeanServer resides is implemented. + * See {@link javax.management.MBeanServer} for more details. + *

        + * + *
      • For the {@link MBeanServer#queryNames queryNames} method, the checks + * are the same as for queryMBeans except that + * "queryNames" is used instead of + * "queryMBeans" in the JMXNamespacePermission + * objects. Note that a "queryMBeans" permission implies + * the corresponding "queryNames" permission.

        + * + *
      • For the {@link MBeanServer#getClassLoader getClassLoader} method, the + * caller's permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, null, <namespace>//<loaderName>, + * "getClassLoader")}, + * where mbean server name/a> is the name of the + * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of + * namespace is registered, and + * loaderName is the name of the ClassLoader MBean + * which is accessed, in that namespace. + *

        + * + *
      • For the {@link MBeanServer#getClassLoaderFor getClassLoaderFor} method, + * the caller's permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, null, <namespace>//<mbean>, + * "getClassLoaderFor")}, + * where mbean server name is the name of the + * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of + * namespace is registered, and + * mbean is the name of the MBean on which the action + * is performed, in that namespace. + *

        + * + *
      • For the {@link MBeanServer#registerMBean registerMBean} method, the + * caller's permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, <class name>, <namespace>//<mbean>, + * "registerMBean")}. Here + * class name is the string returned by {@code + * obj.getClass().getName()} where {@code obj} is the mbean reference, + * is the name of the + * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of + * namespace is registered, and + * mbean is the name of the MBean which is being + * registered, relative to that namespace. + * + *

      • For the createMBean methods, the caller's + * permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, <class name>, <namespace>//<mbean>, + * "instantiate")} and + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, <class name>, <namespace>//<mbean>, + * "registerMBean")}, where + * class name is the string passed as first argument to the {@code + * createMBean} method, + * mbean server name is the name of the + * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of + * namespace is registered, and + * mbean is the name of the MBean which is being + * created, relative to that namespace. + * + *

      • For the {@link MBeanServer#unregisterMBean unregisterMBean} method, + * the caller's permissions must imply {@link + * JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String) + * JMXNamespacePermission(<mbean server name>, null, <namespace>//<mbean>, + * "unregisterMBean")}, + * where mbean server name is the name of the + * {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of + * namespace is registered, and + * mbean is the name of the MBean on which is + * being unregistered, relative to that namespace. + *

        + *
      + *

      + *

      It must be noted that if all namespaces are local, and all + * local namespaces are implemented by regular MBean servers, that is, there + * are no {@linkplain MBeanServerSupport Virtual Namespaces}, then + * simple {@linkplain javax.management.MBeanPermission MBean Permission} + * checks might be enough to secure an application. + * In that case, it is possible to specify the following {@link + * JMXNamespacePermission} permission in the policy file, which implies all + * other JMX namespace permissions: + *

      + *
      + *     permission javax.management.namespace.JMXNamespacePermission "*::*[]", "*";
      + * 
      + * + * @since 1.7 + */ +public class JMXNamespace + implements JMXNamespaceMBean, MBeanRegistration { + + /** + * The standard value of the {@code type} + * property key that must be used to construct valid {@link + * JMXNamespaceMBean} ObjectNames.
      + * This is {@value #TYPE}. + **/ + public static final String TYPE = "JMXNamespace"; + + /** + * The {@link ObjectName#getKeyPropertyListString keyPropertyListString} + * that must be used to construct valid {@link JMXNamespaceMBean} + * ObjectNames.
      + * This is + * {@value #TYPE_ASSIGNMENT}. + **/ + public static final String TYPE_ASSIGNMENT = "type="+TYPE; + + private volatile MBeanServer mbeanServer; // the mbean server in which + // this MBean is registered. + private volatile ObjectName objectName; // the ObjectName of this MBean. + private final MBeanServer sourceServer; // the MBeanServer within = the + // name space (or the MBean server + // that contains it). + private final String uuid; + + /** + * Creates a new JMXNamespace implemented by means of an MBean Server. + * A namespace is equivalent to an MBeanServer within an MBean Server. + * The {@code sourceServer} provided to this constructor is the MBean Server + * within. + * @param sourceServer the MBean server that implemented by this namespace. + * @see #getSourceServer + */ + public JMXNamespace(MBeanServer sourceServer) { + this.sourceServer = sourceServer; + this.uuid = UUID.randomUUID().toString(); + } + + /** + * This method is part of the {@link MBeanRegistration} interface. + * The {@link JMXNamespace} class uses the {@link MBeanRegistration} + * interface in order to get a handle to the MBean server in which it is + * registered. It also check the validity of its own ObjectName. + *

      + * This method is called by the MBean server. + * Application classes should never call this method directly. + *

      + * If this method is overridden, the overriding method should call + * {@code super.preRegister(server,name)}. + * @see MBeanRegistration#preRegister MBeanRegistration + * @see JMXNamespaces#getNamespaceObjectName getNamespaceObjectName + * @param name The object name of the MBean. name must be a + * syntactically valid JMXNamespace name, as returned by + * {@link JMXNamespaces#getNamespaceObjectName(java.lang.String) + * getNamespaceObjectName(namespace)}. + * @return The name under which the MBean is to be registered. + * @throws IllegalArgumentException if the name supplied is not valid. + * @throws Exception can be thrown by subclasses. + */ + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + if (objectName != null && ! objectName.equals(name)) + throw new IllegalStateException( + "Already registered under another name: " + objectName); + objectName = validateHandlerName(name); + mbeanServer = server; + return name; + } + + /** + * Validate the ObjectName supplied to preRegister. + * This method is introduced to allow standard subclasses to use + * an alternate naming scheme. For instance - if we want to + * reuse JMXNamespace in order to implement sessions... + * It is however only available for subclasses in this package. + **/ + ObjectName validateHandlerName(ObjectName supliedName) { + if (supliedName == null) + throw new IllegalArgumentException("Must supply a valid name"); + final String dirName = JMXNamespaces. + normalizeNamespaceName(supliedName.getDomain()); + final ObjectName handlerName = + JMXNamespaces.getNamespaceObjectName(dirName); + if (!supliedName.equals(handlerName)) + throw new IllegalArgumentException("invalid name space name: "+ + supliedName); + return supliedName; + } + + /** + * This method is part of the {@link MBeanRegistration} interface. + * The {@link JMXNamespace} class uses the {@link MBeanRegistration} + * interface in order to get a handle to the MBean server in which it is + * registered. + *

      + * This method is called by the MBean server. Application classes should + * not call this method directly. Subclasses are free to override this + * method with their own specific behavior - but the overriding method + * shoud still call {@code super.postRegister(registrationDone)}. + * @see MBeanRegistration#postRegister MBeanRegistration + */ + public void postRegister(Boolean registrationDone) { + // nothing to do + } + + /** + * This method is part of the {@link MBeanRegistration} interface. + * The {@link JMXNamespace} class uses the {@link MBeanRegistration} + * interface in order to get a handle to the MBean server in which it is + * registered. + *

      + * This method is called by the MBean server. Application classes should + * not call this method directly. Subclasses are free to override this + * method with their own specific behavior - but the overriding method + * shoud still call {@code super.preDeregister()}. + * @see MBeanRegistration#preDeregister MBeanRegistration + */ + public void preDeregister() throws Exception { + // nothing to do + } + + /** + * This method is part of the {@link MBeanRegistration} interface. + * It allows the {@code JMXNamespace} MBean to perform any operations + * needed after having been unregistered in the MBean server. + *

      + * This method is called by the MBean server. Application classes should + * not call this method directly. If a subclass overrides this + * method, the overriding method shoud call {@code super.postDeregister()}. + * @see MBeanRegistration#postDeregister MBeanRegistration + */ + public void postDeregister() { + mbeanServer = null; + objectName = null; + } + + + /** + * Returns the MBeanServer in which this MBean is registered, + * or null. Chiefly of interest for subclasses. + * @return the MBeanServer supplied to {@link #preRegister}. + **/ + public final MBeanServer getMBeanServer() { + return mbeanServer; + } + + /** + * Returns the MBeanServer that contains or emulates the source + * namespace. When a JMXNamespace MBean is registered in an + * MBean server created through the default {@link + * javax.management.MBeanServerBuilder}, the MBeanServer will + * check {@link JMXNamespacePermission} before invoking + * any method on the source MBeanServer of the JMXNamespace. + * See JMX Namespace Permission Checks + * above. + * @return an MBeanServer view of the source namespace + **/ + public MBeanServer getSourceServer() { + return sourceServer; + } + + /** + * Returns the ObjectName with which this MBean was registered, + * or null. Chiefly of interest for subclasses. + * @return the ObjectName supplied to {@link #preRegister}. + **/ + public final ObjectName getObjectName() { + return objectName; + } + + /** + * HandlerName used in traces. + **/ + String getHandlerName() { + final ObjectName name = getObjectName(); + if (name != null) return name.toString(); + return this.toString(); + } + + /** + * In this class, this method returns {@link #getSourceServer + * getSourceServer()}.{@link javax.management.MBeanServer#getMBeanCount + * getMBeanCount()}. + *
      This default behaviour may be redefined in subclasses. + * @throws java.io.IOException can be thrown by subclasses. + */ + public Integer getMBeanCount() throws IOException { + return getSourceServer().getMBeanCount(); + } + + /** + * In this class, this method returns {@link #getSourceServer + * getSourceServer()}.{@link javax.management.MBeanServer#getDomains + * getDomains()}. + *
      This default behaviour may be redefined in subclasses. + * @throws java.io.IOException can be thrown by subclasses. + */ + public String[] getDomains() throws IOException { + return getSourceServer().getDomains(); + } + + /** + * In this class, this method returns {@link #getSourceServer + * getSourceServer()}.{@link javax.management.MBeanServer#getDefaultDomain + * getDefaultDomain()}. + *
      This default behaviour may be redefined in subclasses. + * @throws java.io.IOException can be thrown by subclasses. + */ + public String getDefaultDomain() throws IOException { + return getSourceServer().getDefaultDomain(); + } + + public final String getUUID() { + return uuid; + } + +} diff --git a/src/share/classes/javax/management/namespace/JMXNamespaceMBean.java b/src/share/classes/javax/management/namespace/JMXNamespaceMBean.java new file mode 100644 index 000000000..4632b410a --- /dev/null +++ b/src/share/classes/javax/management/namespace/JMXNamespaceMBean.java @@ -0,0 +1,96 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.namespace; + +import java.io.IOException; +import java.util.UUID; + +/** + * A {@link JMXNamespace} is an MBean that handles a name space in the + * MBeanServer hierarchical name space. + * @see JMXNamespace + * @since 1.7 + */ +public interface JMXNamespaceMBean { + + // Note: since getDomains(), getDefaultDomain(), and getMBeanCount() + // don't take any ObjectName parameters, the only efficient way + // to get these data is to call the corresponding method on the + // JMXNamespaceMBean that handles the name space. + // + // We need these methods to implement 'cd' (JMXNamespaces.narrowToNamespace) + // correctly. + // + + /** + * Returns the list of domains currently implemented in the name space + * handled by this {@link JMXNamespace}. + * @throws IOException if the domain list cannot be obtained due to + * I/O problems (communication failures etc...). + * @return the list of domains currently implemented in the name space + * handled by this {@link JMXNamespace}. + * @see javax.management.MBeanServerConnection#getDomains + * MBeanServerConnection.getDomains + **/ + public String[] getDomains() throws IOException; + + /** + * Returns the default domain for the name space handled by + * this {@link JMXNamespace}. + * @throws IOException if the default domain cannot be obtained due to + * I/O problems (communication failures etc...). + * @return the default domain for the name space handled by + * this {@link JMXNamespace}. + * @see javax.management.MBeanServerConnection#getDefaultDomain + * MBeanServerConnection.getDefaultDomain + **/ + public String getDefaultDomain() throws IOException; + + /** + * Returns the number of MBeans registered in the name space handled by + * this {@link JMXNamespace}. + * + * @return the number of MBeans registered in the name space handled by + * this {@link JMXNamespace}. + * + * @throws IOException if the MBean count cannot be obtained due to + * I/O problems (communication failures etc...). + * @see javax.management.MBeanServerConnection#getMBeanCount + * MBeanServerConnection.getMBeanCount + */ + public Integer getMBeanCount() throws IOException; + + /** + * Returns a {@link java.util.UUID UUID string} which uniquely identifies + * this {@linkplain JMXNamespace} MBean. + * This information can be used to detect loops in the JMX name space graph. + * @return A unique ID identifying this MBean. + * @throws IOException if the MBean UUID cannot be obtained due to + * I/O problems (communication failures etc...). + */ + public String getUUID() throws IOException; + +} diff --git a/src/share/classes/javax/management/namespace/JMXNamespacePermission.java b/src/share/classes/javax/management/namespace/JMXNamespacePermission.java new file mode 100644 index 000000000..5dd012672 --- /dev/null +++ b/src/share/classes/javax/management/namespace/JMXNamespacePermission.java @@ -0,0 +1,1474 @@ +/* + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.namespace; + +import javax.management.*; +import com.sun.jmx.mbeanserver.Util; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.security.Permission; + +/** + *

      A permission controlling access to MBeans located in namespaces. + * If a security manager has been set using {@link + * System#setSecurityManager}, most operations on an MBean mounted in a + * namespace require that the caller's permissions imply a + * JMXNamespacePermission appropriate for the operation. + * This is described in detail in the + * documentation for the + * JMXNamespace + * class.

      + * + *

      As with other {@link Permission} objects, + * a JMXNamespacePermission can represent either a permission that + * you have or a permission that you need. + * When a sensitive operation is being checked for permission, + * a JMXNamespacePermission is constructed + * representing the permission you need. The operation is only + * allowed if the permissions you have {@linkplain #implies imply} the + * permission you need.

      + * + *

      A JMXNamespacePermission contains four items of information:

      + * + *
        + * + *
      • The action.

        + *

        For a permission you need, + * this is one of the actions in the list below. For a permission you have, this is + * a comma-separated list of those actions, or *, + * representing all actions.

        + * + *

        The action is returned by {@link #getActions()}.

        + * + *
      • The MBean Server name.

        + * + *

        For a permission you need, this is the {@linkplain + * javax.management.MBeanServerFactory#getMBeanServerName + * name of the MBeanServer} + * from which the MBean is accessed.

        + * + *

        For a permission you have, this is either the {@linkplain + * javax.management.MBeanServerFactory#getMBeanServerName + * name of the MBeanServer} from which the MBean + * for which you have this permission is accessed, + * or a pattern against which that MBean Server name will be matched.
        + * An {@code mbeanServername} pattern can also be empty, or the single + * character {@code "*"}, both of which match any {@code MBeanServer} name. + * The string {@code "-"} doesn't match any MBeanServer name. + *

        + * + *

        Example:

        + *
        + *   // grant permission to invoke the operation "stop" on any MBean
        + *   // whose name matches "a//**//*:type=JMXConnectorServer" when
        + *   // accessed from any MBeanServer whose name matches myapp.*"
        + *   permission javax.management.namespace.JMXNamespacePermission "myapp.*::stop[a//**//*:type=JMXConnectorServer]", "invoke";
        + * 
        + * + *
      • The member.

        + * + *

        For a permission you need, this is the name of the attribute or + * operation you are accessing. For operations that do not reference + * an attribute or operation, the member is null.

        + * + *

        For a permission you have, this is either the name of an attribute + * or operation you can access, or it is empty or the single character + * "*", both of which grant access to any member.

        + * + *

        There is a special case for actions {@code registerMBean} and + * {@code instantiate}, where for a permission you need, {@code member} + * indicates the name of the class for which you are trying + * to create, instantiate, or register an MBean instance. For a + * permission you have, it is a pattern that will be matched against + * the full class name of the MBean being created, instantiated, or + * registered. + *

        + * + * + *
      • The object name.

        + * + *

        For a permission you need, this is the {@link ObjectName} of the + * MBean you are accessing. It is of the form {@code //} + * where {@code } is the name of the name space for which the + * permission is checked, and {@code } is the name of the MBean + * within that namespace. + *
        + * For operations that do not reference a + * single MBean, the object name is null. It is never an object + * name pattern. + *

        + * + *

        For a permission you have, this is the {@link ObjectName} of the + * MBean or MBeans you can access. It is of the form + * {@code //} + * where {@code } is the name of the name space for which the + * permission is checked, and + * {@code } is the name of the MBean + * within that namespace. Both {@code } and {@code } + * can be patterns. The object name + * may also be empty, which grants access to all MBeans whatever their + * name and namespace. + * When included in a namespace path the special path element + * ** matches any number of sub namespaces + * recursively, but only if used as a complete namespace path element, + * as in **//b//c//D:k=v or a//**//c//D:k=v + * - see below. + *

        + * + * + *
      + * + *

      If you have a JMXNamespacePermission, it allows operations only + * if all four of the items match.

      + * + *

      The MBeanServer name, + * member, and object name + * can be written together + * as a single string, which is the name of this permission. + * The name of the permission is the string returned by {@link + * java.security.Permission#getName() getName()}. + * The format of the string is:

      + * + *
      + * {@code ::[//]} + *
      + * + *

      + * The {@code } is optional. If omitted, {@code "*"} is + * assumed, and these three permission names + * are thus equivalent: + *

      + *
      + * {@code *::[//]}
      + * {@code ::[//]}
      + * {@code [//]}
      + *
      + *

      + * The {@code //} string can be in the form + * of a traditional ObjectName + * pattern - meaning that ? will match any single + * character, and * will match any sequence of characters, + * except {@value + * javax.management.namespace.JMXNamespaces#NAMESPACE_SEPARATOR} + * In addition, when included in a namespace path the special + * path element ** matches any number of sub namespaces + * recursively. + * A {@code //} string of the form + * **//*:* thus means that the permission is + * granted for all MBeans in all namespaces, recursively (see + * below for more details. + *

      + *

      Namespace permission checking may be tricky to configure, depending + * on whether the namespaces crossed to reach the MBean are local or + * remote.
      + * For instance, let a//b//D:k=v be an MBean exposing an + * attribute Foo. + * If namespace a is a plain JMXNamespace pointing to + * a local MBeanServer in the same JVM, then the permissions you need + * to get the attribute Foo will be: + *

      + *
      + *    // granting permission to access attribute 'Foo' of MBean a//b//D:k=v
      + *    // from MBeanServer named 'srv1'
      + *    // This permission will be checked by the MBeanServer that contains 'a'.
      + *    srv1::Foo[a//b//D:k=v]
      + *
      + *    // Since a is local, you also need the following additional permission,
      + *    // which will be checked by the MBeanServer 'srv2' that contains 'b':
      + *    //
      + *    // granting permission to access attribute 'Foo' of MBean b//D:k=v from
      + *    // 'srv2'
      + *    srv2::Foo[b//D:k=v]
      + * 
      + *

      On the other hand, if namespace a is a JMXRemoteNamespace + * pointing to an MBeanServer in a remote JVM, then the only permission you + * need to get the attribute Foo will be: + *

      + *
      + *    // granting permission to access attribute 'Foo' of MBean a//b//D:k=v
      + *    // from 'srv1'
      + *    srv1::Foo[a//b//D:k=v]
      + * 
      + *

      The namespace b resides in the remote JVM, and + * therefore the permissions concerning access to MBeans from + * namespace 'b' will only be checked in the remote JVM, if that JVM is + * configured to do so. + *

      + * + *

      The {@code } is written using the usual syntax for {@link + * ObjectName}. It may contain any legal characters, including + * ]. It is terminated by a ] character + * that is the last character in the string. + *

      + *

      Below are some examples of permission names:

      + *
      + *    // allows access to Foo in 'a//b//*:*' from any MBeanServer in the JVM.
      + *    Foo[a//b//*:*]
      + *
      + *    // allows access to Foo in all subnamespaces of 'a//b', but only for
      + *    // MBeanServers whose name matches 'myapp.*'
      + *    myapp.*::Foo[a//b//**//*:*]
      + *
      + *    // allows access to Foo from all namespaces in the MBeanServer named
      + *    // 'myapp.srv1' - but not recursively.
      + *    myapp.srv1::Foo[*//*:*]
      + * 
      + *

      For instance, the first two permissions listed above + * will let through {@code getAttribute("a//b//D:k=v","Foo");} in + * all MBeanServers, but will block access to + * {@code getAttribute("a//b//c//D:k=v","Foo");} in MBeanServers whose + * name do not start with {@code "myapp."}. + *

      + *

      Depending on how your namespace hierarchy + * is defined some of these wildcard permission names can be useful:

      + *
      + *    // allows access to Foo in all namespaces, recursively.
      + *    //
      + *    *::Foo[**//*:*]
      + *
      + *    // This permission name is the equivalent to the permission names above:
      + *    // Foo[**//*:*] and Foo[] are equivalent.
      + *    //
      + *    Foo[]
      + *
      + *    // This permission name is the equivalent to the two permission names
      + *    // above:
      + *    // Foo[**//*:*], Foo[], Foo are equivalent.
      + *    //
      + *    Foo
      + *
      + *    // allows access to Foo from all namespaces - but not recursively.
      + *    // This wildcard permission complements the previous one: it allows
      + *    // access to 'Foo' from an MBean directly registered in any local namespace.
      + *    //
      + *    Foo[*//*:*]
      + *
      + * 
      + *

      Note on wildcards: In an object name pattern, a path element + * of exactly ** corresponds to a meta + * wildcard that will match any number of sub namespaces. Hence:

      + *
        + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
        patternmatchesdoesn't match
        **//D:k=va//D:k=v
        + * a//b//D:k=v
        + * a//b//c//D:k=v
        D:k=v
        a//**//D:k=va//b//D:k=v
        + * a//b//c//D:k=v
        b//b//c//D:k=v
        + * a//D:k=v
        + * D:k=v
        a//**//e//D:k=va//b//e//D:k=v
        + * a//b//c//e//D:k=v
        a//b//c//c//D:k=v
        + * b//b//c//e//D:k=v
        + * a//e//D:k=v
        + * e//D:k=v
        a//b**//e//D:k=va//b//e//D:k=va//b//c//e//D:k=v
        + * because in that case b**
        + * is not a meta-wildcard - and b**
        + * is thus equivalent to b*.
        + *
      + * + *

      If {@code ::} is omitted, then one of + * member or object name may be omitted. + * If the object name is omitted, + * the [] may be too (but does not have to be). It is + * not legal to omit all items, that is to have a name + * which is the empty string.

      + *

      If {@code } is present, it must be followed by + * the {@code "::"} separator - otherwise it will be interpreted as + * a {@code member name}. + *

      + * + *

      + * One or more of the MBean Server name, + * member + * or object name may be the character "-", + * which is equivalent to a null value. A null value is implied by + * any value (including another null value) but does not imply any + * other value. + *

      + * + *

      The possible actions are these:

      + * + *
        + *
      • addNotificationListener
      • + *
      • getAttribute
      • + *
      • getClassLoader
      • + *
      • getClassLoaderFor
      • + *
      • getClassLoaderRepository
      • + *
      • getMBeanInfo
      • + *
      • getObjectInstance
      • + *
      • instantiate
      • + *
      • invoke
      • + *
      • isInstanceOf
      • + *
      • queryMBeans
      • + *
      • queryNames
      • + *
      • registerMBean
      • + *
      • removeNotificationListener
      • + *
      • setAttribute
      • + *
      • unregisterMBean
      • + *
      + * + *

      In a comma-separated list of actions, spaces are allowed before + * and after each action.

      + * + * @since 1.7 + */ +public class JMXNamespacePermission extends Permission { + + private static final long serialVersionUID = -2416928705275160661L; + + private static final String WILDPATH = "**" + + JMXNamespaces.NAMESPACE_SEPARATOR + "*"; + + /** + * Actions list. + */ + private static final int AddNotificationListener = 0x00001; + private static final int GetAttribute = 0x00002; + private static final int GetClassLoader = 0x00004; + private static final int GetClassLoaderFor = 0x00008; + private static final int GetClassLoaderRepository = 0x00010; + // No GetDomains because it is not possible to route a call to + // getDomains() on a NamespaceInterceptor - getDomains() doesn't + // have any ObjectName. + // private static final int GetDomains = 0x00020; + private static final int GetMBeanInfo = 0x00040; + private static final int GetObjectInstance = 0x00080; + private static final int Instantiate = 0x00100; + private static final int Invoke = 0x00200; + private static final int IsInstanceOf = 0x00400; + private static final int QueryMBeans = 0x00800; + private static final int QueryNames = 0x01000; + private static final int RegisterMBean = 0x02000; + private static final int RemoveNotificationListener = 0x04000; + private static final int SetAttribute = 0x08000; + private static final int UnregisterMBean = 0x10000; + + /** + * No actions. + */ + private static final int NONE = 0x00000; + + /** + * All actions. + */ + // No GetDomains because it is not possible to route a call to + // getDomains() on a NamespaceInterceptor - getDomains() doesn't + // have any ObjectName. + // + private static final int ALL = + AddNotificationListener | + GetAttribute | + GetClassLoader | + GetClassLoaderFor | + GetClassLoaderRepository | + GetMBeanInfo | + GetObjectInstance | + Instantiate | + Invoke | + IsInstanceOf | + QueryMBeans | + QueryNames | + RegisterMBean | + RemoveNotificationListener | + SetAttribute | + UnregisterMBean; + + /** + * The actions string. + */ + private String actions; + + /** + * The actions mask. + */ + private transient int mask; + + /** + * The name of the MBeanServer in which this permission is checked, or + * granted. If null, is implied by any MBean server name + * but does not imply any non-null MBean server name. + */ + private transient String mbeanServerName; + + /** + * The member that must match. If null, is implied by any member + * but does not imply any non-null member. + */ + private transient String member; + + /** + * The objectName that must match. If null, is implied by any + * objectName but does not imply any non-null objectName. + */ + private transient ObjectName objectName; + + /** + * If objectName is missing from name, then allnames will be + * set to true. + */ + private transient boolean allnames = false; + + /** + * Parse actions parameter. + */ + private void parseActions() { + + int amask; + + if (actions == null) + throw new IllegalArgumentException("JMXNamespaceAccessPermission: " + + "actions can't be null"); + if (actions.equals("")) + throw new IllegalArgumentException("JMXNamespaceAccessPermission: " + + "actions can't be empty"); + + amask = getMask(actions); + + if ((amask & ALL) != amask) + throw new IllegalArgumentException("Invalid actions mask"); + if (amask == NONE) + throw new IllegalArgumentException("Invalid actions mask"); + this.mask = amask; + } + + /** + * Parse name parameter. + */ + private void parseName() { + String name = getName(); + + if (name == null) + throw new IllegalArgumentException("JMXNamespaceAccessPermission name " + + "cannot be null"); + + if (name.equals("")) + throw new IllegalArgumentException("JMXNamespaceAccessPermission name " + + "cannot be empty"); + final int sepIndex = name.indexOf("::"); + if (sepIndex < 0) { + setMBeanServerName("*"); + } else { + setMBeanServerName(name.substring(0,sepIndex)); + } + + /* The name looks like "mbeanServerName::member[objectname]". + We subtract elements from the right as we parse, so after + parsing the objectname we have "class#member" and after parsing the + member we have "class". Each element is optional. */ + + // Parse ObjectName + + final int start = (sepIndex<0)?0:sepIndex+2; + int openingBracket = name.indexOf("[",start); + if (openingBracket == -1) { + // If "[on]" missing then ObjectName("*:*") + // + objectName = null; + allnames = true; + openingBracket=name.length(); + } else { + if (!name.endsWith("]")) { + throw new IllegalArgumentException("JMXNamespaceAccessPermission: " + + "The ObjectName in the " + + "target name must be " + + "included in square " + + "brackets"); + } else { + // Create ObjectName + // + String on = name.substring(openingBracket + 1, + name.length() - 1); + try { + // If "[]" then allnames are implied + // + final ObjectName target; + final boolean all; + if (on.equals("")) { + target = null; + all = true; + } else if (on.equals("-")) { + target = null; + all = false; + } else { + target = new ObjectName(on); + all = false; + } + setObjectName(target,all); + } catch (MalformedObjectNameException e) { + throw new IllegalArgumentException( + "JMXNamespaceAccessPermission: " + + "The target name does " + + "not specify a valid " + + "ObjectName", e); + } + } + } + + final String memberName = name.substring(start,openingBracket); + setMember(memberName); + } + + private void setObjectName(ObjectName target, boolean all) { + if (target != null && + !Util.wildpathmatch(target.getDomain(), WILDPATH)) { + throw new IllegalArgumentException( + "The target name does not contain " + + "any namespace: "+String.valueOf(target)); + } else if (target != null) { + final String domain = target.getDomain(); + final int seplen = JMXNamespaces.NAMESPACE_SEPARATOR.length(); + final int sepc = domain.indexOf(JMXNamespaces.NAMESPACE_SEPARATOR); + if (sepc < 0 || (sepc+seplen)==domain.length()) { + throw new IllegalArgumentException(String.valueOf(target)+ + ": no namespace in domain"); + } + } + objectName = target; + allnames = all; + } + + /** + * Assign fields based on className, member, and objectName + * parameters. + */ +// private void initName(String namespaceName, String member, +// ObjectName objectName, boolean allnames) { +// setNamespace(namespaceName); + private void initName(String mbeanServerName, String member, + ObjectName mbeanName, boolean all) { + setMBeanServerName(mbeanServerName); + setMember(member); + setObjectName(mbeanName, all); + } + + private void setMBeanServerName(String mbeanServerName) { + if (mbeanServerName == null || mbeanServerName.equals("-")) { + this.mbeanServerName = null; + } else if (mbeanServerName.equals("")) { + this.mbeanServerName = "*"; + } else { + this.mbeanServerName = mbeanServerName; + } + } + + private void setMember(String member) { + if (member == null || member.equals("-")) + this.member = null; + else if (member.equals("")) + this.member = "*"; + else + this.member = member; + } + + /** + *

      Create a new JMXNamespacePermission object with the + * specified target name and actions.

      + * + *

      The target name is of the form + * "mbeanServerName::member[objectName]" where each part is + * optional. This target name must not be empty or null. + * If objectName is present, it is of + * the form namespace//MBeanName. + *

      + *

      + * For a permission you need, {@code mbeanServerName} is the + * name of the MBeanServer from + * which {@code objectName} is being accessed. + *

      + *

      + * For a permission you have, {@code mbeanServerName} is the + * name of the MBeanServer from + * which access to {@code objectName} is granted. + * It can also be a pattern, and if omitted, {@code "*"} is assumed, + * meaning that access to {@code objectName} is granted in all + * MBean servers in the JVM. + *

      + * + *

      The actions parameter contains a comma-separated list of the + * desired actions granted on the target name. It must not be + * empty or null.

      + * + * @param name the triplet "mbeanServerName::member[objectName]". + * If objectName is present, it is of + * the form namespace//MBeanName. + * @param actions the action string. + * + * @exception IllegalArgumentException if the name or + * actions is invalid. + */ + public JMXNamespacePermission(String name, String actions) { + super(name); + + parseName(); + + this.actions = actions; + parseActions(); + } + + /** + *

      Create a new JMXNamespacePermission object with the specified + * target name (namespace name, member, object name) and actions.

      + * + *

      The {@code MBeanServer} name, member and object name + * parameters define a target name of the form + * "mbeanServerName::member[objectName]" where each + * part is optional. This will be the result of {@link #getName()} on the + * resultant JMXNamespacePermission. + * If the mbeanServerName is empty or exactly {@code "*"}, then + * "{@code mbeanServerName::}" is omitted in that result. + *

      + * + *

      The actions parameter contains a comma-separated list of the + * desired actions granted on the target name. It must not be + * empty or null.

      + * + * @param mbeanServerName the name of the {@code MBeanServer} to which this + * permission applies. + * May be null or "-", which represents an MBeanServer name + * that is implied by any MBeanServer name but does not imply any other + * MBeanServer name. + * @param member the member to which this permission applies. May + * be null or "-", which represents a member that is + * implied by any member but does not imply any other member. + * @param objectName the object name to which this permission + * applies. + * May be null, which represents an object name that is + * implied by any object name but does not imply any other object + * name. If not null, the {@code objectName} must be of the + * form {@code //} - where {@code } + * can be a domain pattern, and {@code } can be an ObjectName + * pattern. + * For a permission you need, {@code } is the name of the + * name space for which the permission is checked, and {@code } + * is the name of the MBean in that namespace. + * The composed name {@code //} thus represents the + * name of the MBean as seen by the {@code mbeanServerName} containing + * {@code }. + * + * @param actions the action string. + */ + public JMXNamespacePermission( + String mbeanServerName, + String member, + ObjectName objectName, + String actions) { + this(mbeanServerName, member, objectName, false, actions); +// this(member, objectName, false, actions); + } + + /** + *

      Create a new JMXNamespacePermission object with the specified + * MBean Server name, member, and actions.

      + * + *

      The {@code MBeanServer} name and member + * parameters define a target name of the form + * "mbeanServerName::member[]" where each + * part is optional. This will be the result of {@link #getName()} on the + * resultant JMXNamespacePermission. + * If the mbeanServerName is empty or exactly {@code "*"}, then + * "{@code mbeanServerName::}" is omitted in that result. + *

      + * + *

      The actions parameter contains a comma-separated list of the + * desired actions granted on the target name. It must not be + * empty or null.

      + * + * @param mbeanServerName the name of the {@code MBeanServer} to which this + * permission applies. + * May be null or "-", which represents an MBeanServer name + * that is implied by any MBeanServer name but does not imply any other + * MBeanServer name. + * @param member the member to which this permission applies. May + * be null or "-", which represents a member that is + * implied by any member but does not imply any other member. + * @param actions the action string. + */ + public JMXNamespacePermission(String mbeanServerName, + String member, + String actions) { + this(mbeanServerName,member,null,true,actions); + // this(member,null,allnames,actions); + } + + /** + *

      Create a new JMXNamespacePermission object with the specified + * target name (namespace name, member, object name) and actions.

      + * + *

      The MBean Server name, member and object name parameters define a + * target name of the form + * "mbeanServerName::member[objectName]" where each part is + * optional. This will be the result of {@link + * java.security.Permission#getName() getName()} on the + * resultant JMXNamespacePermission.

      + * + *

      The actions parameter contains a comma-separated list of the + * desired actions granted on the target name. It must not be + * empty or null.

      + * + * @param mbeanServerName the name of the {@code MBeanServer} to which this + * permission applies. + * May be null or "-", which represents an MBeanServer name + * that is implied by any MBeanServer name but does not imply any other + * MBeanServer name. + * @param member the member to which this permission applies. May + * be null or "-", which represents a member that is + * implied by any member but does not imply any other member. + * @param objectName the object name to which this permission + * applies. If null, and allnames is false, represents an object + * name that is implied by any object name but does not imply any + * other object name. Otherwise, if allnames is true, it represents + * a meta wildcard that matches all object names. It is equivalent to + * a missing objectName ("[]") in the {@link + * java.security.Permission#getName() name} property. + * @param allnames represent a meta wildcard indicating that the + * objectName was not specified. This implies all objectnames + * that match "*:*" and all object names that match + * "**//*:*" + * @param actions the action string. + */ + private JMXNamespacePermission(String mbeanServerName, + String member, + ObjectName objectName, + boolean allnames, + String actions) { + + super(makeName(mbeanServerName, + member, objectName, allnames)); + initName(mbeanServerName, + member, objectName, allnames); + + this.actions = actions; + parseActions(); + } + + private static String makeName(String mbeanServerName, + String memberName, ObjectName objName, boolean allMBeans) { + final StringBuilder name = new StringBuilder(); + if (mbeanServerName == null) + mbeanServerName = "-"; + if (!mbeanServerName.equals("") && !mbeanServerName.equals("*")) + name.append(mbeanServerName).append("::"); + if (memberName == null) + memberName = "-"; + name.append(memberName); + if (objName == null) { + if (allMBeans) + name.append("[]"); + else + name.append("[-]"); + } else { + final String domain = objName.getDomain(); + final int seplen = JMXNamespaces.NAMESPACE_SEPARATOR.length(); + final int sepc = domain.indexOf(JMXNamespaces.NAMESPACE_SEPARATOR); + if (sepc < 0 || (sepc+seplen)==domain.length()) { + throw new IllegalArgumentException(String.valueOf(objName)+ + ": no namespace in domain"); + } + final String can = objName.getCanonicalName(); + name.append("[").append(can).append("]"); + } + return name.toString(); + } + + /** + * Returns the "canonical string representation" of the actions. That is, + * this method always returns actions in alphabetical order. + * + * @return the canonical string representation of the actions. + */ + public String getActions() { + + if (actions == null) + actions = getActions(this.mask); + + return actions; + } + + /** + * Returns the "canonical string representation" + * of the actions from the mask. + */ + private static String getActions(int mask) { + final StringBuilder sb = new StringBuilder(); + boolean comma = false; + + if ((mask & AddNotificationListener) == AddNotificationListener) { + comma = true; + sb.append("addNotificationListener"); + } + + if ((mask & GetAttribute) == GetAttribute) { + if (comma) sb.append(','); + else comma = true; + sb.append("getAttribute"); + } + + if ((mask & GetClassLoader) == GetClassLoader) { + if (comma) sb.append(','); + else comma = true; + sb.append("getClassLoader"); + } + + if ((mask & GetClassLoaderFor) == GetClassLoaderFor) { + if (comma) sb.append(','); + else comma = true; + sb.append("getClassLoaderFor"); + } + + if ((mask & GetClassLoaderRepository) == GetClassLoaderRepository) { + if (comma) sb.append(','); + else comma = true; + sb.append("getClassLoaderRepository"); + } + + if ((mask & GetMBeanInfo) == GetMBeanInfo) { + if (comma) sb.append(','); + else comma = true; + sb.append("getMBeanInfo"); + } + + if ((mask & GetObjectInstance) == GetObjectInstance) { + if (comma) sb.append(','); + else comma = true; + sb.append("getObjectInstance"); + } + + if ((mask & Instantiate) == Instantiate) { + if (comma) sb.append(','); + else comma = true; + sb.append("instantiate"); + } + + if ((mask & Invoke) == Invoke) { + if (comma) sb.append(','); + else comma = true; + sb.append("invoke"); + } + + if ((mask & IsInstanceOf) == IsInstanceOf) { + if (comma) sb.append(','); + else comma = true; + sb.append("isInstanceOf"); + } + + if ((mask & QueryMBeans) == QueryMBeans) { + if (comma) sb.append(','); + else comma = true; + sb.append("queryMBeans"); + } + + if ((mask & QueryNames) == QueryNames) { + if (comma) sb.append(','); + else comma = true; + sb.append("queryNames"); + } + + if ((mask & RegisterMBean) == RegisterMBean) { + if (comma) sb.append(','); + else comma = true; + sb.append("registerMBean"); + } + + if ((mask & RemoveNotificationListener) == RemoveNotificationListener) { + if (comma) sb.append(','); + else comma = true; + sb.append("removeNotificationListener"); + } + + if ((mask & SetAttribute) == SetAttribute) { + if (comma) sb.append(','); + else comma = true; + sb.append("setAttribute"); + } + + if ((mask & UnregisterMBean) == UnregisterMBean) { + if (comma) sb.append(','); + else comma = true; + sb.append("unregisterMBean"); + } + + // No GetDomains because it is not possible to route a call to + // getDomains() on a NamespaceInterceptor - getDomains() doesn't + // have any ObjectName. + + return sb.toString(); + } + + @Override + public int hashCode() { + return this.getName().hashCode() + this.getActions().hashCode(); + } + + /** + * Converts an action String to an integer action mask. + * + * @param action the action string. + * @return the action mask. + */ + private static int getMask(String action) { + + /* + * BE CAREFUL HERE! PARSING ORDER IS IMPORTANT IN THIS ALGORITHM. + * + * The 'string length' test must be performed for the lengthiest + * strings first. + * + * In this permission if the "unregisterMBean" string length test is + * performed after the "registerMBean" string length test the algorithm + * considers the 'unregisterMBean' action as being the 'registerMBean' + * action and a parsing error is returned. + */ + + int mask = NONE; + + if (action == null) { + return mask; + } + + if (action.equals("*")) { + return ALL; + } + + char[] a = action.toCharArray(); + + int i = a.length - 1; + if (i < 0) + return mask; + + while (i != -1) { + char c; + + // skip whitespace + while ((i!=-1) && ((c = a[i]) == ' ' || + c == '\r' || + c == '\n' || + c == '\f' || + c == '\t')) + i--; + + // check for the known strings + int matchlen; + + // No GetDomains because it is not possible to route a call to + // getDomains() on a NamespaceInterceptor - getDomains() doesn't + // have any ObjectName. + + if (i >= 25 && /* removeNotificationListener */ + (a[i-25] == 'r') && + (a[i-24] == 'e') && + (a[i-23] == 'm') && + (a[i-22] == 'o') && + (a[i-21] == 'v') && + (a[i-20] == 'e') && + (a[i-19] == 'N') && + (a[i-18] == 'o') && + (a[i-17] == 't') && + (a[i-16] == 'i') && + (a[i-15] == 'f') && + (a[i-14] == 'i') && + (a[i-13] == 'c') && + (a[i-12] == 'a') && + (a[i-11] == 't') && + (a[i-10] == 'i') && + (a[i-9] == 'o') && + (a[i-8] == 'n') && + (a[i-7] == 'L') && + (a[i-6] == 'i') && + (a[i-5] == 's') && + (a[i-4] == 't') && + (a[i-3] == 'e') && + (a[i-2] == 'n') && + (a[i-1] == 'e') && + (a[i] == 'r')) { + matchlen = 26; + mask |= RemoveNotificationListener; + } else if (i >= 23 && /* getClassLoaderRepository */ + (a[i-23] == 'g') && + (a[i-22] == 'e') && + (a[i-21] == 't') && + (a[i-20] == 'C') && + (a[i-19] == 'l') && + (a[i-18] == 'a') && + (a[i-17] == 's') && + (a[i-16] == 's') && + (a[i-15] == 'L') && + (a[i-14] == 'o') && + (a[i-13] == 'a') && + (a[i-12] == 'd') && + (a[i-11] == 'e') && + (a[i-10] == 'r') && + (a[i-9] == 'R') && + (a[i-8] == 'e') && + (a[i-7] == 'p') && + (a[i-6] == 'o') && + (a[i-5] == 's') && + (a[i-4] == 'i') && + (a[i-3] == 't') && + (a[i-2] == 'o') && + (a[i-1] == 'r') && + (a[i] == 'y')) { + matchlen = 24; + mask |= GetClassLoaderRepository; + } else if (i >= 22 && /* addNotificationListener */ + (a[i-22] == 'a') && + (a[i-21] == 'd') && + (a[i-20] == 'd') && + (a[i-19] == 'N') && + (a[i-18] == 'o') && + (a[i-17] == 't') && + (a[i-16] == 'i') && + (a[i-15] == 'f') && + (a[i-14] == 'i') && + (a[i-13] == 'c') && + (a[i-12] == 'a') && + (a[i-11] == 't') && + (a[i-10] == 'i') && + (a[i-9] == 'o') && + (a[i-8] == 'n') && + (a[i-7] == 'L') && + (a[i-6] == 'i') && + (a[i-5] == 's') && + (a[i-4] == 't') && + (a[i-3] == 'e') && + (a[i-2] == 'n') && + (a[i-1] == 'e') && + (a[i] == 'r')) { + matchlen = 23; + mask |= AddNotificationListener; + } else if (i >= 16 && /* getClassLoaderFor */ + (a[i-16] == 'g') && + (a[i-15] == 'e') && + (a[i-14] == 't') && + (a[i-13] == 'C') && + (a[i-12] == 'l') && + (a[i-11] == 'a') && + (a[i-10] == 's') && + (a[i-9] == 's') && + (a[i-8] == 'L') && + (a[i-7] == 'o') && + (a[i-6] == 'a') && + (a[i-5] == 'd') && + (a[i-4] == 'e') && + (a[i-3] == 'r') && + (a[i-2] == 'F') && + (a[i-1] == 'o') && + (a[i] == 'r')) { + matchlen = 17; + mask |= GetClassLoaderFor; + } else if (i >= 16 && /* getObjectInstance */ + (a[i-16] == 'g') && + (a[i-15] == 'e') && + (a[i-14] == 't') && + (a[i-13] == 'O') && + (a[i-12] == 'b') && + (a[i-11] == 'j') && + (a[i-10] == 'e') && + (a[i-9] == 'c') && + (a[i-8] == 't') && + (a[i-7] == 'I') && + (a[i-6] == 'n') && + (a[i-5] == 's') && + (a[i-4] == 't') && + (a[i-3] == 'a') && + (a[i-2] == 'n') && + (a[i-1] == 'c') && + (a[i] == 'e')) { + matchlen = 17; + mask |= GetObjectInstance; + } else if (i >= 14 && /* unregisterMBean */ + (a[i-14] == 'u') && + (a[i-13] == 'n') && + (a[i-12] == 'r') && + (a[i-11] == 'e') && + (a[i-10] == 'g') && + (a[i-9] == 'i') && + (a[i-8] == 's') && + (a[i-7] == 't') && + (a[i-6] == 'e') && + (a[i-5] == 'r') && + (a[i-4] == 'M') && + (a[i-3] == 'B') && + (a[i-2] == 'e') && + (a[i-1] == 'a') && + (a[i] == 'n')) { + matchlen = 15; + mask |= UnregisterMBean; + } else if (i >= 13 && /* getClassLoader */ + (a[i-13] == 'g') && + (a[i-12] == 'e') && + (a[i-11] == 't') && + (a[i-10] == 'C') && + (a[i-9] == 'l') && + (a[i-8] == 'a') && + (a[i-7] == 's') && + (a[i-6] == 's') && + (a[i-5] == 'L') && + (a[i-4] == 'o') && + (a[i-3] == 'a') && + (a[i-2] == 'd') && + (a[i-1] == 'e') && + (a[i] == 'r')) { + matchlen = 14; + mask |= GetClassLoader; + } else if (i >= 12 && /* registerMBean */ + (a[i-12] == 'r') && + (a[i-11] == 'e') && + (a[i-10] == 'g') && + (a[i-9] == 'i') && + (a[i-8] == 's') && + (a[i-7] == 't') && + (a[i-6] == 'e') && + (a[i-5] == 'r') && + (a[i-4] == 'M') && + (a[i-3] == 'B') && + (a[i-2] == 'e') && + (a[i-1] == 'a') && + (a[i] == 'n')) { + matchlen = 13; + mask |= RegisterMBean; + } else if (i >= 11 && /* getAttribute */ + (a[i-11] == 'g') && + (a[i-10] == 'e') && + (a[i-9] == 't') && + (a[i-8] == 'A') && + (a[i-7] == 't') && + (a[i-6] == 't') && + (a[i-5] == 'r') && + (a[i-4] == 'i') && + (a[i-3] == 'b') && + (a[i-2] == 'u') && + (a[i-1] == 't') && + (a[i] == 'e')) { + matchlen = 12; + mask |= GetAttribute; + } else if (i >= 11 && /* getMBeanInfo */ + (a[i-11] == 'g') && + (a[i-10] == 'e') && + (a[i-9] == 't') && + (a[i-8] == 'M') && + (a[i-7] == 'B') && + (a[i-6] == 'e') && + (a[i-5] == 'a') && + (a[i-4] == 'n') && + (a[i-3] == 'I') && + (a[i-2] == 'n') && + (a[i-1] == 'f') && + (a[i] == 'o')) { + matchlen = 12; + mask |= GetMBeanInfo; + } else if (i >= 11 && /* isInstanceOf */ + (a[i-11] == 'i') && + (a[i-10] == 's') && + (a[i-9] == 'I') && + (a[i-8] == 'n') && + (a[i-7] == 's') && + (a[i-6] == 't') && + (a[i-5] == 'a') && + (a[i-4] == 'n') && + (a[i-3] == 'c') && + (a[i-2] == 'e') && + (a[i-1] == 'O') && + (a[i] == 'f')) { + matchlen = 12; + mask |= IsInstanceOf; + } else if (i >= 11 && /* setAttribute */ + (a[i-11] == 's') && + (a[i-10] == 'e') && + (a[i-9] == 't') && + (a[i-8] == 'A') && + (a[i-7] == 't') && + (a[i-6] == 't') && + (a[i-5] == 'r') && + (a[i-4] == 'i') && + (a[i-3] == 'b') && + (a[i-2] == 'u') && + (a[i-1] == 't') && + (a[i] == 'e')) { + matchlen = 12; + mask |= SetAttribute; + } else if (i >= 10 && /* instantiate */ + (a[i-10] == 'i') && + (a[i-9] == 'n') && + (a[i-8] == 's') && + (a[i-7] == 't') && + (a[i-6] == 'a') && + (a[i-5] == 'n') && + (a[i-4] == 't') && + (a[i-3] == 'i') && + (a[i-2] == 'a') && + (a[i-1] == 't') && + (a[i] == 'e')) { + matchlen = 11; + mask |= Instantiate; + } else if (i >= 10 && /* queryMBeans */ + (a[i-10] == 'q') && + (a[i-9] == 'u') && + (a[i-8] == 'e') && + (a[i-7] == 'r') && + (a[i-6] == 'y') && + (a[i-5] == 'M') && + (a[i-4] == 'B') && + (a[i-3] == 'e') && + (a[i-2] == 'a') && + (a[i-1] == 'n') && + (a[i] == 's')) { + matchlen = 11; + mask |= QueryMBeans; + } else if (i >= 9 && /* queryNames */ + (a[i-9] == 'q') && + (a[i-8] == 'u') && + (a[i-7] == 'e') && + (a[i-6] == 'r') && + (a[i-5] == 'y') && + (a[i-4] == 'N') && + (a[i-3] == 'a') && + (a[i-2] == 'm') && + (a[i-1] == 'e') && + (a[i] == 's')) { + matchlen = 10; + mask |= QueryNames; + } else if (i >= 5 && /* invoke */ + (a[i-5] == 'i') && + (a[i-4] == 'n') && + (a[i-3] == 'v') && + (a[i-2] == 'o') && + (a[i-1] == 'k') && + (a[i] == 'e')) { + matchlen = 6; + mask |= Invoke; + } else { + // parse error + throw new IllegalArgumentException("Invalid permission: " + + action); + } + + // make sure we didn't just match the tail of a word + // like "ackbarfaccept". Also, skip to the comma. + boolean seencomma = false; + while (i >= matchlen && !seencomma) { + switch(a[i-matchlen]) { + case ',': + seencomma = true; + break; + case ' ': case '\r': case '\n': + case '\f': case '\t': + break; + default: + throw new IllegalArgumentException("Invalid permission: " + + action); + } + i--; + } + + // point i at the location of the comma minus one (or -1). + i -= matchlen; + } + + return mask; + } + + /** + *

      Checks if this JMXNamespacePermission object "implies" the + * specified permission.

      + * + *

      More specifically, this method returns true if:

      + * + *
        + * + *
      • p is an instance of JMXNamespacePermission; and
      • + * + *
      • p has a null mbeanServerName or p's mbeanServerName + * matches this object's mbeanServerName; and
      • + * + *
      • p has a null member or p's member matches this + * object's member; and
      • + * + *
      • p has a null object name or p's + * object name matches this object's object name; and
      • + * + *
      • p's actions are a subset of this object's actions
      • + * + *
      + * + *

      If this object's mbeanServerName is a pattern, then p's + * mbeanServerName is matched against that pattern. An empty + * mbeanServerName is equivalent to "{@code *}". A null + * mbeanServerName is equivalent to "{@code -}".

      + *

      If this object's mbeanServerName is "*" or is + * empty, p's mbeanServerName always matches it.

      + * + *

      If this object's member is "*", p's + * member always matches it.

      + * + *

      If this object's objectName n1 is an object name pattern, + * p's objectName n2 matches it if + * {@link ObjectName#equals n1.equals(n2)} or if + * {@link ObjectName#apply n1.apply(n2)}.

      + * + *

      A permission that includes the queryMBeans action + * is considered to include queryNames as well.

      + * + * @param p the permission to check against. + * @return true if the specified permission is implied by this object, + * false if not. + */ + public boolean implies(Permission p) { + if (!(p instanceof JMXNamespacePermission)) + return false; + + JMXNamespacePermission that = (JMXNamespacePermission) p; + + // Actions + // + // The actions in 'this' permission must be a + // superset of the actions in 'that' permission + // + + /* "queryMBeans" implies "queryNames" */ + if ((this.mask & QueryMBeans) == QueryMBeans) { + if (((this.mask | QueryNames) & that.mask) != that.mask) { + //System.out.println("action [with QueryNames] does not imply"); + return false; + } + } else { + if ((this.mask & that.mask) != that.mask) { + //System.out.println("action does not imply"); + return false; + } + } + + // Target name + // + // The 'mbeanServerName' check is true iff: + // 1) the mbeanServerName in 'this' permission is omitted or "*", or + // 2) the mbeanServerName in 'that' permission is omitted or "*", or + // 3) the mbeanServerName in 'this' permission does pattern + // matching with the mbeanServerName in 'that' permission. + // + // The 'member' check is true iff: + // 1) the member in 'this' member is omitted or "*", or + // 2) the member in 'that' member is omitted or "*", or + // 3) the member in 'this' permission equals the member in + // 'that' permission. + // + // The 'object name' check is true iff: + // 1) the object name in 'this' permission is omitted, or + // 2) the object name in 'that' permission is omitted, or + // 3) the object name in 'this' permission does pattern + // matching with the object name in 'that' permission. + // + + if (that.mbeanServerName == null) { + // bottom is implied + } else if (this.mbeanServerName == null) { + // bottom implies nothing but itself + return false; + } else if (that.mbeanServerName.equals(this.mbeanServerName)) { + // exact match + } else if (!Util.wildmatch(that.mbeanServerName,this.mbeanServerName)) { + return false; // no match + } + + /* Check if this.member implies that.member */ + + if (that.member == null) { + // bottom is implied + } else if (this.member == null) { + // bottom implies nothing but itself + return false; + } else if (this.member.equals("*")) { + // wildcard implies everything (including itself) + } else if (this.member.equals(that.member)) { + // exact match + } else if (!Util.wildmatch(that.member,this.member)) { + return false; // no match + } + + /* Check if this.objectName implies that.objectName */ + + if (that.objectName == null) { + // bottom is implied + } else if (this.objectName == null) { + // bottom implies nothing but itself + if (allnames == false) return false; + } else if (!this.objectName.apply(that.objectName)) { + /* ObjectName.apply returns false if that.objectName is a + wildcard so we also allow equals for that case. This + never happens during real permission checks, but means + the implies relation is reflexive. */ + if (!this.objectName.equals(that.objectName)) + return false; + } + + return true; + } + + /** + * Checks two JMXNamespacePermission objects for equality. Checks + * that obj is an JMXNamespacePermission, and has the same + * name and actions as this object. + *

      + * @param obj the object we are testing for equality with this object. + * @return true if obj is an JMXNamespacePermission, and has the + * same name and actions as this JMXNamespacePermission object. + */ + public boolean equals(Object obj) { + if (obj == this) + return true; + + if (! (obj instanceof JMXNamespacePermission)) + return false; + + JMXNamespacePermission that = (JMXNamespacePermission) obj; + + return (this.mask == that.mask) && + (this.getName().equals(that.getName())); + } + + /** + * Deserialize this object based on its name and actions. + */ + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException { + in.defaultReadObject(); + parseName(); + parseActions(); + } +} diff --git a/src/share/classes/javax/management/namespace/JMXNamespaceView.java b/src/share/classes/javax/management/namespace/JMXNamespaceView.java new file mode 100644 index 000000000..eacc0f4bc --- /dev/null +++ b/src/share/classes/javax/management/namespace/JMXNamespaceView.java @@ -0,0 +1,300 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.namespace; + +import java.io.IOException; +import java.util.Set; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +/** + * This class makes it possible to navigate easily within a hierarchical + * namespace view. + * + *

      + * MBeanServerConnnection rootConnection = ...;
      + *
      + * // create a view at the local root of the namespace hierarchy.
      + * //
      + * JMXNamespaceView view = new JMXNamespaceView(rootConnection);
      + *
      + * // list all top level namespaces
      + * String[] list = view.list();
      + *
      + * // select one namespace from the list
      + * String whereToGo = ... ;
      + *
      + * // go down to the selected namespace:
      + * view = view.down(whereToGo);
      + * System.out.println("I am now in: " + view.where());
      + * System.out.println("I can see these MBeans:" +
      + *    view.getMBeanServerConnection().queryNames(null,null));
      + *
      + * // list sub namespaces in current view ('whereToGo')
      + * list = view.list();
      + * System.out.println("Here are the sub namespaces of "+view.where()+": "+
      + *                    Arrays.toString(list));
      + *
      + * // go up one level
      + * view = view.up();
      + * System.out.println("I am now back to: " +
      + *    (view.isRoot() ? "root namespace" : view.where()));
      + * 
      + * @since 1.7 + */ +public class JMXNamespaceView { + + private static final ObjectName ALL_NAMESPACES; + static { + try { + ALL_NAMESPACES = ObjectName.getInstance("*" + + JMXNamespaces.NAMESPACE_SEPARATOR + ":"+ + JMXNamespace.TYPE_ASSIGNMENT); + } catch (MalformedObjectNameException x) { + throw new ExceptionInInitializerError(x); + } + } + private static final int NAMESPACE_SEPARATOR_LENGTH = + JMXNamespaces.NAMESPACE_SEPARATOR.length(); + + private final JMXNamespaceView parent; + private final MBeanServerConnection here; + private final String where; + + private static MBeanServerConnection checkRoot(MBeanServerConnection root) { + if (root == null) + throw new IllegalArgumentException( + "namespaceRoot: null is not a valid value"); + return root; + } + + /** + * Creates a view at the top of a JMX namespace hierarchy. + * @param namespaceRoot The {@code MBeanServerConnection} at the + * top of the hierarchy. + */ + public JMXNamespaceView(MBeanServerConnection namespaceRoot) { + this(null,checkRoot(namespaceRoot),""); + } + + // This constructor should remain private. A user can only create + // JMXNamespaceView at the top of the hierarchy. + // JMXNamespaceView sub nodes are created by their parent nodes. + private JMXNamespaceView(JMXNamespaceView parent, + MBeanServerConnection here, String where) { + this.parent = parent; + this.here = here; + this.where = where; + } + + /** + * Returns the path leading to the namespace in this view, from + * the top of the hierarchy. + * @return The path to the namespace in this view. + */ + public String where() { + return where; + } + + /** + * Lists all direct sub namespaces in this view. The returned strings + * do not contain the {@code //} separator. + * + * @return A list of direct sub name spaces accessible from this + * namespace. + * @throws IOException if the attempt to list the namespaces fails because + * of a communication problem. + */ + public String[] list() throws IOException { + final Set names = + here.queryNames(ALL_NAMESPACES,null); + final String[] res = new String[names.size()]; + int i = 0; + for (ObjectName dirName : names) { + final String dir = dirName.getDomain(); + res[i++]=dir.substring(0,dir.length()-NAMESPACE_SEPARATOR_LENGTH); + } + return res; + } + + /** + * Go down into a sub namespace. + * @param namespace the namespace to go down to. It can contain one or + * more {@code //} separators, to traverse intermediate namespaces, but + * it must not begin or end with {@code //} or contain an empty + * intermediate namespace. If it is the empty string, then {@code this} is + * returned. + * @return A view of the named sub namespace. + * @throws IllegalArgumentException if the {@code namespace} begins or + * ends with {@code //}. + */ + public JMXNamespaceView down(String namespace) { + if (namespace.equals("")) return this; + if (namespace.startsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) + throw new IllegalArgumentException(namespace+": can't start with "+ + JMXNamespaces.NAMESPACE_SEPARATOR); + + // This is a convenience to handle paths like xxx//yyy + final String[] elts = + namespace.split(JMXNamespaces.NAMESPACE_SEPARATOR); + + // Go down the path, creating all sub namespaces along the way. + // Usually there will be a single element in the given namespace + // name, but we don't want to forbid things like + // down("xxx//yyy/www"); + // + JMXNamespaceView previous = this; + String cursor = where; + for (String elt : elts) { + // empty path elements are not allowed. It means we + // had something like "xxx////yyy" + if (elt.equals("")) + throw new IllegalArgumentException(namespace+ + ": invalid path element"); + + // compute the "where" for the child. + cursor = JMXNamespaces.concat(cursor, elt); + + // create the child... + final JMXNamespaceView next = + makeJMXNamespaceView(root(), previous, cursor); + + // the current child will be the parent of the next child... + previous = next; + } + + // We return the last child that was created. + return previous; + } + + /** + * Go back up one level. If this view is at the root of the + * hierarchy, returns {@code null}. + * @return A view of the parent namespace, or {@code null} if we're at + * the root of the hierarchy. + */ + public JMXNamespaceView up() { + return parent; + } + + /** + * Tells whether this view is at the root of the hierarchy. + * @return {@code true} if this view is at the root of the hierachy. + */ + public boolean isRoot() { + return parent == null; + } + + /** + * Returns the view at the root of the hierarchy. + * If we are already at the root, this is {@code this}. + * @return the view at the root of the hierarchy. + */ + public JMXNamespaceView root() { + if (parent == null) return this; + return parent.root(); + } + + /** + * A MBeanServerConnection to the namespace shown by this view. + * This is what would have been obtained by doing: + *
      +     *   JMX.narrowToNamespace(this.root().getMBeanServerConnection(),
      +     *       this.where());
      +     * 
      + * @return A MBeanServerConnection to the namespace shown by this view. + */ + public MBeanServerConnection getMBeanServerConnection() { + return here; + } + + /** + *

      Get the name of the JMXNamespaceMBean handling the namespace shown by + * this view, relative to the root of the hierarchy. If we are at the root + * of the hierarchy, this method returns {@code null}.

      + * + *

      You can use this method to make a proxy for the JMXNamespaceMBean + * as follows:

      + * + *
      +     * JMXNamespaceView view = ...;
      +     * ObjectName namespaceMBeanName = view.getJMXNamespaceMBeanName();
      +     * JMXNamespaceMBean namespaceMBean = JMX.newMBeanProxy(
      +     *     view.root().getMBeanServerConnection(), namespaceMBeanName,
      +     *     JMXNamespaceMBean.class);
      +     * 
      + * + * @return The name of the {@code JMXNamespaceMBean} handling the namespace + * shown by this view, or {@code null}. + */ + public ObjectName getJMXNamespaceMBeanName() { + if (parent == null) + return null; + else + return JMXNamespaces.getNamespaceObjectName(where); + } + + @Override + public int hashCode() { + return where.hashCode(); + } + + /** + * Returns true if this object is equal to the given object. The + * two objects are equal if the other object is also a {@code + * JMXNamespaceView} and both objects have the same {@linkplain #root root} + * MBeanServerConnection and the same {@linkplain #where path}. + * @param o the other object to compare to. + * @return true if both objects are equal. + */ + @Override + public boolean equals(Object o) { + if (o==this) return true; + if (! (o instanceof JMXNamespaceView)) return false; + if (!where.equals(((JMXNamespaceView)o).where)) return false; + return root().getMBeanServerConnection().equals( + ((JMXNamespaceView)o).root().getMBeanServerConnection()); + } + + private JMXNamespaceView makeJMXNamespaceView(final JMXNamespaceView root, + final JMXNamespaceView directParent, final String pathFromRoot) { + if (pathFromRoot.equals("")) return root; + + return new JMXNamespaceView(directParent, + narrowToNamespace(root.getMBeanServerConnection(), + pathFromRoot),pathFromRoot); + } + + private MBeanServerConnection narrowToNamespace(MBeanServerConnection root, + String path) { + if (root instanceof MBeanServer) + return JMXNamespaces.narrowToNamespace((MBeanServer)root, path); + return JMXNamespaces.narrowToNamespace(root, path); + } + +} diff --git a/src/share/classes/javax/management/namespace/JMXNamespaces.java b/src/share/classes/javax/management/namespace/JMXNamespaces.java new file mode 100644 index 000000000..b3eafd28a --- /dev/null +++ b/src/share/classes/javax/management/namespace/JMXNamespaces.java @@ -0,0 +1,374 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.namespace; + +import com.sun.jmx.defaults.JmxProperties; +import com.sun.jmx.namespace.JMXNamespaceUtils; +import com.sun.jmx.namespace.ObjectNameRouter; +import com.sun.jmx.namespace.serial.RewritingProcessor; +import com.sun.jmx.namespace.RoutingConnectionProxy; +import com.sun.jmx.namespace.RoutingServerProxy; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; + +/** + * Static constants and utility methods to help work with + * JMX name spaces. There are no instances of this class. + * @since 1.7 + */ +public class JMXNamespaces { + + /** + * A logger for this class. + **/ + private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; + + /** Creates a new instance of JMXNamespaces */ + private JMXNamespaces() { + } + + /** + * The name space separator. This is an alias for {@link + * ObjectName#NAMESPACE_SEPARATOR}. + **/ + public static final String NAMESPACE_SEPARATOR = + ObjectName.NAMESPACE_SEPARATOR; + private static final int NAMESPACE_SEPARATOR_LENGTH = + NAMESPACE_SEPARATOR.length(); + + + /** + * Returns a connector connected to a sub name space exposed through + * the parent connector. + * @param parent the parent connector. + * @param namespace the {@linkplain javax.management.namespace name space} + * to which the returned connector is + * connected. + * @return A connector connected to a sub name space exposed through + * the parent connector. + **/ + public static JMXConnector narrowToNamespace(final JMXConnector parent, + final String namespace) + throws IOException { + + return JMXNamespaceUtils.cd(parent,namespace,true); + } + + /** + * Creates a new {@code MBeanServerConnection} proxy on a + * {@linkplain javax.management.namespace sub name space} + * of the given parent. + * + * @param parent The parent {@code MBeanServerConnection} that contains + * the name space. + * @param namespace The {@linkplain javax.management.namespace + * name space} in which to narrow. + * @return A new {@code MBeanServerConnection} proxy that shows the content + * of that name space. + * @throws IllegalArgumentException if the name space does not exist, or + * if a proxy for that name space cannot be created. + */ + public static MBeanServerConnection narrowToNamespace( + MBeanServerConnection parent, + String namespace) { + if (LOG.isLoggable(Level.FINER)) + LOG.finer("Making MBeanServerConnection for: " +namespace); + return RoutingConnectionProxy.cd(parent,namespace); + } + + /** + * Creates a new {@code MBeanServer} proxy on a + * {@linkplain javax.management.namespace sub name space} + * of the given parent. + * + * @param parent The parent {@code MBeanServer} that contains + * the name space. + * @param namespace The {@linkplain javax.management.namespace + * name space} in which to narrow. + * @return A new {@code MBeanServer} proxy that shows the content + * of that name space. + * @throws IllegalArgumentException if either argument is null, + * or the name space does not exist, or if a proxy for that name space + * cannot be created. + */ + public static MBeanServer narrowToNamespace(MBeanServer parent, + String namespace) { + if (LOG.isLoggable(Level.FINER)) + LOG.finer("Making NamespaceServerProxy for: " +namespace); + return RoutingServerProxy.cd(parent,namespace); + } + + /** + * Returns an object that is the same as the given object except that + * any {@link ObjectName} it might contain has its domain modified. + * The returned object might be identical to the given object if it + * does not contain any {@code ObjectName} values or if none of them + * were modified. + * This method will replace a prefix ({@code toRemove}) from the path of + * the ObjectNames contained in {@code obj} by another prefix + * ({@code toAdd}). + * Therefore, all contained ObjectNames must have a path that start with + * the given {@code toRemove} prefix. If one of them doesn't, an {@link + * IllegalArgumentException} is thrown. + *

      + * For instance, if {@code obj} contains the ObjectName + * {@code x//y//z//d:k=x}, and {@code toAdd} is {@code v//w}, and + * {@code toRemove} + * is {@code x//y} this method will return a copy of {@code obj} that + * contains {@code v//w//z//d:k=x}.
      + * On the other hand, if {@code obj} contains the ObjectName + * {@code x//y//z//d:k=x}, and {@code toAdd} is {@code v//w}, and + * {@code toRemove} is {@code v} this method + * will raise an exception, because {@code x//y//z//d:k=x} doesn't start + * with {@code v} + *

      + *

      Note: the default implementation of this method can use the + * Java serialization framework to clone and replace ObjectNames in the + * provided {@code obj}. It will usually fail if {@code obj} is not + * Java serializable, or contains objects which are not Java + * serializable. + *

      + * @param obj The object to deep-rewrite + * @param toRemove a prefix already present in contained ObjectNames. + * If {@code toRemove} is the empty string {@code ""}, nothing + * will be removed from the contained ObjectNames. + * @param toAdd the prefix that will replace (@code toRemove} in contained + * ObjectNames. + * If {@code toAdd} is the empty string {@code ""}, nothing + * will be added to the contained ObjectNames. + * @return the rewritten object, or possibly {@code obj} if nothing needed + * to be changed. + * @throws IllegalArgumentException if {@code obj} couldn't be rewritten or + * if {@code toRemove} or {@code toAdd} is null. + **/ + public static T deepReplaceHeadNamespace(T obj, String toRemove, String toAdd) { + final RewritingProcessor processor = + RewritingProcessor.newRewritingProcessor(toAdd,toRemove); + return processor.rewriteOutput(obj); + } + + /** + * Appends {@code namespace} to {@code path}. + * This methods appends {@code namespace} to {@code path} to obtain a + * a full path, and normalizes the result thus obtained: + *
        + *
      • If {@code path} is empty, the full path is + * {@code namespace}.
      • + *
      • Otherwise, if {@code namespace} is empty, + * the full path is {@code path}
      • + *
      • Otherwise, and this is the regular case, the full path is the + * result of the concatenation of + * {@code path}+{@value #NAMESPACE_SEPARATOR}+{@code namespace}
      • + *
      • finally, the full path is normalized: multiple consecutive + * occurrences of {@value #NAMESPACE_SEPARATOR} are replaced by a + * single {@value #NAMESPACE_SEPARATOR} in the result, and trailing + * occurences of {@value #NAMESPACE_SEPARATOR} are removed. + *
      • + *
      + * @param path a name space path prefix + * @param namespace a name space name to append to the path + * @return a syntactically valid name space path, or "" if both parameters + * are null or empty. + * @throws IllegalArgumentException if either argument is null or ends with + * an odd number of {@code /} characters. + **/ + public static String concat(String path, String namespace) { + if (path == null || namespace == null) + throw new IllegalArgumentException("Null argument"); + checkTrailingSlashes(path); + checkTrailingSlashes(namespace); + final String result; + if (path.equals("")) result=namespace; + else if (namespace.equals("")) result=path; + else result=path+NAMESPACE_SEPARATOR+namespace; + return ObjectNameRouter.normalizeNamespacePath(result,false,true,false); + } + + /** + * Returns a syntactically valid name space path. + * If the provided {@code namespace} ends with {@code "//"}, + * recursively strips trailing {@code "//"}. Each sequence of an + * even number of {@code "/"} characters is also replaced by {@code "//"}, + * for example {@code "foo//bar////baz/////buh"} will become + * {@code "foo//bar//baz///buh"}. + * + * @param namespace A name space path + * @return {@code ""} - if the provided {@code namespace} resolves to + * the empty string; otherwise a syntactically valid name space string + * stripped of trailing and redundant {@code "//"}. + * @throws IllegalArgumentException if {@code namespace} is null or + * is not syntactically valid (e.g. it contains + * invalid characters like ':', or it ends with an odd + * number of '/'). + */ + public static String normalizeNamespaceName(String namespace) { + if (namespace == null) + throw new IllegalArgumentException("Null namespace"); + final String sourcePath = + ObjectNameRouter.normalizeNamespacePath(namespace,false,true,false); + if (sourcePath.equals("")) return sourcePath; + + // Will throw an IllegalArgumentException if the namespace name + // is not syntactically valid... + // + getNamespaceObjectName(sourcePath); + return sourcePath; + } + + + /** + * Return a canonical handler name for the provided {@code namespace}, + * The handler name returned will be + * {@link #normalizeNamespaceName normalizeNamespaceName}{@code (namespace) + + * "//:type=JMXNamespace"}. + * + * @param namespace A name space path + * @return a canonical ObjectName for a name space handler. + * @see #normalizeNamespaceName + * @throws IllegalArgumentException if the provided + * {@code namespace} is null or not valid. + */ + public static ObjectName getNamespaceObjectName(String namespace) { + if (namespace == null || namespace.equals("")) + throw new IllegalArgumentException("Null or empty namespace"); + final String sourcePath = + ObjectNameRouter.normalizeNamespacePath(namespace,false, + true,false); + try { + return ObjectName.getInstance(sourcePath+ + NAMESPACE_SEPARATOR+":"+ + JMXNamespace.TYPE_ASSIGNMENT); + } catch (MalformedObjectNameException x) { + throw new IllegalArgumentException(namespace,x); + } + } + + /** + * Returns an ObjectName pattern that can be used to query for all MBeans + * contained in the given name space. + * For instance, if {@code namespace="foo//bar"}, this method will + * return {@code "foo//bar//*:*"} + * @return an ObjectName pattern that selects all MBeans in the given + * name space. + **/ + public static ObjectName getWildcardFor(String namespace) { + return insertPath(namespace,ObjectName.WILDCARD); + } + + + /** + * Returns an ObjectName that can be used to access an MBean + * contained in the given name space. + * For instance, if {@code path="foo//bar"}, and + * {@code to="domain:type=Thing"} this method will + * return {@code "foo//bar//domain:type=Thing"} + * @return an ObjectName that can be used to invoke an MBean located in a + * sub name space. + * @throws IllegalArgumentException if {@code path} ends with an + * odd number of {@code /} characters. + **/ + public static ObjectName insertPath(String path, ObjectName to) { + if (path == null || to == null) + throw new IllegalArgumentException("Null argument"); + checkTrailingSlashes(path); + try { + String prefix = path; + if (!prefix.equals("")) prefix = + ObjectNameRouter.normalizeNamespacePath( + prefix + NAMESPACE_SEPARATOR,false,false,false); + return to.withDomain( + ObjectNameRouter.normalizeDomain( + prefix+to.getDomain(),false)); + } catch (MalformedObjectNameException x) { + throw new IllegalArgumentException(path+": "+x,x); + } + } + + /** + * Returns the normalized name space path of the name space expected to + * contain {@code ObjectName}. + * For instance, for {@code "foo//domain:type=Thing"} this will be + * {@code "foo"}. For {@code "//foo//bar//domain:type=Thing"} this will be + * {@code "foo//bar"}. For {@code //foo//bar//baz//domain:type=Thing} + * this will be {@code "foo//bar//baz"}. For + * {@code //foo//bar//baz//:type=JMXNamespace} + * this will be {@code "foo//bar"}. + * + * @param name an {@code ObjectName} + * @return the name space path of the name space that could contain such + * a name. If {@code name} has no name space, returns {@code ""}. + * @throws IllegalArgumentException if {@code name} is null. + **/ + public static String getContainingNamespace(ObjectName name) { + return getNormalizedPath(name,true); + } + + + static String getNormalizedPath(ObjectName name, + boolean removeLeadingSep) { + if (name == null) + throw new IllegalArgumentException("Null name"); + String domain = + ObjectNameRouter.normalizeDomain(name.getDomain(),removeLeadingSep); + int end = domain.length(); + + // special case of domain part being a single '/' + // + if (domain.endsWith(NAMESPACE_SEPARATOR+"/")) + return domain.substring(0,end-NAMESPACE_SEPARATOR_LENGTH-1); + + // special case of namespace handler + // + if (domain.endsWith(NAMESPACE_SEPARATOR)) + domain = domain.substring(0,end-NAMESPACE_SEPARATOR_LENGTH); + + int last = domain.lastIndexOf(NAMESPACE_SEPARATOR); + if (last < 0) return ""; + if (last == 0) return domain; + + // special case of domain part starting with '/' + // last=0 is not possible - we took care of this above. + if (domain.charAt(last-1) == '/') last--; + + return domain.substring(0,last); + } + + private static void checkTrailingSlashes(String path) { + int i; + for (i = path.length() - 1; i >= 0 && path.charAt(i) == '/'; i--) + continue; + if (path.length() - i % 2 == 0) + throw new IllegalArgumentException("Path ends with odd number of /"); + } +} diff --git a/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java b/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java new file mode 100644 index 000000000..1639fd2fc --- /dev/null +++ b/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java @@ -0,0 +1,837 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.namespace; + +import com.sun.jmx.defaults.JmxProperties; +import com.sun.jmx.mbeanserver.Util; +import com.sun.jmx.namespace.JMXNamespaceUtils; +import com.sun.jmx.namespace.NamespaceInterceptor.DynamicProbe; +import com.sun.jmx.remote.util.EnvHelp; + +import java.io.IOException; +import java.security.AccessControlException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.management.AttributeChangeNotification; + +import javax.management.InstanceNotFoundException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanPermission; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventClient; +import javax.management.remote.JMXConnectionNotification; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +/** + * A {@link JMXNamespace} that will connect to a remote MBeanServer + * by creating a {@link javax.management.remote.JMXConnector} from a + * {@link javax.management.remote.JMXServiceURL}. + *

      + * You can call {@link #connect() connect()} and {@link #close close()} + * several times. This MBean will emit an {@link AttributeChangeNotification} + * when the value of its {@link #isConnected Connected} attribute changes. + *

      + *

      + * The JMX Remote Namespace MBean is not connected until {@link + * #connect() connect()} is explicitly called. The usual sequence of code to + * create a JMX Remote Namespace is thus: + *

      + *
      + *     final String namespace = "mynamespace";
      + *     final ObjectName name = {@link JMXNamespaces#getNamespaceObjectName
      + *       JMXNamespaces.getNamespaceObjectName(namespace)};
      + *     final JMXServiceURL remoteServerURL = .... ;
      + *     final Map optionsMap = .... ;
      + *     final MBeanServer masterMBeanServer = .... ;
      + *     final JMXRemoteNamespace namespaceMBean = {@link #newJMXRemoteNamespace
      + *        JMXRemoteNamespace.newJMXRemoteNamespace(remoteServerURL, optionsMap)};
      + *     masterMBeanServer.registerMBean(namespaceMBean, name);
      + *     namespaceMBean.connect();
      + *     // or: masterMBeanServer.invoke(name, {@link #connect() "connect"}, null, null);
      + * 
      + *

      + * The JMX Remote Namespace MBean will register for {@linkplain + * JMXConnectionNotification JMX Connection Notifications} with its underlying + * {@link JMXConnector}. When a JMX Connection Notification indicates that + * the underlying connection has failed, the JMX Remote Namespace MBean + * closes its underlying connector and switches its {@link #isConnected + * Connected} attribute to false, emitting an {@link + * AttributeChangeNotification}. + *

      + *

      + * At this point, a managing application (or an administrator connected + * through a management console) can attempt to reconnect the + * JMX Remote Namespace MBean by calling its {@link #connect() connect()} method + * again. + *

      + *

      Note that when the connection with the remote namespace fails, or when + * {@link #close} is called, then any notification subscription to + * MBeans registered in that namespace will be lost - unless a custom + * {@linkplain javax.management.event event service} supporting connection-less + * mode was used. + *

      + * @since 1.7 + */ +public class JMXRemoteNamespace + extends JMXNamespace + implements JMXRemoteNamespaceMBean, NotificationEmitter { + + /** + * A logger for this class. + */ + private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; + + private static final Logger PROBE_LOG = Logger.getLogger( + JmxProperties.NAMESPACE_LOGGER_NAME+".probe"); + + + // This connection listener is used to listen for connection events from + // the underlying JMXConnector. It is used in particular to maintain the + // "connected" state in this MBean. + // + private static class ConnectionListener implements NotificationListener { + private final JMXRemoteNamespace handler; + private ConnectionListener(JMXRemoteNamespace handler) { + this.handler = handler; + } + public void handleNotification(Notification notification, + Object handback) { + if (!(notification instanceof JMXConnectionNotification)) + return; + final JMXConnectionNotification cn = + (JMXConnectionNotification)notification; + handler.checkState(this,cn,(JMXConnector)handback); + } + } + + // When the JMXRemoteNamespace is originally created, it is not connected, + // which means that the source MBeanServer should be one that throws + // exceptions for most methods. When it is subsequently connected, + // the methods should be forwarded to the MBeanServerConnection. + // We handle this using MBeanServerConnectionWrapper. The + // MBeanServerConnection that is supplied to the constructor of + // MBeanServerConnectionWrapper is ignored (and in fact it is null) + // because the one that is actually used is the one supplied by the + // override of getMBeanServerConnection(). + private static class JMXRemoteNamespaceDelegate + extends MBeanServerConnectionWrapper + implements DynamicProbe { + private volatile JMXRemoteNamespace parent=null; + + JMXRemoteNamespaceDelegate() { + super(null,null); + } + @Override + public MBeanServerConnection getMBeanServerConnection() { + return parent.getMBeanServerConnection(); + } + @Override + public ClassLoader getDefaultClassLoader() { + return parent.getDefaultClassLoader(); + } + + // Because this class is instantiated in the super() call from the + // constructor of JMXRemoteNamespace, it cannot be an inner class. + // This method achieves the effect that an inner class would have + // had, of giving the class a reference to the outer "this". + synchronized void initParentOnce(JMXRemoteNamespace parent) { + if (this.parent != null) + throw new UnsupportedOperationException("parent already set"); + this.parent=parent; + + } + + public boolean isProbeRequested() { + return this.parent.isProbeRequested(); + } + } + + private static final MBeanNotificationInfo connectNotification = + new MBeanNotificationInfo(new String[] { + AttributeChangeNotification.ATTRIBUTE_CHANGE}, + "Connected", + "Emitted when the Connected state of this object changes"); + + private static long seqNumber=0; + + private final NotificationBroadcasterSupport broadcaster; + private final ConnectionListener listener; + private final JMXServiceURL jmxURL; + private final Map optionsMap; + + private volatile MBeanServerConnection server = null; + private volatile JMXConnector conn = null; + private volatile ClassLoader defaultClassLoader = null; + private volatile boolean probed; + + /** + * Creates a new instance of {@code JMXRemoteNamespace}. + *

      + * This constructor is provided for subclasses. + * To create a new instance of {@code JMXRemoteNamespace} call + * {@link #newJMXRemoteNamespace + * JMXRemoteNamespace.newJMXRemoteNamespace(sourceURL, optionsMap)}. + *

      + * @param sourceURL a JMX service URL that can be used to {@linkplain + * #connect() connect} to the + * source MBean Server. The source MBean Server is the remote + * MBean Server which contains the MBeans that will be mirrored + * in this namespace. + * @param optionsMap the options map that will be passed to the + * {@link JMXConnectorFactory} when {@linkplain + * JMXConnectorFactory#newJMXConnector creating} the + * {@link JMXConnector} used to {@linkplain #connect() connect} + * to the remote source MBean Server. Can be null, which is + * equivalent to an empty map. + * @see #newJMXRemoteNamespace JMXRemoteNamespace.newJMXRemoteNamespace + * @see #connect + */ + protected JMXRemoteNamespace(JMXServiceURL sourceURL, + Map optionsMap) { + super(new JMXRemoteNamespaceDelegate()); + ((JMXRemoteNamespaceDelegate)super.getSourceServer()). + initParentOnce(this); + + // URL must not be null. + this.jmxURL = JMXNamespaceUtils.checkNonNull(sourceURL,"url"); + this.broadcaster = + new NotificationBroadcasterSupport(connectNotification); + + // handles options + this.optionsMap = JMXNamespaceUtils.unmodifiableMap(optionsMap); + + // handles (dis)connection events + this.listener = new ConnectionListener(this); + + // XXX TODO: remove the probe, or simplify it. + this.probed = false; + } + + /** + * Returns the {@code JMXServiceURL} that is (or will be) used to + * connect to the remote name space.

      + * @see #connect + * @return The {@code JMXServiceURL} used to connect to the remote + * name space. + */ + public JMXServiceURL getJMXServiceURL() { + return jmxURL; + } + + /** + * In this class, this method never returns {@code null}, and the + * address returned is the {@link #getJMXServiceURL JMXServiceURL} + * that is used by this object to {@linkplain #connect} to the remote + * name space.

      + * This behaviour might be overriden by subclasses, if needed. + * For instance, a subclass might want to return {@code null} if it + * doesn't want to expose that JMXServiceURL. + */ + public JMXServiceURL getAddress() { + return getJMXServiceURL(); + } + + private Map getEnvMap() { + return optionsMap; + } + + boolean isProbeRequested() { + return probed==false; + } + + public void addNotificationListener(NotificationListener listener, + NotificationFilter filter, Object handback) { + broadcaster.addNotificationListener(listener, filter, handback); + } + + /** + * A subclass that needs to send its own notifications must override + * this method in order to return an {@link MBeanNotificationInfo + * MBeanNotificationInfo[]} array containing both its own notification + * infos and the notification infos of its super class.

      + * The implementation should probably look like: + *

      +     *      final MBeanNotificationInfo[] myOwnNotifs = { .... };
      +     *      final MBeanNotificationInfo[] parentNotifs =
      +     *            super.getNotificationInfo();
      +     *      final Set mergedResult =
      +     *            new HashSet();
      +     *      mergedResult.addAll(Arrays.asList(myOwnNotifs));
      +     *      mergedResult.addAll(Arrays.asList(parentNotifs));
      +     *      return mergeResult.toArray(
      +     *             new MBeanNotificationInfo[mergedResult.size()]);
      +     * 
      + */ + public MBeanNotificationInfo[] getNotificationInfo() { + return broadcaster.getNotificationInfo(); + } + + public void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException { + broadcaster.removeNotificationListener(listener); + } + + public void removeNotificationListener(NotificationListener listener, + NotificationFilter filter, Object handback) + throws ListenerNotFoundException { + broadcaster.removeNotificationListener(listener, filter, handback); + } + + private static synchronized long getNextSeqNumber() { + return seqNumber++; + } + + + /** + * Sends a notification to registered listeners. Before the notification + * is sent, the following steps are performed: + *
      • + * If {@code n.getSequenceNumber() <= 0} set it to the next available + * sequence number.
      • + *
      • If {@code n.getSource() == null}, set it to the value returned by {@link + * #getObjectName getObjectName()}. + *
      + *

      This method can be called by subclasses in order to send their own + * notifications. + * In that case, these subclasses might also need to override + * {@link #getNotificationInfo} in order to declare their own + * {@linkplain MBeanNotificationInfo notification types}. + *

      + * @param n The notification to send to registered listeners. + * @see javax.management.NotificationBroadcasterSupport + * @see #getNotificationInfo + **/ + protected void sendNotification(Notification n) { + if (n.getSequenceNumber()<=0) + n.setSequenceNumber(getNextSeqNumber()); + if (n.getSource()==null) + n.setSource(getObjectName()); + broadcaster.sendNotification(n); + } + + private void checkState(ConnectionListener listener, + JMXConnectionNotification cn, + JMXConnector emittingConnector) { + + // Due to the asynchronous handling of notifications, it is + // possible that this method is called for a JMXConnector + // (or connection) which is already closed and replaced by a newer + // one. + // + // This method attempts to determine the real state of the + // connection - which might be different from what the notification + // says. + // + // This is quite complex logic - because we try not to hold any + // lock while evaluating the true value of the connected state, + // while anyone might also call close() or connect() from a + // different thread. + // + // The method switchConnection() (called from here too) also has the + // same kind of complex logic. + // + // We use the JMXConnector has a handback to the notification listener + // (emittingConnector) in order to be able to determine whether the + // notification concerns the current connector in use, or an older + // one. + // + boolean remove = false; + + // whether the emittingConnector is already 'removed' + synchronized (this) { + if (this.conn != emittingConnector || + JMXConnectionNotification.FAILED.equals(cn.getType())) + remove = true; + } + + // We need to unregister our listener from this 'removed' connector. + // This is the only place where we remove the listener. + // + if (remove) { + try { + // This may fail if the connector is already closed. + // But better unregister anyway... + // + emittingConnector.removeConnectionNotificationListener( + listener,null, + emittingConnector); + } catch (Exception x) { + LOG.log(Level.FINE, + "Failed to unregister connection listener"+x); + LOG.log(Level.FINEST, + "Failed to unregister connection listener",x); + } + try { + // This may fail if the connector is already closed. + // But better call close twice and get an exception than + // leaking... + // + emittingConnector.close(); + } catch (Exception x) { + LOG.log(Level.FINEST, + "Failed to close old connector " + + "(failure was expected): "+x); + } + } + + // Now we checked whether our current connector is still alive. + // + boolean closed = false; + final JMXConnector thisconn = this.conn; + try { + if (thisconn != null) + thisconn.getConnectionId(); + } catch (IOException x) { + LOG.finest("Connector already closed: "+x); + closed = true; + } + + // We got an IOException - the connector is not connected. + // Need to forget it and switch our state to closed. + // + if (closed) { + switchConnection(thisconn,null,null); + try { + // Usually this will fail... Better call close twice + // and get an exception than leaking... + // + if (thisconn != emittingConnector || !remove) + thisconn.close(); + } catch (IOException x) { + LOG.log(Level.FINEST, + "Failed to close connector (failure was expected): " + +x); + } + } + } + + private final void switchConnection(JMXConnector oldc, + JMXConnector newc, + MBeanServerConnection mbs) { + boolean connect = false; + boolean close = false; + synchronized (this) { + if (oldc != conn) { + if (newc != null) { + try { + newc.close(); + } catch (IOException x) { + LOG.log(Level.FINEST, + "Failed to close connector",x); + } + } + return; + } + if (conn == null && newc != null) connect=true; + if (newc == null && conn != null) close = true; + conn = newc; + server = mbs; + } + if (connect || close) { + boolean oldstate = close; + boolean newstate = connect; + final ObjectName myName = getObjectName(); + + // In the uncommon case where the MBean is connected before + // being registered, myName can be null... + // If myName is null - we use 'this' as the source instead... + // + final Object source = (myName==null)?this:myName; + final AttributeChangeNotification acn = + new AttributeChangeNotification(source, + getNextSeqNumber(),System.currentTimeMillis(), + String.valueOf(source)+ + (newstate?" connected":" closed"), + "Connected", + "boolean", + Boolean.valueOf(oldstate), + Boolean.valueOf(newstate)); + sendNotification(acn); + } + } + + private void closeall(JMXConnector... a) { + for (JMXConnector c : a) { + try { + if (c != null) c.close(); + } catch (Exception x) { + // OK: we're gonna throw the original exception later. + LOG.finest("Ignoring exception when closing connector: "+x); + } + } + } + + JMXConnector connect(JMXServiceURL url, Map env) + throws IOException { + final JMXConnector c = newJMXConnector(jmxURL, env); + c.connect(env); + return c; + } + + /** + * Creates a new JMXConnector with the specified {@code url} and + * {@code env} options map. + *

      + * This method first calls {@link JMXConnectorFactory#newJMXConnector + * JMXConnectorFactory.newJMXConnector(jmxURL, env)} to obtain a new + * JMX connector, and returns that. + *

      + *

      + * A subclass of {@link JMXRemoteNamespace} can provide an implementation + * that connects to a sub namespace of the remote server by subclassing + * this class in the following way: + *

      +     * class JMXRemoteSubNamespace extends JMXRemoteNamespace {
      +     *    private final String subnamespace;
      +     *    JMXRemoteSubNamespace(JMXServiceURL url,
      +     *              Map{@code } env, String subnamespace) {
      +     *        super(url,options);
      +     *        this.subnamespace = subnamespace;
      +     *    }
      +     *    protected JMXConnector newJMXConnector(JMXServiceURL url,
      +     *              Map env) throws IOException {
      +     *        final JMXConnector inner = super.newJMXConnector(url,env);
      +     *        return {@link JMXNamespaces#narrowToNamespace(JMXConnector,String)
      +     *               JMXNamespaces.narrowToNamespace(inner,subnamespace)};
      +     *    }
      +     * }
      +     * 
      + *

      + *

      + * Some connectors, like the JMXMP connector server defined by the + * version 1.2 of the JMX API may not have been upgraded to use the + * new {@linkplain javax.management.event Event Service} defined in this + * version of the JMX API. + *

      + * In that case, and if the remote server to which this JMXRemoteNamespace + * connects also contains namespaces, it may be necessary to configure + * explicitly an {@linkplain + * javax.management.event.EventClientDelegate#newForwarder() + * Event Client Forwarder} on the remote server side, and to force the use + * of an {@link EventClient} on this client side. + *
      + * A subclass of {@link JMXRemoteNamespace} can provide an implementation + * of {@code newJMXConnector} that will force notification subscriptions + * to flow through an {@link EventClient} over a legacy protocol by + * overriding this method in the following way: + *

      + *
      +     * class JMXRemoteEventClientNamespace extends JMXRemoteNamespace {
      +     *    JMXRemoteSubNamespaceConnector(JMXServiceURL url,
      +     *              Map env) {
      +     *        super(url,options);
      +     *    }
      +     *    protected JMXConnector newJMXConnector(JMXServiceURL url,
      +     *              Map env) throws IOException {
      +     *        final JMXConnector inner = super.newJMXConnector(url,env);
      +     *        return {@link EventClient#withEventClient(
      +     *                JMXConnector) EventClient.withEventClient(inner)};
      +     *    }
      +     * }
      +     * 
      + *

      + * Note that the remote server also needs to provide an {@link + * javax.management.event.EventClientDelegateMBean}: only configuring + * the client side (this object) is not enough.
      + * In summary, this technique should be used if the remote server + * supports JMX namespaces, but uses a JMX Connector Server whose + * implementation does not transparently use the new Event Service + * (as would be the case with the JMXMPConnectorServer implementation + * from the reference implementation of the JMX Remote API 1.0 + * specification). + *

      + * @param url The JMXServiceURL of the remote server. + * @param optionsMap An unmodifiable options map that will be passed to the + * {@link JMXConnectorFactory} when {@linkplain + * JMXConnectorFactory#newJMXConnector creating} the + * {@link JMXConnector} that can connect to the remote source + * MBean Server. + * @return An unconnected JMXConnector to use to connect to the remote + * server + * @throws java.io.IOException if the connector could not be created. + * @see JMXConnectorFactory#newJMXConnector(javax.management.remote.JMXServiceURL, java.util.Map) + * @see #JMXRemoteNamespace + */ + protected JMXConnector newJMXConnector(JMXServiceURL url, + Map optionsMap) throws IOException { + final JMXConnector c = + JMXConnectorFactory.newJMXConnector(jmxURL, optionsMap); +// TODO: uncomment this when contexts are added +// return ClientContext.withDynamicContext(c); + return c; + } + + public void connect() throws IOException { + if (conn != null) { + try { + // This is much too fragile. It must go away! + PROBE_LOG.finest("Probing again..."); + triggerProbe(getMBeanServerConnection()); + } catch(Exception x) { + close(); + Throwable cause = x; + // if the cause is a security exception - rethrows it... + while (cause != null) { + if (cause instanceof SecurityException) + throw (SecurityException) cause; + cause = cause.getCause(); + } + throw new IOException("connection failed: cycle?",x); + } + } + LOG.fine("connecting..."); + // TODO remove these traces + // System.err.println(getInitParameter()+" connecting"); + final Map env = + new HashMap(getEnvMap()); + try { + // XXX: We should probably document this... + // This allows to specify a loader name - which will be + // retrieved from the paret MBeanServer. + defaultClassLoader = + EnvHelp.resolveServerClassLoader(env,getMBeanServer()); + } catch (InstanceNotFoundException x) { + final IOException io = + new IOException("ClassLoader not found"); + io.initCause(x); + throw io; + } + env.put(JMXConnectorFactory.DEFAULT_CLASS_LOADER,defaultClassLoader); + final JMXServiceURL url = getJMXServiceURL(); + final JMXConnector aconn = connect(url,env); + final MBeanServerConnection msc; + try { + msc = aconn.getMBeanServerConnection(); + aconn.addConnectionNotificationListener(listener,null,aconn); + } catch (IOException io) { + closeall(aconn); + throw io; + } catch (RuntimeException x) { + closeall(aconn); + throw x; + } + + + // XXX Revisit here + // Note from the author: This business of switching connection is + // incredibly complex. Isn't there any means to simplify it? + // + switchConnection(conn,aconn,msc); + try { + triggerProbe(msc); + } catch(Exception x) { + close(); + Throwable cause = x; + // if the cause is a security exception - rethrows it... + while (cause != null) { + if (cause instanceof SecurityException) + throw (SecurityException) cause; + cause = cause.getCause(); + } + throw new IOException("connection failed: cycle?",x); + } + LOG.fine("connected."); + } + + // If this is a self-linking namespace, this method should trigger + // the emission of a probe in the wrapping NamespaceInterceptor. + // The first call to source() in the wrapping NamespaceInterceptor + // causes the emission of the probe. + // + // Note: the MBeanServer returned by getSourceServer + // (our private JMXRemoteNamespaceDelegate inner class) + // implements a sun private interface (DynamicProbe) which is + // used by the NamespaceInterceptor to determine whether it should + // send a probe or not. + // We needed this interface here because the NamespaceInterceptor + // has otherwise no means to knows that this object has just + // connected, and that a new probe should be sent. + // + // Probes work this way: the NamespaceInterceptor sets a flag and sends + // a queryNames() request. If a queryNames() request comes in when the flag + // is on, then it deduces that there is a self-linking loop - and instead + // of calling queryNames() on the JMXNamespace (which would cause the + // loop to go on) it breaks the recursion by returning the probe ObjectName. + // If the NamespaceInterceptor receives the probe ObjectName as result of + // its original queryNames() it knows that it has been looping back on + // itslef and throws an Exception - which will be raised through this + // method, thus preventing the connection to be established... + // + // More info in the com.sun.jmx.namespace.NamespaceInterceptor class + // + // XXX: TODO this probe thing is way too complex and fragile. + // This *must* go away or be replaced by something simpler. + // ideas are welcomed. + // + private void triggerProbe(final MBeanServerConnection msc) + throws MalformedObjectNameException, IOException { + // Query Pattern that we will send through the source server in order + // to detect self-linking namespaces. + // + // + final ObjectName pattern; + pattern = ObjectName.getInstance("*" + + JMXNamespaces.NAMESPACE_SEPARATOR + ":" + + JMXNamespace.TYPE_ASSIGNMENT); + probed = false; + try { + msc.queryNames(pattern, null); + probed = true; + } catch (AccessControlException x) { + // if we have an MBeanPermission missing then do nothing... + if (!(x.getPermission() instanceof MBeanPermission)) + throw x; + PROBE_LOG.finer("Can't check for cycles: " + x); + probed = false; // no need to do it again... + } + } + + public void close() throws IOException { + if (conn == null) return; + LOG.fine("closing..."); + // System.err.println(toString()+": closing..."); + conn.close(); + // System.err.println(toString()+": connector closed"); + switchConnection(conn,null,null); + LOG.fine("closed."); + // System.err.println(toString()+": closed"); + } + + MBeanServerConnection getMBeanServerConnection() { + if (conn == null) + throw newRuntimeIOException("getMBeanServerConnection: not connected"); + return server; + } + + // Better than throwing UndeclaredThrowableException ... + private RuntimeException newRuntimeIOException(String msg) { + final IllegalStateException illegal = new IllegalStateException(msg); + return Util.newRuntimeIOException(new IOException(msg,illegal)); + } + + /** + * Returns the default class loader used by the underlying + * {@link JMXConnector}. + * @return the default class loader used when communicating with the + * remote source MBean server. + **/ + ClassLoader getDefaultClassLoader() { + if (conn == null) + throw newRuntimeIOException("getMBeanServerConnection: not connected"); + return defaultClassLoader; + } + + public boolean isConnected() { + // This is a pleonasm + return (conn != null) && (server != null); + } + + + /** + * This name space handler will automatically {@link #close} its + * connection with the remote source in {@code preDeregister}. + **/ + @Override + public void preDeregister() throws Exception { + try { + close(); + } catch (IOException x) { + LOG.fine("Failed to close properly - exception ignored: " + x); + LOG.log(Level.FINEST, + "Failed to close properly - exception ignored",x); + } + super.preDeregister(); + } + + /** + * This method calls {@link + * javax.management.MBeanServerConnection#getMBeanCount + * getMBeanCount()} on the remote namespace. + * @throws java.io.IOException if an {@link IOException} is raised when + * communicating with the remote source namespace. + */ + @Override + public Integer getMBeanCount() throws IOException { + return getMBeanServerConnection().getMBeanCount(); + } + + /** + * This method returns the result of calling {@link + * javax.management.MBeanServerConnection#getDomains + * getDomains()} on the remote namespace. + * @throws java.io.IOException if an {@link IOException} is raised when + * communicating with the remote source namespace. + */ + @Override + public String[] getDomains() throws IOException { + return getMBeanServerConnection().getDomains(); + } + + /** + * This method returns the result of calling {@link + * javax.management.MBeanServerConnection#getDefaultDomain + * getDefaultDomain()} on the remote namespace. + * @throws java.io.IOException if an {@link IOException} is raised when + * communicating with the remote source namespace. + */ + @Override + public String getDefaultDomain() throws IOException { + return getMBeanServerConnection().getDefaultDomain(); + } + + /** + * Creates a new instance of {@code JMXRemoteNamespace}. + * @param sourceURL a JMX service URL that can be used to connect to the + * source MBean Server. The source MBean Server is the remote + * MBean Server which contains the MBeans that will be mirrored + * in this namespace. + * @param optionsMap An options map that will be passed to the + * {@link JMXConnectorFactory} when {@linkplain + * JMXConnectorFactory#newJMXConnector creating} the + * {@link JMXConnector} used to connect to the remote source + * MBean Server. Can be null, which is equivalent to an empty map. + * @see #JMXRemoteNamespace JMXRemoteNamespace(sourceURL,optionsMap) + * @see JMXConnectorFactory#newJMXConnector(javax.management.remote.JMXServiceURL, java.util.Map) + */ + public static JMXRemoteNamespace newJMXRemoteNamespace( + JMXServiceURL sourceURL, + Map optionsMap) { + return new JMXRemoteNamespace(sourceURL, optionsMap); + } +} diff --git a/src/share/classes/javax/management/namespace/JMXRemoteNamespaceMBean.java b/src/share/classes/javax/management/namespace/JMXRemoteNamespaceMBean.java new file mode 100644 index 000000000..18b0fb86d --- /dev/null +++ b/src/share/classes/javax/management/namespace/JMXRemoteNamespaceMBean.java @@ -0,0 +1,96 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.namespace; + +import java.io.IOException; +import javax.management.remote.JMXServiceURL; + +/** + * A {@link JMXNamespaceMBean} that will connect to a remote MBeanServer + * by creating a {@link javax.management.remote.JMXConnector} from a + * {@link javax.management.remote.JMXServiceURL}. + * You can call {@link #connect connect()} and {@link #close close()} + * several times. + * @since 1.7 + */ +public interface JMXRemoteNamespaceMBean + extends JMXNamespaceMBean { + + /** + * Connects to the underlying remote source name space, if not already + * {@link #isConnected connected}. + * If connected, do nothing. Otherwise, creates a new connector from the + * {@link javax.management.remote.JMXServiceURL JMXServiceURL} provided at + * creation time, and connects to the remote source name space. + *

      + * The source MBeans will not appear in the target name space until the + * JMXRemoteNamespaceMBean is connected. + *

      + * It is possible to call {@code connect()}, {@link #close close()}, and + * {@code connect()} again. + * However, closing the connection with the remote name space may cause + * notification listeners to be lost, unless the client explicitly uses + * the new {@linkplain javax.management.event JMX event service}. + *

      + * @throws IOException if connection to the remote source name space fails. + * @see #isConnected isConnected + **/ + public void connect() + throws IOException; + + /** + * Closes the connection with the remote source name space. + * If the connection is already closed, do nothing. + * Otherwise, closes the underlying {@link + * javax.management.remote.JMXConnector}. + *

      Once closed, it is possible to reopen the connection by + * calling {@link #connect connect}. + *

      + * @throws IOException if the connection to the remote source name space + * can't be closed properly. + * @see #isConnected isConnected + **/ + public void close() + throws IOException; + + /** + * Tells whether the connection to the remote source name space is opened. + * @see #connect connect + * @see #close close + * @return {@code true} if connected. + **/ + public boolean isConnected(); + + /** + * Returns the {@link JMXServiceURL} address that points to the remote name + * space mirrored by this {@link JMXNamespaceMBean JMXNamespace MBean}, + * if available. + * @return The {@link JMXServiceURL} address that points to the remote name + * space mirrored by this {@link JMXNamespaceMBean JMXNamespace MBean}, + * or {@code null}. + */ + public JMXServiceURL getAddress(); +} diff --git a/src/share/classes/javax/management/namespace/MBeanServerConnectionWrapper.java b/src/share/classes/javax/management/namespace/MBeanServerConnectionWrapper.java new file mode 100644 index 000000000..f74785ffd --- /dev/null +++ b/src/share/classes/javax/management/namespace/MBeanServerConnectionWrapper.java @@ -0,0 +1,703 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.namespace; + +import com.sun.jmx.mbeanserver.Util; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.security.AccessController; +import java.util.Set; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.NotCompliantMBeanException; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.OperationsException; +import javax.management.QueryExp; +import javax.management.ReflectionException; +import javax.management.loading.ClassLoaderRepository; + +/** + *

      An object of this class implements the MBeanServer interface + * and, for each of its methods forwards the request to a wrapped + * {@link MBeanServerConnection} object. + * Some methods of the {@link MBeanServer} interface do not have + * any equivalent in {@link MBeanServerConnection}. In that case, an + * {@link UnsupportedOperationException} will be thrown. + * + *

      A typical use of this class is to apply a {@link QueryExp} object locally, + * on an MBean that resides in a remote MBeanServer. Since an + * MBeanServerConnection is not an MBeanServer, it cannot be passed + * to the setMBeanServer() method of the {@link QueryExp} + * object. However, this object can.

      + * + * @since 1.7 + */ +public class MBeanServerConnectionWrapper + implements MBeanServer { + + private final MBeanServerConnection wrapped; + private final ClassLoader defaultCl; + + /** + * Construct a new object that implements {@link MBeanServer} by + * forwarding its methods to the given {@link MBeanServerConnection}. + * This constructor is equivalent to {@link #MBeanServerConnectionWrapper( + * MBeanServerConnection, ClassLoader) MBeanServerConnectionWrapper(wrapped, + * null)}. + * + * @param wrapped the {@link MBeanServerConnection} to which methods + * are to be forwarded. This parameter can be null, in which case the + * {@code MBeanServerConnection} will typically be supplied by overriding + * {@link #getMBeanServerConnection}. + */ + public MBeanServerConnectionWrapper(MBeanServerConnection wrapped) { + this(wrapped, null); + } + + /** + * Construct a new object that implements {@link MBeanServer} by + * forwarding its methods to the given {@link MBeanServerConnection}. + * The {@code defaultCl} parameter specifies the value to be returned + * by {@link #getDefaultClassLoader}. A null value is equivalent to + * {@link Thread#getContextClassLoader()}. + * + * @param wrapped the {@link MBeanServerConnection} to which methods + * are to be forwarded. This parameter can be null, in which case the + * {@code MBeanServerConnection} will typically be supplied by overriding + * {@link #getMBeanServerConnection}. + * @param defaultCl the value to be returned by {@link + * #getDefaultClassLoader}. A null value is equivalent to the current + * thread's {@linkplain Thread#getContextClassLoader()}. + */ + public MBeanServerConnectionWrapper(MBeanServerConnection wrapped, + ClassLoader defaultCl) { + this.wrapped = wrapped; + this.defaultCl = (defaultCl == null) ? + Thread.currentThread().getContextClassLoader() : defaultCl; + } + + /** + * Returns an MBeanServerConnection. This method is called each time + * an operation must be invoked on the underlying MBeanServerConnection. + * The default implementation returns the MBeanServerConnection that + * was supplied to the constructor of this MBeanServerConnectionWrapper. + **/ + protected MBeanServerConnection getMBeanServerConnection() { + return wrapped; + } + + /** + * Returns the default class loader passed to the constructor. If the + * value passed was null, then the returned value will be the + * {@linkplain Thread#getContextClassLoader() context class loader} at the + * time this object was constructed. + * + * @return the ClassLoader that was passed to the constructor. + **/ + public ClassLoader getDefaultClassLoader() { + return defaultCl; + } + + /** + *

      This method is called each time an IOException is raised when + * trying to forward an operation to the underlying + * MBeanServerConnection, as a result of calling + * {@link #getMBeanServerConnection()} or as a result of invoking the + * operation on the returned connection. Since the methods in + * {@link MBeanServer} are not declared to throw {@code IOException}, + * this method must return a {@code RuntimeException} to be thrown + * instead. Typically, the original {@code IOException} will be in the + * {@linkplain Throwable#getCause() cause chain} of the {@code + * RuntimeException}.

      + * + *

      Subclasses may redefine this method if they need to perform any + * specific handling of IOException (logging etc...).

      + * + * @param x The raised IOException. + * @param method The name of the method in which the exception was + * raised. This is one of the methods of the MBeanServer + * interface. + * + * @return A RuntimeException that should be thrown by the caller. + * In this default implementation, this is a + * {@link RuntimeException} wrapping x. + **/ + protected RuntimeException wrapIOException(IOException x, String method) { + return Util.newRuntimeIOException(x); + } + + // Take care of getMBeanServerConnection returning null. + // + private synchronized MBeanServerConnection connection() + throws IOException { + final MBeanServerConnection c = getMBeanServerConnection(); + if (c == null) + throw new IOException("MBeanServerConnection unavailable"); + return c; + } + + //-------------------------------------------- + //-------------------------------------------- + // + // Implementation of the MBeanServer interface + // + //-------------------------------------------- + //-------------------------------------------- + + /** + * Forward this method to the + * wrapped object. + */ + public void addNotificationListener(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException { + try { + connection().addNotificationListener(name, listener, + filter, handback); + } catch (IOException x) { + throw wrapIOException(x,"addNotificationListener"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public void addNotificationListener(ObjectName name, + ObjectName listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException { + try { + connection().addNotificationListener(name, listener, + filter, handback); + } catch (IOException x) { + throw wrapIOException(x,"addNotificationListener"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public ObjectInstance createMBean(String className, ObjectName name) + throws + ReflectionException, + InstanceAlreadyExistsException, + MBeanRegistrationException, + MBeanException, + NotCompliantMBeanException { + try { + return connection().createMBean(className, name); + } catch (IOException x) { + throw wrapIOException(x,"createMBean"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public ObjectInstance createMBean(String className, ObjectName name, + Object params[], String signature[]) + throws + ReflectionException, + InstanceAlreadyExistsException, + MBeanRegistrationException, + MBeanException, + NotCompliantMBeanException { + try { + return connection().createMBean(className, name, + params, signature); + } catch (IOException x) { + throw wrapIOException(x,"createMBean"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public ObjectInstance createMBean(String className, + ObjectName name, + ObjectName loaderName) + throws + ReflectionException, + InstanceAlreadyExistsException, + MBeanRegistrationException, + MBeanException, + NotCompliantMBeanException, + InstanceNotFoundException { + try { + return connection().createMBean(className, name, loaderName); + } catch (IOException x) { + throw wrapIOException(x,"createMBean"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public ObjectInstance createMBean(String className, + ObjectName name, + ObjectName loaderName, + Object params[], + String signature[]) + throws + ReflectionException, + InstanceAlreadyExistsException, + MBeanRegistrationException, + MBeanException, + NotCompliantMBeanException, + InstanceNotFoundException { + try { + return connection().createMBean(className, name, loaderName, + params, signature); + } catch (IOException x) { + throw wrapIOException(x,"createMBean"); + } + } + + /** + * Throws an {@link UnsupportedOperationException}. This behavior can + * be changed by subclasses. + * @deprecated see {@link MBeanServer#deserialize(ObjectName,byte[]) + * MBeanServer} + */ + @Deprecated + public ObjectInputStream deserialize(ObjectName name, byte[] data) + throws InstanceNotFoundException, OperationsException { + throw new UnsupportedOperationException("deserialize"); + } + + /** + * Throws an {@link UnsupportedOperationException}. This behavior can + * be changed by subclasses. + * @deprecated see {@link MBeanServer#deserialize(String,byte[]) + * MBeanServer} + */ + @Deprecated + public ObjectInputStream deserialize(String className, byte[] data) + throws OperationsException, ReflectionException { + throw new UnsupportedOperationException("deserialize"); + } + + /** + * Throws an {@link UnsupportedOperationException}. This behavior can + * be changed by subclasses. + * @deprecated see {@link MBeanServer#deserialize(String,ObjectName,byte[]) + * MBeanServer} + */ + @Deprecated + public ObjectInputStream deserialize(String className, + ObjectName loaderName, + byte[] data) + throws + InstanceNotFoundException, + OperationsException, + ReflectionException { + throw new UnsupportedOperationException("deserialize"); + } + + /** + * Forward this method to the + * wrapped object. + */ + public Object getAttribute(ObjectName name, String attribute) + throws + MBeanException, + AttributeNotFoundException, + InstanceNotFoundException, + ReflectionException { + try { + return connection().getAttribute(name, attribute); + } catch (IOException x) { + throw wrapIOException(x,"getAttribute"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public AttributeList getAttributes(ObjectName name, String[] attributes) + throws InstanceNotFoundException, ReflectionException { + try { + return connection().getAttributes(name, attributes); + } catch (IOException x) { + throw wrapIOException(x,"getAttributes"); + } + } + + /** + * Throws an {@link UnsupportedOperationException}. This behavior can + * be changed by subclasses. + */ + public ClassLoader getClassLoader(ObjectName loaderName) + throws InstanceNotFoundException { + throw new UnsupportedOperationException("getClassLoader"); + } + + /** + * Returns the {@linkplain #getDefaultClassLoader() default class loader}. + * This behavior can be changed by subclasses. + */ + public ClassLoader getClassLoaderFor(ObjectName mbeanName) + throws InstanceNotFoundException { + return getDefaultClassLoader(); + } + + /** + *

      Returns a {@link ClassLoaderRepository} based on the class loader + * returned by {@link #getDefaultClassLoader()}.

      + * @return a {@link ClassLoaderRepository} that contains a single + * class loader, returned by {@link #getDefaultClassLoader()}. + **/ + public ClassLoaderRepository getClassLoaderRepository() { + // We return a new ClassLoaderRepository each time this method is + // called. This is by design, because there's no guarantee that + // getDefaultClassLoader() will always return the same class loader. + return Util.getSingleClassLoaderRepository(getDefaultClassLoader()); + } + + /** + * Forward this method to the + * wrapped object. + */ + public String getDefaultDomain() { + try { + return connection().getDefaultDomain(); + } catch (IOException x) { + throw wrapIOException(x,"getDefaultDomain"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public String[] getDomains() { + try { + return connection().getDomains(); + } catch (IOException x) { + throw wrapIOException(x,"getDomains"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public Integer getMBeanCount() { + try { + return connection().getMBeanCount(); + } catch (IOException x) { + throw wrapIOException(x,"getMBeanCount"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public MBeanInfo getMBeanInfo(ObjectName name) + throws + InstanceNotFoundException, + IntrospectionException, + ReflectionException { + try { + return connection().getMBeanInfo(name); + } catch (IOException x) { + throw wrapIOException(x,"getMBeanInfo"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public ObjectInstance getObjectInstance(ObjectName name) + throws InstanceNotFoundException { + try { + return connection().getObjectInstance(name); + } catch (IOException x) { + throw wrapIOException(x,"getObjectInstance"); + } + } + + /** + * Throws an {@link UnsupportedOperationException}. This behavior can + * be changed by subclasses. + */ + public Object instantiate(String className) + throws ReflectionException, MBeanException { + throw new UnsupportedOperationException("instantiate"); + } + + /** + * Throws an {@link UnsupportedOperationException}. This behavior can + * be changed by subclasses. + */ + public Object instantiate(String className, + Object params[], + String signature[]) + throws ReflectionException, MBeanException { + throw new UnsupportedOperationException("instantiate"); + } + + /** + * Throws an {@link UnsupportedOperationException}. This behavior can + * be changed by subclasses. + */ + public Object instantiate(String className, ObjectName loaderName) + throws ReflectionException, MBeanException, + InstanceNotFoundException { + throw new UnsupportedOperationException("instantiate"); + } + + /** + * Throws an {@link UnsupportedOperationException}. This behavior can + * be changed by subclasses. + */ + public Object instantiate(String className, ObjectName loaderName, + Object params[], String signature[]) + throws ReflectionException, MBeanException, + InstanceNotFoundException { + throw new UnsupportedOperationException("instantiate"); + } + + /** + * Forward this method to the + * wrapped object. + */ + public Object invoke(ObjectName name, String operationName, + Object params[], String signature[]) + throws + InstanceNotFoundException, + MBeanException, + ReflectionException { + try { + return connection().invoke(name,operationName,params,signature); + } catch (IOException x) { + throw wrapIOException(x,"invoke"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public boolean isInstanceOf(ObjectName name, String className) + throws InstanceNotFoundException { + try { + return connection().isInstanceOf(name, className); + } catch (IOException x) { + throw wrapIOException(x,"isInstanceOf"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public boolean isRegistered(ObjectName name) { + try { + return connection().isRegistered(name); + } catch (IOException x) { + throw wrapIOException(x,"isRegistered"); + } + } + + /** + * Forward this method to the + * wrapped object. + * If an IOException is raised, returns an empty Set. + */ + public Set queryMBeans(ObjectName name, QueryExp query) { + try { + return connection().queryMBeans(name, query); + } catch (IOException x) { + throw wrapIOException(x,"queryMBeans"); + //return Collections.emptySet(); + } + } + + /** + * Forward this method to the + * wrapped object. + * If an IOException is raised, returns an empty Set. + */ + public Set queryNames(ObjectName name, QueryExp query) { + try { + return connection().queryNames(name, query); + } catch (IOException x) { + throw wrapIOException(x,"queryNames"); + //return Collections.emptySet(); + } + } + + /** + * Throws an {@link UnsupportedOperationException}. This behavior can + * be changed by subclasses. + */ + public ObjectInstance registerMBean(Object object, ObjectName name) + throws + InstanceAlreadyExistsException, + MBeanRegistrationException, + NotCompliantMBeanException { + throw new UnsupportedOperationException("registerMBean"); + } + + /** + * Forward this method to the + * wrapped object. + */ + public void removeNotificationListener(ObjectName name, + NotificationListener listener) + throws InstanceNotFoundException, ListenerNotFoundException { + try { + connection().removeNotificationListener(name, listener); + } catch (IOException x) { + throw wrapIOException(x,"removeNotificationListener"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public void removeNotificationListener(ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, ListenerNotFoundException { + try { + connection().removeNotificationListener(name, listener, + filter, handback); + } catch (IOException x) { + throw wrapIOException(x,"removeNotificationListener"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public void removeNotificationListener(ObjectName name, + ObjectName listener) + throws InstanceNotFoundException, ListenerNotFoundException { + try { + connection().removeNotificationListener(name, listener); + } catch (IOException x) { + throw wrapIOException(x,"removeNotificationListener"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public void removeNotificationListener(ObjectName name, + ObjectName listener, + NotificationFilter filter, + Object handback) + throws InstanceNotFoundException, ListenerNotFoundException { + try { + connection().removeNotificationListener(name, listener, + filter, handback); + } catch (IOException x) { + throw wrapIOException(x,"removeNotificationListener"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public void setAttribute(ObjectName name, Attribute attribute) + throws + InstanceNotFoundException, + AttributeNotFoundException, + InvalidAttributeValueException, + MBeanException, + ReflectionException { + try { + connection().setAttribute(name, attribute); + } catch (IOException x) { + throw wrapIOException(x,"setAttribute"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public AttributeList setAttributes(ObjectName name, + AttributeList attributes) + throws InstanceNotFoundException, ReflectionException { + try { + return connection().setAttributes(name, attributes); + } catch (IOException x) { + throw wrapIOException(x,"setAttributes"); + } + } + + /** + * Forward this method to the + * wrapped object. + */ + public void unregisterMBean(ObjectName name) + throws InstanceNotFoundException, MBeanRegistrationException { + try { + connection().unregisterMBean(name); + } catch (IOException x) { + throw wrapIOException(x,"unregisterMBean"); + } + } + + //---------------- + // PRIVATE METHODS + //---------------- + +} diff --git a/src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java b/src/share/classes/javax/management/namespace/MBeanServerSupport.java similarity index 99% rename from src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java rename to src/share/classes/javax/management/namespace/MBeanServerSupport.java index e7144cf36..2f0e19838 100644 --- a/src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java +++ b/src/share/classes/javax/management/namespace/MBeanServerSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,9 @@ * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ -package com.sun.jmx.interceptor; +package javax.management.namespace; +import com.sun.jmx.defaults.JmxProperties; import com.sun.jmx.mbeanserver.Util; import java.io.ObjectInputStream; import java.util.Collections; @@ -47,7 +48,6 @@ import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanRegistrationException; import javax.management.MBeanServer; -import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.NotificationBroadcaster; import javax.management.NotificationEmitter; @@ -343,7 +343,7 @@ import javax.management.loading.ClassLoaderRepository; * vem.publish}(name, n). See the example * above.

      * - * @since Java SE 7 + * @since 1.7 */ public abstract class MBeanServerSupport implements MBeanServer { @@ -351,7 +351,7 @@ public abstract class MBeanServerSupport implements MBeanServer { * A logger for this class. */ private static final Logger LOG = - Logger.getLogger(MBeanServerSupport.class.getName()); + JmxProperties.NAMESPACE_LOGGER; /** *

      Make a new {@code MBeanServerSupport} instance.

      @@ -1314,9 +1314,7 @@ public abstract class MBeanServerSupport implements MBeanServer { if (name.getDomain().equals("")) { String defaultDomain = getDefaultDomain(); try { - // XXX change to ObjectName.switchDomain - // current code DOES NOT PRESERVE the order of keys - name = new ObjectName(defaultDomain, name.getKeyPropertyList()); + name = name.withDomain(getDefaultDomain()); } catch (Exception e) { throw newIllegalArgumentException( "Illegal default domain: " + defaultDomain); diff --git a/src/share/classes/javax/management/namespace/VirtualEventManager.java b/src/share/classes/javax/management/namespace/VirtualEventManager.java new file mode 100644 index 000000000..336f37565 --- /dev/null +++ b/src/share/classes/javax/management/namespace/VirtualEventManager.java @@ -0,0 +1,378 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package javax.management.namespace; + +import com.sun.jmx.remote.util.ClassLogger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.management.InstanceNotFoundException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanNotificationInfo; +import javax.management.Notification; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventConsumer; + +/** + *

      This class maintains a list of subscribers for ObjectName patterns and + * allows a notification to be sent to all subscribers for a given ObjectName. + * It is typically used in conjunction with {@link MBeanServerSupport} + * to implement a namespace with Virtual MBeans that can emit notifications. + * The {@code VirtualEventManager} keeps track of the listeners that have been + * added to each Virtual MBean. When an event occurs that should trigger a + * notification from a Virtual MBean, the {@link #publish publish} method can + * be used to send it to the appropriate listeners.

      + * @since 1.7 + */ +public class VirtualEventManager implements EventConsumer { + /** + *

      Create a new {@code VirtualEventManager}.

      + */ + public VirtualEventManager() { + } + + public void subscribe( + ObjectName name, + NotificationListener listener, + NotificationFilter filter, + Object handback) { + + if (logger.traceOn()) + logger.trace("subscribe", "" + name); + + if (name == null) + throw new IllegalArgumentException("Null MBean name"); + + if (listener == null) + throw new IllegalArgumentException("Null listener"); + + Map> map = + name.isPattern() ? patternSubscriptionMap : exactSubscriptionMap; + + final ListenerInfo li = new ListenerInfo(listener, filter, handback); + List list; + + synchronized (map) { + list = map.get(name); + if (list == null) { + list = new ArrayList(); + map.put(name, list); + } + list.add(li); + } + } + + public void unsubscribe( + ObjectName name, NotificationListener listener) + throws ListenerNotFoundException { + + if (logger.traceOn()) + logger.trace("unsubscribe2", "" + name); + + if (name == null) + throw new IllegalArgumentException("Null MBean name"); + + if (listener == null) + throw new ListenerNotFoundException(); + + Map> map = + name.isPattern() ? patternSubscriptionMap : exactSubscriptionMap; + + final ListenerInfo li = new ListenerInfo(listener, null, null); + List list; + synchronized (map) { + list = map.get(name); + if (list == null || !list.remove(li)) + throw new ListenerNotFoundException(); + + if (list.isEmpty()) + map.remove(name); + } + } + + /** + *

      Unsubscribes a listener which is listening to an MBean or a set of + * MBeans represented by an {@code ObjectName} pattern.

      + * + *

      The listener to be removed must have been added by the {@link + * #subscribe subscribe} method with the given {@code name}, {@code filter}, + * and {@code handback}. If the {@code + * name} is a pattern, then the {@code subscribe} must have used the same + * pattern. If the same listener has been subscribed more than once to the + * {@code name} with the same filter and handback, only one listener is + * removed.

      + * + * @param name The name of the MBean or an {@code ObjectName} pattern + * representing a set of MBeans to which the listener was subscribed. + * @param listener A listener that was previously subscribed to the + * MBean(s). + * + * @throws ListenerNotFoundException The given {@code listener} was not + * subscribed to the given {@code name}. + * + * @see #subscribe + */ + public void unsubscribe( + ObjectName name, NotificationListener listener, + NotificationFilter filter, Object handback) + throws ListenerNotFoundException { + + if (logger.traceOn()) + logger.trace("unsubscribe4", "" + name); + + if (name == null) + throw new IllegalArgumentException("Null MBean name"); + + if (listener == null) + throw new ListenerNotFoundException(); + + Map> map = + name.isPattern() ? patternSubscriptionMap : exactSubscriptionMap; + + List list; + synchronized (map) { + list = map.get(name); + boolean removed = false; + for (Iterator it = list.iterator(); it.hasNext(); ) { + ListenerInfo li = it.next(); + if (li.equals(listener, filter, handback)) { + it.remove(); + removed = true; + break; + } + } + if (!removed) + throw new ListenerNotFoundException(); + + if (list.isEmpty()) + map.remove(name); + } + } + + /** + *

      Sends a notification to the subscribers for a given MBean.

      + * + *

      For each listener subscribed with an {@code ObjectName} that either + * is equal to {@code emitterName} or is a pattern that matches {@code + * emitterName}, if the associated filter accepts the notification then it + * is forwarded to the listener.

      + * + * @param emitterName The name of the MBean emitting the notification. + * @param n The notification being sent by the MBean called + * {@code emitterName}. + * + * @throws IllegalArgumentException If the emitterName of the + * notification is null or is an {@code ObjectName} pattern. + */ + public void publish(ObjectName emitterName, Notification n) { + if (logger.traceOn()) + logger.trace("publish", "" + emitterName); + + if (n == null) + throw new IllegalArgumentException("Null notification"); + + if (emitterName == null) { + throw new IllegalArgumentException( + "Null emitter name"); + } else if (emitterName.isPattern()) { + throw new IllegalArgumentException( + "The emitter must not be an ObjectName pattern"); + } + + final List listeners = new ArrayList(); + + // If there are listeners for this exact name, add them. + synchronized (exactSubscriptionMap) { + List exactListeners = + exactSubscriptionMap.get(emitterName); + if (exactListeners != null) + listeners.addAll(exactListeners); + } + + // Loop over subscription patterns, and add all listeners for each + // one that matches the emitterName name. + synchronized (patternSubscriptionMap) { + for (ObjectName on : patternSubscriptionMap.keySet()) { + if (on.apply(emitterName)) + listeners.addAll(patternSubscriptionMap.get(on)); + } + } + + // Send the notification to all the listeners we found. + sendNotif(listeners, n); + } + + /** + *

      Returns a {@link NotificationEmitter} object which can be used to + * subscribe or unsubscribe for notifications with the named + * mbean. The returned object implements {@link + * NotificationEmitter#addNotificationListener + * addNotificationListener(listener, filter, handback)} as + * {@link #subscribe this.subscribe(name, listener, filter, handback)} + * and the two {@code removeNotificationListener} methods from {@link + * NotificationEmitter} as the corresponding {@code unsubscribe} methods + * from this class.

      + * + * @param name The name of the MBean whose notifications are being + * subscribed, or unsuscribed. + * + * @return A {@link NotificationEmitter} + * that can be used to subscribe or unsubscribe for + * notifications emitted by the named MBean, or {@code null} if + * the MBean does not emit notifications and should not + * be considered as a {@code NotificationBroadcaster}. This class + * never returns null but a subclass is allowed to. + * + * @throws InstanceNotFoundException if {@code name} does not exist. + * This implementation never throws {@code InstanceNotFoundException} but + * a subclass is allowed to override this method to do so. + */ + public NotificationEmitter + getNotificationEmitterFor(final ObjectName name) + throws InstanceNotFoundException { + final NotificationEmitter emitter = new NotificationEmitter() { + public void addNotificationListener(NotificationListener listener, + NotificationFilter filter, Object handback) + throws IllegalArgumentException { + subscribe(name, listener, filter, handback); + } + + public void removeNotificationListener( + NotificationListener listener) + throws ListenerNotFoundException { + unsubscribe(name, listener); + } + + public void removeNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object handback) + throws ListenerNotFoundException { + unsubscribe(name, listener, filter, handback); + } + + public MBeanNotificationInfo[] getNotificationInfo() { + // Never called. + return null; + } + }; + return emitter; + } + + // --------------------------------- + // private stuff + // --------------------------------- + + private static class ListenerInfo { + public final NotificationListener listener; + public final NotificationFilter filter; + public final Object handback; + + public ListenerInfo(NotificationListener listener, + NotificationFilter filter, + Object handback) { + + if (listener == null) { + throw new IllegalArgumentException("Null listener."); + } + + this.listener = listener; + this.filter = filter; + this.handback = handback; + } + + /* Two ListenerInfo instances are equal if they have the same + * NotificationListener. This means that we can use List.remove + * to implement the two-argument removeNotificationListener. + */ + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof ListenerInfo)) { + return false; + } + + return listener.equals(((ListenerInfo)o).listener); + } + + /* Method that compares all four fields, appropriate for the + * four-argument removeNotificationListener. + */ + boolean equals( + NotificationListener listener, + NotificationFilter filter, + Object handback) { + return (this.listener == listener && same(this.filter, filter) + && same(this.handback, handback)); + } + + private static boolean same(Object x, Object y) { + if (x == y) + return true; + if (x == null) + return false; + return x.equals(y); + } + + @Override + public int hashCode() { + return listener.hashCode(); + } + } + + private static void sendNotif(List listeners, Notification n) { + for (ListenerInfo li : listeners) { + if (li.filter == null || + li.filter.isNotificationEnabled(n)) { + try { + li.listener.handleNotification(n, li.handback); + } catch (Exception e) { + logger.trace("sendNotif", "handleNotification", e); + } + } + } + } + + // --------------------------------- + // private variables + // --------------------------------- + + private final Map> exactSubscriptionMap = + new HashMap>(); + private final Map> patternSubscriptionMap = + new HashMap>(); + + // trace issue + private static final ClassLogger logger = + new ClassLogger("javax.management.event", "EventManager"); +} diff --git a/src/share/classes/javax/management/namespace/package-info.java b/src/share/classes/javax/management/namespace/package-info.java new file mode 100644 index 000000000..df8354bab --- /dev/null +++ b/src/share/classes/javax/management/namespace/package-info.java @@ -0,0 +1,597 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + *

      The javax.management.namespace package makes it possible + * to federate MBeanServers into a hierarchical name space.

      + * + *

      What Is a Name Space?

      + *

      + * A name space is like an {@link javax.management.MBeanServer} within + * an {@code MBeanServer}. Just as a file system folder can contain + * another file system folder, an {@code MBeanServer} can contain another + * {@code MBeanServer}. Similarly, just as a remote folder on a remote + * disk can be mounted on a parent folder on a local disk, a remote name + * space in a remote {@code MBeanServer} can be mounted on a name + * space in a local parent {@code MBeanServer}. + *

      + *

      + * The javax.management.namespace API thus makes it possible to + * create a hierarchy of MBean servers federated in a hierarchical name + * space inside a single {@code MBeanServer}. + *

      + *

      How To Create a Name Space?

      + *

      + * To create a name space, you only need to register a + * {@link javax.management.namespace.JMXNamespace} MBean in + * an MBean server. We have seen that a namespace is like + * an {@code MBeanServer} within an {@code MBeanServer}, and + * therefore, it is possible to create a namespace that shows the + * content of another {@code MBeanServer}. The simplest case is + * when that {@code MBeanServer} is another {@code MBeanServer} + * created by the {@link javax.management.MBeanServerFactory} as + * shown in the extract below: + *

      + *
      + *  final MBeanServer server = ....;
      + *  final String namespace = "foo";
      + *  final ObjectName namespaceName = {@link javax.management.namespace.JMXNamespaces#getNamespaceObjectName
      + *        JMXNamespaces.getNamespaceObjectName(namespace)};
      + *  server.registerMBean(new JMXNamespace(MBeanServerFactory.newMBeanServer()),
      + *                      namespaceName);
      + *  
      + *

      + * To navigate in namespaces and view their content, the easiest way is + * to use an instance of {@link javax.management.namespace.JMXNamespaceView}. For instance, given + * the {@code server} above, in which we created a namespace {@code "foo"}, + * it is possible to create a {@code JMXNamespaceView} that will make it + * possible to navigate easily in the namespaces and sub-namespaces of that + * server: + *

      + *
      + *  // create a namespace view for 'server'
      + *  final JMXNamespaceView view = new JMXNamespaceView(server);
      + *
      + *  // list all top level namespaces in 'server'
      + *  System.out.println("List of namespaces: " + Arrays.toString({@link javax.management.namespace.JMXNamespaceView#list() view.list()}));
      + *
      + *  // go down into namespace 'foo': provides a namespace view of 'foo' and its
      + *  // sub namespaces...
      + *  final JMXNamespaceView foo = {@link javax.management.namespace.JMXNamespaceView#down view.down("foo")};
      + *
      + *  // list all MBeans contained in namespace 'foo'
      + *  System.out.println({@link javax.management.namespace.JMXNamespaceView#where() foo.where()} + " contains: " +
      + *         {@link javax.management.namespace.JMXNamespaceView#getMBeanServerConnection foo.getMBeanServerConnection()}.queryNames(null,null));
      + *  
      + *

      + * It is also possible to create more complex namespaces, such as namespaces + * that point to MBean servers located in remote JVMs. + *

      + *

      + * For instance, to mount the MBeanServer accessible + * at service:jmx:rmi:///jndi/rmi://localhost:9000/jmxrmi + * in a name space {@code "foo"} inside the {@linkplain + * java.lang.management.ManagementFactory#getPlatformMBeanServer platform + * MBeanServer} you would write the following piece of code: + *

      + *
      + *      final JMXServiceURL sourceURL =
      + *         new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9000/jmxrmi");
      + *      final MBeanServer platform = ManagementFactory.getPlatformMBeanServer();
      + *      final Map<String,Object> options = Collections.emptyMap();
      + *      final JMXRemoteNamespace mbean = {@link
      + *            javax.management.namespace.JMXRemoteNamespace JMXRemoteNamespace}.
      + *         {@link javax.management.namespace.JMXRemoteNamespace#newJMXRemoteNamespace newJMXRemoteNamespace(sourceURL, options)};
      + *      final ObjectName name = {@link javax.management.namespace.JMXNamespaces JMXNamespaces}.{@link javax.management.namespace.JMXNamespaces#getNamespaceObjectName(String) getNamespaceObjectName("foo")};
      + *      final ObjectInstance ref = platform.registerMBean(mbean,name);
      + *      platform.invoke(ref.getObjectName(),"connect",null,null);
      + *  
      + * + *

      What Does a Name Space Look Like?

      + * + *

      + * We have seen that {@link javax.management.namespace.JMXNamespaceView} class + * provides an easy way to navigate within namespaces. It is however also + * possible to interact with namespaces directly from the top level + * {@code MBeanServer} in which they have been created. + * From the outside, a name space only appears as a special MBean in + * the MBean server. There's nothing much you can do with this MBean + * directly. + *

      + *

      + * For instance, let's assume you have registered a {@link + * javax.management.namespace.JMXRemoteNamespaceMBean + * JMXRemoteNamespaceMBean} to manage the name space {@code "foo"}. + *
      If you query for + * platform.queryNames("*//:*",null), then you will see + * one MBean named {@code "foo//:type=JMXNamespace"}. + *
      This is the {@link javax.management.namespace.JMXNamespace} + * MBean which is in charge of handling the namespace {@code "foo"}. + *

      + *

      + * In fact, name space handler MBeans are instances of + * the class {@link javax.management.namespace.JMXNamespace} - or + * instances of a subclass of that class. + * They have a special {@link javax.management.ObjectName} defined by + * {@link javax.management.namespace.JMXNamespaces#getNamespaceObjectName + * JMXNamespaces.getNamespaceObjectName}.
      + * {@link javax.management.namespace.JMXNamespace} instances are able + * to return an {@link + * javax.management.namespace.JMXNamespace#getSourceServer MBeanServer} + * which corresponds to the MBeanServer within (= the name space itself). + *

      + *

      + * So how does it work? How can you see the MBeans contained in the new + * name space? + *

      + *

      In order to address scalability issues, MBeans registered in + * namespaces (such as our namespace {@code "foo"} above) can not be + * seen with {@code mbeanServer.queryNames("*:*",null)}. To see the MBeans + * contained in a namespace, you can use one of these methods: + *

      + *
        + *
      1. + * You can use the {@link javax.management.namespace.JMXNamespaceView} + * class shown above, + *
      2. + *
      3. + * or you can directly look for MBeans + * whose names match + * {@code "foo//*:*"}, + *
      4. + *
      5. + * or you can narrow down to the namespace + * and obtain an MBeanServer + * proxy that corresponds to an MBeanServer view of that namespace. + * The JMXNamespaces class provides a static method that + * allows you to narrow down to a name space, by calling + * {@link javax.management.namespace.JMXNamespaces#narrowToNamespace(MBeanServer,String) + * JMXNamespaces.narrowToNamespace}. + *
      6. + *
      + * + *

      Using Name Space Prefixes

      + *

      + * As we have explained above, MBeans contained in name + * spaces are not returned by {@code server.queryNames(null,null)} - or + * server.queryNames({@link javax.management.ObjectName#WILDCARD ObjectName.WILDCARD},null). + *
      + * However, these MBeans can still be accessed from the top level + * {@code MBeanServer} interface, without using any API specific to the + * version 2.0 of the JMX API, simply by using object names with + * name space prefixes: + *
      To list MBeans contained in a namespace {@code "foo"} you can + * query for MBeans whose names match {@code "foo//*:*"}, as shown + * earlier in this document: + *

      + *         server.queryNames(new ObjectName("foo//*:*", null);
      + *         // or equivalently:
      + *         server.queryNames(JMXNamespaces.getWildcardFor("foo"), null);
      + *      
      + * This will return a list of MBean names whose domain name starts + * with {@code foo//}. + *

      + * Using these names, you can invoke any operation on the corresponding + * MBeans. For instance, to get the {@link javax.management.MBeanInfo + * MBeanInfo} of an MBean + * contained in name space {@code "foo"} (assuming + * the name of the MBean within its name space is domain:type=Thing, + * then simply call: + *

      + *         server.getMBeanInfo(new ObjectName("foo//domain:type=Thing"));
      + *      
      + * An easier way to access MBeans contained in a name space is to + * cd inside the name space, as shown in the following paragraph. + *

      + * + *

      Narrowing Down Into a Name Spaces

      + *

      + * As we have seen, name spaces are like MBean servers within MBean servers. + * Therefore, it is possible to view a name space just as if it were + * an other MBean server. This is similar to opening a sub + * folder from a parent folder.
      + * This operation is illustrated in the code extract below: + *

      + *          final MBeanServer foo =
      + *                JMXNamespaces.narrowToNamespace(platform, "foo");
      + *          final MBeanInfo info =
      + *                foo.getMBeanInfo(new ObjectName("domain:type=Thing"));
      + *      
      + * The {@code MBeanServer} returned by {@link + * javax.management.namespace.JMXNamespaces#narrowToNamespace(MBeanServer,String) + * JMXNamespaces.narrowToNamespace} is an {@code MBeanServer} view that + * narrows down into a given namespace. The MBeans contained inside that + * namespace can now be accessed by their regular local name.
      + * The MBean server obtained by narrowing down + * to name space {@code "foo"} behaves just like a regular MBean server. + * However, it may sometimes throw an {@link + * java.lang.UnsupportedOperationException UnsupportedOperationException} + * wrapped in a JMX exception if you try to call an operation which is not + * supported by the underlying name space handler. + *
      For instance, {@link javax.management.MBeanServer#registerMBean + * registerMBean} is not supported for name spaces mounted from remote + * MBean servers. + *

      + *

      + * Note: If you have a deep hierarchy of namespaces, and if you + * are switching from one namespace to another in the course of your + * application, it might be more convenient to use a + * {@link javax.management.namespace.JMXNamespaceView} + * in order to navigate in your namespaces. + *

      + * + *

      Different Types of Name Spaces

      + *

      + * This API lets you create several types of name spaces: + *

        + *
      • + * You can use the {@link + * javax.management.namespace.JMXRemoteNamespace + * JMXRemoteNamespace} to create + * remote name spaces, mounted from + * a remote sub {@code MBeanServer} source, as shown + * earlier in this document. + *
      • + *
      • + * You can also use {@link + * javax.management.namespace.JMXNamespace + * JMXNamespace} to create + * local name spaces, + * by providing a direct reference to another {@code MBeanServer} + * instance living in the same JVM. + *
      • + *
      • + * Finally, you can create + * name spaces containing virtual MBeans, + * by subclassing the {@link + * javax.management.namespace.MBeanServerSupport + * MBeanServerSupport}, and passing an instance of + * your own subclass to a {@link + * javax.management.namespace.JMXNamespace JMXNamespace}. + *
      • + *
      • + * If none of these classes suit your needs, you can also provide + * your own subclass of {@link + * javax.management.namespace.JMXNamespace + * JMXNamespace}. This is however discouraged. + *
      • + *
      + *

      + * + *

      Name Spaces And Special Operations

      + *

      + * MBean Naming considerations aside, Name Spaces are transparent for + * most {@code MBeanServer} operations. There are however a few + * exceptions: + *

      + *
        + *
      • + *

        MBeanServer only operations - these are the operations which are + * supported by {@link javax.management.MBeanServer MBeanServer} but + * are not present in {@link + * javax.management.MBeanServerConnection + * MBeanServerConnection}. Since a name space can be a local view of + * a remote {@code MBeanServer}, accessible only through an + * {@code MBeanServerConnection}, these + * kinds of operations are not always supported.

        + *
          + *
        • + *

          registerMBean:

          + *

          The {@link javax.management.MBeanServer#registerMBean + * registerMBean} + * operation is not supported by most name spaces. A call + * to + *

          + *   MBeanServer server = ....;
          + *   ThingMBean mbean = new Thing(...);
          + *   ObjectName name = new ObjectName("foo//domain:type=Thing");
          + *   server.registerMBean(mbean, name);
          + *                          
          + * will usually fail, unless the name space + * {@code "foo"} is a local name + * space. In the case where you attempt to cross + * multiple name spaces, then all name spaces in the + * path must support the {@code registerMBean} operation + * in order for it to succeed.
          + * To create an MBean inside a name space, it is + * usually safer to use {@code createMBean} - + * although some special + * considerations can also apply. + *

          + *

          + *
        • + *
        • + *

          getClassLoader:

          + *

          Similarly to registerMBean, + * and for the same reasons, {@link + * javax.management.MBeanServer#getClassLoader + * getClassLoader} will usually fail, unless the + * class loader is an MBean registered in a + * local name space.
          + *

          + *
        • + *
        • + *

          getClassLoaderFor:

          + *

          The implementation of {@link + * javax.management.MBeanServer#getClassLoaderFor + * getClassLoaderFor} also depends on which + * type of name space + * handler is used across the namespace path. + *

          + *

          + * A local name space will usually + * be able to implement this method just as a real + * {@code MBeanServer} would. A + * remote name space will usually + * return the default class loader configured on the + * internal {@link javax.management.remote.JMXConnector + * JMXConnector} used to connect to the remote server. + * When a {@link + * javax.management.namespace.JMXRemoteNamespace + * JMXRemoteNamespace} is used to connect to a + * remote server that contains MBeans which export + * custom types, the {@link + * javax.management.namespace.JMXRemoteNamespace + * JMXRemoteNamespace} must thus be configured with + * an options map such that the underlying connector + * can obtain a default class loader able + * to handle those types. + *

          + *

          + * Other types of name spaces + * may implement this method + * as best as they can. + *

          + *
        • + *
        + *
      • + *
      • + *

        MBean creation

        + *

        MBean creation through {@link + * javax.management.MBeanServerConnection#createMBean + * createMBean} might not be supported by all + * name spaces: local name spaces and + * remote name spaces will usually + * support it, but virtual name + * spaces and custom name + * spaces might not. + *

        + *

        + * In that case, they will throw an {@link + * java.lang.UnsupportedOperationException + * UnsupportedOperationException} usually wrapped into an {@link + * javax.management.MBeanRegistrationException}. + *

        + *
      • + *
      • + *

        Notifications

        + *

        Some namespaces might not support JMX Notifications. In that + * case, a call to add or remove notification listener for an + * MBean contained in that name space will raise a + * {@link javax.management.RuntimeOperationsException + * RuntimeOperationsException} wrapping an {@link + * java.lang.UnsupportedOperationException + * UnsupportedOperationException} exception. + *

        + *
      • + *
      + * + *

      Crossing Several Name Spaces

      + *

      + * Just as folders can contain other folders, name spaces can contain + * other name spaces. For instance, if an {@code MBeanServer} S1 + * containing a name space {@code "bar"} is mounted in another + * {@code MBeanServer} S2 with name space {@code "foo"}, then + * an MBean M1 named {@code "domain:type=Thing"} in namespace + * {@code "bar"} will appear as {@code "foo//bar//domain:type=Thing"} in + * {@code MBeanServer} S2. + *

      + *

      + * When accessing the MBean M1 from server S2, the + * method call will traverse in a cascade {@code MBeanServer} S2, + * then the name space handler for name space {@code "foo"}, then + * {@code MBeanServer} S1, before coming to the name space + * handler for name space {@code "bar"}. Any operation invoked + * on the MBean from a "top-level" name space will therefore need to + * traverse all the name spaces along the name space path until + * it eventually reaches the named MBean. This means that an operation + * like registerMBean for instance, + * can only succeed if all the name spaces along the path support it. + *

      + *

      + * Narrowing to a nested name space works just the same as narrowing + * to a top level name space: + *

      + *          final MBeanServer S2 = .... ;
      + *          final MBeanServer bar =
      + *                JMXNamespaces.narrowToNamespace(S2, "foo//bar");
      + *          final MBeanInfo info =
      + *                foo.getMBeanInfo(new ObjectName("domain:type=Thing"));
      + *      
      + *

      + * + *

      Name Spaces And Operation Results

      + *

      + * Operation results, as well as attribute values returned by an MBean + * contained in a name space must be interpreted in the context of that + * name space.
      + * In other words, if an MBean in name space "foo" has an attribute of + * type {@code ObjectName}, then it must be assumed that the + * {@code ObjectName} returned by that MBean is relative to + * name space "foo".
      + * The same rule aplies for MBean names that can be returned by + * operations invoked on such an MBean. If one of the MBean operations + * return, say, a {@code Set} then those MBean names must + * also be assumed to be relative to name space "foo".
      + *

      + *

      + * In the usual case, a JMX client will first + * narrow to a name space before invoking + * any operation on the MBeans it contains. In that case the names + * returned by the MBean invoked can be directly fed back to the + * narrowed connection. + *
      + * If however, the JMX client directly invoked the MBean from a higher + * name space, without having narrowed to that name space first, then + * the names that might be returned by that MBean will not be directly + * usable - the JMX client will need to either + * narrow to the name space before using the + * returned names, or convert the names to the higher level name space + * context. + *
      + * The {@link javax.management.namespace.JMXNamespaces JMXNamespaces} + * class provides methods that can be used to perform that conversion. + *

      + * + *

      Name Spaces And Notifications

      + *

      + * As already explained, name spaces are very + * similar to {@code MBeanServer}s. It is thus possible to get + * {@link javax.management.MBeanServerNotification MBeanServerNotifications} + * when MBeans are added or removed within a name space, by registering + * with the {@link javax.management.MBeanServerDelegate + * MBeanServerDelegate} MBean of the corresponding name space.
      + * However, it must be noted that the notifications emitted by a + * name space must be interpreted in the context of that name space. + * For instance, if an MBean {@code "domain:type=Thing"} contained in + * namespace "foo//bar" emits a notification, the source of the + * notification will be {@code "domain:type=Thing"}, not + * {@code "foo//bar//domain:type=Thing"}.
      + * It is therefore recommended to keep track of the name space + * information when registering a listener with an MBean contained in + * a name space, especially if the same listener is used to receive + * notifications from different name spaces. An easy solution is to + * use the handback, as illustrated in the code below. + *

      + *            final MBeanServer server = ...;
      + *            final NotificationListener listener = new NotificationListener() {
      + *                public void handleNotification(Notification n, Object handback) {
      + *                    if (!(n instanceof MBeanServerNotification)) {
      + *                        System.err.println("Error: expected MBeanServerNotification");
      + *                        return;
      + *                    }
      + *                    final MBeanServerNotification mbsn =
      + *                            (MBeanServerNotification) n;
      + *
      + *                    // We will pass the namespace path in the handback.
      + *                    //
      + *                    // The received notification must be interpreted in
      + *                    // the context of its source - therefore
      + *                    // mbsn.getMBeanName() does not include the name space
      + *                    // path...
      + *                    //
      + *                    final String namespace = (String) handback;
      + *                    System.out.println("Received " + mbsn.getType() +
      + *                            " for MBean " + mbsn.getMBeanName() +
      + *                            " from name space " + namespace);
      + *                }
      + *            };
      + *            server.addNotificationListener(JMXNamespaces.insertPath("foo//bar",
      + *                    MBeanServerDelegate.DELEGATE_NAME),listener,null,"foo//bar");
      + *            server.addNotificationListener(JMXNamespaces.insertPath("foo//joe",
      + *                    MBeanServerDelegate.DELEGATE_NAME),listener,null,"foo//joe");
      + *          
      + *

      + *

      + * JMX Connectors may require some configuration in order to be able + * to forward notifications from MBeans located in name spaces. + * The RMI JMX Connector Server + * in the Java SE 7 platform is configured by default to internally + * use the new {@linkplain javax.management.event event service} on + * the server side. + * When the connector server is configured in this way, JMX clients + * which use the old JMX Notifications mechanism (such as clients + * running on prior versions of the JDK) will be able to + * to receive notifications from MBeans located in sub name spaces. + * This is because the connector server will transparently delegate + * their subscriptions to the underlying {@linkplain + * javax.management.event event service}. In summary: + *

        + *
      • + * On the server side: When exporting an {@code MBeanServer} + * through a JMX Connector, you will need to make sure that the + * connector server uses the new {@linkplain javax.management.event + * event service} in order to register for notifications. If the + * connector server doesn't use the event service, only clients + * which explicitly use the new {@linkplain javax.management.event + * event service} will be able to register for notifications + * with MBeans located in sub name spaces. + *
      • + *
      • + * On the client side: if the JMX Connector server (on the remote + * server side) was configured to internally use the new + * {@linkplain javax.management.event + * event service}, then clients can continue to use the old + * {@code MBeanServerConnection} add / remove notification + * listener methods transparently. Otherwise, only clients which + * explicitly use the new {@linkplain javax.management.event + * event service} will be able to receive notifications from + * MBeans contained in sub name spaces. + *
      • + *
      + *

      + *

      + * These configuration issues apply at each node in the name space path, + * whenever the name space points to a remote server. The + * {@link javax.management.namespace.JMXRemoteNamespace + * JMXRemoteNamespace} can be configured in such a way that it will + * explicitly use an {@link javax.management.event.EventClient EventClient} + * when forwarding subscription to the remote side. Note that this can be + * unnecessary (and a waste of resources) if the underlying JMXConnector + * returned by the JMXConnectorFactory (client side) already uses the + * {@linkplain javax.management.event event service} to register for + * notifications with the server side. + *

      + * + *

      Name Spaces And Access Control

      + *

      + * Access to MBeans exposed through JMX namespaces is controlled by + * {@linkplain javax.management.namespace.JMXNamespacePermission + * jmx namespace permissions}. These permissions are checked by the + * MBeanServer in which the {@link + * javax.management.namespace.JMXNamespace JMXNamespace} MBean is registered. + * This is described in + * details in the {@link + * javax.management.namespace.JMXNamespace JMXNamespace} class. + *

      + *

      + * To implement a "firewall-like" access control in a JMX agent you + * can also place an {@link + * javax.management.remote.MBeanServerForwarder} in the JMX Connector + * Server which exposes the top-level MBeanServer of your application. + * This {@code MBeanServerForwarder} will be able to perform + * authorization checks for all MBeans, including those located in + * sub name spaces. + *

      + *

      + * For a tighter access control we recommend using a {@link + * java.lang.SecurityManager security manager}. + *

      + * @since 1.7 + *

      + **/ + +package javax.management.namespace; + diff --git a/src/share/classes/javax/management/remote/JMXConnectorFactory.java b/src/share/classes/javax/management/remote/JMXConnectorFactory.java index 23003f007..219865b60 100644 --- a/src/share/classes/javax/management/remote/JMXConnectorFactory.java +++ b/src/share/classes/javax/management/remote/JMXConnectorFactory.java @@ -268,6 +268,14 @@ public class JMXConnectorFactory { return conn; } + private static Map newHashMap() { + return new HashMap(); + } + + private static Map newHashMap(Map map) { + return new HashMap(map); + } + /** *

      Creates a connector client for the connector server at the * given address. The resultant client is not connected until its @@ -300,16 +308,18 @@ public class JMXConnectorFactory { public static JMXConnector newJMXConnector(JMXServiceURL serviceURL, Map environment) throws IOException { - Map envcopy; + + final Map envcopy; if (environment == null) - envcopy = new HashMap(); + envcopy = newHashMap(); else { EnvHelp.checkAttributes(environment); - envcopy = new HashMap(environment); + envcopy = newHashMap(environment); } final ClassLoader loader = resolveClassLoader(envcopy); - final Class targetInterface = JMXConnectorProvider.class; + final Class targetInterface = + JMXConnectorProvider.class; final String protocol = serviceURL.getProtocol(); final String providerClassName = "ClientProvider"; @@ -351,9 +361,10 @@ public class JMXConnectorFactory { } } - envcopy = Collections.unmodifiableMap(envcopy); + final Map fixedenv = + Collections.unmodifiableMap(envcopy); - return provider.newJMXConnector(serviceURL, envcopy); + return provider.newJMXConnector(serviceURL, fixedenv); } private static String resolvePkgs(Map env) throws JMXProviderException { @@ -365,8 +376,8 @@ public class JMXConnectorFactory { if (pkgsObject == null) pkgsObject = - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { + AccessController.doPrivileged(new PrivilegedAction() { + public String run() { return System.getProperty(PROTOCOL_PROVIDER_PACKAGES); } }); @@ -423,8 +434,7 @@ public class JMXConnectorFactory { static Iterator getProviderIterator(final Class providerClass, final ClassLoader loader) { ServiceLoader serviceLoader = - ServiceLoader.load(providerClass, - loader); + ServiceLoader.load(providerClass, loader); return serviceLoader.iterator(); } @@ -528,8 +538,8 @@ public class JMXConnectorFactory { } if (loader == null) - loader = - AccessController.doPrivileged(new PrivilegedAction() { + loader = AccessController.doPrivileged( + new PrivilegedAction() { public ClassLoader run() { return Thread.currentThread().getContextClassLoader(); diff --git a/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java b/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java index 8f5119242..31964ebaa 100644 --- a/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java +++ b/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java @@ -77,6 +77,7 @@ import javax.management.event.EventClientDelegate; import javax.management.event.EventClientDelegateMBean; import javax.management.event.EventClientNotFoundException; import javax.management.event.FetchingEventForwarder; +import javax.management.namespace.JMXNamespaces; import javax.management.remote.JMXServerErrorException; import javax.management.remote.NotificationResult; import javax.management.remote.TargetedNotification; @@ -1292,11 +1293,27 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { public void removeNotificationListener(ObjectName name, Integer id) throws InstanceNotFoundException, ListenerNotFoundException, IOException { + if (!JMXNamespaces.getContainingNamespace(name).equals("")) { + logger.debug("removeNotificationListener", + "This connector server is not configured to support " + + "forwarding of notification subscriptions to name spaces"); + throw new RuntimeOperationsException( + new UnsupportedOperationException( + "removeNotificationListener on name space MBeans. ")); + } forwarder.removeNotificationListener(name,id); } public void removeNotificationListener(ObjectName name, Integer[] ids) throws Exception { + if (!JMXNamespaces.getContainingNamespace(name).equals("")) { + logger.debug("removeNotificationListener", + "This connector server is not configured to support " + + "forwarding of notification subscriptions to name spaces"); + throw new RuntimeOperationsException( + new UnsupportedOperationException( + "removeNotificationListener on name space MBeans. ")); + } forwarder.removeNotificationListener(name,ids); } @@ -1307,6 +1324,14 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { public Integer addNotificationListener(ObjectName name, NotificationFilter filter) throws InstanceNotFoundException, IOException { + if (!JMXNamespaces.getContainingNamespace(name).equals("")) { + logger.debug("addNotificationListener", + "This connector server is not configured to support " + + "forwarding of notification subscriptions to name spaces"); + throw new RuntimeOperationsException( + new UnsupportedOperationException( + "addNotificationListener on name space MBeans. ")); + } return forwarder.addNotificationListener(name,filter); } @@ -1326,6 +1351,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { private final boolean checkNotificationEmission; private final String clientId; private final String connectionId; + private volatile String mbeanServerName; EventSubscriptionManager( MBeanServer mbeanServer, @@ -1343,6 +1369,11 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { this.connectionId = connectionId; } + private String mbeanServerName() { + if (mbeanServerName != null) return mbeanServerName; + else return (mbeanServerName = getMBeanServerName(mbeanServer)); + } + @SuppressWarnings("serial") // no serialVersionUID private class AccessControlFilter implements NotificationFilter { private final NotificationFilter wrapped; @@ -1357,7 +1388,8 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { try { if (checkNotificationEmission) { ServerNotifForwarder.checkMBeanPermission( - mbeanServer, name, "addNotificationListener"); + mbeanServerName(), mbeanServer, name, + "addNotificationListener"); } notifAC.fetchNotification( connectionId, name, notification, getSubject()); @@ -1392,7 +1424,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { if (notifAC != null) notifAC.removeNotificationListener(connectionId, name, getSubject()); try { - delegate.removeListenerOrSubscriber(clientId,id); + delegate.removeListenerOrSubscriber(clientId, id); } catch (EventClientNotFoundException x) { throw new IOException("Unknown clientId: "+clientId,x); } @@ -1405,7 +1437,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { notifAC.removeNotificationListener(connectionId, name, getSubject()); try { for (Integer id : ids) - delegate.removeListenerOrSubscriber(clientId,id); + delegate.removeListenerOrSubscriber(clientId, id); } catch (EventClientNotFoundException x) { throw new IOException("Unknown clientId: "+clientId,x); } @@ -1867,6 +1899,15 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced { return e; } + private static String getMBeanServerName(final MBeanServer server) { + final PrivilegedAction action = new PrivilegedAction() { + public String run() { + return Util.getMBeanServerSecurityName(server); + } + }; + return AccessController.doPrivileged(action); + } + private static final Object[] NO_OBJECTS = new Object[0]; private static final String[] NO_STRINGS = new String[0]; diff --git a/test/javax/management/MBeanServerFactory/NamedMBeanServerTest.java b/test/javax/management/MBeanServerFactory/NamedMBeanServerTest.java new file mode 100644 index 000000000..e26168318 --- /dev/null +++ b/test/javax/management/MBeanServerFactory/NamedMBeanServerTest.java @@ -0,0 +1,440 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @summary Test named MBeanServers. + * @author Daniel Fuchs + * @run clean NamedMBeanServerTest + * @run build NamedMBeanServerTest + * @run main NamedMBeanServerTest + */ + +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.management.MBeanServer; +import javax.management.MBeanServerBuilder; +import javax.management.MBeanServerDelegate; +import javax.management.MBeanServerFactory; + +/** + * This test can probably be leveraged in the JCK to test compatibilty + * of MBeanServerFactory *Name* method implementation. + * @author dfuchs + */ +public class NamedMBeanServerTest { + + /** + * One enum value for each way of creating an MBeanServer through the + * MBeanServerFactory + */ + public static enum Creator { + newMBeanServer() { + public MBeanServer create(String domain) { + return MBeanServerFactory.newMBeanServer(domain); + } + public String test(MBeanServer server, String domain) { + System.out.println(toString()+"("+domain+")"); + return test(server, + MBeanServerFactory.DEFAULT_MBEANSERVER_NAME, + domain); + } + public MBeanServer[] servers(Config config) { + return config.ndServers; + } + public String[] strings(Config config) { + return domains(config); + } + public String[] domains(Config config) { + return config.newDomains; + } + public String[] names(Config config) { + return null; + } + }, + createMBeanServer() { + public MBeanServer create(String domain) { + return MBeanServerFactory.createMBeanServer(domain); + } + public String test(MBeanServer server, String domain) { + System.out.println(toString()+"("+domain+")"); + return test(server,MBeanServerFactory.DEFAULT_MBEANSERVER_NAME, + domain); + } + public MBeanServer[] servers(Config config) { + return config.cdServers; + } + public String[] strings(Config config) { + return domains(config); + } + public String[] domains(Config config) { + return config.createDomains; + } + public String[] names(Config config) { + return null; + } + }, + newNamedMBeanServer() { + public MBeanServer create(String name) { + return MBeanServerFactory.newNamedMBeanServer(name,null); + } + public String test(MBeanServer server, String name) { + System.out.println(toString()+"("+name+",null)"); + return test(server,name,"DefaultDomain"); + } + public MBeanServer[] servers(Config config) { + return config.nnServers; + } + public String[] strings(Config config) { + return names(config); + } + public String[] domains(Config config) { + return null; + } + public String[] names(Config config) { + return config.newNames; + } + }, + createNamedMBeanServer() { + public MBeanServer create(String name) { + return MBeanServerFactory.createNamedMBeanServer(name,null); + } + public String test(MBeanServer server, String name) { + System.out.println(toString()+"("+name+",null)"); + return test(server,name,"DefaultDomain"); + } + public MBeanServer[] servers(Config config) { + return config.cnServers; + } + public String[] strings(Config config) { + return names(config); + } + public String[] domains(Config config) { + return null; + } + public String[] names(Config config) { + return config.createNames; + } + }; + + // creates an MBeanServer using the specified input string. + // either a domain, (for UNNAMED) or a mbeanServerName (for NAMED) + public abstract MBeanServer create(String string); + + // test the created server against the string used as input to create + // it. + public abstract String test(MBeanServer server, String ref); + + public abstract MBeanServer[] servers(Config config); + public abstract String[] strings(Config config); + public abstract String[] names(Config config); + public abstract String[] domains(Config config); + + public MBeanServer[] servers(Config config, String... refs) { + final MBeanServer[] servers = servers(config); + final String[] strings = strings(config); + final MBeanServer[] res = new MBeanServer[refs.length]; + for (int i=0;i found = + MBeanServerFactory.findMBeanServerByName(name); + if (!registered && found.contains(server)) + return " Server "+name+" found by name - " + + "but should not be registered"; + if (!registered && + !name.equals(MBeanServerFactory.DEFAULT_MBEANSERVER_NAME) && + found.size()>0) + return " Server "+name+" had too many matches: " + found.size(); + if (registered && !found.contains(server)) + return " Server "+name+" not found by name - " + + "but is registered!"; + if (registered && + !name.equals(MBeanServerFactory.DEFAULT_MBEANSERVER_NAME) && + !(found.size()==1)) + return " Server "+name+" had too many matches: " + found.size(); + return null; + } + + public static final EnumSet NAMED = + EnumSet.of(createNamedMBeanServer, newNamedMBeanServer); + public static final EnumSet UNNAMED = + EnumSet.complementOf(NAMED); + public static final EnumSet REFERENCED = + EnumSet.of(createMBeanServer, createNamedMBeanServer); + public static final EnumSet UNREFERENCED = + EnumSet.complementOf(REFERENCED); + + } + + public static class Config { + final String[] newDomains; + final String[] createDomains; + final String[] newNames; + final String[] createNames; + final MBeanServer[] ndServers; + final MBeanServer[] cdServers; + final MBeanServer[] nnServers; + final MBeanServer[] cnServers; + final Map> queries; + Config(String[][] data) { + this(data[0],data[1],data[2],data[3]); + } + Config(String[] nd, String[] cd, String[] nn, String[] cn) { + this.newDomains=nd.clone(); + this.createDomains=cd.clone(); + this.newNames=nn.clone(); + this.createNames=cn.clone(); + ndServers = new MBeanServer[nd.length]; + cdServers = new MBeanServer[cd.length]; + nnServers = new MBeanServer[nn.length]; + cnServers = new MBeanServer[cn.length]; + queries = new HashMap>(); + init(); + } + private void init() { + for (Creator c : Creator.values()) fill(c); + addQuery(null,Creator.createMBeanServer.servers(this)); + addQuery(null,Creator.createNamedMBeanServer.servers(this)); + addQuery("?*",Creator.createMBeanServer.servers(this)); + addQuery("?*",Creator.createNamedMBeanServer.servers(this)); + addQuery("*",Creator.createMBeanServer.servers(this)); + addQuery("*",Creator.createNamedMBeanServer.servers(this)); + addQuery(MBeanServerFactory.DEFAULT_MBEANSERVER_NAME, + Creator.createMBeanServer.servers(this)); + } + private void addQuery(String pattern, MBeanServer... servers) { + final Set s = getQuery(pattern); + s.addAll(Arrays.asList(servers)); + } + public Set getQuery(String pattern) { + final Set s = queries.get(pattern); + if (s != null) return s; + queries.put(pattern,new HashSet()); + return queries.get(pattern); + } + public Set getPatterns() { + return queries.keySet(); + } + private void fill(Creator creator) { + fill(creator.servers(this),creator.strings(this),creator); + } + private void fill(MBeanServer[] dest, String[] src, Creator creator) { + for(int i=0;i found = + MBeanServerFactory.findMBeanServerByName(pat); + String sep=" "; + for (MBeanServer m : found) { + System.out.print(sep+MBeanServerFactory.getMBeanServerName(m)); + sep=", "; + } + System.out.println(" ]"); + final Set founds = new HashSet(); + founds.addAll(found); + if (!founds.equals(config.getQuery(pat))) { + final String msg = + "bad result for findMBeanServerByName(\""+ + pat+"\"): expected "+config.getQuery(pat).size()+", "+ + "got "+founds.size(); + throw new Exception(msg); + } + } + } + + public static void testexception(Creator c, String name, + Class error) throws Exception { + Exception failed = null; + MBeanServer server = null; + try { + server = c.create(name); + } catch (Exception x) { + failed = x; + } finally { + if (Creator.REFERENCED.contains(c) && server!=null) { + MBeanServerFactory.releaseMBeanServer(server); + } + } + if (failed == null && error != null) { + throw new Exception("Expected "+error.getName()+ + " for "+c+"("+name+")"); + } + if (error != null && !error.isInstance(failed)) + throw new Exception("Expected "+error.getName()+ + " for "+c+"("+name+"), caught "+failed); + System.out.println(""+c+"("+name+") PASSED: "+ + (failed==null?"no exception":String.valueOf(failed))); + } + + private static final Map> failures = + new LinkedHashMap>(); + private static final Map> legacy = + new LinkedHashMap>(); + private static final String[] illegalnames = { + "", "-", ":", ";", "?", "*", "wom?bat", "ran:tan.plan", + "rin;tin.tin", "tab*mow" + + }; + private static final String[] legalnames = { + "wombat", "top.tip", "ran.tan.plan", "rin.tin.tin!" + }; + private static final String[] nofailures = { + MBeanServerFactory.DEFAULT_MBEANSERVER_NAME, "default", null + }; + static { + for (String s:illegalnames) + failures.put(s, IllegalArgumentException.class); + for (String s:nofailures) + failures.put(s, null); + legacy.putAll(failures); + for (String s:legalnames) + legacy.put(s, UnsupportedOperationException.class); + + } + + public static void test2(Map> config) + throws Exception { + for (Creator c:Creator.NAMED) { + for (String s:config.keySet()) testexception(c, s, config.get(s)); + } + } + + public static class LegacyBuilder extends MBeanServerBuilder { + + @Override + public MBeanServerDelegate newMBeanServerDelegate() { + return new MBeanServerDelegate() { + @Override + public synchronized String getMBeanServerId() { + return "gloups"; + } + }; + } + + } + public static class LegacyBuilder2 extends MBeanServerBuilder { + + @Override + public MBeanServerDelegate newMBeanServerDelegate() { + return new MBeanServerDelegate() { + @Override + public synchronized String getMBeanServerId() { + return "c'est la vie..."; + } + @Override + public synchronized void setMBeanServerName(String name) { + } + + }; + } + + } + + public static void test3(Map> config, + String builderClassName) + throws Exception { + final String builder = + System.getProperty("javax.management.builder.initial"); + System.setProperty("javax.management.builder.initial", + builderClassName); + try { + test2(config); + } finally { + if (builder != null) + System.setProperty("javax.management.builder.initial", builder); + else + System.clearProperty("javax.management.builder.initial"); + } + } + + public static void main(String[] args) throws Exception { + test(test1); + test2(failures); + test3(legacy,LegacyBuilder.class.getName()); + test3(legacy,LegacyBuilder2.class.getName()); + } +} diff --git a/test/javax/management/ObjectName/ApplyWildcardTest.java b/test/javax/management/ObjectName/ApplyWildcardTest.java index b265e7bf6..f3544ffcf 100644 --- a/test/javax/management/ObjectName/ApplyWildcardTest.java +++ b/test/javax/management/ObjectName/ApplyWildcardTest.java @@ -28,10 +28,13 @@ * with wildcards in the key properties value part. * @author Luis-Miguel Alventosa * @run clean ApplyWildcardTest + * @compile -XDignore.symbol.file=true ApplyWildcardTest.java * @run build ApplyWildcardTest * @run main ApplyWildcardTest */ +import com.sun.jmx.mbeanserver.Repository; +import com.sun.jmx.mbeanserver.Util; import javax.management.ObjectName; public class ApplyWildcardTest { @@ -74,6 +77,75 @@ public class ApplyWildcardTest { { "d:k1=\"a?b\",k2=\"c*d\"", "d:k1=\"axb\",k2=\"cyzd\"" }, { "d:k1=\"a?b\",k2=\"c*d\",*", "d:k1=\"axb\",k2=\"cyzd\",k3=\"v3\"" }, { "d:*,k1=\"a?b\",k2=\"c*d\"", "d:k1=\"axb\",k2=\"cyzd\",k3=\"v3\"" }, + + // with namespaces + + { "*//:*", "d//:k=v" }, + { "//?:*", "///:k=v" }, + { "z*x//:*", "zaxcx//:k=v" }, + { "*//:*", "d/xx/q//:k=v" }, + { "z*x//:*", "z/a/x/c/x//:k=v" }, + { "*x?//:*", "dbdbdxk//:k=v" }, + { "z*x?x//:*", "zaxcx//:k=v" }, + { "*x?f//:*", "d/xxf/qxbf//:k=v" }, + { "z*x?c*x//:*", "z/a/x/c/x//:k=v" }, + + { "*//*:*", "d/c/v//x/vgh/:k=v" }, + { "z*x//z*x:*", "zaxcx//zaxcxcx:k=v" }, + { "//*//:*", "//d/xx/q//:k=v" }, + { "z*//*//:*", "z/x/x/z//z/a/x/c/x//:k=v" }, + { "*x?//blur?g*:*", "dbdbdxk//blurhgblurgh/x/:k=v" }, + { "z*x??x//??:*", "zaxcxccx///.:k=v" }, + { "*x?f//?:*", "d/xxf/qxbf///:k=v" }, + { "z*x?c*x//*//z????//g:*", "z/a/x/c/x//gloubs/././/zargh//g:k=v" }, + { "z*x?c*x//*//:*", "z/a/x/c/x//gloubs/././/:k=v"}, + { "*//*//:*", "aza//bzb//:k=v" }, + { "*//:*", "aza//:k=v" }, + + // with or without namespaces, * can also match nothing + { "x*z:*", "xz:k=v"}, + + { "*//:*", "//:k=v" }, + { "z*x//:*", "zx//:k=v" }, + { "*x?//:*", "xk//:k=v" }, + { "z*x?x//:*", "zxcx//:k=v" }, + { "*x?f//:*", "xbf//:k=v" }, + { "z*x?c*x//:*", "zx/cx//:k=v" }, + + { "*//*:*", "//:k=v" }, + { "z*x//z*x:*", "zx//zx:k=v" }, + { "//*//:*", "////:k=v" }, + { "z*//*//:*", "z////:k=v" }, + { "*x?//blur?g*:*", "xk//blurhg:k=v" }, + { "z*x??x//??:*", "zxccx///.:k=v" }, + { "*x?f//?:*", "xbf///:k=v" }, + { "z*x?c*x//*//z????//g:*", "zx/cx////zargh//g:k=v" }, + { "z*x?c*x//*//:*", "zx/cx////:k=v"}, + { "*//*//:*", "////:k=v" }, + { "*//:*", "//:k=v" }, + + // recursive namespace meta-wildcard + {"**//D:k=v", "a//D:k=v"}, + {"**//D:k=v", "a//b//c//D:k=v"}, + {"a//**//D:k=v", "a//b//c//D:k=v"}, + {"a//**//d//D:k=v", "a//b//c//d//D:k=v"}, + {"a//**//d//D:k=v", "a//b//c//d//d//D:k=v"}, + {"a//**//d//D:k=v", "a//a//b//c//d//d//D:k=v"}, + {"a//**//d//**//e//D:k=v", "a//a//b//d//c//d//e//D:k=v"}, + + // special cases for names ending with // + { "*:*", "d//:k=v" }, + { "z*x*:*", "zaxcx//:k=v" }, + { "*:*", "d/xx/q//:k=v" }, + { "z*x??:*", "z/a/x/c/x//:k=v" }, + { "*x???:*", "dbdbdxk//:k=v" }, + { "z*x?c*x*:*", "z/a/x/c/x//:k=v" }, + { "?/*/?:*", "d/xx/q//:k=v" }, + { "**//*:*", "a//b//jmx.rmi:k=v"}, + { "**//*:*", "a//b//jmx.rmi//:k=v"}, + { "*//*:*", "wombat//:type=Wombat" }, + { "**//*:*", "jmx.rmi//:k=v"}, + }; private static final String negativeTests[][] = { @@ -114,6 +186,33 @@ public class ApplyWildcardTest { { "d:k1=\"a?b\",k2=\"c*d\"", "d:k1=\"ab\",k2=\"cd\"" }, { "d:k1=\"a?b\",k2=\"c*d\",*", "d:k1=\"ab\",k2=\"cd\",k3=\"v3\"" }, { "d:*,k1=\"a?b\",k2=\"c*d\"", "d:k1=\"ab\",k2=\"cd\",k3=\"v3\"" }, + + // with namespaces + + { "z*x?x*:*", "zaxcx//blougs:k=v" }, + { "*x?f??rata:*", "d/xxf/qxbf//rata:k=v" }, + { "z*x?c*x*b*:*", "z/a/x/c/x//b//:k=v" }, + + { "*:*", "d/c/v//x/vgh/:k=v" }, + { "z*x??z*x:*", "zaxcx//zaxcxcx:k=v" }, + { "?/*/?:*", "//d/xx/q//:k=v" }, + { "z*/?*/?:*", "z/x/x/z//z/a/x/c/x//:k=v" }, + { "*x?/?blur?g*:*", "dbdbdxk//blurhgblurgh/x/:k=v" }, + { "z*x??x/???:*", "zaxcxccx///.:k=v" }, + { "*x?f?/?:*", "d/xxf/qxbf///:k=v" }, + { "z*x?c*x/?*z????*g:*", "z/a/x/c/x//gloubs/././/zargh//g:k=v" }, + + // recursive namespace meta-wildcard + {"**//D:k=v", "D:k=v"}, + {"b//**//D:k=v", "a//b//c//D:k=v"}, + {"a//**//D:k=v", "a//D:k=v"}, + {"a//**//d//D:k=v", "a//b//c//d//e//D:k=v"}, + {"a//**//d//D:k=v", "a//b//c//D:k=v"}, + {"a//**//d//D:k=v", "a//b//c//d//d//e//D:k=v"}, + {"a//**//d//**//e//D:k=v", "a//a//b//c//d//e//D:k=v"}, + {"a//**//d//**//e//D:k=v", "a//a//b//c//e//D:k=v"}, + { "**//*:*", "jmx.rmi:k=v"}, + }; private static int runPositiveTests() { @@ -129,6 +228,8 @@ public class ApplyWildcardTest { if (result == false) { error++; System.out.println("Test failed!"); + throw new Error("test failed for "+ + "\"" + on1 + "\".apply(\"" + on2 + "\")"); } else { System.out.println("Test passed!"); } @@ -168,10 +269,85 @@ public class ApplyWildcardTest { return error; } + private static int runRepositoryPositiveTests() { + int error = 0; + for (int i = 0; i < positiveTests.length; i++) { + try { + ObjectName on1 = ObjectName.getInstance(positiveTests[i][0]); + ObjectName on2 = ObjectName.getInstance(positiveTests[i][1]); + if (on1.isPropertyPattern()) { + if (!on1.getKeyPropertyListString().equals("")) continue; + } else if (!on1.getCanonicalKeyPropertyListString() + .equals(on2.getCanonicalKeyPropertyListString())) { + continue; + } + System.out.println("Repository Positive Match Test ---------------"); + final String dom1 = on1.getDomain(); + final String dom2 = on2.getDomain(); + System.out.println("Util.wildpathmatch(\"" + dom2 + "\",\"" + dom1 + "\")"); + boolean result = + Util.wildpathmatch(dom2,dom1); + System.out.println("Result = " + result); + if (result == false) { + error++; + System.out.println("Test failed!"); + } else { + System.out.println("Test passed!"); + } + } catch (Exception e) { + error++; + System.out.println("Got Unexpected Exception = " + e.toString()); + System.out.println("Test failed!"); + } + System.out.println("----------------------------------------------"); + } + return error; + } + + private static int runRepositoryNegativeTests() { + int error = 0; + for (int i = 0; i < negativeTests.length; i++) { + try { + ObjectName on1 = ObjectName.getInstance(negativeTests[i][0]); + ObjectName on2 = ObjectName.getInstance(negativeTests[i][1]); + if (on1.isPropertyPattern()) { + if (!on1.getKeyPropertyListString().equals("")) continue; + } else if (!on1.getCanonicalKeyPropertyListString() + .equals(on2.getCanonicalKeyPropertyListString())) { + continue; + } + System.out.println("Repository Negative Match Test ---------------"); + final String dom1 = on1.getDomain(); + final String dom2 = on2.getDomain(); + System.out.println("Util.wildpathmatch(\"" + dom2 + "\",\"" + dom1 + "\")"); + boolean result = + Util.wildpathmatch(dom2,dom1); + System.out.println("Result = " + result); + if (result == true) { + error++; + System.out.println("Test failed!"); + } else { + System.out.println("Test passed!"); + } + } catch (Exception e) { + error++; + System.out.println("Got Unexpected Exception = " + e.toString()); + System.out.println("Test failed!"); + } + System.out.println("----------------------------------------------"); + } + return error; + } + public static void main(String[] args) throws Exception { + int error = 0; + if (!(new ObjectName("z*x*:*").apply(new ObjectName("zaxcx//:k=v")))) + throw new Exception(); + + // Check null values // System.out.println("----------------------------------------------"); @@ -253,6 +429,10 @@ public class ApplyWildcardTest { error += runPositiveTests(); error += runNegativeTests(); + System.out.println("----------------------------------------------"); + error += runRepositoryPositiveTests(); + System.out.println("----------------------------------------------"); + error += runRepositoryNegativeTests(); if (error > 0) { final String msg = "Test FAILED! Got " + error + " error(s)"; diff --git a/test/javax/management/namespace/DomainCreationTest.java b/test/javax/management/namespace/DomainCreationTest.java new file mode 100644 index 000000000..93a98dc62 --- /dev/null +++ b/test/javax/management/namespace/DomainCreationTest.java @@ -0,0 +1,329 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * + * @test DomainCreationTest.java + * @summary Test the creation and registration of JMXDomain instances. + * @author Daniel Fuchs + * @run clean DomainCreationTest Wombat WombatMBean + * @run build DomainCreationTest Wombat WombatMBean + * @run main DomainCreationTest + */ + + +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.DynamicMBean; +import javax.management.InstanceNotFoundException; +import javax.management.InvalidAttributeValueException; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanRegistration; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.NotificationEmitter; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.RuntimeMBeanException; +import javax.management.RuntimeOperationsException; +import javax.management.namespace.JMXDomain; +import javax.management.namespace.MBeanServerSupport; + +/** + * Test simple creation/registration of namespace. + * + */ +public class DomainCreationTest { + private static Map emptyEnvMap() { + return Collections.emptyMap(); + } + + + public static class LocalDomainRepository + extends MBeanServerSupport { + private final MBeanServer server; + private final String domain; + + + public class DynamicMBeanProxy implements DynamicMBean { + + private final MBeanServer server; + private final ObjectName name; + + public DynamicMBeanProxy(MBeanServer s, ObjectName n) { + this.server = s; + this.name = n; + } + + public Object getAttribute(String attribute) + throws AttributeNotFoundException, + MBeanException, ReflectionException { + try { + return server.getAttribute(name, attribute); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + public void setAttribute(Attribute attribute) + throws AttributeNotFoundException, + InvalidAttributeValueException, MBeanException, + ReflectionException { + try { + server.setAttribute(name, attribute); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + public AttributeList getAttributes(String[] attributes) { + try { + return server.getAttributes(name, attributes); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + public AttributeList setAttributes(AttributeList attributes) { + try { + return server.setAttributes(name, attributes); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + public Object invoke(String actionName, Object[] params, + String[] signature) throws MBeanException, + ReflectionException { + try { + return server.invoke(name, actionName, params, signature); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + public MBeanInfo getMBeanInfo() { + try { + return server.getMBeanInfo(name); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException(x); + } + } + } + + public LocalDomainRepository(String domain) { + this.server = MBeanServerFactory.newMBeanServer(); + this.domain = domain; + } + + @Override + protected Set getNames() { + try { + final ObjectName name = + ObjectName.getInstance(domain+":*"); + return server.queryNames(name, null); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + @Override + public DynamicMBean getDynamicMBeanFor(ObjectName name) + throws InstanceNotFoundException { + return new DynamicMBeanProxy(server, name); + } + + @Override + public NotificationEmitter + getNotificationEmitterFor(ObjectName name) + throws InstanceNotFoundException { + DynamicMBean mbean = getDynamicMBeanFor(name); + if (mbean instanceof NotificationEmitter) + return (NotificationEmitter) mbean; + return null; + } + + } + + private static MBeanServer newMBeanServer() { + return MBeanServerFactory.newMBeanServer(); + } + + public static interface ThingMBean {} + public static class Thing implements ThingMBean, MBeanRegistration { + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + if (name == null) return new ObjectName(":type=Thing"); + else return name; + } + public void postRegister(Boolean registrationDone) { + } + + public void preDeregister() throws Exception { + } + public void postDeregister() { + } + } + + /** + * Test that it is possible to create a dummy MBean with a null + * ObjectName - this is just a sanity check - as there are already + * other JMX tests that check that. + * + * @throws java.lang.Exception + */ + public static void testCreateWithNull() throws Exception { + final MBeanServer server = newMBeanServer(); + final ObjectInstance oi = server.registerMBean(new Thing(),null); + server.unregisterMBean(oi.getObjectName()); + System.out.println("testCreateWithNull PASSED"); + } + + /** + * Check that we can register a JMXNamespace MBean, using its standard + * ObjectName. + * @throws java.lang.Exception + */ + public static void testGoodObjectName() throws Exception { + MBeanServer server = newMBeanServer(); + final ObjectName name = + JMXDomain.getDomainObjectName("gloups"); + final ObjectInstance oi = + server.registerMBean(new JMXDomain( + new LocalDomainRepository("gloups")),name); + System.out.println("Succesfully registered namespace: "+name); + try { + if (! name.equals(oi.getObjectName())) + throw new RuntimeException("testGoodObjectName: TEST failed: " + + "namespace registered as: "+ + oi.getObjectName()+" expected: "+name); + } finally { + server.unregisterMBean(oi.getObjectName()); + } + System.out.println("Succesfully unregistered namespace: "+name); + System.out.println("testGoodObjectName PASSED"); + } + + /** + * Check that we cannot register a JMXNamespace MBean, if we don't use + * its standard ObjectName. + * @throws java.lang.Exception + */ + public static void testBadObjectName() throws Exception { + MBeanServer server = newMBeanServer(); + Throwable exp = null; + final ObjectName name = new ObjectName("d:k=v"); + try { + server.registerMBean(new JMXDomain( + new LocalDomainRepository("d")),name); + System.out.println("testBadObjectName: " + + "Error: MBean registered, no exception thrown."); + } catch(RuntimeMBeanException x) { + exp = x.getCause(); + } catch(Exception x) { + throw new RuntimeException("testBadObjectName: TEST failed: " + + "expected RuntimeMBeanException - got "+ + x); + } + if (exp == null) server.unregisterMBean(name); + if (exp == null) + throw new RuntimeException("testBadObjectName: TEST failed: " + + "expected IllegalArgumentException - got none"); + if (!(exp instanceof IllegalArgumentException)) + throw new RuntimeException("testBadObjectName: TEST failed: " + + "expected IllegalArgumentException - got "+ + exp.toString(),exp); + System.out.println("Got expected exception: "+exp); + System.out.println("testBadObjectName PASSED"); + } + + /** + * Check that we cannot register a Domain MBean in a domain that already + * exists. + * + * @throws java.lang.Exception + */ + public static void testBadDomain() throws Exception { + MBeanServer server = newMBeanServer(); + Throwable exp = null; + final ObjectName name = new ObjectName("glips:k=v"); + server.registerMBean(new Wombat(),name); + + final ObjectName dname = + JMXDomain.getDomainObjectName("glips"); + + try { + server.registerMBean(new JMXDomain( + new LocalDomainRepository("glips")),dname); + System.out.println("testBadDomain: " + + "Error: MBean registered, no exception thrown."); + } catch(RuntimeOperationsException x) { + exp = x.getCause(); + } catch(Exception x) { + throw new RuntimeException("testBadDomain: TEST failed: " + + "expected RuntimeOperationsException - got "+ + x); + } finally { + server.unregisterMBean(name); + } + if (exp == null) { + server.unregisterMBean(dname); + } + if (exp == null) + throw new RuntimeException("testBadDomain: TEST failed: " + + "expected IllegalArgumentException - got none"); + if (!(exp instanceof IllegalArgumentException)) + throw new RuntimeException("testBadDomain: TEST failed: " + + "expected IllegalArgumentException - got "+ + exp.toString(),exp); + System.out.println("Got expected exception: "+exp); + System.out.println("testBadDomain PASSED"); + } + + + public static void main(String... args) throws Exception { + testCreateWithNull(); + testGoodObjectName(); + testBadObjectName(); + testBadDomain(); + } +} diff --git a/test/javax/management/namespace/EventWithNamespaceControlTest.java b/test/javax/management/namespace/EventWithNamespaceControlTest.java new file mode 100644 index 000000000..c1e5a9d46 --- /dev/null +++ b/test/javax/management/namespace/EventWithNamespaceControlTest.java @@ -0,0 +1,92 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * + * @test EventWithNamespaceControlTest.java + * @summary Check -Djmx.remote.use.event.service=true and + * -Djmx.remote.delegate.event.service + * @author Daniel Fuchs + * @run clean EventWithNamespaceTest EventWithNamespaceControlTest + * Wombat WombatMBean JMXRemoteTargetNamespace + * NamespaceController NamespaceControllerMBean + * @compile -XDignore.symbol.file=true EventWithNamespaceTest.java + EventWithNamespaceControlTest.java + * Wombat.java WombatMBean.java JMXRemoteTargetNamespace.java + * NamespaceController.java NamespaceControllerMBean.java + * @run main/othervm -Djmx.remote.use.event.service=true EventWithNamespaceControlTest + * @run main/othervm EventWithNamespaceControlTest + * @run main/othervm -Djmx.remote.delegate.event.service=false EventWithNamespaceControlTest java.lang.UnsupportedOperationException + */ + +import java.util.Collections; +import java.util.Map; +import java.util.logging.Logger; +import javax.management.RuntimeOperationsException; + +/** + * + * @author Sun Microsystems, Inc. + */ +public class EventWithNamespaceControlTest extends EventWithNamespaceTest { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(EventWithNamespaceControlTest.class.getName()); + + /** Creates a new instance of EventWithNamespaceTest */ + public EventWithNamespaceControlTest() { + } + + + + public static void main(String[] args) { + final EventWithNamespaceControlTest test = + new EventWithNamespaceControlTest(); + if (args.length == 0) { + test.run(args); + System.out.println("Test successfully passed"); + } else { + try { + test.run(args); + throw new RuntimeException("Test should have failed."); + } catch (RuntimeOperationsException x) { + if (! args[0].equals(x.getCause().getClass().getName())) { + System.err.println("Unexpected wrapped exception: "+ + x.getCause()); + throw x; + } else { + System.out.println("Got expected exception: "+x.getCause()); + } + } + } + } + + public Map getServerMap() { + Map retValue = Collections.emptyMap(); + return retValue; + } + +} diff --git a/test/javax/management/namespace/EventWithNamespaceTest.java b/test/javax/management/namespace/EventWithNamespaceTest.java new file mode 100644 index 000000000..44fca88c7 --- /dev/null +++ b/test/javax/management/namespace/EventWithNamespaceTest.java @@ -0,0 +1,241 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * + * @test EventWithNamespaceTest.java 1.8 + * @bug 6539857 + * @summary General Namespace & Notifications test. + * @author Daniel Fuchs + * @run clean EventWithNamespaceTest Wombat WombatMBean + * JMXRemoteTargetNamespace + * NamespaceController NamespaceControllerMBean + * @compile -XDignore.symbol.file=true EventWithNamespaceTest.java + * Wombat.java WombatMBean.java JMXRemoteTargetNamespace.java + * NamespaceController.java NamespaceControllerMBean.java + * @run main EventWithNamespaceTest + */ + +import java.lang.management.ManagementFactory; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +import javax.management.JMX; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationEmitter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.namespace.JMXNamespaces; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +/** + * + * @author Sun Microsystems, Inc. + */ +public class EventWithNamespaceTest { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(EventWithNamespaceTest.class.getName()); + + /** Creates a new instance of EventWithNamespaceTest */ + public EventWithNamespaceTest() { + } + + private static Map singletonMap(String key, Object value) { + final Map map = new HashMap(); + map.put(key,value); + return map; + } + + public Map getServerMap() { + return singletonMap(JMXConnectorServer.DELEGATE_TO_EVENT_SERVICE,"true"); + } + + public JMXServiceURL export(MBeanServer server) + throws Exception { + final JMXServiceURL in = new JMXServiceURL("rmi",null,0); + final Map env = getServerMap(); + + final JMXConnectorServer cs = + JMXConnectorServerFactory.newJMXConnectorServer(in,env,null); + final ObjectName csname = ObjectName. + getInstance(cs.getClass().getPackage().getName()+ + ":type="+cs.getClass().getSimpleName()); + server.registerMBean(cs,csname); + cs.start(); + return cs.getAddress(); + } + + public static class Counter { + int count; + public synchronized int count() { + count++; + notifyAll(); + return count; + } + public synchronized int peek() { + return count; + } + public synchronized int waitfor(int max, long timeout) + throws InterruptedException { + final long start = System.currentTimeMillis(); + while (count < max && timeout > 0) { + final long rest = timeout - + (System.currentTimeMillis() - start); + if (rest <= 0) break; + wait(rest); + } + return count; + } + } + + public static class CounterListener + implements NotificationListener { + final private Counter counter; + public CounterListener(Counter counter) { + this.counter = counter; + } + public void handleNotification(Notification notification, + Object handback) { + System.out.println("Received notif from " + handback + + ":\n\t" + notification); + if (!notification.getSource().equals(handback)) { + System.err.println("OhOh... Unexpected source: \n\t"+ + notification.getSource()+"\n\twas expecting:\n\t"+ + handback); + } + counter.count(); + } + } + + public void simpleTest(String[] args) { + try { + final MBeanServer server1 = + ManagementFactory.getPlatformMBeanServer(); + final JMXServiceURL url1 = export(server1); + + final MBeanServer server2 = + MBeanServerFactory.createMBeanServer("server2"); + final JMXServiceURL url2 = export(server2); + + final MBeanServer server3 = + MBeanServerFactory.createMBeanServer("server3"); + final JMXServiceURL url3 = export(server3); + + final ObjectInstance ncinst = + NamespaceController.createInstance(server1); + + final NamespaceControllerMBean nc = + JMX.newMBeanProxy(server1,ncinst.getObjectName(), + NamespaceControllerMBean.class); + + final String mount2 = nc.mount(url2,"server2",null); + final String mount3 = nc.mount(url3,"server2//server3", + null); + + final ObjectName deep = + new ObjectName("server2//server3//bush:type=Wombat,name=kanga"); + server1.createMBean(Wombat.class.getName(),deep); + + System.err.println("There's a wombat in the bush!"); + + final Counter counter = new Counter(); + + final NotificationListener listener = + new CounterListener(counter); + + final JMXConnector jc = JMXConnectorFactory.connect(url1); + final MBeanServerConnection conn1 = + jc.getMBeanServerConnection(); + final ObjectName shallow = + new ObjectName("bush:"+ + deep.getKeyPropertyListString()); + final MBeanServerConnection conn2 = + JMXNamespaces.narrowToNamespace(conn1,"server2//server3"); + + final WombatMBean proxy1 = + JMX.newMBeanProxy(conn1,deep,WombatMBean.class,true); + final WombatMBean proxy2 = + JMX.newMBeanProxy(conn2,shallow,WombatMBean.class,true); + + + System.err.println("Adding first Notification Listener"); + conn1.addNotificationListener(deep,listener,null,deep); + System.err.println("Adding second Notification Listener"); + ((NotificationEmitter)proxy2). + addNotificationListener(listener,null,shallow); + final JMXConnector c3 = JMXConnectorFactory.connect(url3, + singletonMap(JMXConnector.USE_EVENT_SERVICE,"false")); + System.err.println("Adding third Notification Listener"); + c3.getMBeanServerConnection(). + addNotificationListener(shallow,listener,null,shallow); + System.err.println("Set attribute to trigger notif"); + proxy1.setCaption("I am a new Wombat!"); + System.err.println("Get attribute"); + System.err.println("New caption: "+proxy2.getCaption()); + System.err.println("Wait for Notifs..."); + final int rcvcount = counter.waitfor(3,3000); + if (rcvcount != 3) + throw new RuntimeException("simpleTest failed: "+ + "received count is " +rcvcount); + System.err.println("simpleTest: got expected "+rcvcount+ + " notifs"); + + System.err.println("removing all listeners"); + conn1.removeNotificationListener(deep,listener,null,deep); + ((NotificationEmitter)proxy2) + .removeNotificationListener(listener,null,shallow); + c3.getMBeanServerConnection(). + removeNotificationListener(shallow,listener,null,shallow); + + System.err.println("simpleTest passed: got "+rcvcount+ + " notifs"); + + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException("simpleTest failed: " + x,x); + } + } + + public void run(String[] args) { + simpleTest(args); + } + + public static void main(String[] args) { + new EventWithNamespaceTest().run(args); + } + +} diff --git a/test/javax/management/namespace/ExportNamespaceTest.java b/test/javax/management/namespace/ExportNamespaceTest.java new file mode 100644 index 000000000..e73452714 --- /dev/null +++ b/test/javax/management/namespace/ExportNamespaceTest.java @@ -0,0 +1,99 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * + * @test ExportNamespaceTest.java + * @summary Test that you can export a single namespace through a + * JMXConnectorServer. + * @author Daniel Fuchs + * @run clean ExportNamespaceTest Wombat WombatMBean + * @run build ExportNamespaceTest Wombat WombatMBean + * @run main ExportNamespaceTest + */ + +import javax.management.JMX; +import javax.management.ObjectName; +import javax.management.namespace.JMXNamespace; +import javax.management.namespace.JMXNamespaces; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + + +/** + * Test simple creation/registration of namespace. + * + */ +public class ExportNamespaceTest { + + public static void testExport() throws Exception { + final JMXNamespace my = + new JMXNamespace(MBeanServerFactory.newMBeanServer()); + final MBeanServer s = MBeanServerFactory.newMBeanServer(); + final ObjectName myname = JMXNamespaces.getNamespaceObjectName("my"); + final ObjectName wname = ObjectName.getInstance("backyard:type=Wombat"); + my.getSourceServer().registerMBean(new Wombat(),wname); + s.registerMBean(my,myname); + + if (!s.queryNames(new ObjectName("my//b*:*"),null).contains( + JMXNamespaces.insertPath("my", wname))) { + throw new RuntimeException("1: Wombat not found: "+wname); + } + + final MBeanServer cd = JMXNamespaces.narrowToNamespace(s, "my"); + if (!cd.queryNames(new ObjectName("b*:*"),null).contains(wname)) { + throw new RuntimeException("2: Wombat not found: "+wname); + } + + final JMXServiceURL url = new JMXServiceURL("rmi",null,0); + final JMXConnectorServer server = + JMXConnectorServerFactory.newJMXConnectorServer(url, null, cd); + server.start(); + + final JMXConnector jc = JMXConnectorFactory. + connect(server.getAddress(),null); + final MBeanServerConnection mbsc = jc.getMBeanServerConnection(); + + if (!mbsc.queryNames(new ObjectName("b*:*"),null).contains(wname)) { + throw new RuntimeException("3: Wombat not found: "+wname); + } + System.out.println("Found a Wombat in my backyard."); + + final String deepThoughts = "I want to leave this backyard!"; + final WombatMBean w = JMX.newMBeanProxy(mbsc, wname, WombatMBean.class); + w.setCaption(deepThoughts); + if (!deepThoughts.equals(w.getCaption())) + throw new RuntimeException("4: Wombat is not thinking right: "+ + w.getCaption()); + + } + + public static void main(String... args) throws Exception { + testExport(); + } +} diff --git a/test/javax/management/namespace/JMXDomainTest.java b/test/javax/management/namespace/JMXDomainTest.java new file mode 100644 index 000000000..4a1a14e96 --- /dev/null +++ b/test/javax/management/namespace/JMXDomainTest.java @@ -0,0 +1,512 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * + * @test JMXDomainTest.java + * @summary Basic test for JMXDomain. + * @author Daniel Fuchs + * @run clean JMXDomainTest Wombat WombatMBean + * @run build JMXDomainTest Wombat WombatMBean + * @run main JMXDomainTest + */ + + +import java.util.Collections; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.Map; +import java.util.Set; +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.DynamicMBean; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.InvalidAttributeValueException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanRegistration; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.MBeanServerDelegate; +import javax.management.MBeanServerFactory; +import javax.management.MBeanServerNotification; +import javax.management.NotCompliantMBeanException; +import javax.management.Notification; +import javax.management.NotificationBroadcaster; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.namespace.JMXDomain; +import javax.management.namespace.MBeanServerSupport; + +/** + * Test simple creation/registration of namespace. + * + */ +public class JMXDomainTest { + private static Map emptyEnvMap() { + return Collections.emptyMap(); + } + + + public static class LocalDomainRepository + extends MBeanServerSupport { + private final MBeanServer server; + private final String domain; + + public class DynamicMBeanProxy implements DynamicMBean { + + private final MBeanServer server; + private final ObjectName name; + + public DynamicMBeanProxy(MBeanServer s, ObjectName n) { + this.server = s; + this.name = n; + } + + public Object getAttribute(String attribute) + throws AttributeNotFoundException, + MBeanException, ReflectionException { + try { + return server.getAttribute(name, attribute); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + public void setAttribute(Attribute attribute) + throws AttributeNotFoundException, + InvalidAttributeValueException, MBeanException, + ReflectionException { + try { + server.setAttribute(name, attribute); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + public AttributeList getAttributes(String[] attributes) { + try { + return server.getAttributes(name, attributes); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + public AttributeList setAttributes(AttributeList attributes) { + try { + return server.setAttributes(name, attributes); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + public Object invoke(String actionName, Object[] params, + String[] signature) throws MBeanException, + ReflectionException { + try { + return server.invoke(name, actionName, params, signature); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + public MBeanInfo getMBeanInfo() { + try { + return server.getMBeanInfo(name); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException(x); + } + } + } + + public LocalDomainRepository(String domain) { + this.server = MBeanServerFactory.newMBeanServer(); + this.domain = domain; + } + + @Override + protected Set getNames() { + try { + final ObjectName name = + ObjectName.getInstance(domain+":*"); + return server.queryNames(name, null); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException(x); + } + } + + @Override + public DynamicMBean getDynamicMBeanFor(ObjectName name) + throws InstanceNotFoundException { + if (server.isRegistered(name)) + return new DynamicMBeanProxy(server, name); + throw new InstanceNotFoundException(name); + } + + + @Override + public NotificationEmitter + getNotificationEmitterFor(final ObjectName name) + throws InstanceNotFoundException { + if (server.isInstanceOf(name, NotificationEmitter.class.getName())) { + return new NotificationEmitter() { + + public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException { + try { + server.removeNotificationListener(name, listener, filter, handback); + } catch (InstanceNotFoundException x) { + throw new IllegalArgumentException(String.valueOf(name), x); + } + } + + public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException { + try { + server.addNotificationListener(name, listener, filter, handback); + } catch (InstanceNotFoundException x) { + throw new IllegalArgumentException(String.valueOf(name), x); + } + } + + public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { + try { + server.removeNotificationListener(name, listener); + } catch (InstanceNotFoundException x) { + throw new IllegalArgumentException(String.valueOf(name), x); + } + } + + public MBeanNotificationInfo[] getNotificationInfo() { + try { + return server.getMBeanInfo(name).getNotifications(); + } catch (Exception x) { + throw new IllegalArgumentException(String.valueOf(name), x); + } + } + }; + } + return null; + } + + @Override + public ObjectInstance registerMBean(Object object, ObjectName name) + throws InstanceAlreadyExistsException, + MBeanRegistrationException, NotCompliantMBeanException { + return server.registerMBean(object, name); + } + + @Override + public void unregisterMBean(ObjectName name) + throws InstanceNotFoundException, + MBeanRegistrationException { + server.unregisterMBean(name); + } + + @Override + public ObjectInstance createMBean(String className, + ObjectName name, ObjectName loaderName, Object[] params, + String[] signature, boolean useCLR) + throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException { + if (useCLR && loaderName == null) { + return server.createMBean(className, name, params, signature); + } + return server.createMBean(className, name, loaderName, + params, signature); + } + + + } + + private static MBeanServer newMBeanServer() { + return MBeanServerFactory.newMBeanServer(); + } + + public static interface ThingMBean {} + public static class Thing implements ThingMBean, MBeanRegistration { + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + if (name == null) return new ObjectName(":type=Thing"); + else return name; + } + public void postRegister(Boolean registrationDone) { + } + + public void preDeregister() throws Exception { + } + public void postDeregister() { + } + } + + /** + * Test that it is possible to create a dummy MBean with a null + * ObjectName - this is just a sanity check - as there are already + * other JMX tests that check that. + * + * @throws java.lang.Exception + */ + public static void testCreateWithNull() throws Exception { + final MBeanServer server = newMBeanServer(); + final ObjectInstance oi = server.registerMBean(new Thing(),null); + server.unregisterMBean(oi.getObjectName()); + System.out.println("testCreateWithNull PASSED"); + } + + public static void testRegisterSimple() throws Exception { + final ObjectName name = + JMXDomain.getDomainObjectName("gloups"); + final JMXDomain jmxDomain = new JMXDomain( + MBeanServerFactory.newMBeanServer()); + testRegister("testRegisterSimple: ",name,jmxDomain); + } + + public static void testRegisterPseudoVirtual() + throws Exception { + final ObjectName name = + JMXDomain.getDomainObjectName("gloups"); + final JMXDomain jmxDomain = new JMXDomain( + new LocalDomainRepository("gloups")); + testRegister("testRegisterPseudoVirtual: ",name,jmxDomain); + } + + public static void testRegister(final String test, + final ObjectName name, + final JMXDomain jmxDomain) throws Exception { + System.out.println(test+" START"); + MBeanServer server = newMBeanServer(); + final ObjectInstance oi = + server.registerMBean(jmxDomain,name); + System.out.println(test+"Succesfully registered namespace: "+name); + if (!server.isRegistered(name)) + fail(test+name+" is not registered!"); + if (!server.queryNames(new ObjectName(name.getDomain()+":*"), null). + contains(name)) + fail(test+name+" not in queryNames"); + + final Thing thing = new Thing(); + final ObjectName thingName = new ObjectName("gloups:type=Thing"); + server.registerMBean(thing,thingName); + if (!server.isRegistered(thingName)) + fail(test+thingName+" is not registered!"); + if (!jmxDomain.getSourceServer().isRegistered(thingName)) + fail(test+thingName+" is not registered in domain!"); + if (!server.queryNames(new ObjectName(name.getDomain()+":*"), null). + contains(thingName)) + fail(test+thingName+" not in queryNames"); + + server.unregisterMBean(name); + if (server.isRegistered(thingName)) + fail(test+thingName+" is still registered!"); + if (server.queryNames(new ObjectName(name.getDomain()+":*"), null). + contains(thingName)) + fail(test+thingName+" still in queryNames"); + + server.registerMBean(jmxDomain, name); + if (!server.isRegistered(thingName)) + fail(test+thingName+" is not registered again!"); + + System.out.println(test+" PASSED"); + } + + private static MBeanServerNotification pop( + BlockingQueue queue, + String type, + ObjectName mbean, + String test) + throws InterruptedException { + final Notification n = queue.poll(1, TimeUnit.SECONDS); + if (!(n instanceof MBeanServerNotification)) + fail(test+"expected MBeanServerNotification, got "+n); + final MBeanServerNotification msn = (MBeanServerNotification)n; + if (!type.equals(msn.getType())) + fail(test+"expected "+type+", got "+msn.getType()); + if (!mbean.apply(msn.getMBeanName())) + fail(test+"expected "+mbean+", got "+msn.getMBeanName()); + System.out.println(test+" got: "+msn); + return msn; + } + private static MBeanServerNotification popADD( + BlockingQueue queue, + ObjectName mbean, + String test) + throws InterruptedException { + return pop(queue, MBeanServerNotification.REGISTRATION_NOTIFICATION, + mbean, test); + } + + private static MBeanServerNotification popREM( + BlockingQueue queue, + ObjectName mbean, + String test) + throws InterruptedException { + return pop(queue, MBeanServerNotification.UNREGISTRATION_NOTIFICATION, + mbean, test); + } + + + public static void testRegisterNotifSimple() throws Exception { + final ObjectName name = + JMXDomain.getDomainObjectName("gloups"); + final JMXDomain jmxDomain = new JMXDomain( + MBeanServerFactory.newMBeanServer()); + testRegisterNotif("testRegisterNotifSimple: ",name,jmxDomain); + } + + public static void testRegisterNotifPseudoVirtual() + throws Exception { + final ObjectName name = + JMXDomain.getDomainObjectName("gloups"); + final JMXDomain jmxDomain = new JMXDomain( + new LocalDomainRepository("gloups")); + testRegisterNotif("testRegisterNotifPseudoVirtual: ",name,jmxDomain); + } + + public static void testRegisterNotif(final String test, + final ObjectName name, + final JMXDomain jmxDomain) throws Exception { + System.out.println(test+" START"); + MBeanServer server = newMBeanServer(); + final ObjectInstance oi = + server.registerMBean(jmxDomain,name); + System.out.println(test+"Succesfully registered namespace: "+name); + if (!server.isRegistered(name)) + fail(test+name+" is not registered!"); + + final BlockingQueue queue = + new ArrayBlockingQueue(10); + + final NotificationListener l = new NotificationListener() { + + public void handleNotification(Notification notification, + Object handback) { + try { + if (!queue.offer(notification,5,TimeUnit.SECONDS)) + throw new RuntimeException("timeout exceeded"); + } catch (Exception x) { + fail(test+"failed to handle notif", x); + } + } + }; + + server.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, l, + null, null); + + final Thing thing = new Thing(); + final ObjectName thingName = new ObjectName("gloups:type=Thing"); + + server.registerMBean(thing,thingName); + if (!jmxDomain.getSourceServer().isRegistered(thingName)) + fail(test+thingName+" is not registered in domain!"); + popADD(queue, thingName, test); + server.unregisterMBean(thingName); + if (jmxDomain.getSourceServer().isRegistered(thingName)) + fail(test+thingName+" is still registered in domain!"); + popREM(queue, thingName, test); + if (queue.size() != 0) + fail(test+queue.size()+" notifs remain in queue "+queue); + + server.unregisterMBean(name); + popREM(queue, name, test); + + jmxDomain.getSourceServer().registerMBean(thing,thingName); + if (server.isRegistered(thingName)) + fail(test+thingName+" is still registered in domain!"); + jmxDomain.getSourceServer().unregisterMBean(thingName); + if (queue.size() != 0) + fail(test+queue.size()+" notifs remain in queue "+queue); + + server.registerMBean(jmxDomain, name); + if (!server.isRegistered(name)) + fail(test+name+" is not registered again!"); + popADD(queue, name, test); + if (queue.size() != 0) + fail(test+queue.size()+" notifs remain in queue "+queue); + + server.registerMBean(thing,thingName); + if (!jmxDomain.getSourceServer().isRegistered(thingName)) + fail(test+thingName+" is not registered in domain!"); + popADD(queue, thingName, test); + server.unregisterMBean(thingName); + if (jmxDomain.getSourceServer().isRegistered(thingName)) + fail(test+thingName+" is still registered in domain!"); + popREM(queue, thingName, test); + if (queue.size() != 0) + fail(test+queue.size()+" notifs remain in queue "+queue); + + System.out.println(test+" PASSED"); + } + + + + private static void fail(String msg) { + raise(new RuntimeException(msg)); + } + + private static void fail(String msg, Throwable cause) { + raise(new RuntimeException(msg,cause)); + } + + private static void raise(RuntimeException x) { + lastException = x; + exceptionCount++; + throw x; + } + + private static volatile Exception lastException = null; + private static volatile int exceptionCount = 0; + + public static void main(String... args) throws Exception { + testCreateWithNull(); + + testRegisterSimple(); + testRegisterNotifSimple(); + + testRegisterPseudoVirtual(); + testRegisterNotifPseudoVirtual(); + + if (lastException != null) + throw lastException; + } +} diff --git a/test/javax/management/namespace/JMXNamespaceSecurityTest.java b/test/javax/management/namespace/JMXNamespaceSecurityTest.java new file mode 100644 index 000000000..b948012f1 --- /dev/null +++ b/test/javax/management/namespace/JMXNamespaceSecurityTest.java @@ -0,0 +1,272 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * + * @test JMXNamespaceSecurityTest.java + * @summary General JMXNamespaceSecurityTest test. + * @author Daniel Fuchs + * @run clean JMXNamespaceViewTest JMXNamespaceSecurityTest Wombat WombatMBean + * LazyDomainTest + * @run build JMXNamespaceSecurityTest JMXNamespaceViewTest Wombat WombatMBean + * LazyDomainTest + * @run main/othervm JMXNamespaceSecurityTest namespace.policy + */ +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; +import java.lang.management.ManagementFactory; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectName; +import javax.management.namespace.JMXDomain; +import javax.management.namespace.JMXNamespace; +import javax.management.namespace.JMXNamespaces; +import javax.management.remote.JMXConnectorServer; + +/** + * + * @author Sun Microsystems, Inc. + */ +public class JMXNamespaceSecurityTest extends JMXNamespaceViewTest { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(JMXNamespaceSecurityTest.class.getName()); + + public static class NamedMBeanServerCreator + extends JMXNamespaceViewTest.MBeanServerConfigCreator { + public MBeanServer createMBeanServerFor(NamespaceConfig config) { + return MBeanServerFactory. + createNamedMBeanServer(config.name,config.name); + } + } + /** + * Creates a config for a hierarchy of namespaces, mixing local namespaces + * and remote namespaces using the given protocol. + * @param protocol The protocol that should be used for remote namespaces. + * @return A namespace config hierarchy. + * @throws java.lang.Exception + */ + public static NamespaceConfig[] makeConfig(String protocol) + throws Exception { + final NamespaceConfig[] config = { + // Top level namespace "top1" (local) + config("top1",wombats("wchief","w1","w2","w3"), + // top1//local1 + config("local1",wombats("wchief","ww1","ww2")), + // top1//local2 + config("local2",wombats("wchief","ww4","ww5","ww6"), + // top1//local2//local3 + config("local3",wombats("wchief","www1","www2")), + // top1//local2//rmi1 + config("rmi1",url(protocol),wombats("wchief","www3","www4","www5"))), + // top1//rmi2 + config("rmi2",url(protocol),wombats("wchief","ww7","ww8","ww9"), + // top1//rmi2//local4 + config("local4",wombats("wchief","www6","www7")), + // top1//rmi2//rmi3 + config("rmi3",url(protocol),wombats("wchief","www3","www4","www5"), + // top1//rmi2//rmi3//local5 + config("local5",wombats("wchief","wwww1"))))), + // Top level namespace "top2" (local) + config("top2",wombats("wchief","w21","w22","w23"), + // top2//local21 + config("local21",wombats("wchief","ww21","ww22")), + // top2//rmi22 + config("rmi22",url(protocol),wombats("wchief","ww27","ww28","ww29"), + // top2//rmi22//local24 + config("local24",wombats("wchief","www26","www27")), + // top2//rmi22//rmi23 + config("rmi23",url(protocol),wombats("wchief","www23","www24","www25"), + // top2//rmi22//rmi23//local25 + config("local25",wombats("wchief","wwww21"))))), + // Top level namespace "top3" (remote) + config("top3",url(protocol),wombats("wchief","w31","w32","w33"), + // top3//local31 + config("local31",wombats("wchief","ww31","ww32")), + // top3//rmi32 + config("rmi32",url(protocol),wombats("wchief","ww37","ww38","ww39"), + // top3//rmi32//local34 + config("local34",wombats("wchief","www36","www37")), + // top3//rmi32//rmi33 + config("rmi33",url(protocol),wombats("wchief","www33","www34","www35"), + // top3//rmi32//local35 + config("local35",wombats("wchief","wwww31"))))), + }; + return config; + } + + public static void test(MBeanServer server, NamespaceConfig[] namespaces) + throws Exception { + System.out.println("Launching test..."); + List cslist = load(server, + new NamedMBeanServerCreator(), namespaces); + Map inputMap = + new HashMap(); + + for (NamespaceConfig cfg : namespaces) { + fillMap(inputMap,"",cfg); + } + final MBeanServer platform = ManagementFactory.getPlatformMBeanServer(); + //if (System.getProperty("jmx.wait")!=null) { + /* + // if we wanted to lazy load the platform MBeanServer: + final LazyDomainTest.MBeanServerLoader loader = + new LazyDomainTest.MBeanServerLoader() { + public MBeanServer loadMBeanServer() { + return ManagementFactory.getPlatformMBeanServer(); + } + }; + final LazyDomainTest.MBeanServerProxy proxy = + new LazyDomainTest.MBeanServerProxy(loader); + final LazyDomainTest.LazyDomain domain = + new LazyDomainTest.LazyDomain(proxy); + server.registerMBean(domain, + JMXDomain.getDomainObjectName("java.lang")); + */ + // Mount java.lang MBeans into our private server so that + // visualvm can connect. + server.registerMBean( + new JMXDomain(platform), + JMXDomain.getDomainObjectName("java.lang")); + //} + if (System.getProperty("jmx.wait")!=null) { + platform.registerMBean(new JMXNamespace(server), + JMXNamespaces.getNamespaceObjectName("test")); + } + + System.setSecurityManager(new SecurityManager()); + + // Some sanity checks... The policy file should allow access + // to java.lang MBeans. + final ObjectName platnames = new ObjectName("java.lang:*"); + for (ObjectName o : platform.queryNames(platnames,null)) { + server.getMBeanInfo(o); + } + final Set lang = + new HashSet(server.queryNames(platnames, null)); + lang.remove(JMXDomain.getDomainObjectName("java.lang")); + if (!lang.equals(platform. + queryNames(platnames, null))) + throw new Exception("Wrong list of platform names: "+lang); + System.out.println("Got all java.lang MBeans: "+lang); + + // The policy file should allow to see all namespaces. + // check this... + final List patterns = new ArrayList(); + final Set paths = new TreeSet(); + final Set uuids = new HashSet(); + patterns.add(new ObjectName("*//:*")); + while (patterns.size()>0) { + System.out.println("server.queryNames("+patterns.get(0)+",null)"); + Set names = server.queryNames(patterns.remove(0),null); + System.out.println("found: "+names); + + for (ObjectName no : names) { + final String uuid = (String) server.getAttribute(no, "UUID"); + if (uuids.contains(uuid)) { + System.out.print("namespace "+no+", uuid="+uuid+ + " already parsed. Skipping"); + continue; + } + uuids.add(uuid); + patterns.add(new ObjectName(no.getDomain()+"*//:*")); + System.out.println("added pattern: "+ + new ObjectName(no.getDomain()+"*//:*")); + if (no.getDomain().endsWith(ClientContext.NAMESPACE+ + JMXNamespaces.NAMESPACE_SEPARATOR)) continue; + paths.add(no.getDomain().substring(0, + no.getDomain().length()- + JMXNamespaces.NAMESPACE_SEPARATOR.length())); + } + } + final TreeSet expected = new TreeSet(inputMap.keySet()); + if (!expected.equals(paths)) { + throw new Exception("wrong set of namespaces, expected "+ + expected+", got "+paths); + } + + System.out.println("Got all namespaces: "+paths); + + // Check that we can see all wombats. + // + ObjectName wchief = + new ObjectName("top1//rmi2//wombat:name=wchief,type=Wombat"); + String caption = (String) server.getAttribute(wchief,"Caption"); + System.out.println("wchief says "+caption); + Object mood = server.getAttribute(wchief,"Mood"); + System.out.println("wchief's mood on a scale of 100 is "+mood); + + ObjectName wchief2 = + new ObjectName("top1//wombat:name=wchief,type=Wombat"); + String caption2 = (String) server.getAttribute(wchief2,"Caption"); + System.out.println("wchief2 says "+caption2); + try { + Object mood2 = server.getAttribute(wchief2,"Mood"); + System.out.println("wchief2's mood on a scale of 100 is "+mood2); + throw new Exception("Expected security exception for "+ + "getAttribute("+wchief2+", \"Mood\""); + } catch (SecurityException x) { + System.out.println("wchief2's mood is unavailable: "+x); + } + try { + exportAndWaitIfNeeded(server); + } finally { + closeAll(cslist); + } + + } + /** Creates a new instance of JMXNamespaceTest */ + public JMXNamespaceSecurityTest() { + } + + public static void main(String[] args) throws Exception { + String osName = System.getProperty("os.name"); + System.out.println("os.name = " + osName); + if (!osName.equals("SunOS")) { + System.out.println("This test runs on Solaris only."); + System.out.println("Bye! Bye!"); + return; + } + final String policy = System.getProperty("test.src") + + File.separator + args[0]; + System.out.println("PolicyFile = " + policy); + System.setProperty("java.security.policy", policy); + if (!new File(System.getProperty("java.security.policy")).canRead()) + throw new IOException("no such file: "+ + System.getProperty("java.security.policy")); + test(MBeanServerFactory.createNamedMBeanServer("root","root"), + makeConfig("rmi")); + } + +} diff --git a/test/javax/management/namespace/JMXNamespaceTest.java b/test/javax/management/namespace/JMXNamespaceTest.java new file mode 100644 index 000000000..da553f9c8 --- /dev/null +++ b/test/javax/management/namespace/JMXNamespaceTest.java @@ -0,0 +1,714 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * + * @test JMXNamespaceTest.java + * @summary General JMXNamespace test. + * @author Daniel Fuchs + * @run clean JMXNamespaceTest + * Wombat WombatMBean JMXRemoteTargetNamespace + * NamespaceController NamespaceControllerMBean + * @compile -XDignore.symbol.file=true JMXNamespaceTest.java + * Wombat.java WombatMBean.java JMXRemoteTargetNamespace.java + * NamespaceController.java NamespaceControllerMBean.java + * @run main/othervm JMXNamespaceTest + */ +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; +import javax.management.DynamicMBean; +import javax.management.InstanceNotFoundException; +import javax.management.InvalidAttributeValueException; +import javax.management.JMX; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.NotificationEmitter; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.RuntimeOperationsException; +import javax.management.StandardMBean; +import javax.management.namespace.JMXNamespaces; +import javax.management.namespace.JMXNamespace; +import javax.management.namespace.JMXNamespaceMBean; +import javax.management.namespace.JMXRemoteNamespaceMBean; +import javax.management.namespace.MBeanServerConnectionWrapper; +import javax.management.namespace.MBeanServerSupport; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +/** + * + * @author Sun Microsystems, Inc. + */ +public class JMXNamespaceTest { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(JMXNamespaceTest.class.getName()); + + /** Creates a new instance of JMXNamespaceTest */ + public JMXNamespaceTest() { + } + + public static class WombatRepository extends MBeanServerSupport { + final Wombat wombat; + final StandardMBean mbean; + final ObjectName wombatName; + + public WombatRepository(ObjectName wombatName) { + try { + wombat = new Wombat(); + mbean = wombat; + this.wombatName = wombatName; + wombat.preRegister(null,wombatName); + } catch (Exception x) { + throw new IllegalArgumentException(x); + } + } + + @Override + public DynamicMBean getDynamicMBeanFor(ObjectName name) + throws InstanceNotFoundException { + if (wombatName.equals(name)) return mbean; + else throw new InstanceNotFoundException(String.valueOf(name)); + } + + @Override + protected Set getNames() { + final Set res = Collections.singleton(wombatName); + return res; + } + + @Override + public NotificationEmitter + getNotificationEmitterFor(ObjectName name) + throws InstanceNotFoundException { + final DynamicMBean mb = getDynamicMBeanFor(name); + if (mb instanceof NotificationEmitter) + return (NotificationEmitter)mb; + return null; + } + } + + public static class SimpleTest { + public final String descr; + private final Class testClass; + private final Method method; + public SimpleTest(String descr) { + this.descr = descr; + this.testClass = JMXNamespaceTest.class; + try { + method = testClass. + getDeclaredMethod(descr,SimpleTestConf.class, + Object[].class); + } catch (NoSuchMethodException x) { + throw new IllegalArgumentException(descr+": test not found", + x); + } + } + + public void run(SimpleTestConf conf, Object... args) + throws Exception { + try { + method.invoke(null,conf,args); + } catch (InvocationTargetException x) { + final Throwable cause = x.getCause(); + if (cause instanceof Exception) throw (Exception)cause; + if (cause instanceof Error) throw (Error)cause; + throw x; + } + } + } + + private static class SimpleTestConf { + public final Wombat wombat; + public final StandardMBean mbean; + public final String dirname; + public final ObjectName handlerName; + public final ObjectName wombatNickName; + public final ObjectName wombatName; + public final JMXNamespace wombatNamespace; + public final MBeanServer server; + public final WombatMBean proxy; + public SimpleTestConf(String[] args) throws Exception { + wombat = new Wombat(); + mbean = wombat; + dirname = "wombat"; + handlerName = + new ObjectName(dirname+"//:type=JMXNamespace"); + + wombatNickName = + new ObjectName("burrow:type=Wombat"); + + wombatName = + new ObjectName(dirname+"//"+wombatNickName); + + wombatNamespace = + new JMXNamespace( + new WombatRepository(wombatNickName)); + + server = ManagementFactory.getPlatformMBeanServer(); + System.out.println(handlerName+" registered="+ + server.isRegistered(handlerName)); + server.registerMBean(wombatNamespace,handlerName); + + try { + proxy = JMX.newMBeanProxy(server,wombatName, + WombatMBean.class); + } catch (Exception x) { + server.unregisterMBean(handlerName); + throw x; + } + } + + public void close() { + try { + server.unregisterMBean(handlerName); + } catch (Exception x) { + System.out.println("Failed to close: " + x); + x.printStackTrace(); + } + } + + public void test(SimpleTest test,Object... args) + throws Exception { + try { + test.run(this,args); + passed++; + } catch (Exception x) { + failed++; + System.err.println(test.descr+" failed: " + x); + x.printStackTrace(); + } + } + + public volatile int failed = 0; + public volatile int passed = 0; + } + + static void checkValue(String name,Object expected, Object returned) + throws InvalidAttributeValueException { + if (Collections.singletonList(expected). + equals(Collections.singletonList(returned))) return; + + throw new InvalidAttributeValueException("Bad value for "+ + name+": ["+returned+"] - was expecting ["+expected+"]"); + } + + // --------------------------------------------------------------- + // SIMPLE TESTS BEGIN HERE + // --------------------------------------------------------------- + + static void getCaptionTest(SimpleTestConf env, Object... args) + throws Exception { + System.out.println(env.proxy.getCaption()); + } + + static void setCaptionTest(SimpleTestConf env, Object... args) + throws Exception { + env.proxy.setCaption((String)args[0]); + final String result = env.proxy.getCaption(); + System.out.println(result); + checkValue("Caption",args[0],result); + } + + static void queryNamesTest1(SimpleTestConf env, Object... args) + throws Exception { + final ObjectName pat = + new ObjectName(env.handlerName.getDomain()+"*:*"); + final Set res = + env.server.queryNames(pat,null); + System.out.println("queryNamesTest1: "+res); + checkValue("names",Collections.singleton(env.wombatName),res); + } + + static void queryNamesTest2(SimpleTestConf env, Object... args) + throws Exception { + final ObjectName pat = + new ObjectName("*:"+ + env.wombatName.getKeyPropertyListString()); + final Set res = + env.server.queryNames(pat,null); + System.out.println("queryNamesTest2: "+res); + checkValue("names",Collections.emptySet(),res); + } + + static void getDomainsTest(SimpleTestConf env, Object... args) + throws Exception { + final List domains = + Arrays.asList(env.server.getDomains()); + System.out.println("getDomainsTest: "+domains); + if (domains.contains(env.wombatName.getDomain())) + throw new InvalidAttributeValueException("domain: "+ + env.wombatName.getDomain()); + if (!domains.contains(env.handlerName.getDomain())) + throw new InvalidAttributeValueException("domain not found: "+ + env.handlerName.getDomain()); + } + + // --------------------------------------------------------------- + // SIMPLE TESTS END HERE + // --------------------------------------------------------------- + + private static void simpleTest(String[] args) { + final SimpleTestConf conf; + try { + conf = new SimpleTestConf(args); + try { + conf.test(new SimpleTest("getCaptionTest")); + conf.test(new SimpleTest("setCaptionTest"), + "I am a new Wombat!"); + conf.test(new SimpleTest("queryNamesTest1")); + conf.test(new SimpleTest("queryNamesTest2")); + conf.test(new SimpleTest("getDomainsTest")); + } finally { + conf.close(); + } + } catch (Exception x) { + System.err.println("simpleTest FAILED: " +x); + x.printStackTrace(); + throw new RuntimeException(x); + } + System.out.println("simpleTest: "+conf.passed+ + " PASSED, " + conf.failed + " FAILED."); + if (conf.failed>0) { + System.err.println("simpleTest FAILED ["+conf.failed+"]"); + throw new RuntimeException("simpleTest FAILED ["+conf.failed+"]"); + } else { + System.err.println("simpleTest PASSED ["+conf.passed+"]"); + } + } + + public static void recursiveTest(String[] args) { + final SimpleTestConf conf; + try { + conf = new SimpleTestConf(args); + try { + final JMXServiceURL url = + new JMXServiceURL("rmi","localHost",0); + final Map empty = Collections.emptyMap(); + final JMXConnectorServer server = + JMXConnectorServerFactory.newJMXConnectorServer(url, + empty,conf.server); + server.start(); + final JMXServiceURL address = server.getAddress(); + final JMXConnector client = + JMXConnectorFactory.connect(address, + empty); + final String[] signature = { + JMXServiceURL.class.getName(), + Map.class.getName(), + }; + final String[] signature2 = { + JMXServiceURL.class.getName(), + Map.class.getName(), + String.class.getName(), + }; + final Object[] params = { + address, + null, + }; + final MBeanServerConnection c = + client.getMBeanServerConnection(); + final ObjectName dirName1 = + new ObjectName("kanga//:type=JMXNamespace"); + c.createMBean(JMXRemoteTargetNamespace.class.getName(), + dirName1, params,signature); + c.invoke(dirName1, "connect", null, null); + try { + final MemoryMXBean memory = + JMX.newMXBeanProxy(c, + new ObjectName("kanga//"+ + ManagementFactory.MEMORY_MXBEAN_NAME), + MemoryMXBean.class); + System.out.println("HeapMemory #1: "+ + memory.getHeapMemoryUsage().toString()); + final MemoryMXBean memory2 = + JMX.newMXBeanProxy(c, + new ObjectName("kanga//kanga//"+ + ManagementFactory.MEMORY_MXBEAN_NAME), + MemoryMXBean.class); + System.out.println("HeapMemory #2: "+ + memory2.getHeapMemoryUsage().toString()); + final Object[] params2 = { + address, + null, + "kanga//kanga" + // "kanga//kanga//roo//kanga", <= cycle + }; + final ObjectName dirName2 = + new ObjectName("kanga//roo//:type=JMXNamespace"); + c.createMBean(JMXRemoteTargetNamespace.class.getName(), + dirName2, params2, signature2); + System.out.println(dirName2 + " created!"); + JMX.newMBeanProxy(c,dirName2, + JMXRemoteNamespaceMBean.class).connect(); + try { + final ObjectName wombatName1 = + new ObjectName("kanga//roo//"+conf.wombatName); + final ObjectName wombatName2 = + new ObjectName("kanga//roo//"+wombatName1); + final WombatMBean wombat1 = + JMX.newMBeanProxy(c,wombatName1,WombatMBean.class); + final WombatMBean wombat2 = + JMX.newMBeanProxy(c,wombatName2,WombatMBean.class); + final String newCaption="I am still the same old wombat"; + wombat1.setCaption(newCaption); + final String caps = conf.proxy.getCaption(); + System.out.println("Caption: "+caps); + checkValue("Caption",newCaption,caps); + final String caps1 = wombat1.getCaption(); + System.out.println("Caption #1: "+caps1); + checkValue("Caption #1",newCaption,caps1); + final String caps2 = wombat2.getCaption(); + System.out.println("Caption #2: "+caps2); + checkValue("Caption #2",newCaption,caps2); + final ObjectInstance instance = + NamespaceController.createInstance(conf.server); + final NamespaceControllerMBean controller = + JMX.newMBeanProxy(conf.server,instance.getObjectName(), + NamespaceControllerMBean.class); + final String[] dirs = controller.findNamespaces(); + System.out.println("directories: " + + Arrays.asList(dirs)); + final int depth = 4; + final String[] dirs2 = controller.findNamespaces(null,null,depth); + System.out.println("directories[depth="+depth+"]: " + + Arrays.asList(dirs2)); + for (String dir : dirs2) { + if (dir.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) + dir = dir.substring(0,dir.length()- + JMXNamespaces.NAMESPACE_SEPARATOR.length()); + if (dir.split(JMXNamespaces.NAMESPACE_SEPARATOR).length + > (depth+1)) { + throw new RuntimeException(dir+": depth exceeds "+depth); + } + final ObjectName handlerName = + JMXNamespaces.getNamespaceObjectName(dir); + final JMXNamespaceMBean handler = + JMX.newMBeanProxy(conf.server,handlerName, + JMXNamespaceMBean.class); + try { + System.err.println("Directory "+dir+" domains: "+ + Arrays.asList(handler.getDomains())); + System.err.println("Directory "+dir+" default domain: "+ + handler.getDefaultDomain()); + System.err.println("Directory "+dir+" MBean count: "+ + handler.getMBeanCount()); + } catch(Exception x) { + System.err.println("get info failed for " + + dir +", "+handlerName+": "+x); + x.getCause().printStackTrace(); + throw x; + } + } + + } finally { + c.unregisterMBean(dirName2); + } + } finally { + c.unregisterMBean(dirName1); + client.close(); + server.stop(); + } + } finally { + conf.close(); + } + System.err.println("recursiveTest PASSED"); + } catch (Exception x) { + System.err.println("recursiveTest FAILED: " +x); + x.printStackTrace(); + throw new RuntimeException(x); + } + } + + /** + * Test cycle detection. + * mkdir test ; cd test ; ln -s . kanga ; ln -s kanga/kanga/roo/kanga roo + * touch kanga/roo/wombat + **/ + public static void probeKangaRooTest(String[] args) { + final SimpleTestConf conf; + try { + conf = new SimpleTestConf(args); + try { + final JMXServiceURL url = + new JMXServiceURL("rmi","localHost",0); + final Map empty = Collections.emptyMap(); + final JMXConnectorServer server = + JMXConnectorServerFactory.newJMXConnectorServer(url, + empty,conf.server); + server.start(); + final JMXServiceURL address = server.getAddress(); + final JMXConnector client = + JMXConnectorFactory.connect(address, + empty); + final String[] signature = { + JMXServiceURL.class.getName(), + Map.class.getName(), + }; + + final Object[] params = { + address, + null, + }; + final MBeanServerConnection c = + client.getMBeanServerConnection(); + + // ln -s . kanga + final ObjectName dirName1 = + new ObjectName("kanga//:type=JMXNamespace"); + c.createMBean(JMXRemoteTargetNamespace.class.getName(), + dirName1, params,signature); + c.invoke(dirName1, "connect", null, null); + try { + // ln -s kanga//kanga//roo//kanga roo + final JMXNamespace local = new JMXNamespace( + new MBeanServerConnectionWrapper(null, + JMXNamespaceTest.class.getClassLoader()){ + + @Override + protected MBeanServerConnection getMBeanServerConnection() { + return JMXNamespaces.narrowToNamespace(c, + "kanga//kanga//roo//kanga" + ); + } + + }); + final ObjectName dirName2 = + new ObjectName("roo//:type=JMXNamespace"); + conf.server.registerMBean(local,dirName2); + System.out.println(dirName2 + " created!"); + try { + // touch kanga/roo/wombat + final ObjectName wombatName1 = + new ObjectName("kanga//roo//"+conf.wombatName); + final WombatMBean wombat1 = + JMX.newMBeanProxy(c,wombatName1,WombatMBean.class); + final String newCaption="I am still the same old wombat"; + Exception x = null; + try { + wombat1.setCaption(newCaption); + } catch (RuntimeOperationsException r) { + x=r.getTargetException(); + System.out.println("Got expected exception: " + x); + // r.printStackTrace(); + } + if (x == null) + throw new RuntimeException("cycle not detected!"); + } finally { + c.unregisterMBean(dirName2); + } + } finally { + c.unregisterMBean(dirName1); + client.close(); + server.stop(); + } + } finally { + conf.close(); + } + System.err.println("probeKangaRooTest PASSED"); + } catch (Exception x) { + System.err.println("probeKangaRooTest FAILED: " +x); + x.printStackTrace(); + throw new RuntimeException(x); + } + } + /** + * Test cycle detection 2. + * mkdir test ; cd test ; ln -s . roo ; ln -s roo/roo kanga + * touch kanga/roo/wombat ; rm roo ; ln -s kanga roo ; + * touch kanga/roo/wombat + * + **/ + public static void probeKangaRooCycleTest(String[] args) { + final SimpleTestConf conf; + try { + conf = new SimpleTestConf(args); + Exception failed = null; + try { + final JMXServiceURL url = + new JMXServiceURL("rmi","localHost",0); + final Map empty = Collections.emptyMap(); + final JMXConnectorServer server = + JMXConnectorServerFactory.newJMXConnectorServer(url, + empty,conf.server); + server.start(); + final JMXServiceURL address = server.getAddress(); + final JMXConnector client = + JMXConnectorFactory.connect(address, + empty); + final String[] signature = { + JMXServiceURL.class.getName(), + Map.class.getName(), + }; + final String[] signature2 = { + JMXServiceURL.class.getName(), + Map.class.getName(), + String.class.getName() + }; + final Object[] params = { + address, + Collections.emptyMap(), + }; + final Object[] params2 = { + address, + null, + "kanga", + }; + final MBeanServerConnection c = + client.getMBeanServerConnection(); + + // ln -s . roo + final ObjectName dirName1 = + new ObjectName("roo//:type=JMXNamespace"); + c.createMBean(JMXRemoteTargetNamespace.class.getName(), + dirName1, params,signature); + c.invoke(dirName1, "connect",null,null); + try { + final Map emptyMap = + Collections.emptyMap(); + final JMXNamespace local = new JMXNamespace( + new MBeanServerConnectionWrapper( + JMXNamespaces.narrowToNamespace(c, + "roo//roo//"), + JMXNamespaceTest.class.getClassLoader())) { + }; + // ln -s roo/roo kanga + final ObjectName dirName2 = + new ObjectName("kanga//:type=JMXNamespace"); + conf.server.registerMBean(local,dirName2); + System.out.println(dirName2 + " created!"); + try { + // touch kanga/roo/wombat + final ObjectName wombatName1 = + new ObjectName("kanga//roo//"+conf.wombatName); + final WombatMBean wombat1 = + JMX.newMBeanProxy(c,wombatName1,WombatMBean.class); + final String newCaption="I am still the same old wombat"; + wombat1.setCaption(newCaption); + // rm roo + c.unregisterMBean(dirName1); + // ln -s kanga roo + System.err.println("**** Creating " + dirName1 + + " ****"); + c.createMBean(JMXRemoteTargetNamespace.class.getName(), + dirName1, params2,signature2); + System.err.println("**** Created " + dirName1 + + " ****"); + Exception x = null; + try { + // touch kanga/roo/wombat + wombat1.setCaption(newCaption+" I hope"); + } catch (RuntimeOperationsException r) { + x=(Exception)r.getCause(); + System.out.println("Got expected exception: " + x); + //r.printStackTrace(); + } + if (x == null) + throw new RuntimeException("should have failed!"); + x = null; + try { + // ls kanga/roo/wombat + System.err.println("**** Connecting " + dirName1 + + " ****"); + JMX.newMBeanProxy(c,dirName1, + JMXRemoteNamespaceMBean.class).connect(); + System.err.println("**** Connected " + dirName1 + + " ****"); + } catch (IOException r) { + x=r; + System.out.println("Got expected exception: " + x); + //r.printStackTrace(); + } + System.err.println("**** Expected Exception Not Raised ****"); + if (x == null) { + System.out.println(dirName1+" contains: "+ + c.queryNames(new ObjectName( + dirName1.getDomain()+"*:*"),null)); + throw new RuntimeException("cycle not detected!"); + } + } catch (Exception t) { + if (failed == null) failed = t; + } finally { + c.unregisterMBean(dirName2); + } + } finally { + try { + c.unregisterMBean(dirName1); + } catch (Exception t) { + if (failed == null) failed = t; + System.err.println("Failed to unregister "+dirName1+ + ": "+t); + } + try { + client.close(); + } catch (Exception t) { + if (failed == null) failed = t; + System.err.println("Failed to close client: "+t); + } + try { + server.stop(); + } catch (Exception t) { + if (failed == null) failed = t; + System.err.println("Failed to stop server: "+t); + } + } + } finally { + try { + conf.close(); + } catch (Exception t) { + if (failed == null) failed = t; + System.err.println("Failed to stop server: "+t); + } + } + if (failed != null) throw failed; + System.err.println("probeKangaRooCycleTest PASSED"); + } catch (Exception x) { + System.err.println("probeKangaRooCycleTest FAILED: " +x); + x.printStackTrace(); + throw new RuntimeException(x); + } + } + public static void main(String[] args) { + simpleTest(args); + recursiveTest(args); + probeKangaRooTest(args); + probeKangaRooCycleTest(args); + } + +} diff --git a/test/javax/management/namespace/JMXNamespaceViewTest.java b/test/javax/management/namespace/JMXNamespaceViewTest.java new file mode 100644 index 000000000..ad51af301 --- /dev/null +++ b/test/javax/management/namespace/JMXNamespaceViewTest.java @@ -0,0 +1,549 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * + * @test JMXNamespaceViewTest.java + * @summary Test the JMXNamespaceView class. + * @author Daniel Fuchs + * @run clean JMXNamespaceViewTest Wombat WombatMBean + * @run build JMXNamespaceViewTest Wombat WombatMBean + * @run main JMXNamespaceViewTest + */ + + +import java.lang.management.ManagementFactory; +import java.net.ServerSocket; +import java.rmi.registry.LocateRegistry; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.management.JMException; +import javax.management.MBeanRegistration; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.namespace.JMXNamespace; +import javax.management.namespace.JMXNamespaceView; +import javax.management.namespace.JMXNamespaces; +import javax.management.namespace.JMXRemoteNamespace; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +/** + * A simple test to test the JMXNamespaceViewTest... + * @author dfuchs + */ +public class JMXNamespaceViewTest { + + // TODO: Remove this when contexts are added. + public static class ClientContext { + public final static String NAMESPACE = "jmx.context"; + } + + /** + * Describe the configuration of a namespace + */ + public static class NamespaceConfig { + /** name of the namespace - no // allowed **/ + public String name; + /** + * JMXServiceURL through which the namespace is exported, if it + * is a remote namespace. {@code null} if the namespace is local. + * This is an inpur URL - eg: new JMXServiceURL("rmi",null,0).toString() + * is acceptable here. + */ + public String jmxurl; + /** + * Values of the name= key for each WombatMBean contained in the + * namespace. + */ + public String[] wombats; + /** list of child namespace **/ + public NamespaceConfig[] children; + } + + /** + * Creates a NamespaceConfig record for a local namespace. + * @param name name of the namespace + * @param wombats names of WombatMBean it should contain. + * @return a NamespaceConfig. + */ + public static NamespaceConfig config(String name, String[] wombats) { + return config(name,null,wombats); + } + + /** + * Creates a NamespaceConfig record for a remote namespace. + * @param name name of the namespace + * @param jmxurl input JMXServiceURL for creating the JMXConnectorServer + * @param wombats names of WombatMBean it should contain. + * @return a NamespaceConfig. + */ + public static NamespaceConfig config(String name, String jmxurl, + String[] wombats) { + return config(name,jmxurl,wombats,(NamespaceConfig[])null); + } + + /** + * Creates a NamespaceConfig record for a local namespace. + * @param name name of the namespace + * @param wombats names of WombatMBean it should contain. + * @param children list of sub namespaces. + * @return a NamespaceConfig. + */ + public static NamespaceConfig config(String name, String[] wombats, + NamespaceConfig... children) { + return config(name,null,wombats,children); + } + + /** + * Creates a NamespaceConfig record for a remote namespace. + * @param name name of the namespace + * @param jmxurl input JMXServiceURL for creating the JMXConnectorServer + * @param wombats names of WombatMBean it should contain. + * @param children list of sub namespaces. + * @return a NamespaceConfig. + */ + static NamespaceConfig config(String name, String jmxurl, String[] wombats, + NamespaceConfig... children) { + final NamespaceConfig cfg = new NamespaceConfig(); + cfg.name=name; cfg.jmxurl=jmxurl; cfg.wombats=wombats; + cfg.children=children; + return cfg; + } + + /** + * Returns the given names. This is a utility method to ease code + * reading. + * @param names names of Wombat MBeans. + * @return the given names. + */ + static String[] wombats(String... names) { + return names; + } + + /** + * Creates a JMXServiceURL string for the given protocol. + * This is also a utility method to ease code reading. + * @param protocol The protocol name (e.g. "rmi") + * @return A JMXServiceURL string. + * @throws Exception if creation of the JMXServiceURL fails. + */ + static String url(String protocol) throws Exception { + return new JMXServiceURL(protocol,null,0).toString(); + } + + /** + * Creates a config for a hierarchy of namespaces, mixing local namespaces + * and remote namespaces using the given protocol. + * @param protocol The protocol that should be used for remote namespaces. + * @return A namespace config hierarchy. + * @throws java.lang.Exception + */ + public static NamespaceConfig[] makeConfig(String protocol) + throws Exception { + final NamespaceConfig[] config = { + // Top level namespace "top1" (local) + config("top1",wombats("wchief","w1","w2","w3"), + // top1//local1 + config("local1",wombats("wchief","ww1","ww2")), + // top1//local2 + config("local2",wombats("wchief","ww4","ww5","ww6"), + // top1//local2//local3 + config("local3",wombats("wchief","www1","www2")), + // top1//local2//rmi1 + config("rmi1",url(protocol),wombats("wchief","www3","www4","www5"))), + // top1//rmi2 + config("rmi2",url(protocol),wombats("wchief","ww7","ww8","ww9"), + // top1//rmi2//local4 + config("local4",wombats("wchief","www6","www7")), + // top1//rmi2//rmi3 + config("rmi3",url(protocol),wombats("wchief","www3","www4","www5"), + // top1//rmi2//rmi3//local5 + config("local5",wombats("wchief","wwww1"))))), + // Top level namespace "top2" (local) + config("top2",wombats("wchief","w21","w22","w23"), + // top2//local21 + config("local21",wombats("wchief","ww21","ww22")), + // top2//rmi22 + config("rmi22",url(protocol),wombats("wchief","ww27","ww28","ww29"), + // top2//rmi22//local24 + config("local24",wombats("wchief","www26","www27")), + // top2//rmi22//rmi23 + config("rmi23",url(protocol),wombats("wchief","www23","www24","www25"), + // top2//rmi22//rmi23//local25 + config("local25",wombats("wchief","wwww21"))))), + // Top level namespace "top3" (remote) + config("top3",url(protocol),wombats("wchief","w31","w32","w33"), + // top3//local31 + config("local31",wombats("wchief","ww31","ww32")), + // top3//rmi32 + config("rmi32",url(protocol),wombats("wchief","ww37","ww38","ww39"), + // top3//rmi32//local34 + config("local34",wombats("wchief","www36","www37")), + // top3//rmi32//rmi33 + config("rmi33",url(protocol),wombats("wchief","www33","www34","www35"), + // top3//rmi32//local35 + config("local35",wombats("wchief","wwww31"))))), + }; + return config; + } + + /** + * Close all connector servers in the list. + * @param cslist List of connector servers to close. + */ + public static void closeAll(List cslist) { + for (JMXConnectorServer cs : cslist) { + try { + cs.stop(); + } catch (Exception xx) { + System.err.println("Failed to stop connector: " + xx); + } + } + } + + public static class MBeanServerConfigCreator { + public MBeanServer createMBeanServerFor(NamespaceConfig config) { + return MBeanServerFactory.newMBeanServer(); + } + } + + /** + * Load the given namespace configuration inside the given MBeanServer. + * Return a list of connector servers created in the process. + * @param server The MBeanServer in which the namespaces must + * be created. + * @param namespaces The list of namespaces to create. + * @return a list of started connector servers. + * @throws java.lang.Exception failed to create the specified namespaces. + */ + public static List load(MBeanServer server, + MBeanServerConfigCreator factory, + NamespaceConfig... namespaces) throws Exception { + final List cslist = + new ArrayList(); + try { + final ObjectName creator = + new ObjectName("jmx.creator:type=JMXNamespaceCreator"); + if (System.getProperty("jmx.wait")!=null + && !server.isRegistered(creator)) { + server.registerMBean(new JMXNamespaceCreator(),creator); + } + for (NamespaceConfig cfg : namespaces) { + final MBeanServer srv = factory.createMBeanServerFor(cfg); + if (System.getProperty("jmx.wait")!=null + && !srv.isRegistered(creator)) { + srv.registerMBean(new JMXNamespaceCreator(),creator); + } + if (cfg.wombats != null) { + for (String w : cfg.wombats) { + final ObjectName n = + new ObjectName("wombat:type=Wombat,name=" + w); + final WombatMBean ww = new Wombat(); + srv.registerMBean(ww, n); + } + } + if (cfg.children != null) { + cslist.addAll(load(srv, factory, cfg.children)); + } + JMXNamespace nm; + if (cfg.jmxurl == null) { + nm = new JMXNamespace(srv); + } else { + JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(new JMXServiceURL(cfg.jmxurl), + null, srv); + srv.registerMBean(cs, + new ObjectName("jmx.remote:type=JMXConnectorServer")); + cs.start(); + cslist.add(cs); + nm = JMXRemoteNamespace. + newJMXRemoteNamespace(cs.getAddress(), + null); + } + server.registerMBean(nm, + JMXNamespaces.getNamespaceObjectName(cfg.name)); + if (nm instanceof JMXRemoteNamespace) { + server.invoke( + JMXNamespaces.getNamespaceObjectName(cfg.name), + "connect", null, null); + } + } + } catch (Exception x) { + closeAll(cslist); + throw x; + } + return cslist; + } + + /** + * Add an entry {@code } in the map for the given + * namespace and its subnamespaces. + * @param map A {@code Map}. + * @param parent The path of the parent workspace. + * @param cfg The NamespaceConfig hierarchy to index in the map. + */ + public static void fillMap(Map map, String parent, + NamespaceConfig cfg) { + + final String where; + if (parent == null || parent.equals("")) + where=cfg.name; + else + where=parent+JMXNamespaces.NAMESPACE_SEPARATOR+cfg.name; + map.put(where,cfg); + if (cfg.children==null) return; + for(NamespaceConfig child:cfg.children) { + fillMap(map,where,child); + } + } + + /** + * Compare a list of namespace names obtained from JMXNamespaceView.list() + * with the expected clildren list of the corresponding NamespaceConfig. + * @param list A list of namespace names + * @param children A list of NamespaceConfig correspondng to expected + * namespace. + * @param fail If true and the comparison yields false, throws an + * exception instead of simply returning false. + * @return true if OK, false if NOK. + */ + private static boolean compare(String[] list, NamespaceConfig[] children, + boolean fail) { + final List found = new ArrayList(Arrays.asList(list)); + if (found.contains(ClientContext.NAMESPACE)) + found.remove(ClientContext.NAMESPACE); + + if (children == null && found.size()==0) return true; + if (children == null && fail == false) return false; + if (children == null) throw new RuntimeException( + "No child expected. Found "+Arrays.toString(list)); + final Set names = new HashSet(); + for (NamespaceConfig cfg : children) { + names.add(cfg.name); + if (found.contains(cfg.name)) continue; + if (!fail) return false; + throw new RuntimeException(cfg.name+" not found in "+ + found); + } + found.removeAll(names); + if (found.size()==0) return true; + if (fail==false) return false; + throw new RuntimeException("found additional namespaces: "+ + found); + } + + /** + * Compares the result of queryNames(null,null) with a set of expected + * wombats. + * @param where The path of the namespace that was queried. + * @param list The set of ObjectNames found. + * @param wombats The expected list of wombats. + * @param fail If true and the comparison yields false, throws an + * exception instead of simply returning false. + * @return true if OK, false if NOK. + * @throws java.lang.Exception something went wrong. + */ + private static boolean compare(String where, + Setlist, String[] wombats, + boolean fail) throws Exception { + final Set found = new HashSet(); + final Set expected = new HashSet(); + for (ObjectName n : list) { + if ("Wombat".equals(n.getKeyProperty("type"))) + found.add(n); + } + for(String w : wombats) { + final ObjectName n = + new ObjectName("wombat:type=Wombat,name=" + w); + expected.add(n); + if (found.contains(n)) continue; + if (fail == false) return false; + throw new RuntimeException(where+ + ": Wombat "+w+" not found in "+found); + } + found.removeAll(expected); + if (found.size()==0) { + System.out.println(where+": found all expected: "+expected); + return true; + } + if (fail==false) return false; + throw new RuntimeException(where+": found additional MBeans: "+ + found); + } + + /** + * A generic test to test JMXNamespaceView over a namespace configuration. + * @param server The MBeanServer in which to load the namespace + * config. + * @param namespaces The namespace config to run the test over... + * @throws java.lang.Exception + */ + public static void doTest(MBeanServer server, NamespaceConfig... namespaces) + throws Exception { + List cslist = load(server, + new MBeanServerConfigCreator(), namespaces); + Map inputMap = + new HashMap(); + + for (NamespaceConfig cfg : namespaces) { + fillMap(inputMap,"",cfg); + } + try { + final JMXNamespaceView root = new JMXNamespaceView(server); + List vlist = new ArrayList(); + vlist.add(root); + + while (!vlist.isEmpty()) { + JMXNamespaceView v = vlist.remove(0); + final String where = v.isRoot()?"root":v.where(); + System.out.println(where+": "+ + v.getMBeanServerConnection().queryNames(null,null)); + for (String ns : v.list()) { + final JMXNamespaceView down = v.down(ns); + vlist.add(down); + if (!down.where().equals(v.isRoot()?ns:where+ + JMXNamespaces.NAMESPACE_SEPARATOR+ns)) { + throw new RuntimeException("path of "+down.where()+ + " should be "+(v.isRoot()?ns:where+ + JMXNamespaces.NAMESPACE_SEPARATOR+ns)); + } + if (down.up().equals(v)) continue; + throw new RuntimeException("parent of "+down.where()+ + " should be "+where); + } + final NamespaceConfig[] children; + final NamespaceConfig cfg; + if (v.isRoot()) { + children=namespaces; + cfg = null; + } else { + cfg = inputMap.get(where); + children = cfg==null?null:cfg.children; + } + compare(v.list(),children,true); + if (!v.isRoot()) { + if (where.endsWith(ClientContext.NAMESPACE)) { + System.out.println(where+": skipping queryNames analysis"); + continue; + } + //System.out.println(where+": cfg is: "+cfg); + compare(where,v.getMBeanServerConnection(). + queryNames(null, null),cfg.wombats,true); + } + } + + exportAndWaitIfNeeded(server); + } finally { + closeAll(cslist); + } + } + + public static interface JMXNamespaceCreatorMBean { + public ObjectInstance createLocalNamespace(String namespace) + throws JMException ; + public void removeLocalNamespace(String namespace) + throws JMException; + } + + public static class JMXNamespaceCreator + implements MBeanRegistration, + JMXNamespaceCreatorMBean { + + private volatile MBeanServer mbeanServer; + + public ObjectInstance createLocalNamespace(String namespace) + throws JMException { + return mbeanServer.registerMBean( + new JMXNamespace(MBeanServerFactory.newMBeanServer()), + JMXNamespaces.getNamespaceObjectName(namespace)); + } + + public void removeLocalNamespace(String namespace) + throws JMException { + mbeanServer.unregisterMBean( + JMXNamespaces.getNamespaceObjectName(namespace)); + } + + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + mbeanServer = server; + return name; + } + + public void postRegister(Boolean registrationDone) { + } + + public void preDeregister() throws Exception { + } + + public void postDeregister() { + } + + } + + public static void exportAndWaitIfNeeded(MBeanServer server) + throws Exception { + if (System.getProperty("jmx.wait")!=null) { + final int port = getPortFor("rmi"); + LocateRegistry.createRegistry(port); + final JMXServiceURL url = + new JMXServiceURL("rmi",null,port, + "/jndi/rmi://localhost:"+port+"/jmxrmi"); + final JMXConnectorServer cs = + JMXConnectorServerFactory. + newJMXConnectorServer(url, null, server); + cs.start(); + try { + System.out.println("RMI Server waiting at: "+cs.getAddress()); + System.in.read(); + } finally { + cs.stop(); + } + } + } + + public static int getPortFor(String protocol) throws Exception { + final int aport = + Integer.valueOf(System.getProperty("jmx."+protocol+".port","0")); + if (aport > 0) return aport; + final ServerSocket s = new ServerSocket(0); + try { + final int port = s.getLocalPort(); + return port; + } finally { + s.close(); + } + } + + public static void main(String[] args) throws Exception { + doTest(ManagementFactory.getPlatformMBeanServer(),makeConfig("rmi")); + } + +} diff --git a/test/javax/management/namespace/JMXNamespacesTest.java b/test/javax/management/namespace/JMXNamespacesTest.java new file mode 100644 index 000000000..4249bf104 --- /dev/null +++ b/test/javax/management/namespace/JMXNamespacesTest.java @@ -0,0 +1,647 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * @test JMXNamespacesTest.java + * @summary Test the static method that rewrite ObjectNames in JMXNamespacesTest + * @author Daniel Fuchs + * @run clean JMXNamespacesTest + * @compile -XDignore.symbol.file=true JMXNamespacesTest.java + * @run main JMXNamespacesTest + */ + +import com.sun.jmx.namespace.ObjectNameRouter; +import java.io.Serializable; +import java.util.Arrays; +import java.util.logging.Logger; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.namespace.JMXNamespaces; + +/** + * Class JMXNamespacesTest + * @author Sun Microsystems, 2005 - All rights reserved. + */ +public class JMXNamespacesTest { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(JMXNamespacesTest.class.getName()); + + /** Creates a new instance of JMXNamespacesTest */ + public JMXNamespacesTest() { + } + + public static class CustomObject implements Serializable { + ObjectName toto; + String titi; + CustomObject(String toto, String titi) { + try { + this.toto = new ObjectName(toto); + } catch (MalformedObjectNameException m) { + throw new IllegalArgumentException(m); + } + this.titi = titi; + } + private Object[] data() { + return new Object[] {toto, titi}; + } + @Override + public boolean equals(Object other) { + if (! (other instanceof CustomObject)) return false; + return Arrays.deepEquals(data(),((CustomObject)other).data()); + } + @Override + public int hashCode() { + return Arrays.deepHashCode(data()); + } + } + + public static CustomObject obj(String toto, String titi) { + return new CustomObject(toto,titi); + } + + private static String failure; + + public static void testDeepRewrite() throws Exception { + failure = null; + String s1 = "x//y//d:k=v"; + String s2 = "v//w//x//y//d:k=v"; + String p1 = "v//w"; + String p3 = "a//b"; + + System.out.println("inserting "+p1); + final CustomObject foo1 = + JMXNamespaces.deepReplaceHeadNamespace(obj(s1,s1),"",p1); + assertEquals(foo1.toto.toString(),p1+"//"+s1); + assertEquals(foo1.titi,s1); + + System.out.println("removing "+p1); + final CustomObject foo2 = + JMXNamespaces.deepReplaceHeadNamespace(obj(s2,s2),p1,""); + assertEquals(foo2.toto.toString(),s1); + assertEquals(foo2.titi,s2); + + System.out.println("removing "+p1); + final CustomObject foo3 = + JMXNamespaces.deepReplaceHeadNamespace(obj(p1+"//"+s2,s2),p1,""); + assertEquals(foo3.toto.toString(),s2); + assertEquals(foo3.titi,s2); + + System.out.println("replacing "+p1+" with "+p3); + final CustomObject foo4 = + JMXNamespaces.deepReplaceHeadNamespace(obj(s2,s2),p1,p3); + assertEquals(foo4.toto.toString(),p3+"//"+s1); + assertEquals(foo4.titi,s2); + + System.out.println("replacing "+p1+" with "+p1); + final CustomObject foo5 = + JMXNamespaces.deepReplaceHeadNamespace(obj(s2,s2),p1,p1); + assertEquals(foo5.toto.toString(),s2); + assertEquals(foo5.titi,s2); + + System.out.println("removing x//y in "+s2); + try { + final CustomObject foo7 = + JMXNamespaces.deepReplaceHeadNamespace(obj(s2,s2),"x//y",""); + failed("Remove x//y in "+s2+" should have failed!"); + } catch (IllegalArgumentException x) { + System.out.println("Received expected exception: "+x); + } + + System.out.println("replacing x//y with "+p3+" in "+s2); + try { + final CustomObject foo7 = + JMXNamespaces.deepReplaceHeadNamespace(obj(s2,s2),"x//y",p3); + failed("Replace x//y in "+s2+" should have failed!"); + } catch (IllegalArgumentException x) { + System.out.println("Received expected exception: "+x); + } + + if (failure != null) throw new Exception(failure); + } + + private static String[][] wildcards = { + { "", "*:*" }, + { "//", "//*:*" }, + { "foo", "foo//*:*" }, + { "//foo", "//foo//*:*" }, + { "////foo", "//foo//*:*" }, + { "foo//", "foo//*:*" }, + { "foo////", "foo//*:*" }, + { "//foo//", "//foo//*:*" }, + { "////foo//", "//foo//*:*" }, + { "////foo////", "//foo//*:*" }, + { "foo//bar", "foo//bar//*:*" }, + { "//foo//bar", "//foo//bar//*:*" }, + { "////foo//bar", "//foo//bar//*:*" }, + { "foo//bar//", "foo//bar//*:*" }, + { "foo//bar////", "foo//bar//*:*" }, + { "//foo//bar//", "//foo//bar//*:*" }, + { "////foo//bar//", "//foo//bar//*:*" }, + { "////foo//bar////", "//foo//bar//*:*" }, + { "foo////bar", "foo//bar//*:*" }, + { "//foo////bar", "//foo//bar//*:*" }, + { "////foo////bar", "//foo//bar//*:*" }, + { "foo////bar//", "foo//bar//*:*" }, + { "foo////bar////", "foo//bar//*:*" }, + { "//foo////bar//", "//foo//bar//*:*" }, + { "////foo////bar//", "//foo//bar//*:*" }, + { "////foo////bar////", "//foo//bar//*:*" }, + { "fo/o", "fo/o//*:*" }, + { "//f/oo", "//f/oo//*:*" }, + { "////f/o/o", "//f/o/o//*:*" }, + { "fo/o//", "fo/o//*:*" }, + { "f/oo////", "f/oo//*:*" }, + { "//fo/o//", "//fo/o//*:*" }, + { "////f/oo//", "//f/oo//*:*" }, + { "////f/o/o////", "//f/o/o//*:*" }, + { "foo//b/a/r", "foo//b/a/r//*:*" }, + { "//fo/o//bar", "//fo/o//bar//*:*" }, + { "////foo//b/ar", "//foo//b/ar//*:*" }, + { "foo//ba/r//", "foo//ba/r//*:*" }, + { "f/oo//bar////", "f/oo//bar//*:*" }, + { "//f/o/o//bar//", "//f/o/o//bar//*:*" }, + { "////foo//b/a/r//", "//foo//b/a/r//*:*" }, + { "////f/o/o//b/a/r////", "//f/o/o//b/a/r//*:*" }, + { "foo////ba/r", "foo//ba/r//*:*" }, + { "//foo////b/ar", "//foo//b/ar//*:*" }, + { "////f/oo////bar", "//f/oo//bar//*:*" }, + { "fo/o////bar//", "fo/o//bar//*:*" }, + { "foo////ba/r////", "foo//ba/r//*:*" }, + { "//fo/o////ba/r//", "//fo/o//ba/r//*:*" }, + { "////f/oo////b/ar//", "//f/oo//b/ar//*:*" }, + { "////f/o/o////b/a/r////", "//f/o/o//b/a/r//*:*" }, + }; + private final static String[] badguys = { + null, + "/", "/*:*", + "///", "///*:*" , + "/foo", "/foo//*:*", + "//foo/", "//foo///*:*" , + "/////foo", "///foo//*:*", + "/foo//", "/foo//*:*", + "foo/////", "foo///*:*", + "///foo//", "///foo//*:*", + "////foo///", "//foo///*:*" , + "/////foo/////", "///foo///*:*", + "/foo//bar", "/foo//bar//*:*", + "//foo///bar", "//foo///bar//*:*", + "/////foo////bar/", "///foo//bar///*:*", + "foo///bar//", "foo//bar///*:*", + "foo//bar/////", "foo///bar//*:*", + "///foo//bar//", "//foo///bar//*:*" , + }; + public static void testWildcard() throws Exception { + int i = 0; + for (String[] pair : wildcards) { + i++; + final String msg = "testWildcard[good,"+i+"] "+Arrays.asList(pair)+": "; + assertEquals(msg, new ObjectName(pair[1]), + JMXNamespaces.getWildcardFor(pair[0])); + } + i=0; + for (String bad : badguys) { + i++; + try { + JMXNamespaces.getWildcardFor(bad); + failed("testWildcard[bad,"+i+"] "+bad+" incorrectly accepted. " + + "IllegalArgumentException was expected"); + } catch (IllegalArgumentException x) { + // OK + } + } + if (failure != null) throw new Exception(failure); + } + + private static String[][] goodinsert = { + {"","d:k=v","d:k=v"}, + {"","//d:k=v","//d:k=v"}, + {"//","d:k=v","//d:k=v"}, + {"//","//d:k=v","//d:k=v"}, + {"//","a//d:k=v","//a//d:k=v"}, + {"//","//a//d:k=v","//a//d:k=v"}, + {"//","////a////d:k=v","//a//d:k=v"}, + {"//b","////a////d:k=v","//b//a//d:k=v"}, + {"b","////a////d:k=v","b//a//d:k=v"}, + {"b","d:k=v","b//d:k=v"}, + {"b//","d:k=v","b//d:k=v"}, + {"//b//","d:k=v","//b//d:k=v"}, + {"//b","////a////d:k=v","//b//a//d:k=v"}, + {"b//c","////a////d:k=v","b//c//a//d:k=v"}, + {"b//c","d:k=v","b//c//d:k=v"}, + {"b//c//","d:k=v","b//c//d:k=v"}, + {"//b//c//","d:k=v","//b//c//d:k=v"}, + {"","/d:k=v","/d:k=v"}, + {"","///d:k=v","///d:k=v"}, + {"//","/d:k=v","///d:k=v"}, + {"//","///d:k=v","///d:k=v"}, + {"//","a///d:k=v","//a///d:k=v"}, + {"//","//a///d:k=v","//a///d:k=v"}, + {"//","////a////d/:k=v","//a//d/:k=v"}, + {"//b","////a/////d:k=v","//b//a///d:k=v"}, + {"b","////a////d/:k=v","b//a//d/:k=v"}, + {"b","/d:k=v","b///d:k=v"}, + {"b//","/d:k=v","b///d:k=v"}, + {"//b//","/d:k=v","//b///d:k=v"}, + {"//b","////a/////d:k=v","//b//a///d:k=v"}, + {"b//c","////a/////d:k=v","b//c//a///d:k=v"}, + {"b//c","/d:k=v","b//c///d:k=v"}, + {"b//c//","/d:k=v","b//c///d:k=v"}, + {"//b//c//","d/:k=v","//b//c//d/:k=v"}, + }; + + private static String[][] badinsert = { + {"/","d:k=v"}, + {"/","//d:k=v"}, + {"///","d:k=v"}, + {"///","//d:k=v"}, + {"///","/a//d:k=v"}, + {"///","///a//d:k=v"}, + {"///","/////a////d:k=v"}, + {"//b","/////a////d:k=v"}, + {"b/","////a////d:k=v"}, + {"b/","d:k=v"}, + {"b///","d:k=v"}, + {"//b///","d:k=v"}, + {"//b/","////a////d:k=v"}, + {"b///c","////a////d:k=v"}, + {"b//c/","d:k=v"}, + {"b///c//","d:k=v"}, + {"//b///c//","d:k=v"}, + + }; + + public static void testInsertPath() throws Exception { + int i = 0; + for (String[] pair : goodinsert) { + i++; + final String msg = "testInsertPath[good,"+i+"] "+Arrays.asList(pair)+": "; + assertEquals(msg,new ObjectName(pair[2]), + JMXNamespaces.insertPath(pair[0], + new ObjectName(pair[1]))); + } + i=0; + for (String[] bad : badinsert) { + i++; + try { + JMXNamespaces.insertPath(bad[0], + new ObjectName(bad[1])); + failed("testInsertPath[bad,"+i+"] "+ + Arrays.asList(bad)+" incorrectly accepted. " + + "IllegalArgumentException was expected"); + } catch (IllegalArgumentException x) { + // OK + } + } + if (failure != null) throw new Exception(failure); + } + + private static String[][] testpath = { + {"/a/a/:k=v",""}, + {"/:k=v",""}, + {"bli:k=v",""}, + {"///a/a/:k=v",""}, + {"///:k=v",""}, + {"//bli:k=v",""}, + {"/////a/a/:k=v",""}, + {"/////:k=v",""}, + {"////bli:k=v",""}, + {"y///a/a/:k=v","y"}, + {"y///:k=v","y"}, + {"y//bli:k=v","y"}, + {"y/////a/a/:k=v","y"}, + {"y/////:k=v","y"}, + {"y////bli:k=v","y"}, + {"//y///a/a/:k=v","y"}, + {"//y///:k=v","y"}, + {"//y//bli:k=v","y"}, + {"//y/////a/a/:k=v","y"}, + {"//y/////:k=v","y"}, + {"//y////bli:k=v","y"}, + {"////y///a/a/:k=v","y"}, + {"////y///:k=v","y"}, + {"////y//bli:k=v","y"}, + {"////y/////a/a/:k=v","y"}, + {"////y/////:k=v","y"}, + {"////y////bli:k=v","y"}, + + {"z//y///a/a/:k=v","z//y"}, + {"z//y///:k=v","z//y"}, + {"z//y//bli:k=v","z//y"}, + {"z//y/////a/a/:k=v","z//y"}, + {"z//y/////:k=v","z//y"}, + {"z//y////bli:k=v","z//y"}, + {"//z//y///a/a/:k=v","z//y"}, + {"//z//y///:k=v","z//y"}, + {"//z//y//bli:k=v","z//y"}, + {"//z//y/////a/a/:k=v","z//y"}, + {"//z//y/////:k=v","z//y"}, + {"//z//y////bli:k=v","z//y"}, + {"z////y///a/a/:k=v","z//y"}, + {"z////y///:k=v","z//y"}, + {"z////y//bli:k=v","z//y"}, + {"z////y/////a/a/:k=v","z//y"}, + {"z////y/////:k=v","z//y"}, + {"z////y////bli:k=v","z//y"}, + {"//z////y///a/a/:k=v","z//y"}, + {"//z////y///:k=v","z//y"}, + {"//z////y//bli:k=v","z//y"}, + {"//z////y/////a/a/:k=v","z//y"}, + {"//z////y/////:k=v","z//y"}, + {"//z////y////bli:k=v","z//y"}, + {"////z////y///a/a/:k=v","z//y"}, + {"////z////y///:k=v","z//y"}, + {"////z////y//bli:k=v","z//y"}, + {"////z////y/////a/a/:k=v","z//y"}, + {"////z////y/////:k=v","z//y"}, + {"////z////y////bli:k=v","z//y"}, + + }; + + public static void testGetNormalizedPath() throws Exception { + int i = 0; + for (String[] pair : testpath) { + i++; + final String msg = "testGetNormalizedPath["+i+"] "+Arrays.asList(pair)+": "; + assertEquals(msg,pair[1], + JMXNamespaces.getContainingNamespace(new ObjectName(pair[0]))); + } + if (failure != null) throw new Exception(failure); + } + + private static String[][] testdomain = { + {"/a/a/","/a/a/"}, + {"/","/"}, + {"bli","bli"}, + {"///a/a/","///a/a/"}, + {"///","///"}, + {"//bli","//bli"}, + {"/////a/a/","///a/a/"}, + {"/////","///"}, + {"////bli","//bli"}, + {"y///a/a/","y///a/a/"}, + {"y///","y///"}, + {"y//bli","y//bli"}, + {"y/////a/a/","y///a/a/"}, + {"y/////","y///"}, + {"y////bli","y//bli"}, + {"//y///a/a/","//y///a/a/"}, + {"//y///","//y///"}, + {"//y//bli","//y//bli"}, + {"//y/////a/a/","//y///a/a/"}, + {"//y/////","//y///"}, + {"//y////bli","//y//bli"}, + {"////y///a/a/","//y///a/a/"}, + {"////y///","//y///"}, + {"////y//bli","//y//bli"}, + {"////y/////a/a/","//y///a/a/"}, + {"////y/////","//y///"}, + {"////y////bli","//y//bli"}, + + {"z//y///a/a/","z//y///a/a/"}, + {"z//y///","z//y///"}, + {"z//y//bli","z//y//bli"}, + {"z//y/////a/a/","z//y///a/a/"}, + {"z//y/////","z//y///"}, + {"z//y////bli","z//y//bli"}, + {"//z//y///a/a/","//z//y///a/a/"}, + {"//z//y///","//z//y///"}, + {"//z//y//bli","//z//y//bli"}, + {"//z//y/////a/a/","//z//y///a/a/"}, + {"//z//y/////","//z//y///"}, + {"//z//y////bli","//z//y//bli"}, + {"z////y///a/a/","z//y///a/a/"}, + {"z////y///","z//y///"}, + {"z////y//bli","z//y//bli"}, + {"z////y/////a/a/","z//y///a/a/"}, + {"z////y/////","z//y///"}, + {"z////y////bli","z//y//bli"}, + {"//z////y///a/a/","//z//y///a/a/"}, + {"//z////y///","//z//y///"}, + {"//z////y//bli","//z//y//bli"}, + {"//z////y/////a/a/","//z//y///a/a/"}, + {"//z////y/////","//z//y///"}, + {"//z////y////bli","//z//y//bli"}, + {"////z////y///a/a/","//z//y///a/a/"}, + {"////z////y///","//z//y///"}, + {"////z////y//bli","//z//y//bli"}, + {"////z////y/////a/a/","//z//y///a/a/"}, + {"////z////y/////","//z//y///"}, + {"////z////y////bli","//z//y//bli"}, + + {"bli//","bli//"}, + {"//bli//","//bli//"}, + {"////bli//","//bli//"}, + {"y////","y//"}, + {"y//bli//","y//bli//"}, + {"y////","y//"}, + {"y////bli//","y//bli//"}, + {"//y////","//y//"}, + {"//y//bli//","//y//bli//"}, + {"//y//////","//y//"}, + {"//y////bli//","//y//bli//"}, + {"////y////","//y//"}, + {"////y//bli////","//y//bli//"}, + {"////y//////","//y//"}, + {"////y////bli////","//y//bli//"}, + {"z//y////","z//y//"}, + {"z//y//bli//","z//y//bli//"}, + {"z//y//////","z//y//"}, + {"z//y////bli//","z//y//bli//"}, + {"//z//y////","//z//y//"}, + {"//z//y//bli//","//z//y//bli//"}, + {"//z//y//////","//z//y//"}, + {"//z//y////bli//","//z//y//bli//"}, + {"z////y////","z//y//"}, + {"z////y//bli//","z//y//bli//"}, + {"z////y//////","z//y//"}, + {"z////y////bli//","z//y//bli//"}, + {"//z////y////","//z//y//"}, + {"//z////y//bli//","//z//y//bli//"}, + {"//z////y//////","//z//y//"}, + {"//z////y////bli//","//z//y//bli//"}, + {"////z////y////","//z//y//"}, + {"////z////y//bli//","//z//y//bli//"}, + {"////z////y//////","//z//y//"}, + {"////z////y////bli//","//z//y//bli//"}, + + }; + private static String[][] testnolead = { + {"/a/a/","/a/a/"}, + {"/","/"}, + {"bli","bli"}, + {"///a/a/","/a/a/"}, + {"///","/"}, + {"//bli","bli"}, + {"/////a/a/","/a/a/"}, + {"/////","/"}, + {"////bli","bli"}, + {"y///a/a/","y///a/a/"}, + {"y///","y///"}, + {"y//bli","y//bli"}, + {"y/////a/a/","y///a/a/"}, + {"y/////","y///"}, + {"y////bli","y//bli"}, + {"//y///a/a/","y///a/a/"}, + {"//y///","y///"}, + {"//y//bli","y//bli"}, + {"//y/////a/a/","y///a/a/"}, + {"//y/////","y///"}, + {"//y////bli","y//bli"}, + {"////y///a/a/","y///a/a/"}, + {"////y///","y///"}, + {"////y//bli","y//bli"}, + {"////y/////a/a/","y///a/a/"}, + {"////y/////","y///"}, + {"////y////bli","y//bli"}, + + {"z//y///a/a/","z//y///a/a/"}, + {"z//y///","z//y///"}, + {"z//y//bli","z//y//bli"}, + {"z//y/////a/a/","z//y///a/a/"}, + {"z//y/////","z//y///"}, + {"z//y////bli","z//y//bli"}, + {"//z//y///a/a/","z//y///a/a/"}, + {"//z//y///","z//y///"}, + {"//z//y//bli","z//y//bli"}, + {"//z//y/////a/a/","z//y///a/a/"}, + {"//z//y/////","z//y///"}, + {"//z//y////bli","z//y//bli"}, + {"z////y///a/a/","z//y///a/a/"}, + {"z////y///","z//y///"}, + {"z////y//bli","z//y//bli"}, + {"z////y/////a/a/","z//y///a/a/"}, + {"z////y/////","z//y///"}, + {"z////y////bli","z//y//bli"}, + {"//z////y///a/a/","z//y///a/a/"}, + {"//z////y///","z//y///"}, + {"//z////y//bli","z//y//bli"}, + {"//z////y/////a/a/","z//y///a/a/"}, + {"//z////y/////","z//y///"}, + {"//z////y////bli","z//y//bli"}, + {"////z////y///a/a/","z//y///a/a/"}, + {"////z////y///","z//y///"}, + {"////z////y//bli","z//y//bli"}, + {"////z////y/////a/a/","z//y///a/a/"}, + {"////z////y/////","z//y///"}, + {"////z////y////bli","z//y//bli"}, + + {"bli//","bli//"}, + {"//bli//","bli//"}, + {"////bli//","bli//"}, + {"y////","y//"}, + {"y//bli//","y//bli//"}, + {"y////","y//"}, + {"y////bli//","y//bli//"}, + {"//y////","y//"}, + {"//y//bli//","y//bli//"}, + {"//y//////","y//"}, + {"//y////bli//","y//bli//"}, + {"////y////","y//"}, + {"////y//bli////","y//bli//"}, + {"////y//////","y//"}, + {"////y////bli////","y//bli//"}, + {"z//y////","z//y//"}, + {"z//y//bli//","z//y//bli//"}, + {"z//y//////","z//y//"}, + {"z//y////bli//","z//y//bli//"}, + {"//z//y////","z//y//"}, + {"//z//y//bli//","z//y//bli//"}, + {"//z//y//////","z//y//"}, + {"//z//y////bli//","z//y//bli//"}, + {"z////y////","z//y//"}, + {"z////y//bli//","z//y//bli//"}, + {"z////y//////","z//y//"}, + {"z////y////bli//","z//y//bli//"}, + {"//z////y////","z//y//"}, + {"//z////y//bli//","z//y//bli//"}, + {"//z////y//////","z//y//"}, + {"//z////y////bli//","z//y//bli//"}, + {"////z////y////","z//y//"}, + {"////z////y//bli//","z//y//bli//"}, + {"////z////y//////","z//y//"}, + {"////z////y////bli//","z//y//bli//"}, + + }; + + public static void testNormalizeDomain() throws Exception { + int i = 0; + for (String[] pair : testdomain) { + i++; + final String msg = "testNormalizeDomain["+i+", false] "+Arrays.asList(pair)+": "; + assertEquals(msg,pair[1], + ObjectNameRouter.normalizeDomain(pair[0],false)); + } + if (failure != null) throw new Exception(failure); + i = 0; + for (String[] pair : testnolead) { + i++; + final String msg = "testNormalizeDomain["+i+", true] "+Arrays.asList(pair)+": "; + assertEquals(msg,pair[1], + ObjectNameRouter.normalizeDomain(pair[0],true)); + } + if (failure != null) throw new Exception(failure); + } + + public static void main(String[] args) throws Exception { + testDeepRewrite(); + testNormalizeDomain(); + testInsertPath(); + testWildcard(); + testGetNormalizedPath(); + } + + private static void assertEquals(Object x, Object y) { + assertEquals("",x,y); + } + + private static void assertEquals(String msg, Object x, Object y) { + if (msg == null) msg=""; + if (!equal(x, y)) + failed(msg+"expected " + string(x) + "; got " + string(y)); + } + + private static boolean equal(Object x, Object y) { + if (x == y) + return true; + if (x == null || y == null) + return false; + if (x.getClass().isArray()) + return Arrays.deepEquals(new Object[] {x}, new Object[] {y}); + return x.equals(y); + } + + private static String string(Object x) { + String s = Arrays.deepToString(new Object[] {x}); + return s.substring(1, s.length() - 1); + } + + + private static void failed(String why) { + failure = why; + new Throwable("FAILED: " + why).printStackTrace(System.out); + } + +} diff --git a/test/javax/management/namespace/JMXRemoteNamespaceTest.java b/test/javax/management/namespace/JMXRemoteNamespaceTest.java new file mode 100644 index 000000000..ccc73bfaf --- /dev/null +++ b/test/javax/management/namespace/JMXRemoteNamespaceTest.java @@ -0,0 +1,189 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * + * @test JMXRemoteNamespaceTest.java + * @summary Basic tests on a JMXRemoteNamespace. + * @author Daniel Fuchs + * @run clean JMXRemoteNamespaceTest Wombat WombatMBean + * @run build JMXRemoteNamespaceTest Wombat WombatMBean + * @run main JMXRemoteNamespaceTest + */ + +import javax.management.JMX; +import javax.management.Notification; +import javax.management.ObjectName; +import javax.management.namespace.JMXNamespaces; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.NotificationListener; +import javax.management.namespace.JMXRemoteNamespace; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.io.IOException; +import javax.management.AttributeChangeNotification; + +/** + * Test simple creation/registration of namespace. + * + */ +public class JMXRemoteNamespaceTest { + + static class MyConnect implements NotificationListener { + private final JMXRemoteNamespace my; + private final List list; + private volatile int connectCount=0; + private int closeCount=0; + private final ObjectName myname; + public MyConnect(JMXRemoteNamespace my, ObjectName myname) { + this.my=my; + this.myname = myname; + list = Collections.synchronizedList(new ArrayList()); + my.addNotificationListener(this, null, null); + } + + public synchronized void connect() throws IOException { + my.connect(); + if (!my.isConnected()) + throw new IOException(myname+" should be connected"); + connectCount++; + } + + public void close() throws IOException { + my.close(); + if (my.isConnected()) + throw new IOException(myname+" shouldn't be connected"); + closeCount++; + } + + public synchronized int getConnectCount() { + return connectCount; + } + public synchronized int getClosedCount() { + return closeCount; + } + + public synchronized void handleNotification(Notification notification, + Object handback) { + list.add(notification); + } + + public synchronized void checkNotifs(int externalConnect, + int externalClosed) throws Exception { + System.err.println("Connected: "+connectCount+" time"+ + ((connectCount>1)?"s":"")); + System.err.println("Closed: "+closeCount+" time"+ + ((closeCount>1)?"s":"")); + System.err.println("Received:"); + int cl=0; + int co=0; + for (Notification n : list) { + System.err.println("\t"+n); + if (!(n instanceof AttributeChangeNotification)) + throw new Exception("Unexpected notif: "+n.getClass()); + final AttributeChangeNotification acn = + (AttributeChangeNotification)n; + if (((Boolean)acn.getNewValue()).booleanValue()) + co++; + else cl++; + if ((((Boolean)acn.getNewValue()).booleanValue()) + == (((Boolean)acn.getOldValue()).booleanValue())) { + throw new Exception("Bad values: old=new"); + } + } + if (! (list.size()==(closeCount+connectCount+ + externalClosed+externalConnect))) { + throw new Exception("Bad notif count - got "+list.size()); + } + if (cl!=(closeCount+externalClosed)) { + throw new Exception("Bad count of close notif: expected " + +(closeCount+externalClosed)+", got"+cl); + } + if (co!=(connectCount+externalConnect)) { + throw new Exception("Bad count of connect notif: expected " + +(connectCount+externalConnect)+", got"+co); + } + } + } + + public static void testConnectClose() throws Exception { + final MBeanServer myServer = MBeanServerFactory.newMBeanServer(); + final JMXConnectorServer myRMI = + JMXConnectorServerFactory.newJMXConnectorServer( + new JMXServiceURL("rmi",null,0), null, myServer); + myRMI.start(); + try { + final JMXRemoteNamespace my = + JMXRemoteNamespace.newJMXRemoteNamespace( + myRMI.getAddress(),null); + final MBeanServer s = MBeanServerFactory.newMBeanServer(); + final ObjectName myname = JMXNamespaces.getNamespaceObjectName("my"); + final ObjectName wname = ObjectName.getInstance("backyard:type=Wombat"); + myServer.registerMBean(new Wombat(),wname); + final MyConnect myc = new MyConnect(my,myname); + myc.connect(); + myc.close(); + myc.connect(); + s.registerMBean(my,myname); + myc.close(); + myc.connect(); + if (!s.queryNames(new ObjectName("my//b*:*"),null).contains( + JMXNamespaces.insertPath("my", wname))) { + throw new RuntimeException("1: Wombat not found: "+wname); + } + myc.close(); + myc.connect(); + final MBeanServer cd = JMXNamespaces.narrowToNamespace(s, "my"); + if (!cd.queryNames(new ObjectName("b*:*"),null).contains(wname)) { + throw new RuntimeException("2: Wombat not found: "+wname); + } + myc.close(); + myc.connect(); + System.out.println("Found a Wombat in my backyard."); + + final String deepThoughts = "I want to leave this backyard!"; + final WombatMBean w = JMX.newMBeanProxy(cd, wname, WombatMBean.class); + w.setCaption(deepThoughts); + if (!deepThoughts.equals(w.getCaption())) + throw new RuntimeException("4: Wombat is not thinking right: "+ + w.getCaption()); + s.unregisterMBean(myname); + if (my.isConnected()) + throw new Exception(myname+" shouldn't be connected"); + myc.connect(); + myc.close(); + myc.checkNotifs(0,1); + } finally { + myRMI.stop(); + } + + } + + public static void main(String... args) throws Exception { + testConnectClose(); + } +} diff --git a/test/javax/management/namespace/JMXRemoteTargetNamespace.java b/test/javax/management/namespace/JMXRemoteTargetNamespace.java new file mode 100644 index 000000000..3d83844b1 --- /dev/null +++ b/test/javax/management/namespace/JMXRemoteTargetNamespace.java @@ -0,0 +1,222 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + + +import java.io.IOException; +import java.util.Map; +import java.util.logging.Logger; + +import javax.management.InstanceAlreadyExistsException; +import javax.management.MBeanException; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServerConnection; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.event.EventClient; +import javax.management.namespace.JMXNamespace; +import javax.management.namespace.JMXNamespaces; +import javax.management.namespace.JMXRemoteNamespace; +import javax.management.namespace.JMXRemoteNamespaceMBean; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXServiceURL; + +// These options originally in the draft of javax/management/namespaces +// but we decided to retire them - since they could be implemented +// by subclasses. The JMXRemoteTargetNamespace is such a subclass. +// +public class JMXRemoteTargetNamespace extends JMXRemoteNamespace { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(JMXRemoteTargetNamespace.class.getName()); + public static final String CREATE_EVENT_CLIENT = + "jmx.test.create.event.client"; + + private final String sourceNamespace; + private final boolean createEventClient; + + public JMXRemoteTargetNamespace(JMXServiceURL sourceURL, + Map optionsMap) { + this(sourceURL,optionsMap,null); + } + + public JMXRemoteTargetNamespace(JMXServiceURL sourceURL, + Map optionsMap, String sourceNamespace) { + this(sourceURL,optionsMap,sourceNamespace,false); + } + + public JMXRemoteTargetNamespace(JMXServiceURL sourceURL, + Map optionsMap, String sourceNamespace, + boolean createEventClient) { + super(sourceURL,optionsMap); + this.sourceNamespace = sourceNamespace; + this.createEventClient = createEventClient(optionsMap); + } + + private boolean createEventClient(Map options) { + if (options == null) return false; + final Object createValue = options.get(CREATE_EVENT_CLIENT); + if (createValue == null) return false; + if (createValue instanceof Boolean) + return ((Boolean)createValue).booleanValue(); + if (createValue instanceof String) + return Boolean.valueOf((String)createValue); + throw new IllegalArgumentException("Bad type for value of property " + + CREATE_EVENT_CLIENT+": "+createValue.getClass().getName()); + } + + @Override + protected JMXConnector newJMXConnector(JMXServiceURL url, + Map env) throws IOException { + JMXConnector sup = super.newJMXConnector(url, env); + if (sourceNamespace == null || "".equals(sourceNamespace)) + return sup; + if (createEventClient) + sup = EventClient.withEventClient(sup); + return JMXNamespaces.narrowToNamespace(sup, sourceNamespace); + } + + + /** + * Creates a target name space to mirror a remote source name space in + * the target server. + * @param targetServer A connection to the target MBean server in which + * the new name space should be created. + * @param targetPath the name space to create in the target server. Note + * that if the target name space is a path - that is if + * {@code targetPath} contains '//', then the parent name space + * must be pre-existing in the target server. Attempting to create + * {code targetPath="a//b//c"} in {@code targetServer} + * will fail if name space {@code "a//b"} doesn't already exists + * in {@code targetServer}. + * @param sourceURL a JMX service URL that can be used to connect to the + * source MBean server. + * @param options the set of options to use when creating the + * {@link #JMXRemoteNamespace JMXRemoteNamespace} that will + * handle the new name space. + * @return An {@code ObjectInstance} representing the + * {@link JMXRemoteNamespaceMBean} which handles the + * new name space. + * + **/ + public static ObjectInstance createNamespace( + MBeanServerConnection targetServer, + String targetPath, + JMXServiceURL sourceURL, + Map options) + throws IOException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException { + final ObjectName name = + JMXNamespaces.getNamespaceObjectName(targetPath); + return createInstance(targetServer, name, sourceURL, options, null); + } + + /** + * Creates a target name space to mirror a remote source name space in + * the target server. + * @param targetServer A connection to the target MBean server in which + * the new name space should be created. + * @param targetPath the name space to create in the target server. Note + * that if the target name space is a path - that is if + * {@code targetPath} contains '//', then the parent name space + * must be pre-existing in the target server. Attempting to create + * {code targetPath="a//b//c"} in {@code targetServer} + * will fail if name space {@code "a//b"} doesn't already exists + * in {@code targetServer}. + * @param sourceURL a JMX service URL that can be used to connect to the + * source MBean server. + * @param sourcePath the source namespace path insode the source server. + * @param options the set of options to use when creating the + * {@link #JMXRemoteNamespace JMXRemoteNamespace} that will + * handle the new name space. + * @return An {@code ObjectInstance} representing the + * {@link JMXRemoteNamespaceMBean} which handles the + * new name space. + * + **/ + public static ObjectInstance createNamespace( + MBeanServerConnection targetServer, + String targetPath, + JMXServiceURL sourceURL, + Map options, + String sourcePath) + throws IOException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException { + final ObjectName name = + JMXNamespaces.getNamespaceObjectName(targetPath); + return createInstance(targetServer, name, sourceURL, options, sourcePath); + } + + /** + * Creates and registers a {@link JMXRemoteNamespaceMBean} in a target + * server, to mirror a remote source name space. + * + * @param server A connection to the target MBean server in which + * the new name space should be created. + * @param handlerName the name of the JMXRemoteNamespace to create. + * This must be a compliant name space handler name as returned + * by {@link + * JMXNamespaces#getNamespaceObjectName JMXNamespaces.getNamespaceObjectName}. + * @param sourceURL a JMX service URL that can be used to connect to the + * source MBean server. + * @param sourcePath the path inside the source server + * @param options the set of options to use when creating the + * {@link #JMXRemoteNamespace JMXRemoteNamespace} that will + * handle the new name space. + * @return An {@code ObjectInstance} representing the new + * {@link JMXRemoteNamespaceMBean} created. + * @see #createNamespace createNamespace + */ + static ObjectInstance createInstance(MBeanServerConnection server, + ObjectName handlerName, + JMXServiceURL sourceURL, Map options, + String sourcePath) + throws IOException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException { + try { + final String[] signature = { + JMXServiceURL.class.getName(), + Map.class.getName(), + String.class.getName() + }; + final Object[] params = { + sourceURL,options,sourcePath + }; + final ObjectInstance instance = + server.createMBean(JMXRemoteTargetNamespace.class.getName(), + handlerName,params,signature); + return instance; + } catch (NotCompliantMBeanException ex) { + throw new RuntimeException("unexpected exception: " + ex, ex); + } catch (ReflectionException ex) { + throw new RuntimeException("unexpected exception: " + ex, ex); + } + } + +} diff --git a/test/javax/management/namespace/LazyDomainTest.java b/test/javax/management/namespace/LazyDomainTest.java new file mode 100644 index 000000000..83439011e --- /dev/null +++ b/test/javax/management/namespace/LazyDomainTest.java @@ -0,0 +1,789 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * + * @test LazyDomainTest.java + * @summary Basic test for Lazy Domains. + * @author Daniel Fuchs + * @run clean LazyDomainTest Wombat WombatMBean + * @run build LazyDomainTest Wombat WombatMBean + * @run main LazyDomainTest + */ + + +import java.lang.management.ClassLoadingMXBean; +import java.lang.management.ManagementFactory; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.Map; +import java.util.Set; +import javax.management.JMX; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanServer; +import javax.management.MBeanServerBuilder; +import javax.management.MBeanServerDelegate; +import javax.management.MBeanServerFactory; +import javax.management.MBeanServerNotification; +import javax.management.Notification; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.namespace.JMXDomain; +import javax.management.remote.MBeanServerForwarder; + +/** + * Test simple creation/registration of namespace. + * + */ +public class LazyDomainTest { + private static Map emptyEnvMap() { + return Collections.emptyMap(); + } + + + public static interface MBeanServerLoader { + public MBeanServer loadMBeanServer(); + } + + + public static class MBeanServerProxy implements InvocationHandler { + + private final static Map localMap; + static { + localMap = new HashMap(); + for (Method m : MBeanServerForwarder.class.getDeclaredMethods()) { + try { + final Method loc = MBeanServerProxy.class. + getMethod(m.getName(), m.getParameterTypes()); + localMap.put(m, loc); + } catch (Exception x) { + // not defined... + } + } + try { + localMap.put(MBeanServer.class. + getMethod("getMBeanCount", (Class[]) null), + MBeanServerProxy.class. + getMethod("getMBeanCount", (Class[]) null)); + } catch (NoSuchMethodException x) { + // OK. + } + } + + private final MBeanServerLoader loader; + private MBeanServer server; + private final Set domains; + + public MBeanServerProxy(MBeanServerLoader loader) { + if (loader == null) + throw new IllegalArgumentException("null loader"); + this.loader = loader; + this.server = null; + domains = new HashSet(); + } + + + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + if (method.getDeclaringClass().equals(Object.class)) { + return invokeMethod(this,method,args); + } + final Method local = localMap.get(method); + if (local != null) { + return invokeMethod(this,local,args); + } + if (method.getDeclaringClass().equals(MBeanServer.class)) { + return invokeMethod(getMBeanServer(),method,args); + } + throw new NoSuchMethodException(method.getName()); + } + + private Object invokeMethod(Object on, Method method, Object[] args) + throws Throwable { + try { + return method.invoke(on, args); + } catch (InvocationTargetException ex) { + throw ex.getTargetException(); + } + } + + public synchronized MBeanServer getMBeanServer() { + if (server == null) setMBeanServer(loader.loadMBeanServer()); + return server; + } + + public synchronized void setMBeanServer(MBeanServer mbs) { + this.server = mbs; + if (mbs != null) { + for (LazyDomain dom : domains) dom.loaded(); + domains.clear(); + } + } + + public synchronized boolean isLoaded() { + return server != null; + } + + public synchronized void add(LazyDomain dom) { + if (isLoaded()) dom.loaded(); + else domains.add(dom); + } + + public synchronized boolean remove(LazyDomain dom) { + return domains.remove(dom); + } + + public Integer getMBeanCount() { + if (isLoaded()) return server.getMBeanCount(); + else return Integer.valueOf(0); + } + } + + public static class LazyDomain extends JMXDomain { + public static MBeanServer makeProxyFor(MBeanServerProxy proxy) { + return (MBeanServer) + Proxy.newProxyInstance(LazyDomain.class.getClassLoader(), + new Class[] {MBeanServer.class, MBeanServerForwarder.class}, + proxy); + } + + private final MBeanServerProxy proxy; + private volatile NotificationListener listener; + private volatile NotificationFilter filter; + + public LazyDomain(MBeanServerProxy proxy) { + super(makeProxyFor(proxy)); + this.proxy = proxy; + } + + @Override + public Integer getMBeanCount() { + if (proxy.isLoaded()) + return super.getMBeanCount(); + return 0; + } + + + @Override + public synchronized void addMBeanServerNotificationListener( + NotificationListener listener, + NotificationFilter filter) { + if (proxy.isLoaded()) { + super.addMBeanServerNotificationListener(listener, filter); + } else { + this.listener = listener; + this.filter = filter; + proxy.add(this); + } + } + + @Override + public synchronized void removeMBeanServerNotificationListener( + NotificationListener listener) + throws ListenerNotFoundException { + if (this.listener != listener) + throw new ListenerNotFoundException(); + this.listener = null; + this.filter = null; + if (proxy.isLoaded()) + super.removeMBeanServerNotificationListener(listener); + proxy.remove(this); + } + + public synchronized void loaded() { + if (listener != null) + addMBeanServerNotificationListener(listener, filter); + } + + } + + /** + * This is a use case for e.g GlassFish: the LazyStarterDomain MBean + * is a place holder that will unregister itself and autoload a set + * of MBeans in place of its own domain when that domain is + * accessed. + * This is an abstract class, where the only abstract method + * is loadMBeans(MBeanServer). + * Subclasses should implement that method to register whatever MBeans + * in the domain previously held by that LazyStarterDomain object. + * In other words: the LazyStarterDomain MBean is 'replaced' by the + * MBeans loaded by loadMBeans(); + */ + public static abstract class LazyStarterDomain extends LazyDomain { + + /** + * This is a loader that will unregister the JMXDomain that + * created it, and register a bunch of MBeans in its place + * by calling LazyStarterDomain.loadMBeans + * + * That one gave me "la migraine". + */ + private static class HalfGrainLoader implements MBeanServerLoader { + private volatile LazyStarterDomain domain; + public MBeanServer loadMBeanServer() { + if (domain == null) + throw new IllegalStateException( + "JMXDomain MBean not registered!"); + final MBeanServer server = domain.getMBeanServer(); + final ObjectName domainName = domain.getObjectName(); + try { + server.unregisterMBean(domainName); + } catch (Exception x) { + throw new IllegalStateException("Can't unregister " + + "JMXDomain: "+x,x); + } + domain.loadMBeans(server,domainName.getDomain()); + return server; + } + public void setDomain(LazyStarterDomain domain) { + this.domain = domain; + } + } + + /** + * This is an MBeanServerProxy which create a loader for the + * LazyStarterDomain MBean. + */ + private static class DomainStarter extends MBeanServerProxy { + + public DomainStarter() { + this(new HalfGrainLoader()); + } + + private final HalfGrainLoader loader; + private DomainStarter(HalfGrainLoader loader) { + super(loader); + this.loader = loader; + } + + public void setDomain(LazyStarterDomain domain) { + loader.setDomain(domain); + } + } + + /** + * A new LazyStarterDomain. When the domain monitored by this + * MBean is accessed, this MBean will unregister itself and call + * the abstract loadMBeans(MBeanServer) method. + * Subclasses need only to implement loadMBeans(). + */ + public LazyStarterDomain() { + this(new DomainStarter()); + } + + private LazyStarterDomain(DomainStarter starter) { + super(starter); + starter.setDomain(this); + } + + // Contrarily to its LazyDomain superclass, this LazyDomain + // doesn't wrapp another MBeanServer: it simply registers a bunch + // of MBeans in its own MBeanServer. + // Thus, there's no notifications to forward. + // + @Override + public void addMBeanServerNotificationListener( + NotificationListener listener, NotificationFilter filter) { + // nothing to do. + } + + // Contrarily to its LazyDomain superclass, this LazyDomain + // doesn't wrapp another MBeanServer: it simply registers a bunch + // of MBeans in its own MBeanServer. + // Thus, there's no notifications to forward. + // + @Override + public void removeMBeanServerNotificationListener( + NotificationListener listener) throws ListenerNotFoundException { + // nothing to do + } + + // If this domain is registered, it contains no MBean. + // If it is not registered, then it no longer contain any MBean. + // The MBeanCount is thus always 0. + @Override + public Integer getMBeanCount() { + return 0; + } + + /** + * Called when the domain is first accessed. + * {@code server} is the server in which this MBean was registered. + * A subclass must override this method in order to register + * the MBeans that should be contained in domain. + * + * @param server the server in which to load the MBeans. + * @param domain the domain in which the MBeans should be registered. + */ + protected abstract void loadMBeans(MBeanServer server, String domain); + + + } + + private static MBeanServerNotification pop( + BlockingQueue queue, + String type, + ObjectName mbean, + String test) + throws InterruptedException { + final Notification n = queue.poll(1, TimeUnit.SECONDS); + if (!(n instanceof MBeanServerNotification)) + fail(test+"expected MBeanServerNotification, got "+n); + final MBeanServerNotification msn = (MBeanServerNotification)n; + if (!type.equals(msn.getType())) + fail(test+"expected "+type+", got "+msn.getType()); + if (!mbean.apply(msn.getMBeanName())) + fail(test+"expected "+mbean+", got "+msn.getMBeanName()); + System.out.println(test+" got: "+msn); + return msn; + } + private static MBeanServerNotification popADD( + BlockingQueue queue, + ObjectName mbean, + String test) + throws InterruptedException { + return pop(queue, MBeanServerNotification.REGISTRATION_NOTIFICATION, + mbean, test); + } + + private static MBeanServerNotification popREM( + BlockingQueue queue, + ObjectName mbean, + String test) + throws InterruptedException { + return pop(queue, MBeanServerNotification.UNREGISTRATION_NOTIFICATION, + mbean, test); + } + + + private static void fail(String msg) { + raise(new RuntimeException(msg)); + } + + private static void fail(String msg, Throwable cause) { + raise(new RuntimeException(msg,cause)); + } + + private static void raise(RuntimeException x) { + lastException = x; + exceptionCount++; + throw x; + } + + private static volatile Exception lastException = null; + private static volatile int exceptionCount = 0; + + // ZZZ need to add a test case with several LazyDomains, and + // need to test that nothing is loaded until the lazy domains + // are accessed... + // + + private static void registerWombats(MBeanServer server, String domain, + int count) { + try { + for (int i=0;i queue = + new ArrayBlockingQueue(100); + + // A listener that puts notifs in the queue. + final NotificationListener l = new NotificationListener() { + + public void handleNotification(Notification notification, + Object handback) { + try { + if (!queue.offer(notification, 5, TimeUnit.SECONDS)) { + throw new RuntimeException("timeout exceeded"); + } + } catch (Exception x) { + fail(test + "failed to handle notif", x); + } + } + }; + + // Create a LazyDomain for each of the platform domain. + // All platform domain share the same MBeanServer proxy, which means + // that loading one domain will also load all the others. + // + Map domainsMap = new HashMap(); + for (String dom : platformDomains) { + domainsMap.put(dom, new LazyDomain(platform)); + } + domainsMap.put("custom.awomb", new LazyDomain(customa)); + domainsMap.put("custom.bwomb", new LazyDomain(customb)); + + for (Map.Entry e : domainsMap.entrySet()) { + server.registerMBean(e.getValue(), + JMXDomain.getDomainObjectName(e.getKey())); + } + + // check that lazy MBeans are not there... + checkSize(test,server,domainsMap.size()+1); + + System.out.println(test+" registering listener with delegate."); + server.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, l, + null, null); + + // check that lazy MBeans are not there... + checkSize(test,server,domainsMap.size()+1); + + // force loading of custom.awomb. + final ObjectName awombat = new ObjectName( + "custom.awomb:type=Wombat,name=wombat#"+customCount/2); + if (!server.isRegistered(awombat)) + fail(test+"Expected "+awombat+" to be reggistered!"); + + final int oldCount = domainsMap.size()+1+customCount; + checkSize(test,server,oldCount); + + if (queue.peek() != null) + fail(test+"Received unexpected notifications: "+queue); + + + System.out.println(test+"creating a proxy for ClassLoadingMXBean."); + final ClassLoadingMXBean cl = + JMX.newMXBeanProxy(server, + new ObjectName(ManagementFactory.CLASS_LOADING_MXBEAN_NAME), + ClassLoadingMXBean.class); + + checkSize(test,server,oldCount); + + System.out.println(test+"Loaded classes: "+cl.getLoadedClassCount()); + + final int newCount = server.getMBeanCount(); + if (newCount < oldCount+6) + fail(test+"Expected at least "+(oldCount+6)+ + " MBeans. Found "+newCount); + + final ObjectName jwombat = new ObjectName("java.lang:type=Wombat"); + server.createMBean("Wombat", jwombat); + System.out.println(test+"Created "+jwombat); + checkSize(test,server,newCount+1); + + popADD(queue, jwombat, test); + if (queue.peek() != null) + fail(test+"Received unexpected notifications: "+queue); + + + int platcount = 0; + for (String dom : platformDomains) { + final Set found = + server.queryNames(new ObjectName(dom+":*"),null); + final int jcount = found.size(); + System.out.println(test+"Found "+jcount+" MBeans in "+dom+ + ": "+found); + checkSize(test,server,newCount+1); + platcount += (jcount-1); + } + checkSize(test,server,oldCount+platcount); + + final ObjectName owombat = new ObjectName("custom:type=Wombat"); + server.createMBean("Wombat", owombat); + System.out.println(test+"Created "+owombat); + checkSize(test,server,newCount+2); + popADD(queue, owombat, test); + if (queue.peek() != null) + fail(test+"Received unexpected notifications: "+queue); + + final Set jwombatView = (Set) + server.invoke(jwombat, "listMatching", new Object[] {null}, + new String[] {ObjectName.class.getName()}); + System.out.println(test+jwombat+" sees: "+jwombatView); + checkSize(test, server, newCount+2); + if (jwombatView.size() != (platcount+1)) + fail(test+jwombat+" sees "+jwombatView.size()+" MBeans - should" + + " have seen "+(platcount+1)); + + final Set platformMBeans = + ManagementFactory.getPlatformMBeanServer(). + queryNames(null, null); + if (!platformMBeans.equals(jwombatView)) + fail(test+jwombat+" should have seen "+platformMBeans); + + // check that awombat triggers loading of bwombats + final Set awombatView = (Set) + server.invoke(awombat, "listMatching", new Object[] {null}, + new String[] {ObjectName.class.getName()}); + System.out.println(test+awombat+" sees: "+awombatView); + final int totalCount = newCount+2+customCount; + checkSize(test, server, totalCount); + if (awombatView.size() != totalCount) + fail(test+jwombat+" sees "+jwombatView.size()+" MBeans - should" + + " have seen "+totalCount); + + final Set allMBeans = server. + queryNames(null, null); + if (!allMBeans.equals(awombatView)) + fail(test+awombat+" should have seen "+allMBeans); + + System.out.println(test + " PASSED"); + + } + + + public static void lazyStarterTest() throws Exception { + final String test = "lazyStarterTest: "; + System.out.println("" + + "\nThis test checks that it is possible to perform lazy loading" + + "\nof MBeans in a given domain by using a transient JMXDomain" + + "\nsubclass for that domain. "); + + System.out.println(test + " START"); + + // The "global" MBeanServer... + final MBeanServer platform = + ManagementFactory.getPlatformMBeanServer(); + + // A notification queue. + final BlockingQueue queue = + new ArrayBlockingQueue(100); + + // A listener that puts notifs in the queue. + final NotificationListener l = new NotificationListener() { + + public void handleNotification(Notification notification, + Object handback) { + try { + if (!queue.offer(notification, 5, TimeUnit.SECONDS)) { + throw new RuntimeException("timeout exceeded"); + } + } catch (Exception x) { + fail(test + "failed to handle notif", x); + } + } + }; + + System.out.println(test+" registering listener with delegate."); + platform.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, l, + null, null); + + final String ld1 = "lazy1"; + final String ld2 = "lazy2"; + final int wCount = 5; + final LazyStarterDomain lazy1 = new LazyStarterDomain() { + @Override + protected void loadMBeans(MBeanServer server, String domain) { + registerWombats(server, ld1, wCount); + } + }; + final LazyStarterDomain lazy2 = new LazyStarterDomain() { + @Override + protected void loadMBeans(MBeanServer server, String domain) { + registerWombats(server, ld2, wCount); + } + }; + final ObjectName lo1 = JMXDomain.getDomainObjectName(ld1); + final ObjectName lo2 = JMXDomain.getDomainObjectName(ld2); + + final int initial = platform.getMBeanCount(); + + platform.registerMBean(lazy1, lo1); + System.out.println(test+"registered "+lo1); + checkSize(test, platform, initial+1); + popADD(queue, lo1, test); + + platform.registerMBean(lazy2, lo2); + System.out.println(test+"registered "+lo2); + checkSize(test, platform, initial+2); + popADD(queue, lo2, test); + + + final ObjectName awombat = new ObjectName( + ld1+":type=Wombat,name=wombat#"+wCount/2); + if (!platform.isRegistered(awombat)) + fail(test+"Expected "+awombat+" to be reggistered!"); + checkSize(test,platform,initial+wCount+1); + popREM(queue, lo1, test); + final ObjectName pat1 = + new ObjectName(ld1+":type=Wombat,name=wombat#*"); + for (int i=0;i all = platform.queryNames(null, null); + popREM(queue, lo2, test); + System.out.println(test+"Now found: "+all); + checkSize(test,platform,initial+wCount+wCount); + final ObjectName pat2 = + new ObjectName(ld2+":type=Wombat,name=wombat#*"); + for (int i=0;i testConcurrent = + new HashMap(); + for (int i=0;i<(100/wCount);i++) { + final String ld = "concurrent.lazy"+i; + final LazyStarterDomain lazy = new LazyStarterDomain() { + @Override + protected void loadMBeans(MBeanServer server, String domain) { + registerWombats(server, ld, wCount-1); + } + }; + testConcurrent.put(ld, lazy); + final ObjectName lo = JMXDomain.getDomainObjectName(ld); + platform.registerMBean(lazy, lo); + popADD(queue, lo, test); + } + + System.out.println(test+"Big autoload: "+ + platform.queryNames(null,null)); + System.out.println(test+"Big after load: "+ + platform.queryNames(null,null)); + if (!platform.queryNames(JMXDomain.getDomainObjectName("*"), null). + isEmpty()) { + fail(test+" some domains are still here: "+ + platform.queryNames( + JMXDomain.getDomainObjectName("*"), null)); + } + queue.clear(); + System.out.println(test+"PASSED: The DomainDispatcher appears to be " + + "resilient to concurrent modifications."); + } + + public static void main(String... args) throws Exception { + + lazyTest(); + lazyStarterTest(); + + if (lastException != null) + throw lastException; + } +} diff --git a/test/javax/management/namespace/MXBeanRefTest.java b/test/javax/management/namespace/MXBeanRefTest.java new file mode 100644 index 000000000..afdc0ae97 --- /dev/null +++ b/test/javax/management/namespace/MXBeanRefTest.java @@ -0,0 +1,181 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * @test MXBeanRefTest.java + * @bug 5072476 + * @summary Test that MXBean proxy references work correctly in the presence + * of namespaces. + * @author Eamonn Mcmanus + */ + +/** + * The idea is that we will create a hierarchy like this: + * a// + * X + * b// + * Y + * Z + * and we will use MXBean references so we have links like this: + * a// + * X----+ + * b// | + * / + * Y + * \ + * / + * Z + * In other words, X.getY() will return a proxy for Y, which the MXBean + * framework will map to b//Y. A proxy for a//X should then map this + * into a proxy for a//b//Y. That's easy. But then if we call getZ() + * on this proxy, the MXBean framework will return just Z, and the proxy + * must map that into a proxy for a//b//Z. + */ + +import java.lang.management.ManagementFactory; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.lang.reflect.UndeclaredThrowableException; +import javax.management.JMX; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.MBeanServerInvocationHandler; +import javax.management.ObjectName; +import javax.management.namespace.JMXNamespace; +import javax.management.namespace.JMXNamespaces; +import javax.management.openmbean.OpenDataException; + +public class MXBeanRefTest { + + public static interface ZMXBean { + public void success(); + } + public static class ZImpl implements ZMXBean { + public void success() {} + } + + public static interface YMXBean { + public ZMXBean getZ(); + public void setZ(ZMXBean z); + } + public static class YImpl implements YMXBean { + private ZMXBean z; + + public YImpl(ZMXBean z) { + this.z = z; + } + + public ZMXBean getZ() { + return z; + } + + public void setZ(ZMXBean z) { + this.z = z; + } + } + + public static interface XMXBean { + public YMXBean getY(); + } + public static class XImpl implements XMXBean { + private final YMXBean yProxy; + + public XImpl(YMXBean yProxy) { + this.yProxy = yProxy; + } + + public YMXBean getY() { + return yProxy; + } + } + + public static void main(String[] args) throws Exception { + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + // Set up namespace hierarchy a//b// + MBeanServer ambs = MBeanServerFactory.newMBeanServer(); + MBeanServer bmbs = MBeanServerFactory.newMBeanServer(); + JMXNamespace bHandler = new JMXNamespace(bmbs); + ObjectName bHandlerName = JMXNamespaces.getNamespaceObjectName("b"); + System.out.println(bHandlerName); + ambs.registerMBean(bHandler, bHandlerName); + JMXNamespace aHandler = new JMXNamespace(ambs); + ObjectName aHandlerName = JMXNamespaces.getNamespaceObjectName("a"); + mbs.registerMBean(aHandler, aHandlerName); + + ZMXBean z = new ZImpl(); + ObjectName zName = new ObjectName("foo:type=Z"); + bmbs.registerMBean(z, zName); + + YMXBean y = new YImpl(z); + ObjectName yName = new ObjectName("foo:type=Y"); + bmbs.registerMBean(y, yName); + + ObjectName yNameInA = new ObjectName("b//" + yName); + System.out.println("MBeanInfo for Y as seen from a//:"); + System.out.println(ambs.getMBeanInfo(yNameInA)); + YMXBean yProxyInA = JMX.newMXBeanProxy(ambs, yNameInA, YMXBean.class); + XMXBean x = new XImpl(yProxyInA); + ObjectName xName = new ObjectName("foo:type=X"); + ambs.registerMBean(x, xName); + + ObjectName xNameFromTop = new ObjectName("a//" + xName); + XMXBean xProxy = JMX.newMXBeanProxy(mbs, xNameFromTop, XMXBean.class); + System.out.println("Name of X Proxy: " + proxyName(xProxy)); + YMXBean yProxy = xProxy.getY(); + System.out.println("Name of Y Proxy: " + proxyName(yProxy)); + ZMXBean zProxy = yProxy.getZ(); + System.out.println("Name of Z Proxy: " + proxyName(zProxy)); + + System.out.println("Operation through Z proxy..."); + zProxy.success(); + + System.out.println("Changing Y's ref to Z..."); + yProxy.setZ(zProxy); + zProxy = yProxy.getZ(); + System.out.println("Name of Z Proxy now: " + proxyName(zProxy)); + System.out.println("Operation through Z proxy again..."); + zProxy.success(); + + System.out.println("Changing Y's ref to a bogus one..."); + ZMXBean zProxyBad = JMX.newMXBeanProxy(mbs, zName, ZMXBean.class); + try { + yProxy.setZ(zProxyBad); + } catch (UndeclaredThrowableException e) { + Throwable cause = e.getCause(); + if (cause instanceof OpenDataException) { + System.out.println("...correctly got UndeclaredThrowableException"); + System.out.println("...wrapping: " + cause); + } else + throw new Exception("FAILED: wrong exception: " + cause); + } + + System.out.println("Test passed"); + } + + private static ObjectName proxyName(Object proxy) { + InvocationHandler ih = Proxy.getInvocationHandler(proxy); + MBeanServerInvocationHandler mbsih = (MBeanServerInvocationHandler) ih; + return mbsih.getObjectName(); + } +} diff --git a/test/javax/management/namespace/NamespaceController.java b/test/javax/management/namespace/NamespaceController.java new file mode 100644 index 000000000..f5a1c42e2 --- /dev/null +++ b/test/javax/management/namespace/NamespaceController.java @@ -0,0 +1,405 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import com.sun.jmx.namespace.ObjectNameRouter; +import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.JMX; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanRegistration; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.namespace.JMXNamespace; +import javax.management.namespace.JMXNamespaces; +import javax.management.namespace.JMXRemoteNamespaceMBean; +import javax.management.remote.JMXServiceURL; + +/** + * The {@code NamespaceController} MBean makes it possible to easily + * create mount points ({@linkplain JMXNamespace JMXNamespaces}) in an + * {@code MBeanServer}. + * There is at most one instance of NamespaceController in an + * MBeanServer - which can be created using the {@link #createInstance + * createInstance} method. The {@code NamespaceController} MBean will + * make it possible to remotely create name spaces by mounting remote + * MBeanServers into the MBeanServer in which it was registered. + */ +// This API was originally in the draft of javax/management/namespaces +// but we decided to retire it. Rather than removing all the associated +// tests I have moved the API to the test hierarchy - so it is now used as +// an additional (though somewhat complex) test case... +// +public class NamespaceController implements NamespaceControllerMBean, + NotificationEmitter, MBeanRegistration { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(NamespaceController.class.getName()); + + private static long seqNumber=0; + + private final NotificationBroadcasterSupport broadcaster = + new NotificationBroadcasterSupport(); + + private volatile MBeanServer mbeanServer = null; + + private volatile ObjectName objectName = null; + + //was: NamespaceController.class.getPackage().getName() + public static final String NAMESPACE_CONTROLLER_DOMAIN = "jmx.ns"; + + /** + * Creates a new NamespaceController. + * Using {@link #createInstance} should be preferred. + **/ + public NamespaceController() { + this(null); + } + + public NamespaceController(MBeanServer mbeanServer) { + this.mbeanServer = mbeanServer; + } + + /* + * MBeanNotification support + * You shouldn't update these methods + */ + public final void addNotificationListener(NotificationListener listener, + NotificationFilter filter, Object handback) { + broadcaster.addNotificationListener(listener, filter, handback); + } + + public MBeanNotificationInfo[] getNotificationInfo() { + return new MBeanNotificationInfo[] { + }; + } + + public final void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException { + broadcaster.removeNotificationListener(listener); + } + + public final void removeNotificationListener(NotificationListener listener, + NotificationFilter filter, Object handback) + throws ListenerNotFoundException { + broadcaster.removeNotificationListener(listener, filter, handback); + } + + public static synchronized long getNextSeqNumber() { + return seqNumber++; + } + + protected final void sendNotification(Notification n) { + if (n.getSequenceNumber()<=0) + n.setSequenceNumber(getNextSeqNumber()); + if (n.getSource()==null) + n.setSource(objectName); + broadcaster.sendNotification(n); + } + + /** + * The ObjectName with which this MBean was registered. + *

      Unless changed by subclasses, this is + * {@code + * "javax.management.namespace:type="+this.getClass().getSimpleName()}. + * @return this MBean's ObjectName, or null if this MBean was never + * registered. + **/ + public final ObjectName getObjectName() { + return objectName; + } + + /** + * The MBeanServer served by this NamespaceController. + * @return the MBeanServer served by this NamespaceController. + **/ + public final MBeanServer getMBeanServer() { + return mbeanServer; + } + + /** + * Allows the MBean to perform any operations it needs before being + * registered in the MBean server. If the name of the MBean is not + * specified, the MBean can provide a name for its registration. If + * any exception is raised, the MBean will not be registered in the + * MBean server. Subclasses which override {@code preRegister} + * must call {@code super.preRegister(name,server)}; + * @param server The MBean server in which the MBean will be registered. + * @param name The object name of the MBean. + * The name must be either {@code null} - or equal to that + * described by {@link #getObjectName}. + * @return The name under which the MBean is to be registered. + * This will be the name described by {@link #getObjectName}. + * @throws MalformedObjectNameException if the supplied name does not + * meet expected requirements. + */ + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws MalformedObjectNameException { + objectName = name; + final ObjectName single = + ObjectName.getInstance(NAMESPACE_CONTROLLER_DOMAIN+ + ":type="+this.getClass().getSimpleName()); + if (name!=null && !single.equals(name)) + throw new MalformedObjectNameException(name.toString()); + if (mbeanServer == null) mbeanServer = server; + return single; + } + + /** + * Allows the MBean to perform any operations needed after having + * been registered in the MBean server or after the registration has + * failed. + * @param registrationDone Indicates whether or not the MBean has been + * successfully registered in the MBean server. The value false means + * that the registration has failed. + */ + public void postRegister(Boolean registrationDone) { + //TODO postRegister implementation; + } + + /** + * Allows the MBean to perform any operations it needs before being + * unregistered by the MBean server. + * @throws Exception This exception will be caught by the MBean server and + * re-thrown as an MBeanRegistrationException. + */ + public void preDeregister() throws Exception { + //TODO preDeregister implementation; + } + + /** + * Allows the MBean to perform any operations needed after having been + * unregistered in the MBean server. + */ + public void postDeregister() { + //TODO postDeregister implementation; + } + + public String mount(JMXServiceURL url, + String targetPath, + Map optionsMap) + throws IOException { + return mount(url, targetPath, "", optionsMap); + } + + // see NamespaceControllerMBean + public String mount(JMXServiceURL url, + String targetPath, + String sourcePath, + Map optionsMap) + throws IOException { + + // TODO: handle description. + final String dirName = + JMXNamespaces.normalizeNamespaceName(targetPath); + + try { + final ObjectInstance moi = + JMXRemoteTargetNamespace.createNamespace(mbeanServer, + dirName,url,optionsMap, + JMXNamespaces.normalizeNamespaceName(sourcePath) + ); + final ObjectName nsMBean = moi.getObjectName(); + try { + mbeanServer.invoke(nsMBean, "connect", null,null); + } catch (Throwable t) { + mbeanServer.unregisterMBean(nsMBean); + throw t; + } + return getMountPointID(nsMBean); + } catch (InstanceAlreadyExistsException x) { + throw new IllegalArgumentException(targetPath,x); + } catch (IOException x) { + throw x; + } catch (Throwable x) { + if (x instanceof Error) throw (Error)x; + Throwable cause = x.getCause(); + if (cause instanceof IOException) + throw ((IOException)cause); + if (cause == null) cause = x; + + final IOException io = + new IOException("connect failed: "+cause); + io.initCause(cause); + throw io; + } + } + + private String getMountPointID(ObjectName dirName) { + return dirName.toString(); + } + + private ObjectName getHandlerName(String mountPointID) { + try { + final ObjectName tryit = ObjectName.getInstance(mountPointID); + final ObjectName formatted = + JMXNamespaces.getNamespaceObjectName(tryit.getDomain()); + if (!formatted.equals(tryit)) + throw new IllegalArgumentException(mountPointID+ + ": invalid mountPointID"); + return formatted; + } catch (MalformedObjectNameException x) { + throw new IllegalArgumentException(mountPointID,x); + } + } + + public boolean unmount(String mountPointID) + throws IOException { + final ObjectName dirName = getHandlerName(mountPointID); + if (!mbeanServer.isRegistered(dirName)) + throw new IllegalArgumentException(mountPointID+ + ": no such name space"); + final JMXRemoteNamespaceMBean mbean = + JMX.newMBeanProxy(mbeanServer,dirName, + JMXRemoteNamespaceMBean.class); + try { + mbean.close(); + } catch (IOException io) { + LOG.fine("Failed to close properly - ignoring exception: "+io); + LOG.log(Level.FINEST, + "Failed to close properly - ignoring exception",io); + } finally { + try { + mbeanServer.unregisterMBean(dirName); + } catch (InstanceNotFoundException x) { + throw new IllegalArgumentException(mountPointID+ + ": no such name space", x); + } catch (MBeanRegistrationException x) { + final IOException io = + new IOException(mountPointID +": failed to unmount"); + io.initCause(x); + throw io; + } + } + return true; + } + + public boolean ismounted(String targetPath) { + return mbeanServer.isRegistered(JMXNamespaces.getNamespaceObjectName(targetPath)); + } + + public ObjectName getHandlerNameFor(String targetPath) { + return JMXNamespaces.getNamespaceObjectName(targetPath); + } + + public String[] findNamespaces() { + return findNamespaces(null,null,0); + } + + + private ObjectName getDirPattern(String from) { + try { + if (from == null) + return ObjectName.getInstance(ALL_NAMESPACES); + final String namespace = + ObjectNameRouter.normalizeNamespacePath(from,false,true,false); + if (namespace.equals("")) + return ObjectName.getInstance(ALL_NAMESPACES); + if (JMXNamespaces.getNamespaceObjectName(namespace).isDomainPattern()) + throw new IllegalArgumentException(from); + return ObjectName.getInstance(namespace+NAMESPACE_SEPARATOR+ALL_NAMESPACES); + } catch (MalformedObjectNameException x) { + throw new IllegalArgumentException(from,x); + } + } + + public String[] findNamespaces(String from, String regex, int depth) { + if (depth < 0) return new String[0]; + final Set res = new TreeSet(); + final ObjectName all = getDirPattern(from); + Set names = mbeanServer.queryNames(all,null); + for (ObjectName dirName : names) { + final String dir = dirName.getDomain(); + if (regex == null || dir.matches(regex)) + res.add(dir); + if (depth > 0) + res.addAll(Arrays.asList(findNamespaces(dir,regex,depth-1))); + } + return res.toArray(new String[res.size()]); + } + + /** + * Creates a {@link NamespaceController} MBean in the provided + * {@link MBeanServerConnection}. + *

      The name of the MBean is that returned by {@link #preRegister} + * as described by {@link #getObjectName}. + * @throws IOException if an {@code IOException} is raised when invoking + * the provided connection. + * @throws InstanceAlreadyExistsException if an MBean was already + * registered with the NamespaceController's name. + * @throws MBeanRegistrationException if thrown by {@link + * MBeanServerConnection#createMBean(java.lang.String,javax.management.ObjectName) + * server.createMBean} + * @throws MBeanException if thrown by {@link + * MBeanServerConnection#createMBean(java.lang.String,javax.management.ObjectName) + * server.createMBean} + * @return the {@link ObjectInstance}, as returned by {@link + * MBeanServerConnection#createMBean(java.lang.String,javax.management.ObjectName) + * server.createMBean} + **/ + public static ObjectInstance createInstance(MBeanServerConnection server) + throws IOException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException { + try { + final ObjectInstance instance = + server.createMBean(NamespaceController.class.getName(), null); + return instance; + } catch (NotCompliantMBeanException ex) { + throw new RuntimeException("unexpected exception: " + ex, ex); + } catch (ReflectionException ex) { + throw new RuntimeException("unexpected exception: " + ex, ex); + } + } + + private final static String ALL_NAMESPACES= + "*"+NAMESPACE_SEPARATOR+":"+ + JMXNamespace.TYPE_ASSIGNMENT; + +} diff --git a/test/javax/management/namespace/NamespaceControllerMBean.java b/test/javax/management/namespace/NamespaceControllerMBean.java new file mode 100644 index 000000000..b7bc3457e --- /dev/null +++ b/test/javax/management/namespace/NamespaceControllerMBean.java @@ -0,0 +1,143 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.io.IOException; +import java.util.Map; + +import javax.management.ObjectName; +import javax.management.remote.JMXServiceURL; + +/** + * The {@link NamespaceController} MBean makes it possible to easily + * create mount points ({@link JMXNamespace JMXNamespaces}) in an + * {@code MBeanServer}. + */ +// This API was originally in the draft of javax/management/namespaces +// but we decided to retire it. Rather than removing all the associated +// tests I have moved the API to the test hierarchy - so it is now used as +// an additional (though somewhat complex) test case... +// +public interface NamespaceControllerMBean { + /** + * Mount MBeans from the source path of the source URL into the specified + * target path of the target. + * @param url URL of the mounted source. + * @param targetPath Target path in which MBeans will be mounted. + * @param optionsMap connection map and options. See {@link + * javax.management.namespace.JMXRemoteNamespace.Options + * JMXRemoteNamespace.Options} + * @throws IOException Connection with the source failed + * @throws IllegalArgumentException Supplied parameters are + * illegal, or combination of supplied parameters is illegal. + * @return A mount point id. + */ + public String mount(JMXServiceURL url, + String targetPath, + Map optionsMap) + throws IOException, IllegalArgumentException; + + /** + * Mount MBeans from the source path of the source URL into the specified + * target path of the target. + * @param url URL of the mounted source. + * @param targetPath Target path in which MBeans will be mounted. + * @param sourcePath source namespace path. + * @param optionsMap connection map and options. See {@link + * javax.management.namespace.JMXRemoteNamespace.Options + * JMXRemoteNamespace.Options} + * @throws IOException Connection with the source failed + * @throws IllegalArgumentException Supplied parameters are + * illegal, or combination of supplied parameters is illegal. + * @return A mount point id. + */ + public String mount(JMXServiceURL url, + String targetPath, + String sourcePath, + Map optionsMap) + throws IOException, IllegalArgumentException; + + /** + * Unmount a previously mounted mount point. + * @param mountPointId A mount point id, as previously returned + * by mount. + * @throws IllegalArgumentException Supplied parameters are + * illegal, or combination of supplied parameters is illegal. + * @throws IOException thrown if the mount point {@link JMXNamespace} + * couldn't be unregistered. + */ + public boolean unmount(String mountPointId) + throws IOException, IllegalArgumentException; + + /** + * Tells whether there already exists a {@link JMXNamespace} for + * the given targetPath. + * @param targetPath a target name space path. + * @return true if a {@link JMXNamespace} is registered for that + * name space path. + **/ + public boolean ismounted(String targetPath); + + /** + * Returns the handler name for the provided target name space + * path. Can throw IllegalArgumentException if the provided + * targetPath contains invalid characters (like e.g. ':'). + * @param targetPath A target name space path. + * @return the handler name for the provided target name space + * path. + **/ + public ObjectName getHandlerNameFor(String targetPath); + + /** + * Return a sorted array of locally mounted name spaces. + * This is equivalent to calling {@link + * #findNamespaces(java.lang.String,java.lang.String,int) + * findNamespaces(null,null,0)}; + * @return a sorted array of locally mounted name spaces. + **/ + public String[] findNamespaces(); + + /** + * Return a sorted array of mounted name spaces, starting at + * from (if non null), and recursively searching up to + * provided depth. + * @param from A name spaces from which to start the search. If null, + * will start searching from the MBeanServer root. + * If not null, all returned names will start with from//. + * @param regex A regular expression that the returned names must match. + * If null - no matching is performed and all found names are + * returned. If not null, then all returned names satisfy + * {@link String#matches name.matches(regex)}; + * @param depth the maximum number of levels that the search algorithm + * will cross. 0 includes only top level name spaces, 1 top level + * and first level children etc... depth is evaluated + * with regard to where the search starts - if a non null + * from parameter is provided - then {@code depth=0} + * corresponds to all name spaces found right below + * from//. + * @return A sorted array of name spaces matching the provided criteria. + * All returned names end with "//". + **/ + public String[] findNamespaces(String from, String regex, int depth); +} diff --git a/test/javax/management/namespace/NamespaceCreationTest.java b/test/javax/management/namespace/NamespaceCreationTest.java new file mode 100644 index 000000000..981cdda42 --- /dev/null +++ b/test/javax/management/namespace/NamespaceCreationTest.java @@ -0,0 +1,262 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * + * @test NamespaceCreationTest.java + * @summary General JMXNamespace test. + * @author Daniel Fuchs + * @run clean NamespaceCreationTest Wombat WombatMBean + * @run build NamespaceCreationTest Wombat WombatMBean + * @run main NamespaceCreationTest + */ + + +import java.util.Collections; +import java.util.Map; +import javax.management.MBeanRegistration; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.RuntimeMBeanException; +import javax.management.namespace.JMXNamespace; +import javax.management.namespace.JMXNamespaces; + +/** + * Test simple creation/registration of namespace. + * + */ +public class NamespaceCreationTest { + private static Map emptyEnvMap() { + return Collections.emptyMap(); + } + + + public static class LocalNamespace extends JMXNamespace { + + public LocalNamespace() { + super(MBeanServerFactory.newMBeanServer()); + } + + } + + private static MBeanServer newMBeanServer() { + return MBeanServerFactory.newMBeanServer(); + } + + public static interface ThingMBean {} + public static class Thing implements ThingMBean, MBeanRegistration { + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + if (name == null) return new ObjectName(":type=Thing"); + else return name; + } + public void postRegister(Boolean registrationDone) { + } + + public void preDeregister() throws Exception { + } + public void postDeregister() { + } + } + + /** + * Test that it is possible to create a dummy MBean with a null + * ObjectName - this is just a sanity check - as there are already + * other JMX tests that check that. + * + * @throws java.lang.Exception + */ + public static void testCreateWithNull() throws Exception { + final MBeanServer server = newMBeanServer(); + final ObjectInstance oi = server.registerMBean(new Thing(),null); + server.unregisterMBean(oi.getObjectName()); + System.out.println("testCreateWithNull PASSED"); + } + + /** + * Check that we can register a JMXNamespace MBean, using its standard + * ObjectName. + * @throws java.lang.Exception + */ + public static void testGoodObjectName() throws Exception { + MBeanServer server = newMBeanServer(); + final ObjectName name = + JMXNamespaces.getNamespaceObjectName("gloups"); + final ObjectInstance oi = + server.registerMBean(new LocalNamespace(),name); + System.out.println("Succesfully registered namespace: "+name); + try { + if (! name.equals(oi.getObjectName())) + throw new RuntimeException("testGoodObjectName: TEST failed: " + + "namespace registered as: "+ + oi.getObjectName()+" expected: "+name); + } finally { + server.unregisterMBean(oi.getObjectName()); + } + System.out.println("Succesfully unregistered namespace: "+name); + System.out.println("testGoodObjectName PASSED"); + } + + /** + * Check that we cannot register a JMXNamespace MBean, if we don't use + * its standard ObjectName. + * @throws java.lang.Exception + */ + public static void testBadObjectName() throws Exception { + MBeanServer server = newMBeanServer(); + Throwable exp = null; + final ObjectName name = new ObjectName("d:k=v"); + try { + server.registerMBean(new LocalNamespace(),name); + System.out.println("testBadObjectName: " + + "Error: MBean registered, no exception thrown."); + } catch(RuntimeMBeanException x) { + exp = x.getCause(); + } catch(Exception x) { + throw new RuntimeException("testBadObjectName: TEST failed: " + + "expected RuntimeMBeanException - got "+ + x); + } + if (exp == null) server.unregisterMBean(name); + if (exp == null) + throw new RuntimeException("testBadObjectName: TEST failed: " + + "expected IllegalArgumentException - got none"); + if (!(exp instanceof IllegalArgumentException)) + throw new RuntimeException("testBadObjectName: TEST failed: " + + "expected IllegalArgumentException - got "+ + exp.toString(),exp); + System.out.println("Got expected exception: "+exp); + System.out.println("testBadObjectName PASSED"); + } + + /** + * Check that we cannot register a Wombat MBean in a namespace that does + * not exists. + * + * @throws java.lang.Exception + */ + public static void testBadNamespace() throws Exception { + MBeanServer server = newMBeanServer(); + Throwable exp = null; + final ObjectName name = new ObjectName("glips//d:k=v"); + + try { + server.registerMBean(new Wombat(),name); + System.out.println("testBadNamespace: " + + "Error: MBean registered, no exception thrown."); + } catch(MBeanRegistrationException x) { + exp = x.getCause(); + } catch(Exception x) { + throw new RuntimeException("testBadNamespace: TEST failed: " + + "expected MBeanRegistrationException - got "+ + x); + } + if (exp == null) server.unregisterMBean(name); + if (exp == null) + throw new RuntimeException("testBadNamespace: TEST failed: " + + "expected IllegalArgumentException - got none"); + if (!(exp instanceof IllegalArgumentException)) + throw new RuntimeException("testBadNamespace: TEST failed: " + + "expected IllegalArgumentException - got "+ + exp.toString(),exp); + System.out.println("Got expected exception: "+exp); + System.out.println("testBadNamespace PASSED"); + } + + /** + * Check that we cannot register a Wombat MBean with a domain name + * that ends with //. This is reserved for namespaces. + * + * @throws java.lang.Exception + */ + public static void testBadDomain() throws Exception { + MBeanServer server = newMBeanServer(); + Throwable exp = null; + final ObjectName name = new ObjectName("glups//:k=v"); + + try { + server.registerMBean(new Wombat(),name); + System.out.println("testBadDomain: Error: MBean registered, no exception thrown."); + } catch(RuntimeMBeanException x) { + exp = x.getCause(); + } catch(Exception x) { + throw new RuntimeException("testBadDomain: TEST failed: " + + "expected RuntimeMBeanException - got "+ + x); + } + if (exp == null) server.unregisterMBean(name); + if (exp == null) + throw new RuntimeException("testBadDomain: TEST failed: " + + "expected IllegalArgumentException - got none"); + if (!(exp instanceof IllegalArgumentException)) + throw new RuntimeException("testBadDomain: TEST failed: " + + "expected IllegalArgumentException - got "+ + exp.toString(),exp); + System.out.println("Got expected exception: "+exp); + System.out.println("testBadDomain PASSED"); + } + + /** + * Check that we cannot register a Wombat MBean as if it were a + * JMXNamespace. Only JMXNamespace MBeans can have JMX Namespace names. + * @throws java.lang.Exception + */ + public static void testBadClassName() throws Exception { + MBeanServer server = newMBeanServer(); + Throwable exp = null; + final ObjectName name = + JMXNamespaces.getNamespaceObjectName("glops"); + try { + server.registerMBean(new Wombat(),name); + System.out.println("testBadClassName: " + + "Error: MBean registered, no exception thrown."); + } catch(RuntimeMBeanException x) { + exp = x.getCause(); + } catch(Exception x) { + throw new RuntimeException("testBadClassName: TEST failed: " + + "expected RuntimeMBeanException - got "+ + x); + } + if (exp == null) server.unregisterMBean(name); + if (exp == null) + throw new RuntimeException("testBadClassName: TEST failed: " + + "expected IllegalArgumentException - got none"); + if (!(exp instanceof IllegalArgumentException)) + throw new RuntimeException("testBadClassName: TEST failed: " + + "expected IllegalArgumentException - got "+ + exp.toString(),exp); + System.out.println("Got expected exception: "+exp); + System.out.println("testBadClassName PASSED"); + } + + public static void main(String... args) throws Exception { + testCreateWithNull(); + testGoodObjectName(); + testBadObjectName(); + testBadNamespace(); + testBadDomain(); + testBadClassName(); + } +} diff --git a/test/javax/management/namespace/NamespaceNotificationsTest.java b/test/javax/management/namespace/NamespaceNotificationsTest.java new file mode 100644 index 000000000..ae5bb3c59 --- /dev/null +++ b/test/javax/management/namespace/NamespaceNotificationsTest.java @@ -0,0 +1,388 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * + * @test NamespaceNotificationsTest.java 1.12 + * @summary General Namespace & Notifications test. + * @author Daniel Fuchs + * @run clean NamespaceNotificationsTest + * Wombat WombatMBean JMXRemoteTargetNamespace + * NamespaceController NamespaceControllerMBean + * @compile -XDignore.symbol.file=true NamespaceNotificationsTest.java + * Wombat.java WombatMBean.java JMXRemoteTargetNamespace.java + * NamespaceController.java NamespaceControllerMBean.java + * @run main NamespaceNotificationsTest + */ +import com.sun.jmx.remote.util.EventClientConnection; +import java.lang.management.ManagementFactory; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; +import javax.management.JMX; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerDelegate; +import javax.management.MBeanServerFactory; +import javax.management.MBeanServerNotification; +import javax.management.MalformedObjectNameException; +import javax.management.Notification; +import javax.management.NotificationEmitter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.loading.MLet; +import javax.management.namespace.JMXNamespace; +import javax.management.namespace.JMXNamespaces; +import javax.management.remote.JMXAddressable; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +/** + * + * @author Sun Microsystems, Inc. + */ +public class NamespaceNotificationsTest { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(NamespaceNotificationsTest.class.getName()); + + /** Creates a new instance of NamespaceNotificationsTest */ + public NamespaceNotificationsTest() { + } + + + public static JMXServiceURL export(MBeanServer server) + throws Exception { + final JMXServiceURL in = new JMXServiceURL("rmi",null,0); + final JMXConnectorServer cs = + JMXConnectorServerFactory.newJMXConnectorServer(in,null,null); + final ObjectName csname = ObjectName. + getInstance(cs.getClass().getPackage().getName()+ + ":type="+cs.getClass().getSimpleName()); + server.registerMBean(cs,csname); + cs.start(); + return cs.getAddress(); + } + + public static class Counter { + int count; + public synchronized int count() { + count++; + notifyAll(); + return count; + } + public synchronized int peek() { + return count; + } + public synchronized int waitfor(int max, long timeout) + throws InterruptedException { + final long start = System.currentTimeMillis(); + while (count < max && timeout > 0) { + final long rest = timeout - + (System.currentTimeMillis() - start); + if (rest <= 0) break; + wait(rest); + } + return count; + } + } + + public static class CounterListener + implements NotificationListener { + final private Counter counter; + public CounterListener(Counter counter) { + this.counter = counter; + } + public void handleNotification(Notification notification, + Object handback) { + System.out.println("Received notif from " + handback + + ":\n\t" + notification); + if (!notification.getSource().equals(handback)) { + System.err.println("OhOh... Unexpected source: \n\t"+ + notification.getSource()+"\n\twas expecting:\n\t"+ + handback); + } + counter.count(); + } + } + + public static void simpleTest(String[] args) { + try { + final MBeanServer server1 = + ManagementFactory.getPlatformMBeanServer(); + final JMXServiceURL url1 = export(server1); + + final MBeanServer server2 = + MBeanServerFactory.createMBeanServer("server2"); + final JMXServiceURL url2 = export(server2); + + final MBeanServer server3 = + MBeanServerFactory.createMBeanServer("server3"); + final JMXServiceURL url3 = export(server3); + + final ObjectInstance ncinst = + NamespaceController.createInstance(server1); + + final NamespaceControllerMBean nc = + JMX.newMBeanProxy(server1,ncinst.getObjectName(), + NamespaceControllerMBean.class); + + final Map options = new HashMap(); + options.put(JMXRemoteTargetNamespace.CREATE_EVENT_CLIENT,"true"); + + final String mount1 = + nc.mount(url1,"server1",options); + final String mount2 = nc.mount(url2,"server1//server2", + options); + final String mount3 = nc.mount(url3, + "server1//server2//server3", + options); + final String mount13 = nc.mount( + url1, + "server3", + "server2//server3", + options); + final String mount21 = nc.mount(url1,"server2//server1", + options); + final String mount31 = nc.mount( + url1, + "server3//server1", + "server1", + options); + final String mount32 = nc.mount( + url1, + "server3//server2", + "server2", + options); + + + final ObjectName deep = + new ObjectName("server1//server2//server3//bush:type=Wombat,name=kanga"); + server1.createMBean(Wombat.class.getName(),deep); + + System.err.println("There's a wombat in the bush!"); + + final Counter counter = new Counter(); + + final NotificationListener listener = + new CounterListener(counter); + + final JMXConnector jc = JMXConnectorFactory.connect(url1); + final MBeanServerConnection aconn = + EventClientConnection.getEventConnectionFor( + jc.getMBeanServerConnection(),null); + aconn.addNotificationListener(deep,listener,null,deep); + + + final JMXServiceURL urlx = new JMXServiceURL(url1.toString()); + System.out.println("conn: "+urlx); + final JMXConnector jc2 = JMXNamespaces.narrowToNamespace( + JMXConnectorFactory.connect(urlx),"server1//server1"); + final JMXConnector jc3 = JMXNamespaces.narrowToNamespace(jc2,"server3"); + jc3.connect(); + System.out.println("JC#3: " + + ((jc3 instanceof JMXAddressable)? + ((JMXAddressable)jc3).getAddress(): + jc3.toString())); + final MBeanServerConnection bconn = + jc3.getMBeanServerConnection(); + final ObjectName shallow = + new ObjectName("bush:"+ + deep.getKeyPropertyListString()); + final WombatMBean proxy = + JMX.newMBeanProxy(EventClientConnection.getEventConnectionFor( + bconn,null),shallow,WombatMBean.class,true); + + ((NotificationEmitter)proxy). + addNotificationListener(listener,null,shallow); + proxy.setCaption("I am a new Wombat!"); + System.err.println("New caption: "+proxy.getCaption()); + final int rcvcount = counter.waitfor(2,3000); + if (rcvcount != 2) + throw new RuntimeException("simpleTest failed: "+ + "received count is " +rcvcount); + + System.err.println("simpleTest passed: got "+rcvcount+ + " notifs"); + + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new RuntimeException("simpleTest failed: " + x,x); + } + } + + public static class LocalNamespace extends + JMXNamespace { + LocalNamespace() { + super(MBeanServerFactory.newMBeanServer()); + } + + } + + public static class ContextObject { + public final K name; + public final V object; + public ContextObject(K name, V object) { + this.name = name; + this.object = object; + } + private Object[] data() { + return new Object[] {name,object}; + } + + @Override + public boolean equals(Object x) { + if (x instanceof ContextObject) + return Arrays.deepEquals(data(),((ContextObject)x).data()); + return false; + } + @Override + public int hashCode() { + return Arrays.deepHashCode(data()); + } + } + + private static ContextObject context(K k, V v) { + return new ContextObject(k,v); + } + + private static ObjectName name(String name) { + try { + return new ObjectName(name); + } catch(MalformedObjectNameException x) { + throw new IllegalArgumentException(name,x); + } + } + + public static void simpleTest2() { + try { + System.out.println("\nsimpleTest2: STARTING\n"); + final LocalNamespace foo = new LocalNamespace(); + final LocalNamespace joe = new LocalNamespace(); + final LocalNamespace bar = new LocalNamespace(); + final MBeanServer server = MBeanServerFactory.newMBeanServer(); + + server.registerMBean(foo,JMXNamespaces.getNamespaceObjectName("foo")); + server.registerMBean(joe,JMXNamespaces.getNamespaceObjectName("foo//joe")); + server.registerMBean(bar,JMXNamespaces.getNamespaceObjectName("foo//bar")); + final BlockingQueue> queue = + new ArrayBlockingQueue>(20); + + final NotificationListener listener = new NotificationListener() { + public void handleNotification(Notification n, Object handback) { + if (!(n instanceof MBeanServerNotification)) { + System.err.println("Error: expected MBeanServerNotification"); + return; + } + final MBeanServerNotification mbsn = + (MBeanServerNotification) n; + + // We will pass the namespace name in the handback. + // + final String namespace = (String) handback; + System.out.println("Received " + mbsn.getType() + + " for MBean " + mbsn.getMBeanName() + + " from name space " + namespace); + try { + queue.offer(context(namespace,mbsn),500,TimeUnit.MILLISECONDS); + } catch (Exception x) { + System.err.println("Failed to enqueue received notif: "+mbsn); + x.printStackTrace(); + } + } + }; + + server.addNotificationListener(JMXNamespaces.insertPath("foo//joe", + MBeanServerDelegate.DELEGATE_NAME),listener,null,"foo//joe"); + server.addNotificationListener(JMXNamespaces.insertPath("foo//bar", + MBeanServerDelegate.DELEGATE_NAME),listener,null,"foo//bar"); + server.createMBean(MLet.class.getName(), + name("foo//joe//domain:type=MLet")); + checkQueue(queue,"foo//joe", + MBeanServerNotification.REGISTRATION_NOTIFICATION); + server.createMBean(MLet.class.getName(), + name("foo//bar//domain:type=MLet")); + checkQueue(queue,"foo//bar", + MBeanServerNotification.REGISTRATION_NOTIFICATION); + server.unregisterMBean( + name("foo//joe//domain:type=MLet")); + checkQueue(queue,"foo//joe", + MBeanServerNotification.UNREGISTRATION_NOTIFICATION); + server.unregisterMBean( + name("foo//bar//domain:type=MLet")); + checkQueue(queue,"foo//bar", + MBeanServerNotification.UNREGISTRATION_NOTIFICATION); + } catch (RuntimeException x) { + System.err.println("FAILED: "+x); + throw x; + } catch(Exception x) { + System.err.println("FAILED: "+x); + throw new RuntimeException("Unexpected exception: "+x,x); + } + } + + + private static void checkQueue( + BlockingQueue> q, + String path, String type) { + try { + final ContextObject ctxt = + q.poll(500,TimeUnit.MILLISECONDS); + if (ctxt == null) + throw new RuntimeException("Timeout expired: expected notif from "+ + path +", type="+type); + if (!ctxt.name.equals(path)) + throw new RuntimeException("expected notif from "+ + path +", got "+ctxt.name); + if (!ctxt.object.getType().equals(type)) + throw new RuntimeException(ctxt.name+": expected type="+ + type +", got "+ctxt.object.getType()); + if (!ctxt.object.getType().equals(type)) + throw new RuntimeException(ctxt.name+": expected type="+ + type +", got "+ctxt.object.getType()); + if (!ctxt.object.getMBeanName().equals(name("domain:type=MLet"))) + throw new RuntimeException(ctxt.name+": expected MBean=domain:type=MLet"+ + ", got "+ctxt.object.getMBeanName()); + } catch(InterruptedException x) { + throw new RuntimeException("unexpected interruption: "+x,x); + } + } + + public static void main(String[] args) { + simpleTest(args); + simpleTest2(); + } + +} diff --git a/test/javax/management/namespace/NullDomainObjectNameTest.java b/test/javax/management/namespace/NullDomainObjectNameTest.java new file mode 100644 index 000000000..3251d388a --- /dev/null +++ b/test/javax/management/namespace/NullDomainObjectNameTest.java @@ -0,0 +1,284 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * @test NullDomainObjectNameTest.java + * @summary Test that null domains are correctly handled in namespaces. + * @author Daniel Fuchs + * @run clean NullDomainObjectNameTest Wombat WombatMBean + * @compile -XDignore.symbol.file=true NullDomainObjectNameTest.java + * @run build NullDomainObjectNameTest Wombat WombatMBean + * @run main NullDomainObjectNameTest + */ + +import com.sun.jmx.namespace.RoutingServerProxy; +import java.lang.management.ManagementFactory; +import java.util.Arrays; +import java.util.logging.Logger; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.namespace.JMXNamespaces; +import javax.management.namespace.JMXRemoteNamespace; +import javax.management.namespace.JMXNamespace; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +/** + * Class NullDomainObjectNameTest + * @author Sun Microsystems, 2005 - All rights reserved. + */ +public class NullDomainObjectNameTest { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(NullDomainObjectNameTest.class.getName()); + + /** Creates a new instance of NullDomainObjectNameTest */ + public NullDomainObjectNameTest() { + } + + public static class MyWombat + extends Wombat { + public MyWombat() throws NotCompliantMBeanException { + super(); + } + @Override + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + + if (name == null) + name = new ObjectName(":type=Wombat"); + + return super.preRegister(server, name); + } + + } + + static String failure=null; + + public static void testRegister() throws Exception { + final MBeanServer top = ManagementFactory.getPlatformMBeanServer(); + final MBeanServer sub = MBeanServerFactory.createMBeanServer(); + final JMXServiceURL url = new JMXServiceURL("rmi",null,0); + final JMXConnectorServer srv = + JMXConnectorServerFactory.newJMXConnectorServer(url,null,sub); + srv.start(); + + try { + + // Create a namespace rmi// that points to 'sub' and flows through + // a JMXRemoteNamespace connected to 'srv' + // The namespace rmi// will accept createMBean, but not registerMBean. + // + final JMXRemoteNamespace rmiHandler = JMXRemoteNamespace. + newJMXRemoteNamespace(srv.getAddress(), + null); + top.registerMBean(rmiHandler,JMXNamespaces.getNamespaceObjectName("rmi")); + top.invoke(JMXNamespaces.getNamespaceObjectName("rmi"), + "connect", null, null); + + // Create a namespace direct// that points to 'sub' and flows + // through a direct reference to 'sub'. + // The namespace direct// will accept createMBean, and registerMBean. + // + final JMXNamespace directHandler = new JMXNamespace(sub); + top.registerMBean(directHandler, + JMXNamespaces.getNamespaceObjectName("direct")); + + // Now cd to each of the created namespace. + // + MBeanServer cdrmi = JMXNamespaces.narrowToNamespace(top,"rmi"); + MBeanServer cddirect = JMXNamespaces.narrowToNamespace(top,"direct"); + boolean ok = false; + + // Check that calling createMBean with a null domain works + // for namespace rmi// + // + try { + final ObjectInstance moi1 = + cdrmi.createMBean(MyWombat.class.getName(), + new ObjectName(":type=Wombat")); + System.out.println(moi1.getObjectName().toString()+ + ": created through rmi//"); + assertEquals(moi1.getObjectName().getDomain(), + cddirect.getDefaultDomain()); + cddirect.unregisterMBean(moi1.getObjectName()); + } catch (MBeanRegistrationException x) { + System.out.println("Received unexpected exception: " + x); + failed("Received unexpected exception: " + x); + } + + // Check that calling refgisterMBean with a null domain works + // for namespace direct// + // + try { + final ObjectInstance moi2 = + cddirect.registerMBean(new MyWombat(), + new ObjectName(":type=Wombat")); + System.out.println(moi2.getObjectName().toString()+ + ": created through direct//"); + assertEquals(moi2.getObjectName().getDomain(), + cdrmi.getDefaultDomain()); + cdrmi.unregisterMBean(moi2.getObjectName()); + } catch (MBeanRegistrationException x) { + System.out.println("Received unexpected exception: " + x); + failed("Received unexpected exception: " + x); + } + + // Now artificially pretend that 'sub' is contained in a faked// + // namespace. + // + RoutingServerProxy proxy = + new RoutingServerProxy(sub, "", "faked", false); + + // These should fail because the ObjectName doesn't start + // with "faked//" + try { + final ObjectInstance moi3 = + proxy.registerMBean(new MyWombat(), + new ObjectName(":type=Wombat")); + System.out.println(moi3.getObjectName().toString()+ + ": created through faked//"); + failed("expected MBeanRegistrationException"); + } catch (MBeanRegistrationException x) { + System.out.println("Received expected exception: " + x); + if (!(x.getCause() instanceof IllegalArgumentException)) { + System.err.println("Bad wrapped exception: "+ x.getCause()); + failed("expected IllegalArgumentException"); + } + } + + // null should work with "faked//" + final ObjectInstance moi3 = + proxy.registerMBean(new MyWombat(),null); + assertEquals(moi3.getObjectName().getDomain(), + "faked//"+sub.getDefaultDomain()); + + System.out.println(moi3.getObjectName().toString() + + ": created through faked//"); + + // Now check that null is correctly handled (accepted or rejected) + // in queries for each of the above configs. + // + ObjectName wombat = moi3.getObjectName().withDomain( + moi3.getObjectName().getDomain().substring("faked//".length())); + ObjectInstance moi = new ObjectInstance(wombat,moi3.getClassName()); + + System.out.println("Checking queryNames(" + + "new ObjectName(\":*\"),null) with rmi//"); + assertEquals(cdrmi.queryNames( + new ObjectName(":*"),null).contains(wombat),true); + System.out.println("Checking queryNames(" + + "new ObjectName(\":*\"),null) with direct//"); + assertEquals(cddirect.queryNames( + new ObjectName(":*"),null).contains(wombat),true); + System.out.println("Checking queryMBeans(" + + "new ObjectName(\":*\"),null) with rmi//"); + assertEquals(cdrmi.queryMBeans( + new ObjectName(":*"),null).contains(moi),true); + System.out.println("Checking queryMBeans(" + + "new ObjectName(\":*\"),null) with direct//"); + assertEquals(cddirect.queryMBeans( + new ObjectName(":*"),null).contains(moi),true); + + // These should fail because the ObjectName doesn't start + // with "faked//" + try { + System.out.println("Checking queryNames(" + + "new ObjectName(\":*\"),null) with faked//"); + assertEquals(proxy.queryNames( + new ObjectName(":*"),null). + contains(moi3.getObjectName()),true); + failed("queryNames(null,null) should have failed for faked//"); + } catch (IllegalArgumentException x) { + System.out.println("Received expected exception for faked//: "+x); + } + // These should fail because the ObjectName doesn't start + // with "faked//" + try { + System.out.println("Checking queryMBeans(" + + "new ObjectName(\":*\"),null) with faked//"); + assertEquals(proxy.queryMBeans( + new ObjectName(":*"),null).contains(moi3),true); + failed("queryMBeans(null,null) should have failed for faked//"); + } catch (IllegalArgumentException x) { + System.out.println("Received expected exception for faked//: "+x); + } + + System.out.println("Checking queryNames(faked//*:*,null)"); + assertEquals(proxy.queryNames(new ObjectName("faked//*:*"),null). + contains(moi3.getObjectName()),true); + + System.out.println("Checking queryMBeans(faked//*:*,null)"); + assertEquals(proxy.queryMBeans(new ObjectName("faked//*:*"),null). + contains(moi3),true); + + proxy.unregisterMBean(moi3.getObjectName()); + + // ADD NEW TESTS HERE ^^^ + + } finally { + srv.stop(); + } + + if (failure != null) + throw new Exception(failure); + + + } + private static void assertEquals(Object x, Object y) { + if (!equal(x, y)) + failed("expected " + string(x) + "; got " + string(y)); + } + + private static boolean equal(Object x, Object y) { + if (x == y) + return true; + if (x == null || y == null) + return false; + if (x.getClass().isArray()) + return Arrays.deepEquals(new Object[] {x}, new Object[] {y}); + return x.equals(y); + } + + private static String string(Object x) { + String s = Arrays.deepToString(new Object[] {x}); + return s.substring(1, s.length() - 1); + } + + + private static void failed(String why) { + failure = why; + new Throwable("FAILED: " + why).printStackTrace(System.out); + } + + public static void main(String[] args) throws Exception { + testRegister(); + } +} diff --git a/test/javax/management/namespace/NullObjectNameTest.java b/test/javax/management/namespace/NullObjectNameTest.java new file mode 100644 index 000000000..7624fb9d9 --- /dev/null +++ b/test/javax/management/namespace/NullObjectNameTest.java @@ -0,0 +1,251 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * @test NullObjectNameTest.java + * @summary Test that null ObjectName are correctly handled in namespaces. + * @author Daniel Fuchs + * @run clean NullObjectNameTest Wombat WombatMBean + * @compile -XDignore.symbol.file=true NullObjectNameTest.java + * @run build NullObjectNameTest Wombat WombatMBean + * @run main NullObjectNameTest + */ + +import com.sun.jmx.namespace.RoutingServerProxy; +import java.lang.management.ManagementFactory; +import java.util.Arrays; +import java.util.logging.Logger; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.namespace.JMXNamespaces; +import javax.management.namespace.JMXRemoteNamespace; +import javax.management.namespace.JMXNamespace; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +/** + * Class NullObjectNameTest + * @author Sun Microsystems, 2005 - All rights reserved. + */ +public class NullObjectNameTest { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(NullObjectNameTest.class.getName()); + + /** Creates a new instance of NullObjectNameTest */ + public NullObjectNameTest() { + } + + public static class MyWombat + extends Wombat { + public MyWombat() throws NotCompliantMBeanException { + super(); + } + @Override + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + + if (name == null) + name = new ObjectName(":type=Wombat"); + + return super.preRegister(server, name); + } + + } + + static String failure=null; + + public static void testRegister() throws Exception { + final MBeanServer top = ManagementFactory.getPlatformMBeanServer(); + final MBeanServer sub = MBeanServerFactory.createMBeanServer(); + final JMXServiceURL url = new JMXServiceURL("rmi",null,0); + final JMXConnectorServer srv = + JMXConnectorServerFactory.newJMXConnectorServer(url,null,sub); + srv.start(); + + try { + + // Create a namespace rmi// that points to 'sub' and flows through + // a JMXRemoteNamespace connected to 'srv' + // The namespace rmi// will accept createMBean, but not registerMBean. + // + final JMXRemoteNamespace rmiHandler = JMXRemoteNamespace. + newJMXRemoteNamespace(srv.getAddress(),null); + top.registerMBean(rmiHandler,JMXNamespaces.getNamespaceObjectName("rmi")); + top.invoke(JMXNamespaces.getNamespaceObjectName("rmi"), + "connect", null, null); + + // Create a namespace direct// that points to 'sub' and flows + // through a direct reference to 'sub'. + // The namespace direct// will accept createMBean, and registerMBean. + // + final JMXNamespace directHandler = new JMXNamespace(sub); + top.registerMBean(directHandler, + JMXNamespaces.getNamespaceObjectName("direct")); + + // Now cd to each of the created namespace. + // + MBeanServer cdrmi = JMXNamespaces.narrowToNamespace(top,"rmi"); + MBeanServer cddirect = JMXNamespaces.narrowToNamespace(top,"direct"); + boolean ok = false; + + // Check that calling createMBean with a null ObjectName fails + // gracefully for namespace rmi// (we can't add rmi// to a null + // ObjectName. + // + // TODO: do this test for all createMBean flavors! + try { + final ObjectInstance moi1 = + cdrmi.createMBean(MyWombat.class.getName(),null); + System.out.println(moi1.getObjectName().toString()+ + ": created through rmi//"); + cddirect.unregisterMBean(moi1.getObjectName()); + failed("expected MBeanRegistrationException"); + } catch (MBeanRegistrationException x) { + System.out.println("Received expected exception: " + x); + if (!(x.getCause() instanceof IllegalArgumentException)) { + System.err.println("Bad wrapped exception: "+ x.getCause()); + failed("expected IllegalArgumentException"); + } + } + + // Check that calling refgisterMBean with a null ObjectName fails + // gracefully for namespace direct// (we can't add direct// to a null + // ObjectName. + // + try { + final ObjectInstance moi2 = + cddirect.registerMBean(new MyWombat(), (ObjectName)null); + System.out.println(moi2.getObjectName().toString()+ + ": created through direct//"); + cdrmi.unregisterMBean(moi2.getObjectName()); + failed("expected MBeanRegistrationException"); + } catch (MBeanRegistrationException x) { + System.out.println("Received expected exception: " + x); + if (!(x.getCause() instanceof IllegalArgumentException)) { + System.err.println("Bad wrapped exception: "+ x.getCause()); + failed("expected IllegalArgumentException"); + } + } + + // Now artificially pretend that 'sub' is contained in a faked// + // namespace. + // We should be able to use 'null' in registerMBean/createMBean in + // this case. + // + RoutingServerProxy proxy = + new RoutingServerProxy(sub,"","faked",false); + final ObjectInstance moi3 = + proxy.registerMBean(new MyWombat(),null); + System.out.println(moi3.getObjectName().toString()+ + ": created through faked//"); + + // Now check that null is correctly handled (accepted or rejected) + // in queries for each of the above configs. + // + ObjectName wombat = moi3.getObjectName().withDomain( + moi3.getObjectName().getDomain().substring("faked//".length())); + ObjectInstance moi = new ObjectInstance(wombat,moi3.getClassName()); + + System.out.println("Checking queryNames(null,null) with rmi//"); + assertEquals(cdrmi.queryNames(null,null).contains(wombat),true); + System.out.println("Checking queryNames(null,null) with direct//"); + assertEquals(cddirect.queryNames(null,null).contains(wombat),true); + System.out.println("Checking queryMBeans(null,null) with rmi//"); + assertEquals(cdrmi.queryMBeans(null,null).contains(moi),true); + System.out.println("Checking queryMBeans(null,null) with direct//"); + assertEquals(cddirect.queryMBeans(null,null).contains(moi),true); + + try { + System.out.println("Checking queryNames(null,null) with faked//"); + assertEquals(proxy.queryNames(null,null). + contains(moi3.getObjectName()),true); + failed("queryNames(null,null) should have failed for faked//"); + } catch (IllegalArgumentException x) { + System.out.println("Received expected exception for faked//: "+x); + } + try { + System.out.println("Checking queryMBeans(null,null) with faked//"); + assertEquals(proxy.queryMBeans(null,null).contains(moi3),true); + failed("queryMBeans(null,null) should have failed for faked//"); + } catch (IllegalArgumentException x) { + System.out.println("Received expected exception for faked//: "+x); + } + System.out.println("Checking queryNames(faked//*:*,null)"); + assertEquals(proxy.queryNames(new ObjectName("faked//*:*"),null). + contains(moi3.getObjectName()),true); + + System.out.println("Checking queryMBeans(faked//*:*,null)"); + assertEquals(proxy.queryMBeans(new ObjectName("faked//*:*"),null). + contains(moi3),true); + + proxy.unregisterMBean(moi3.getObjectName()); + + // ADD NEW TESTS HERE ^^^ + + } finally { + srv.stop(); + } + + if (failure != null) + throw new Exception(failure); + + + } + private static void assertEquals(Object x, Object y) { + if (!equal(x, y)) + failed("expected " + string(x) + "; got " + string(y)); + } + + private static boolean equal(Object x, Object y) { + if (x == y) + return true; + if (x == null || y == null) + return false; + if (x.getClass().isArray()) + return Arrays.deepEquals(new Object[] {x}, new Object[] {y}); + return x.equals(y); + } + + private static String string(Object x) { + String s = Arrays.deepToString(new Object[] {x}); + return s.substring(1, s.length() - 1); + } + + + private static void failed(String why) { + failure = why; + new Throwable("FAILED: " + why).printStackTrace(System.out); + } + + public static void main(String[] args) throws Exception { + testRegister(); + } +} diff --git a/test/javax/management/namespace/QueryNamesTest.java b/test/javax/management/namespace/QueryNamesTest.java new file mode 100644 index 000000000..6e15c9a75 --- /dev/null +++ b/test/javax/management/namespace/QueryNamesTest.java @@ -0,0 +1,408 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * + * @test QueryNamesTest.java 1.4 + * @summary Test how queryNames works with Namespaces. + * @author Daniel Fuchs + * @run clean QueryNamesTest Wombat WombatMBean + * @run build QueryNamesTest Wombat WombatMBean + * @run main QueryNamesTest + */ + + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.logging.Logger; +import javax.management.InstanceNotFoundException; +import javax.management.JMException; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.namespace.JMXNamespace; +import javax.management.namespace.JMXNamespaces; + +/** + * Class QueryNamesTest + * @author Sun Microsystems, 2005 - All rights reserved. + */ +public class QueryNamesTest { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(QueryNamesTest.class.getName()); + + public static class LocalNamespace + extends JMXNamespace { + + private static MBeanServer check(MBeanServer server) { + if (server == null) + throw new IllegalArgumentException("MBeanServer can't be null"); + return server; + } + + public LocalNamespace() { + this(MBeanServerFactory.createMBeanServer()); + } + + public LocalNamespace(MBeanServer server) { + super(check(server)); + } + + + public static String add(MBeanServerConnection server, + String nspath) + throws IOException, JMException { + server.createMBean(LocalNamespace.class.getName(), + JMXNamespaces.getNamespaceObjectName(nspath)); + return nspath; + } + } + + /** Creates a new instance of QueryNamesTest */ + public QueryNamesTest() { + } + + private static String[] namespaces = { + "greg", "greg//chichille", "greg//chichille//petard", + "greg//alambic", "greg//alambic//canette", + "greg//chichille/virgule", "greg//chichille/funeste", + "greg//chichille/virgule//bidouble", + "greg//chichille/virgule//bi/double", + "fran", "fran//gast", "fran//gast//gaf", + "fran//longtar", "fran//longtar//parcmetre" + }; + + private static void createNamespaces(MBeanServer server) throws Exception { + final LinkedList all = new LinkedList(); + try { + for (String ns : namespaces) + all.addFirst(LocalNamespace.add(server,ns)); + } catch (Exception e) { + removeNamespaces(server,all.toArray(new String[all.size()])); + throw e; + } + } + + // Dummy test that checks that all JMXNamespaces are registered, + // but are not returned by queryNames("*:*"); + // + private static void checkRegistration(MBeanServer server) + throws Exception { + final Set handlerNames = new HashSet(namespaces.length); + for (String ns : namespaces) + handlerNames.add(JMXNamespaces.getNamespaceObjectName(ns)); + for (ObjectName nh : handlerNames) // check handler registration + if (!server.isRegistered(nh)) + throw new InstanceNotFoundException("handler "+nh+ + " is not registered"); + + // global: queryNames("*:*") from top level + final Set all1 = server.queryNames(null,null); + final Set all2 = server.queryNames(ObjectName.WILDCARD,null); + if (!all1.equals(all2)) + throw new Exception("queryNames(*:*) != queryNames(null)"); + final Set common = new HashSet(all1); + common.retainAll(handlerNames); + + final Set ref = new HashSet(); + for (String ns : namespaces) { + if (!ns.contains(JMXNamespaces.NAMESPACE_SEPARATOR)) + ref.add(JMXNamespaces.getNamespaceObjectName(ns)); + } + if (!common.equals(ref)) { + throw new Exception("some handler names were not returned by " + + "wildcard query - only returned: "+common+ + ", expected: "+ref); + } + + // for each namespace: queryNames("//*:*"); + for (String ns : namespaces) { + final ObjectName pattern = new ObjectName(ns+ + JMXNamespaces.NAMESPACE_SEPARATOR+"*:*"); + final Set all4 = + server.queryNames(pattern,null); + final Set common4 = new HashSet(all4); + common4.retainAll(handlerNames); + + final Set ref4 = new HashSet(); + for (String ns2 : namespaces) { + if (! ns2.startsWith(ns+JMXNamespaces.NAMESPACE_SEPARATOR)) + continue; + if (!ns2.substring(ns.length()+ + JMXNamespaces.NAMESPACE_SEPARATOR.length()). + contains(JMXNamespaces.NAMESPACE_SEPARATOR)) + ref4.add(JMXNamespaces.getNamespaceObjectName(ns2)); + } + if (!common4.equals(ref4)) { + throw new Exception("some handler names were not returned by " + + "wildcard query on "+pattern+" - only returned: "+common4+ + ", expected: "+ref4); + } + } + } + + // Make a Map + private static Map> makeNsTree(String[] nslist) { + final Map> nsTree = + new LinkedHashMap>(nslist.length); + for (String ns : nslist) { + if (nsTree.get(ns) == null) + nsTree.put(ns,new LinkedHashSet()); + final String[] elts = ns.split(JMXNamespaces.NAMESPACE_SEPARATOR); + int last = ns.lastIndexOf(JMXNamespaces.NAMESPACE_SEPARATOR); + if (last<0) continue; + while (last > 0 && ns.charAt(last-1) == '/') last--; + final String parent = ns.substring(0,last); + if (nsTree.get(parent) == null) + nsTree.put(parent,new LinkedHashSet()); + nsTree.get(parent).add(ns); + } + return nsTree; + } + + private static class Rigolo { + final static String[] ones = { "a", "e", "i", "o", "u", "y", "ai", "oo", + "ae", "ey", "ay", "oy", "au", "ou", "eu", "oi", "ei", "ea"}; + final static String[] twos = { "b", "bz", "c", "cz", "ch", + "ct", "ck", "cs", "d", "ds", "f", "g", "gh", "h", "j", "k", "l", "m", + "n", "p", "ps", "q", "r", "s", "sh", "t", "v", "w", "x", + "z"}; + final static String[] threes = {"rr","tt","pp","ss","dd","ff","ll", "mm", "nn", + "zz", "cc", "bb"}; + final static String[] fours = {"x", "s", "ght", "cks", "rt", "rts", "ghts", "bs", + "ts", "gg" }; + final static String[] fives = { "br", "bl", "cr", "cn", "cth", "dr", + "fr", "fl", "cl", "chr", "gr", "gl", "kr", "kh", "pr", "pl", "ph", + "rh", "sr", "tr", "vr"}; + + private Random rg = new Random(); + + private String next(String[] table) { + return table[rg.nextInt(table.length)]; + } + + public String nextName(int max) { + final Random rg = new Random(); + final int nl = 3 + rg.nextInt(max); + boolean begin = rg.nextBoolean(); + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < nl ; j++) { + if (begin) { + sb.append(next(ones)); + } else if (j > 0 && j < nl-1 && rg.nextInt(4)==0) { + sb.append(next(threes)); + } else if (j < nl-1 && rg.nextInt(3)==0) { + sb.append(next(fives)); + } else { + sb.append(next(twos)); + } + begin = !begin; + } + if (!begin && rg.nextInt(2)==0) + sb.append(next(fours)); + return sb.toString(); + } + + private ObjectName getWombatName(String ns, String domain, String name) + throws MalformedObjectNameException { + String d = domain; + if (ns != null && !ns.equals("")) + d = ns + JMXNamespaces.NAMESPACE_SEPARATOR + domain; + return new ObjectName(d+":type=Wombat,name="+name); + } + + public Set nextWombats(String ns) + throws MalformedObjectNameException { + final int dcount = 1 + rg.nextInt(5); + final Set wombats = new HashSet(); + for (int i = 0; i < dcount ; i++) { + final String d = nextName(7); + final int ncount = 5 + rg.nextInt(20); + for (int j = 0 ; j> nsTree = makeNsTree(namespaces); + final Random rg = new Random(); + final Rigolo rigolo = new Rigolo(); + for (String ns : namespaces) { + final ObjectName name = JMXNamespaces.getNamespaceObjectName(ns); + final String[] doms = + (String[])server.getAttribute(name,"Domains"); + final Set subs = new HashSet(); + for (String d : doms) { + if (d.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) { + subs.add(ns+JMXNamespaces.NAMESPACE_SEPARATOR+d.substring(0, + d.length()-JMXNamespaces.NAMESPACE_SEPARATOR.length())); + } + } + + final Set expectNs = new HashSet(nsTree.get(ns)); + + if (! subs.containsAll(expectNs)) + throw new Exception("getDomains didn't return all namespaces: "+ + "returned="+subs+", expected="+expectNs); + if (! expectNs.containsAll(subs)) + throw new Exception("getDomains returned additional namespaces: "+ + "returned="+subs+", expected="+expectNs); + + final Set nsNames = server.queryNames( + new ObjectName(ns+ + JMXNamespaces.NAMESPACE_SEPARATOR+"*"+ + JMXNamespaces.NAMESPACE_SEPARATOR+":*"),null); + + final Set expect = + new HashSet(expectNs.size()); + for (String sub : expectNs) { + expect.add(JMXNamespaces.getNamespaceObjectName(sub)); + } + + if (! nsNames.containsAll(expect)) + throw new Exception("queryNames didn't return all namespaces: "+ + "returned="+nsNames+", expected="+expect); + if (! expect.containsAll(nsNames)) + throw new Exception("getDomains returned additional namespaces: "+ + "returned="+nsNames+", expected="+expect); + + } + } + + private static void addWombats(MBeanServer server, Set names) + throws Exception { + for (ObjectName on : names) { + if (! server.isRegistered(on)) { + server.createMBean(Wombat.class.getName(),on); + System.out.println("A new wombat is born: "+on); + } + } + } + + private static void addWombats(MBeanServer server, + Map> wombats) + throws Exception { + for (String ns : wombats.keySet()) { + addWombats(server,wombats.get(ns)); + } + } + + private static Map> nameWombats() + throws Exception { + final Rigolo rigolo = new Rigolo(); + final Map> wombats = + new HashMap>(namespaces.length); + + for (String ns : namespaces) { + wombats.put(ns,rigolo.nextWombats(ns)); + } + wombats.put("",rigolo.nextWombats("")); + return wombats; + } + + private static boolean removeWombats(MBeanServer server, + Map> wombats) { + boolean res = true; + for (String ns : wombats.keySet()) { + res = res && removeWombats(server,wombats.get(ns)); + } + return res; + } + + private static boolean removeWombats(MBeanServer server, + Set wombats) { + boolean res = true; + for (ObjectName on : wombats) { + try { + if (server.isRegistered(on)) + server.unregisterMBean(on); + } catch (Exception x) { + res = false; + System.out.println("Failed to remove "+on+": "+x); + } + } + return res; + } + + public static void main(String[] args) + throws Exception { + final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + Map> wombats = nameWombats(); + createNamespaces(server); + try { + addWombats(server,wombats); + System.out.println("MBeans: " +server.getMBeanCount()); + System.out.println("Visible: " +server.queryNames(null,null).size()); + System.out.println("Domains: " +Arrays.asList(server.getDomains())); + checkRegistration(server); + checkNsQuery(server); + } finally { + boolean res = true; + res = res && removeWombats(server, wombats); + if (!res) + throw new RuntimeException("failed to cleanup some namespaces"); + } + + } + + private static boolean removeNamespaces(MBeanServer server) { + final List l = Arrays.asList(namespaces); + Collections.reverse(l); + return removeNamespaces(server, l.toArray(new String[namespaces.length])); + } + + private static boolean removeNamespaces(MBeanServer server, String[] t) { + boolean success = true; + for (String ns : t) { + try { + server.unregisterMBean(JMXNamespaces.getNamespaceObjectName(ns)); + } catch (Exception x) { + System.out.println("failed to remove namespace: "+ ns); + success = false; + } + } + return success; + } + +} diff --git a/test/javax/management/namespace/RemoveNotificationListenerTest.java b/test/javax/management/namespace/RemoveNotificationListenerTest.java new file mode 100644 index 000000000..08375c0db --- /dev/null +++ b/test/javax/management/namespace/RemoveNotificationListenerTest.java @@ -0,0 +1,376 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * + * @test RemoveNotificationListenerTest.java 1.8 + * @summary General RemoveNotificationListenerTest test. + * @author Daniel Fuchs + * @run clean RemoveNotificationListenerTest JMXRemoteTargetNamespace + * @compile -XDignore.symbol.file=true JMXRemoteTargetNamespace.java + * @run build RemoveNotificationListenerTest JMXRemoteTargetNamespace + * @run main/othervm RemoveNotificationListenerTest + */ + +import com.sun.jmx.remote.util.EventClientConnection; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.security.Principal; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; +import javax.management.JMException; +import javax.management.JMX; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.namespace.JMXNamespaces; +import javax.management.remote.JMXAuthenticator; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXConnectorServerMBean; +import javax.management.remote.JMXPrincipal; +import javax.management.remote.JMXServiceURL; +import javax.security.auth.Subject; + +/** + * Class RemoveNotificationListenerTest + */ +public class RemoveNotificationListenerTest { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(RemoveNotificationListenerTest.class.getName()); + + /** Creates a new instance of RemoveNotificationListenerTest */ + public RemoveNotificationListenerTest() { + } + + public static class SubjectAuthenticator implements JMXAuthenticator { + final Set authorized; + public SubjectAuthenticator(Subject[] authorized) { + this.authorized = new HashSet(Arrays.asList(authorized)); + } + + public Subject authenticate(Object credentials) { + if (authorized.contains(credentials)) + return (Subject)credentials; + else + throw new SecurityException("Subject not authorized: "+credentials); + } + + } + + public static interface LongtarMBean { + public void sendNotification(Object userData) + throws IOException, JMException; + } + public static class Longtar extends NotificationBroadcasterSupport + implements LongtarMBean { + public Longtar() { + super(new MBeanNotificationInfo[] { + new MBeanNotificationInfo(new String[] {"papillon"}, + "pv","M'enfin???") + }); + } + + public void sendNotification(Object userData) + throws IOException, JMException { + final Notification n = + new Notification("papillon",this,nextseq(),"M'enfin???"); + n.setUserData(userData); + System.out.println("Sending notification: "+userData); + sendNotification(n); + } + + private static synchronized long nextseq() {return ++seqnb;} + private static volatile long seqnb=0; + } + + private static final String NS = JMXNamespaces.NAMESPACE_SEPARATOR; + private static final String CS = "jmx.rmi:type=JMXConnectorServer"; + private static final String BD = "longtar:type=Longtar"; + + private static void createNamespace(MBeanServerConnection server, + String namespace, Subject creator, boolean forwarding) + throws Exception { + final MBeanServer sub = MBeanServerFactory.createMBeanServer(); + final JMXServiceURL url = new JMXServiceURL("rmi",null,0); + final Map smap = new HashMap(); + smap.put(JMXConnectorServer.AUTHENTICATOR, + new SubjectAuthenticator(new Subject[] {creator})); + final JMXConnectorServer rmi = + JMXConnectorServerFactory.newJMXConnectorServer(url,smap,null); + final ObjectName name = new ObjectName(CS); + sub.registerMBean(rmi,name); + rmi.start(); + final Map cmap = new HashMap(); + cmap.put(JMXConnector.CREDENTIALS,creator); + final Map options = new HashMap(cmap); + options.put(JMXRemoteTargetNamespace.CREATE_EVENT_CLIENT,"true"); + JMXRemoteTargetNamespace.createNamespace(server, + namespace, + rmi.getAddress(), + options + ); + server.invoke(JMXNamespaces.getNamespaceObjectName(namespace), + "connect", null,null); + } + private static void closeNamespace(MBeanServerConnection server, + String namespace) { + try { + final ObjectName hname = + JMXNamespaces.getNamespaceObjectName(namespace); + if (!server.isRegistered(hname)) + return; + final ObjectName sname = + new ObjectName(namespace+NS+CS); + if (!server.isRegistered(sname)) + return; + final JMXConnectorServerMBean cs = + JMX.newMBeanProxy(server,sname, + JMXConnectorServerMBean.class,true); + try { + cs.stop(); + } finally { + server.unregisterMBean(hname); + } + } catch (Throwable t) { + t.printStackTrace(); + } + } + + private static Subject newSubject(String[] principals) { + final Set ps = new HashSet(); + for (String p:principals) ps.add(new JMXPrincipal(p)); + return new Subject(true,ps,Collections.emptySet(),Collections.emptySet()); + } + + + public static void testSubject() throws Exception { + final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + final String a = "a"; + final String b = a + NS + "b"; + + final Subject s1 = newSubject(new String[] {"chichille"}); + final Subject s2 = newSubject(new String[] {"alambic"}); + final Subject s3 = newSubject(new String[] {"virgule"}); + final Subject s4 = newSubject(new String[] {"funeste"}); + + final JMXServiceURL url = new JMXServiceURL("rmi",null,0); + final Map smap = new HashMap(); + smap.put(JMXConnectorServer.AUTHENTICATOR, + new SubjectAuthenticator(new Subject[] {s1})); + final JMXConnectorServer rmi = + JMXConnectorServerFactory.newJMXConnectorServer(url,smap,null); + final ObjectName name = new ObjectName(CS); + server.registerMBean(rmi,name); + rmi.start(); + + try { + + final Map map = new HashMap(); + map.put(JMXConnector.CREDENTIALS,s1); + final JMXConnector c = + JMXConnectorFactory.connect(rmi.getAddress(),map); + final MBeanServerConnection mbsorig = c.getMBeanServerConnection(); + + final MBeanServerConnection mbs = + EventClientConnection.getEventConnectionFor(mbsorig,null); + + createNamespace(mbs,a,s2,true); + createNamespace(mbs,b,s3,true); + + final ObjectName longtar = new ObjectName(b+NS+BD); + + mbs.createMBean(Longtar.class.getName(),longtar); + final LongtarMBean proxy = + JMX.newMBeanProxy(mbs,longtar,LongtarMBean.class,true); + + + final BlockingQueue bbq = + new ArrayBlockingQueue(10); + final NotificationListener listener1 = new NotificationListener() { + public void handleNotification(Notification notification, + Object handback) { + System.out.println(notification.getSequenceNumber()+": "+ + notification.getMessage()); + bbq.add(notification); + } + }; + final NotificationListener listener2 = new NotificationListener() { + public void handleNotification(Notification notification, + Object handback) { + System.out.println(notification.getSequenceNumber()+": "+ + notification.getMessage()); + bbq.add(notification); + } + }; + + final NotificationEmitter ubpdalfdla = (NotificationEmitter)proxy; + try { + + // Add 1 NL, send 1 notif (1) + ubpdalfdla.addNotificationListener(listener1,null,listener1); + proxy.sendNotification(new Integer(1)); + // Thread.sleep(180000); + + // We should have 1 notif with userdata = 1 + final Notification n1 = bbq.poll(3,TimeUnit.SECONDS); + // may throw NPE => would indicate a bug. + if (((Integer)n1.getUserData()).intValue() != 1) + throw new Exception("Expected 1, got"+n1.getUserData()); + + // remove NL, send 1 notif (2) => we shouldn't receive it + ubpdalfdla.removeNotificationListener(listener1,null,listener1); + proxy.sendNotification(new Integer(2)); + + // add NL, send 1 notif (3) + ubpdalfdla.addNotificationListener(listener1,null,listener1); + proxy.sendNotification(new Integer(3)); + + // we should receive only 1 notif (3) + final Notification n3 = bbq.poll(3,TimeUnit.SECONDS); + // may throw NPE => would indicate a bug. + if (((Integer)n3.getUserData()).intValue() != 3) + throw new Exception("Expected 3, got"+n3.getUserData()); + + // remove NL, send 1 notif (4) => we shouldn't receive it. + ubpdalfdla.removeNotificationListener(listener1); + proxy.sendNotification(new Integer(4)); + + // add NL, send 1 notif (5). + ubpdalfdla.addNotificationListener(listener1,null,listener1); + proxy.sendNotification(new Integer(5)); + + // next notif in queue should be (5) + final Notification n5 = bbq.poll(3,TimeUnit.SECONDS); + // may throw NPE => would indicate a bug. + if (((Integer)n5.getUserData()).intValue() != 5) + throw new Exception("Expected 5, got"+n5.getUserData()); + + // add 2 NL, send 1 notif (6) + ubpdalfdla.addNotificationListener(listener2,null,listener2); + ubpdalfdla.addNotificationListener(listener2,null,null); + proxy.sendNotification(new Integer(6)); + + // We have 3 NL, we should receive (6) 3 times.... + final Notification n61 = bbq.poll(3,TimeUnit.SECONDS); + // may throw NPE => would indicate a bug. + if (((Integer)n61.getUserData()).intValue() != 6) + throw new Exception("Expected 6 (#1), got"+n61.getUserData()); + final Notification n62 = bbq.poll(3,TimeUnit.SECONDS); + // may throw NPE => would indicate a bug. + if (((Integer)n62.getUserData()).intValue() != 6) + throw new Exception("Expected 6 (#2), got"+n62.getUserData()); + final Notification n63 = bbq.poll(3,TimeUnit.SECONDS); + // may throw NPE => would indicate a bug. + if (((Integer)n63.getUserData()).intValue() != 6) + throw new Exception("Expected 6 (#3), got"+n63.getUserData()); + + // Remove 1 NL, send 1 notif (7) + ubpdalfdla.removeNotificationListener(listener2,null,null); + proxy.sendNotification(new Integer(7)); + + // next notifs in queue should be (7), twice... + final Notification n71 = bbq.poll(3,TimeUnit.SECONDS); + // may throw NPE => would indicate a bug. + if (((Integer)n71.getUserData()).intValue() != 7) + throw new Exception("Expected 7 (#1), got"+n71.getUserData()); + final Notification n72 = bbq.poll(3,TimeUnit.SECONDS); + // may throw NPE => would indicate a bug. + if (((Integer)n72.getUserData()).intValue() != 7) + throw new Exception("Expected 7 (#2), got"+n72.getUserData()); + + // Add 1 NL, send 1 notif (8) + ubpdalfdla.addNotificationListener(listener2,null,null); + proxy.sendNotification(new Integer(8)); + + // Next notifs in queue should be (8), 3 times. + final Notification n81 = bbq.poll(3,TimeUnit.SECONDS); + // may throw NPE => would indicate a bug. + if (((Integer)n81.getUserData()).intValue() != 8) + throw new Exception("Expected 8 (#1), got"+n81.getUserData()); + final Notification n82 = bbq.poll(3,TimeUnit.SECONDS); + // may throw NPE => would indicate a bug. + if (((Integer)n82.getUserData()).intValue() != 8) + throw new Exception("Expected 8 (#2), got"+n82.getUserData()); + final Notification n83 = bbq.poll(3,TimeUnit.SECONDS); + // may throw NPE => would indicate a bug. + if (((Integer)n83.getUserData()).intValue() != 8) + throw new Exception("Expected 8 (#3), got"+n83.getUserData()); + + // Remove 2 NL, send 1 notif (9) + ubpdalfdla.removeNotificationListener(listener2); + proxy.sendNotification(new Integer(9)); + + // Next notifs in queue should be (9), 1 time only. + final Notification n9 = bbq.poll(3,TimeUnit.SECONDS); + // may throw NPE => would indicate a bug. + if (((Integer)n9.getUserData()).intValue() != 9) + throw new Exception("Expected 9, got"+n9.getUserData()); + + // send 1 notif (10) + proxy.sendNotification(new Integer(10)); + + // Next notifs in queue should be (10), 1 time only. + final Notification n10 = bbq.poll(3,TimeUnit.SECONDS); + // may throw NPE => would indicate a bug. + if (((Integer)n10.getUserData()).intValue() != 10) + throw new Exception("Expected 10, got"+n10.getUserData()); + + ubpdalfdla.removeNotificationListener(listener1); + mbs.unregisterMBean(longtar); + + } finally { + c.close(); + } + } finally { + closeNamespace(server,b); + closeNamespace(server,a); + rmi.stop(); + } + + } + + public static void main(String[] args) throws Exception { + testSubject(); + } + +} diff --git a/test/javax/management/namespace/RoutingServerProxyTest.java b/test/javax/management/namespace/RoutingServerProxyTest.java new file mode 100644 index 000000000..c0302698c --- /dev/null +++ b/test/javax/management/namespace/RoutingServerProxyTest.java @@ -0,0 +1,399 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test RoutingServerProxyTest.java 1.6 + * @summary General RoutingServerProxyTest test. + * @author Daniel Fuchs + * @run clean RoutingServerProxyTest Wombat WombatMBean + * @compile -XDignore.symbol.file=true RoutingServerProxyTest.java + * @run build RoutingServerProxyTest Wombat WombatMBean + * @run main RoutingServerProxyTest + */ + +import com.sun.jmx.namespace.RoutingServerProxy; +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + +import javax.management.DynamicMBean; +import javax.management.InstanceNotFoundException; +import javax.management.JMException; +import javax.management.JMX; +import javax.management.MBeanInfo; +import javax.management.MBeanRegistration; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; +import javax.management.NotificationEmitter; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.StandardEmitterMBean; +import javax.management.namespace.JMXNamespace; +import javax.management.namespace.JMXNamespaces; +import javax.management.namespace.MBeanServerSupport; + +/** + * Class RoutingServerProxyTest + * + * @author Sun Microsystems, Inc. + */ +public class RoutingServerProxyTest { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(RoutingServerProxyTest.class.getName()); + + /** + * Creates a new instance of RoutingServerProxyTest + */ + public RoutingServerProxyTest() { + } + + public static class DynamicWombat extends StandardEmitterMBean { + DynamicWombat(Wombat w) throws NotCompliantMBeanException { + super(w,WombatMBean.class,w); + } + + @Override + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + final ObjectName myname = ((Wombat)getImplementation()). + preRegister(server,name); + return super.preRegister(server,myname); + } + + @Override + public void postRegister(Boolean registrationDone) { + try { + ((Wombat)getImplementation()). + postRegister(registrationDone); + } finally { + super.postRegister(registrationDone); + } + } + + @Override + public void preDeregister() throws Exception { + ((Wombat)getImplementation()). + preDeregister(); + super.preDeregister(); + + } + + @Override + public void postDeregister() { + try { + ((Wombat)getImplementation()). + postDeregister(); + } finally { + super.postDeregister(); + } + } + } + + public static class VirtualWombatHandler + extends JMXNamespace { + + public static class VirtualWombatRepository + extends MBeanServerSupport { + + final Map bush; + + VirtualWombatRepository(Map bush) { + this.bush = bush; + } + + @Override + protected Set getNames() { + return bush.keySet(); + } + + @Override + public DynamicMBean getDynamicMBeanFor(ObjectName name) + throws InstanceNotFoundException { + final DynamicMBean mb = bush.get(name); + if (mb == null) { + throw new InstanceNotFoundException(String.valueOf(name)); + } + return mb; + } + + @Override + public NotificationEmitter getNotificationEmitterFor( + ObjectName name) throws InstanceNotFoundException { + DynamicMBean mbean = getDynamicMBeanFor(name); + if (mbean instanceof NotificationEmitter) { + return (NotificationEmitter) mbean; + } + return null; + } + } + VirtualWombatRepository bush; + + VirtualWombatHandler(Map bush) { + this(new VirtualWombatRepository(Collections.synchronizedMap(bush))); + } + + private VirtualWombatHandler(VirtualWombatRepository repository) { + super(repository); + bush = repository; + } + + @Override + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + final ObjectName myname = super.preRegister(server, name); + return myname; + } + + @Override + public void postRegister(Boolean registrationDone) { + if (!registrationDone.booleanValue()) { + return; + } + final MBeanServer me = JMXNamespaces.narrowToNamespace(getMBeanServer(), + getObjectName().getDomain()); + for (Map.Entry e : bush.bush.entrySet()) { + final DynamicMBean obj = e.getValue(); + try { + if (obj instanceof MBeanRegistration) { + ((MBeanRegistration) obj).preRegister(me, e.getKey()); + } + } catch (Exception x) { + System.err.println("preRegister failed for " + + e.getKey() + ": " + x); + bush.bush.remove(e.getKey()); + } + } + for (Map.Entry e : bush.bush.entrySet()) { + final Object obj = e.getValue(); + if (obj instanceof MBeanRegistration) { + ((MBeanRegistration) obj).postRegister(registrationDone); + } + } + } + + @Override + public void preDeregister() throws Exception { + for (Map.Entry e : bush.bush.entrySet()) { + final Object obj = e.getValue(); + if (obj instanceof MBeanRegistration) { + ((MBeanRegistration) obj).preDeregister(); + } + } + } + + @Override + public void postDeregister() { + for (Map.Entry e : bush.bush.entrySet()) { + final Object obj = e.getValue(); + if (obj instanceof MBeanRegistration) { + ((MBeanRegistration) obj).postDeregister(); + } + } + } + } + + public static ObjectName getWombatName(String name) + throws MalformedObjectNameException { + return ObjectName.getInstance("australian.bush:type=Wombat,name="+name); + } + + public static ObjectName addDir(String dir, ObjectName name) + throws MalformedObjectNameException { + return name.withDomain( + dir+JMXNamespaces.NAMESPACE_SEPARATOR+ name.getDomain()); + } + + public static void simpleTest() + throws JMException, IOException { + final MBeanServer master = MBeanServerFactory.createMBeanServer(); + final MBeanServer agent1 = MBeanServerFactory.createMBeanServer(); + final Wombat w1 = new Wombat(); + final Wombat w2 = new Wombat(); + final Wombat w3 = new Wombat(); + final Map wombats = + new ConcurrentHashMap(); + wombats.put(getWombatName("LittleWombat"), + new DynamicWombat(w2)); + wombats.put(getWombatName("BigWombat"), + new DynamicWombat(w3)); + final Wombat w4 = new Wombat(); + final Wombat w5 = new Wombat(); + + final JMXNamespace agent2 = + new VirtualWombatHandler(wombats); + agent1.registerMBean(w4,getWombatName("LittleWombat")); + master.registerMBean(w1,getWombatName("LittleWombat")); + master.registerMBean(new JMXNamespace(agent1), + JMXNamespaces.getNamespaceObjectName("south.east")); + master.registerMBean(agent2, + JMXNamespaces.getNamespaceObjectName("north")); + master.registerMBean(w5,addDir("south.east", + getWombatName("GrandWombat"))); + + MBeanServer se = null; + + try { + se = JMXNamespaces.narrowToNamespace(master,"south.easht"); + } catch (Exception x) { + System.out.println("Caught expected exception: "+x); + } + if (se != null) + throw new RuntimeException("Expected exception for "+ + "cd(south.easht)"); + se = JMXNamespaces.narrowToNamespace(master,"south.east"); + + MBeanServer nth = JMXNamespaces.narrowToNamespace(master,"north"); + + final ObjectName ln = getWombatName("LittleWombat"); + MBeanInfo mb1 = master.getMBeanInfo(ln); + MBeanInfo mb2 = se.getMBeanInfo(ln); + MBeanInfo mb3 = nth.getMBeanInfo(ln); + + final WombatMBean grand = JMX.newMBeanProxy(se, + getWombatName("GrandWombat"),WombatMBean.class); + final WombatMBean big = JMX.newMBeanProxy(nth, + getWombatName("BigWombat"),WombatMBean.class); + grand.getCaption(); + big.getCaption(); + grand.setCaption("I am GrandWombat"); + big.setCaption("I am BigWombat"); + + final WombatMBean grand2 = + JMX.newMBeanProxy(master,addDir("south.east", + getWombatName("GrandWombat")),WombatMBean.class); + final WombatMBean big2 = + JMX.newMBeanProxy(master,addDir("north", + getWombatName("BigWombat")),WombatMBean.class); + if (!"I am GrandWombat".equals(grand2.getCaption())) + throw new RuntimeException("bad caption for GrandWombat"+ + grand2.getCaption()); + if (!"I am BigWombat".equals(big2.getCaption())) + throw new RuntimeException("bad caption for BigWombat"+ + big2.getCaption()); + + + final Set northWombats = + nth.queryMBeans(ObjectName.WILDCARD,null); + final Set seWombats = + se.queryMBeans(ObjectName.WILDCARD,null); + if (!northWombats.equals( + agent2.getSourceServer().queryMBeans(ObjectName.WILDCARD,null))) { + throw new RuntimeException("Bad Wombat census in northern territory: got " + +northWombats+", expected "+ + agent2.getSourceServer(). + queryMBeans(ObjectName.WILDCARD,null)); + } + if (!seWombats.equals( + agent1.queryMBeans(ObjectName.WILDCARD,null))) { + throw new RuntimeException("Bad Wombat census in south east: got " + +seWombats+", expected "+ + agent1. + queryMBeans(ObjectName.WILDCARD,null)); + } + + final MBeanServer supermaster = MBeanServerFactory.createMBeanServer(); + supermaster.registerMBean(new JMXNamespace(master), + JMXNamespaces.getNamespaceObjectName("australia")); + final MBeanServer proxymaster = + JMXNamespaces.narrowToNamespace(supermaster,"australia"); + final MBeanServer sem = + JMXNamespaces.narrowToNamespace(proxymaster,"south.east"); + final MBeanServer nthm = + JMXNamespaces.narrowToNamespace(proxymaster,"north"); + final Set northWombats2 = + nthm.queryMBeans(ObjectName.WILDCARD,null); + final Set seWombats2 = + sem.queryMBeans(ObjectName.WILDCARD,null); + if (!northWombats2.equals( + agent2.getSourceServer().queryMBeans(ObjectName.WILDCARD,null))) { + throw new RuntimeException("Bad Wombat census in " + + "Australia // North"); + } + if (!seWombats2.equals( + agent1.queryMBeans(ObjectName.WILDCARD,null))) { + throw new RuntimeException("Bad Wombat census in " + + "Australia // South East"); + } + final WombatMBean grand3 = + JMX.newMBeanProxy(supermaster, + addDir("australia//south.east", + getWombatName("GrandWombat")),WombatMBean.class); + final WombatMBean big3 = + JMX.newMBeanProxy(supermaster,addDir("australia//north", + getWombatName("BigWombat")),WombatMBean.class); + if (!"I am GrandWombat".equals(grand3.getCaption())) + throw new RuntimeException("bad caption for " + + "australia//south.east//GrandWombat"+ + grand3.getCaption()); + if (!"I am BigWombat".equals(big3.getCaption())) + throw new RuntimeException("bad caption for " + + "australia//north//BigWombat"+ + big3.getCaption()); + final WombatMBean grand4 = + JMX.newMBeanProxy(sem, + getWombatName("GrandWombat"),WombatMBean.class); + final WombatMBean big4 = + JMX.newMBeanProxy(nthm, + getWombatName("BigWombat"),WombatMBean.class); + if (!"I am GrandWombat".equals(grand4.getCaption())) + throw new RuntimeException("bad caption for " + + "[australia//south.east//] GrandWombat"+ + grand4.getCaption()); + if (!"I am BigWombat".equals(big4.getCaption())) + throw new RuntimeException("bad caption for " + + "[australia//north//] BigWombat"+ + big4.getCaption()); + + if (!(nthm instanceof RoutingServerProxy)) + throw new AssertionError("expected RoutingServerProxy for nthm"); + if (!(sem instanceof RoutingServerProxy)) + throw new AssertionError("expected RoutingServerProxy for sem"); + + if (!"australia//north".equals(( + (RoutingServerProxy)nthm).getSourceNamespace())) + throw new RuntimeException("north territory should be in australia"); + if (!"australia//south.east".equals(( + (RoutingServerProxy)sem).getSourceNamespace())) + throw new RuntimeException("south east territory should be in australia"); + + } + + public static void main(String[] args) { + try { + simpleTest(); + } catch (Exception x) { + System.err.println("SimpleTest failed: "+x); + throw new RuntimeException(x); + } + } + +} diff --git a/test/javax/management/namespace/SerialParamProcessorTest.java b/test/javax/management/namespace/SerialParamProcessorTest.java new file mode 100644 index 000000000..26dd77572 --- /dev/null +++ b/test/javax/management/namespace/SerialParamProcessorTest.java @@ -0,0 +1,571 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * + * @test SerialParamProcessorTest.java 1.8 + * @summary General SerialParamProcessorTest test. + * @author Daniel Fuchs + * @run clean SerialParamProcessorTest Wombat WombatMBean + * @compile -XDignore.symbol.file=true SerialParamProcessorTest.java + * @run build SerialParamProcessorTest Wombat WombatMBean + * @run main SerialParamProcessorTest + */ + +import com.sun.jmx.namespace.serial.RewritingProcessor; +import java.beans.ConstructorProperties; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import javax.management.AttributeChangeNotification; +import javax.management.AttributeList; +import javax.management.JMException; +import javax.management.Notification; +import javax.management.ObjectName; +import javax.management.StandardMBean; + +/** + * Class SerialParamProcessorTest + * + * @author Sun Microsystems, Inc. + */ +public class SerialParamProcessorTest { + + /** + * Creates a new instance of SerialParamProcessorTest + */ + public SerialParamProcessorTest() { + } + + public static class MyCompositeData implements Serializable { + private static final long serialVersionUID = 3186492415099133506L; + public MyCompositeData(ObjectName foobar,ObjectName absolute, + long count, String name) { + this(foobar,absolute,count,name,new ObjectName[]{foobar,absolute}); + } + @ConstructorProperties(value={"fooBar","absolute","count","name", + "allNames"}) + public MyCompositeData(ObjectName foobar,ObjectName absolute, + long count, String name, ObjectName[] allnames) { + this.foobar = foobar; + this.absolute = absolute; + this.count = count; + this.name = name; + this.allnames = allnames; + } + ObjectName foobar,absolute,allnames[]; + long count; + String name; + public ObjectName getFooBar() { + return foobar; + } + public ObjectName getAbsolute() { + return absolute; + } + public ObjectName[] getAllNames() { + return allnames; + } + public long getCount() { + return count; + } + public String getName() { + return name; + } + private Object[] toArray() { + final Object[] props = { + getName(),getFooBar(),getAbsolute(),getAllNames(),getCount() + }; + return props; + } + @Override + public boolean equals(Object o) { + if (o instanceof MyCompositeData) + return Arrays.deepEquals(toArray(), + ((MyCompositeData)o).toArray()); + return false; + } + @Override + public int hashCode() { + return Arrays.deepHashCode(toArray()); + } + } + + public static interface MyMXBean { + public Map getAll(); + public MyCompositeData lookup(String name); + public void put(String name, MyCompositeData data); + public MyCompositeData remove(String name); + } + + public static class My implements MyMXBean { + Map datas = + new HashMap(); + public Map getAll() { + return datas; + } + public MyCompositeData lookup(String name) { + return datas.get(name); + } + public void put(String name, MyCompositeData data) { + datas.put(name,data); + } + public MyCompositeData remove(String name) { + return datas.remove(name); + } + } + + public static class BandicootClass implements Serializable { + private static final long serialVersionUID = -5494055748633966355L; + public final Object gloups; + public BandicootClass(Object gloups) { + this.gloups = gloups; + } + private Object[] toArray() { + final Object[] one = {gloups}; + return one; + } + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BandicootClass)) return false; + final Object[] one = {gloups}; + return Arrays.deepEquals(toArray(),((BandicootClass)obj).toArray()); + } + @Override + public int hashCode() { + if (gloups == null) return 0; + return Arrays.deepHashCode(toArray()); + } + } + + // Need this to override equals. + public static class BandicootNotification extends Notification { + private static final long serialVersionUID = 664758643764049001L; + public BandicootNotification(String type, Object source, long seq) { + super(type,source,seq,0L,""); + } + private Object[] toArray() { + final Object[] vals = {getMessage(),getSequenceNumber(), + getSource(),getTimeStamp(),getType(),getUserData()}; + return vals; + } + @Override + public boolean equals(Object o) { + if (!(o instanceof BandicootNotification)) return false; + return Arrays.deepEquals(toArray(), + ((BandicootNotification)o).toArray()); + } + @Override + public int hashCode() { + return Arrays.deepHashCode(toArray()); + } + + } + + // Need this to override equals. + public static class BandicootAttributeChangeNotification + extends AttributeChangeNotification { + private static final long serialVersionUID = -1392435607144396125L; + public BandicootAttributeChangeNotification(Object source, + long seq, long time, String msg, String name, String type, + Object oldv, Object newv) { + super(source,seq,time,msg,name,type,oldv,newv); + } + private Object[] toArray() { + final Object[] vals = {getMessage(),getSequenceNumber(), + getSource(),getTimeStamp(),getType(),getUserData(), + getAttributeName(), getAttributeType(),getNewValue(), + getOldValue()}; + return vals; + } + @Override + public boolean equals(Object o) { + if (!(o instanceof BandicootAttributeChangeNotification)) + return false; + return Arrays.deepEquals(toArray(), + ((BandicootAttributeChangeNotification)o).toArray()); + } + @Override + public int hashCode() { + return Arrays.deepHashCode(toArray()); + } + @Override + public String toString() { + final StringBuilder b = new StringBuilder(); + b.append(this.getClass().getName()).append(": "); + b.append("[type=").append(getType()).append("]"); + b.append("[source=").append(getSource()).append("]"); + b.append("[message=").append(getMessage()).append("]"); + b.append("[sequence=").append(getSequenceNumber()).append("]"); + + b.append("[attribute=").append(getAttributeName()).append("]"); + b.append("[class=").append(getAttributeType()).append("]"); + b.append("[oldvalue=").append(getOldValue()).append("]"); + b.append("[newvalue=").append(getNewValue()).append("]"); + + b.append("[time=").append(getTimeStamp()).append("]"); + b.append("[data=").append(getUserData()).append("]"); + return b.toString(); + } + } + + private static void addToList(Object[] foos, List foolist) { + final ArrayList fal = new ArrayList(foos.length); + for (Object f : foos) { + if (f.getClass().isArray()) { + foolist.add(new BandicootClass(f)); + fal.add(new BandicootClass(f)); + } else { + foolist.add(f); + fal.add(f); + } + } + foolist.add(new BandicootClass(foos)); + foolist.add(fal); + } + + public static void testSerial(String msg, Object foo, Object bar, + RewritingProcessor procForFoo, + RewritingProcessor procForBar, List foolist, + List barlist, boolean recurse) { + System.err.println(msg+" Testing serial - "+foo.getClass().getName()); + final Object bar1 = procForFoo.rewriteInput(foo); + final Object foo1 = procForFoo.rewriteOutput(bar); + final Object bar2 = procForFoo.rewriteInput(foo1); + final Object foo2 = procForFoo.rewriteOutput(bar1); + + final Object bar3 = procForBar.rewriteOutput(foo); + final Object foo3 = procForBar.rewriteInput(bar); + final Object bar4 = procForBar.rewriteOutput(foo3); + final Object foo4 = procForBar.rewriteInput(bar3); + + final Object bar5 = procForFoo.rewriteInput(foo3); + final Object foo5 = procForFoo.rewriteOutput(bar3); + + final Object bar6 = procForBar.rewriteOutput(foo1); + final Object foo6 = procForBar.rewriteInput(bar1); + + final Object[] foos = {foo, foo1, foo2, foo3, foo4, foo5, foo6}; + final Object[] bars = {bar, bar1, bar2, bar3, bar4, bar5, bar6}; + + final Object[] foot = { foo }; + final Object[] bart = { bar }; + for (int j=1;j foolist = new LinkedList(); + final List barlist = new LinkedList(); + for (Object[] row : objects) { + i++; + Object foo = row[0]; + Object bar = row[1]; + String msg1 = "[" +foo.getClass().getName() + "] step " + + i +": "; + + testSerial(msg1,foo,bar,procForFoo,procForBar,foolist,barlist,true); + + final BandicootClass kfoo = new BandicootClass(foo); + final BandicootClass kbar = new BandicootClass(bar); + + String msg2 = "[" +kfoo.getClass().getName() + "] step " + + i +": "; + testSerial(msg2,kfoo,kbar,procForFoo,procForBar,foolist,barlist,true); + } + String msg31 = "foo[] and bar[]: "; + testSerial(msg31,foolist.toArray(),barlist.toArray(), + procForFoo,procForBar,foolist,barlist,false); + + String msg3 = "foolist and barlist: "; + testSerial(msg3,new LinkedList(foolist), + new LinkedList(barlist), + procForFoo,procForBar,foolist,barlist,false); + + final BandicootClass kfoolist = new BandicootClass(foolist); + final BandicootClass kbarlist = new BandicootClass(barlist); + String msg4 = "kfoolist and kbarlist: "; + testSerial(msg4,kfoolist,kbarlist,procForFoo,procForBar,foolist,barlist,false); + } + + /** + * The idea of this method is to convert {@code foo} things into + * {@code bar} things... + * @param foo the string to replace. + * @param bar the replacement for {@code foo} + * ({@code foo} becomes {@code bar}). + * @param sfoo a string that may contain {@code foo}, that will be embedded + * in non-replaceable parts of the domain in order to attempt to + * trick the replacement logic. + * @param sbar a string that may contain {@code bar}, that will be embedded + * in non-replaceable parts of the domain in order to attempt to + * trick the replacement logic. + **/ + public static void doSerialTest(String foo, String bar, String sfoo, + String sbar) { + try { + final RewritingProcessor procForFoo = RewritingProcessor. + newRewritingProcessor(foo,bar); + final RewritingProcessor procForBar =RewritingProcessor. + newRewritingProcessor(bar,foo); + final String foop = (foo.isEmpty())?foo:foo+"//"; + final String pfoo = (foo.isEmpty())?foo:"//"+foo; + final String barp = (bar.isEmpty())?bar:bar+"//"; + final String pbar = (bar.isEmpty())?bar:"//"+bar; + final String sfoop = (sfoo.isEmpty())?sfoo:sfoo+"//"; + final String psfoo = (sfoo.isEmpty())?sfoo:"//"+sfoo; + final String sbarp = (sbar.isEmpty())?sbar:sbar+"//"; + final String psbar = (sbar.isEmpty())?sbar:"//"+sbar; + + // A trick to avoid writing Open Data by hand... + final My tricks = new My(); + + // A treat to automagically convert trick things into Open Data. + final StandardMBean treats = + new StandardMBean(tricks,MyMXBean.class,true); + + // datas[i][0] is expected to be transformed in datas[i][1] + // + final MyCompositeData[][] datas = { + { // this foo thing: + new MyCompositeData(new ObjectName(foop+sbarp+"x:y=z"), + new ObjectName("//"+foop+sbarp+"x:y=z"),1,sfoop+sbarp+"foobar"), + // should be transformed into this bar thing: + new MyCompositeData(new ObjectName(barp+sbarp+"x:y=z"), + new ObjectName("//"+foop+sbarp+"x:y=z"),1,sfoop+sbarp+"foobar"), + }, + { // this foo thing: + new MyCompositeData(new ObjectName(foop+sfoop+"x:y=z"), + new ObjectName("//"+foop+sfoop+"x:y=z"),1,sfoop+sbarp+"barfoo"), + // should be transformed into this bar thing: + new MyCompositeData(new ObjectName(barp+sfoop+"x:y=z"), + new ObjectName("//"+foop+sfoop+"x:y=z"),1,sfoop+sbarp+"barfoo"), + } + }; + + // objects[i][0] is expected to be transformed into objects[i][1] + // + final Object[][] objects = new Object[][] { + {new Long(1), new Long(1)}, + { + new ObjectName(foop+sbarp+"x:y=z"), + new ObjectName(barp+sbarp+"x:y=z") + }, + { + new ObjectName(foop+sfoop+"x:y=z"), + new ObjectName(barp+sfoop+"x:y=z") + }, + { + new ObjectName("//"+foop+sbarp+"x:y=z"), + new ObjectName("//"+foop+sbarp+"x:y=z"), + }, + { + new ObjectName("//"+foop+sfoop+"x:y=z"), + new ObjectName("//"+foop+sfoop+"x:y=z") + }, + { + foop+sbarp+"x:y=z",foop+sbarp+"x:y=z" + }, + { + foop+sfoop+"x:y=z",foop+sfoop+"x:y=z" + }, + { + barp+sbarp+"x:y=z",barp+sbarp+"x:y=z" + }, + { + barp+sfoop+"x:y=z",barp+sfoop+"x:y=z" + }, + { + new BandicootNotification("test",new ObjectName(foop+sfoop+"x:y=z"),1L), + new BandicootNotification("test",new ObjectName(barp+sfoop+"x:y=z"),1L), + }, + { + new BandicootNotification("test",new ObjectName("//"+foop+sfoop+"x:y=z"),2L), + new BandicootNotification("test",new ObjectName("//"+foop+sfoop+"x:y=z"),2L), + }, + { + new BandicootAttributeChangeNotification( + new ObjectName(foop+sfoop+"x:y=z"),1L,2L,"blah","attrname", + ObjectName.class.getName(), + new ObjectName(foop+sfoop+"x:y=old"), + new ObjectName(foop+sfoop+"x:y=new")), + new BandicootAttributeChangeNotification( + new ObjectName(barp+sfoop+"x:y=z"),1L,2L,"blah","attrname", + ObjectName.class.getName(), + new ObjectName(barp+sfoop+"x:y=old"), + new ObjectName(barp+sfoop+"x:y=new")), + }, + { + new BandicootAttributeChangeNotification( + new ObjectName("//"+foop+sfoop+"x:y=z"),1L,2L,"blah","attrname", + ObjectName.class.getName(), + new ObjectName("//"+foop+sfoop+"x:y=old"), + new ObjectName(foop+sfoop+"x:y=new")), + new BandicootAttributeChangeNotification( + new ObjectName("//"+foop+sfoop+"x:y=z"),1L,2L,"blah","attrname", + ObjectName.class.getName(), + new ObjectName("//"+foop+sfoop+"x:y=old"), + new ObjectName(barp+sfoop+"x:y=new")), + } + }; + + // List that will merge datas & objects & datas converted to open + // types... + // + final List list = new ArrayList(); + + // Add all objects... + // + list.addAll(Arrays.asList(objects)); + + // Build Map with datas[i][0] (cfoo) + // + for (int i=0;i to TabularData + // (foo things) + final Object cfoo = treats.getAttribute("All"); + final AttributeList afoo = treats.getAttributes(new String[] {"All"}); + + // Build Map with datas[i][1] (cbar) + // + for (int i=0;i to TabularData + // (bar things) + final Object cbar = treats.getAttribute("All"); + final AttributeList abar = treats.getAttributes(new String[] {"All"}); + + // Add all datas to list + for (int i=0;i unmapped = new HashSet(); + final static Set mapped = new HashSet(); + + /** + * For each method define in one of the interfaces intf, tries + * to find a corresponding method in the reference class ref, where + * the method in ref has the same name, and takes an additional + * ObjectName as first parameter. + * + * So for instance, if ref is MBeanServer and intf is {DynamicMBean} + * the result map is: + * DynamicMBean.getAttribute -> MBeanServer.getAttribute + * DynamicMBean.setAttribute -> MBeanServer.setAttribute + * etc... + * If a method was mapped, it is additionally added to 'mapped' + * If a method couldn't be mapped, it is added to 'unmmapped'. + * In our example above, DynamicMBean.getNotificationInfo will end + * up in 'unmapped'. + * + * @param ref The reference class - to which calls will be forwarded + * with an additional ObjectName parameter inserted. + * @param intf The proxy interface classes - for which we must find an + * equivalent in 'ref' + * @return A map mapping the methods from intfs to the method of ref. + */ + static Map makeMapFor(Class ref, Class... intf) { + final Map map = new HashMap(); + for (Class clazz : intf) { + for (Method m : clazz.getMethods()) { + try { + final Method m2 = + ref.getMethod(m.getName(), + concat(ObjectName.class,m.getParameterTypes())); + map.put(m,m2); + mapped.add(m); + } catch (Exception x) { + unmapped.add(m); + } + } + } + return map; + } + + /** + * Tries to map all methods from DynamicMBean.class and + * NotificationEmitter.class to their equivalent in MBeanServer. + * This should be all the methods except + * DynamicMBean.getNotificationInfo. + */ + static final Map mbeanmap = + makeMapFor(MBeanServer.class,DynamicMBean.class, + NotificationEmitter.class); + /** + * Tries to map all methods from DynamicMBean.class and + * NotificationEmitter.class to an equivalent in DynamicWrapper. + * This time only DynamicMBean.getNotificationInfo will be mapped. + */ + static final Map selfmap = + makeMapFor(DynamicWrapper.class,DynamicMBean.class, + NotificationEmitter.class); + + /** + * Now check that we have mapped all methods. + */ + static { + unmapped.removeAll(mapped); + if (unmapped.size() > 0) + throw new ExceptionInInitializerError("Couldn't map "+ unmapped); + } + + /** + * The wrapped MBeanServer to which everything is delegated. + */ + private final MBeanServer server; + + /** + * The name of the MBean we're proxying. + */ + private final ObjectName name; + DynamicWrapper(MBeanServer server, ObjectName name) { + this.server=server; + this.name=name; + } + + /** + * Creates a new proxy for the given MBean. Implements + * NotificationEmitter/NotificationBroadcaster if the proxied + * MBean also does. + * @param name the name of the proxied MBean + * @param server the wrapped server + * @return a DynamicMBean proxy + * @throws javax.management.InstanceNotFoundException + */ + public static DynamicMBean newProxy(ObjectName name, MBeanServer server) + throws InstanceNotFoundException { + if (server.isInstanceOf(name, + NotificationEmitter.class.getName())) { + // implements NotificationEmitter + return (DynamicMBean) + Proxy.newProxyInstance( + DynamicWrapper.class.getClassLoader(), + new Class[] {NotificationEmitter.class, + DynamicMBean.class}, + new DynamicWrapper(server, name)); + } + if (server.isInstanceOf(name, + NotificationBroadcaster.class.getName())) { + // implements NotificationBroadcaster + return (DynamicMBean) + Proxy.newProxyInstance( + DynamicWrapper.class.getClassLoader(), + new Class[] {NotificationBroadcaster.class, + DynamicMBean.class}, + new DynamicWrapper(server, name)); + } + // Only implements DynamicMBean. + return (DynamicMBean) + Proxy.newProxyInstance( + DynamicWrapper.class.getClassLoader(), + new Class[] {DynamicMBean.class}, + new DynamicWrapper(server, name)); + } + + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + // Look for a method on this class (takes precedence) + final Method self = selfmap.get(method); + if (self != null) + return call(this,self,concat(name,args)); + + // no method found on this class, look for the same method + // on the wrapped MBeanServer + final Method mbean = mbeanmap.get(method); + if (mbean != null) + return call(server,mbean,concat(name,args)); + + // This isn't a method that can be forwarded to MBeanServer. + // If it's a method from Object, call it on this. + if (method.getDeclaringClass().equals(Object.class)) + return call(this,method,args); + throw new NoSuchMethodException(method.getName()); + } + + // Call a method using reflection, unwraps invocation target exceptions + public Object call(Object handle, Method m, Object[] args) + throws Throwable { + try { + return m.invoke(handle, args); + } catch (InvocationTargetException x) { + throw x.getCause(); + } + } + + // this method is called when DynamicMBean.getNotificationInfo() is + // called. This is the method that should be mapped in + // 'selfmap' + public MBeanNotificationInfo[] getNotificationInfo(ObjectName name) + throws JMException { + return server.getMBeanInfo(name).getNotifications(); + } + } + + /** + * Just so that we can call the same test twice but with two + * different implementations of VirtualMBeanServerSupport. + */ + public static interface MBeanServerWrapperFactory { + public MBeanServer wrapMBeanServer(MBeanServer wrapped); + } + + /** + * A VirtualMBeanServerSupport that wrapps an MBeanServer and does not + * use VirtualEventManager. + */ + public static class VirtualMBeanServerTest + extends MBeanServerSupport { + + final MBeanServer wrapped; + + public VirtualMBeanServerTest(MBeanServer wrapped) { + this.wrapped=wrapped; + } + + @Override + public DynamicMBean getDynamicMBeanFor(final ObjectName name) + throws InstanceNotFoundException { + if (wrapped.isRegistered(name)) + return DynamicWrapper.newProxy(name,wrapped); + throw new InstanceNotFoundException(String.valueOf(name)); + } + + @Override + protected Set getNames() { + return wrapped.queryNames(null, null); + } + + public final static MBeanServerWrapperFactory factory = + new MBeanServerWrapperFactory() { + + public MBeanServer wrapMBeanServer(MBeanServer wrapped) { + return new VirtualMBeanServerTest(wrapped); + } + @Override + public String toString() { + return VirtualMBeanServerTest.class.getName(); + } + }; + } + + /** + * A VirtualMBeanServerSupport that wrapps an MBeanServer and + * uses a VirtualEventManager. + */ + public static class VirtualMBeanServerTest2 + extends VirtualMBeanServerTest { + + final EventSubscriber sub; + final NotificationListener nl; + final VirtualEventManager mgr; + + /** + * We use an EventSubscriber to subscribe for all notifications from + * the wrapped MBeanServer, and publish them through a + * VirtualEventManager. Not a very efficient way of doing things. + * @param wrapped + */ + public VirtualMBeanServerTest2(MBeanServer wrapped) { + super(wrapped); + this.sub = EventSubscriber.getEventSubscriber(wrapped); + this.mgr = new VirtualEventManager(); + this.nl = new NotificationListener() { + public void handleNotification(Notification notification, Object handback) { + mgr.publish((ObjectName)notification.getSource(), notification); + } + }; + try { + sub.subscribe(ObjectName.WILDCARD, nl, null, null); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new IllegalStateException("can't subscribe for notifications!"); + } + } + + @Override + public NotificationEmitter + getNotificationEmitterFor(ObjectName name) + throws InstanceNotFoundException { + final DynamicMBean mbean = getDynamicMBeanFor(name); + if (mbean instanceof NotificationEmitter) + return mgr.getNotificationEmitterFor(name); + return null; + } + + public final static MBeanServerWrapperFactory factory = + new MBeanServerWrapperFactory() { + + public MBeanServer wrapMBeanServer(MBeanServer wrapped) { + return new VirtualMBeanServerTest2(wrapped); + } + @Override + public String toString() { + return VirtualMBeanServerTest2.class.getName(); + } + }; + } + + + public static void test(MBeanServerWrapperFactory factory) throws Exception { + final MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + + // names[] are NotificationEmitters + final ObjectName[] emitters = new ObjectName[2]; + // shields[] have been shielded by wrapping them in a StandardMBean, + // so although the resource is an MBean that implements + // NotificationEmitter, the registered MBean (the wrapper) doesn't. + final ObjectName[] shielded = new ObjectName[2]; + + final List registered = new ArrayList(4); + + try { + // register two MBeans before wrapping + server.registerMBean(new Wombat(), + emitters[0] = new ObjectName("bush:type=Wombat,name=wom")); + registered.add(emitters[0]); + + // we shield the second MBean in a StandardMBean so that it does + // not appear as a NotificationEmitter. + server.registerMBean( + new StandardMBean(new Wombat(), WombatMBean.class), + shielded[0] = new ObjectName("bush:type=Wombat,name=womshield")); + registered.add(shielded[0]); + + final MBeanServer vserver = factory.wrapMBeanServer(server); + + // register two other MBeans after wrapping + server.registerMBean(new Wombat(), + emitters[1] = new ObjectName("bush:type=Wombat,name=bat")); + registered.add(emitters[1]); + + // we shield the second MBean in a StandardMBean so that it does + // not appear as a NotificationEmitter. + server.registerMBean( + new StandardMBean(new Wombat(), WombatMBean.class), + shielded[1] = new ObjectName("bush:type=Wombat,name=batshield")); + registered.add(shielded[1]); + + // Call test with this config - we have two wombats who broadcast + // notifs (emitters) and two wombats who don't (shielded). + test(vserver, emitters, shielded); + + System.out.println("*** Test passed for: " + factory); + } finally { + // Clean up the platform mbean server for the next test... + for (ObjectName n : registered) { + try { + server.unregisterMBean(n); + } catch (Exception x) { + x.printStackTrace(); + } + } + } + } + + /** + * Perform the actual test. + * @param vserver A virtual MBeanServerSupport implementation + * @param emitters Names of NotificationBroadcaster MBeans + * @param shielded Names of non NotificationBroadcaster MBeans + * @throws java.lang.Exception + */ + public static void test(MBeanServer vserver, ObjectName[] emitters, + ObjectName[] shielded) throws Exception { + + // To catch exception in NotificationListener + final List fail = new CopyOnWriteArrayList(); + + // A queue of received notifications + final BlockingQueue notifs = + new ArrayBlockingQueue(50); + + // A notification listener that puts the notification it receives + // in the queue. + final NotificationListener handler = new NotificationListener() { + + public void handleNotification(Notification notification, + Object handback) { + try { + notifs.put(notification); + } catch (Exception x) { + fail.add(x); + } + } + }; + + // A list of attribute names for which we might receive an + // exception. If an exception is received when getting these + // attributes - the test will not fail. + final List exceptions = Arrays.asList( new String[] { + "UsageThresholdCount","UsageThreshold","UsageThresholdExceeded", + "CollectionUsageThresholdCount","CollectionUsageThreshold", + "CollectionUsageThresholdExceeded" + }); + + // This is just a sanity check. Get all attributes of all MBeans. + for (ObjectName n : vserver.queryNames(null, null)) { + final MBeanInfo m = vserver.getMBeanInfo(n); + for (MBeanAttributeInfo mba : m.getAttributes()) { + // System.out.println(n+":"); + Object val; + try { + val = vserver.getAttribute(n, mba.getName()); + } catch (Exception x) { + // only accept exception for those attributes that + // have a valid reason to fail... + if (exceptions.contains(mba.getName())) val = x; + else throw new Exception("Failed to get " + + mba.getName() + " from " + n,x); + } + // System.out.println("\t "+mba.getName()+": "+ val); + } + } + + // The actual tests. Register for notifications with notif emitters + for (ObjectName n : emitters) { + vserver.addNotificationListener(n, handler, null, n); + } + + // Trigger the emission of notifications, check that we received them. + for (ObjectName n : emitters) { + vserver.setAttribute(n, + new Attribute("Caption","I am a new wombat!")); + final Notification notif = notifs.poll(4, TimeUnit.SECONDS); + if (!notif.getSource().equals(n)) + throw new Exception("Bad source for "+ notif); + if (fail.size() > 0) + throw new Exception("Failed to handle notif",fail.remove(0)); + } + + // Check that we didn't get more notifs than expected + if (notifs.size() > 0) + throw new Exception("Extra notifications in queue: "+notifs); + + // Check that if the MBean doesn't exist, we get InstanceNotFound. + try { + vserver.addNotificationListener(new ObjectName("toto:toto=toto"), + handler, null, null); + throw new Exception("toto:toto=toto doesn't throw INFE"); + } catch (InstanceNotFoundException x) { + System.out.println("Received "+x+" as expected."); + } + + // For those MBeans that shouldn't be NotificationEmitters, check that + // we get IllegalArgumentException + for (ObjectName n : shielded) { + try { + vserver.addNotificationListener(n, handler, null, n); + } catch (RuntimeOperationsException x) { + System.out.println("Received "+x+" as expected."); + System.out.println("Cause is: "+x.getCause()); + if (!(x.getCause() instanceof IllegalArgumentException)) + throw new Exception("was expecting IllegalArgumentException cause. Got "+x.getCause(),x); + } + } + + // Sanity check. Remove our listeners. + for (ObjectName n : emitters) { + vserver.removeNotificationListener(n, handler, null, n); + } + + // That's it. + // Sanity check: we shouldn't have received any new notif. + if (notifs.size() > 0) + throw new Exception("Extra notifications in queue: "+notifs); + // The NotifListener shouldn't have logged any new exception. + if (fail.size() > 0) + throw new Exception("Failed to handle notif",fail.remove(0)); + } + + public static void main(String[] args) throws Exception { + // test with a regular MBeanServer (no VirtualMBeanServerSupport) + final MBeanServerWrapperFactory identity = + new MBeanServerWrapperFactory() { + public MBeanServer wrapMBeanServer(MBeanServer wrapped) { + return wrapped; + } + }; + test(identity); + // test with no EventManager + test(VirtualMBeanServerTest.factory); + // test with VirtualEventManager + test(VirtualMBeanServerTest2.factory); + } +} diff --git a/test/javax/management/namespace/VirtualMBeanTest.java b/test/javax/management/namespace/VirtualMBeanTest.java new file mode 100644 index 000000000..85860df34 --- /dev/null +++ b/test/javax/management/namespace/VirtualMBeanTest.java @@ -0,0 +1,409 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test VirtualMBeanTest.java + * @bug 5108776 + * @summary Test that Virtual MBeans can be implemented and emit notifs. + * @author Eamonn McManus + */ + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; +import javax.management.DynamicMBean; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanInfo; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.MalformedObjectNameException; +import javax.management.Notification; +import javax.management.NotificationBroadcaster; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.RuntimeOperationsException; +import javax.management.SendNotification; +import javax.management.StandardEmitterMBean; +import javax.management.StandardMBean; +import javax.management.namespace.JMXNamespace; +import javax.management.namespace.JMXNamespaces; +import javax.management.namespace.VirtualEventManager; +import javax.management.namespace.MBeanServerSupport; +import javax.management.timer.TimerMBean; + +// In this test, we check that the two main use case types for +// MBeanServerSupport work correctly: +// (1) as a special-purpose implementation of MBeanServer for a fixed number +// of MBeans (e.g. for QueryNotificationFilter) +// (2) as an MBeanServer supporting Virtual MBeans. +// In each case we are particularly interested in the notification behaviour. +// We check that the behaviour is correct when calling addNotificationListener +// (a) for an MBean that does not exist; (b) for an MBean that does exist but +// is not a NotificationEmitter; and (c) for an MBean that exists and is +// a NotificationEmitter. We also check the degenerate and usual case +// where the MBeanServerSupport subclass does not support notifications +// at all. +// +// Each subclass will have an MBean called test:type=NotEmitter that +// does not support addNotificationListener. If it also has MBeans called +// test:type=Emitter,* then they are expected to support addNL. No subclass +// will have any other MBeans, so in particular no subclass will have +// test:type=Nonexistent. +// +public class VirtualMBeanTest { + static final ObjectName + nonExistentName, notEmitterName, emitterName1, emitterName2; + static { + try { + nonExistentName = new ObjectName("test:type=NonExistent"); + notEmitterName = new ObjectName("test:type=NotEmitter"); + emitterName1 = new ObjectName("test:type=Emitter,id=1"); + emitterName2 = new ObjectName("test:type=Emitter,id=2"); + } catch (MalformedObjectNameException e) { + throw new AssertionError(e); + } + } + + static final StandardMBean.Options wrappedVisible = new StandardMBean.Options(); + static { + wrappedVisible.setWrappedObjectVisible(true); + } + + public static interface NothingMBean {} + public static class Nothing implements NothingMBean {} + public static class NothingNBS extends NotificationBroadcasterSupport + implements NothingMBean {} + + // Class that has hardwired MBeans test:type=NotEmitter, + // test:type=Broadcaster, and test:type=Emitter. + private static class HardwiredMBS extends MBeanServerSupport + implements SendNotification { + private final DynamicMBean notEmitter = + new StandardMBean(new Nothing(), NothingMBean.class, wrappedVisible); + private final StandardEmitterMBean emitter1, emitter2; + { + NothingNBS nnbs1 = new NothingNBS(); + emitter1 = new StandardEmitterMBean( + nnbs1, NothingMBean.class, wrappedVisible, nnbs1); + NothingNBS nnbs2 = new NothingNBS(); + emitter2 = new StandardEmitterMBean( + nnbs2, NothingMBean.class, wrappedVisible, nnbs2); + } + + private final Map map = + new TreeMap(); + { + map.put(notEmitterName, notEmitter); + map.put(emitterName1, emitter1); + map.put(emitterName2, emitter2); + } + + + @Override + public DynamicMBean getDynamicMBeanFor(ObjectName name) + throws InstanceNotFoundException { + DynamicMBean mbean = map.get(name); + if (mbean != null) + return mbean; + else + throw new InstanceNotFoundException(name); + } + + @Override + protected Set getNames() { + return map.keySet(); + } + + @Override + public String toString() { + return "Hardwired MBeanServerSupport"; + } + + public void sendNotification(Notification notification) { + emitter1.sendNotification(notification); + emitter2.sendNotification(notification); + } + } + + // Class that has the notEmitter MBean but not either of the others, so does + // not support listeners. + private static class VirtualMBSWithoutListeners + extends MBeanServerSupport { + @Override + public DynamicMBean getDynamicMBeanFor(ObjectName name) + throws InstanceNotFoundException { + if (name.equals(notEmitterName)) { + return new StandardMBean( + new Nothing(), NothingMBean.class, wrappedVisible); + } else + throw new InstanceNotFoundException(name); + } + + @Override + protected Set getNames() { + return Collections.singleton(notEmitterName); + } + + @Override + public String toString() { + return "Virtual MBeanServerSupport without listener support"; + } + } + + // Class that has the notEmitter and emitter MBeans as Virtual MBeans, using + // VirtualEventManager to handle listeners for the emitter MBean. We + // implement the broadcaster MBean (which is a NotificationBroadcaster but + // not a NotificationEmitter) even though it's very hard to imagine a real + // use case where that would happen. + private static class VirtualMBSWithListeners + extends MBeanServerSupport implements SendNotification { + private final VirtualEventManager vem = new VirtualEventManager(); + + private static final List names = + Arrays.asList(notEmitterName, emitterName1, emitterName2); + + @Override + public DynamicMBean getDynamicMBeanFor(ObjectName name) + throws InstanceNotFoundException { + if (names.contains(name)) { + return new StandardMBean( + new Nothing(), NothingMBean.class, wrappedVisible); + } else + throw new InstanceNotFoundException(name); + } + + @Override + public NotificationEmitter getNotificationEmitterFor( + ObjectName name) throws InstanceNotFoundException { + if (name.equals(emitterName1) || name.equals(emitterName2)) + return vem.getNotificationEmitterFor(name); + else if (name.equals(notEmitterName)) + return null; + else + throw new InstanceNotFoundException(name); + } + + @Override + protected Set getNames() { + return new TreeSet(Arrays.asList(notEmitterName, emitterName2)); + } + + @Override + public String toString() { + return "Virtual MBeanServerSupport with listener support"; + } + + public void sendNotification(Notification notification) { + vem.publish(emitterName1, notification); + vem.publish(emitterName2, notification); + } + } + + private static final MBeanServer[] vmbsss = { + new HardwiredMBS(), + new VirtualMBSWithoutListeners(), + new VirtualMBSWithListeners(), + }; + + public static void main(String[] args) throws Exception { + Exception lastEx = null; + for (MBeanServer vmbs : vmbsss) { + String testName = "\"" + vmbs + "\""; + System.out.println("===Test " + testName + "==="); + try { + test(vmbs); + } catch (Exception e) { + System.out.println( + "===Test " + testName + " failed with exception " + e); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + pw.flush(); + String es = sw.toString(); + System.out.println("......" + es.replace("\n", "\n......")); + lastEx = e; + } + } + if (lastEx != null) + throw lastEx; + System.out.println("TEST PASSED"); + } + + private static class NothingListener implements NotificationListener { + public void handleNotification(Notification notification, + Object handback) { + throw new UnsupportedOperationException("Not supported yet."); + } + } + + private static class QueueListener implements NotificationListener { + final BlockingQueue queue = + new ArrayBlockingQueue(10); + + public void handleNotification(Notification notification, + Object handback) { + queue.add(notification); + } + } + + private static void test(MBeanServer vmbs) throws Exception { + MBeanServer mmbs = MBeanServerFactory.newMBeanServer(); + ObjectName namespaceName = new ObjectName("test//:type=JMXNamespace"); + JMXNamespace namespace = new JMXNamespace(vmbs); + mmbs.registerMBean(namespace, namespaceName); + MBeanServer mbs = JMXNamespaces.narrowToNamespace(mmbs, "test"); + + Set names = mbs.queryNames(null, null); + //names.remove(new ObjectName(":type=JMXNamespace")); + + // Make sure that notEmitterName exists according to query... + System.out.println("Checking query"); + if (!names.contains(notEmitterName)) + throw new Exception("Bad query result: " + names); + + // ...and according to getMBeanInfo + System.out.println("Checking getMBeanInfo(" + notEmitterName + ")"); + MBeanInfo mbi = mbs.getMBeanInfo(notEmitterName); + if (mbi.getNotifications().length > 0) + throw new Exception("notEmitter has NotificationInfo"); + + // Make sure we get the right exception for getMBeanInfo on a + // non-existent MBean + System.out.println("Checking getMBeanInfo on a non-existent MBean"); + try { + mbi = mbs.getMBeanInfo(nonExistentName); + throw new Exception("getMBI succeeded but should not have"); + } catch (InstanceNotFoundException e) { + } + + // Make sure we get the right exception for addNotificationListener on a + // non-existent MBean + System.out.println( + "Checking addNotificationListener on a non-existent MBean"); + try { + mbs.addNotificationListener( + nonExistentName, new NothingListener(), null, null); + throw new Exception("addNL succeeded but should not have"); + } catch (InstanceNotFoundException e) { + } + + // Make sure we get the right exception for isInstanceOf on a + // non-existent MBean + System.out.println( + "Checking isInstanceOf on a non-existent MBean"); + for (Class c : new Class[] { + Object.class, NotificationBroadcaster.class, NotificationEmitter.class, + }) { + try { + boolean is = mbs.isInstanceOf(nonExistentName, c.getName()); + throw new Exception( + "isInstanceOf " + c.getName() + + " succeeded but should not have"); + } catch (InstanceNotFoundException e) { + } + } + + // Make sure isInstanceOf works correctly for classes without special + // treatment + System.out.println( + "Checking isInstanceOf on normal classes"); + for (ObjectName name : names) { + boolean isNothing = mbs.isInstanceOf(name, NothingMBean.class.getName()); + if (!isNothing) { + throw new Exception("isInstanceOf " + NothingMBean.class.getName() + + " returned false, should be true"); + } + boolean isTimer = mbs.isInstanceOf(name, TimerMBean.class.getName()); + if (isTimer) { + throw new Exception("isInstanceOf " + TimerMBean.class.getName() + + " returned true, should be false"); + } + } + + // Make sure that addNL on notEmitterName gets the right exception + System.out.println("Checking addNL on non-broadcaster"); + try { + mbs.addNotificationListener( + notEmitterName, new NothingListener(), null, null); + throw new Exception("addNL succeeded but should not have"); + } catch (RuntimeOperationsException e) { + if (!(e.getCause() instanceof IllegalArgumentException)) + throw new Exception("Wrong exception from addNL", e); + } + + if (!(vmbs instanceof SendNotification)) { + System.out.println("Not testing notifications for this implementation"); + return; + } + + QueueListener qListener = new QueueListener(); + + System.out.println("Testing addNL on emitters"); + mbs.addNotificationListener(emitterName1, qListener, null, null); + mbs.addNotificationListener(emitterName2, qListener, null, null); + + System.out.println("Testing that listeners work"); + Notification notif = new Notification("notif.type", "source", 0L); + + ((SendNotification) vmbs).sendNotification(notif); + testListeners(qListener, "notif.type", 2); + + System.out.println("Testing 2-arg removeNL on emitter1"); + mbs.removeNotificationListener(emitterName1, qListener); + + ((SendNotification) vmbs).sendNotification(notif); + testListeners(qListener, "notif.type", 1); + + System.out.println("Testing 4-arg removeNL on emitter2"); + mbs.removeNotificationListener(emitterName2, qListener, null, null); + + ((SendNotification) vmbs).sendNotification(notif); + testListeners(qListener, "notif.type", 0); + } + + private static void testListeners( + QueueListener qListener, String expectedNotifType, int expectedNotifs) + throws Exception { + for (int i = 1; i <= expectedNotifs; i++) { + Notification rNotif = qListener.queue.poll(1, TimeUnit.SECONDS); + if (rNotif == null) + throw new Exception("Notification " + i + " never arrived"); + if (!rNotif.getType().equals(expectedNotifType)) + throw new Exception("Wrong type notif: " + rNotif.getType()); + } + Notification xNotif = qListener.queue.poll(10, TimeUnit.MILLISECONDS); + if (xNotif != null) + throw new Exception("Extra notif: " + xNotif); + } +} diff --git a/test/javax/management/namespace/VirtualNamespaceQueryTest.java b/test/javax/management/namespace/VirtualNamespaceQueryTest.java new file mode 100644 index 000000000..020c1224f --- /dev/null +++ b/test/javax/management/namespace/VirtualNamespaceQueryTest.java @@ -0,0 +1,127 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * + * @test VirtualNamespaceQueryTest.java + * @summary General VirtualNamespaceQueryTest test. + * @author Daniel Fuchs + * @run clean VirtualNamespaceQueryTest Wombat WombatMBean + * NamespaceController NamespaceControllerMBean + * JMXRemoteTargetNamespace + * @compile -XDignore.symbol.file=true VirtualNamespaceQueryTest.java + * Wombat.java WombatMBean.java + * NamespaceController.java NamespaceControllerMBean.java + * JMXRemoteTargetNamespace.java + * @run main VirtualNamespaceQueryTest + */ + +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import javax.management.DynamicMBean; +import javax.management.InstanceNotFoundException; +import javax.management.JMX; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.NotificationEmitter; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.StandardMBean; +import javax.management.namespace.JMXNamespace; +import javax.management.namespace.JMXNamespaces; +import javax.management.namespace.MBeanServerSupport; + +/** + * + * @author dfuchs + */ +public class VirtualNamespaceQueryTest { + public static class WombatRepository extends MBeanServerSupport { + final Wombat wombat; + final StandardMBean mbean; + final ObjectName wombatName; + + public WombatRepository(ObjectName wombatName) { + try { + wombat = new Wombat(); + mbean = wombat; + this.wombatName = wombatName; + wombat.preRegister(null,wombatName); + } catch (Exception x) { + throw new IllegalArgumentException(x); + } + } + + @Override + public DynamicMBean getDynamicMBeanFor(ObjectName name) + throws InstanceNotFoundException { + if (wombatName.equals(name)) return mbean; + else throw new InstanceNotFoundException(String.valueOf(name)); + } + + @Override + protected Set getNames() { + final Set res = Collections.singleton(wombatName); + return res; + } + + @Override + public NotificationEmitter getNotificationEmitterFor( + ObjectName name) throws InstanceNotFoundException { + DynamicMBean mb = getDynamicMBeanFor(name); + if (mb instanceof NotificationEmitter) + return (NotificationEmitter)mb; + return null; + } + } + public static class WombatNamespace extends JMXNamespace { + public WombatNamespace(ObjectName wombatName) { + super(new WombatRepository(wombatName)); + } + } + + public static void simpleTest() throws Exception { + final MBeanServer server = MBeanServerFactory.newMBeanServer(); + final ObjectName wombatName = new ObjectName("burrow:type=Wombat"); + final JMXNamespace ns = new WombatNamespace(wombatName); + server.registerMBean(ns, JMXNamespaces.getNamespaceObjectName("wombats")); + final Set dirs = + server.queryNames(new ObjectName("wombats//*//:type=JMXNamespace"), + wombatName); + System.out.println("all dirs: "+dirs); + if (dirs.size()>0) + throw new RuntimeException("Unexpected ObjectNames returned: "+dirs); + + final ObjectInstance inst = NamespaceController.createInstance(server); + final NamespaceControllerMBean controller = + JMX.newMBeanProxy(server, inst.getObjectName(), + NamespaceControllerMBean.class); + final String[] dirNames = controller.findNamespaces(null,null,2); + System.err.println(Arrays.toString(dirNames)); + } + + public static void main(String[] args) throws Exception { + simpleTest(); + } +} diff --git a/test/javax/management/namespace/VirtualPropsTest.java b/test/javax/management/namespace/VirtualPropsTest.java new file mode 100644 index 000000000..8bb57edd5 --- /dev/null +++ b/test/javax/management/namespace/VirtualPropsTest.java @@ -0,0 +1,179 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 5108776 + * @summary Test the properties use case for Virtual MBeans that is documented + * in MBeanServerSupport. + * @author Eamonn McManus + */ + +import java.lang.management.ManagementFactory; +import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; +import javax.management.DynamicMBean; +import javax.management.InstanceNotFoundException; +import javax.management.JMX; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.Notification; +import javax.management.NotificationEmitter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.StandardMBean; +import javax.management.namespace.JMXNamespace; +import javax.management.namespace.JMXNamespaces; +import javax.management.namespace.VirtualEventManager; +import javax.management.namespace.MBeanServerSupport; + +public class VirtualPropsTest { + public static interface PropertyMBean { + public String getValue(); + } + + public static class PropsMBS extends MBeanServerSupport { + private static ObjectName newObjectName(String name) { + try { + return new ObjectName(name); + } catch (MalformedObjectNameException e) { + throw new AssertionError(e); + } + } + + public static class PropertyImpl implements PropertyMBean { + private final String name; + + public PropertyImpl(String name) { + this.name = name; + } + + public String getValue() { + return System.getProperty(name); + } + } + + @Override + public DynamicMBean getDynamicMBeanFor(ObjectName name) + throws InstanceNotFoundException { + ObjectName namePattern = newObjectName( + "com.example:type=Property,name=\"*\""); + if (!namePattern.apply(name)) + throw new InstanceNotFoundException(name); + + String propName = ObjectName.unquote(name.getKeyProperty("name")); + if (System.getProperty(propName) == null) + throw new InstanceNotFoundException(name); + PropertyMBean propMBean = new PropertyImpl(propName); + return new StandardMBean(propMBean, PropertyMBean.class, false); + } + + @Override + protected Set getNames() { + Set names = new TreeSet(); + Properties props = System.getProperties(); + for (String propName : props.stringPropertyNames()) { + ObjectName objectName = newObjectName( + "com.example:type=Property,name=" + + ObjectName.quote(propName)); + names.add(objectName); + } + return names; + } + + private final VirtualEventManager vem = new VirtualEventManager(); + + @Override + public NotificationEmitter getNotificationEmitterFor( + ObjectName name) throws InstanceNotFoundException { + getDynamicMBeanFor(name); // check that the name is valid + return vem.getNotificationEmitterFor(name); + } + + public void propertyChanged(String name, String newValue) { + ObjectName objectName = newObjectName( + "com.example:type=Property,name=" + ObjectName.quote(name)); + Notification n = new Notification( + "com.example.property.changed", objectName, 0L, + "Property " + name + " changed"); + n.setUserData(newValue); + vem.publish(objectName, n); + } + } + + static class QueueListener implements NotificationListener { + BlockingQueue q = new ArrayBlockingQueue(10); + public void handleNotification(Notification notification, + Object handback) { + q.add(notification); + } + } + + public static void main(String[] args) throws Exception { + MBeanServer mmbs = ManagementFactory.getPlatformMBeanServer(); + String namespace = "props"; + PropsMBS pmbs = new PropsMBS(); + Object namespaceMBean = new JMXNamespace(pmbs); + mmbs.registerMBean(namespaceMBean, new ObjectName( + namespace + "//:type=JMXNamespace")); + MBeanServer mbs = JMXNamespaces.narrowToNamespace(mmbs, namespace); + + Properties props = System.getProperties(); + + int nprops = props.stringPropertyNames().size(); + if (nprops != mbs.getMBeanCount()) { + throw new Exception(String.format("Properties: %d; MBeans: %d", + nprops, mbs.getMBeanCount())); + } + + for (String propName : props.stringPropertyNames()) { + ObjectName propObjectName = new ObjectName( + "com.example:type=Property,name=" + ObjectName.quote(propName)); + PropertyMBean propProx = JMX.newMBeanProxy( + mbs, propObjectName, PropertyMBean.class); + String propValue = propProx.getValue(); + String realPropValue = props.getProperty(propName); + if (!realPropValue.equals(propValue)) { + throw new Exception(String.format("Property %s: value is \"%s\"; " + + "mbean says \"%s\"", propName, realPropValue, propValue)); + } + } + + ObjectName fooPropObjectName = + new ObjectName("com.example:type=Property,name=\"java.home\""); + QueueListener ql = new QueueListener(); + mbs.addNotificationListener(fooPropObjectName, ql, null, null); + pmbs.propertyChanged("java.home", "bar"); + Notification n = ql.q.poll(1, TimeUnit.SECONDS); + if (n == null) + throw new Exception("Notif didn't arrive"); + if (!"bar".equals(n.getUserData())) + throw new Exception("Bad user data: " + n.getUserData()); + + System.out.println("TEST PASSED"); + } +} diff --git a/test/javax/management/namespace/Wombat.java b/test/javax/management/namespace/Wombat.java new file mode 100644 index 000000000..03dbd020e --- /dev/null +++ b/test/javax/management/namespace/Wombat.java @@ -0,0 +1,254 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.util.Random; +import java.util.Set; +import javax.management.AttributeChangeNotification; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import javax.management.MBeanRegistration; +import javax.management.MBeanServer; +import javax.management.NotCompliantMBeanException; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.StandardMBean; + + +/** + * Dynamic MBean based on StandardMBean + * Class Wombat + * Wombat Description + * @author dfuchs + */ +public class Wombat extends StandardMBean + implements WombatMBean, NotificationEmitter, MBeanRegistration { + + /** + * Attribute : Caption + */ + private String caption = "I'm a wombat"; + + private final long MAX_SEED = 36000; + private final long seed; + private final long period; + private volatile int mood = 0; + + public int getMood() { + final long degree = seed + (System.currentTimeMillis()/period)%MAX_SEED; + final double angle = ((double)degree)/100; + mood = (int)(100.0*Math.sin(angle)); + return mood; + } + + public Wombat() throws NotCompliantMBeanException { + super(WombatMBean.class); + final Random r = new Random(); + seed = ((r.nextLong() % MAX_SEED) + MAX_SEED)%MAX_SEED; + period = 200 + (((r.nextLong()%80)+80)%80)*10; + } + + /** + * Next are the methods to compute MBeanInfo. + * You shouldn't update these methods. + */ + @Override + protected String getDescription(MBeanInfo info) { + return "Wombats are strange beasts. You will find them down under " + + "and in some computer programms."; + } + + @Override + protected String getDescription(MBeanAttributeInfo info) { + String description = null; + if (info.getName().equals("Caption")) { + description = "A simple caption to describe a wombat"; + } + if (info.getName().equals("Mood")) { + description = "This Wombat's mood on a [-100,+100] scale."+ + " -100 means that this wombat is very angry."; + } + return description; + } + + @Override + protected String getDescription(MBeanOperationInfo op, + MBeanParameterInfo param, + int sequence) { + return null; + } + + @Override + protected String getParameterName(MBeanOperationInfo op, + MBeanParameterInfo param, + int sequence) { + return null; + } + + @Override + protected String getDescription(MBeanOperationInfo info) { + String description = null; + return description; + } + + @Override + public MBeanInfo getMBeanInfo() { + MBeanInfo mbinfo = super.getMBeanInfo(); + return new MBeanInfo(mbinfo.getClassName(), + mbinfo.getDescription(), + mbinfo.getAttributes(), + mbinfo.getConstructors(), + mbinfo.getOperations(), + getNotificationInfo()); + } + + /** + * Get A simple caption to describe a wombat + */ + public synchronized String getCaption() { + return caption; + } + + /** + * Set A simple caption to describe a wombat + */ + public void setCaption(String value) { + final String oldValue; + synchronized (this) { + oldValue = caption; + caption = value; + } + final AttributeChangeNotification notif = + new AttributeChangeNotification(objectName, + getNextSeqNumber(), + System.currentTimeMillis(), + "Caption changed","Caption", + String.class.getName(),oldValue,value); + broadcaster.sendNotification(notif); + } + + /** + * MBeanNotification support + * You shouldn't update these methods + */ + public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException { + broadcaster.addNotificationListener(listener, filter, handback); + } + + public MBeanNotificationInfo[] getNotificationInfo() { + return new MBeanNotificationInfo[] { + new MBeanNotificationInfo(new String[] { + AttributeChangeNotification.ATTRIBUTE_CHANGE}, + javax.management.AttributeChangeNotification.class.getName(), + "Sent when the caption changes") + }; + } + + public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { + broadcaster.removeNotificationListener(listener); + } + + public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException { + broadcaster.removeNotificationListener(listener, filter, handback); + } + + private synchronized long getNextSeqNumber() { + return seqNumber++; + } + + private long seqNumber; + + private final NotificationBroadcasterSupport broadcaster = new NotificationBroadcasterSupport(); + + /** + * Allows the MBean to perform any operations it needs before being + * registered in the MBean server. If the name of the MBean is not + * specified, the MBean can provide a name for its registration. If + * any exception is raised, the MBean will not be registered in the + * MBean server. + * @param server The MBean server in which the MBean will be registered. + * @param name The object name of the MBean. This name is null if the + * name parameter to one of the createMBean or registerMBean methods in + * the MBeanServer interface is null. In that case, this method must + * return a non-null ObjectName for the new MBean. + * @return The name under which the MBean is to be registered. This value + * must not be null. If the name parameter is not null, it will usually + * but not necessarily be the returned value. + * @throws Exception This exception will be caught by the MBean server and + * re-thrown as an MBeanRegistrationException. + */ + @Override + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + objectName = name; + mbeanServer = server; + return super.preRegister(server, name); + } + + /** + * Allows the MBean to perform any operations needed after having + * been registered in the MBean server or after the registration has + * failed. + * @param registrationDone Indicates wether or not the MBean has been + * successfully registered in the MBean server. The value false means + * that the registration has failed. + */ + @Override + public void postRegister(Boolean registrationDone) { + super.postRegister(registrationDone); + } + + /** + * Allows the MBean to perform any operations it needs before being + * unregistered by the MBean server. + * @throws Exception This exception will be caught by the MBean server and + * re-thrown as an MBeanRegistrationException. + */ + @Override + public void preDeregister() throws Exception { + super.preDeregister(); + } + + /** + * Allows the MBean to perform any operations needed after having been + * unregistered in the MBean server. + */ + @Override + public void postDeregister() { + super.postDeregister(); + } + + public Set listMatching(ObjectName pattern) { + return mbeanServer.queryNames(pattern, null); + } + + private MBeanServer mbeanServer; + + private ObjectName objectName; +} diff --git a/test/javax/management/namespace/WombatMBean.java b/test/javax/management/namespace/WombatMBean.java new file mode 100644 index 000000000..73f487531 --- /dev/null +++ b/test/javax/management/namespace/WombatMBean.java @@ -0,0 +1,59 @@ + +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.util.Set; +import javax.management.ObjectName; + +/** + * Interface WombatMBean + * Wombat Description + * @author dfuchs + */ +public interface WombatMBean +{ + /** + * This Wombat's mood on a [-100,+100] scale. + * -100 means that this wombat is very angry. + * @return The wombat's mood. + */ + public int getMood(); + + /** + * Get A simple caption to describe a wombat + */ + public String getCaption(); + + /** + * Set A simple caption to describe a wombat + */ + public void setCaption(String value); + + /** + * List matching MBeans in the same server. + * @param pattern an ObjectName pattern or null. + * @return A list of matching MBeans. + */ + public Set listMatching(ObjectName pattern); + +} diff --git a/test/javax/management/namespace/namespace.policy b/test/javax/management/namespace/namespace.policy new file mode 100644 index 000000000..e77bb79c9 --- /dev/null +++ b/test/javax/management/namespace/namespace.policy @@ -0,0 +1,85 @@ +grant codebase "file:/-" { + permission java.util.PropertyPermission "jmx.wait", "read"; + permission java.util.PropertyPermission "jmx.rmi.port", "read"; + permission java.net.SocketPermission "*", "accept,connect,resolve"; + permission java.security.SecurityPermission "*"; + + // Attribute Caption: allow get everywhere + // ================== + + // allow getAttribute(*:*,Caption) in all MBeanServers + permission javax.management.MBeanPermission "#Caption", "getAttribute"; + // allow getAttribute(*:*,Caption) in all namespaces recursively. + permission javax.management.namespace.JMXNamespacePermission "Caption", + "getAttribute"; + + // Attribute Mood: allow get only in MBeanServers named rmi* + // =============== + + // allow to get attribute Mood of Wombat MBeans only in namespaces + // whose name match rmi*, wherever they are. + // for this we need two permissions: + permission javax.management.namespace.JMXNamespacePermission + "*::Mood[**//rmi*//wombat:*]", + "getAttribute"; + permission javax.management.namespace.JMXNamespacePermission + "*::Mood[rmi*//wombat:*]", + "getAttribute"; + + // allow to get attribute mood in any MBeanServer whose name starts with + // rmi + permission javax.management.MBeanPermission "rmi*::#Mood", + "getAttribute"; + + // Attribute UUID: + // =============== + + // allow to get attribute "UUID" everywhere. + permission javax.management.namespace.JMXNamespacePermission + "*::UUID[*//**//:*]", + "getAttribute"; + permission javax.management.MBeanPermission + "#UUID[*//:*]", + "getAttribute"; + + + + // Let getMBeanInfo and queryNames through everywhere... + // + permission javax.management.namespace.JMXNamespacePermission "[]", + "getMBeanInfo,queryNames"; + permission javax.management.MBeanPermission "*", + "getMBeanInfo,queryNames"; + + // special permission for all wombats: + // + permission javax.management.namespace.JMXNamespacePermission + "[**//*:type=Wombat,*]", + "getObjectInstance,isInstanceOf,queryMBeans"; + permission javax.management.MBeanPermission "[*:type=Wombat,*]", + "getObjectInstance,isInstanceOf,queryMBeans"; + + // allow JMXNamespace::getDefaultDomain + permission javax.management.namespace.JMXNamespacePermission + "*::DefaultDomain", + "getAttribute"; + + // These permissions are required to connect visualvm. + // + permission javax.management.MBeanPermission "default::[java.lang:*]", + "getObjectInstance,isInstanceOf,getAttribute,getMBeanInfo,queryNames,queryMBeans"; + permission javax.management.MBeanPermission "root::", + "isInstanceOf,queryNames,queryMBeans,getAttribute,getMBeanInfo,getObjectInstance,getDomains"; + permission javax.management.namespace.JMXNamespacePermission + "[**//JMImplementation:type=MBeanServerDelegate]", + "addNotificationListener,removeNotificationListener,isInstanceOf,queryNames,queryMBeans,getAttribute,getMBeanInfo,getObjectInstance"; + permission javax.management.MBeanPermission + "javax.management.MBeanServerDelegate", + "addNotificationListener,removeNotificationListener,isInstanceOf,queryNames,queryMBeans,getAttribute,getMBeanInfo,getObjectInstance"; + + // Thread monitoring + permission java.lang.management.ManagementPermission "monitor"; + permission javax.management.MBeanPermission "*::sun.management.*#*[java.lang:*]", "invoke"; +}; + + -- GitLab From 310702350ed89d80caa994ebba737654bbfcf7ab Mon Sep 17 00:00:00 2001 From: jccollet Date: Thu, 4 Sep 2008 15:26:53 +0200 Subject: [PATCH 088/139] 6692802: HttpCookie needs to support HttpOnly attribute Summary: Added HttpOnly tag support to HttpCookie class. Reviewed-by: chegar, michaelm --- src/share/classes/java/net/HttpCookie.java | 33 +++++++++++++++++++ .../net/CookieHandler/TestHttpCookie.java | 19 ++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/share/classes/java/net/HttpCookie.java b/src/share/classes/java/net/HttpCookie.java index 6e495e060..1fcdd6c51 100644 --- a/src/share/classes/java/net/HttpCookie.java +++ b/src/share/classes/java/net/HttpCookie.java @@ -75,6 +75,7 @@ public final class HttpCookie implements Cloneable { private String path; // Path=VALUE ... URLs that see the cookie private String portlist; // Port[="portlist"] ... the port cookie may be returned to private boolean secure; // Secure ... e.g. use SSL + private boolean httpOnly; // HttpOnly ... i.e. not accessible to scripts private int version = 1; // Version=1 ... RFC 2965 style // @@ -656,6 +657,32 @@ public final class HttpCookie implements Cloneable { version = v; } + /** + * Returns {@code true} if this cookie contains the HttpOnly + * attribute. This means that the cookie should not be accessible to + * scripting engines, like javascript. + * + * @return {@code true} if this cookie should be considered http only. + * @see #setHttpOnly(boolean) + */ + public boolean isHttpOnly() + { + return httpOnly; + } + + /** + * Indicates whether the cookie should be considered HTTP Only. If set to + * {@code true} it means the cookie should not be accessible to scripting + * engines like javascript. + * + * @param httpOnly if {@code true} make the cookie HTTP only, i.e. + * only visible as part of an HTTP request. + * @see #isHttpOnly() + */ + public void setHttpOnly(boolean httpOnly) + { + this.httpOnly = httpOnly; + } /** * The utility method to check whether a host name is in a domain @@ -877,6 +904,7 @@ public final class HttpCookie implements Cloneable { || name.equalsIgnoreCase("Port") // rfc2965 only || name.equalsIgnoreCase("Secure") || name.equalsIgnoreCase("Version") + || name.equalsIgnoreCase("HttpOnly") || name.charAt(0) == '$') { return true; @@ -996,6 +1024,11 @@ public final class HttpCookie implements Cloneable { cookie.setSecure(true); } }); + assignors.put("httponly", new CookieAttributeAssignor(){ + public void assign(HttpCookie cookie, String attrName, String attrValue) { + cookie.setHttpOnly(true); + } + }); assignors.put("version", new CookieAttributeAssignor(){ public void assign(HttpCookie cookie, String attrName, String attrValue) { try { diff --git a/test/java/net/CookieHandler/TestHttpCookie.java b/test/java/net/CookieHandler/TestHttpCookie.java index c62722704..f18cf40b9 100644 --- a/test/java/net/CookieHandler/TestHttpCookie.java +++ b/test/java/net/CookieHandler/TestHttpCookie.java @@ -24,7 +24,7 @@ /** * @test * @summary Unit test for java.net.HttpCookie - * @bug 6244040 6277796 6277801 6277808 6294071 + * @bug 6244040 6277796 6277801 6277808 6294071 6692802 * @author Edward Wang */ @@ -178,6 +178,19 @@ public class TestHttpCookie { } TestHttpCookie port(String p) { return port(0, p); } + // check http only + TestHttpCookie httpOnly(int index, boolean b) { + HttpCookie cookie = cookies.get(index); + if (cookie == null || b != cookie.isHttpOnly()) { + raiseError("HttpOnly", String.valueOf(cookie.isHttpOnly()), String.valueOf(b)); + } + return this; + } + + TestHttpCookie httpOnly(boolean b) { + return httpOnly(0, b); + } + // check equality static void eq(HttpCookie ck1, HttpCookie ck2, boolean same) { testCount++; @@ -362,6 +375,10 @@ public class TestHttpCookie { } catch (IllegalArgumentException ignored) { // expected exception; no-op } + + // CR 6692802: HttpOnly flag + test("set-cookie: CUSTOMER=WILE_E_COYOTE;HttpOnly").httpOnly(true); + test("set-cookie: CUSTOMER=WILE_E_COYOTE").httpOnly(false); } static void header(String prompt) { -- GitLab From 9517f779748e5c4f7c9088ef026d4c92c2fb69e0 Mon Sep 17 00:00:00 2001 From: peytoia Date: Mon, 8 Sep 2008 10:44:57 +0900 Subject: [PATCH 089/139] 6665028: native code of method j*.text.Bidi.nativeBidiChars is using the contents of a primitive array direct Reviewed-by: okutsu --- src/share/native/sun/font/bidi/ubidi.c | 26 ++--- test/java/text/Bidi/Bug6665028.java | 129 +++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 13 deletions(-) create mode 100644 test/java/text/Bidi/Bug6665028.java diff --git a/src/share/native/sun/font/bidi/ubidi.c b/src/share/native/sun/font/bidi/ubidi.c index 97392b91d..41be82c38 100644 --- a/src/share/native/sun/font/bidi/ubidi.c +++ b/src/share/native/sun/font/bidi/ubidi.c @@ -384,23 +384,23 @@ ubidi_setPara(UBiDi *pBiDi, const UChar *text, int32_t length, return; } - /* are explicit levels specified? */ - if(embeddingLevels==NULL) { - /* no: determine explicit levels according to the (Xn) rules */\ - if(getLevelsMemory(pBiDi, length)) { - pBiDi->levels=pBiDi->levelsMemory; + if (getLevelsMemory(pBiDi, length)) { + pBiDi->levels=pBiDi->levelsMemory; + /* are explicit levels specified? */ + if(embeddingLevels==NULL) { + /* no: determine explicit levels according to the (Xn) rules */ direction=resolveExplicitLevels(pBiDi); } else { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return; + /* set BN for all explicit codes, check that all levels are paraLevel..UBIDI_MAX_EXPLICIT_LEVEL */ + icu_memcpy(pBiDi->levels, embeddingLevels, length); + direction=checkExplicitLevels(pBiDi, pErrorCode); + if(U_FAILURE(*pErrorCode)) { + return; + } } } else { - /* set BN for all explicit codes, check that all levels are paraLevel..UBIDI_MAX_EXPLICIT_LEVEL */ - pBiDi->levels=embeddingLevels; - direction=checkExplicitLevels(pBiDi, pErrorCode); - if(U_FAILURE(*pErrorCode)) { - return; - } + *pErrorCode=U_MEMORY_ALLOCATION_ERROR; + return; } /* diff --git a/test/java/text/Bidi/Bug6665028.java b/test/java/text/Bidi/Bug6665028.java new file mode 100644 index 000000000..e8edd54a5 --- /dev/null +++ b/test/java/text/Bidi/Bug6665028.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6665028 + * @summary verify that the memory corruption doesn't happen. Note + * that this test case fails without the fix in some different ways, + * including timeout, due to the memory corruption. + * @build Bug6665028 + * @run main/othervm/timeout=60 -Xmx16m Bug6665028 + */ + +import java.awt.font.TextAttribute; +import java.text.AttributedString; +import java.text.Bidi; + +// test1() and test2() were derived from BidiEmbeddingTest. +public class Bug6665028 { + + private static boolean runrun = true; + + private static class Test extends Thread { + public void run() { + while (runrun) { + test1(); + test2(); + } + } + } + + public static void main(String[] args) { + Test[] tests = new Test[4]; + for (int i = 0; i < tests.length; i++) { + Test t = new Test(); + tests[i] = t; + t.start(); + } + + try { + Thread.sleep(45000); + } catch (InterruptedException e) { + } + + runrun = false; + + for (int i = 0; i < tests.length; i++) { + try { + tests[i].join(); + } catch (InterruptedException e) { + } + } + } + + static String target; + static { + String s = "A Bidi object provides information on the bidirectional reordering of the text used to create it. This is required, for example, to properly display Arabic or Hebrew text. "; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 1000; i++) { + sb.append(s); + } + target = sb.toString(); + } + + static void test1() { + String str = "If this text is >" + target + "< the test passed."; + int start = str.indexOf(target); + int limit = start + target.length(); + + AttributedString astr = new AttributedString(str); + astr.addAttribute(TextAttribute.BIDI_EMBEDDING, + new Integer(-1), + start, + limit); + + Bidi bidi = new Bidi(astr.getIterator()); + + byte[] embs = new byte[str.length() + 3]; + for (int i = start + 1; i < limit + 1; ++i) { + embs[i] = -1; + } + + Bidi bidi2 = new Bidi(str.toCharArray(), 0, embs, 1, str.length(), Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); + if (bidi.getRunCount() != 3 || bidi2.getRunCount() != 3) { + throw new Error("Bidi run count incorrect"); + } + } + + static void test2() { + String str = "If this text is >" + target + "< the test passed."; + int length = str.length(); + int start = str.indexOf(target); + int limit = start + target.length(); + + AttributedString astr = new AttributedString(str); + astr.addAttribute(TextAttribute.RUN_DIRECTION, TextAttribute.RUN_DIRECTION_RTL); + + astr.addAttribute(TextAttribute.BIDI_EMBEDDING, + new Integer(-3), + start, + limit); + + Bidi bidi = new Bidi(astr.getIterator()); + + if (bidi.getRunCount() != 6) { // runs of spaces and angles at embedding bound,s and final period, each get level 1 + throw new Error("Bidi embedding processing failed"); + } + } +} -- GitLab From 56e6388abee43e506db67e436e35f126665e8000 Mon Sep 17 00:00:00 2001 From: peytoia Date: Mon, 8 Sep 2008 11:49:49 +0900 Subject: [PATCH 090/139] 6607310: InputContext may cause loading of swing classes even for non-Swing applets Reviewed-by: okutsu --- src/share/classes/sun/awt/im/CompositionArea.java | 3 ++- src/share/classes/sun/awt/im/InputContext.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/share/classes/sun/awt/im/CompositionArea.java b/src/share/classes/sun/awt/im/CompositionArea.java index ff8bfb60f..4808aa457 100644 --- a/src/share/classes/sun/awt/im/CompositionArea.java +++ b/src/share/classes/sun/awt/im/CompositionArea.java @@ -56,7 +56,8 @@ import javax.swing.border.LineBorder; * @author JavaSoft International */ -public class CompositionArea extends JPanel implements InputMethodListener { +// This class is final due to the 6607310 fix. Refer to the CR for details. +public final class CompositionArea extends JPanel implements InputMethodListener { private CompositionAreaHandler handler; diff --git a/src/share/classes/sun/awt/im/InputContext.java b/src/share/classes/sun/awt/im/InputContext.java index 8948d55b4..d0c84c726 100644 --- a/src/share/classes/sun/awt/im/InputContext.java +++ b/src/share/classes/sun/awt/im/InputContext.java @@ -297,7 +297,7 @@ public class InputContext extends java.awt.im.InputContext */ synchronized (source.getTreeLock()) { synchronized (this) { - if (source instanceof CompositionArea) { + if ("sun.awt.im.CompositionArea".equals(source.getClass().getName())) { // no special handling for this one } else if (getComponentWindow(source) instanceof InputMethodWindow) { // no special handling for this one either -- GitLab From b0ec6b8be475231345bc91728094cf2b53e50ff2 Mon Sep 17 00:00:00 2001 From: peytoia Date: Mon, 8 Sep 2008 13:31:45 +0900 Subject: [PATCH 091/139] 6645292: [Fmt-Da] Timezone Western Summer Time (Australia) is parsed incorrectly Reviewed-by: okutsu --- .../classes/java/text/SimpleDateFormat.java | 9 ++- .../text/Format/DateFormat/Bug6645292.java | 66 +++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 test/java/text/Format/DateFormat/Bug6645292.java diff --git a/src/share/classes/java/text/SimpleDateFormat.java b/src/share/classes/java/text/SimpleDateFormat.java index 548ffb730..38893c9d1 100644 --- a/src/share/classes/java/text/SimpleDateFormat.java +++ b/src/share/classes/java/text/SimpleDateFormat.java @@ -1482,10 +1482,13 @@ public class SimpleDateFormat extends DateFormat { // If the time zone matched uses the same name // (abbreviation) for both standard and daylight time, // let the time zone in the Calendar decide which one. - if (!useSameName) { + // + // Also if tz.getDSTSaving() returns 0 for DST, use tz to + // determine the local time. (6645292) + int dstAmount = (nameIndex >= 3) ? tz.getDSTSavings() : 0; + if (!(useSameName || (nameIndex >= 3 && dstAmount == 0))) { calendar.set(Calendar.ZONE_OFFSET, tz.getRawOffset()); - calendar.set(Calendar.DST_OFFSET, - nameIndex >= 3 ? tz.getDSTSavings() : 0); + calendar.set(Calendar.DST_OFFSET, dstAmount); } return (start + zoneNames[nameIndex].length()); } diff --git a/test/java/text/Format/DateFormat/Bug6645292.java b/test/java/text/Format/DateFormat/Bug6645292.java new file mode 100644 index 000000000..9c62833cf --- /dev/null +++ b/test/java/text/Format/DateFormat/Bug6645292.java @@ -0,0 +1,66 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6645292 + * @summary Make sure to parse a DST time zone name with which the + * last DST rule doesn't observe DST. + */ + +import java.text.*; +import java.util.*; +import static java.util.Calendar.*; + +public class Bug6645292 { + public static void main(String[] args) { + Locale loc = Locale.getDefault(); + TimeZone zone = TimeZone.getDefault(); + try { + Locale.setDefault(Locale.US); + // Use "Asia/Shanghai" with an old time stamp rather than + // "Australia/Perth" because if Perth decides to obserb DST + // permanently, that decision will make this test case + // useless. There's the same risk with China, though. + TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.set(1986, JUNE, 1); + Date d1 = cal.getTime(); + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzzz"); + String s = df.format(d1); + Date d2 = null; + try { + d2 = df.parse(s); + } catch (Exception e) { + throw new RuntimeException(e); + } + if (!d1.equals(d2)) { + throw new RuntimeException("d1 (" + d1 + ") != d2 (" + d2 + ")"); + } + } finally { + Locale.setDefault(loc); + TimeZone.setDefault(zone); + } + } +} -- GitLab From b7be04e98cc7afd8f75fd180842d1cc5b4b48abe Mon Sep 17 00:00:00 2001 From: peytoia Date: Mon, 8 Sep 2008 14:31:08 +0900 Subject: [PATCH 092/139] 4823811: [Fmt-Da] SimpleDateFormat patterns don't allow embedding of some literal punctuation Reviewed-by: okutsu --- .../classes/java/text/SimpleDateFormat.java | 115 ++- .../text/Format/DateFormat/Bug4823811.java | 789 ++++++++++++++++++ 2 files changed, 895 insertions(+), 9 deletions(-) create mode 100644 test/java/text/Format/DateFormat/Bug4823811.java diff --git a/src/share/classes/java/text/SimpleDateFormat.java b/src/share/classes/java/text/SimpleDateFormat.java index 38893c9d1..e543899fe 100644 --- a/src/share/classes/java/text/SimpleDateFormat.java +++ b/src/share/classes/java/text/SimpleDateFormat.java @@ -373,6 +373,24 @@ public class SimpleDateFormat extends DateFormat { */ private String pattern; + /** + * Saved numberFormat and pattern. + * @see SimpleDateFormat#checkNegativeNumberExpression + */ + transient private NumberFormat originalNumberFormat; + transient private String originalNumberPattern; + + /** + * The minus sign to be used with format and parse. + */ + transient private char minusSign = '-'; + + /** + * True when a negative sign follows a number. + * (True as default in Arabic.) + */ + transient private boolean hasFollowingMinusSign = false; + /** * The compiled pattern. */ @@ -1226,6 +1244,8 @@ public class SimpleDateFormat extends DateFormat { */ public Date parse(String text, ParsePosition pos) { + checkNegativeNumberExpression(); + int start = pos.index; int oldStart = start; int textLength = text.length(); @@ -1271,14 +1291,42 @@ public class SimpleDateFormat extends DateFormat { // digit text (e.g., "20010704") with a pattern which // has no delimiters between fields, like "yyyyMMdd". boolean obeyCount = false; + + // In Arabic, a minus sign for a negative number is put after + // the number. Even in another locale, a minus sign can be + // put after a number using DateFormat.setNumberFormat(). + // If both the minus sign and the field-delimiter are '-', + // subParse() needs to determine whether a '-' after a number + // in the given text is a delimiter or is a minus sign for the + // preceding number. We give subParse() a clue based on the + // information in compiledPattern. + boolean useFollowingMinusSignAsDelimiter = false; + if (i < compiledPattern.length) { int nextTag = compiledPattern[i] >>> 8; - if (!(nextTag == TAG_QUOTE_ASCII_CHAR || nextTag == TAG_QUOTE_CHARS)) { + if (!(nextTag == TAG_QUOTE_ASCII_CHAR || + nextTag == TAG_QUOTE_CHARS)) { obeyCount = true; } + + if (hasFollowingMinusSign && + (nextTag == TAG_QUOTE_ASCII_CHAR || + nextTag == TAG_QUOTE_CHARS)) { + int c; + if (nextTag == TAG_QUOTE_ASCII_CHAR) { + c = compiledPattern[i] & 0xff; + } else { + c = compiledPattern[i+1]; + } + + if (c == minusSign) { + useFollowingMinusSignAsDelimiter = true; + } + } } start = subParse(text, start, tag, count, obeyCount, - ambiguousYear, pos); + ambiguousYear, pos, + useFollowingMinusSignAsDelimiter); if (start < 0) { pos.index = oldStart; return null; @@ -1514,8 +1562,8 @@ public class SimpleDateFormat extends DateFormat { */ private int subParse(String text, int start, int patternCharIndex, int count, boolean obeyCount, boolean[] ambiguousYear, - ParsePosition origPos) - { + ParsePosition origPos, + boolean useFollowingMinusSignAsDelimiter) { Number number = null; int value = 0; ParsePosition pos = new ParsePosition(0); @@ -1540,10 +1588,10 @@ public class SimpleDateFormat extends DateFormat { // a number value. We handle further, more generic cases below. We need // to handle some of them here because some fields require extra processing on // the parsed value. - if (patternCharIndex == 4 /*HOUR_OF_DAY1_FIELD*/ || - patternCharIndex == 15 /*HOUR1_FIELD*/ || - (patternCharIndex == 2 /*MONTH_FIELD*/ && count <= 2) || - patternCharIndex == 1) { + if (patternCharIndex == 4 /* HOUR_OF_DAY1_FIELD */ || + patternCharIndex == 15 /* HOUR1_FIELD */ || + (patternCharIndex == 2 /* MONTH_FIELD */ && count <= 2) || + patternCharIndex == 1 /* YEAR_FIELD */) { // It would be good to unify this with the obeyCount logic below, // but that's going to be difficult. if (obeyCount) { @@ -1560,6 +1608,15 @@ public class SimpleDateFormat extends DateFormat { } } else { value = number.intValue(); + + if (useFollowingMinusSignAsDelimiter && (value < 0) && + (((pos.index < text.length()) && + (text.charAt(pos.index) != minusSign)) || + ((pos.index == text.length()) && + (text.charAt(pos.index-1) == minusSign)))) { + value = -value; + pos.index--; + } } } @@ -1891,7 +1948,18 @@ public class SimpleDateFormat extends DateFormat { number = numberFormat.parse(text, pos); } if (number != null) { - calendar.set(field, number.intValue()); + value = number.intValue(); + + if (useFollowingMinusSignAsDelimiter && (value < 0) && + (((pos.index < text.length()) && + (text.charAt(pos.index) != minusSign)) || + ((pos.index == text.length()) && + (text.charAt(pos.index-1) == minusSign)))) { + value = -value; + pos.index--; + } + + calendar.set(field, value); return pos.index; } break parsing; @@ -2102,4 +2170,33 @@ public class SimpleDateFormat extends DateFormat { } } } + + /** + * Analyze the negative subpattern of DecimalFormat and set/update values + * as necessary. + */ + private void checkNegativeNumberExpression() { + if ((numberFormat instanceof DecimalFormat) && + !numberFormat.equals(originalNumberFormat)) { + String numberPattern = ((DecimalFormat)numberFormat).toPattern(); + if (!numberPattern.equals(originalNumberPattern)) { + hasFollowingMinusSign = false; + + int separatorIndex = numberPattern.indexOf(';'); + // If the negative subpattern is not absent, we have to analayze + // it in order to check if it has a following minus sign. + if (separatorIndex > -1) { + int minusIndex = numberPattern.indexOf('-', separatorIndex); + if ((minusIndex > numberPattern.lastIndexOf('0')) && + (minusIndex > numberPattern.lastIndexOf('#'))) { + hasFollowingMinusSign = true; + minusSign = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getMinusSign(); + } + } + originalNumberPattern = numberPattern; + } + originalNumberFormat = numberFormat; + } + } + } diff --git a/test/java/text/Format/DateFormat/Bug4823811.java b/test/java/text/Format/DateFormat/Bug4823811.java new file mode 100644 index 000000000..65b4aa263 --- /dev/null +++ b/test/java/text/Format/DateFormat/Bug4823811.java @@ -0,0 +1,789 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * @test + * @bug 4823811 + * @summary Confirm that text which includes numbers with a trailing minus sign is parsed correctly. + */ + +import java.text.*; +import java.util.*; + +public class Bug4823811 { + + private static Locale localeEG = new Locale("ar", "EG"); + private static Locale localeUS = Locale.US; + + private static String JuneInArabic = "\u064a\u0648\u0646\u064a\u0648"; + private static String JulyInArabic = "\u064a\u0648\u0644\u064a\u0648"; + private static String JuneInEnglish = "June"; + private static String JulyInEnglish = "July"; + + private static String BORDER = + "============================================================"; + + /* + * I don't use static import here intentionally so that this test program + * can be run on JDK 1.4.2. + */ + private static int ERA = Calendar.ERA; + private static int BC = GregorianCalendar.BC; +// private static int JAN = Calendar.JANUARY; +// private static int FEB = Calendar.FEBRUARY; +// private static int MAR = Calendar.MARCH; + private static int APR = Calendar.APRIL; + private static int MAY = Calendar.MAY; + private static int JUN = Calendar.JUNE; + private static int JUL = Calendar.JULY; +// private static int AUG = Calendar.AUGUST; +// private static int SEP = Calendar.SEPTEMBER; +// private static int OCT = Calendar.OCTOBER; +// private static int NOV = Calendar.NOVEMBER; +// private static int DEC = Calendar.DECEMBER; + + private static String[] patterns = { + "yyyy MMMM d H m s", + "yyyy MM dd hh mm ss", + + /* + * Because 1-based HOUR_OF_DAY, 1-based HOUR, MONTH, and YEAR fields + * are parsed using different code from the code for other numeric + * fields, I prepared YEAR-preceding patterns and SECOND-preceding + * patterns. + */ + "yyyy M d h m s", + " yyyy M d h m s", + "yyyy M d h m s ", + + "s m h d M yyyy", + " s m h d M yyyy", + "s m h d M yyyy ", + }; + + private static char originalMinusSign1 = ':'; + private static char originalMinusSign2 = '\uff0d'; // fullwidth minus + private static String[] delimiters = {"-", "/", ":", "/", "\uff0d", "/"}; + private static String[][] specialDelimiters = { + // for Arabic formatter and modified English formatter + {"--", "-/", "::", ":/", "\uff0d\uff0d", "\uff0d/"}, + + // for English formatter and modified Arabic formatter + {"--", "/-", "::", "/:", "\uff0d\uff0d", "/\uff0d"}, + }; + + /* + * Format: + * +-------------------------------------------------------------------+ + * | Input | Output | + * +---------------------+---------------------------------------------| + * | datesEG & datesUS | formattedDatesEG & formattedDatesUS | + * +-------------------------------------------------------------------+ + * + * Parse: + * +-------------------------------------------------------------------+ + * | Input | Output | + * |---------------------+---------------------------------------------| + * | datesToParse | datesEG & datesUS | + * +-------------------------------------------------------------------+ + */ + private static String[][] datesToParse = { + // "JUNE" and "JULY" are replaced with a localized month name later. + {"2008 JULY 20 3 12 83", + "2008 JULY 20 3 12 83", + "2008 JULY 20 3 12 83"}, + + {"2008 07 20 03 12 83", + "2008 07 20 03 12 83", + "2008 07 20 03 12 83"}, + + {"2008 7 20 3 12 83", + "2008 7 20 3 12 83", + "2008 7 20 3 12 83"}, + + {" 2008 7 20 3 12 83", + " 2008 7 20 3 12 83", + " 2008 7 20 3 12 83", + "2008 7 20 3 12 83"}, + + {"2008 7 20 3 12 83 ", + "2008 7 20 3 12 83 ", + "2008 7 20 3 12 83"}, + + {"83 12 3 20 7 2008", + "83 12 3 20 7 2008", + "83 12 3 20 7 2008"}, + + {" 83 12 3 20 7 2008", + " 83 12 3 20 7 2008", + " 83 12 3 20 7 2008", + "83 12 3 20 7 2008"}, + + {"83 12 3 20 7 2008 ", + "83 12 3 20 7 2008 ", + "83 12 3 20 7 2008"}, + }; + + // For formatting + private static String[][] formattedDatesEG = { + {"2008 JULY 20 3 13 23", + "2009 JULY 20 3 13 23", + null}, + + {"2008 07 20 03 13 23", + "2009 07 20 03 13 23", + "2007 05 20 03 13 23"}, + + {"2008 7 20 3 13 23", + "2009 6 10 3 13 23", + "2007 4 10 3 13 23"}, + + {" 2008 7 20 3 13 23", + null, + " 2009 7 20 3 13 23", + null}, + + {"2008 7 20 3 13 23 ", + "2008 7 20 3 10 37 ", + null}, + + {"23 13 3 20 7 2008", + "37 10 9 19 7 2008", + "23 49 8 19 7 2008"}, + + {" 23 13 3 20 7 2008", + null, + " 37 10 3 20 7 2008", + null}, + + {"23 13 3 20 7 2008 ", + "23 13 3 20 7 2009 ", + null}, + }; + + private static String[][] formattedDatesUS = { + {"2008 JULY 20 3 13 23", + null, + "2008 JUNE 10 3 13 23"}, + + {"2008 07 20 03 13 23", + "2007 05 20 03 13 23", + "2008 06 10 03 13 23"}, + + {"2008 7 20 3 13 23", + "2007 5 19 9 13 23", + "2008 6 9 9 13 23"}, + + {" 2008 7 20 3 13 23", + " 2009 7 20 3 13 23", + " 2007 5 20 3 13 23", + null}, + + {"2008 7 20 3 13 23 ", + "2008 7 20 3 13 23 ", + null}, + + {"23 13 3 20 7 2008", + "23 49 2 10 6 2008", + "23 13 9 9 6 2008"}, + + {" 23 13 3 20 7 2008", + " 37 10 3 20 7 2008", + " 23 49 2 20 7 2008", + null}, + + {"23 13 3 20 7 2008 ", + "23 13 3 20 7 2008 ", + null}, + }; + + private static GregorianCalendar[][] datesEG = { + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + new GregorianCalendar(-2008, JUL, 20, 3, 12, 83), + null}, + + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + new GregorianCalendar(-2008, JUL, 20, 3, 12, 83), + new GregorianCalendar( 2007, MAY, 20, 3, 12, 83)}, + + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + new GregorianCalendar(-2008, JUL, -20, 3, 12, 83), + new GregorianCalendar( 2007, APR, 10, 3, 12, 83)}, + + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + null, + new GregorianCalendar(-2008, JUL, 20, 3, 12, 83), + null}, + + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + new GregorianCalendar( 2008, JUL, 20, 3, 12, -83), + null}, + + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + new GregorianCalendar( 2008, JUL, 20, -3, 12, -83), + new GregorianCalendar( 2008, JUL, 20, -3, -12, 83)}, + + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + null, + new GregorianCalendar( 2008, JUL, 20, 3, 12, -83), + null}, + + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + new GregorianCalendar(-2008, JUL, 20, 3, 12, 83), + null}, + }; + + private static GregorianCalendar[][] datesUS = { + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + null, + new GregorianCalendar( 2008, JUN, 10, 3, 12, 83)}, + + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + new GregorianCalendar( 2007, MAY, 20, 3, 12, 83), + new GregorianCalendar( 2008, JUN, 10, 3, 12, 83)}, + + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + new GregorianCalendar( 2007, MAY, 20, -3, 12, 83), + new GregorianCalendar( 2008, JUL, -20, -3, 12, 83)}, + + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + new GregorianCalendar(-2008, JUL, 20, 3, 12, 83), + new GregorianCalendar( 2007, MAY, 20, 3, 12, 83), + null}, + + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + null}, + + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + new GregorianCalendar( 2008, JUL, -20, 3, -12, 83), + new GregorianCalendar( 2008, JUL, -20, -3, 12, 83)}, + + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + new GregorianCalendar( 2008, JUL, 20, 3, 12, -83), + new GregorianCalendar( 2008, JUL, 20, 3, -12, 83), + null}, + + {new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + new GregorianCalendar( 2008, JUL, 20, 3, 12, 83), + null}, + }; + + /* flags */ + private static boolean err = false; + private static boolean verbose = false; + + + public static void main(String[] args) { + if (args.length == 1 && args[0].equals("-v")) { + verbose = true; + } + + Locale defaultLocale = Locale.getDefault(); + TimeZone defaultTimeZone = TimeZone.getDefault(); + + TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo")); + + try { + /* + * Test SimpleDateFormat.parse() and format() for original + * SimpleDateFormat instances + */ + testDateFormat1(); + + /* + * Test SimpleDateFormat.parse() and format() for modified + * SimpleDateFormat instances using an original minus sign, + * pattern, and diffenrent month names in DecimalFormat + */ + testDateFormat2(); + + /* + * Test SimpleDateFormat.parse() and format() for modified + * SimpleDateFormat instances using a fullwidth minus sign + */ + testDateFormat3(); + + /* + * Just to confirm that regressions aren't introduced in + * DecimalFormat. This cannot happen, though. Because I didn't + * change DecimalFormat at all. + */ + testNumberFormat(); + } + catch (Exception e) { + err = true; + System.err.println("Unexpected exception: " + e); + } + finally { + Locale.setDefault(defaultLocale); + TimeZone.setDefault(defaultTimeZone); + + if (err) { + System.err.println(BORDER + " Test failed."); + throw new RuntimeException("Date/Number formatting/parsing error."); + } else { + System.out.println(BORDER + " Test passed."); + } + } + } + + + // + // DateFormat test + // + private static void testDateFormat1() { + for (int i = 0; i < patterns.length; i++) { + System.out.println(BORDER); + for (int j = 0; j <= 1; j++) { + // Generate a pattern + String pattern = patterns[i].replaceAll(" ", delimiters[j]); + System.out.println("Pattern=\"" + pattern + "\""); + + System.out.println("*** DateFormat.format test in ar_EG"); + testDateFormatFormattingInRTL(pattern, i, j, null, localeEG, false); + + System.out.println("*** DateFormat.parse test in ar_EG"); + testDateFormatParsingInRTL(pattern, i, j, null, localeEG, false); + + System.out.println("*** DateFormat.format test in en_US"); + testDateFormatFormattingInLTR(pattern, i, j, null, localeUS, true); + + System.out.println("*** DateFormat.parse test in en_US"); + testDateFormatParsingInLTR(pattern, i, j, null, localeUS, true); + } + } + } + + private static void testDateFormat2() { + /* + * modified ar_EG Date&Time formatter : + * minus sign: ':' + * pattern: "#,##0.###" + * month names: In Arabic + * + * modified en_US Date&Time formatter : + * minus sign: ':' + * pattern: "#,##0.###;#,##0.###-" + * month names: In English + */ + DecimalFormat dfEG = (DecimalFormat)NumberFormat.getInstance(localeEG); + DecimalFormat dfUS = (DecimalFormat)NumberFormat.getInstance(localeUS); + + DecimalFormatSymbols dfsEG = dfEG.getDecimalFormatSymbols(); + DecimalFormatSymbols dfsUS = dfUS.getDecimalFormatSymbols(); + dfsEG.setMinusSign(originalMinusSign1); + dfsUS.setMinusSign(originalMinusSign1); + dfEG.setDecimalFormatSymbols(dfsUS); + dfUS.setDecimalFormatSymbols(dfsEG); + + String patternEG = dfEG.toPattern(); + String patternUS = dfUS.toPattern(); + + dfEG.applyPattern(patternUS); + dfUS.applyPattern(patternEG); + + for (int i = 0; i < patterns.length; i++) { + System.out.println(BORDER); + for (int j = 2; j <= 3; j++) { + // Generate a pattern + String pattern = patterns[i].replaceAll(" ", delimiters[j]); + System.out.println("Pattern=\"" + pattern + "\""); + + System.out.println("*** DateFormat.format test in modified en_US"); + testDateFormatFormattingInRTL(pattern, i, j, dfUS, localeUS, true); + + System.out.println("*** DateFormat.parse test in modified en_US"); + testDateFormatParsingInRTL(pattern, i, j, dfUS, localeUS, true); + + System.out.println("*** DateFormat.format test in modified ar_EG"); + testDateFormatFormattingInLTR(pattern, i, j, dfEG, localeEG, false); + + System.out.println("*** DateFormat.parse test in modified ar_EG"); + testDateFormatParsingInLTR(pattern, i, j, dfEG, localeEG, false); + } + } + } + + private static void testDateFormat3() { + /* + * modified ar_EG Date&Time formatter : + * minus sign: '\uff0d' // fullwidth minus + * pattern: "#,##0.###;#,##0.###-" + * month names: In Arabic + * + * modified en_US Date&Time formatter : + * minus sign: '\uff0d' // fullwidth minus + * pattern: "#,##0.###" + * month names: In English + */ + DecimalFormat dfEG = (DecimalFormat)NumberFormat.getInstance(localeEG); + DecimalFormat dfUS = (DecimalFormat)NumberFormat.getInstance(localeUS); + + DecimalFormatSymbols dfsEG = dfEG.getDecimalFormatSymbols(); + DecimalFormatSymbols dfsUS = dfUS.getDecimalFormatSymbols(); + dfsEG.setMinusSign(originalMinusSign2); + dfsUS.setMinusSign(originalMinusSign2); + dfEG.setDecimalFormatSymbols(dfsEG); + dfUS.setDecimalFormatSymbols(dfsUS); + + for (int i = 0; i < patterns.length; i++) { + System.out.println(BORDER); + for (int j = 4; j <= 5; j++) { + // Generate a pattern + String pattern = patterns[i].replaceAll(" ", delimiters[j]); + System.out.println("Pattern=\"" + pattern + "\""); + + System.out.println("*** DateFormat.format test in modified ar_EG"); + testDateFormatFormattingInRTL(pattern, i, j, dfEG, localeEG, false); + + System.out.println("*** DateFormat.parse test in modified ar_EG"); + testDateFormatParsingInRTL(pattern, i, j, dfEG, localeEG, false); + + System.out.println("*** DateFormat.format test in modified en_US"); + testDateFormatFormattingInLTR(pattern, i, j, dfUS, localeUS, true); + + System.out.println("*** DateFormat.parse test in modified en_US"); + testDateFormatParsingInLTR(pattern, i, j, dfUS, localeUS, true); + } + } + } + + private static void testDateFormatFormattingInRTL(String pattern, + int basePattern, + int delimiter, + NumberFormat nf, + Locale locale, + boolean useEnglishMonthName) { + Locale.setDefault(locale); + + SimpleDateFormat sdf = new SimpleDateFormat(pattern); + if (nf != null) { + sdf.setNumberFormat(nf); + } + for (int i = 0; i < datesToParse[basePattern].length; i++) { + if (datesEG[basePattern][i] == null) { + continue; + } + + String expected = formattedDatesEG[basePattern][i] + .replaceAll("JUNE", (useEnglishMonthName ? + JuneInEnglish : JuneInArabic)) + .replaceAll("JULY", (useEnglishMonthName ? + JulyInEnglish : JulyInArabic)) + .replaceAll(" ", delimiters[delimiter]); + testDateFormatFormatting(sdf, pattern, datesEG[basePattern][i], + expected, locale.toString()); + } + } + + private static void testDateFormatFormattingInLTR(String pattern, + int basePattern, + int delimiter, + NumberFormat nf, + Locale locale, + boolean useEnglishMonthName) { + Locale.setDefault(locale); + + SimpleDateFormat sdf = new SimpleDateFormat(pattern); + if (nf != null) { + sdf.setNumberFormat(nf); + } + for (int i = 0; i < datesToParse[basePattern].length; i++) { + if (datesUS[basePattern][i] == null) { + continue; + } + + String expected = formattedDatesUS[basePattern][i] + .replaceAll("JUNE", (useEnglishMonthName ? + JuneInEnglish : JuneInArabic)) + .replaceAll("JULY", (useEnglishMonthName ? + JulyInEnglish : JulyInArabic)) + .replaceAll(" ", delimiters[delimiter]); + testDateFormatFormatting(sdf, pattern, datesUS[basePattern][i], + expected, locale.toString()); + } + } + + private static void testDateFormatFormatting(SimpleDateFormat sdf, + String pattern, + GregorianCalendar givenGC, + String expected, + String locale) { + Date given = givenGC.getTime(); + String str = sdf.format(given); + if (expected.equals(str)) { + if (verbose) { + System.out.print(" Passed: SimpleDateFormat("); + System.out.print(locale + ", \"" + pattern + "\").format("); + System.out.println(given + ")"); + + System.out.print(" ---> \"" + str + "\" "); + System.out.println((givenGC.get(ERA) == BC) ? "(BC)" : "(AD)"); + } + } else { + err = true; + + System.err.print(" Failed: Unexpected SimpleDateFormat("); + System.out.print(locale + ", \"" + pattern + "\").format("); + System.out.println(given + ") result."); + + System.out.println(" Expected: \"" + expected + "\""); + + System.out.print(" Got: \"" + str + "\" "); + System.out.println((givenGC.get(ERA) == BC) ? "(BC)" : "(AD)"); + } + } + + private static void testDateFormatParsingInRTL(String pattern, + int basePattern, + int delimiter, + NumberFormat nf, + Locale locale, + boolean useEnglishMonthName) { + Locale.setDefault(locale); + + SimpleDateFormat sdf = new SimpleDateFormat(pattern); + if (nf != null) { + sdf.setNumberFormat(nf); + } + for (int i = 0; i < datesToParse[basePattern].length; i++) { + String given = datesToParse[basePattern][i] + .replaceAll(" ", specialDelimiters[0][delimiter]) + .replaceAll(" ", delimiters[delimiter]); + + testDateFormatParsing(sdf, pattern, + given.replaceAll("JULY", (useEnglishMonthName ? + JulyInEnglish : JulyInArabic)), + datesEG[basePattern][i], locale.toString()); + } + } + + private static void testDateFormatParsingInLTR(String pattern, + int basePattern, + int delimiter, + NumberFormat nf, + Locale locale, + boolean useEnglishMonthName) { + Locale.setDefault(locale); + + SimpleDateFormat sdf = new SimpleDateFormat(pattern); + if (nf != null) { + sdf.setNumberFormat(nf); + } + for (int i = 0; i < datesToParse[basePattern].length; i++) { + String given = datesToParse[basePattern][i] + .replaceAll(" ", specialDelimiters[1][delimiter]) + .replaceAll(" ", delimiters[delimiter]); + + testDateFormatParsing(sdf, pattern, + given.replaceAll("JULY", (useEnglishMonthName ? + JulyInEnglish : JulyInArabic)), + datesUS[basePattern][i], locale.toString()); + } + } + + private static void testDateFormatParsing(SimpleDateFormat sdf, + String pattern, + String given, + GregorianCalendar expectedGC, + String locale) { + try { + Date d = sdf.parse(given); + if (expectedGC == null) { + err = true; + System.err.print(" Failed: SimpleDateFormat(" + locale); + System.err.print(", \"" + pattern + "\").parse(\"" + given); + System.err.println("\") should have thrown ParseException"); + } else if (expectedGC.getTime().equals(d)) { + if (verbose) { + System.out.print(" Passed: SimpleDateFormat(" + locale); + System.out.print(", \"" + pattern + "\").parse(\"" + given); + System.out.println("\")"); + + System.out.print(" ---> " + d + " (" + d.getTime()); + System.out.println(")"); + } + } else { + err = true; + System.err.print(" Failed: SimpleDateFormat(" + locale); + System.err.print(", \"" + pattern + "\").parse(\"" + given); + System.err.println("\")"); + + System.err.print(" Expected: " + expectedGC.getTime()); + System.err.println(" (" + d.getTime() + ")"); + + System.err.print(" Got: " + d + " (" + d.getTime()); + System.err.println(")"); + + System.err.print(" Pattern: \""); + System.err.print(((DecimalFormat)sdf.getNumberFormat()).toPattern()); + System.err.println("\""); + } + } + catch (ParseException pe) { + if (expectedGC == null) { + if (verbose) { + System.out.print(" Passed: SimpleDateFormat(" + locale); + System.out.print(", \"" + pattern + "\").parse(\"" + given); + System.out.println("\")"); + + System.out.println(" threw ParseException as expected"); + } + } else { + err = true; + System.err.println(" Failed: Unexpected exception with"); + + System.err.print(" SimpleDateFormat(" + locale); + System.err.print(", \"" + pattern + "\").parse(\""); + System.err.println(given + "\"):"); + + System.err.println(" " + pe); + + System.err.print(" Pattern: \""); + System.err.print(((DecimalFormat)sdf.getNumberFormat()).toPattern()); + System.err.println("\""); + + System.err.print(" Month 0: "); + System.err.println(sdf.getDateFormatSymbols().getMonths()[0]); + } + } + } + + + // + // NumberFormat test + // + private static void testNumberFormat() { + NumberFormat nfEG = NumberFormat.getInstance(localeEG); + NumberFormat nfUS = NumberFormat.getInstance(localeUS); + + System.out.println("*** DecimalFormat.format test in ar_EG"); + testNumberFormatFormatting(nfEG, -123456789, "123,456,789-", "ar_EG"); + testNumberFormatFormatting(nfEG, -456, "456-", "ar_EG"); + + System.out.println("*** DecimalFormat.parse test in ar_EG"); + testNumberFormatParsing(nfEG, "123-", new Long(-123), "ar_EG"); + testNumberFormatParsing(nfEG, "123--", new Long(-123), "ar_EG"); + testNumberFormatParsingCheckException(nfEG, "-123", 0, "ar_EG"); + + System.out.println("*** DecimalFormat.format test in en_US"); + testNumberFormatFormatting(nfUS, -123456789, "-123,456,789", "en_US"); + testNumberFormatFormatting(nfUS, -456, "-456", "en_US"); + + System.out.println("*** DecimalFormat.parse test in en_US"); + testNumberFormatParsing(nfUS, "123-", new Long(123), "en_US"); + testNumberFormatParsing(nfUS, "-123", new Long(-123), "en_US"); + testNumberFormatParsingCheckException(nfUS, "--123", 0, "en_US"); + } + + private static void testNumberFormatFormatting(NumberFormat nf, + int given, + String expected, + String locale) { + String str = nf.format(given); + if (expected.equals(str)) { + if (verbose) { + System.out.print(" Passed: NumberFormat(" + locale); + System.out.println(").format(" + given + ")"); + + System.out.println(" ---> \"" + str + "\""); + } + } else { + err = true; + System.err.print(" Failed: Unexpected NumberFormat(" + locale); + System.err.println(").format(" + given + ") result."); + + System.err.println(" Expected: \"" + expected + "\""); + + System.err.println(" Got: \"" + str + "\""); + } + } + + private static void testNumberFormatParsing(NumberFormat nf, + String given, + Number expected, + String locale) { + try { + Number n = nf.parse(given); + if (n.equals(expected)) { + if (verbose) { + System.out.print(" Passed: NumberFormat(" + locale); + System.out.println(").parse(\"" + given + "\")"); + + System.out.println(" ---> " + n); + } + } else { + err = true; + System.err.print(" Failed: Unexpected NumberFormat(" + locale); + System.err.println(").parse(\"" + given + "\") result."); + + System.err.println(" Expected: " + expected); + + System.err.println(" Got: " + n); + } + } + catch (ParseException pe) { + err = true; + System.err.print(" Failed: Unexpected exception with NumberFormat("); + System.err.println(locale + ").parse(\"" + given + "\") :"); + + System.err.println(" " + pe); + } + } + + private static void testNumberFormatParsingCheckException(NumberFormat nf, + String given, + int expected, + String locale) { + try { + Number n = nf.parse(given); + err = true; + + System.err.print(" Failed: NumberFormat(" + locale); + System.err.println(").parse(\"" + given + "\")"); + + System.err.println(" should have thrown ParseException"); + } + catch (ParseException pe) { + int errorOffset = pe.getErrorOffset(); + if (errorOffset == expected) { + if (verbose) { + System.out.print(" Passed: NumberFormat(" + locale); + System.out.println(").parse(\"" + given + "\")"); + + System.out.print(" threw ParseException as expected, and its errorOffset was correct: "); + System.out.println(errorOffset); + } + } else { + err = true; + System.err.print(" Failed: NumberFormat(" + locale); + System.err.println(").parse(\"" + given + "\")"); + + System.err.print(" threw ParseException as expected, but its errorOffset was incorrect: "); + System.err.println(errorOffset); + } + } + } + +} -- GitLab From e2644ea0c39309e4b6c85f15064e34de91ba84eb Mon Sep 17 00:00:00 2001 From: peytoia Date: Mon, 8 Sep 2008 14:48:14 +0900 Subject: [PATCH 093/139] 6650748: (tz) Java runtime doesn't detect VET time zone correctly on Windows Reviewed-by: okutsu --- src/windows/lib/tzmappings | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/windows/lib/tzmappings b/src/windows/lib/tzmappings index 998ff0754..a45200b75 100644 --- a/src/windows/lib/tzmappings +++ b/src/windows/lib/tzmappings @@ -65,8 +65,8 @@ Newfoundland:-1,81::America/St_Johns: Newfoundland Standard Time:-1,81::America/St_Johns: Pacific SA:-1,82::America/Santiago: Pacific SA Standard Time:-1,82::America/Santiago: -SA Western:-1,82::America/Caracas: -SA Western Standard Time:-1,82::America/Caracas: +SA Western:-1,82:BO:America/La_Paz: +SA Western Standard Time:-1,82:BO:America/La_Paz: SA Pacific:-1,83::America/Bogota: SA Pacific Standard Time:-1,83::America/Bogota: US Eastern:-1,84::America/Indianapolis: @@ -177,3 +177,4 @@ Jordan Standard Time:908,908:JO:Asia/Amman: Middle East Standard Time:909,909:LB:Asia/Beirut: Armenian Standard Time:910,910:AM:Asia/Yerevan: Montevideo Standard Time:911,911:UY:America/Montevideo: +Venezuela Standard Time:912,912::America/Caracas: -- GitLab From 11d254cf8b4ae1052fc8237ce325a923d049e2af Mon Sep 17 00:00:00 2001 From: weijun Date: Mon, 8 Sep 2008 14:17:22 +0800 Subject: [PATCH 094/139] 6740833: krb5.conf does not accept kdc=hostname (no spaces around =) Reviewed-by: xuelei --- src/share/classes/sun/security/krb5/Config.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/share/classes/sun/security/krb5/Config.java b/src/share/classes/sun/security/krb5/Config.java index 56e5fb1d6..2a16b983f 100644 --- a/src/share/classes/sun/security/krb5/Config.java +++ b/src/share/classes/sun/security/krb5/Config.java @@ -803,7 +803,7 @@ public class Config { for (int j = 0; j < line.length(); j++) { if (line.charAt(j) == '=') { int index; - key = line.substring(0, j - 1).trim(); + key = line.substring(0, j).trim(); if (! exists(key, keyVector)) { keyVector.addElement(key); nameVector = new Vector (); -- GitLab From e74d9b03e263ca832d043c079f865d51ca5add9e Mon Sep 17 00:00:00 2001 From: peytoia Date: Mon, 8 Sep 2008 15:21:55 +0900 Subject: [PATCH 095/139] 6466476: (tz) Introduction of tzdata2005r can introduce incompatility issues with some JDK1.1 3-letter TZ Ids Reviewed-by: okutsu --- make/java/java/FILES_java.gmk | 1 + .../sun/util/calendar/TzIDOldMapping.java | 68 +++++++++++ .../classes/sun/util/calendar/ZoneInfo.java | 30 ++++- test/java/util/TimeZone/OldIDMappingTest.java | 110 ++++++++++++++++++ test/java/util/TimeZone/OldIDMappingTest.sh | 59 ++++++++++ 5 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 src/share/classes/sun/util/calendar/TzIDOldMapping.java create mode 100644 test/java/util/TimeZone/OldIDMappingTest.java create mode 100644 test/java/util/TimeZone/OldIDMappingTest.sh diff --git a/make/java/java/FILES_java.gmk b/make/java/java/FILES_java.gmk index b4a07fc35..18b1d3511 100644 --- a/make/java/java/FILES_java.gmk +++ b/make/java/java/FILES_java.gmk @@ -209,6 +209,7 @@ JAVA_JAVA_java = \ sun/util/TimeZoneNameUtility.java \ sun/util/calendar/ZoneInfo.java \ sun/util/calendar/ZoneInfoFile.java \ + sun/util/calendar/TzIDOldMapping.java \ java/util/TooManyListenersException.java \ java/util/Comparator.java \ java/util/Collections.java \ diff --git a/src/share/classes/sun/util/calendar/TzIDOldMapping.java b/src/share/classes/sun/util/calendar/TzIDOldMapping.java new file mode 100644 index 000000000..1e3c6caa7 --- /dev/null +++ b/src/share/classes/sun/util/calendar/TzIDOldMapping.java @@ -0,0 +1,68 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.util.calendar; + +import java.util.Map; +import java.util.HashMap; + +class TzIDOldMapping { + static final Map MAP = new HashMap(); + static { + String[][] oldmap = { + { "ACT", "Australia/Darwin" }, + { "AET", "Australia/Sydney" }, + { "AGT", "America/Argentina/Buenos_Aires" }, + { "ART", "Africa/Cairo" }, + { "AST", "America/Anchorage" }, + { "BET", "America/Sao_Paulo" }, + { "BST", "Asia/Dhaka" }, + { "CAT", "Africa/Harare" }, + { "CNT", "America/St_Johns" }, + { "CST", "America/Chicago" }, + { "CTT", "Asia/Shanghai" }, + { "EAT", "Africa/Addis_Ababa" }, + { "ECT", "Europe/Paris" }, + { "EST", "America/New_York" }, + { "HST", "Pacific/Honolulu" }, + { "IET", "America/Indianapolis" }, + { "IST", "Asia/Calcutta" }, + { "JST", "Asia/Tokyo" }, + { "MIT", "Pacific/Apia" }, + { "MST", "America/Denver" }, + { "NET", "Asia/Yerevan" }, + { "NST", "Pacific/Auckland" }, + { "PLT", "Asia/Karachi" }, + { "PNT", "America/Phoenix" }, + { "PRT", "America/Puerto_Rico" }, + { "PST", "America/Los_Angeles" }, + { "SST", "Pacific/Guadalcanal" }, + { "VST", "Asia/Saigon" }, + }; + for (String[] pair : oldmap) { + MAP.put(pair[0], pair[1]); + } + } +} diff --git a/src/share/classes/sun/util/calendar/ZoneInfo.java b/src/share/classes/sun/util/calendar/ZoneInfo.java index 709ac3815..cde5959e4 100644 --- a/src/share/classes/sun/util/calendar/ZoneInfo.java +++ b/src/share/classes/sun/util/calendar/ZoneInfo.java @@ -28,10 +28,12 @@ package sun.util.calendar; import java.io.IOException; import java.io.ObjectInputStream; import java.lang.ref.SoftReference; +import java.security.AccessController; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.SimpleTimeZone; import java.util.TimeZone; @@ -77,6 +79,14 @@ public class ZoneInfo extends TimeZone { private static final long ABBR_MASK = 0xf00L; private static final int TRANSITION_NSHIFT = 12; + // Flag for supporting JDK backward compatible IDs, such as "EST". + private static final boolean USE_OLDMAPPING; + static { + String oldmapping = AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("sun.timezone.ids.oldmapping", "false")).toLowerCase(Locale.ROOT); + USE_OLDMAPPING = (oldmapping.equals("yes") || oldmapping.equals("true")); + } + private static final CalendarSystem gcal = CalendarSystem.getGregorianCalendar(); /** @@ -595,9 +605,21 @@ public class ZoneInfo extends TimeZone { * time zone of the ID. */ public static TimeZone getTimeZone(String ID) { - ZoneInfo zi = null; + String givenID = null; + + /* + * If old JDK compatibility is specified, get the old alias + * name. + */ + if (USE_OLDMAPPING) { + String compatibleID = TzIDOldMapping.MAP.get(ID); + if (compatibleID != null) { + givenID = ID; + ID = compatibleID; + } + } - zi = ZoneInfoFile.getZoneInfo(ID); + ZoneInfo zi = ZoneInfoFile.getZoneInfo(ID); if (zi == null) { // if we can't create an object for the ID, try aliases. try { @@ -616,6 +638,10 @@ public class ZoneInfo extends TimeZone { // ignore exceptions } } + + if (givenID != null && zi != null) { + zi.setID(givenID); + } return zi; } diff --git a/test/java/util/TimeZone/OldIDMappingTest.java b/test/java/util/TimeZone/OldIDMappingTest.java new file mode 100644 index 000000000..90752b342 --- /dev/null +++ b/test/java/util/TimeZone/OldIDMappingTest.java @@ -0,0 +1,110 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * See OldMappingTest.sh + */ + +import java.lang.reflect.*; +import java.util.*; + +public class OldIDMappingTest { + private static final String MAPPING_PROPERTY_NAME = "sun.timezone.ids.oldmapping"; + private static final Map newmap = new HashMap(); + static { + // Add known new mappings + newmap.put("EST", "EST"); + newmap.put("MST", "MST"); + newmap.put("HST", "HST"); + } + + public static void main(String[] args) { + boolean useOldMapping = true; + String arg = args[0]; + if (arg.equals("-new")) { + useOldMapping = false; + } else if (arg.equals("-old")) { + useOldMapping = true; + } else { + throw new RuntimeException("-old or -new must be specified; got " + arg); + } + + // Get a Field for TzIDOldMapping in sun.util.calendar. + Map oldmap = null; + try { + Class oldmapClass = Class.forName("sun.util.calendar.TzIDOldMapping"); + Field map = oldmapClass.getDeclaredField("MAP"); + map.setAccessible(true); + oldmap = (Map) map.get(null); + } catch (Exception e) { + throw new RuntimeException("can't get TzIDOldMapping.MAP", e); + } + + String prop = System.getProperty(MAPPING_PROPERTY_NAME); + System.out.println(MAPPING_PROPERTY_NAME + "=" + prop); + + // Try the test multiple times with modifying TimeZones to + // make sure TimeZone instances for the old mapping are + // properly copied (defensive copy). + for (int count = 0; count < 3; count++) { + for (String id : oldmap.keySet()) { + TimeZone tzAlias = TimeZone.getTimeZone(id); + TimeZone tz = TimeZone.getTimeZone(oldmap.get(id)); + if (useOldMapping) { + if (!tzAlias.hasSameRules(tz)) { + throw new RuntimeException("OLDMAP: " + MAPPING_PROPERTY_NAME + "=" + prop + ": " + + id + " isn't an alias of " + oldmap.get(id)); + } + if (count == 0) { + System.out.println(" " + id + " => " + oldmap.get(id)); + } + tzAlias.setRawOffset(tzAlias.getRawOffset() * count); + } else { + if (!newmap.containsKey(id)) { + // ignore ids not contained in the new map + if (count == 0) { + System.out.println(" " + id + " => " + oldmap.get(id)); + } + tzAlias.setRawOffset(tzAlias.getRawOffset() * count); + continue; + } + if (tzAlias.hasSameRules(tz)) { + throw new RuntimeException("NEWMAP: " + MAPPING_PROPERTY_NAME + "=" + prop + ": " + + id + " is an alias of " + oldmap.get(id)); + } + tz = TimeZone.getTimeZone(newmap.get(id)); + if (!tzAlias.hasSameRules(tz)) { + throw new RuntimeException("NEWMAP: " + MAPPING_PROPERTY_NAME + "=" + prop + ": " + + id + " isn't an alias of " + newmap.get(id)); + } + if (count == 0) { + System.out.println(" " + id + " => " + newmap.get(id)); + } + tzAlias.setRawOffset(tzAlias.getRawOffset() * count); + } + } + } + } +} diff --git a/test/java/util/TimeZone/OldIDMappingTest.sh b/test/java/util/TimeZone/OldIDMappingTest.sh new file mode 100644 index 000000000..156d4f60a --- /dev/null +++ b/test/java/util/TimeZone/OldIDMappingTest.sh @@ -0,0 +1,59 @@ +# Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code 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 General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. + +# @test +# @bug 6466476 +# @summary Compatibility test for the old JDK ID mapping and Olson IDs +# @build OldIDMappingTest +# @run shell OldIDMappingTest.sh + +: ${TESTJAVA:=${JAVA_HOME}} +: ${TESTCLASSES:="`pwd`"} + +JAVA="${TESTJAVA}/bin/java" + +STATUS=0 + +# Expecting the new (Olson compatible) mapping (default) +for I in "" " " no No NO false False FALSE Hello +do + if [ x"$I" != x ]; then + D="-Dsun.timezone.ids.oldmapping=${I}" + fi + if ! ${JAVA} ${D} -cp ${TESTCLASSES} OldIDMappingTest -new; then + STATUS=1 + fi +done + +# Expecting the old mapping +for I in true True TRUE yes Yes YES +do + if [ "x$I" != x ]; then + D="-Dsun.timezone.ids.oldmapping=${I}" + fi + if ! ${JAVA} ${D} -cp ${TESTCLASSES} OldIDMappingTest -old; then + STATUS=1 + fi +done + +exit ${STATUS} -- GitLab From 921a835c3f566f56cd6f7aa9e831eeaefdc2ddce Mon Sep 17 00:00:00 2001 From: peytoia Date: Mon, 8 Sep 2008 17:35:07 +0900 Subject: [PATCH 096/139] 6730743: (tz) Support tzdata2008e Reviewed-by: okutsu --- make/sun/javazic/tzdata/VERSION | 2 +- make/sun/javazic/tzdata/africa | 131 ++++++++- make/sun/javazic/tzdata/asia | 185 ++++++++++++- make/sun/javazic/tzdata/australasia | 4 +- make/sun/javazic/tzdata/backward | 3 + make/sun/javazic/tzdata/europe | 75 ++++- make/sun/javazic/tzdata/iso3166.tab | 4 +- make/sun/javazic/tzdata/leapseconds | 32 ++- make/sun/javazic/tzdata/northamerica | 77 +++++- make/sun/javazic/tzdata/southamerica | 260 +++++++++++++++--- make/sun/javazic/tzdata/zone.tab | 15 +- .../sun/util/resources/TimeZoneNames.java | 14 +- .../sun/util/resources/TimeZoneNames_de.java | 14 +- .../sun/util/resources/TimeZoneNames_es.java | 14 +- .../sun/util/resources/TimeZoneNames_fr.java | 14 +- .../sun/util/resources/TimeZoneNames_it.java | 14 +- .../sun/util/resources/TimeZoneNames_ja.java | 14 +- .../sun/util/resources/TimeZoneNames_ko.java | 14 +- .../sun/util/resources/TimeZoneNames_sv.java | 14 +- .../util/resources/TimeZoneNames_zh_CN.java | 14 +- .../util/resources/TimeZoneNames_zh_TW.java | 14 +- 21 files changed, 794 insertions(+), 134 deletions(-) diff --git a/make/sun/javazic/tzdata/VERSION b/make/sun/javazic/tzdata/VERSION index 95ed6ee53..2f4f9df80 100644 --- a/make/sun/javazic/tzdata/VERSION +++ b/make/sun/javazic/tzdata/VERSION @@ -21,4 +21,4 @@ # CA 95054 USA or visit www.sun.com if you need additional information or # have any questions. # -tzdata2007h +tzdata2008e diff --git a/make/sun/javazic/tzdata/africa b/make/sun/javazic/tzdata/africa index f1f4b5f1b..8141f0809 100644 --- a/make/sun/javazic/tzdata/africa +++ b/make/sun/javazic/tzdata/africa @@ -409,9 +409,63 @@ Zone Africa/Nouakchott -1:03:48 - LMT 1912 0:00 - GMT # Mauritius + +# From Steffen Thorsen (2008-06-25): +# Mauritius plans to observe DST from 2008-11-01 to 2009-03-31 on a trial +# basis.... +# It seems that Mauritius observed daylight saving time from 1982-10-10 to +# 1983-03-20 as well, but that was not successful.... +# http://www.timeanddate.com/news/time/mauritius-daylight-saving-time.html + +# From Alex Krivenyshev (2008-06-25): +# http://economicdevelopment.gov.mu/portal/site/Mainhomepage/menuitem.a42b24128104d9845dabddd154508a0c/?content_id=0a7cee8b5d69a110VgnVCM1000000a04a8c0RCRD + +# From Arthur David Olson (2008-06-30): +# The www.timeanddate.com article cited by Steffen Thorsen notes that "A +# final decision has yet to be made on the times that daylight saving +# would begin and end on these dates." As a place holder, use midnight. + +# From Paul Eggert (2008-06-30): +# Follow Thorsen on DST in 1982/1983, instead of Shanks & Pottenger. + +# From Steffen Thorsen (2008-07-10): +# According to +# +# http://www.lexpress.mu/display_article.php?news_id=111216 +# +# (in French), Mauritius will start and end their DST a few days earlier +# than previously announced (2008-11-01 to 2009-03-31). The new start +# date is 2008-10-26 at 02:00 and the new end date is 2009-03-27 (no time +# given, but it is probably at either 2 or 3 wall clock time). +# +# A little strange though, since the article says that they moved the date +# to align itself with Europe and USA which also change time on that date, +# but that means they have not paid attention to what happened in +# USA/Canada last year (DST ends first Sunday in November). I also wonder +# why that they end on a Friday, instead of aligning with Europe which +# changes two days later. + +# From Alex Krivenyshev (2008-07-11): +# Seems that English language article "The revival of daylight saving +# time: Energy conservation?"-# No. 16578 (07/11/2008) was originally +# published on Monday, June 30, 2008... +# +# I guess that article in French "Le gouvernement avance l'introduction +# de l'heure d'ete" stating that DST in Mauritius starting on October 26 +# and ending on March 27, 2009 is the most recent one. +# ... +# +# http://www.worldtimezone.com/dst_news/dst_news_mauritius02.html +# + +# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +Rule Mauritius 1982 only - Oct 10 0:00 1:00 S +Rule Mauritius 1983 only - Mar 21 0:00 0 - +Rule Mauritius 2008 only - Oct 26 2:00s 1:00 S +Rule Mauritius 2009 only - Mar 27 2:00s 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis - 4:00 - MUT # Mauritius Time + 4:00 Mauritius MU%sT # Mauritius Time # Agalega Is, Rodriguez # no information; probably like Indian/Mauritius @@ -422,6 +476,77 @@ Zone Indian/Mayotte 3:00:56 - LMT 1911 Jul # Mamoutzou # Morocco # See the `europe' file for Spanish Morocco (Africa/Ceuta). + +# From Alex Krivenyshev (2008-05-09): +# Here is an article that Morocco plan to introduce Daylight Saving Time between +# 1 June, 2008 and 27 September, 2008. +# +# "... Morocco is to save energy by adjusting its clock during summer so it will +# be one hour ahead of GMT between 1 June and 27 September, according to +# Communication Minister and Gov ernment Spokesman, Khalid Naciri...." +# +# +# http://www.worldtimezone.net/dst_news/dst_news_morocco01.html +# +# OR +# +# http://en.afrik.com/news11892.html +# + +# From Alex Krivenyshev (2008-05-09): +# The Morocco time change can be confirmed on Morocco web site Maghreb Arabe Presse: +# +# http://www.map.ma/eng/sections/box3/morocco_shifts_to_da/view +# +# +# Morocco shifts to daylight time on June 1st through September 27, Govt. +# spokesman. + +# From Patrice Scattolin (2008-05-09): +# According to this article: +# +# http://www.avmaroc.com/actualite/heure-dete-comment-a127896.html +# +# (and republished here: +# +# http://www.actu.ma/heure-dete-comment_i127896_0.html +# +# ) +# the changes occurs at midnight: +# +# saturday night may 31st at midnight (which in french is to be +# intrepreted as the night between saturday and sunday) +# sunday night the 28th at midnight +# +# Seeing that the 28th is monday, I am guessing that she intends to say +# the midnight of the 28th which is the midnight between sunday and +# monday, which jives with other sources that say that it's inclusive +# june1st to sept 27th. +# +# The decision was taken by decree *2-08-224 *but I can't find the decree +# published on the web. +# +# It's also confirmed here: +# +# http://www.maroc.ma/NR/exeres/FACF141F-D910-44B0-B7FA-6E03733425D1.htm +# +# on a government portal as being between june 1st and sept 27th (not yet +# posted in english). +# +# The following google query will generate many relevant hits: +# +# http://www.google.com/search?hl=en&q=Conseil+de+gouvernement+maroc+heure+avance&btnG=Search +# + +# From Alex Krivenyshev (2008-05-09): +# Is Western Sahara (part which administrated by Morocco) going to follow +# Morocco DST changes? Any information? What about other part of +# Western Sahara - under administration of POLISARIO Front (also named +# SADR Saharawi Arab Democratic Republic)? + +# From Arthur David Olson (2008-05-09): +# XXX--guess that it is only Morocco for now; guess only 2008 for now. + # RULE NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Morocco 1939 only - Sep 12 0:00 1:00 S Rule Morocco 1939 only - Nov 19 0:00 0 - @@ -438,11 +563,13 @@ Rule Morocco 1976 only - Aug 1 0:00 0 - Rule Morocco 1977 only - Sep 28 0:00 0 - Rule Morocco 1978 only - Jun 1 0:00 1:00 S Rule Morocco 1978 only - Aug 4 0:00 0 - +Rule Morocco 2008 only - Jun 1 0:00 1:00 S +Rule Morocco 2008 only - Sep 28 0:00 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Africa/Casablanca -0:30:20 - LMT 1913 Oct 26 0:00 Morocco WE%sT 1984 Mar 16 1:00 - CET 1986 - 0:00 - WET + 0:00 Morocco WE%sT # Western Sahara Zone Africa/El_Aaiun -0:52:48 - LMT 1934 Jan -1:00 - WAT 1976 Apr 14 diff --git a/make/sun/javazic/tzdata/asia b/make/sun/javazic/tzdata/asia index bec343b77..be5819700 100644 --- a/make/sun/javazic/tzdata/asia +++ b/make/sun/javazic/tzdata/asia @@ -251,6 +251,28 @@ Rule PRC 1987 1991 - Apr Sun>=10 0:00 1:00 D # (could be true), for the moment I am assuming that those two # counties are mistakes in the astro.com data. +# From Paul Eggert (2008-02-11): +# I just now checked Google News for western news sources that talk +# about China's single time zone, and couldn't find anything before 1986 +# talking about China being in one time zone. (That article was: Jim +# Mann, "A clumsy embrace for another western custom: China on daylight +# time--sort of", Los Angeles Times, 1986-05-05. By the way, this +# article confirms the tz database's data claiming that China began +# observing daylight saving time in 1986. +# +# From Thomas S. Mullaney (2008-02-11): +# I think you're combining two subjects that need to treated +# separately: daylight savings (which, you're correct, wasn't +# implemented until the 1980s) and the unified time zone centered near +# Beijing (which was implemented in 1949). Briefly, there was also a +# "Lhasa Time" in Tibet and "Urumqi Time" in Xinjiang. The first was +# ceased, and the second eventually recognized (again, in the 1980s). +# +# From Paul Eggert (2008-06-30): +# There seems to be a good chance China switched to a single time zone in 1949 +# rather than in 1980 as Shanks & Pottenger have it, but we don't have a +# reliable documentary source saying so yet, so for now we still go with +# Shanks & Pottenger. # Zone NAME GMTOFF RULES FORMAT [UNTIL] # Changbai Time ("Long-white Time", Long-white = Heilongjiang area) @@ -468,13 +490,13 @@ Zone Asia/Dili 8:22:20 - LMT 1912 # India # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Calcutta 5:53:28 - LMT 1880 # Kolkata +Zone Asia/Kolkata 5:53:28 - LMT 1880 # Kolkata 5:53:20 - HMT 1941 Oct # Howrah Mean Time? 6:30 - BURT 1942 May 15 # Burma Time 5:30 - IST 1942 Sep 5:30 1:00 IST 1945 Oct 15 5:30 - IST -# The following are like Asia/Calcutta: +# The following are like Asia/Kolkata: # Andaman Is # Lakshadweep (Laccadive, Minicoy and Amindivi Is) # Nicobar Is @@ -599,6 +621,15 @@ Zone Asia/Jayapura 9:22:48 - LMT 1932 Nov # daylight saving time ... # http://uk.reuters.com/article/oilRpt/idUKBLA65048420070916 # +# From Roozbeh Pournader (2007-11-05): +# This is quoted from Official Gazette of the Islamic Republic of +# Iran, Volume 63, Number 18242, dated Tuesday 1386/6/24 +# [2007-10-16]. I am doing the best translation I can:... +# The official time of the country will be moved forward for one hour +# on the 24 hours of the first day of the month of Farvardin and will +# be changed back to its previous state on the 24 hours of the +# thirtieth day of Shahrivar. +# # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Iran 1978 1980 - Mar 21 0:00 1:00 D Rule Iran 1978 only - Oct 21 0:00 0 S @@ -673,6 +704,21 @@ Zone Asia/Tehran 3:25:44 - LMT 1916 # # So we'll ignore the Economist's claim. +# From Steffen Thorsen (2008-03-10): +# The cabinet in Iraq abolished DST last week, according to the following +# news sources (in Arabic): +# +# http://www.aljeeran.net/wesima_articles/news-20080305-98602.html +# +# +# http://www.aswataliraq.info/look/article.tpl?id=2047&IdLanguage=17&IdPublication=4&NrArticle=71743&NrIssue=1&NrSection=10 +# +# +# We have published a short article in English about the change: +# +# http://www.timeanddate.com/news/time/iraq-dumps-daylight-saving.html +# + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Iraq 1982 only - May 1 0:00 1:00 D Rule Iraq 1982 1984 - Oct 1 0:00 0 S @@ -683,8 +729,8 @@ Rule Iraq 1986 1990 - Mar lastSun 1:00s 1:00 D # IATA SSIM (1991/1996) says Apr 1 12:01am UTC; guess the `:01' is a typo. # Shanks & Pottenger say Iraq did not observe DST 1992/1997; ignore this. # -Rule Iraq 1991 max - Apr 1 3:00s 1:00 D -Rule Iraq 1991 max - Oct 1 3:00s 0 S +Rule Iraq 1991 2007 - Apr 1 3:00s 1:00 D +Rule Iraq 1991 2007 - Oct 1 3:00s 0 S # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Asia/Baghdad 2:57:40 - LMT 1890 2:57:36 - BMT 1918 # Baghdad Mean Time? @@ -1374,6 +1420,42 @@ Zone Indian/Maldives 4:54:00 - LMT 1880 # Male # They decided not to adopt daylight-saving time.... # http://www.mongolnews.mn/index.php?module=unuudur&sec=view&id=15742 +# From Deborah Goldsmith (2008-03-30): +# We received a bug report claiming that the tz database UTC offset for +# Asia/Choibalsan (GMT+09:00) is incorrect, and that it should be GMT +# +08:00 instead. Different sources appear to disagree with the tz +# database on this, e.g.: +# +# +# http://www.timeanddate.com/worldclock/city.html?n=1026 +# +# +# http://www.worldtimeserver.com/current_time_in_MN.aspx +# +# +# both say GMT+08:00. + +# From Steffen Thorsen (2008-03-31): +# eznis airways, which operates several domestic flights, has a flight +# schedule here: +# +# http://www.eznis.com/Container.jsp?id=112 +# +# (click the English flag for English) +# +# There it appears that flights between Choibalsan and Ulaanbatar arrive +# about 1:35 - 1:50 hours later in local clock time, no matter the +# direction, while Ulaanbaatar-Khvod takes 2 hours in the Eastern +# direction and 3:35 back, which indicates that Ulaanbatar and Khvod are +# in different time zones (like we know about), while Choibalsan and +# Ulaanbatar are in the same time zone (correction needed). + +# From Arthur David Olson (2008-05-19): +# Assume that Choibalsan is indeed offset by 8:00. +# XXX--in the absence of better information, assume that transition +# was at the start of 2008-03-31 (the day of Steffen Thorsen's report); +# this is almost surely wrong. + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Mongol 1983 1984 - Apr 1 0:00 1:00 S Rule Mongol 1983 only - Oct 1 0:00 0 - @@ -1409,7 +1491,8 @@ Zone Asia/Ulaanbaatar 7:07:32 - LMT 1905 Aug Zone Asia/Choibalsan 7:38:00 - LMT 1905 Aug 7:00 - ULAT 1978 8:00 - ULAT 1983 Apr - 9:00 Mongol CHO%sT # Choibalsan Time + 9:00 Mongol CHO%sT 2008 Mar 31 # Choibalsan Time + 8:00 Mongol CHO%sT # Nepal # Zone NAME GMTOFF RULES FORMAT [UNTIL] @@ -1459,10 +1542,32 @@ Zone Asia/Muscat 3:54:20 - LMT 1920 # The minister told a news conference that the experiment had rather # shown 8 per cent higher consumption of electricity. +# From Alex Krivenyshev (2008-05-15): +# +# Here is an article that Pakistan plan to introduce Daylight Saving Time +# on June 1, 2008 for 3 months. +# +# "... The federal cabinet on Wednesday announced a new conservation plan to help +# reduce load shedding by approving the closure of commercial centres at 9pm and +# moving clocks forward by one hour for the next three months. +# ...." +# +# +# http://www.worldtimezone.net/dst_news/dst_news_pakistan01.html +# +# OR +# +# http://www.dailytimes.com.pk/default.asp?page=2008%5C05%5C15%5Cstory_15-5-2008_pg1_4 +# + +# From Arthur David Olson (2008-05-19): +# XXX--midnight transitions is a guess; 2008 only is a guess. # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Pakistan 2002 only - Apr Sun>=2 0:01 1:00 S Rule Pakistan 2002 only - Oct Sun>=2 0:01 0 - +Rule Pakistan 2008 only - Jun 1 0:00 1:00 S +Rule Pakistan 2008 only - Sep 1 0:00 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Asia/Karachi 4:28:12 - LMT 1907 5:30 - IST 1942 Sep @@ -1700,7 +1805,7 @@ Zone Asia/Singapore 6:55:25 - LMT 1901 Jan 1 # kept their clocks set five and a half hours ahead of Greenwich Mean # Time (GMT), in line with neighbor India. # From Paul Eggert (2006-04-18): -# People who live in regions under Tamil control can use TZ='Asia/Calcutta', +# People who live in regions under Tamil control can use [TZ='Asia/Kolkata'], # as that zone has agreed with the Tamil areas since our cutoff date of 1970. # From K Sethu (2006-04-25): @@ -1790,10 +1895,62 @@ Rule Syria 2006 only - Sep 22 0:00 0 - # From Paul Eggert (2007-03-29): # Today the AP reported "Syria will switch to summertime at midnight Thursday." # http://www.iht.com/articles/ap/2007/03/29/africa/ME-GEN-Syria-Time-Change.php -# For lack of better info, assume the rule changed to "last Friday in March" -# this year. -Rule Syria 2007 max - Mar lastFri 0:00 1:00 S -Rule Syria 2007 max - Oct 1 0:00 0 - +Rule Syria 2007 only - Mar lastFri 0:00 1:00 S +# From Jesper Norgard (2007-10-27): +# The sister center ICARDA of my work CIMMYT is confirming that Syria DST will +# not take place 1.st November at 0:00 o'clock but 1.st November at 24:00 or +# rather Midnight between Thursday and Friday. This does make more sence than +# having it between Wednesday and Thursday (two workdays in Syria) since the +# weekend in Syria is not Saturday and Sunday, but Friday and Saturday. So now +# it is implemented at midnight of the last workday before weekend... +# +# From Steffen Thorsen (2007-10-27): +# Jesper Norgaard Welen wrote: +# +# > "Winter local time in Syria will be observed at midnight of Thursday 1 +# > November 2007, and the clock will be put back 1 hour." +# +# I found confirmation on this in this gov.sy-article (Arabic): +# http://wehda.alwehda.gov.sy/_print_veiw.asp?FileName=12521710520070926111247 +# +# which using Google's translate tools says: +# Council of Ministers also approved the commencement of work on +# identifying the winter time as of Friday, 2/11/2007 where the 60th +# minute delay at midnight Thursday 1/11/2007. +Rule Syria 2007 only - Nov Fri>=1 0:00 0 - + +# From Stephen Colebourne (2008-03-17): +# For everyone's info, I saw an IATA time zone change for [Syria] for +# this month (March 2008) in the last day or so...This is the data IATA +# are now using: +# Country Time Standard --- DST Start --- --- DST End --- DST +# Name Zone Variation Time Date Time Date +# Variation +# Syrian Arab +# Republic SY +0200 2200 03APR08 2100 30SEP08 +0300 +# 2200 02APR09 2100 30SEP09 +0300 +# 2200 01APR10 2100 30SEP10 +0300 + +# From Arthur David Olson (2008-03-17): +# Here's a link to English-language coverage by the Syrian Arab News +# Agency (SANA)... +# +# http://www.sana.sy/eng/21/2008/03/11/165173.htm +# ...which reads (in part) "The Cabinet approved the suggestion of the +# Ministry of Electricity to begin daylight savings time on Friday April +# 4th, advancing clocks one hour ahead on midnight of Thursday April 3rd." +# Since Syria is two hours east of UTC, the 2200 and 2100 transition times +# shown above match up with midnight in Syria. + +# From Arthur David Olson (2008-03-18): +# My buest guess at a Syrian rule is "the Friday nearest April 1"; +# coding that involves either using a "Mar Fri>=29" construct that old time zone +# compilers can't handle or having multiple Rules (a la Israel). +# For now, use "Apr Fri>=1", and go with IATA on a uniform Sep 30 end. + +Rule Syria 2008 max - Apr Fri>=1 0:00 1:00 S +Rule Syria 2008 max - Oct 1 0:00 0 - + # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Asia/Damascus 2:25:12 - LMT 1920 # Dimashq 2:00 Syria EE%sT @@ -1847,13 +2004,13 @@ Zone Asia/Tashkent 4:37:12 - LMT 1924 May 2 # Vietnam -# From Paul Eggert (1993-11-18): -# Saigon's official name is Thanh-Pho Ho Chi Minh, but it's too long. -# We'll stick with the traditional name for now. +# From Arthur David Olson (2008-03-18): +# The English-language name of Vietnam's most populous city is "Ho Chi Min City"; +# we use Ho_Chi_Minh below to avoid a name of more than 14 characters. # From Shanks & Pottenger: # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Saigon 7:06:40 - LMT 1906 Jun 9 +Zone Asia/Ho_Chi_Minh 7:06:40 - LMT 1906 Jun 9 7:06:20 - SMT 1911 Mar 11 0:01 # Saigon MT? 7:00 - ICT 1912 May 8:00 - ICT 1931 May diff --git a/make/sun/javazic/tzdata/australasia b/make/sun/javazic/tzdata/australasia index eee5c0b54..823550415 100644 --- a/make/sun/javazic/tzdata/australasia +++ b/make/sun/javazic/tzdata/australasia @@ -1368,7 +1368,7 @@ Zone Pacific/Wallis 12:15:20 - LMT 1901 # * Tonga will introduce DST in November # # I was given this link by John Letts: -# +# # http://news.bbc.co.uk/hi/english/world/asia-pacific/newsid_424000/424764.stm # # @@ -1378,7 +1378,7 @@ Zone Pacific/Wallis 12:15:20 - LMT 1901 # (12 + 1 hour DST). # From Arthur David Olson (1999-09-20): -# According to # http://www.tongaonline.com/news/sept1799.html # : # "Daylight Savings Time will take effect on Oct. 2 through April 15, 2000 diff --git a/make/sun/javazic/tzdata/backward b/make/sun/javazic/tzdata/backward index 0e64882a4..85522740f 100644 --- a/make/sun/javazic/tzdata/backward +++ b/make/sun/javazic/tzdata/backward @@ -46,12 +46,15 @@ Link America/St_Thomas America/Virgin Link Asia/Ashgabat Asia/Ashkhabad Link Asia/Chongqing Asia/Chungking Link Asia/Dhaka Asia/Dacca +Link Asia/Kolkata Asia/Calcutta Link Asia/Macau Asia/Macao Link Asia/Jerusalem Asia/Tel_Aviv +Link Asia/Ho_Chi_Minh Asia/Saigon Link Asia/Thimphu Asia/Thimbu Link Asia/Makassar Asia/Ujung_Pandang Link Asia/Ulaanbaatar Asia/Ulan_Bator Link Atlantic/Faroe Atlantic/Faeroe +Link Europe/Oslo Atlantic/Jan_Mayen Link Australia/Sydney Australia/ACT Link Australia/Sydney Australia/Canberra Link Australia/Lord_Howe Australia/LHI diff --git a/make/sun/javazic/tzdata/europe b/make/sun/javazic/tzdata/europe index 16fedf803..307243283 100644 --- a/make/sun/javazic/tzdata/europe +++ b/make/sun/javazic/tzdata/europe @@ -479,7 +479,7 @@ Rule EU 1979 1995 - Sep lastSun 1:00u 0 - Rule EU 1981 max - Mar lastSun 1:00u 1:00 S Rule EU 1996 max - Oct lastSun 1:00u 0 - # The most recent directive covers the years starting in 2002. See: -# # Directive 2000/84/EC of the European Parliament and of the Council # of 19 January 2001 on summer-time arrangements. # @@ -502,9 +502,48 @@ Rule C-Eur 1940 only - Apr 1 2:00s 1:00 S Rule C-Eur 1942 only - Nov 2 2:00s 0 - Rule C-Eur 1943 only - Mar 29 2:00s 1:00 S Rule C-Eur 1943 only - Oct 4 2:00s 0 - -Rule C-Eur 1944 only - Apr 3 2:00s 1:00 S +Rule C-Eur 1944 1945 - Apr Mon>=1 2:00s 1:00 S # Whitman gives 1944 Oct 7; go with Shanks & Pottenger. Rule C-Eur 1944 only - Oct 2 2:00s 0 - +# From Jesper Norgaard Welen (2008-07-13): +# +# I found what is probably a typo of 2:00 which should perhaps be 2:00s +# in the C-Eur rule from tz database version 2008d (this part was +# corrected in version 2008d). The circumstancial evidence is simply the +# tz database itself, as seen below: +# +# Zone Europe/Paris 0:09:21 - LMT 1891 Mar 15 0:01 +# 0:00 France WE%sT 1945 Sep 16 3:00 +# +# Zone Europe/Monaco 0:29:32 - LMT 1891 Mar 15 +# 0:00 France WE%sT 1945 Sep 16 3:00 +# +# Zone Europe/Belgrade 1:22:00 - LMT 1884 +# 1:00 1:00 CEST 1945 Sep 16 2:00s +# +# Rule France 1945 only - Sep 16 3:00 0 - +# Rule Belgium 1945 only - Sep 16 2:00s 0 - +# Rule Neth 1945 only - Sep 16 2:00s 0 - +# +# The rule line to be changed is: +# +# Rule C-Eur 1945 only - Sep 16 2:00 0 - +# +# It seems that Paris, Monaco, Rule France, Rule Belgium all agree on +# 2:00 standard time, e.g. 3:00 local time. However there are no +# countries that use C-Eur rules in September 1945, so the only items +# affected are apparently these ficticious zones that translates acronyms +# CET and MET: +# +# Zone CET 1:00 C-Eur CE%sT +# Zone MET 1:00 C-Eur ME%sT +# +# It this is right then the corrected version would look like: +# +# Rule C-Eur 1945 only - Sep 16 2:00s 0 - +# +# A small step for mankind though 8-) +Rule C-Eur 1945 only - Sep 16 2:00s 0 - Rule C-Eur 1977 1980 - Apr Sun>=1 2:00s 1:00 S Rule C-Eur 1977 only - Sep lastSun 2:00s 0 - Rule C-Eur 1978 only - Oct 1 2:00s 0 - @@ -747,7 +786,8 @@ Rule Bulg 1981 only - Sep 27 2:00 0 - Zone Europe/Sofia 1:33:16 - LMT 1880 1:56:56 - IMT 1894 Nov 30 # Istanbul MT? 2:00 - EET 1942 Nov 2 3:00 - 1:00 C-Eur CE%sT 1945 Apr 2 3:00 + 1:00 C-Eur CE%sT 1945 + 1:00 - CET 1945 Apr 2 3:00 2:00 - EET 1979 Mar 31 23:00 2:00 Bulg EE%sT 1982 Sep 26 2:00 2:00 C-Eur EE%sT 1991 @@ -1115,33 +1155,40 @@ Zone Europe/Paris 0:09:21 - LMT 1891 Mar 15 0:01 # [See tz-link.htm for the URL.] # From Joerg Schilling (2002-10-23): -# In 1945, Berlin was switched to Moscow Summer time (GMT+4) by +# In 1945, Berlin was switched to Moscow Summer time (GMT+4) by +# # General [Nikolai] Bersarin. # From Paul Eggert (2003-03-08): # +# http://www.parlament-berlin.de/pds-fraktion.nsf/727459127c8b66ee8525662300459099/defc77cb784f180ac1256c2b0030274b/$FILE/bersarint.pdf +# # says that Bersarin issued an order to use Moscow time on May 20. # However, Moscow did not observe daylight saving in 1945, so # this was equivalent to CEMT (GMT+3), not GMT+4. # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Germany 1945 only - Apr 2 2:00s 1:00 S -Rule Germany 1945 only - May 24 2:00 2:00 M # Midsummer -Rule Germany 1945 only - Sep 24 3:00 1:00 S -Rule Germany 1945 only - Nov 18 2:00s 0 - Rule Germany 1946 only - Apr 14 2:00s 1:00 S Rule Germany 1946 only - Oct 7 2:00s 0 - Rule Germany 1947 1949 - Oct Sun>=1 2:00s 0 - -Rule Germany 1947 only - Apr 6 2:00s 1:00 S +# http://www.ptb.de/de/org/4/44/441/salt.htm says the following transition +# occurred at 3:00 MEZ, not the 2:00 MEZ given in Shanks & Pottenger. +# Go with the PTB. +Rule Germany 1947 only - Apr 6 3:00s 1:00 S Rule Germany 1947 only - May 11 2:00s 2:00 M Rule Germany 1947 only - Jun 29 3:00 1:00 S Rule Germany 1948 only - Apr 18 2:00s 1:00 S Rule Germany 1949 only - Apr 10 2:00s 1:00 S + +Rule SovietZone 1945 only - May 24 2:00 2:00 M # Midsummer +Rule SovietZone 1945 only - Sep 24 3:00 1:00 S +Rule SovietZone 1945 only - Nov 18 2:00s 0 - + # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Europe/Berlin 0:53:28 - LMT 1893 Apr - 1:00 C-Eur CE%sT 1945 Apr 2 2:00 + 1:00 C-Eur CE%sT 1945 May 24 2:00 + 1:00 SovietZone CE%sT 1946 1:00 Germany CE%sT 1980 1:00 EU CE%sT @@ -1218,7 +1265,7 @@ Rule Hungary 1980 only - Apr 6 1:00 1:00 S Zone Europe/Budapest 1:16:20 - LMT 1890 Oct 1:00 C-Eur CE%sT 1918 1:00 Hungary CE%sT 1941 Apr 6 2:00 - 1:00 C-Eur CE%sT 1945 May 1 23:00 + 1:00 C-Eur CE%sT 1945 1:00 Hungary CE%sT 1980 Sep 28 2:00s 1:00 EU CE%sT @@ -1736,7 +1783,6 @@ Zone Europe/Oslo 0:43:00 - LMT 1895 Jan 1 # come up with more definitive info about the timekeeping during the # war years it's probably best just do do the following for now: Link Europe/Oslo Arctic/Longyearbyen -Link Europe/Oslo Atlantic/Jan_Mayen # Poland # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S @@ -2136,7 +2182,8 @@ Zone Asia/Anadyr 11:49:56 - LMT 1924 May 2 # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Europe/Belgrade 1:22:00 - LMT 1884 1:00 - CET 1941 Apr 18 23:00 - 1:00 C-Eur CE%sT 1945 May 8 2:00s + 1:00 C-Eur CE%sT 1945 + 1:00 - CET 1945 May 8 2:00s 1:00 1:00 CEST 1945 Sep 16 2:00s # Metod Kozelj reports that the legal date of # transition to EU rules was 1982-11-27, for all of Yugoslavia at the time. diff --git a/make/sun/javazic/tzdata/iso3166.tab b/make/sun/javazic/tzdata/iso3166.tab index d976eb1a1..6931ee04a 100644 --- a/make/sun/javazic/tzdata/iso3166.tab +++ b/make/sun/javazic/tzdata/iso3166.tab @@ -28,7 +28,7 @@ # # This file contains a table with the following columns: # 1. ISO 3166-1 alpha-2 country code, current as of -# ISO 3166-1 Newsletter No. V-12 (2006-09-26). See: +# ISO 3166-1 Newsletter VI-1 (2007-09-21). See: # # ISO 3166 Maintenance agency (ISO 3166/MA) # . @@ -69,6 +69,7 @@ BG Bulgaria BH Bahrain BI Burundi BJ Benin +BL St Barthelemy BM Bermuda BN Brunei BO Bolivia @@ -181,6 +182,7 @@ MA Morocco MC Monaco MD Moldova ME Montenegro +MF St Martin (French part) MG Madagascar MH Marshall Islands MK Macedonia diff --git a/make/sun/javazic/tzdata/leapseconds b/make/sun/javazic/tzdata/leapseconds index 77d28dae9..4526cddf5 100644 --- a/make/sun/javazic/tzdata/leapseconds +++ b/make/sun/javazic/tzdata/leapseconds @@ -66,8 +66,10 @@ Leap 1995 Dec 31 23:59:60 + S Leap 1997 Jun 30 23:59:60 + S Leap 1998 Dec 31 23:59:60 + S Leap 2005 Dec 31 23:59:60 + S +Leap 2008 Dec 31 23:59:60 + S # INTERNATIONAL EARTH ROTATION AND REFERENCE SYSTEMS SERVICE (IERS) +# # SERVICE INTERNATIONAL DE LA ROTATION TERRESTRE ET DES SYSTEMES DE REFERENCE # # SERVICE DE LA ROTATION TERRESTRE @@ -75,30 +77,38 @@ Leap 2005 Dec 31 23:59:60 + S # 61, Av. de l'Observatoire 75014 PARIS (France) # Tel. : 33 (0) 1 40 51 22 26 # FAX : 33 (0) 1 40 51 22 91 -# Internet : services.iers@obspm.fr +# e-mail : services.iers@obspm.fr +# http://hpiers.obspm.fr/eop-pc # -# Paris, 28 June 2007 +# Paris, 4 July 2008 # -# Bulletin C 34 +# Bulletin C 36 # # To authorities responsible # for the measurement and # distribution of time # -# INFORMATION ON UTC - TAI +# UTC TIME STEP +# on the 1st of January 2009 +# +# A positive leap second will be introduced at the end of December 2008. +# The sequence of dates of the UTC second markers will be: +# +# 2008 December 31, 23h 59m 59s +# 2008 December 31, 23h 59m 60s +# 2009 January 1, 0h 0m 0s # -# NO positive leap second will be introduced at the end of December 2007. -# The difference between Coordinated Universal Time UTC and the -# International Atomic Time TAI is : +# The difference between UTC and the International Atomic Time TAI is: # -# from 2006 January 1, 0h UTC, until further notice : UTC-TAI = -33 s +# from 2006 January 1, 0h UTC, to 2009 January 1 0h UTC : UTC-TAI = - 33s +# from 2009 January 1, 0h UTC, until further notice : UTC-TAI = - 34s # # Leap seconds can be introduced in UTC at the end of the months of December -# or June, depending on the evolution of UT1-TAI. Bulletin C is mailed every -# six months, either to announce a time step in UTC, or to confirm that there +# or June, depending on the evolution of UT1-TAI. Bulletin C is mailed every +# six months, either to announce a time step in UTC or to confirm that there # will be no time step at the next possible date. # # Daniel GAMBIS -# Director +# Head # Earth Orientation Center of IERS # Observatoire de Paris, France diff --git a/make/sun/javazic/tzdata/northamerica b/make/sun/javazic/tzdata/northamerica index ab5bb5b21..6e0317277 100644 --- a/make/sun/javazic/tzdata/northamerica +++ b/make/sun/javazic/tzdata/northamerica @@ -2098,8 +2098,8 @@ Zone America/Antigua -4:07:12 - LMT 1912 Mar 2 # http://www.jonesbahamas.com/?c=45&a=10412 # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Bahamas 1964 2006 - Oct lastSun 2:00 0 S -Rule Bahamas 1964 1986 - Apr lastSun 2:00 1:00 D +Rule Bahamas 1964 1975 - Oct lastSun 2:00 0 S +Rule Bahamas 1964 1975 - Apr lastSun 2:00 1:00 D # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone America/Nassau -5:09:24 - LMT 1912 Mar 2 -5:00 Bahamas E%sT 1976 @@ -2209,6 +2209,69 @@ Zone America/Costa_Rica -5:36:20 - LMT 1890 # San Jose # says Cuban clocks will advance at midnight on March 10. # For lack of better information, assume Cuba will use US rules, # except that it switches at midnight standard time as usual. +# +# From Steffen Thorsen (2007-10-25): +# Carlos Alberto Fonseca Arauz informed me that Cuba will end DST one week +# earlier - on the last Sunday of October, just like in 2006. +# +# He supplied these references: +# +# http://www.prensalatina.com.mx/article.asp?ID=%7B4CC32C1B-A9F7-42FB-8A07-8631AFC923AF%7D&language=ES +# http://actualidad.terra.es/sociedad/articulo/cuba_llama_ahorrar_energia_cambio_1957044.htm +# +# From Alex Kryvenishev (2007-10-25): +# Here is also article from Granma (Cuba): +# +# [Regira] el Horario Normal desde el [proximo] domingo 28 de octubre +# http://www.granma.cubaweb.cu/2007/10/24/nacional/artic07.html +# +# http://www.worldtimezone.com/dst_news/dst_news_cuba03.html + +# From Arthur David Olson (2008-03-09): +# I'm in Maryland which is now observing United States Eastern Daylight +# Time. At 9:44 local time I used RealPlayer to listen to +# +# http://media.enet.cu/radioreloj +# , a Cuban information station, and heard +# the time announced as "ocho cuarenta y cuatro" ("eight forty-four"), +# indicating that Cuba is still on standard time. + +# From Steffen Thorsen (2008-03-12): +# It seems that Cuba will start DST on Sunday, 2007-03-16... +# It was announced yesterday, according to this source (in Spanish): +# +# http://www.nnc.cubaweb.cu/marzo-2008/cien-1-11-3-08.htm +# +# +# Some more background information is posted here: +# +# http://www.timeanddate.com/news/time/cuba-starts-dst-march-16.html +# +# +# The article also says that Cuba has been observing DST since 1963, +# while Shanks (and tzdata) has 1965 as the first date (except in the +# 1940's). Many other web pages in Cuba also claim that it has been +# observed since 1963, but with the exception of 1970 - an exception +# which is not present in tzdata/Shanks. So there is a chance we need to +# change some historic records as well. +# +# One example: +# +# http://www.radiohc.cu/espanol/noticias/mar07/11mar/hor.htm +# + +# From Jesper Norgaard Welen (2008-03-13): +# The Cuban time change has just been confirmed on the most authoritative +# web site, the Granma. Please check out +# +# http://www.granma.cubaweb.cu/2008/03/13/nacional/artic10.html +# +# +# Basically as expected after Steffen Thorsens information, the change +# will take place midnight between Saturday and Sunday. + +# From Arthur David Olson (2008-03-12): +# Assume Sun>=15 (third Sunday) going forward. # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Cuba 1928 only - Jun 10 0:00 1:00 D @@ -2240,9 +2303,9 @@ Rule Cuba 1997 only - Oct 12 0:00s 0 S Rule Cuba 1998 1999 - Mar lastSun 0:00s 1:00 D Rule Cuba 1998 2003 - Oct lastSun 0:00s 0 S Rule Cuba 2000 2006 - Apr Sun>=1 0:00s 1:00 D -Rule Cuba 2006 only - Oct lastSun 0:00s 0 S -Rule Cuba 2007 max - Mar Sun>=8 0:00s 1:00 D -Rule Cuba 2007 max - Nov Sun>=1 0:00s 0 S +Rule Cuba 2006 max - Oct lastSun 0:00s 0 S +Rule Cuba 2007 only - Mar Sun>=8 0:00s 1:00 D +Rule Cuba 2008 max - Mar Sun>=15 0:00s 1:00 D # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone America/Havana -5:29:28 - LMT 1890 @@ -2309,6 +2372,10 @@ Zone America/Grenada -4:07:00 - LMT 1911 Jul # St George's # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone America/Guadeloupe -4:06:08 - LMT 1911 Jun 8 # Pointe a Pitre -4:00 - AST +# St Barthelemy +Link America/Guadeloupe America/St_Barthelemy +# St Martin (French part) +Link America/Guadeloupe America/Marigot # Guatemala # diff --git a/make/sun/javazic/tzdata/southamerica b/make/sun/javazic/tzdata/southamerica index a147a1b08..06a8d130e 100644 --- a/make/sun/javazic/tzdata/southamerica +++ b/make/sun/javazic/tzdata/southamerica @@ -127,7 +127,11 @@ Rule Arg 1989 1992 - Oct Sun>=15 0:00 1:00 S # which did not result in the switch of a time zone, as they stayed 9 hours # from the International Date Line. Rule Arg 1999 only - Oct Sun>=1 0:00 1:00 S -Rule Arg 2000 only - Mar Sun>=1 0:00 0 - +# From Paul Eggert (2007-12-28): +# DST was set to expire on March 5, not March 3, but since it was converted +# to standard time on March 3 it's more convenient for us to pretend that +# it ended on March 3. +Rule Arg 2000 only - Mar 3 0:00 0 - # # From Peter Gradelski via Steffen Thorsen (2000-03-01): # We just checked with our Sao Paulo office and they say the government of @@ -162,6 +166,30 @@ Rule Arg 2000 only - Mar Sun>=1 0:00 0 - # This kind of things had always been done this way in Argentina. # We are still -03:00 all year round in all of the country. # +# From Steffen Thorsen (2007-12-21): +# A user (Leonardo Chaim) reported that Argentina will adopt DST.... +# all of the country (all Zone-entries) are affected. News reports like +# http://www.lanacion.com.ar/opinion/nota.asp?nota_id=973037 indicate +# that Argentina will use DST next year as well, from October to +# March, although exact rules are not given. +# +# From Jesper Norgaard Welen (2007-12-26) +# The last hurdle of Argentina DST is over, the proposal was approved in +# the lower chamber too (Deputados) with a vote 192 for and 2 against. +# By the way thanks to Mariano Absatz and Daniel Mario Vega for the link to +# the original scanned proposal, where the dates and the zero hours are +# clear and unambiguous...This is the article about final approval: +# +# http://www.lanacion.com.ar/politica/nota.asp?nota_id=973996 +# +# +# From Paul Eggert (2007-12-22): +# For dates after mid-2008, the following rules are my guesses and +# are quite possibly wrong, but are more likely than no DST at all. +Rule Arg 2007 only - Dec 30 0:00 1:00 S +Rule Arg 2008 max - Mar Sun>=15 0:00 0 - +Rule Arg 2008 max - Oct Sun>=1 0:00 1:00 S + # From Mariano Absatz (2004-05-21): # Today it was officially published that the Province of Mendoza is changing # its timezone this winter... starting tomorrow night.... @@ -222,10 +250,80 @@ Rule Arg 2000 only - Mar Sun>=1 0:00 0 - # http://www.sanjuan.gov.ar/prensa/archivo/000426.html # http://www.sanjuan.gov.ar/prensa/archivo/000441.html +# From Alex Krivenyshev (2008-01-17): +# Here are articles that Argentina Province San Luis is planning to end DST +# as earlier as upcoming Monday January 21, 2008 or February 2008: +# +# Provincia argentina retrasa reloj y marca diferencia con resto del pais +# (Argentine Province delayed clock and mark difference with the rest of the +# country) +# +# http://cl.invertia.com/noticias/noticia.aspx?idNoticia=200801171849_EFE_ET4373&idtel +# +# +# Es inminente que en San Luis atrasen una hora los relojes +# (It is imminent in San Luis clocks one hour delay) +# +# http://www.lagaceta.com.ar/vernotae.asp?id_nota=253414 +# +# +# +# http://www.worldtimezone.net/dst_news/dst_news_argentina02.html +# + +# From Jesper Norgaard Welen (2008-01-18): +# The page of the San Luis provincial government +# +# http://www.sanluis.gov.ar/notas.asp?idCanal=0&id=22812 +# +# confirms what Alex Krivenyshev has earlier sent to the tz +# emailing list about that San Luis plans to return to standard +# time much earlier than the rest of the country. It also +# confirms that upon request the provinces San Juan and Mendoza +# refused to follow San Luis in this change. +# +# The change is supposed to take place Monday the 21.st at 0:00 +# hours. As far as I understand it if this goes ahead, we need +# a new timezone for San Luis (although there are also documented +# independent changes in the southamerica file of San Luis in +# 1990 and 1991 which has not been confirmed). + +# From Jesper Norgaard Welen (2008-01-25): +# Unfortunately the below page has become defunct, about the San Luis +# time change. Perhaps because it now is part of a group of pages "Most +# important pages of 2008." +# +# You can use +# +# http://www.sanluis.gov.ar/notas.asp?idCanal=8141&id=22834 +# +# instead it seems. Or use "Buscador" from the main page of the San Luis +# government, and fill in "huso" and click OK, and you will get 3 pages +# from which the first one is identical to the above. + +# From Mariano Absatz (2008-01-28): +# I can confirm that the Province of San Luis (and so far only that +# province) decided to go back to UTC-3 effective midnight Jan 20th 2008 +# (that is, Monday 21st at 0:00 is the time the clocks were delayed back +# 1 hour), and they intend to keep UTC-3 as their timezone all year round +# (that is, unless they change their mind any minute now). +# +# So we'll have to add yet another city to 'southamerica' (I think San +# Luis city is the mos populated city in the Province, so it'd be +# America/Argentina/San_Luis... of course I can't remember if San Luis's +# history of particular changes goes along with Mendoza or San Juan :-( +# (I only remember not being able to collect hard facts about San Luis +# back in 2004, when these provinces changed to UTC-4 for a few days, I +# mailed them personally and never got an answer). + +# From Paul Eggert (2008-06-30): # Unless otherwise specified, data are from Shanks & Pottenger through 1992, # from the IATA otherwise. As noted below, Shanks & Pottenger say that -# America/Cordoba split into 6 subregions during 1991/1992, but we -# haven't verified this yet so for now we'll keep it a single region. +# America/Cordoba split into 6 subregions during 1991/1992, one of which +# was America/San_Luis, but we haven't verified this yet so for now we'll +# keep America/Cordoba a single region rather than splitting it into the +# other 5 subregions. + # # Zone NAME GMTOFF RULES FORMAT [UNTIL] # @@ -236,18 +334,16 @@ Zone America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 Oct 31 -4:00 Arg AR%sT 1969 Oct 5 -3:00 Arg AR%sT 1999 Oct 3 -4:00 Arg AR%sT 2000 Mar 3 - -3:00 - ART + -3:00 Arg AR%sT # # Santa Fe (SF), Entre Rios (ER), Corrientes (CN), Misiones (MN), Chaco (CC), # Formosa (FM), Salta (SA), Santiago del Estero (SE), Cordoba (CB), -# San Luis (SL), La Pampa (LP), Neuquen (NQ), Rio Negro (RN) +# La Pampa (LP), Neuquen (NQ), Rio Negro (RN) # # Shanks & Pottenger also make the following claims, which we haven't verified: # - Formosa switched to -3:00 on 1991-01-07. # - Misiones switched to -3:00 on 1990-12-29. # - Chaco switched to -3:00 on 1991-01-04. -# - San Luis switched to -4:00 on 1990-03-14, then to -3:00 on 1990-10-15, -# then to -4:00 on 1991-03-01, then to -3:00 on 1991-06-01. # - Santiago del Estero switched to -4:00 on 1991-04-01, # then to -3:00 on 1991-04-26. # @@ -259,7 +355,7 @@ Zone America/Argentina/Cordoba -4:16:48 - LMT 1894 Oct 31 -4:00 - WART 1991 Oct 20 -3:00 Arg AR%sT 1999 Oct 3 -4:00 Arg AR%sT 2000 Mar 3 - -3:00 - ART + -3:00 Arg AR%sT # # Tucuman (TM) Zone America/Argentina/Tucuman -4:20:52 - LMT 1894 Oct 31 @@ -272,7 +368,7 @@ Zone America/Argentina/Tucuman -4:20:52 - LMT 1894 Oct 31 -4:00 Arg AR%sT 2000 Mar 3 -3:00 - ART 2004 Jun 1 -4:00 - WART 2004 Jun 13 - -3:00 - ART + -3:00 Arg AR%sT # # La Rioja (LR) Zone America/Argentina/La_Rioja -4:27:24 - LMT 1894 Oct 31 @@ -285,7 +381,7 @@ Zone America/Argentina/La_Rioja -4:27:24 - LMT 1894 Oct 31 -4:00 Arg AR%sT 2000 Mar 3 -3:00 - ART 2004 Jun 1 -4:00 - WART 2004 Jun 20 - -3:00 - ART + -3:00 Arg AR%sT # # San Juan (SJ) Zone America/Argentina/San_Juan -4:34:04 - LMT 1894 Oct 31 @@ -298,7 +394,7 @@ Zone America/Argentina/San_Juan -4:34:04 - LMT 1894 Oct 31 -4:00 Arg AR%sT 2000 Mar 3 -3:00 - ART 2004 May 31 -4:00 - WART 2004 Jul 25 - -3:00 - ART + -3:00 Arg AR%sT # # Jujuy (JY) Zone America/Argentina/Jujuy -4:21:12 - LMT 1894 Oct 31 @@ -312,7 +408,7 @@ Zone America/Argentina/Jujuy -4:21:12 - LMT 1894 Oct 31 -3:00 1:00 ARST 1992 -3:00 Arg AR%sT 1999 Oct 3 -4:00 Arg AR%sT 2000 Mar 3 - -3:00 - ART + -3:00 Arg AR%sT # # Catamarca (CT), Chubut (CH) Zone America/Argentina/Catamarca -4:23:08 - LMT 1894 Oct 31 @@ -325,7 +421,7 @@ Zone America/Argentina/Catamarca -4:23:08 - LMT 1894 Oct 31 -4:00 Arg AR%sT 2000 Mar 3 -3:00 - ART 2004 Jun 1 -4:00 - WART 2004 Jun 20 - -3:00 - ART + -3:00 Arg AR%sT # # Mendoza (MZ) Zone America/Argentina/Mendoza -4:35:16 - LMT 1894 Oct 31 @@ -342,6 +438,23 @@ Zone America/Argentina/Mendoza -4:35:16 - LMT 1894 Oct 31 -4:00 Arg AR%sT 2000 Mar 3 -3:00 - ART 2004 May 23 -4:00 - WART 2004 Sep 26 + -3:00 Arg AR%sT +# +# San Luis (SL) +Zone America/Argentina/San_Luis -4:25:24 - LMT 1894 Oct 31 + -4:16:48 - CMT 1920 May + -4:00 - ART 1930 Dec + -4:00 Arg AR%sT 1969 Oct 5 + -3:00 Arg AR%sT 1990 + -3:00 1:00 ARST 1990 Mar 14 + -4:00 - WART 1990 Oct 15 + -4:00 1:00 WARST 1991 Mar 1 + -4:00 - WART 1991 Jun 1 + -3:00 - ART 1999 Oct 3 + -4:00 1:00 WARST 2000 Mar 3 + -3:00 - ART 2004 May 31 + -4:00 - WART 2004 Jul 25 + -3:00 Arg AR%sT 2008 Jan 21 -3:00 - ART # # Santa Cruz (SC) @@ -353,7 +466,7 @@ Zone America/Argentina/Rio_Gallegos -4:36:52 - LMT 1894 Oct 31 -4:00 Arg AR%sT 2000 Mar 3 -3:00 - ART 2004 Jun 1 -4:00 - WART 2004 Jun 20 - -3:00 - ART + -3:00 Arg AR%sT # # Tierra del Fuego, Antartida e Islas del Atlantico Sur (TF) Zone America/Argentina/Ushuaia -4:33:12 - LMT 1894 Oct 31 @@ -364,7 +477,7 @@ Zone America/Argentina/Ushuaia -4:33:12 - LMT 1894 Oct 31 -4:00 Arg AR%sT 2000 Mar 3 -3:00 - ART 2004 May 30 -4:00 - WART 2004 Jun 20 - -3:00 - ART + -3:00 Arg AR%sT # Aruba # Zone NAME GMTOFF RULES FORMAT [UNTIL] @@ -450,6 +563,50 @@ Zone America/La_Paz -4:32:36 - LMT 1890 # Brazil will start DST on 2007-10-14 00:00 and end on 2008-02-17 00:00: # http://www.mme.gov.br/site/news/detail.do;jsessionid=BBA06811AFCAAC28F0285210913513DA?newsId=13975 +# From Paul Schulze (2008-06-24): +# ...by law number 11.662 of April 24, 2008 (published in the "Diario +# Oficial da Uniao"...) in Brazil there are changes in the timezones, +# effective today (00:00am at June 24, 2008) as follows: +# +# a) The timezone UTC+5 is e[x]tinguished, with all the Acre state and the +# part of the Amazonas state that had this timezone now being put to the +# timezone UTC+4 +# b) The whole Para state now is put at timezone UTC+3, instead of just +# part of it, as was before. +# +# This change follows a proposal of senator Tiao Viana of Acre state, that +# proposed it due to concerns about open television channels displaying +# programs inappropriate to youths in the states that had the timezone +# UTC+5 too early in the night. In the occasion, some more corrections +# were proposed, trying to unify the timezones of any given state. This +# change modifies timezone rules defined in decree 2.784 of 18 June, +# 1913. + +# From Rodrigo Severo (2008-06-24): +# Just correcting the URL: +# +# https://www.in.gov.br/imprensa/visualiza/index.jsp?jornal=3Ddo&secao=3D1&pagina=3D1&data=3D25/04/2008 +# +# +# As a result of the above Decree I believe the America/Rio_Branco +# timezone shall be modified from UTC-5 to UTC-4 and a new timezone shall +# be created to represent the the west side of the Para State. I +# suggest this new timezone be called Santarem as the most +# important/populated city in the affected area. +# +# This new timezone would be the same as the Rio_Branco timezone up to +# the 2008/06/24 change which would be to UTC-3 instead of UTC-4. + +# From Alex Krivenyshev (2008-06-24): +# This is a quick reference page for New and Old Brazil Time Zones map. +# +# http://www.worldtimezone.com/brazil-time-new-old.php +# +# +# - 4 time zones replaced by 3 time zones-eliminating time zone UTC- 05 +# (state Acre and the part of the Amazonas will be UTC/GMT- 04) - western +# part of Par state is moving to one timezone UTC- 03 (from UTC -04). + # From Paul Eggert (2002-10-10): # The official decrees referenced below are mostly taken from # @@ -572,13 +729,13 @@ Rule Brazil 2000 only - Feb 27 0:00 0 - Rule Brazil 2000 2001 - Oct Sun>=8 0:00 1:00 S Rule Brazil 2001 2006 - Feb Sun>=15 0:00 0 - # Decree 4,399 (2002-10-01) repeals DST in AL, CE, MA, PB, PE, PI, RN, SE. -# +# 4,399 Rule Brazil 2002 only - Nov 3 0:00 1:00 S # Decree 4,844 (2003-09-24; corrected 2003-09-26) repeals DST in BA, MT, TO. -# +# 4,844 Rule Brazil 2003 only - Oct 19 0:00 1:00 S # Decree 5,223 (2004-10-01) reestablishes DST in MT. -# +# 5,223 Rule Brazil 2004 only - Nov 2 0:00 1:00 S # Decree 5,539 (2005-09-19), # adopted by the same states as before. @@ -587,9 +744,8 @@ Rule Brazil 2005 only - Oct 16 0:00 1:00 S # adopted by the same states as before. Rule Brazil 2006 only - Nov 5 0:00 1:00 S Rule Brazil 2007 only - Feb 25 0:00 0 - -# (Decree number not yet known) -# http://www.brasil.gov.br/noticias/ultimas_noticias/horario_verao070920/ -# (2007-09-20) after a heads-up from Steffen Thorsen: +# Decree 6,212 (2007-09-26), +# adopted by the same states as before. Rule Brazil 2007 max - Oct Sun>=8 0:00 1:00 S Rule Brazil 2008 max - Feb Sun>=15 0:00 0 - # The latest ruleset listed above says that the following states observe DST: @@ -597,7 +753,6 @@ Rule Brazil 2008 max - Feb Sun>=15 0:00 0 - # For dates after mid-2008, the above rules with TO="max" are guesses # and are quite possibly wrong, but are more likely than no DST at all. - # Zone NAME GMTOFF RULES FORMAT [UNTIL] # # Fernando de Noronha (administratively part of PE) @@ -623,6 +778,13 @@ Zone America/Belem -3:13:56 - LMT 1914 -3:00 Brazil BR%sT 1988 Sep 12 -3:00 - BRT # +# west Para (PA) +# West Para includes Altamira, Oribidos, Prainha, Oriximina, and Santarem. +Zone America/Santarem -3:38:48 - LMT 1914 + -4:00 Brazil AM%sT 1988 Sep 12 + -4:00 - AMT 2008 Jun 24 00:00 + -3:00 - BRT +# # Maranhao (MA), Piaui (PI), Ceara (CE), Rio Grande do Norte (RN), # Paraiba (PB) Zone America/Fortaleza -2:34:00 - LMT 1914 @@ -685,8 +847,7 @@ Zone America/Cuiaba -3:44:20 - LMT 1914 -4:00 - AMT 2004 Oct 1 -4:00 Brazil AM%sT # -# west Para (PA), Rondonia (RO) -# West Para includes Altamira, Oribidos, Prainha, Oriximina, and Santarem. +# Rondonia (RO) Zone America/Porto_Velho -4:15:36 - LMT 1914 -4:00 Brazil AM%sT 1988 Sep 12 -4:00 - AMT @@ -713,13 +874,14 @@ Zone America/Eirunepe -4:39:28 - LMT 1914 -5:00 Brazil AC%sT 1988 Sep 12 -5:00 - ACT 1993 Sep 28 -5:00 Brazil AC%sT 1994 Sep 22 - -5:00 - ACT + -5:00 - ACT 2008 Jun 24 00:00 + -4:00 - AMT # # Acre (AC) Zone America/Rio_Branco -4:31:12 - LMT 1914 -5:00 Brazil AC%sT 1988 Sep 12 - -5:00 - ACT - + -5:00 - ACT 2008 Jun 24 00:00 + -4:00 - AMT # Chile @@ -753,6 +915,26 @@ Zone America/Rio_Branco -4:31:12 - LMT 1914 # America/Santiago. The pre-1980 Pacific/Easter data are dubious, # but we have no other source. +# From German Poo-Caaman~o (2008-03-03): +# Due to drought, Chile extends Daylight Time in three weeks. This +# is one-time change (Saturday 3/29 at 24:00 for America/Santiago +# and Saturday 3/29 at 22:00 for Pacific/Easter) +# The Supreme Decree is located at +# +# http://www.shoa.cl/servicios/supremo316.pdf +# +# and the instructions for 2008 are located in: +# +# http://www.horaoficial.cl/cambio.htm +# . + +# From Jose Miguel Garrido (2008-03-05): +# ... +# You could see the announces of the change on +# +# http://www.shoa.cl/noticias/2008/04hora/hora.htm +# . + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Chile 1927 1932 - Sep 1 0:00 1:00 S Rule Chile 1928 1932 - Apr 1 0:00 0 - @@ -783,7 +965,11 @@ Rule Chile 1998 only - Mar Sun>=9 3:00u 0 - Rule Chile 1998 only - Sep 27 4:00u 1:00 S Rule Chile 1999 only - Apr 4 3:00u 0 - Rule Chile 1999 max - Oct Sun>=9 4:00u 1:00 S -Rule Chile 2000 max - Mar Sun>=9 3:00u 0 - +Rule Chile 2000 2007 - Mar Sun>=9 3:00u 0 - +# N.B.: the end of March 29 in Chile is March 30 in Universal time, +# which is used below in specifying the transition. +Rule Chile 2008 only - Mar 30 3:00u 0 - +Rule Chile 2009 max - Mar Sun>=9 3:00u 0 - # IATA SSIM anomalies: (1992-02) says 1992-03-14; # (1996-09) says 1998-03-08. Ignore these. # Zone NAME GMTOFF RULES FORMAT [UNTIL] @@ -1129,19 +1315,17 @@ Zone America/Montevideo -3:44:44 - LMT 1898 Jun 28 # Venezuela # -# From Kiraz Janicke (2007-09-25), in -# http://www.venezuelanalysis.com/analysis/2645: -# The proposal ... involves turning the clock back half an hour from -# +4.00 Greenwich Mean Time (GMT), to +4.30GMT, the time zone -# Venezuela had until December 31, 1964, when the current time zone -# was adopted. The change was due to take place on September 17 and -# then on September 24, but has since been postponed until December -# 31, to allow for compliance with international organizations, such -# as the International Office of Weights and Measures. +# From John Stainforth (2007-11-28): +# ... the change for Venezuela originally expected for 2007-12-31 has +# been brought forward to 2007-12-09. The official announcement was +# published today in the "Gaceta Oficial de la Republica Bolivariana +# de Venezuela, numero 38.819" (official document for all laws or +# resolution publication) +# http://www.globovision.com/news.php?nid=72208 # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone America/Caracas -4:27:44 - LMT 1890 -4:27:40 - CMT 1912 Feb 12 # Caracas Mean Time? -4:30 - VET 1965 # Venezuela Time - -4:00 - VET 2008 + -4:00 - VET 2007 Dec 9 03:00 -4:30 - VET diff --git a/make/sun/javazic/tzdata/zone.tab b/make/sun/javazic/tzdata/zone.tab index b252abd4f..a9c686227 100644 --- a/make/sun/javazic/tzdata/zone.tab +++ b/make/sun/javazic/tzdata/zone.tab @@ -64,7 +64,8 @@ AQ -7824+10654 Antarctica/Vostok Vostok Station, S Magnetic Pole AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville Station, Terre Adelie AQ -690022+0393524 Antarctica/Syowa Syowa Station, E Ongul I AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF) -AR -3124-06411 America/Argentina/Cordoba most locations (CB, CC, CN, ER, FM, LP, MN, NQ, RN, SA, SE, SF, SL) +AR -3124-06411 America/Argentina/Cordoba most locations (CB, CC, CN, ER, FM, LP, MN, NQ, RN, SA, SE, SF) +AR -3319-06621 America/Argentina/San_Luis San Luis (SL) AR -2411-06518 America/Argentina/Jujuy Jujuy (JY) AR -2649-06513 America/Argentina/Tucuman Tucuman (TM) AR -2828-06547 America/Argentina/Catamarca Catamarca (CT), Chubut (CH) @@ -99,6 +100,7 @@ BG +4241+02319 Europe/Sofia BH +2623+05035 Asia/Bahrain BI -0323+02922 Africa/Bujumbura BJ +0629+00237 Africa/Porto-Novo +BL +1753-06251 America/St_Barthelemy BM +3217-06446 Atlantic/Bermuda BN +0456+11455 Asia/Brunei BO -1630-06809 America/La_Paz @@ -112,7 +114,8 @@ BR -1259-03831 America/Bahia Bahia BR -2332-04637 America/Sao_Paulo S & SE Brazil (GO, DF, MG, ES, RJ, SP, PR, SC, RS) BR -2027-05437 America/Campo_Grande Mato Grosso do Sul BR -1535-05605 America/Cuiaba Mato Grosso -BR -0846-06354 America/Porto_Velho W Para, Rondonia +BR -0226-05452 America/Santarem W Para +BR -0846-06354 America/Porto_Velho Rondonia BR +0249-06040 America/Boa_Vista Roraima BR -0308-06001 America/Manaus E Amazonas BR -0640-06952 America/Eirunepe W Amazonas @@ -230,7 +233,7 @@ ID -0232+14042 Asia/Jayapura Irian Jaya & the Moluccas IE +5320-00615 Europe/Dublin IL +3146+03514 Asia/Jerusalem IM +5409-00428 Europe/Isle_of_Man -IN +2232+08822 Asia/Calcutta +IN +2232+08822 Asia/Kolkata IO -0720+07225 Indian/Chagos IQ +3321+04425 Asia/Baghdad IR +3540+05126 Asia/Tehran @@ -272,6 +275,7 @@ MA +3339-00735 Africa/Casablanca MC +4342+00723 Europe/Monaco MD +4700+02850 Europe/Chisinau ME +4226+01916 Europe/Podgorica +MF +1804-06305 America/Marigot MG -1855+04731 Indian/Antananarivo MH +0709+17112 Pacific/Majuro most locations MH +0905+16720 Pacific/Kwajalein Kwajalein @@ -361,8 +365,7 @@ SE +5920+01803 Europe/Stockholm SG +0117+10351 Asia/Singapore SH -1555-00542 Atlantic/St_Helena SI +4603+01431 Europe/Ljubljana -SJ +7800+01600 Arctic/Longyearbyen Svalbard -SJ +7059-00805 Atlantic/Jan_Mayen Jan Mayen +SJ +7800+01600 Arctic/Longyearbyen SK +4809+01707 Europe/Bratislava SL +0830-01315 Africa/Freetown SM +4355+01228 Europe/San_Marino @@ -432,7 +435,7 @@ VC +1309-06114 America/St_Vincent VE +1030-06656 America/Caracas VG +1827-06437 America/Tortola VI +1821-06456 America/St_Thomas -VN +1045+10640 Asia/Saigon +VN +1045+10640 Asia/Ho_Chi_Minh VU -1740+16825 Pacific/Efate WF -1318-17610 Pacific/Wallis WS -1350-17144 Pacific/Apia diff --git a/src/share/classes/sun/util/resources/TimeZoneNames.java b/src/share/classes/sun/util/resources/TimeZoneNames.java index 770900f1b..6925ba12d 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames.java @@ -291,6 +291,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"America/Argentina/Mendoza", AGT}, {"America/Argentina/Rio_Gallegos", AGT}, {"America/Argentina/San_Juan", AGT}, + {"America/Argentina/San_Luis", AGT}, {"America/Argentina/Tucuman", AGT}, {"America/Argentina/Ushuaia", AGT}, {"America/Aruba", AST}, @@ -329,7 +330,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"America/Detroit", EST}, {"America/Dominica", AST}, {"America/Edmonton", MST}, - {"America/Eirunepe", ACT}, + {"America/Eirunepe", AMT}, {"America/El_Salvador", CST}, {"America/Ensenada", PST}, {"America/Fort_Wayne", EST}, @@ -372,6 +373,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"America/Maceio", BRT}, {"America/Managua", CST}, {"America/Manaus", AMT}, + {"America/Marigot", AST}, {"America/Martinique", AST}, {"America/Mazatlan", MST}, {"America/Mendoza", AGT}, @@ -398,7 +400,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { "Suriname Summer Time", "SRST"}}, {"America/Port-au-Prince", EST}, {"America/Port_of_Spain", AST}, - {"America/Porto_Acre", ACT}, + {"America/Porto_Acre", AMT}, {"America/Porto_Velho", AMT}, {"America/Puerto_Rico", AST}, {"America/Rainy_River", CST}, @@ -406,13 +408,15 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"America/Recife", BRT}, {"America/Regina", CST}, {"America/Resolute", EST}, - {"America/Rio_Branco", ACT}, + {"America/Rio_Branco", AMT}, {"America/Rosario", AGT}, + {"America/Santarem", BRT}, {"America/Santiago", CLT}, {"America/Santo_Domingo", AST}, {"America/Sao_Paulo", BRT}, {"America/Scoresbysund", EGT}, {"America/Shiprock", MST}, + {"America/St_Barthelemy", AST}, {"America/St_Kitts", AST}, {"America/St_Lucia", AST}, {"America/St_Thomas", AST}, @@ -485,6 +489,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { "Tajikistan Summer Time", "TJST"}}, {"Asia/Gaza", EET}, {"Asia/Harbin", CTT}, + {"Asia/Ho_Chi_Minh", ICT}, {"Asia/Hong_Kong", HKT}, {"Asia/Hovd", new String[] {"Hovd Time", "HOVT", "Hovd Summer Time", "HOVST"}}, @@ -502,6 +507,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Katmandu", new String[] {"Nepal Time", "NPT", "Nepal Summer Time", "NPST"}}, + {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"Krasnoyarsk Time", "KRAT", "Krasnoyarsk Summer Time", "KRAST"}}, {"Asia/Kuala_Lumpur", MYT}, @@ -599,7 +605,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"Australia/Yancowinna", BROKEN_HILL}, {"BET", BRT}, {"BST", BDT}, - {"Brazil/Acre", ACT}, + {"Brazil/Acre", AMT}, {"Brazil/DeNoronha", NORONHA}, {"Brazil/East", BRT}, {"Brazil/West", AMT}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_de.java b/src/share/classes/sun/util/resources/TimeZoneNames_de.java index f67f3d8a6..eed4bbfb3 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_de.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_de.java @@ -291,6 +291,7 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle { {"America/Argentina/Mendoza", AGT}, {"America/Argentina/Rio_Gallegos", AGT}, {"America/Argentina/San_Juan", AGT}, + {"America/Argentina/San_Luis", AGT}, {"America/Argentina/Tucuman", AGT}, {"America/Argentina/Ushuaia", AGT}, {"America/Aruba", AST}, @@ -329,7 +330,7 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle { {"America/Detroit", EST}, {"America/Dominica", AST}, {"America/Edmonton", MST}, - {"America/Eirunepe", ACT}, + {"America/Eirunepe", AMT}, {"America/El_Salvador", CST}, {"America/Ensenada", PST}, {"America/Fort_Wayne", EST}, @@ -372,6 +373,7 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle { {"America/Maceio", BRT}, {"America/Managua", CST}, {"America/Manaus", AMT}, + {"America/Marigot", AST}, {"America/Martinique", AST}, {"America/Mazatlan", MST}, {"America/Mendoza", AGT}, @@ -398,7 +400,7 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle { "Suriname Sommerzeit", "SRST"}}, {"America/Port-au-Prince", EST}, {"America/Port_of_Spain", AST}, - {"America/Porto_Acre", ACT}, + {"America/Porto_Acre", AMT}, {"America/Porto_Velho", AMT}, {"America/Puerto_Rico", AST}, {"America/Rainy_River", CST}, @@ -406,13 +408,15 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle { {"America/Recife", BRT}, {"America/Regina", CST}, {"America/Resolute", EST}, - {"America/Rio_Branco", ACT}, + {"America/Rio_Branco", AMT}, {"America/Rosario", AGT}, + {"America/Santarem", BRT}, {"America/Santiago", CLT}, {"America/Santo_Domingo", AST}, {"America/Sao_Paulo", BRT}, {"America/Scoresbysund", EGT}, {"America/Shiprock", MST}, + {"America/St_Barthelemy", AST}, {"America/St_Kitts", AST}, {"America/St_Lucia", AST}, {"America/St_Thomas", AST}, @@ -485,6 +489,7 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle { "Tadschikische Sommerzeit", "TJST"}}, {"Asia/Gaza", EET}, {"Asia/Harbin", CTT}, + {"Asia/Ho_Chi_Minh", ICT}, {"Asia/Hong_Kong", HKT}, {"Asia/Hovd", new String[] {"Hovd Zeit", "HOVT", "Hovd Sommerzeit", "HOVST"}}, @@ -502,6 +507,7 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Katmandu", new String[] {"Nepalesische Zeit", "NPT", "Nepalesische Sommerzeit", "NPST"}}, + {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"Krasnojarsker Zeit", "KRAT", "Krasnojarsker Sommerzeit", "KRAST"}}, {"Asia/Kuala_Lumpur", MYT}, @@ -599,7 +605,7 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle { {"Australia/Yancowinna", BROKEN_HILL}, {"BET", BRT}, {"BST", BDT}, - {"Brazil/Acre", ACT}, + {"Brazil/Acre", AMT}, {"Brazil/DeNoronha", NORONHA}, {"Brazil/East", BRT}, {"Brazil/West", AMT}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_es.java b/src/share/classes/sun/util/resources/TimeZoneNames_es.java index 5b3001b70..27bf65331 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_es.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_es.java @@ -291,6 +291,7 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle { {"America/Argentina/Mendoza", AGT}, {"America/Argentina/Rio_Gallegos", AGT}, {"America/Argentina/San_Juan", AGT}, + {"America/Argentina/San_Luis", AGT}, {"America/Argentina/Tucuman", AGT}, {"America/Argentina/Ushuaia", AGT}, {"America/Aruba", AST}, @@ -329,7 +330,7 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle { {"America/Detroit", EST}, {"America/Dominica", AST}, {"America/Edmonton", MST}, - {"America/Eirunepe", ACT}, + {"America/Eirunepe", AMT}, {"America/El_Salvador", CST}, {"America/Ensenada", PST}, {"America/Fort_Wayne", EST}, @@ -372,6 +373,7 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle { {"America/Maceio", BRT}, {"America/Managua", CST}, {"America/Manaus", AMT}, + {"America/Marigot", AST}, {"America/Martinique", AST}, {"America/Mazatlan", MST}, {"America/Mendoza", AGT}, @@ -398,7 +400,7 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle { "Hora de verano de Surinam", "SRST"}}, {"America/Port-au-Prince", EST}, {"America/Port_of_Spain", AST}, - {"America/Porto_Acre", ACT}, + {"America/Porto_Acre", AMT}, {"America/Porto_Velho", AMT}, {"America/Puerto_Rico", AST}, {"America/Rainy_River", CST}, @@ -406,13 +408,15 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle { {"America/Recife", BRT}, {"America/Regina", CST}, {"America/Resolute", EST}, - {"America/Rio_Branco", ACT}, + {"America/Rio_Branco", AMT}, {"America/Rosario", AGT}, + {"America/Santarem", BRT}, {"America/Santiago", CLT}, {"America/Santo_Domingo", AST}, {"America/Sao_Paulo", BRT}, {"America/Scoresbysund", EGT}, {"America/Shiprock", MST}, + {"America/St_Barthelemy", AST}, {"America/St_Kitts", AST}, {"America/St_Lucia", AST}, {"America/St_Thomas", AST}, @@ -485,6 +489,7 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle { "Hora de verano de Tajikist\u00e1n", "TJST"}}, {"Asia/Gaza", EET}, {"Asia/Harbin", CTT}, + {"Asia/Ho_Chi_Minh", ICT}, {"Asia/Hong_Kong", HKT}, {"Asia/Hovd", new String[] {"Hora de Hovd", "HOVT", "Hora de verano de Hovd", "HOVST"}}, @@ -502,6 +507,7 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Katmandu", new String[] {"Hora de Nepal", "NPT", "Hora de verano de Nepal", "NPST"}}, + {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"Hora de Krasnoyarsk", "KRAT", "Hora de verano de Krasnoyarsk", "KRAST"}}, {"Asia/Kuala_Lumpur", MYT}, @@ -599,7 +605,7 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle { {"Australia/Yancowinna", BROKEN_HILL}, {"BET", BRT}, {"BST", BDT}, - {"Brazil/Acre", ACT}, + {"Brazil/Acre", AMT}, {"Brazil/DeNoronha", NORONHA}, {"Brazil/East", BRT}, {"Brazil/West", AMT}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_fr.java b/src/share/classes/sun/util/resources/TimeZoneNames_fr.java index 3e0f61ce6..e9a3811d1 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_fr.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_fr.java @@ -291,6 +291,7 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle { {"America/Argentina/Mendoza", AGT}, {"America/Argentina/Rio_Gallegos", AGT}, {"America/Argentina/San_Juan", AGT}, + {"America/Argentina/San_Luis", AGT}, {"America/Argentina/Tucuman", AGT}, {"America/Argentina/Ushuaia", AGT}, {"America/Aruba", AST}, @@ -329,7 +330,7 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle { {"America/Detroit", EST}, {"America/Dominica", AST}, {"America/Edmonton", MST}, - {"America/Eirunepe", ACT}, + {"America/Eirunepe", AMT}, {"America/El_Salvador", CST}, {"America/Ensenada", PST}, {"America/Fort_Wayne", EST}, @@ -372,6 +373,7 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle { {"America/Maceio", BRT}, {"America/Managua", CST}, {"America/Manaus", AMT}, + {"America/Marigot", AST}, {"America/Martinique", AST}, {"America/Mazatlan", MST}, {"America/Mendoza", AGT}, @@ -398,7 +400,7 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle { "Heure d'\u00e9t\u00e9 du Surinam", "SRST"}}, {"America/Port-au-Prince", EST}, {"America/Port_of_Spain", AST}, - {"America/Porto_Acre", ACT}, + {"America/Porto_Acre", AMT}, {"America/Porto_Velho", AMT}, {"America/Puerto_Rico", AST}, {"America/Rainy_River", CST}, @@ -406,13 +408,15 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle { {"America/Recife", BRT}, {"America/Regina", CST}, {"America/Resolute", EST}, - {"America/Rio_Branco", ACT}, + {"America/Rio_Branco", AMT}, {"America/Rosario", AGT}, + {"America/Santarem", BRT}, {"America/Santiago", CLT}, {"America/Santo_Domingo", AST}, {"America/Sao_Paulo", BRT}, {"America/Scoresbysund", EGT}, {"America/Shiprock", MST}, + {"America/St_Barthelemy", AST}, {"America/St_Kitts", AST}, {"America/St_Lucia", AST}, {"America/St_Thomas", AST}, @@ -485,6 +489,7 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle { "Heure d'\u00e9t\u00e9 du Tadjikistan", "TJST"}}, {"Asia/Gaza", EET}, {"Asia/Harbin", CTT}, + {"Asia/Ho_Chi_Minh", ICT}, {"Asia/Hong_Kong", HKT}, {"Asia/Hovd", new String[] {"Heure de Hovd", "HOVT", "Heure d'\u00e9t\u00e9 de Hovd", "HOVST"}}, @@ -502,6 +507,7 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Katmandu", new String[] {"Heure du N\u00e9pal", "NPT", "Heure d'\u00e9t\u00e9 du N\u00e9pal", "NPST"}}, + {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"Heure de Krasno\u00efarsk", "KRAT", "Heure d'\u00e9t\u00e9 de Krasno\u00efarsk", "KRAST"}}, {"Asia/Kuala_Lumpur", MYT}, @@ -599,7 +605,7 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle { {"Australia/Yancowinna", BROKEN_HILL}, {"BET", BRT}, {"BST", BDT}, - {"Brazil/Acre", ACT}, + {"Brazil/Acre", AMT}, {"Brazil/DeNoronha", NORONHA}, {"Brazil/East", BRT}, {"Brazil/West", AMT}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_it.java b/src/share/classes/sun/util/resources/TimeZoneNames_it.java index 2ca155757..8012c36f7 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_it.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_it.java @@ -291,6 +291,7 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle { {"America/Argentina/Mendoza", AGT}, {"America/Argentina/Rio_Gallegos", AGT}, {"America/Argentina/San_Juan", AGT}, + {"America/Argentina/San_Luis", AGT}, {"America/Argentina/Tucuman", AGT}, {"America/Argentina/Ushuaia", AGT}, {"America/Aruba", AST}, @@ -329,7 +330,7 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle { {"America/Detroit", EST}, {"America/Dominica", AST}, {"America/Edmonton", MST}, - {"America/Eirunepe", ACT}, + {"America/Eirunepe", AMT}, {"America/El_Salvador", CST}, {"America/Ensenada", PST}, {"America/Fort_Wayne", EST}, @@ -372,6 +373,7 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle { {"America/Maceio", BRT}, {"America/Managua", CST}, {"America/Manaus", AMT}, + {"America/Marigot", AST}, {"America/Martinique", AST}, {"America/Mazatlan", MST}, {"America/Mendoza", AGT}, @@ -398,7 +400,7 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle { "Ora estiva di Suriname", "SRST"}}, {"America/Port-au-Prince", EST}, {"America/Port_of_Spain", AST}, - {"America/Porto_Acre", ACT}, + {"America/Porto_Acre", AMT}, {"America/Porto_Velho", AMT}, {"America/Puerto_Rico", AST}, {"America/Rainy_River", CST}, @@ -406,13 +408,15 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle { {"America/Recife", BRT}, {"America/Regina", CST}, {"America/Resolute", EST}, - {"America/Rio_Branco", ACT}, + {"America/Rio_Branco", AMT}, {"America/Rosario", AGT}, + {"America/Santarem", BRT}, {"America/Santiago", CLT}, {"America/Santo_Domingo", AST}, {"America/Sao_Paulo", BRT}, {"America/Scoresbysund", EGT}, {"America/Shiprock", MST}, + {"America/St_Barthelemy", AST}, {"America/St_Kitts", AST}, {"America/St_Lucia", AST}, {"America/St_Thomas", AST}, @@ -485,6 +489,7 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle { "Ora estiva del Tagikistan", "TJST"}}, {"Asia/Gaza", EET}, {"Asia/Harbin", CTT}, + {"Asia/Ho_Chi_Minh", ICT}, {"Asia/Hong_Kong", HKT}, {"Asia/Hovd", new String[] {"Ora di Hovd", "HOVT", "Ora estiva di Hovd", "HOVST"}}, @@ -502,6 +507,7 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Katmandu", new String[] {"Ora del Nepal", "NPT", "Ora estiva del Nepal", "NPST"}}, + {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"Ora di Krasnojarsk", "KRAT", "Ora estiva di Krasnojarsk", "KRAST"}}, {"Asia/Kuala_Lumpur", MYT}, @@ -599,7 +605,7 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle { {"Australia/Yancowinna", BROKEN_HILL}, {"BET", BRT}, {"BST", BDT}, - {"Brazil/Acre", ACT}, + {"Brazil/Acre", AMT}, {"Brazil/DeNoronha", NORONHA}, {"Brazil/East", BRT}, {"Brazil/West", AMT}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_ja.java b/src/share/classes/sun/util/resources/TimeZoneNames_ja.java index 19b1291a1..33a1913bd 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_ja.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_ja.java @@ -291,6 +291,7 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle { {"America/Argentina/Mendoza", AGT}, {"America/Argentina/Rio_Gallegos", AGT}, {"America/Argentina/San_Juan", AGT}, + {"America/Argentina/San_Luis", AGT}, {"America/Argentina/Tucuman", AGT}, {"America/Argentina/Ushuaia", AGT}, {"America/Aruba", AST}, @@ -329,7 +330,7 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle { {"America/Detroit", EST}, {"America/Dominica", AST}, {"America/Edmonton", MST}, - {"America/Eirunepe", ACT}, + {"America/Eirunepe", AMT}, {"America/El_Salvador", CST}, {"America/Ensenada", PST}, {"America/Fort_Wayne", EST}, @@ -372,6 +373,7 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle { {"America/Maceio", BRT}, {"America/Managua", CST}, {"America/Manaus", AMT}, + {"America/Marigot", AST}, {"America/Martinique", AST}, {"America/Mazatlan", MST}, {"America/Mendoza", AGT}, @@ -398,7 +400,7 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle { "\u30b9\u30ea\u30ca\u30e0\u590f\u6642\u9593", "SRST"}}, {"America/Port-au-Prince", EST}, {"America/Port_of_Spain", AST}, - {"America/Porto_Acre", ACT}, + {"America/Porto_Acre", AMT}, {"America/Porto_Velho", AMT}, {"America/Puerto_Rico", AST}, {"America/Rainy_River", CST}, @@ -406,13 +408,15 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle { {"America/Recife", BRT}, {"America/Regina", CST}, {"America/Resolute", EST}, - {"America/Rio_Branco", ACT}, + {"America/Rio_Branco", AMT}, {"America/Rosario", AGT}, + {"America/Santarem", BRT}, {"America/Santiago", CLT}, {"America/Santo_Domingo", AST}, {"America/Sao_Paulo", BRT}, {"America/Scoresbysund", EGT}, {"America/Shiprock", MST}, + {"America/St_Barthelemy", AST}, {"America/St_Kitts", AST}, {"America/St_Lucia", AST}, {"America/St_Thomas", AST}, @@ -485,6 +489,7 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle { "\u30bf\u30b8\u30ad\u30b9\u30bf\u30f3\u590f\u6642\u9593", "TJST"}}, {"Asia/Gaza", EET}, {"Asia/Harbin", CTT}, + {"Asia/Ho_Chi_Minh", ICT}, {"Asia/Hong_Kong", HKT}, {"Asia/Hovd", new String[] {"\u30db\u30d6\u30c9\u6642\u9593", "HOVT", "\u30db\u30d6\u30c9\u590f\u6642\u9593", "HOVST"}}, @@ -502,6 +507,7 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Katmandu", new String[] {"\u30cd\u30d1\u30fc\u30eb\u6642\u9593", "NPT", "\u30cd\u30d1\u30fc\u30eb\u590f\u6642\u9593", "NPST"}}, + {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"\u30af\u30e9\u30b9\u30ce\u30e4\u30eb\u30b9\u30af\u6642\u9593", "KRAT", "\u30af\u30e9\u30b9\u30ce\u30e4\u30eb\u30b9\u30af\u590f\u6642\u9593", "KRAST"}}, {"Asia/Kuala_Lumpur", MYT}, @@ -599,7 +605,7 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle { {"Australia/Yancowinna", BROKEN_HILL}, {"BET", BRT}, {"BST", BDT}, - {"Brazil/Acre", ACT}, + {"Brazil/Acre", AMT}, {"Brazil/DeNoronha", NORONHA}, {"Brazil/East", BRT}, {"Brazil/West", AMT}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_ko.java b/src/share/classes/sun/util/resources/TimeZoneNames_ko.java index 9cf574709..806823797 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_ko.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_ko.java @@ -291,6 +291,7 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle { {"America/Argentina/Mendoza", AGT}, {"America/Argentina/Rio_Gallegos", AGT}, {"America/Argentina/San_Juan", AGT}, + {"America/Argentina/San_Luis", AGT}, {"America/Argentina/Tucuman", AGT}, {"America/Argentina/Ushuaia", AGT}, {"America/Aruba", AST}, @@ -329,7 +330,7 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle { {"America/Detroit", EST}, {"America/Dominica", AST}, {"America/Edmonton", MST}, - {"America/Eirunepe", ACT}, + {"America/Eirunepe", AMT}, {"America/El_Salvador", CST}, {"America/Ensenada", PST}, {"America/Fort_Wayne", EST}, @@ -372,6 +373,7 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle { {"America/Maceio", BRT}, {"America/Managua", CST}, {"America/Manaus", AMT}, + {"America/Marigot", AST}, {"America/Martinique", AST}, {"America/Mazatlan", MST}, {"America/Mendoza", AGT}, @@ -398,7 +400,7 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle { "\uc218\ub9ac\ub0a8 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "SRST"}}, {"America/Port-au-Prince", EST}, {"America/Port_of_Spain", AST}, - {"America/Porto_Acre", ACT}, + {"America/Porto_Acre", AMT}, {"America/Porto_Velho", AMT}, {"America/Puerto_Rico", AST}, {"America/Rainy_River", CST}, @@ -406,13 +408,15 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle { {"America/Recife", BRT}, {"America/Regina", CST}, {"America/Resolute", EST}, - {"America/Rio_Branco", ACT}, + {"America/Rio_Branco", AMT}, {"America/Rosario", AGT}, + {"America/Santarem", BRT}, {"America/Santiago", CLT}, {"America/Santo_Domingo", AST}, {"America/Sao_Paulo", BRT}, {"America/Scoresbysund", EGT}, {"America/Shiprock", MST}, + {"America/St_Barthelemy", AST}, {"America/St_Kitts", AST}, {"America/St_Lucia", AST}, {"America/St_Thomas", AST}, @@ -485,6 +489,7 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle { "\ud0c0\uc9c0\ud0a4\uc2a4\ud0c4 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "TJST"}}, {"Asia/Gaza", EET}, {"Asia/Harbin", CTT}, + {"Asia/Ho_Chi_Minh", ICT}, {"Asia/Hong_Kong", HKT}, {"Asia/Hovd", new String[] {"Hovd \uc2dc\uac04", "HOVT", "Hovd \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "HOVST"}}, @@ -502,6 +507,7 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Katmandu", new String[] {"\ub124\ud314 \uc2dc\uac04", "NPT", "\ub124\ud314 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "NPST"}}, + {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"\ud06c\ub77c\uc2a4\ub178\uc57c\ub974\uc2a4\ud06c \uc2dc\uac04", "KRAT", "\ud06c\ub77c\uc2a4\ub178\uc57c\ub974\uc2a4\ud06c \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "KRAST"}}, {"Asia/Kuala_Lumpur", MYT}, @@ -599,7 +605,7 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle { {"Australia/Yancowinna", BROKEN_HILL}, {"BET", BRT}, {"BST", BDT}, - {"Brazil/Acre", ACT}, + {"Brazil/Acre", AMT}, {"Brazil/DeNoronha", NORONHA}, {"Brazil/East", BRT}, {"Brazil/West", AMT}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_sv.java b/src/share/classes/sun/util/resources/TimeZoneNames_sv.java index c8a6589a3..cc872089f 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_sv.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_sv.java @@ -291,6 +291,7 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle { {"America/Argentina/Mendoza", AGT}, {"America/Argentina/Rio_Gallegos", AGT}, {"America/Argentina/San_Juan", AGT}, + {"America/Argentina/San_Luis", AGT}, {"America/Argentina/Tucuman", AGT}, {"America/Argentina/Ushuaia", AGT}, {"America/Aruba", AST}, @@ -329,7 +330,7 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle { {"America/Detroit", EST}, {"America/Dominica", AST}, {"America/Edmonton", MST}, - {"America/Eirunepe", ACT}, + {"America/Eirunepe", AMT}, {"America/El_Salvador", CST}, {"America/Ensenada", PST}, {"America/Fort_Wayne", EST}, @@ -372,6 +373,7 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle { {"America/Maceio", BRT}, {"America/Managua", CST}, {"America/Manaus", AMT}, + {"America/Marigot", AST}, {"America/Martinique", AST}, {"America/Mazatlan", MST}, {"America/Mendoza", AGT}, @@ -398,7 +400,7 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle { "Surinam, sommartid", "SRST"}}, {"America/Port-au-Prince", EST}, {"America/Port_of_Spain", AST}, - {"America/Porto_Acre", ACT}, + {"America/Porto_Acre", AMT}, {"America/Porto_Velho", AMT}, {"America/Puerto_Rico", AST}, {"America/Rainy_River", CST}, @@ -406,13 +408,15 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle { {"America/Recife", BRT}, {"America/Regina", CST}, {"America/Resolute", EST}, - {"America/Rio_Branco", ACT}, + {"America/Rio_Branco", AMT}, {"America/Rosario", AGT}, + {"America/Santarem", BRT}, {"America/Santiago", CLT}, {"America/Santo_Domingo", AST}, {"America/Sao_Paulo", BRT}, {"America/Scoresbysund", EGT}, {"America/Shiprock", MST}, + {"America/St_Barthelemy", AST}, {"America/St_Kitts", AST}, {"America/St_Lucia", AST}, {"America/St_Thomas", AST}, @@ -485,6 +489,7 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle { "Tadzjikistan, sommartid", "TJST"}}, {"Asia/Gaza", EET}, {"Asia/Harbin", CTT}, + {"Asia/Ho_Chi_Minh", ICT}, {"Asia/Hong_Kong", HKT}, {"Asia/Hovd", new String[] {"Hovd, normaltid", "HOVT", "Hovd, sommartid", "HOVST"}}, @@ -502,6 +507,7 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Katmandu", new String[] {"Nepal, normaltid", "NPT", "Nepal, sommartid", "NPST"}}, + {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"Krasnojarsk, normaltid", "KRAT", "Krasnojarsk, sommartid", "KRAST"}}, {"Asia/Kuala_Lumpur", MYT}, @@ -599,7 +605,7 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle { {"Australia/Yancowinna", BROKEN_HILL}, {"BET", BRT}, {"BST", BDT}, - {"Brazil/Acre", ACT}, + {"Brazil/Acre", AMT}, {"Brazil/DeNoronha", NORONHA}, {"Brazil/East", BRT}, {"Brazil/West", AMT}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_zh_CN.java b/src/share/classes/sun/util/resources/TimeZoneNames_zh_CN.java index 996386398..62304e985 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_zh_CN.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_zh_CN.java @@ -291,6 +291,7 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle { {"America/Argentina/Mendoza", AGT}, {"America/Argentina/Rio_Gallegos", AGT}, {"America/Argentina/San_Juan", AGT}, + {"America/Argentina/San_Luis", AGT}, {"America/Argentina/Tucuman", AGT}, {"America/Argentina/Ushuaia", AGT}, {"America/Aruba", AST}, @@ -329,7 +330,7 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle { {"America/Detroit", EST}, {"America/Dominica", AST}, {"America/Edmonton", MST}, - {"America/Eirunepe", ACT}, + {"America/Eirunepe", AMT}, {"America/El_Salvador", CST}, {"America/Ensenada", PST}, {"America/Fort_Wayne", EST}, @@ -372,6 +373,7 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle { {"America/Maceio", BRT}, {"America/Managua", CST}, {"America/Manaus", AMT}, + {"America/Marigot", AST}, {"America/Martinique", AST}, {"America/Mazatlan", MST}, {"America/Mendoza", AGT}, @@ -398,7 +400,7 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle { "\u82cf\u5229\u5357\u590f\u4ee4\u65f6", "SRST"}}, {"America/Port-au-Prince", EST}, {"America/Port_of_Spain", AST}, - {"America/Porto_Acre", ACT}, + {"America/Porto_Acre", AMT}, {"America/Porto_Velho", AMT}, {"America/Puerto_Rico", AST}, {"America/Rainy_River", CST}, @@ -406,13 +408,15 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle { {"America/Recife", BRT}, {"America/Regina", CST}, {"America/Resolute", EST}, - {"America/Rio_Branco", ACT}, + {"America/Rio_Branco", AMT}, {"America/Rosario", AGT}, + {"America/Santarem", BRT}, {"America/Santiago", CLT}, {"America/Santo_Domingo", AST}, {"America/Sao_Paulo", BRT}, {"America/Scoresbysund", EGT}, {"America/Shiprock", MST}, + {"America/St_Barthelemy", AST}, {"America/St_Kitts", AST}, {"America/St_Lucia", AST}, {"America/St_Thomas", AST}, @@ -485,6 +489,7 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle { "\u5854\u5409\u514b\u65af\u5766\u590f\u4ee4\u65f6", "TJST"}}, {"Asia/Gaza", EET}, {"Asia/Harbin", CTT}, + {"Asia/Ho_Chi_Minh", ICT}, {"Asia/Hong_Kong", HKT}, {"Asia/Hovd", new String[] {"\u79d1\u5e03\u591a\u65f6\u95f4", "HOVT", "\u79d1\u5e03\u591a\u590f\u4ee4\u65f6", "HOVST"}}, @@ -502,6 +507,7 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Katmandu", new String[] {"\u5c3c\u6cca\u5c14\u65f6\u95f4", "NPT", "\u5c3c\u6cca\u5c14\u590f\u4ee4\u65f6", "NPST"}}, + {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"\u514b\u62c9\u65af\u8bfa\u4e9a\u5c14\u65af\u514b\u65f6\u95f4", "KRAT", "\u514b\u62c9\u65af\u8bfa\u4e9a\u5c14\u65af\u514b\u590f\u4ee4\u65f6", "KRAST"}}, {"Asia/Kuala_Lumpur", MYT}, @@ -599,7 +605,7 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle { {"Australia/Yancowinna", BROKEN_HILL}, {"BET", BRT}, {"BST", BDT}, - {"Brazil/Acre", ACT}, + {"Brazil/Acre", AMT}, {"Brazil/DeNoronha", NORONHA}, {"Brazil/East", BRT}, {"Brazil/West", AMT}, diff --git a/src/share/classes/sun/util/resources/TimeZoneNames_zh_TW.java b/src/share/classes/sun/util/resources/TimeZoneNames_zh_TW.java index d4796f1c1..77f539fe9 100644 --- a/src/share/classes/sun/util/resources/TimeZoneNames_zh_TW.java +++ b/src/share/classes/sun/util/resources/TimeZoneNames_zh_TW.java @@ -291,6 +291,7 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle { {"America/Argentina/Mendoza", AGT}, {"America/Argentina/Rio_Gallegos", AGT}, {"America/Argentina/San_Juan", AGT}, + {"America/Argentina/San_Luis", AGT}, {"America/Argentina/Tucuman", AGT}, {"America/Argentina/Ushuaia", AGT}, {"America/Aruba", AST}, @@ -329,7 +330,7 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle { {"America/Detroit", EST}, {"America/Dominica", AST}, {"America/Edmonton", MST}, - {"America/Eirunepe", ACT}, + {"America/Eirunepe", AMT}, {"America/El_Salvador", CST}, {"America/Ensenada", PST}, {"America/Fort_Wayne", EST}, @@ -372,6 +373,7 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle { {"America/Maceio", BRT}, {"America/Managua", CST}, {"America/Manaus", AMT}, + {"America/Marigot", AST}, {"America/Martinique", AST}, {"America/Mazatlan", MST}, {"America/Mendoza", AGT}, @@ -398,7 +400,7 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle { "\u8607\u5229\u5357\u590f\u4ee4\u6642\u9593", "SRST"}}, {"America/Port-au-Prince", EST}, {"America/Port_of_Spain", AST}, - {"America/Porto_Acre", ACT}, + {"America/Porto_Acre", AMT}, {"America/Porto_Velho", AMT}, {"America/Puerto_Rico", AST}, {"America/Rainy_River", CST}, @@ -406,13 +408,15 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle { {"America/Recife", BRT}, {"America/Regina", CST}, {"America/Resolute", EST}, - {"America/Rio_Branco", ACT}, + {"America/Rio_Branco", AMT}, {"America/Rosario", AGT}, + {"America/Santarem", BRT}, {"America/Santiago", CLT}, {"America/Santo_Domingo", AST}, {"America/Sao_Paulo", BRT}, {"America/Scoresbysund", EGT}, {"America/Shiprock", MST}, + {"America/St_Barthelemy", AST}, {"America/St_Kitts", AST}, {"America/St_Lucia", AST}, {"America/St_Thomas", AST}, @@ -485,6 +489,7 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle { "\u5854\u5409\u514b\u590f\u4ee4\u6642\u9593", "TJST"}}, {"Asia/Gaza", EET}, {"Asia/Harbin", CTT}, + {"Asia/Ho_Chi_Minh", ICT}, {"Asia/Hong_Kong", HKT}, {"Asia/Hovd", new String[] {"\u4faf\u5fb7 (Hovd) \u6642\u9593", "HOVT", "\u4faf\u5fb7 (Hovd) \u590f\u4ee4\u6642\u9593", "HOVST"}}, @@ -502,6 +507,7 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Katmandu", new String[] {"\u5c3c\u6cca\u723e\u6642\u9593", "NPT", "\u5c3c\u6cca\u723e\u590f\u4ee4\u6642\u9593", "NPST"}}, + {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"\u514b\u62c9\u65af\u8afe\u4e9e\u723e\u65af\u514b\u6642\u9593", "KRAT", "\u514b\u62c9\u65af\u8afe\u4e9e\u723e\u65af\u514b\u590f\u4ee4\u6642\u9593", "KRAST"}}, {"Asia/Kuala_Lumpur", MYT}, @@ -599,7 +605,7 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle { {"Australia/Yancowinna", BROKEN_HILL}, {"BET", BRT}, {"BST", BDT}, - {"Brazil/Acre", ACT}, + {"Brazil/Acre", AMT}, {"Brazil/DeNoronha", NORONHA}, {"Brazil/East", BRT}, {"Brazil/West", AMT}, -- GitLab From bdaaed3393bf97e51a49aed5334a3c8635d83652 Mon Sep 17 00:00:00 2001 From: bristor Date: Mon, 8 Sep 2008 13:44:32 -0700 Subject: [PATCH 097/139] 6661861: Decrease memory use of Inflaters by ZipFile Summary: Fix allows release of native resources earlier than without fix Reviewed-by: alanb --- src/share/classes/java/util/zip/Inflater.java | 7 +++++-- src/share/classes/java/util/zip/ZipFile.java | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/share/classes/java/util/zip/Inflater.java b/src/share/classes/java/util/zip/Inflater.java index bc0bdf55f..68c8d6233 100644 --- a/src/share/classes/java/util/zip/Inflater.java +++ b/src/share/classes/java/util/zip/Inflater.java @@ -1,5 +1,5 @@ /* - * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -73,11 +73,13 @@ package java.util.zip; public class Inflater { private long strm; - private byte[] buf = new byte[0]; + private byte[] buf = defaultBuf; private int off, len; private boolean finished; private boolean needDict; + private static final byte[] defaultBuf = new byte[0]; + static { /* Zip library is loaded from System.initializeSystemClass */ initIDs(); @@ -318,6 +320,7 @@ class Inflater { public synchronized void reset() { ensureOpen(); reset(strm); + buf = defaultBuf; finished = false; needDict = false; off = len = 0; diff --git a/src/share/classes/java/util/zip/ZipFile.java b/src/share/classes/java/util/zip/ZipFile.java index 8ad177fdc..37e92ddec 100644 --- a/src/share/classes/java/util/zip/ZipFile.java +++ b/src/share/classes/java/util/zip/ZipFile.java @@ -1,5 +1,5 @@ /* - * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1995-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -278,7 +278,6 @@ class ZipFile implements ZipConstants { int size = inflaters.size(); if (size > 0) { Inflater inf = (Inflater)inflaters.remove(size - 1); - inf.reset(); return inf; } else { return new Inflater(true); @@ -291,6 +290,7 @@ class ZipFile implements ZipConstants { */ private void releaseInflater(Inflater inf) { synchronized (inflaters) { + inf.reset(); inflaters.add(inf); } } -- GitLab From 8a58e7557d1dbb21a430d4e62e6d3ea140599e5d Mon Sep 17 00:00:00 2001 From: bristor Date: Mon, 8 Sep 2008 14:11:13 -0700 Subject: [PATCH 098/139] 6356642: extcheck.exe -verbose throws ArrayIndexOutOfBoundsException exception Summary: Fix causes printing of user-level error messages instead of throwing exceptions Reviewed-by: sherman --- .../com/sun/tools/extcheck/ExtCheck.java | 69 +++++++------- .../classes/com/sun/tools/extcheck/Main.java | 36 ++++++-- .../sun/tools/extcheck/TestExtcheckArgs.java | 92 +++++++++++++++++++ .../sun/tools/extcheck/TestExtcheckArgs.sh | 47 ++++++++++ 4 files changed, 199 insertions(+), 45 deletions(-) create mode 100644 test/com/sun/tools/extcheck/TestExtcheckArgs.java create mode 100644 test/com/sun/tools/extcheck/TestExtcheckArgs.sh diff --git a/src/share/classes/com/sun/tools/extcheck/ExtCheck.java b/src/share/classes/com/sun/tools/extcheck/ExtCheck.java index b83381a65..b69cf00cd 100644 --- a/src/share/classes/com/sun/tools/extcheck/ExtCheck.java +++ b/src/share/classes/com/sun/tools/extcheck/ExtCheck.java @@ -1,5 +1,5 @@ /* - * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -256,13 +256,13 @@ public class ExtCheck { private boolean isNotOlderThan(String already,String target) throws NumberFormatException { - if (already == null || already.length() < 1) { + if (already == null || already.length() < 1) { throw new NumberFormatException("Empty version string"); } - // Until it matches scan and compare numbers - StringTokenizer dtok = new StringTokenizer(target, ".", true); - StringTokenizer stok = new StringTokenizer(already, ".", true); + // Until it matches scan and compare numbers + StringTokenizer dtok = new StringTokenizer(target, ".", true); + StringTokenizer stok = new StringTokenizer(already, ".", true); while (dtok.hasMoreTokens() || stok.hasMoreTokens()) { int dver; int sver; @@ -276,19 +276,19 @@ public class ExtCheck { } else sver = 0; - if (sver < dver) - return false; // Known to be incompatible - if (sver > dver) - return true; // Known to be compatible - - // Check for and absorb separators - if (dtok.hasMoreTokens()) - dtok.nextToken(); - if (stok.hasMoreTokens()) - stok.nextToken(); - // Compare next component - } - // All components numerically equal + if (sver < dver) + return false; // Known to be incompatible + if (sver > dver) + return true; // Known to be compatible + + // Check for and absorb separators + if (dtok.hasMoreTokens()) + dtok.nextToken(); + if (stok.hasMoreTokens()) + stok.nextToken(); + // Compare next component + } + // All components numerically equal return true; } @@ -307,11 +307,10 @@ public class ExtCheck { } /** - * Print out the error message and exit from the program + * Throws a RuntimeException with a message describing the error. */ - static void error(String message){ - System.err.println(message); - System.exit(-1); + static void error(String message) throws RuntimeException { + throw new RuntimeException(message); } @@ -356,19 +355,19 @@ public class ExtCheck { } private JarFile findJarFile(URL url) throws IOException { - // Optimize case where url refers to a local jar file - if ("file".equals(url.getProtocol())) { - String path = url.getFile().replace('/', File.separatorChar); - File file = new File(path); - if (!file.exists()) { - throw new FileNotFoundException(path); - } - return new JarFile(path); - } - URLConnection uc = getBaseURL().openConnection(); - //uc.setRequestProperty(USER_AGENT_JAVA_VERSION, JAVA_VERSION); - return ((JarURLConnection)uc).getJarFile(); - } + // Optimize case where url refers to a local jar file + if ("file".equals(url.getProtocol())) { + String path = url.getFile().replace('/', File.separatorChar); + File file = new File(path); + if (!file.exists()) { + throw new FileNotFoundException(path); + } + return new JarFile(path); + } + URLConnection uc = getBaseURL().openConnection(); + //uc.setRequestProperty(USER_AGENT_JAVA_VERSION, JAVA_VERSION); + return ((JarURLConnection)uc).getJarFile(); + } /* diff --git a/src/share/classes/com/sun/tools/extcheck/Main.java b/src/share/classes/com/sun/tools/extcheck/Main.java index 893af063f..022346efc 100644 --- a/src/share/classes/com/sun/tools/extcheck/Main.java +++ b/src/share/classes/com/sun/tools/extcheck/Main.java @@ -1,5 +1,5 @@ /* - * Copyright 1998 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,10 @@ import java.io.*; */ public final class Main { - + public static final String INSUFFICIENT = "Insufficient number of arguments"; + public static final String MISSING = "Missing argument"; + public static final String DOES_NOT_EXIST = "Jarfile does not exist: "; + public static final String EXTRA = "Extra command line argument: "; /** * Terminates with one of the following codes @@ -40,26 +43,36 @@ public final class Main { * 0 No newer jar file was found * -1 An internal error occurred */ - public static void main(String args[]){ - - if (args.length < 1){ - System.err.println("Usage: extcheck [-verbose] "); + public static void main(String args[]) { + try { + realMain(args); + } catch (Exception ex) { + System.err.println(ex.getMessage()); System.exit(-1); } + } + + public static void realMain(String[] args) throws Exception { + if (args.length < 1) { + usage(INSUFFICIENT); + } int argIndex = 0; boolean verboseFlag = false; - if (args[argIndex].equals("-verbose")){ + if (args[argIndex].equals("-verbose")) { verboseFlag = true; argIndex++; + if (argIndex >= args.length) { + usage(MISSING); + } } String jarName = args[argIndex]; argIndex++; File jarFile = new File(jarName); if (!jarFile.exists()){ - ExtCheck.error("Jarfile " + jarName + " does not exist"); + usage(DOES_NOT_EXIST + jarName); } if (argIndex < args.length) { - ExtCheck.error("Extra command line argument :"+args[argIndex]); + usage(EXTRA + args[argIndex]); } ExtCheck jt = ExtCheck.create(jarFile,verboseFlag); boolean result = jt.checkInstalledAgainstTarget(); @@ -68,7 +81,10 @@ public final class Main { } else { System.exit(1); } - } + private static void usage(String msg) throws Exception { + throw new Exception(msg + "\nUsage: extcheck [-verbose] "); + } } + diff --git a/test/com/sun/tools/extcheck/TestExtcheckArgs.java b/test/com/sun/tools/extcheck/TestExtcheckArgs.java new file mode 100644 index 000000000..0e5514b87 --- /dev/null +++ b/test/com/sun/tools/extcheck/TestExtcheckArgs.java @@ -0,0 +1,92 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * @test + * @bug 6356642 + * @summary Verify that extcheck exits appropriately when invalid args are given. + * @run shell TestExtcheckArgs.sh + * @author Dave Bristor + */ + +import java.io.File; +import com.sun.tools.extcheck.Main; + +/* + * Test extcheck by using Runtime.exec instead of invoking + * com.sun.tools.extcheck.Main.main, since the latter does its own + * System.exit under the conditions checked here. + */ +public class TestExtcheckArgs { + public static void realMain(String[] args) throws Throwable { + String testJar = System.getenv("TESTJAVA") + File.separator + + "lib" + File.separator + "jconsole.jar"; + + verify(new String[] { + }, Main.INSUFFICIENT); + verify(new String[] { + "-verbose" + }, Main.MISSING); + verify(new String[] { + "-verbose", + "foo" + }, Main.DOES_NOT_EXIST); + verify(new String[] { + testJar, + "bar" + }, Main.EXTRA); + verify(new String[] { + "-verbose", + testJar, + "bar" + }, Main.EXTRA); + } + + static void verify(String[] args, String expected) throws Throwable { + try { + Main.realMain(args); + fail(); + } catch (Exception ex) { + if (ex.getMessage().startsWith(expected)) { + pass(); + } else { + fail("Unexpected message: " + ex.getMessage()); + } + } + } + + //--------------------- Infrastructure --------------------------- + static volatile int passed = 0, failed = 0; + static boolean pass() {passed++; return true;} + static boolean fail() {failed++; Thread.dumpStack(); return false;} + static boolean fail(String msg) {System.out.println(msg); return fail();} + static void unexpected(Throwable t) {failed++; t.printStackTrace();} + static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;} + static boolean equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) return pass(); + else return fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + try {realMain(args);} catch (Throwable t) {unexpected(t);} + System.out.println("\nPassed = " + passed + " failed = " + failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} diff --git a/test/com/sun/tools/extcheck/TestExtcheckArgs.sh b/test/com/sun/tools/extcheck/TestExtcheckArgs.sh new file mode 100644 index 000000000..3c478c1e1 --- /dev/null +++ b/test/com/sun/tools/extcheck/TestExtcheckArgs.sh @@ -0,0 +1,47 @@ +#! /bin/sh + +# +# Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code 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 General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +if [ "x$TESTJAVA" = x ]; then + TESTJAVA=$1; shift + TESTCLASSES=. + TESTSRC=. +fi +export TESTJAVA + +case "`uname`" in Windows*|CYGWIN* ) PS=';';; *) PS=':';; esac + +${TESTJAVA}/bin/javac -d ${TESTCLASSES} -classpath ${TESTJAVA}/lib/tools.jar${PS}${TESTCLASSES} ${TESTSRC}/TestExtcheckArgs.java +rc=$? +if [ $rc != 0 ]; then + echo Compilation failure with exit status $rc + exit $rc +fi + +${TESTJAVA}/bin/java -classpath ${TESTJAVA}/lib/tools.jar${PS}${TESTCLASSES} TestExtcheckArgs +rc=$? +if [ $rc != 0 ]; then + echo Execution failure with exit status $rc + exit $rc +fi -- GitLab From dc55077fc7afc8c0d8c2158171e8b6b5d033187e Mon Sep 17 00:00:00 2001 From: sjiang Date: Tue, 9 Sep 2008 14:17:29 +0200 Subject: [PATCH 099/139] 6736611: [Evt Srv] EventSubscriber.unsubscribe removes other listeners Reviewed-by: emcmanus --- .../management/event/EventSubscriber.java | 82 ++++++------ .../management/eventService/SubUnsubTest.java | 125 ++++++++++++++++++ 2 files changed, 164 insertions(+), 43 deletions(-) create mode 100644 test/javax/management/eventService/SubUnsubTest.java diff --git a/src/share/classes/javax/management/event/EventSubscriber.java b/src/share/classes/javax/management/event/EventSubscriber.java index 242634555..9948810bf 100644 --- a/src/share/classes/javax/management/event/EventSubscriber.java +++ b/src/share/classes/javax/management/event/EventSubscriber.java @@ -149,10 +149,10 @@ public class EventSubscriber implements EventConsumer { if (listener == null) throw new IllegalArgumentException("Null listener"); - final ListenerInfo li = new ListenerInfo(listener, filter, handback); - List list; + final MyListenerInfo li = new MyListenerInfo(listener, filter, handback); + List list; - Map> map; + Map> map; Set names; if (name.isPattern()) { map = patternSubscriptionMap; @@ -165,7 +165,7 @@ public class EventSubscriber implements EventConsumer { synchronized (map) { list = map.get(name); if (list == null) { - list = new ArrayList(); + list = new ArrayList(); map.put(name, list); } list.add(li); @@ -186,7 +186,6 @@ public class EventSubscriber implements EventConsumer { public void unsubscribe(ObjectName name, NotificationListener listener) throws ListenerNotFoundException, IOException { - if (logger.traceOn()) logger.trace("unsubscribe", "" + name); @@ -196,7 +195,7 @@ public class EventSubscriber implements EventConsumer { if (listener == null) throw new ListenerNotFoundException(); - Map> map; + Map> map; Set names; if (name.isPattern()) { @@ -207,22 +206,39 @@ public class EventSubscriber implements EventConsumer { names = Collections.singleton(name); } - final ListenerInfo li = new ListenerInfo(listener, null, null); - List list; + List toRemove = new ArrayList(); synchronized (map) { - list = map.get(name); - if (list == null || !list.remove(li)) + List list = map.get(name); + if (list == null) { + throw new ListenerNotFoundException(); + } + + for (MyListenerInfo info : list) { + if (info.listener == listener) { + toRemove.add(info); + } + } + + if (toRemove.isEmpty()) { throw new ListenerNotFoundException(); + } + + for (MyListenerInfo info : toRemove) { + list.remove(info); + } if (list.isEmpty()) map.remove(name); } for (ObjectName mbeanName : names) { - try { - mbeanServer.removeNotificationListener(mbeanName, li.listener); - } catch (Exception e) { - logger.fine("unsubscribe", "removeNotificationListener", e); + for (MyListenerInfo i : toRemove) { + try { + mbeanServer.removeNotificationListener(mbeanName, + i.listener, i.filter, i.handback); + } catch (Exception e) { + logger.fine("unsubscribe", "removeNotificationListener", e); + } } } } @@ -256,12 +272,12 @@ public class EventSubscriber implements EventConsumer { return; } - final List listeners = new ArrayList(); + final List listeners = new ArrayList(); // If there are subscribers for the exact name that has just arrived // then add their listeners to the list. synchronized (exactSubscriptionMap) { - List exactListeners = exactSubscriptionMap.get(name); + List exactListeners = exactSubscriptionMap.get(name); if (exactListeners != null) listeners.addAll(exactListeners); } @@ -277,7 +293,7 @@ public class EventSubscriber implements EventConsumer { } // Add all the listeners just found to the new MBean. - for (ListenerInfo li : listeners) { + for (MyListenerInfo li : listeners) { try { mbeanServer.addNotificationListener( name, @@ -292,12 +308,12 @@ public class EventSubscriber implements EventConsumer { } }; - private static class ListenerInfo { + private static class MyListenerInfo { public final NotificationListener listener; public final NotificationFilter filter; public final Object handback; - public ListenerInfo(NotificationListener listener, + public MyListenerInfo(NotificationListener listener, NotificationFilter filter, Object handback) { @@ -308,26 +324,6 @@ public class EventSubscriber implements EventConsumer { this.filter = filter; this.handback = handback; } - - /* Two ListenerInfo instances are equal if they have the same - * NotificationListener. This means that we can use List.remove - * to implement the two-argument removeNotificationListener. - */ - @Override - public boolean equals(Object o) { - if (o == this) - return true; - - if (!(o instanceof ListenerInfo)) - return false; - - return listener.equals(((ListenerInfo)o).listener); - } - - @Override - public int hashCode() { - return listener.hashCode(); - } } // --------------------------------- @@ -338,10 +334,10 @@ public class EventSubscriber implements EventConsumer { // --------------------------------- private final MBeanServer mbeanServer; - private final Map> exactSubscriptionMap = - new HashMap>(); - private final Map> patternSubscriptionMap = - new HashMap>(); + private final Map> exactSubscriptionMap = + new HashMap>(); + private final Map> patternSubscriptionMap = + new HashMap>(); diff --git a/test/javax/management/eventService/SubUnsubTest.java b/test/javax/management/eventService/SubUnsubTest.java new file mode 100644 index 000000000..f88898823 --- /dev/null +++ b/test/javax/management/eventService/SubUnsubTest.java @@ -0,0 +1,125 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test SubUnsubTest + * @bug 6736611 + * @summary Test not to remove other listeners when calling unsubscribe + * @author Shanliang JIANG + * @run clean SubUnsubTest + * @run build SubUnsubTest + * @run main SubUnsubTest + */ + +import java.lang.management.ManagementFactory; +import javax.management.MBeanServer; +import javax.management.Notification; +import javax.management.NotificationFilter; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventSubscriber; +import javax.management.event.EventClient; +public class SubUnsubTest { + private static class CountListener implements NotificationListener { + volatile int count; + + public void handleNotification(Notification n, Object h) { + count++; + } + } + + public static interface SenderMBean {} + + public static class Sender extends NotificationBroadcasterSupport + implements SenderMBean { + void send() { + Notification n = new Notification("type", this, 1L); + sendNotification(n); + } + } + + public static void main(String[] args) throws Exception { + System.out.println("Testing EventSubscriber-unsubscribe method."); + + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + ObjectName name1 = new ObjectName("d:type=Sender,id=1"); + ObjectName name2 = new ObjectName("d:type=Sender,id=2"); + ObjectName pattern = new ObjectName("d:type=Sender,*"); + Sender sender1 = new Sender(); + Sender sender2 = new Sender(); + mbs.registerMBean(sender1, name1); + mbs.registerMBean(sender2, name2); + + EventSubscriber sub = EventSubscriber.getEventSubscriber(mbs); + + System.out.println("Single subscribe covering both MBeans"); + CountListener listener = new CountListener(); + + System.out.println("Subscribing and adding listeners ..."); + sub.subscribe(pattern, listener, null, null); + sub.subscribe(name2, listener, null, null); + mbs.addNotificationListener(name2, listener, null, null); + + sender1.send(); + sender2.send(); + if (listener.count != 4) { + throw new RuntimeException("Do not receive all notifications: "+ + "Expect 4, got "+listener.count); + } + + System.out.println("Unsubscribe the listener with the pattern."); + sub.unsubscribe(pattern, listener); + listener.count = 0; + sender1.send(); + sender2.send(); + if (listener.count != 2) { + throw new RuntimeException("The method unsubscribe removes wrong listeners."); + } + + System.out.println("Unsubscribe the listener with the ObjectName."); + sub.unsubscribe(name2, listener); + listener.count = 0; + sender1.send(); + sender2.send(); + if (listener.count != 1) { + throw new RuntimeException("The method unsubscribe removes wrong listeners."); + } + + System.out.println("Subscribe twice to same MBean with same listener " + + "but different handback."); + sub.subscribe(name1, listener, null, new Object()); + sub.subscribe(name1, listener, null, new Object()); + listener.count = 0; + + sub.unsubscribe(name1, listener); + sender1.send(); + if (listener.count > 0) { + throw new RuntimeException("EventSubscriber: the method unsubscribe" + + " does not remove a listener which was subscribed 2 times."); + } + + System.out.println("Bye bye!"); + return; + } +} \ No newline at end of file -- GitLab From d1327450b36134ca6e8e2437f217360c59b6c208 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Tue, 9 Sep 2008 14:57:30 +0200 Subject: [PATCH 100/139] 6746196: Some JMX classes do not compile with Eclipse compiler Reviewed-by: dfuchs Contributed-by: roman.kennke@aicas.com --- .../jmx/mbeanserver/DefaultMXBeanMappingFactory.java | 10 +++++----- .../com/sun/jmx/mbeanserver/MBeanIntrospector.java | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java b/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java index 3509c40f3..b86b2e001 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java @@ -1172,10 +1172,10 @@ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory { final Class propertyNamesClass = ConstructorProperties.class; Class targetClass = getTargetClass(); - Constructor[] constrs = targetClass.getConstructors(); + Constructor[] constrs = targetClass.getConstructors(); // Applicable if and only if there are any annotated constructors - List annotatedConstrList = newList(); + List> annotatedConstrList = newList(); for (Constructor constr : constrs) { if (Modifier.isPublic(constr.getModifiers()) && constr.getAnnotation(propertyNamesClass) != null) @@ -1206,7 +1206,7 @@ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory { // Also remember the set of properties in that constructor // so we can test unambiguity. Set getterIndexSets = newSet(); - for (Constructor constr : annotatedConstrList) { + for (Constructor constr : annotatedConstrList) { String[] propertyNames = constr.getAnnotation(propertyNamesClass).value(); @@ -1363,10 +1363,10 @@ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory { } private static class Constr { - final Constructor constructor; + final Constructor constructor; final int[] paramIndexes; final BitSet presentParams; - Constr(Constructor constructor, int[] paramIndexes, + Constr(Constructor constructor, int[] paramIndexes, BitSet presentParams) { this.constructor = constructor; this.paramIndexes = paramIndexes; diff --git a/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java b/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java index 725292bca..99aaa8546 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java @@ -623,7 +623,7 @@ abstract class MBeanIntrospector { } private static MBeanConstructorInfo[] findConstructors(Class c) { - Constructor[] cons = c.getConstructors(); + Constructor[] cons = c.getConstructors(); MBeanConstructorInfo[] mbc = new MBeanConstructorInfo[cons.length]; for (int i = 0; i < cons.length; i++) { String descr = "Public constructor of the MBean"; -- GitLab From 9d7df9ae2cbe9962842240da4dd2919a438a24a4 Mon Sep 17 00:00:00 2001 From: dfuchs Date: Tue, 9 Sep 2008 17:01:45 +0200 Subject: [PATCH 101/139] 6745832: jmx namespaces: Some refactoring/commenting would improve code readability. Reviewed-by: emcmanus --- .../DefaultMBeanServerInterceptor.java | 4 +- .../jmx/interceptor/DispatchInterceptor.java | 94 ++++---- .../DomainDispatchInterceptor.java | 82 +++++-- .../NamespaceDispatchInterceptor.java | 40 ++-- .../classes/com/sun/jmx/mbeanserver/Util.java | 15 +- .../sun/jmx/namespace/DomainInterceptor.java | 83 +++---- .../sun/jmx/namespace/HandlerInterceptor.java | 64 ++--- .../sun/jmx/namespace/JMXNamespaceUtils.java | 21 +- .../jmx/namespace/NamespaceInterceptor.java | 4 - .../jmx/namespace/RoutingConnectionProxy.java | 55 ++--- .../RoutingMBeanServerConnection.java | 218 ++++++++++-------- .../com/sun/jmx/namespace/RoutingProxy.java | 148 ++++++++++-- .../sun/jmx/namespace/RoutingServerProxy.java | 52 ++--- .../javax/management/namespace/JMXDomain.java | 12 +- .../management/namespace/JMXNamespace.java | 38 +-- .../management/namespace/JMXNamespaces.java | 6 +- .../namespace/JMXRemoteNamespace.java | 49 ++-- .../MBeanServerConnectionWrapper.java | 1 - test/javax/management/namespace/Wombat.java | 7 +- 19 files changed, 579 insertions(+), 414 deletions(-) diff --git a/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java index 7d95a77fd..7faeb8727 100644 --- a/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java +++ b/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java @@ -2021,7 +2021,7 @@ public class DefaultMBeanServerInterceptor private void addJMXNamespace(JMXNamespace namespace, final ObjectName logicalName, final Queue postQueue) { - dispatcher.addNamespace(logicalName, namespace, postQueue); + dispatcher.addInterceptorFor(logicalName, namespace, postQueue); } /** @@ -2035,7 +2035,7 @@ public class DefaultMBeanServerInterceptor private void removeJMXNamespace(JMXNamespace namespace, final ObjectName logicalName, final Queue postQueue) { - dispatcher.removeNamespace(logicalName, namespace, postQueue); + dispatcher.removeInterceptorFor(logicalName, namespace, postQueue); } /** diff --git a/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java index 9e8625d16..4a79567fe 100644 --- a/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java +++ b/src/share/classes/com/sun/jmx/interceptor/DispatchInterceptor.java @@ -194,7 +194,7 @@ public abstract class DispatchInterceptor // found in the handlerMap. Note: there doesn't need to be an interceptor // for that key in the Map. // - public abstract String getHandlerKey(ObjectName name); + abstract String getHandlerKey(ObjectName name); // Returns an interceptor for that name, or null if there's no interceptor // for that name. @@ -277,7 +277,7 @@ public abstract class DispatchInterceptor // of JMXNamespace (or a subclass of it) is registered as an MBean. // This method is usually invoked from within the repository lock, // hence the necessity of the postRegisterQueue. - public void addNamespace(ObjectName name, N jmxNamespace, + public void addInterceptorFor(ObjectName name, N jmxNamespace, Queue postRegisterQueue) { final String key = getHandlerKey(name); validateHandlerNameFor(key,name); @@ -298,7 +298,7 @@ public abstract class DispatchInterceptor // of JMXNamespace (or a subclass of it) is deregistered. // This method is usually invoked from within the repository lock, // hence the necessity of the postDeregisterQueue. - public void removeNamespace(ObjectName name, N jmxNamespace, + public void removeInterceptorFor(ObjectName name, N jmxNamespace, Queue postDeregisterQueue) { final String key = getHandlerKey(name); final T ns; @@ -330,7 +330,7 @@ public abstract class DispatchInterceptor } // From MBeanServer - public ObjectInstance createMBean(String className, ObjectName name) + public final ObjectInstance createMBean(String className, ObjectName name) throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException { @@ -338,7 +338,7 @@ public abstract class DispatchInterceptor } // From MBeanServer - public ObjectInstance createMBean(String className, ObjectName name, + public final ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName) throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, @@ -347,7 +347,7 @@ public abstract class DispatchInterceptor } // From MBeanServer - public ObjectInstance createMBean(String className, ObjectName name, + public final ObjectInstance createMBean(String className, ObjectName name, Object params[], String signature[]) throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, @@ -357,7 +357,7 @@ public abstract class DispatchInterceptor } // From MBeanServer - public ObjectInstance createMBean(String className, ObjectName name, + public final ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, Object params[], String signature[]) throws ReflectionException, InstanceAlreadyExistsException, @@ -368,42 +368,43 @@ public abstract class DispatchInterceptor } // From MBeanServer - public ObjectInstance registerMBean(Object object, ObjectName name) + public final ObjectInstance registerMBean(Object object, ObjectName name) throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { return getInterceptorForCreate(name).registerMBean(object,name); } // From MBeanServer - public void unregisterMBean(ObjectName name) + public final void unregisterMBean(ObjectName name) throws InstanceNotFoundException, MBeanRegistrationException { getInterceptorForInstance(name).unregisterMBean(name); } // From MBeanServer - public ObjectInstance getObjectInstance(ObjectName name) + public final ObjectInstance getObjectInstance(ObjectName name) throws InstanceNotFoundException { return getInterceptorForInstance(name).getObjectInstance(name); } // From MBeanServer - public Set queryMBeans(ObjectName name, QueryExp query) { - final QueryInterceptor mbs = + public final Set queryMBeans(ObjectName name, + QueryExp query) { + final QueryInterceptor queryInvoker = getInterceptorForQuery(name); - if (mbs == null) return Collections.emptySet(); - else return mbs.queryMBeans(name,query); + if (queryInvoker == null) return Collections.emptySet(); + else return queryInvoker.queryMBeans(name,query); } // From MBeanServer - public Set queryNames(ObjectName name, QueryExp query) { - final QueryInterceptor mbs = + public final Set queryNames(ObjectName name, QueryExp query) { + final QueryInterceptor queryInvoker = getInterceptorForQuery(name); - if (mbs == null) return Collections.emptySet(); - else return mbs.queryNames(name,query); + if (queryInvoker == null) return Collections.emptySet(); + else return queryInvoker.queryNames(name,query); } // From MBeanServer - public boolean isRegistered(ObjectName name) { + public final boolean isRegistered(ObjectName name) { final MBeanServer mbs = getInterceptorOrNullFor(name); if (mbs == null) return false; else return mbs.isRegistered(name); @@ -415,20 +416,21 @@ public abstract class DispatchInterceptor } // From MBeanServer - public Object getAttribute(ObjectName name, String attribute) + public final Object getAttribute(ObjectName name, String attribute) throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException { return getInterceptorForInstance(name).getAttribute(name,attribute); } // From MBeanServer - public AttributeList getAttributes(ObjectName name, String[] attributes) + public final AttributeList getAttributes(ObjectName name, + String[] attributes) throws InstanceNotFoundException, ReflectionException { return getInterceptorForInstance(name).getAttributes(name,attributes); } // From MBeanServer - public void setAttribute(ObjectName name, Attribute attribute) + public final void setAttribute(ObjectName name, Attribute attribute) throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { @@ -436,14 +438,14 @@ public abstract class DispatchInterceptor } // From MBeanServer - public AttributeList setAttributes(ObjectName name, + public final AttributeList setAttributes(ObjectName name, AttributeList attributes) throws InstanceNotFoundException, ReflectionException { return getInterceptorForInstance(name).setAttributes(name,attributes); } // From MBeanServer - public Object invoke(ObjectName name, String operationName, + public final Object invoke(ObjectName name, String operationName, Object params[], String signature[]) throws InstanceNotFoundException, MBeanException, ReflectionException { @@ -463,63 +465,69 @@ public abstract class DispatchInterceptor public abstract String[] getDomains(); // From MBeanServer - public void addNotificationListener(ObjectName name, + public final void addNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, Object handback) throws InstanceNotFoundException { - getInterceptorForInstance(name).addNotificationListener(name,listener,filter, + getInterceptorForInstance(name). + addNotificationListener(name,listener,filter, handback); } // From MBeanServer - public void addNotificationListener(ObjectName name, + public final void addNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, Object handback) throws InstanceNotFoundException { - getInterceptorForInstance(name).addNotificationListener(name,listener,filter, + getInterceptorForInstance(name). + addNotificationListener(name,listener,filter, handback); } // From MBeanServer - public void removeNotificationListener(ObjectName name, + public final void removeNotificationListener(ObjectName name, ObjectName listener) throws InstanceNotFoundException, ListenerNotFoundException { - getInterceptorForInstance(name).removeNotificationListener(name,listener); + getInterceptorForInstance(name). + removeNotificationListener(name,listener); } // From MBeanServer - public void removeNotificationListener(ObjectName name, + public final void removeNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, Object handback) throws InstanceNotFoundException, ListenerNotFoundException { - getInterceptorForInstance(name).removeNotificationListener(name,listener,filter, + getInterceptorForInstance(name). + removeNotificationListener(name,listener,filter, handback); } // From MBeanServer - public void removeNotificationListener(ObjectName name, + public final void removeNotificationListener(ObjectName name, NotificationListener listener) throws InstanceNotFoundException, ListenerNotFoundException { - getInterceptorForInstance(name).removeNotificationListener(name,listener); + getInterceptorForInstance(name). + removeNotificationListener(name,listener); } // From MBeanServer - public void removeNotificationListener(ObjectName name, + public final void removeNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, Object handback) throws InstanceNotFoundException, ListenerNotFoundException { - getInterceptorForInstance(name).removeNotificationListener(name,listener,filter, + getInterceptorForInstance(name). + removeNotificationListener(name,listener,filter, handback); } // From MBeanServer - public MBeanInfo getMBeanInfo(ObjectName name) + public final MBeanInfo getMBeanInfo(ObjectName name) throws InstanceNotFoundException, IntrospectionException, ReflectionException { return getInterceptorForInstance(name).getMBeanInfo(name); @@ -527,21 +535,23 @@ public abstract class DispatchInterceptor // From MBeanServer - public boolean isInstanceOf(ObjectName name, String className) + public final boolean isInstanceOf(ObjectName name, String className) throws InstanceNotFoundException { return getInterceptorForInstance(name).isInstanceOf(name,className); } // From MBeanServer - public ClassLoader getClassLoaderFor(ObjectName mbeanName) + public final ClassLoader getClassLoaderFor(ObjectName mbeanName) throws InstanceNotFoundException { - return getInterceptorForInstance(mbeanName).getClassLoaderFor(mbeanName); + return getInterceptorForInstance(mbeanName). + getClassLoaderFor(mbeanName); } // From MBeanServer - public ClassLoader getClassLoader(ObjectName loaderName) + public final ClassLoader getClassLoader(ObjectName loaderName) throws InstanceNotFoundException { - return getInterceptorForInstance(loaderName).getClassLoader(loaderName); + return getInterceptorForInstance(loaderName). + getClassLoader(loaderName); } } diff --git a/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java index cb1489ee5..9b9b1d6b1 100644 --- a/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java +++ b/src/share/classes/com/sun/jmx/interceptor/DomainDispatchInterceptor.java @@ -75,7 +75,7 @@ class DomainDispatchInterceptor private final DomainDispatchInterceptor parent; AggregatingQueryInterceptor(DomainDispatchInterceptor dispatcher) { - super(dispatcher.localNamespace); + super(dispatcher.nextInterceptor); parent = dispatcher; } @@ -91,9 +91,8 @@ class DomainDispatchInterceptor // Add all matching MBeans from local namespace. final Set res = Util.cloneSet(local); - final boolean all = (pattern == null || - pattern.getDomain().equals("*")); if (pattern == null) pattern = ObjectName.WILDCARD; + final boolean all = pattern.getDomain().equals("*"); final String domain = pattern.getDomain(); @@ -142,7 +141,7 @@ class DomainDispatchInterceptor } } - private final DefaultMBeanServerInterceptor localNamespace; + private final DefaultMBeanServerInterceptor nextInterceptor; private final String mbeanServerName; private final MBeanServerDelegate delegate; @@ -165,7 +164,7 @@ class DomainDispatchInterceptor MBeanInstantiator instantiator, Repository repository, NamespaceDispatchInterceptor namespaces) { - localNamespace = new DefaultMBeanServerInterceptor(outer, + nextInterceptor = new DefaultMBeanServerInterceptor(outer, delegate, instantiator,repository,namespaces); mbeanServerName = Util.getMBeanServerSecurityName(delegate); this.delegate = delegate; @@ -182,7 +181,7 @@ class DomainDispatchInterceptor @Override void validateHandlerNameFor(String key, ObjectName name) { super.validateHandlerNameFor(key,name); - final String[] domains = localNamespace.getDomains(); + final String[] domains = nextInterceptor.getDomains(); for (int i=0;i postRegisterQueue) { if (handler instanceof JMXDomain) - localNamespace.addNamespace(name, + nextInterceptor.addInterceptorFor(name, (JMXDomain)handler,postRegisterQueue); - else super.addNamespace(name,handler,postRegisterQueue); + else super.addInterceptorFor(name,handler,postRegisterQueue); } @Override - public void removeNamespace(ObjectName name, JMXNamespace handler, + public void removeInterceptorFor(ObjectName name, JMXNamespace handler, Queue postDeregisterQueue) { if (handler instanceof JMXDomain) - localNamespace.removeNamespace(name,(JMXDomain)handler, + nextInterceptor.removeInterceptorFor(name,(JMXDomain)handler, postDeregisterQueue); - else super.removeNamespace(name,handler,postDeregisterQueue); + else super.removeInterceptorFor(name,handler,postDeregisterQueue); } diff --git a/src/share/classes/com/sun/jmx/mbeanserver/Util.java b/src/share/classes/com/sun/jmx/mbeanserver/Util.java index fcbdd9041..93c5c97f6 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/Util.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/Util.java @@ -57,7 +57,8 @@ import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR; public class Util { private final static int NAMESPACE_SEPARATOR_LENGTH = NAMESPACE_SEPARATOR.length(); - public final static String ILLEGAL_MBEANSERVER_NAME_CHARS=";:*?"; + public final static char[] ILLEGAL_MBEANSERVER_NAME_CHARS=";:*?". + toCharArray(); static Map newMap() { @@ -621,7 +622,7 @@ public class Util { * is {@code null}. * @throws IllegalArgumentException if mbeanServerName contains illegal * characters, or is empty, or is {@code "-"}. - * Illegal characters are {@value #ILLEGAL_MBEANSERVER_NAME_CHARS}. + * Illegal characters are {@link #ILLEGAL_MBEANSERVER_NAME_CHARS}. */ public static String checkServerName(String mbeanServerName) { if ("".equals(mbeanServerName)) @@ -632,7 +633,7 @@ public class Util { "\"-\" is not a valid MBean server name"); if (isMBeanServerNameUndefined(mbeanServerName)) return MBeanServerFactory.DEFAULT_MBEANSERVER_NAME; - for (char c : ILLEGAL_MBEANSERVER_NAME_CHARS.toCharArray()) { + for (char c : ILLEGAL_MBEANSERVER_NAME_CHARS) { if (mbeanServerName.indexOf(c) >= 0) throw new IllegalArgumentException( "invalid character in MBeanServer name: "+c); @@ -662,15 +663,15 @@ public class Util { } // Log the exception and its causes without logging the stack trace. - // Use with care - it is usally preferable to log the whole stack trace! + // Use with care - it is usually preferable to log the whole stack trace! // We don't want to log the whole stack trace here: logshort() is // called in those cases where the exception might not be abnormal. private static void logshort(String msg, Throwable t) { if (JmxProperties.MISC_LOGGER.isLoggable(Level.FINE)) { StringBuilder toprint = new StringBuilder(msg); - toprint.append("\nCaused By: ").append(String.valueOf(t)); - while ((t=t.getCause())!=null) - toprint.append("\nCaused By: ").append(String.valueOf(t)); + do { + toprint.append("\nCaused By: ").append(String.valueOf(t)); + } while ((t=t.getCause())!=null); JmxProperties.MISC_LOGGER.fine(toprint.toString()); } } diff --git a/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java b/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java index d7619814d..84644d831 100644 --- a/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java +++ b/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java @@ -85,7 +85,7 @@ public class DomainInterceptor extends HandlerInterceptor { final ObjectName pattern; public PatternNotificationFilter(ObjectName pattern) { - this.pattern = pattern; + this.pattern = pattern==null?ObjectName.WILDCARD:pattern; } public boolean isNotificationEnabled(Notification notification) { @@ -93,7 +93,7 @@ public class DomainInterceptor extends HandlerInterceptor { return false; final MBeanServerNotification mbsn = (MBeanServerNotification) notification; - if (pattern == null || pattern.apply(mbsn.getMBeanName())) + if (pattern.apply(mbsn.getMBeanName())) return true; return false; } @@ -110,6 +110,7 @@ public class DomainInterceptor extends HandlerInterceptor { super(handler); this.domainName = domainName; this.serverName = serverName; + ALL = Util.newObjectName(domainName+":*"); } @Override @@ -118,27 +119,27 @@ public class DomainInterceptor extends HandlerInterceptor { ", domain="+this.domainName+")"; } - public void connectDelegate(final MBeanServerDelegate delegate) + final void connectDelegate(final MBeanServerDelegate delegate) throws InstanceNotFoundException { final NotificationFilter filter = new PatternNotificationFilter(getPatternFor(null)); synchronized (this) { - if (mbsListener == null) + if (mbsListener == null) { mbsListener = new NotificationListener() { - - public void handleNotification(Notification notification, - Object handback) { - if (filter.isNotificationEnabled(notification)) - delegate.sendNotification(notification); - } - }; + public void handleNotification(Notification notification, + Object handback) { + if (filter.isNotificationEnabled(notification)) + delegate.sendNotification(notification); + } + }; + } } - getNamespace(). + getHandlerInterceptorMBean(). addMBeanServerNotificationListener(mbsListener, filter); } - public void disconnectDelegate() + final void disconnectDelegate() throws InstanceNotFoundException, ListenerNotFoundException { final NotificationListener l; synchronized (this) { @@ -146,10 +147,10 @@ public class DomainInterceptor extends HandlerInterceptor { if (l == null) return; mbsListener = null; } - getNamespace().removeMBeanServerNotificationListener(l); + getHandlerInterceptorMBean().removeMBeanServerNotificationListener(l); } - public void addPostRegisterTask(Queue queue, + public final void addPostRegisterTask(Queue queue, final MBeanServerDelegate delegate) { if (queue == null) throw new IllegalArgumentException("task queue must not be null"); @@ -158,14 +159,15 @@ public class DomainInterceptor extends HandlerInterceptor { try { connectDelegate(delegate); } catch (Exception x) { - throw new UnsupportedOperationException("notification forwarding",x); + throw new UnsupportedOperationException( + "notification forwarding",x); } } }; queue.add(task1); } - public void addPostDeregisterTask(Queue queue, + public final void addPostDeregisterTask(Queue queue, final MBeanServerDelegate delegate) { if (queue == null) throw new IllegalArgumentException("task queue must not be null"); @@ -174,17 +176,18 @@ public class DomainInterceptor extends HandlerInterceptor { try { disconnectDelegate(); } catch (Exception x) { - throw new UnsupportedOperationException("notification forwarding",x); + throw new UnsupportedOperationException( + "notification forwarding",x); } } }; queue.add(task1); } - /** - * Throws IllegalArgumentException if targetName.getDomain() is not - * in the domain handled. - **/ + // No name conversion for JMXDomains... + // Throws IllegalArgumentException if targetName.getDomain() is not + // in the domain handled. + // @Override protected ObjectName toSource(ObjectName targetName) { if (targetName == null) return null; @@ -198,6 +201,7 @@ public class DomainInterceptor extends HandlerInterceptor { return targetName; } + // No name conversion for JMXDomains... @Override protected ObjectName toTarget(ObjectName sourceName) { return sourceName; @@ -255,16 +259,16 @@ public class DomainInterceptor extends HandlerInterceptor { if (LOG.isLoggable(Level.FINE)) LOG.fine("Unexpected exception raised in queryNames: "+x); LOG.log(Level.FINEST,"Unexpected exception raised in queryNames",x); + return Collections.emptySet(); } - // We reach here only when an exception was raised. - // - final Set empty = Collections.emptySet(); - return empty; } + // Compute a new pattern which is a sub pattern of 'name' but only selects + // the MBeans in domain 'domainName' + // When we reach here, it has been verified that 'name' matches our domain + // name (done by DomainDispatchInterceptor) private ObjectName getPatternFor(final ObjectName name) { try { - if (ALL == null) ALL = ObjectName.getInstance(domainName + ":*"); if (name == null) return ALL; if (name.getDomain().equals(domainName)) return name; return name.withDomain(domainName); @@ -284,11 +288,8 @@ public class DomainInterceptor extends HandlerInterceptor { if (LOG.isLoggable(Level.FINE)) LOG.fine("Unexpected exception raised in queryNames: "+x); LOG.log(Level.FINEST,"Unexpected exception raised in queryNames",x); + return Collections.emptySet(); } - // We reach here only when an exception was raised. - // - final Set empty = Collections.emptySet(); - return empty; } @Override @@ -306,7 +307,7 @@ public class DomainInterceptor extends HandlerInterceptor { // in the domain. @Override public Integer getMBeanCount() { - return getNamespace().getMBeanCount(); + return getHandlerInterceptorMBean().getMBeanCount(); } private boolean checkOn() { @@ -320,8 +321,8 @@ public class DomainInterceptor extends HandlerInterceptor { @Override void check(ObjectName routingName, String member, String action) { if (!checkOn()) return; - final String act = (action==null)?"-":action.intern(); - if(act == "queryMBeans" || act == "queryNames") { // ES: OK + final String act = (action==null)?"-":action; + if("queryMBeans".equals(act) || "queryNames".equals(act)) { // This is tricky. check with 3 parameters is called // by queryNames/queryMBeans before performing the query. // At this point we must check with no class name. @@ -355,16 +356,8 @@ public class DomainInterceptor extends HandlerInterceptor { if (!checkOn()) return; final MBeanPermission perm; - // action is most probably already an intern string. - // string literals are intern strings. - // we create a new intern string for 'action' - just to be on - // the safe side... - // We intern it in order to be able to use == rather than equals - // below, because if we don't, and if action is not one of the - // 4 literals below, we would have to do a full string comparison. - // - final String act = (action==null)?"-":action.intern(); - if (act == "getDomains") { // ES: OK + final String act = (action==null)?"-":action; + if ("getDomains".equals(act)) { // ES: OK perm = new MBeanPermission(serverName,"-",member, routingName,act); } else { @@ -381,7 +374,7 @@ public class DomainInterceptor extends HandlerInterceptor { String getClassName(ObjectName routingName) { if (routingName == null || routingName.isPattern()) return "-"; try { - return getNamespace().getSourceServer(). + return getHandlerInterceptorMBean().getSourceServer(). getObjectInstance(routingName).getClassName(); } catch (InstanceNotFoundException ex) { LOG.finest("Can't get class name for "+routingName+ diff --git a/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java b/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java index eb48ef849..7c2f39348 100644 --- a/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java +++ b/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java @@ -63,8 +63,8 @@ import javax.management.namespace.JMXNamespace; /** * This interceptor wraps a JMXNamespace, and performs * {@code ObjectName} rewriting. {@code HandlerInterceptor} are - * usually created and managed by a {@link NamespaceDispatcher} or - * {@link DomainDispatcher}. + * created and managed by a {@link NamespaceDispatchInterceptor} or a + * {@link DomainDispatchInterceptor}. *

      * This API is a Sun internal API and is subject to changes without notice. *

      @@ -90,6 +90,12 @@ public abstract class HandlerInterceptor this.handler = handler; } + // + // The {@code source} connection is a connection to the MBeanServer + // that contains the actual MBeans. + // In the case of cascading, that would be a connection to the sub + // agent. Practically, this is JMXNamespace.getSourceServer(); + // @Override protected MBeanServer source() { return handler.getSourceServer(); @@ -105,7 +111,9 @@ public abstract class HandlerInterceptor return source(); } - T getNamespace() { + // The namespace or domain handler - this either a JMXNamespace or a + // a JMXDomain + T getHandlerInterceptorMBean() { return handler; } @@ -122,7 +130,7 @@ public abstract class HandlerInterceptor Util.newRuntimeIOException(x)); } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public AttributeList getAttributes(ObjectName name, String[] attributes) throws InstanceNotFoundException, ReflectionException { @@ -172,7 +180,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void removeNotificationListener(ObjectName name, ObjectName listener) throws InstanceNotFoundException, ListenerNotFoundException { @@ -183,7 +191,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public String getDefaultDomain() { try { @@ -193,7 +201,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public String[] getDomains() { try { @@ -203,7 +211,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public Integer getMBeanCount() { try { @@ -213,7 +221,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void setAttribute(ObjectName name, Attribute attribute) throws InstanceNotFoundException, AttributeNotFoundException, @@ -226,7 +234,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public Set queryNames(ObjectName name, QueryExp query) { try { @@ -236,7 +244,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public Set queryMBeans(ObjectName name, QueryExp query) { try { @@ -246,7 +254,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public boolean isInstanceOf(ObjectName name, String className) throws InstanceNotFoundException { @@ -257,7 +265,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public ObjectInstance createMBean(String className, ObjectName name) throws ReflectionException, InstanceAlreadyExistsException, @@ -270,7 +278,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName) @@ -284,7 +292,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public Object getAttribute(ObjectName name, String attribute) throws MBeanException, AttributeNotFoundException, @@ -296,7 +304,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void removeNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, Object handback) @@ -309,7 +317,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void removeNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, @@ -323,7 +331,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void removeNotificationListener(ObjectName name, NotificationListener listener) @@ -336,7 +344,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void addNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, @@ -349,7 +357,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void addNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, Object handback) @@ -362,7 +370,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public boolean isRegistered(ObjectName name) { try { @@ -372,7 +380,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public void unregisterMBean(ObjectName name) throws InstanceNotFoundException, MBeanRegistrationException { @@ -383,7 +391,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public MBeanInfo getMBeanInfo(ObjectName name) throws InstanceNotFoundException, IntrospectionException, @@ -395,7 +403,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public ObjectInstance getObjectInstance(ObjectName name) throws InstanceNotFoundException { @@ -406,7 +414,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public ObjectInstance createMBean(String className, ObjectName name, Object[] params, String[] signature) @@ -421,7 +429,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, Object[] params, String[] signature) @@ -437,7 +445,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public AttributeList setAttributes(ObjectName name,AttributeList attributes) throws InstanceNotFoundException, ReflectionException { @@ -448,7 +456,7 @@ public abstract class HandlerInterceptor } } - // From MBeanServer: catch & handles IOException + // From MBeanServerConnection: catch & handles IOException @Override public Object invoke(ObjectName name, String operationName, Object[] params, String[] signature) diff --git a/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java b/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java index 1fccfcbc7..49f875144 100644 --- a/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java +++ b/src/share/classes/com/sun/jmx/namespace/JMXNamespaceUtils.java @@ -29,7 +29,6 @@ import com.sun.jmx.defaults.JmxProperties; import java.io.IOException; import java.util.Collections; -import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; import java.util.logging.Level; @@ -40,6 +39,8 @@ import javax.management.MBeanServerConnection; import javax.management.NotificationFilter; import javax.management.NotificationListener; import javax.management.event.EventClient; +import javax.management.event.EventClientDelegateMBean; +import javax.management.namespace.JMXNamespace; import javax.management.namespace.JMXNamespaces; import javax.management.remote.JMXAddressable; import javax.management.remote.JMXConnector; @@ -66,26 +67,10 @@ public final class JMXNamespaceUtils { return new WeakHashMap(); } - /** Creates a new instance of JMXNamespaces */ + /** There are no instances of this class */ private JMXNamespaceUtils() { } - /** - * Returns an unmodifiable option map in which the given keys have been - * filtered out. - * @param keys keys to filter out from the map. - * @return An unmodifiable option map in which the given keys have been - * filtered out. - */ - public static Map filterMap(Map map, K... keys) { - final Map filtered; - filtered=new HashMap(map); - for (K key : keys) { - filtered.remove(key); - } - return unmodifiableMap(filtered); - } - // returns un unmodifiable view of a map. public static Map unmodifiableMap(Map aMap) { if (aMap == null || aMap.isEmpty()) diff --git a/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java b/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java index da85fc6d4..11afeb2af 100644 --- a/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java +++ b/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java @@ -54,10 +54,6 @@ import javax.management.namespace.JMXNamespacePermission; */ public class NamespaceInterceptor extends HandlerInterceptor { - /** - * A logger for this class. - **/ - private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; private static final Logger PROBE_LOG = Logger.getLogger( JmxProperties.NAMESPACE_LOGGER+".probe"); diff --git a/src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java b/src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java index 787343eec..443c80f2a 100644 --- a/src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java +++ b/src/share/classes/com/sun/jmx/namespace/RoutingConnectionProxy.java @@ -45,6 +45,9 @@ import javax.management.namespace.JMXNamespaces; *

      * @since 1.7 */ +// See class hierarchy and detailled explanations in RoutingProxy in this +// package. +// public class RoutingConnectionProxy extends RoutingProxy { @@ -93,40 +96,28 @@ public class RoutingConnectionProxy targetNs+"\", "+forwardsContext+")"; } + static final RoutingProxyFactory + + FACTORY = new RoutingProxyFactory + () { + + public RoutingConnectionProxy newInstance(MBeanServerConnection source, + String sourcePath, String targetPath, + boolean forwardsContext) { + return new RoutingConnectionProxy(source,sourcePath, + targetPath,forwardsContext); + } + + public RoutingConnectionProxy newInstance( + MBeanServerConnection source, String sourcePath) { + return new RoutingConnectionProxy(source,sourcePath); + } + }; + public static MBeanServerConnection cd(MBeanServerConnection source, String sourcePath) { - if (source == null) throw new IllegalArgumentException("null"); - if (source.getClass().equals(RoutingConnectionProxy.class)) { - // cast is OK here, but findbugs complains unless we use class.cast - final RoutingConnectionProxy other = - RoutingConnectionProxy.class.cast(source); - final String target = other.getTargetNamespace(); - - // Avoid multiple layers of serialization. - // - // We construct a new proxy from the original source instead of - // stacking a new proxy on top of the old one. - // - that is we replace - // cd ( cd ( x, dir1), dir2); - // by - // cd (x, dir1//dir2); - // - // We can do this only when the source class is exactly - // NamespaceConnectionProxy. - // - if (target == null || target.equals("")) { - final String path = - JMXNamespaces.concat(other.getSourceNamespace(), - sourcePath); - return new RoutingConnectionProxy(other.source(),path,"", - other.forwardsContext); - } - // Note: we could do possibly something here - but it would involve - // removing part of targetDir, and possibly adding - // something to sourcePath. - // Too complex to bother! => simply default to stacking... - } - return new RoutingConnectionProxy(source,sourcePath); + return RoutingProxy.cd(RoutingConnectionProxy.class, FACTORY, + source, sourcePath); } } diff --git a/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java b/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java index 904e5848e..70df9b504 100644 --- a/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java +++ b/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java @@ -83,18 +83,32 @@ public abstract class RoutingMBeanServerConnection//}. - * @param attributes The list of attributes to check permission for. - * @param action one of "getAttribute" or "setAttribute" - * @return The list of attributes for which the callers has the - * appropriate {@link - * javax.management.namespace.JMXNamespacePermission}. - */ - String[] checkAttributes(ObjectName routingName, - String[] attributes, String action) { - check(routingName,null,action); - return attributes; - } - - /** - * This method is a hook to implement permission checking in subclasses. - * By default, this method does nothing and simply returns - * {@code attribute}. - * - * @param routingName The name of the MBean in the enclosing context. - * This is of the form {@code //}. - * @param attributes The list of attributes to check permission for. - * @param action one of "getAttribute" or "setAttribute" - * @return The list of attributes for which the callers has the - * appropriate {@link - * javax.management.namespace.JMXNamespacePermission}. - */ - AttributeList checkAttributes(ObjectName routingName, - AttributeList attributes, String action) { - check(routingName,null,action); - return attributes; - } - // from MBeanServerConnection public AttributeList getAttributes(ObjectName name, String[] attributes) throws InstanceNotFoundException, ReflectionException, IOException { @@ -195,37 +171,6 @@ public abstract class RoutingMBeanServerConnection//}. - * @param member The {@link - * javax.management.namespace.JMXNamespacePermission#getMember member} - * name. - * @param action The {@link - * javax.management.namespace.JMXNamespacePermission#getActions action} - * name. - */ - void check(ObjectName routingName, - String member, String action) { - } - - void checkPattern(ObjectName routingPattern, - String member, String action) { - // pattern is checked only at posteriori by checkQuery. - // checking it a priori usually doesn't work, because ObjectName.apply - // does not work between two patterns. - check(null,null,action); - } - - void checkCreate(ObjectName routingName, String className, - String action) { - } - // from MBeanServerConnection public Object invoke(ObjectName name, String operationName, Object[] params, String[] signature) @@ -446,24 +391,6 @@ public abstract class RoutingMBeanServerConnection//}. - * @param action one of "queryNames" or "queryMBeans" - * @return true if {@code sourceName} can be returned. - */ - boolean checkQuery(ObjectName routingName, String action) { - return true; - } // Return names in the target's context. ObjectInstance processOutputInstance(ObjectInstance source) { @@ -643,6 +570,111 @@ public abstract class RoutingMBeanServerConnection//}. + * @param attributes The list of attributes to check permission for. + * @param action one of "getAttribute" or "setAttribute" + * @return The list of attributes for which the callers has the + * appropriate {@link + * javax.management.namespace.JMXNamespacePermission}. + */ + String[] checkAttributes(ObjectName routingName, + String[] attributes, String action) { + check(routingName,null,action); + return attributes; + } + + /** + * This method is a hook to implement permission checking in subclasses. + * By default, this method does nothing and simply returns + * {@code attribute}. + * + * @param routingName The name of the MBean in the enclosing context. + * This is of the form {@code //}. + * @param attributes The list of attributes to check permission for. + * @param action one of "getAttribute" or "setAttribute" + * @return The list of attributes for which the callers has the + * appropriate {@link + * javax.management.namespace.JMXNamespacePermission}. + */ + AttributeList checkAttributes(ObjectName routingName, + AttributeList attributes, String action) { + check(routingName,null,action); + return attributes; + } + + /** + * This method is a hook to implement permission checking in subclasses. + * By default, this method does nothing. + * A subclass may override this method and throw a {@link + * SecurityException} if the permission is denied. + * + * @param routingName The name of the MBean in the enclosing context. + * This is of the form {@code //}. + * @param member The {@link + * javax.management.namespace.JMXNamespacePermission#getMember member} + * name. + * @param action The {@link + * javax.management.namespace.JMXNamespacePermission#getActions action} + * name. + */ + void check(ObjectName routingName, + String member, String action) { + } + + // called in createMBean and registerMBean + void checkCreate(ObjectName routingName, String className, + String action) { + } + + // A priori check for queryNames/queryMBeans/ + void checkPattern(ObjectName routingPattern, + String member, String action) { + // pattern is checked only at posteriori by checkQuery. + // checking it a priori usually doesn't work, because ObjectName.apply + // does not work between two patterns. + // We only check that we have the permission requested for 'action'. + check(null,null,action); + } + + + /** + * This is a hook to implement permission checking in subclasses. + * + * Checks that the caller has sufficient permission for returning + * information about {@code sourceName} in {@code action}. + * + * By default always return true. Subclass may override this method + * and return false if the caller doesn't have sufficient permissions. + * + * @param routingName The name of the MBean to include or exclude from + * the query, expressed in the enclosing context. + * This is of the form {@code //}. + * @param action one of "queryNames" or "queryMBeans" + * @return true if {@code sourceName} can be returned. + */ + boolean checkQuery(ObjectName routingName, String action) { + return true; + } + /** * This method is a hook to implement permission checking in subclasses. * Checks that the caller as the necessary permissions to view the @@ -658,14 +690,4 @@ public abstract class RoutingMBeanServerConnection{@link RoutingConnectionProxy}: to cd in an MBeanServerConnection.

      - *

      {@link RoutingServerProxy}: to cd in an MBeanServer.

      + *

      {@link RoutingConnectionProxy}: to narrow down into an + * MBeanServerConnection.

      + *

      {@link RoutingServerProxy}: to narrow down into an MBeanServer.

      *

      * This API is a Sun internal API and is subject to changes without notice. *

      * @since 1.7 */ +// +// RoutingProxies are client side objects which are used to narrow down +// into a namespace. They are used to perform ObjectName translation, +// adding the namespace to the routing ObjectName before sending it over +// to the source connection, and removing that prefix from results of +// queries, createMBean, registerMBean, and getObjectInstance. +// This translation is the opposite to that which is performed by +// NamespaceInterceptors. +// +// There is however a special case where routing proxies are used on the +// 'server' side to remove a namespace - rather than to add it: +// This the case of ClientContext. +// When an ObjectName like "jmx.context//c1=v1,c2=v2//D:k=v" reaches the +// jmx.context namespace, a routing proxy is used to remove the prefix +// c1=v1,c2=v2// from the routing objectname. +// +// For a RoutingProxy used in a narrowDownToNamespace operation, we have: +// targetNs="" // targetNS is the namespace 'to remove' +// sourceNS= // namespace 'to add' +// +// For a RoutingProxy used in a ClientContext operation, we have: +// targetNs= // context must be removed from object name +// sourceNs="" // nothing to add... +// +// RoutingProxies can also be used on the client side to implement +// "withClientContext" operations. In that case, the boolean parameter +// 'forwards context' is set to true, targetNs is "", and sourceNS may +// also be "". When forwardsContext is true, the RoutingProxy dynamically +// creates an ObjectNameRouter for each operation - in order to dynamically add +// the context attached to the thread to the routing ObjectName. This is +// performed in the getObjectNameRouter() method. +// +// Finally, in order to avoid too many layers of wrapping, +// RoutingConnectionProxy and RoutingServerProxy can be created through a +// factory method that can concatenate namespace pathes in order to +// return a single RoutingProxy - rather than wrapping a RoutingProxy inside +// another RoutingProxy. See RoutingConnectionProxy.cd and +// RoutingServerProxy.cd +// +// The class hierarchy is as follows: +// +// RoutingMBeanServerConnection +// [abstract class for all routing interceptors, +// such as RoutingProxies and HandlerInterceptors] +// / \ +// / \ +// RoutingProxy HandlerInterceptor +// [base class for [base class for server side +// client-side objects used objects, created by +// in narrowDownTo] DispatchInterceptors] +// / \ | \ +// RoutingConnectionProxy \ | NamespaceInterceptor +// [wraps MBeanServerConnection \ | [used to remove +// objects] \ | namespace prefix and +// RoutingServerProxy | wrap JMXNamespace] +// [wraps MBeanServer | +// Objects] | +// DomainInterceptor +// [used to wrap JMXDomain] +// +// RoutingProxies also differ from HandlerInterceptors in that they transform +// calls to MBeanServerConnection operations that do not have any parameters +// into a call to the underlying JMXNamespace MBean. +// So for instance a call to: +// JMXNamespaces.narrowDownToNamespace(conn,"foo").getDomains() +// is transformed into +// conn.getAttribute("foo//type=JMXNamespace","Domains"); +// public abstract class RoutingProxy extends RoutingMBeanServerConnection { @@ -179,17 +245,11 @@ public abstract class RoutingProxy throw x; } catch (MBeanException ex) { throw new IOException("Failed to get "+attributeName+": "+ - ex.getMessage(), + ex, ex.getTargetException()); - } catch (AttributeNotFoundException ex) { + } catch (Exception ex) { throw new IOException("Failed to get "+attributeName+": "+ - ex.getMessage(),ex); - } catch (InstanceNotFoundException ex) { - throw new IOException("Failed to get "+attributeName+": "+ - ex.getMessage(),ex); - } catch (ReflectionException ex) { - throw new IOException("Failed to get "+attributeName+": "+ - ex.getMessage(),ex); + ex,ex); } } @@ -279,4 +339,62 @@ public abstract class RoutingProxy (" mounted on targetNs="+targetNs)); } + // Creates an instance of a subclass 'R' of RoutingProxy + // RoutingServerProxy and RoutingConnectionProxy have their own factory + // instance. + static interface RoutingProxyFactory> { + R newInstance(T source, + String sourcePath, String targetPath, + boolean forwardsContext); + R newInstance(T source, + String sourcePath); + } + + // Performs a narrowDownToNamespace operation. + // This method will attempt to merge two RoutingProxies in a single + // one if they are of the same class. + // + // This method is never called directly - it should be called only by + // subclasses of RoutingProxy. + // + // As for now it is called by: + // RoutingServerProxy.cd and RoutingConnectionProxy.cd. + // + static > + R cd(Class routingProxyClass, + RoutingProxyFactory factory, + T source, String sourcePath) { + if (source == null) throw new IllegalArgumentException("null"); + if (source.getClass().equals(routingProxyClass)) { + // cast is OK here, but findbugs complains unless we use class.cast + final R other = routingProxyClass.cast(source); + final String target = other.getTargetNamespace(); + + // Avoid multiple layers of serialization. + // + // We construct a new proxy from the original source instead of + // stacking a new proxy on top of the old one. + // - that is we replace + // cd ( cd ( x, dir1), dir2); + // by + // cd (x, dir1//dir2); + // + // We can do this only when the source class is exactly + // RoutingServerProxy. + // + if (target == null || target.equals("")) { + final String path = + JMXNamespaces.concat(other.getSourceNamespace(), + sourcePath); + return factory.newInstance(other.source(),path,"", + other.forwardsContext); + } + // Note: we could do possibly something here - but it would involve + // removing part of targetDir, and possibly adding + // something to sourcePath. + // Too complex to bother! => simply default to stacking... + } + return factory.newInstance(source,sourcePath); + } } diff --git a/src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java b/src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java index 94aa139dd..f58e39816 100644 --- a/src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java +++ b/src/share/classes/com/sun/jmx/namespace/RoutingServerProxy.java @@ -69,6 +69,9 @@ import javax.management.namespace.JMXNamespaces; * * @since 1.7 */ +// See class hierarchy and detailled explanations in RoutingProxy in this +// package. +// public class RoutingServerProxy extends RoutingProxy implements MBeanServer { @@ -564,39 +567,24 @@ public class RoutingServerProxy } } + static final RoutingProxyFactory + FACTORY = new RoutingProxyFactory() { + + public RoutingServerProxy newInstance(MBeanServer source, + String sourcePath, String targetPath, + boolean forwardsContext) { + return new RoutingServerProxy(source,sourcePath, + targetPath,forwardsContext); + } + + public RoutingServerProxy newInstance( + MBeanServer source, String sourcePath) { + return new RoutingServerProxy(source,sourcePath); + } + }; public static MBeanServer cd(MBeanServer source, String sourcePath) { - if (source == null) throw new IllegalArgumentException("null"); - if (source.getClass().equals(RoutingServerProxy.class)) { - // cast is OK here, but findbugs complains unless we use class.cast - final RoutingServerProxy other = - RoutingServerProxy.class.cast(source); - final String target = other.getTargetNamespace(); - - // Avoid multiple layers of serialization. - // - // We construct a new proxy from the original source instead of - // stacking a new proxy on top of the old one. - // - that is we replace - // cd ( cd ( x, dir1), dir2); - // by - // cd (x, dir1//dir2); - // - // We can do this only when the source class is exactly - // NamespaceServerProxy. - // - if (target == null || target.equals("")) { - final String path = - JMXNamespaces.concat(other.getSourceNamespace(), - sourcePath); - return new RoutingServerProxy(other.source(),path,"", - other.forwardsContext); - } - // Note: we could do possibly something here - but it would involve - // removing part of targetDir, and possibly adding - // something to sourcePath. - // Too complex to bother! => simply default to stacking... - } - return new RoutingServerProxy(source,sourcePath); + return RoutingProxy.cd(RoutingServerProxy.class, FACTORY, + source, sourcePath); } } diff --git a/src/share/classes/javax/management/namespace/JMXDomain.java b/src/share/classes/javax/management/namespace/JMXDomain.java index a54fde7f3..bff3c1370 100644 --- a/src/share/classes/javax/management/namespace/JMXDomain.java +++ b/src/share/classes/javax/management/namespace/JMXDomain.java @@ -308,17 +308,17 @@ public class JMXDomain extends JMXNamespace { * It is however only available for subclasses in this package. **/ @Override - ObjectName validateHandlerName(ObjectName supliedName) { - if (supliedName == null) + ObjectName validateHandlerName(ObjectName suppliedName) { + if (suppliedName == null) throw new IllegalArgumentException("Must supply a valid name"); final String dirName = JMXNamespaces. - normalizeNamespaceName(supliedName.getDomain()); + normalizeNamespaceName(suppliedName.getDomain()); final ObjectName handlerName = getDomainObjectName(dirName); - if (!supliedName.equals(handlerName)) + if (!suppliedName.equals(handlerName)) throw new IllegalArgumentException("invalid name space name: "+ - supliedName); + suppliedName); - return supliedName; + return suppliedName; } /** diff --git a/src/share/classes/javax/management/namespace/JMXNamespace.java b/src/share/classes/javax/management/namespace/JMXNamespace.java index 39cb11b02..23f3004eb 100644 --- a/src/share/classes/javax/management/namespace/JMXNamespace.java +++ b/src/share/classes/javax/management/namespace/JMXNamespace.java @@ -482,8 +482,8 @@ public class JMXNamespace /** * This method is part of the {@link MBeanRegistration} interface. * The {@link JMXNamespace} class uses the {@link MBeanRegistration} - * interface in order to get a handle to the MBean server in which it is - * registered. It also check the validity of its own ObjectName. + * interface in order to get a reference to the MBean server in which it is + * registered. It also checks the validity of its own ObjectName. *

      * This method is called by the MBean server. * Application classes should never call this method directly. @@ -502,11 +502,14 @@ public class JMXNamespace */ public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { - if (objectName != null && ! objectName.equals(name)) - throw new IllegalStateException( + // need to synchronize to protect against multiple registration. + synchronized(this) { + if (objectName != null && ! objectName.equals(name)) + throw new IllegalStateException( "Already registered under another name: " + objectName); - objectName = validateHandlerName(name); - mbeanServer = server; + objectName = validateHandlerName(name); + mbeanServer = server; + } return name; } @@ -517,23 +520,23 @@ public class JMXNamespace * reuse JMXNamespace in order to implement sessions... * It is however only available for subclasses in this package. **/ - ObjectName validateHandlerName(ObjectName supliedName) { - if (supliedName == null) + ObjectName validateHandlerName(ObjectName suppliedName) { + if (suppliedName == null) throw new IllegalArgumentException("Must supply a valid name"); final String dirName = JMXNamespaces. - normalizeNamespaceName(supliedName.getDomain()); + normalizeNamespaceName(suppliedName.getDomain()); final ObjectName handlerName = JMXNamespaces.getNamespaceObjectName(dirName); - if (!supliedName.equals(handlerName)) + if (!suppliedName.equals(handlerName)) throw new IllegalArgumentException("invalid name space name: "+ - supliedName); - return supliedName; + suppliedName); + return suppliedName; } /** * This method is part of the {@link MBeanRegistration} interface. * The {@link JMXNamespace} class uses the {@link MBeanRegistration} - * interface in order to get a handle to the MBean server in which it is + * interface in order to get a reference to the MBean server in which it is * registered. *

      * This method is called by the MBean server. Application classes should @@ -549,7 +552,7 @@ public class JMXNamespace /** * This method is part of the {@link MBeanRegistration} interface. * The {@link JMXNamespace} class uses the {@link MBeanRegistration} - * interface in order to get a handle to the MBean server in which it is + * interface in order to get a reference to the MBean server in which it is * registered. *

      * This method is called by the MBean server. Application classes should @@ -573,8 +576,11 @@ public class JMXNamespace * @see MBeanRegistration#postDeregister MBeanRegistration */ public void postDeregister() { - mbeanServer = null; - objectName = null; + // need to synchronize to protect against multiple registration. + synchronized(this) { + mbeanServer = null; + objectName = null; + } } diff --git a/src/share/classes/javax/management/namespace/JMXNamespaces.java b/src/share/classes/javax/management/namespace/JMXNamespaces.java index b3eafd28a..429a9d466 100644 --- a/src/share/classes/javax/management/namespace/JMXNamespaces.java +++ b/src/share/classes/javax/management/namespace/JMXNamespaces.java @@ -266,11 +266,15 @@ public class JMXNamespaces { ObjectNameRouter.normalizeNamespacePath(namespace,false, true,false); try { + // We could use Util.newObjectName here - but throwing an + // IllegalArgumentException that contains just the supplied + // namespace instead of the whole ObjectName seems preferable. return ObjectName.getInstance(sourcePath+ NAMESPACE_SEPARATOR+":"+ JMXNamespace.TYPE_ASSIGNMENT); } catch (MalformedObjectNameException x) { - throw new IllegalArgumentException(namespace,x); + throw new IllegalArgumentException("Invalid namespace: " + + namespace,x); } } diff --git a/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java b/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java index 1639fd2fc..1e877e0ce 100644 --- a/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java +++ b/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.security.AccessControlException; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; import java.util.logging.Logger; @@ -125,10 +126,8 @@ public class JMXRemoteNamespace // the underlying JMXConnector. It is used in particular to maintain the // "connected" state in this MBean. // - private static class ConnectionListener implements NotificationListener { - private final JMXRemoteNamespace handler; - private ConnectionListener(JMXRemoteNamespace handler) { - this.handler = handler; + private class ConnectionListener implements NotificationListener { + private ConnectionListener() { } public void handleNotification(Notification notification, Object handback) { @@ -136,7 +135,11 @@ public class JMXRemoteNamespace return; final JMXConnectionNotification cn = (JMXConnectionNotification)notification; - handler.checkState(this,cn,(JMXConnector)handback); + final String type = cn.getType(); + if (JMXConnectionNotification.CLOSED.equals(type) + || JMXConnectionNotification.FAILED.equals(type)) { + checkState(this,cn,(JMXConnector)handback); + } } } @@ -188,7 +191,7 @@ public class JMXRemoteNamespace "Connected", "Emitted when the Connected state of this object changes"); - private static long seqNumber=0; + private static AtomicLong seqNumber = new AtomicLong(0); private final NotificationBroadcasterSupport broadcaster; private final ConnectionListener listener; @@ -237,7 +240,7 @@ public class JMXRemoteNamespace this.optionsMap = JMXNamespaceUtils.unmodifiableMap(optionsMap); // handles (dis)connection events - this.listener = new ConnectionListener(this); + this.listener = new ConnectionListener(); // XXX TODO: remove the probe, or simplify it. this.probed = false; @@ -313,8 +316,8 @@ public class JMXRemoteNamespace broadcaster.removeNotificationListener(listener, filter, handback); } - private static synchronized long getNextSeqNumber() { - return seqNumber++; + private static long getNextSeqNumber() { + return seqNumber.getAndIncrement(); } @@ -362,14 +365,18 @@ public class JMXRemoteNamespace // lock while evaluating the true value of the connected state, // while anyone might also call close() or connect() from a // different thread. - // // The method switchConnection() (called from here too) also has the - // same kind of complex logic. + // same kind of complex logic: // // We use the JMXConnector has a handback to the notification listener // (emittingConnector) in order to be able to determine whether the // notification concerns the current connector in use, or an older - // one. + // one. The 'emittingConnector' is the connector from which the + // notification originated. This could be an 'old' connector - as + // closed() and connect() could already have been called before the + // notification arrived. So what we do is to compare the + // 'emittingConnector' with the current connector, to see if the + // notification actually comes from the curent connector. // boolean remove = false; @@ -486,14 +493,12 @@ public class JMXRemoteNamespace } } - private void closeall(JMXConnector... a) { - for (JMXConnector c : a) { - try { - if (c != null) c.close(); - } catch (Exception x) { - // OK: we're gonna throw the original exception later. - LOG.finest("Ignoring exception when closing connector: "+x); - } + private void close(JMXConnector c) { + try { + if (c != null) c.close(); + } catch (Exception x) { + // OK: we're gonna throw the original exception later. + LOG.finest("Ignoring exception when closing connector: "+x); } } @@ -640,10 +645,10 @@ public class JMXRemoteNamespace msc = aconn.getMBeanServerConnection(); aconn.addConnectionNotificationListener(listener,null,aconn); } catch (IOException io) { - closeall(aconn); + close(aconn); throw io; } catch (RuntimeException x) { - closeall(aconn); + close(aconn); throw x; } diff --git a/src/share/classes/javax/management/namespace/MBeanServerConnectionWrapper.java b/src/share/classes/javax/management/namespace/MBeanServerConnectionWrapper.java index f74785ffd..7eeda961e 100644 --- a/src/share/classes/javax/management/namespace/MBeanServerConnectionWrapper.java +++ b/src/share/classes/javax/management/namespace/MBeanServerConnectionWrapper.java @@ -28,7 +28,6 @@ package javax.management.namespace; import com.sun.jmx.mbeanserver.Util; import java.io.IOException; import java.io.ObjectInputStream; -import java.security.AccessController; import java.util.Set; import javax.management.Attribute; diff --git a/test/javax/management/namespace/Wombat.java b/test/javax/management/namespace/Wombat.java index 03dbd020e..bef648c0f 100644 --- a/test/javax/management/namespace/Wombat.java +++ b/test/javax/management/namespace/Wombat.java @@ -68,7 +68,12 @@ public class Wombat extends StandardMBean } public Wombat() throws NotCompliantMBeanException { - super(WombatMBean.class); + this(WombatMBean.class); + } + + public Wombat(Class clazz) + throws NotCompliantMBeanException { + super(clazz); final Random r = new Random(); seed = ((r.nextLong() % MAX_SEED) + MAX_SEED)%MAX_SEED; period = 200 + (((r.nextLong()%80)+80)%80)*10; -- GitLab From aaee3a0a6a6fad6921a8386e619bdf1ee34e1582 Mon Sep 17 00:00:00 2001 From: martin Date: Tue, 9 Sep 2008 15:20:07 -0700 Subject: [PATCH 102/139] 6728229: (str) StringBuilder.append(CharSequence) does not throw IndexOutOfBoundsException Summary: Major spec rework Reviewed-by: alanb --- .../java/lang/AbstractStringBuilder.java | 444 +++++++++--------- src/share/classes/java/lang/StringBuffer.java | 57 +-- .../classes/java/lang/StringBuilder.java | 56 +-- 3 files changed, 244 insertions(+), 313 deletions(-) diff --git a/src/share/classes/java/lang/AbstractStringBuilder.java b/src/share/classes/java/lang/AbstractStringBuilder.java index 4ba130b49..3ee4c21d0 100644 --- a/src/share/classes/java/lang/AbstractStringBuilder.java +++ b/src/share/classes/java/lang/AbstractStringBuilder.java @@ -42,7 +42,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ - char value[]; + char[] value; /** * The count is the number of characters used. @@ -333,8 +333,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * dst.length * */ - public void getChars(int srcBegin, int srcEnd, char dst[], - int dstBegin) + public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) { if (srcBegin < 0) throw new StringIndexOutOfBoundsException(srcBegin); @@ -366,14 +365,14 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the Object - * argument. + * Appends the string representation of the {@code Object} argument. *

      - * The argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then appended to this sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(Object)}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * - * @param obj an Object. + * @param obj an {@code Object}. * @return a reference to this object. */ public AbstractStringBuilder append(Object obj) { @@ -383,17 +382,17 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * Appends the specified string to this character sequence. *

      - * The characters of the String argument are appended, in + * The characters of the {@code String} argument are appended, in * order, increasing the length of this sequence by the length of the - * argument. If str is null, then the four - * characters "null" are appended. + * argument. If {@code str} is {@code null}, then the four + * characters {@code "null"} are appended. *

      * Let n be the length of this character sequence just prior to - * execution of the append method. Then the character at + * execution of the {@code append} method. Then the character at * index k in the new character sequence is equal to the character * at index k in the old character sequence, if k is less * than n; otherwise, it is equal to the character at index - * k-n in the argument str. + * k-n in the argument {@code str}. * * @param str a string. * @return a reference to this object. @@ -435,33 +434,33 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends a subsequence of the specified CharSequence to this + * Appends a subsequence of the specified {@code CharSequence} to this * sequence. *

      - * Characters of the argument s, starting at - * index start, are appended, in order, to the contents of - * this sequence up to the (exclusive) index end. The length - * of this sequence is increased by the value of end - start. + * Characters of the argument {@code s}, starting at + * index {@code start}, are appended, in order, to the contents of + * this sequence up to the (exclusive) index {@code end}. The length + * of this sequence is increased by the value of {@code end - start}. *

      * Let n be the length of this character sequence just prior to - * execution of the append method. Then the character at + * execution of the {@code append} method. Then the character at * index k in this character sequence becomes equal to the * character at index k in this sequence, if k is less than * n; otherwise, it is equal to the character at index - * k+start-n in the argument s. + * k+start-n in the argument {@code s}. *

      - * If s is null, then this method appends + * If {@code s} is {@code null}, then this method appends * characters as if the s parameter was a sequence containing the four - * characters "null". + * characters {@code "null"}. * * @param s the sequence to append. * @param start the starting index of the subsequence to be appended. * @param end the end index of the subsequence to be appended. * @return a reference to this object. * @throws IndexOutOfBoundsException if - * start or end are negative, or - * start is greater than end or - * end is greater than s.length() + * {@code start} is negative, or + * {@code start} is greater than {@code end} or + * {@code end} is greater than {@code s.length()} */ public AbstractStringBuilder append(CharSequence s, int start, int end) { if (s == null) @@ -483,22 +482,22 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the char array + * Appends the string representation of the {@code char} array * argument to this sequence. *

      * The characters of the array argument are appended, in order, to * the contents of this sequence. The length of this sequence * increases by the length of the argument. *

      - * The overall effect is exactly as if the argument were converted to - * a string by the method {@link String#valueOf(char[])} and the - * characters of that string were then {@link #append(String) appended} - * to this character sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(char[])}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * * @param str the characters to be appended. * @return a reference to this object. */ - public AbstractStringBuilder append(char str[]) { + public AbstractStringBuilder append(char[] str) { int newCount = count + str.length; if (newCount > value.length) expandCapacity(newCount); @@ -509,22 +508,25 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * Appends the string representation of a subarray of the - * char array argument to this sequence. + * {@code char} array argument to this sequence. *

      - * Characters of the char array str, starting at - * index offset, are appended, in order, to the contents + * Characters of the {@code char} array {@code str}, starting at + * index {@code offset}, are appended, in order, to the contents * of this sequence. The length of this sequence increases - * by the value of len. + * by the value of {@code len}. *

      - * The overall effect is exactly as if the arguments were converted to - * a string by the method {@link String#valueOf(char[],int,int)} and the - * characters of that string were then {@link #append(String) appended} - * to this character sequence. + * The overall effect is exactly as if the arguments were converted + * to a string by the method {@link String#valueOf(char[],int,int)}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * * @param str the characters to be appended. - * @param offset the index of the first char to append. - * @param len the number of chars to append. + * @param offset the index of the first {@code char} to append. + * @param len the number of {@code char}s to append. * @return a reference to this object. + * @throws IndexOutOfBoundsException + * if {@code offset < 0} or {@code len < 0} + * or {@code offset+len > str.length} */ public AbstractStringBuilder append(char str[], int offset, int len) { int newCount = count + len; @@ -536,14 +538,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the boolean + * Appends the string representation of the {@code boolean} * argument to the sequence. *

      - * The argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then appended to this sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(boolean)}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * - * @param b a boolean. + * @param b a {@code boolean}. * @return a reference to this object. */ public AbstractStringBuilder append(boolean b) { @@ -569,18 +572,18 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the char + * Appends the string representation of the {@code char} * argument to this sequence. *

      * The argument is appended to the contents of this sequence. - * The length of this sequence increases by 1. + * The length of this sequence increases by {@code 1}. *

      - * The overall effect is exactly as if the argument were converted to - * a string by the method {@link String#valueOf(char)} and the character - * in that string were then {@link #append(String) appended} to this - * character sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(char)}, + * and the character in that string were then + * {@link #append(String) appended} to this character sequence. * - * @param c a char. + * @param c a {@code char}. * @return a reference to this object. */ public AbstractStringBuilder append(char c) { @@ -592,14 +595,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the int + * Appends the string representation of the {@code int} * argument to this sequence. *

      - * The argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then appended to this sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(int)}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * - * @param i an int. + * @param i an {@code int}. * @return a reference to this object. */ public AbstractStringBuilder append(int i) { @@ -618,14 +622,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the long + * Appends the string representation of the {@code long} * argument to this sequence. *

      - * The argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then appended to this sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(long)}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * - * @param l a long. + * @param l a {@code long}. * @return a reference to this object. */ public AbstractStringBuilder append(long l) { @@ -644,14 +649,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the float + * Appends the string representation of the {@code float} * argument to this sequence. *

      - * The argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then appended to this string sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(float)}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * - * @param f a float. + * @param f a {@code float}. * @return a reference to this object. */ public AbstractStringBuilder append(float f) { @@ -660,14 +666,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the double + * Appends the string representation of the {@code double} * argument to this sequence. *

      - * The argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then appended to this sequence. + * The overall effect is exactly as if the argument were converted + * to a string by the method {@link String#valueOf(double)}, + * and the characters of that string were then + * {@link #append(String) appended} to this character sequence. * - * @param d a double. + * @param d a {@code double}. * @return a reference to this object. */ public AbstractStringBuilder append(double d) { @@ -677,17 +684,17 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * Removes the characters in a substring of this sequence. - * The substring begins at the specified start and extends to - * the character at index end - 1 or to the end of the + * The substring begins at the specified {@code start} and extends to + * the character at index {@code end - 1} or to the end of the * sequence if no such character exists. If - * start is equal to end, no changes are made. + * {@code start} is equal to {@code end}, no changes are made. * * @param start The beginning index, inclusive. * @param end The ending index, exclusive. * @return This object. - * @throws StringIndexOutOfBoundsException if start - * is negative, greater than length(), or - * greater than end. + * @throws StringIndexOutOfBoundsException if {@code start} + * is negative, greater than {@code length()}, or + * greater than {@code end}. */ public AbstractStringBuilder delete(int start, int end) { if (start < 0) @@ -705,7 +712,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Appends the string representation of the codePoint + * Appends the string representation of the {@code codePoint} * argument to this sequence. * *

      The argument is appended to the contents of this sequence. @@ -713,15 +720,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * {@link Character#charCount(int) Character.charCount(codePoint)}. * *

      The overall effect is exactly as if the argument were - * converted to a char array by the method {@link - * Character#toChars(int)} and the character in that array were - * then {@link #append(char[]) appended} to this character + * converted to a {@code char} array by the method + * {@link Character#toChars(int)} and the character in that array + * were then {@link #append(char[]) appended} to this character * sequence. * * @param codePoint a Unicode code point * @return a reference to this object. * @exception IllegalArgumentException if the specified - * codePoint isn't a valid Unicode code point + * {@code codePoint} isn't a valid Unicode code point */ public AbstractStringBuilder appendCodePoint(int codePoint) { if (!Character.isValidCodePoint(codePoint)) { @@ -879,27 +886,27 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of a subarray of the str + * Inserts the string representation of a subarray of the {@code str} * array argument into this sequence. The subarray begins at the - * specified offset and extends len chars. + * specified {@code offset} and extends {@code len} {@code char}s. * The characters of the subarray are inserted into this sequence at - * the position indicated by index. The length of this - * sequence increases by len chars. + * the position indicated by {@code index}. The length of this + * sequence increases by {@code len} {@code char}s. * * @param index position at which to insert subarray. - * @param str A char array. - * @param offset the index of the first char in subarray to + * @param str A {@code char} array. + * @param offset the index of the first {@code char} in subarray to * be inserted. - * @param len the number of chars in the subarray to + * @param len the number of {@code char}s in the subarray to * be inserted. * @return This object - * @throws StringIndexOutOfBoundsException if index - * is negative or greater than length(), or - * offset or len are negative, or - * (offset+len) is greater than - * str.length. + * @throws StringIndexOutOfBoundsException if {@code index} + * is negative or greater than {@code length()}, or + * {@code offset} or {@code len} are negative, or + * {@code (offset+len)} is greater than + * {@code str.length}. */ - public AbstractStringBuilder insert(int index, char str[], int offset, + public AbstractStringBuilder insert(int index, char[] str, int offset, int len) { if ((index < 0) || (index > length())) @@ -918,20 +925,21 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the Object + * Inserts the string representation of the {@code Object} * argument into this character sequence. *

      - * The second argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then inserted into this sequence at the indicated - * offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(Object)}, + * and the characters of that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. *

      - * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. - * @param obj an Object. + * @param obj an {@code Object}. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ @@ -942,28 +950,28 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * Inserts the string into this character sequence. *

      - * The characters of the String argument are inserted, in + * The characters of the {@code String} argument are inserted, in * order, into this sequence at the indicated offset, moving up any * characters originally above that position and increasing the length * of this sequence by the length of the argument. If - * str is null, then the four characters - * "null" are inserted into this sequence. + * {@code str} is {@code null}, then the four characters + * {@code "null"} are inserted into this sequence. *

      * The character at index k in the new character sequence is * equal to: *

        *
      • the character at index k in the old character sequence, if - * k is less than offset - *
      • the character at index k-offset in the - * argument str, if k is not less than - * offset but is less than offset+str.length() - *
      • the character at index k-str.length() in the + * k is less than {@code offset} + *
      • the character at index k{@code -offset} in the + * argument {@code str}, if k is not less than + * {@code offset} but is less than {@code offset+str.length()} + *
      • the character at index k{@code -str.length()} in the * old character sequence, if k is not less than - * offset+str.length() + * {@code offset+str.length()} *

      - * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. * @param str a string. @@ -986,27 +994,30 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the char array + * Inserts the string representation of the {@code char} array * argument into this sequence. *

      * The characters of the array argument are inserted into the * contents of this sequence at the position indicated by - * offset. The length of this sequence increases by + * {@code offset}. The length of this sequence increases by * the length of the argument. *

      - * The overall effect is exactly as if the argument were converted to - * a string by the method {@link String#valueOf(char[])} and the - * characters of that string were then - * {@link #insert(int,String) inserted} into this - * character sequence at the position indicated by - * offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(char[])}, + * and the characters of that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. + *

      + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. * @param str a character array. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ - public AbstractStringBuilder insert(int offset, char str[]) { + public AbstractStringBuilder insert(int offset, char[] str) { if ((offset < 0) || (offset > length())) throw new StringIndexOutOfBoundsException(offset); int len = str.length; @@ -1020,18 +1031,20 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the specified CharSequence into this sequence. + * Inserts the specified {@code CharSequence} into this sequence. *

      - * The characters of the CharSequence argument are inserted, + * The characters of the {@code CharSequence} argument are inserted, * in order, into this sequence at the indicated offset, moving up * any characters originally above that position and increasing the length * of this sequence by the length of the argument s. *

      * The result of this method is exactly the same as if it were an - * invocation of this object's insert(dstOffset, s, 0, s.length()) method. + * invocation of this object's + * {@link #insert(int,CharSequence,int,int) insert}(dstOffset, s, 0, s.length()) + * method. * - *

      If s is null, then the four characters - * "null" are inserted into this sequence. + *

      If {@code s} is {@code null}, then the four characters + * {@code "null"} are inserted into this sequence. * * @param dstOffset the offset. * @param s the sequence to be inserted @@ -1047,51 +1060,51 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts a subsequence of the specified CharSequence into + * Inserts a subsequence of the specified {@code CharSequence} into * this sequence. *

      - * The subsequence of the argument s specified by - * start and end are inserted, + * The subsequence of the argument {@code s} specified by + * {@code start} and {@code end} are inserted, * in order, into this sequence at the specified destination offset, moving * up any characters originally above that position. The length of this - * sequence is increased by end - start. + * sequence is increased by {@code end - start}. *

      * The character at index k in this sequence becomes equal to: *

        *
      • the character at index k in this sequence, if - * k is less than dstOffset - *
      • the character at index k+start-dstOffset in - * the argument s, if k is greater than or equal to - * dstOffset but is less than dstOffset+end-start - *
      • the character at index k-(end-start) in this + * k is less than {@code dstOffset} + *
      • the character at index k{@code +start-dstOffset} in + * the argument {@code s}, if k is greater than or equal to + * {@code dstOffset} but is less than {@code dstOffset+end-start} + *
      • the character at index k{@code -(end-start)} in this * sequence, if k is greater than or equal to - * dstOffset+end-start + * {@code dstOffset+end-start} *

      - * The dstOffset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code dstOffset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. *

      The start argument must be nonnegative, and not greater than - * end. + * {@code end}. *

      The end argument must be greater than or equal to - * start, and less than or equal to the length of s. + * {@code start}, and less than or equal to the length of s. * - *

      If s is null, then this method inserts + *

      If {@code s} is {@code null}, then this method inserts * characters as if the s parameter was a sequence containing the four - * characters "null". + * characters {@code "null"}. * * @param dstOffset the offset in this sequence. * @param s the sequence to be inserted. * @param start the starting index of the subsequence to be inserted. * @param end the end index of the subsequence to be inserted. * @return a reference to this object. - * @throws IndexOutOfBoundsException if dstOffset - * is negative or greater than this.length(), or - * start or end are negative, or - * start is greater than end or - * end is greater than s.length() + * @throws IndexOutOfBoundsException if {@code dstOffset} + * is negative or greater than {@code this.length()}, or + * {@code start} or {@code end} are negative, or + * {@code start} is greater than {@code end} or + * {@code end} is greater than {@code s.length()} */ public AbstractStringBuilder insert(int dstOffset, CharSequence s, - int start, int end) { + int start, int end) { if (s == null) s = "null"; if ((dstOffset < 0) || (dstOffset > this.length())) @@ -1115,20 +1128,21 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the boolean + * Inserts the string representation of the {@code boolean} * argument into this sequence. *

      - * The second argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then inserted into this sequence at the indicated - * offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(boolean)}, + * and the characters of that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. *

      - * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. - * @param b a boolean. + * @param b a {@code boolean}. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ @@ -1137,25 +1151,21 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the char + * Inserts the string representation of the {@code char} * argument into this sequence. *

      - * The second argument is inserted into the contents of this sequence - * at the position indicated by offset. The length - * of this sequence increases by one. - *

      - * The overall effect is exactly as if the argument were converted to - * a string by the method {@link String#valueOf(char)} and the character - * in that string were then {@link #insert(int, String) inserted} into - * this character sequence at the position indicated by - * offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(char)}, + * and the character in that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. *

      - * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. - * @param c a char. + * @param c a {@code char}. * @return a reference to this object. * @throws IndexOutOfBoundsException if the offset is invalid. */ @@ -1170,20 +1180,21 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the second int + * Inserts the string representation of the second {@code int} * argument into this sequence. *

      - * The second argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then inserted into this sequence at the indicated - * offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(int)}, + * and the characters of that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. *

      - * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. - * @param i an int. + * @param i an {@code int}. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ @@ -1192,20 +1203,21 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the long + * Inserts the string representation of the {@code long} * argument into this sequence. *

      - * The second argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then inserted into this sequence at the position - * indicated by offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(long)}, + * and the characters of that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. *

      - * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. - * @param l a long. + * @param l a {@code long}. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ @@ -1214,20 +1226,21 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the float + * Inserts the string representation of the {@code float} * argument into this sequence. *

      - * The second argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then inserted into this sequence at the indicated - * offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(float)}, + * and the characters of that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. *

      - * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. - * @param f a float. + * @param f a {@code float}. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ @@ -1236,20 +1249,21 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { } /** - * Inserts the string representation of the double + * Inserts the string representation of the {@code double} * argument into this sequence. *

      - * The second argument is converted to a string as if by the method - * String.valueOf, and the characters of that - * string are then inserted into this sequence at the indicated - * offset. + * The overall effect is exactly as if the second argument were + * converted to a string by the method {@link String#valueOf(double)}, + * and the characters of that string were then + * {@link #insert(int,String) inserted} into this character + * sequence at the indicated offset. *

      - * The offset argument must be greater than or equal to - * 0, and less than or equal to the length of this - * sequence. + * The {@code offset} argument must be greater than or equal to + * {@code 0}, and less than or equal to the {@linkplain #length() length} + * of this sequence. * * @param offset the offset. - * @param d a double. + * @param d a {@code double}. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ diff --git a/src/share/classes/java/lang/StringBuffer.java b/src/share/classes/java/lang/StringBuffer.java index e0ecbef0e..f44bb2bae 100644 --- a/src/share/classes/java/lang/StringBuffer.java +++ b/src/share/classes/java/lang/StringBuffer.java @@ -212,7 +212,7 @@ package java.lang; * @throws NullPointerException {@inheritDoc} * @throws IndexOutOfBoundsException {@inheritDoc} */ - public synchronized void getChars(int srcBegin, int srcEnd, char dst[], + public synchronized void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) { super.getChars(srcBegin, srcEnd, dst, dstBegin); @@ -228,10 +228,6 @@ package java.lang; value[index] = ch; } - /** - * @see java.lang.String#valueOf(java.lang.Object) - * @see #append(java.lang.String) - */ public synchronized StringBuffer append(Object obj) { super.append(String.valueOf(obj)); return this; @@ -314,20 +310,19 @@ package java.lang; return this; } - public synchronized StringBuffer append(char str[]) { + public synchronized StringBuffer append(char[] str) { super.append(str); return this; } - public synchronized StringBuffer append(char str[], int offset, int len) { + /** + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public synchronized StringBuffer append(char[] str, int offset, int len) { super.append(str, offset, len); return this; } - /** - * @see java.lang.String#valueOf(boolean) - * @see #append(java.lang.String) - */ public synchronized StringBuffer append(boolean b) { super.append(b); return this; @@ -338,10 +333,6 @@ package java.lang; return this; } - /** - * @see java.lang.String#valueOf(int) - * @see #append(java.lang.String) - */ public synchronized StringBuffer append(int i) { super.append(i); return this; @@ -355,28 +346,16 @@ package java.lang; return this; } - /** - * @see java.lang.String#valueOf(long) - * @see #append(java.lang.String) - */ public synchronized StringBuffer append(long lng) { super.append(lng); return this; } - /** - * @see java.lang.String#valueOf(float) - * @see #append(java.lang.String) - */ public synchronized StringBuffer append(float f) { super.append(f); return this; } - /** - * @see java.lang.String#valueOf(double) - * @see #append(java.lang.String) - */ public synchronized StringBuffer append(double d) { super.append(d); return this; @@ -437,7 +416,7 @@ package java.lang; * @throws StringIndexOutOfBoundsException {@inheritDoc} * @since 1.2 */ - public synchronized StringBuffer insert(int index, char str[], int offset, + public synchronized StringBuffer insert(int index, char[] str, int offset, int len) { super.insert(index, str, offset, len); @@ -446,9 +425,6 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(java.lang.Object) - * @see #insert(int, java.lang.String) - * @see #length() */ public synchronized StringBuffer insert(int offset, Object obj) { super.insert(offset, String.valueOf(obj)); @@ -457,7 +433,6 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see #length() */ public synchronized StringBuffer insert(int offset, String str) { super.insert(offset, str); @@ -467,7 +442,7 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ - public synchronized StringBuffer insert(int offset, char str[]) { + public synchronized StringBuffer insert(int offset, char[] str) { super.insert(offset, str); return this; } @@ -498,9 +473,6 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(boolean) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuffer insert(int offset, boolean b) { return insert(offset, String.valueOf(b)); @@ -508,7 +480,6 @@ package java.lang; /** * @throws IndexOutOfBoundsException {@inheritDoc} - * @see #length() */ public synchronized StringBuffer insert(int offset, char c) { super.insert(offset, c); @@ -517,9 +488,6 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(int) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuffer insert(int offset, int i) { return insert(offset, String.valueOf(i)); @@ -527,9 +495,6 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(long) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuffer insert(int offset, long l) { return insert(offset, String.valueOf(l)); @@ -537,9 +502,6 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(float) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuffer insert(int offset, float f) { return insert(offset, String.valueOf(f)); @@ -547,9 +509,6 @@ package java.lang; /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(double) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuffer insert(int offset, double d) { return insert(offset, String.valueOf(d)); diff --git a/src/share/classes/java/lang/StringBuilder.java b/src/share/classes/java/lang/StringBuilder.java index 99f19768d..e7eea47be 100644 --- a/src/share/classes/java/lang/StringBuilder.java +++ b/src/share/classes/java/lang/StringBuilder.java @@ -124,10 +124,6 @@ public final class StringBuilder append(seq); } - /** - * @see java.lang.String#valueOf(java.lang.Object) - * @see #append(java.lang.String) - */ public StringBuilder append(Object obj) { return append(String.valueOf(obj)); } @@ -175,7 +171,6 @@ public final class StringBuilder } /** - * @throws IndexOutOfBoundsException {@inheritDoc} */ public StringBuilder append(CharSequence s) { if (s == null) @@ -197,20 +192,19 @@ public final class StringBuilder return this; } - public StringBuilder append(char str[]) { + public StringBuilder append(char[] str) { super.append(str); return this; } - public StringBuilder append(char str[], int offset, int len) { + /** + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public StringBuilder append(char[] str, int offset, int len) { super.append(str, offset, len); return this; } - /** - * @see java.lang.String#valueOf(boolean) - * @see #append(java.lang.String) - */ public StringBuilder append(boolean b) { super.append(b); return this; @@ -221,37 +215,21 @@ public final class StringBuilder return this; } - /** - * @see java.lang.String#valueOf(int) - * @see #append(java.lang.String) - */ public StringBuilder append(int i) { super.append(i); return this; } - /** - * @see java.lang.String#valueOf(long) - * @see #append(java.lang.String) - */ public StringBuilder append(long lng) { super.append(lng); return this; } - /** - * @see java.lang.String#valueOf(float) - * @see #append(java.lang.String) - */ public StringBuilder append(float f) { super.append(f); return this; } - /** - * @see java.lang.String#valueOf(double) - * @see #append(java.lang.String) - */ public StringBuilder append(double d) { super.append(d); return this; @@ -292,7 +270,7 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ - public StringBuilder insert(int index, char str[], int offset, + public StringBuilder insert(int index, char[] str, int offset, int len) { super.insert(index, str, offset, len); @@ -301,9 +279,6 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(java.lang.Object) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuilder insert(int offset, Object obj) { return insert(offset, String.valueOf(obj)); @@ -311,7 +286,6 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see #length() */ public StringBuilder insert(int offset, String str) { super.insert(offset, str); @@ -321,7 +295,7 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ - public StringBuilder insert(int offset, char str[]) { + public StringBuilder insert(int offset, char[] str) { super.insert(offset, str); return this; } @@ -349,9 +323,6 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(boolean) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuilder insert(int offset, boolean b) { super.insert(offset, b); @@ -360,7 +331,6 @@ public final class StringBuilder /** * @throws IndexOutOfBoundsException {@inheritDoc} - * @see #length() */ public StringBuilder insert(int offset, char c) { super.insert(offset, c); @@ -369,9 +339,6 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(int) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuilder insert(int offset, int i) { return insert(offset, String.valueOf(i)); @@ -379,9 +346,6 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(long) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuilder insert(int offset, long l) { return insert(offset, String.valueOf(l)); @@ -389,9 +353,6 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(float) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuilder insert(int offset, float f) { return insert(offset, String.valueOf(f)); @@ -399,9 +360,6 @@ public final class StringBuilder /** * @throws StringIndexOutOfBoundsException {@inheritDoc} - * @see java.lang.String#valueOf(double) - * @see #insert(int, java.lang.String) - * @see #length() */ public StringBuilder insert(int offset, double d) { return insert(offset, String.valueOf(d)); -- GitLab From 74422e53deb1ff6c962a2a4e564c7873cc053455 Mon Sep 17 00:00:00 2001 From: martin Date: Tue, 9 Sep 2008 15:20:07 -0700 Subject: [PATCH 103/139] 6733145: (bf) CharBuffer.subSequence can be updated to take advantage of covariant returns Summary: Change return type to CharBuffer Reviewed-by: alanb --- src/share/classes/java/nio/ByteBufferAs-X-Buffer.java | 2 +- src/share/classes/java/nio/Direct-X-Buffer.java | 2 +- src/share/classes/java/nio/Heap-X-Buffer.java | 2 +- src/share/classes/java/nio/StringCharBuffer.java | 2 +- src/share/classes/java/nio/X-Buffer.java | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/share/classes/java/nio/ByteBufferAs-X-Buffer.java b/src/share/classes/java/nio/ByteBufferAs-X-Buffer.java index 5761fd867..1b959ab6e 100644 --- a/src/share/classes/java/nio/ByteBufferAs-X-Buffer.java +++ b/src/share/classes/java/nio/ByteBufferAs-X-Buffer.java @@ -186,7 +186,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private // --- Methods to support CharSequence --- - public CharSequence subSequence(int start, int end) { + public CharBuffer subSequence(int start, int end) { int pos = position(); int lim = limit(); assert (pos <= lim); diff --git a/src/share/classes/java/nio/Direct-X-Buffer.java b/src/share/classes/java/nio/Direct-X-Buffer.java index d1cfb6b5c..44915f207 100644 --- a/src/share/classes/java/nio/Direct-X-Buffer.java +++ b/src/share/classes/java/nio/Direct-X-Buffer.java @@ -402,7 +402,7 @@ class Direct$Type$Buffer$RW$$BO$ // --- Methods to support CharSequence --- - public CharSequence subSequence(int start, int end) { + public CharBuffer subSequence(int start, int end) { int pos = position(); int lim = limit(); assert (pos <= lim); diff --git a/src/share/classes/java/nio/Heap-X-Buffer.java b/src/share/classes/java/nio/Heap-X-Buffer.java index 0c19ca554..ed59c73a8 100644 --- a/src/share/classes/java/nio/Heap-X-Buffer.java +++ b/src/share/classes/java/nio/Heap-X-Buffer.java @@ -566,7 +566,7 @@ class Heap$Type$Buffer$RW$ // --- Methods to support CharSequence --- - public CharSequence subSequence(int start, int end) { + public CharBuffer subSequence(int start, int end) { if ((start < 0) || (end > length()) || (start > end)) diff --git a/src/share/classes/java/nio/StringCharBuffer.java b/src/share/classes/java/nio/StringCharBuffer.java index 3f49ae1eb..648b1986f 100644 --- a/src/share/classes/java/nio/StringCharBuffer.java +++ b/src/share/classes/java/nio/StringCharBuffer.java @@ -99,7 +99,7 @@ class StringCharBuffer // package-private return str.toString().substring(start + offset, end + offset); } - public final CharSequence subSequence(int start, int end) { + public final CharBuffer subSequence(int start, int end) { try { int pos = position(); return new StringCharBuffer(str, -1, diff --git a/src/share/classes/java/nio/X-Buffer.java b/src/share/classes/java/nio/X-Buffer.java index 99a7468ff..67cf6db3d 100644 --- a/src/share/classes/java/nio/X-Buffer.java +++ b/src/share/classes/java/nio/X-Buffer.java @@ -1239,13 +1239,13 @@ public abstract class $Type$Buffer * smaller than start and no larger than * remaining() * - * @return The new character sequence + * @return The new character buffer * * @throws IndexOutOfBoundsException * If the preconditions on start and end * do not hold */ - public abstract CharSequence subSequence(int start, int end); + public abstract CharBuffer subSequence(int start, int end); // --- Methods to support Appendable --- -- GitLab From 0550b4b2e42b611d9a6951b2745966e9225cffe1 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Wed, 10 Sep 2008 13:36:47 +0200 Subject: [PATCH 104/139] 6734813: Provide a way to construct an ObjectName without checked exceptions 6746649: ObjectName constructors and methods declare unchecked exceptions in throws clauses Reviewed-by: dfuchs --- .../DefaultMBeanServerInterceptor.java | 5 +- .../com/sun/jmx/mbeanserver/Repository.java | 2 +- .../classes/com/sun/jmx/mbeanserver/Util.java | 8 - .../sun/jmx/namespace/DomainInterceptor.java | 4 +- .../lang/management/PlatformComponent.java | 2 +- .../classes/java/util/logging/Logging.java | 2 +- .../javax/management/MBeanServerDelegate.java | 2 +- .../classes/javax/management/ObjectName.java | 161 +++++++++++++--- .../management/QueryNotificationFilter.java | 2 +- .../event/EventClientDelegateMBean.java | 2 +- .../namespace/MBeanServerSupport.java | 14 +- src/share/classes/sun/management/Util.java | 6 +- .../management/ObjectName/ValueOfTest.java | 175 ++++++++++++++++++ 13 files changed, 327 insertions(+), 58 deletions(-) create mode 100644 test/javax/management/ObjectName/ValueOfTest.java diff --git a/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java b/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java index 7faeb8727..7da3406b9 100644 --- a/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java +++ b/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java @@ -613,8 +613,7 @@ public class DefaultMBeanServerInterceptor List result = new ArrayList(domains.length); for (int i = 0; i < domains.length; i++) { try { - ObjectName dom = - Util.newObjectName(domains[i] + ":x=x"); + ObjectName dom = ObjectName.valueOf(domains[i] + ":x=x"); checkMBeanPermission(mbeanServerName, (String) null, null, dom, "getDomains"); result.add(domains[i]); } catch (SecurityException e) { @@ -1170,7 +1169,7 @@ public class DefaultMBeanServerInterceptor if one is supplied where it shouldn't be). */ final String completeName = domain + name; - return Util.newObjectName(completeName); + return ObjectName.valueOf(completeName); } public String getDefaultDomain() { diff --git a/src/share/classes/com/sun/jmx/mbeanserver/Repository.java b/src/share/classes/com/sun/jmx/mbeanserver/Repository.java index 5c3339833..03f3e5277 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/Repository.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/Repository.java @@ -396,7 +396,7 @@ public class Repository { // Set domain to default if domain is empty and not already set if (dom.length() == 0) - name = Util.newObjectName(domain + name.toString()); + name = ObjectName.valueOf(domain + name.toString()); // Do we have default domain ? if (dom == domain) { // ES: OK (dom & domain are interned) diff --git a/src/share/classes/com/sun/jmx/mbeanserver/Util.java b/src/share/classes/com/sun/jmx/mbeanserver/Util.java index 93c5c97f6..6307adbf8 100644 --- a/src/share/classes/com/sun/jmx/mbeanserver/Util.java +++ b/src/share/classes/com/sun/jmx/mbeanserver/Util.java @@ -110,14 +110,6 @@ public class Util { return new ArrayList(c); } - public static ObjectName newObjectName(String s) { - try { - return new ObjectName(s); - } catch (MalformedObjectNameException e) { - throw new IllegalArgumentException(e); - } - } - /* This method can be used by code that is deliberately violating the * allowed checked casts. Rather than marking the whole method containing * the code with @SuppressWarnings, you can use a call to this method for diff --git a/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java b/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java index 84644d831..7b88087a8 100644 --- a/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java +++ b/src/share/classes/com/sun/jmx/namespace/DomainInterceptor.java @@ -110,7 +110,7 @@ public class DomainInterceptor extends HandlerInterceptor { super(handler); this.domainName = domainName; this.serverName = serverName; - ALL = Util.newObjectName(domainName+":*"); + ALL = ObjectName.valueOf(domainName+":*"); } @Override @@ -437,7 +437,7 @@ public class DomainInterceptor extends HandlerInterceptor { int count=0; for (int i=0;i set = mbs.queryNames(on, null); for (PlatformComponent pc : subComponents) { set.addAll(pc.getObjectNames(mbs)); diff --git a/src/share/classes/java/util/logging/Logging.java b/src/share/classes/java/util/logging/Logging.java index 72468699e..d99782407 100644 --- a/src/share/classes/java/util/logging/Logging.java +++ b/src/share/classes/java/util/logging/Logging.java @@ -118,6 +118,6 @@ class Logging implements LoggingMXBean { } public ObjectName getObjectName() { - return com.sun.jmx.mbeanserver.Util.newObjectName(LogManager.LOGGING_MXBEAN_NAME); + return ObjectName.valueOf(LogManager.LOGGING_MXBEAN_NAME); } } diff --git a/src/share/classes/javax/management/MBeanServerDelegate.java b/src/share/classes/javax/management/MBeanServerDelegate.java index ea4799d3f..00b82a92f 100644 --- a/src/share/classes/javax/management/MBeanServerDelegate.java +++ b/src/share/classes/javax/management/MBeanServerDelegate.java @@ -304,7 +304,7 @@ public class MBeanServerDelegate implements MBeanServerDelegateMBean, * @since 1.6 */ public static final ObjectName DELEGATE_NAME = - Util.newObjectName("JMImplementation:type=MBeanServerDelegate"); + ObjectName.valueOf("JMImplementation:type=MBeanServerDelegate"); /* Return a timestamp that is monotonically increasing even if System.currentTimeMillis() isn't (for example, if you call this diff --git a/src/share/classes/javax/management/ObjectName.java b/src/share/classes/javax/management/ObjectName.java index 20bcfce72..8185edc27 100644 --- a/src/share/classes/javax/management/ObjectName.java +++ b/src/share/classes/javax/management/ObjectName.java @@ -413,7 +413,7 @@ public class ObjectName implements Comparable, QueryExp { } private void copyToOtherDomain(String domain, ObjectName aname) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { // The domain cannot be null if (domain == null) @@ -467,7 +467,7 @@ public class ObjectName implements Comparable, QueryExp { * is null. */ private void construct(String name) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { // The name cannot be null if (name == null) @@ -729,7 +729,7 @@ public class ObjectName implements Comparable, QueryExp { * @exception NullPointerException One of the parameters is null. */ private void construct(String domain, Map props) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { // The domain cannot be null if (domain == null) @@ -1071,7 +1071,7 @@ public class ObjectName implements Comparable, QueryExp { * Check if the supplied key is a valid key. */ private static void checkKey(String key) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { if (key == null) throw new NullPointerException("Invalid key (null)"); @@ -1359,9 +1359,10 @@ public class ObjectName implements Comparable, QueryExp { * @exception NullPointerException The name parameter * is null. * + * @see #valueOf(String) */ public static ObjectName getInstance(String name) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { return new ObjectName(name); } @@ -1386,10 +1387,11 @@ public class ObjectName implements Comparable, QueryExp { * follow the rules for quoting. * @exception NullPointerException One of the parameters is null. * + * @see #valueOf(String, String, String) */ public static ObjectName getInstance(String domain, String key, String value) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { return new ObjectName(domain, key, value); } @@ -1417,10 +1419,11 @@ public class ObjectName implements Comparable, QueryExp { * quoting. * @exception NullPointerException One of the parameters is null. * + * @see #valueOf(String, Hashtable) */ public static ObjectName getInstance(String domain, Hashtable table) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { return new ObjectName(domain, table); } @@ -1453,11 +1456,120 @@ public class ObjectName implements Comparable, QueryExp { * @exception NullPointerException The name is null. * */ - public static ObjectName getInstance(ObjectName name) - throws NullPointerException { + public static ObjectName getInstance(ObjectName name) { if (name.getClass().equals(ObjectName.class)) return name; - return Util.newObjectName(name.getSerializedNameString()); + return valueOf(name.getSerializedNameString()); + } + + /** + *

      Return an instance of ObjectName that can be used anywhere + * an object obtained with {@link #ObjectName(String) new + * ObjectName(name)} can be used. The returned object may be of + * a subclass of ObjectName. Calling this method twice with the + * same parameters may return the same object or two equal but + * not identical objects.

      + * + *

      This method is equivalent to {@link #getInstance(String)} except that + * it does not throw any checked exceptions.

      + * + * @param name A string representation of the object name. + * + * @return an ObjectName corresponding to the given String. + * + * @exception IllegalArgumentException The string passed as a + * parameter does not have the right format. The {@linkplain + * Throwable#getCause() cause} of this exception will be a + * {@link MalformedObjectNameException}. + * @exception NullPointerException The name parameter + * is null. + * + * @since 1.7 + */ + public static ObjectName valueOf(String name) { + try { + return getInstance(name); + } catch (MalformedObjectNameException e) { + throw new IllegalArgumentException(e.getMessage(), e); + // Just plain IllegalArgumentException(e) produces an exception + // message "javax.management.MalformedObjectNameException: ..." + // which is distracting. + } + } + + /** + *

      Return an instance of ObjectName that can be used anywhere + * an object obtained with {@link #ObjectName(String, String, + * String) new ObjectName(domain, key, value)} can be used. The + * returned object may be of a subclass of ObjectName. Calling + * this method twice with the same parameters may return the same + * object or two equal but not identical objects.

      + * + *

      This method is equivalent to {@link #getInstance(String, String, + * String)} except that it does not throw any checked exceptions.

      + * + * @param domain The domain part of the object name. + * @param key The attribute in the key property of the object name. + * @param value The value in the key property of the object name. + * + * @return an ObjectName corresponding to the given domain, + * key, and value. + * + * @exception IllegalArgumentException The + * domain, key, or value + * contains an illegal character, or value does not + * follow the rules for quoting. The {@linkplain + * Throwable#getCause() cause} of this exception will be a + * {@link MalformedObjectNameException}. + * @exception NullPointerException One of the parameters is null. + * + * @since 1.7 + */ + public static ObjectName valueOf(String domain, String key, String value) { + try { + return getInstance(domain, key, value); + } catch (MalformedObjectNameException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } + } + + /** + *

      Return an instance of ObjectName that can be used anywhere + * an object obtained with {@link #ObjectName(String, Hashtable) + * new ObjectName(domain, table)} can be used. The returned + * object may be of a subclass of ObjectName. Calling this method + * twice with the same parameters may return the same object or + * two equal but not identical objects.

      + * + *

      This method is equivalent to {@link #getInstance(String, Hashtable)} + * except that it does not throw any checked exceptions.

      + * + * @param domain The domain part of the object name. + * @param table A hash table containing one or more key + * properties. The key of each entry in the table is the key of a + * key property in the object name. The associated value in the + * table is the associated value in the object name. + * + * @return an ObjectName corresponding to the given domain and + * key mappings. + * + * @exception IllegalArgumentException The domain + * contains an illegal character, or one of the keys or values in + * table contains an illegal character, or one of the + * values in table does not follow the rules for + * quoting. The {@linkplain Throwable#getCause() cause} of this exception + * will be a {@link MalformedObjectNameException}. + * @exception NullPointerException One of the parameters is null. + * + * @since 1.7 + */ + public static ObjectName valueOf(String domain, + Hashtable table) { + try { + return new ObjectName(domain, table); + } catch (MalformedObjectNameException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } } /** @@ -1477,7 +1589,7 @@ public class ObjectName implements Comparable, QueryExp { * @since 1.7 **/ public final ObjectName withDomain(String newDomain) - throws NullPointerException, MalformedObjectNameException { + throws MalformedObjectNameException { return new ObjectName(newDomain, this); } @@ -1490,9 +1602,11 @@ public class ObjectName implements Comparable, QueryExp { * parameter does not have the right format. * @exception NullPointerException The name parameter * is null. + * + * @see #valueOf(String) */ public ObjectName(String name) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { construct(name); } @@ -1508,9 +1622,11 @@ public class ObjectName implements Comparable, QueryExp { * contains an illegal character, or value does not * follow the rules for quoting. * @exception NullPointerException One of the parameters is null. + * + * @see #valueOf(String, String, String) */ public ObjectName(String domain, String key, String value) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { // If key or value are null a NullPointerException // will be thrown by the put method in Hashtable. // @@ -1533,9 +1649,11 @@ public class ObjectName implements Comparable, QueryExp { * values in table does not follow the rules for * quoting. * @exception NullPointerException One of the parameters is null. + * + * @see #valueOf(String, Hashtable) */ public ObjectName(String domain, Hashtable table) - throws MalformedObjectNameException, NullPointerException { + throws MalformedObjectNameException { construct(domain, table); /* The exception for when a key or value in the table is not a String is now ClassCastException rather than @@ -1629,8 +1747,7 @@ public class ObjectName implements Comparable, QueryExp { * * @since 1.6 */ - public boolean isPropertyValuePattern(String property) - throws NullPointerException, IllegalArgumentException { + public boolean isPropertyValuePattern(String property) { if (property == null) throw new NullPointerException("key property can't be null"); for (int i = 0; i < _ca_array.length; i++) { @@ -1691,7 +1808,7 @@ public class ObjectName implements Comparable, QueryExp { * * @exception NullPointerException If property is null. */ - public String getKeyProperty(String property) throws NullPointerException { + public String getKeyProperty(String property) { return _getKeyPropertyList().get(property); } @@ -1950,8 +2067,7 @@ public class ObjectName implements Comparable, QueryExp { * @exception NullPointerException if s is null. * */ - public static String quote(String s) - throws NullPointerException { + public static String quote(String s) { final StringBuilder buf = new StringBuilder("\""); final int len = s.length(); for (int i = 0; i < len; i++) { @@ -1995,8 +2111,7 @@ public class ObjectName implements Comparable, QueryExp { * @exception NullPointerException if q is null. * */ - public static String unquote(String q) - throws IllegalArgumentException, NullPointerException { + public static String unquote(String q) { final StringBuilder buf = new StringBuilder(); final int len = q.length(); if (len < 2 || q.charAt(0) != '"' || q.charAt(len - 1) != '"') @@ -2041,7 +2156,7 @@ public class ObjectName implements Comparable, QueryExp { * * @since 1.6 */ - public static final ObjectName WILDCARD = Util.newObjectName("*:*"); + public static final ObjectName WILDCARD = valueOf("*:*"); // Category : Utilities <=================================== @@ -2064,7 +2179,7 @@ public class ObjectName implements Comparable, QueryExp { * @exception NullPointerException if name is null. * */ - public boolean apply(ObjectName name) throws NullPointerException { + public boolean apply(ObjectName name) { if (name == null) throw new NullPointerException(); diff --git a/src/share/classes/javax/management/QueryNotificationFilter.java b/src/share/classes/javax/management/QueryNotificationFilter.java index 42451088f..7d1990fa2 100644 --- a/src/share/classes/javax/management/QueryNotificationFilter.java +++ b/src/share/classes/javax/management/QueryNotificationFilter.java @@ -170,7 +170,7 @@ public class QueryNotificationFilter implements NotificationFilter { private static final long serialVersionUID = -8408613922660635231L; private static final ObjectName DEFAULT_NAME = - Util.newObjectName(":type=Notification"); + ObjectName.valueOf(":type=Notification"); private static final QueryExp trueQuery; static { ValueExp zero = Query.value(0); diff --git a/src/share/classes/javax/management/event/EventClientDelegateMBean.java b/src/share/classes/javax/management/event/EventClientDelegateMBean.java index ba57cce9c..7c0b3107c 100644 --- a/src/share/classes/javax/management/event/EventClientDelegateMBean.java +++ b/src/share/classes/javax/management/event/EventClientDelegateMBean.java @@ -96,7 +96,7 @@ public interface EventClientDelegateMBean { * {@value #OBJECT_NAME_STRING}. */ public final static ObjectName OBJECT_NAME = - Util.newObjectName(OBJECT_NAME_STRING); + ObjectName.valueOf(OBJECT_NAME_STRING); /** * A unique listener identifier specified for an EventClient. diff --git a/src/share/classes/javax/management/namespace/MBeanServerSupport.java b/src/share/classes/javax/management/namespace/MBeanServerSupport.java index 2f0e19838..903be3c30 100644 --- a/src/share/classes/javax/management/namespace/MBeanServerSupport.java +++ b/src/share/classes/javax/management/namespace/MBeanServerSupport.java @@ -193,14 +193,6 @@ import javax.management.loading.ClassLoaderRepository; * } * * public class PropsMBS extends MBeanServerSupport { - * private static ObjectName newObjectName(String name) { - * try { - * return new ObjectName(name); - * } catch (MalformedObjectNameException e) { - * throw new AssertionError(e); - * } - * } - * * public static class PropertyImpl implements PropertyMBean { * private final String name; * @@ -219,7 +211,7 @@ import javax.management.loading.ClassLoaderRepository; * throws InstanceNotFoundException { * * // Check that the name is a legal one for a Property MBean - * ObjectName namePattern = newObjectName( + * ObjectName namePattern = ObjectName.valueOf( * "com.example:type=Property,name=\"*\""); * if (!namePattern.apply(name)) * throw new InstanceNotFoundException(name); @@ -239,7 +231,7 @@ import javax.management.loading.ClassLoaderRepository; * {@code Set names = new TreeSet();} * Properties props = System.getProperties(); * for (String propName : props.stringPropertyNames()) { - * ObjectName objectName = newObjectName( + * ObjectName objectName = ObjectName.valueOf( * "com.example:type=Property,name=" + * ObjectName.quote(propName)); * names.add(objectName); @@ -278,7 +270,7 @@ import javax.management.loading.ClassLoaderRepository; * } * * public void propertyChanged(String name, String newValue) { - * ObjectName objectName = newObjectName( + * ObjectName objectName = ObjectName.valueOf( * "com.example:type=Property,name=" + ObjectName.quote(name)); * Notification n = new Notification( * "com.example.property.changed", objectName, 0L, diff --git a/src/share/classes/sun/management/Util.java b/src/share/classes/sun/management/Util.java index 3c0997531..1da808300 100644 --- a/src/share/classes/sun/management/Util.java +++ b/src/share/classes/sun/management/Util.java @@ -43,12 +43,8 @@ class Util { return (String[]) list.toArray(EMPTY_STRING_ARRAY); } - static ObjectName newObjectName(String name) { - return com.sun.jmx.mbeanserver.Util.newObjectName(name); - } - public static ObjectName newObjectName(String domainAndType, String name) { - return newObjectName(domainAndType + ",name=" + name); + return ObjectName.valueOf(domainAndType + ",name=" + name); } private static ManagementPermission monitorPermission = diff --git a/test/javax/management/ObjectName/ValueOfTest.java b/test/javax/management/ObjectName/ValueOfTest.java new file mode 100644 index 000000000..4a68a3d4c --- /dev/null +++ b/test/javax/management/ObjectName/ValueOfTest.java @@ -0,0 +1,175 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6734813 + * @summary Test the ObjectName.valueOf methods + * @author Eamonn McManus + */ + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Hashtable; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +public class ValueOfTest { + public static void main(String[] args) throws Exception { + // Calls that should work + testPositive("d:foo=bar,baz=buh"); + testPositive("foo", "bar", "baz"); + Hashtable h = new Hashtable(); + h.put("foo", "bar"); + h.put("baz", "buh"); + testPositive("domain", h); + + // Calls that should not work + testNegative("d"); + testNegative("d:"); + testNegative("d::foo=bar"); + testNegative("d:", "foo", "bar"); + testNegative("d", "foo=", "bar"); + testNegative("d:", h); + testNegative("d", new Hashtable()); + } + + private static void testPositive(Object... args) throws Exception { + Method valueOf = valueOfMethod(args); + Method getInstance = getInstanceMethod(args); + Constructor constructor = constructor(args); + + Object valueOfValue = valueOf.invoke(null, args); + Object getInstanceValue = getInstance.invoke(null, args); + Object constructorValue = constructor.newInstance(args); + + String argString = + Arrays.toString(args).replace('[', '(').replace(']', ')'); + + if (!valueOfValue.equals(getInstanceValue)) { + throw new Exception( + "valueOf" + argString + " differs from getInstance" + + argString); + } + + if (!valueOfValue.equals(constructorValue)) { + throw new Exception( + "valueOf" + argString + " differs from new ObjectName " + + argString); + } + + System.out.println("OK: valueOf" + argString); + } + + private static void testNegative(Object... args) throws Exception { + Method valueOf = valueOfMethod(args); + Method getInstance = getInstanceMethod(args); + + String argString = + Arrays.toString(args).replace('[', '(').replace(']', ')'); + + final Throwable valueOfException; + try { + valueOf.invoke(null, args); + throw new Exception("valueOf" + argString + " did not fail but should"); + } catch (InvocationTargetException e) { + valueOfException = e.getCause(); + } + if (!(valueOfException instanceof IllegalArgumentException)) { + throw new Exception( + "valueOf" + argString + " threw " + + valueOfException.getClass().getName() + " instead of " + + "IllegalArgumentException", valueOfException); + } + + final Throwable valueOfCause = valueOfException.getCause(); + if (!(valueOfCause instanceof MalformedObjectNameException)) { + throw new Exception( + "valueOf" + argString + " threw exception with wrong " + + "type of cause", valueOfCause); + } + + if (!valueOfException.getMessage().equals(valueOfCause.getMessage())) { + // The IllegalArgumentException should have the same message as + // the MalformedObjectNameException it wraps. + // This isn't specified but is desirable. + throw new Exception( + "valueOf" + argString + ": message in wrapping " + + "IllegalArgumentException (" + valueOfException.getMessage() + + ") differs from message in wrapped " + + "MalformedObjectNameException (" + valueOfCause.getMessage() + + ")"); + } + + final Throwable getInstanceException; + try { + getInstance.invoke(null, args); + throw new Exception("getInstance" + argString + " did not fail but should"); + } catch (InvocationTargetException e) { + getInstanceException = e.getCause(); + } + if (!(getInstanceException instanceof MalformedObjectNameException)) { + throw new Exception( + "getInstance" + argString + " threw wrong exception", + getInstanceException); + } + + if (!valueOfException.getMessage().equals(getInstanceException.getMessage())) { + // Again this is not specified. + throw new Exception( + "Exception message from valueOf" + argString + " (" + + valueOfException.getMessage() + ") differs from message " + + "from getInstance" + argString + " (" + + getInstanceException.getMessage() + ")"); + } + + System.out.println("OK (correct exception): valueOf" + argString); + } + + private static Method valueOfMethod(Object[] args) throws Exception { + return method("valueOf", args); + } + + private static Method getInstanceMethod(Object[] args) throws Exception { + return method("getInstance", args); + } + + private static Method method(String name, Object[] args) throws Exception { + Class[] argTypes = argTypes(args); + return ObjectName.class.getMethod(name, argTypes); + } + + private static Constructor constructor(Object[] args) throws Exception { + Class[] argTypes = argTypes(args); + return ObjectName.class.getConstructor(argTypes); + } + + private static Class[] argTypes(Object[] args) { + Class[] argTypes = new Class[args.length]; + for (int i = 0; i < args.length; i++) + argTypes[i] = args[i].getClass(); + return argTypes; + } +} -- GitLab From c5d62cdbe32939dd5b3692133cfcc80e09864960 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Wed, 10 Sep 2008 14:56:57 +0200 Subject: [PATCH 105/139] 6746759: Fix for 6734813 introduced build break Reviewed-by: dfuchs --- src/share/classes/sun/management/ClassLoadingImpl.java | 2 +- src/share/classes/sun/management/CompilationImpl.java | 2 +- src/share/classes/sun/management/HotSpotDiagnostic.java | 2 +- src/share/classes/sun/management/HotspotInternal.java | 2 +- src/share/classes/sun/management/ManagementFactoryHelper.java | 4 ++-- src/share/classes/sun/management/MemoryImpl.java | 2 +- src/share/classes/sun/management/OperatingSystemImpl.java | 2 +- src/share/classes/sun/management/RuntimeImpl.java | 2 +- src/share/classes/sun/management/ThreadImpl.java | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/share/classes/sun/management/ClassLoadingImpl.java b/src/share/classes/sun/management/ClassLoadingImpl.java index 2f4b6ed07..9eda58380 100644 --- a/src/share/classes/sun/management/ClassLoadingImpl.java +++ b/src/share/classes/sun/management/ClassLoadingImpl.java @@ -71,6 +71,6 @@ class ClassLoadingImpl implements ClassLoadingMXBean { native static void setVerboseClass(boolean value); public ObjectName getObjectName() { - return Util.newObjectName(ManagementFactory.CLASS_LOADING_MXBEAN_NAME); + return ObjectName.valueOf(ManagementFactory.CLASS_LOADING_MXBEAN_NAME); } } diff --git a/src/share/classes/sun/management/CompilationImpl.java b/src/share/classes/sun/management/CompilationImpl.java index d69d9cc64..9c13e67eb 100644 --- a/src/share/classes/sun/management/CompilationImpl.java +++ b/src/share/classes/sun/management/CompilationImpl.java @@ -70,7 +70,7 @@ class CompilationImpl implements CompilationMXBean { } public ObjectName getObjectName() { - return Util.newObjectName(ManagementFactory.COMPILATION_MXBEAN_NAME); + return ObjectName.valueOf(ManagementFactory.COMPILATION_MXBEAN_NAME); } diff --git a/src/share/classes/sun/management/HotSpotDiagnostic.java b/src/share/classes/sun/management/HotSpotDiagnostic.java index f42188ff1..d33337fcf 100644 --- a/src/share/classes/sun/management/HotSpotDiagnostic.java +++ b/src/share/classes/sun/management/HotSpotDiagnostic.java @@ -117,6 +117,6 @@ public class HotSpotDiagnostic implements HotSpotDiagnosticMXBean { } public ObjectName getObjectName() { - return Util.newObjectName("com.sun.management:type=HotSpotDiagnostic"); + return ObjectName.valueOf("com.sun.management:type=HotSpotDiagnostic"); } } diff --git a/src/share/classes/sun/management/HotspotInternal.java b/src/share/classes/sun/management/HotspotInternal.java index 7006699a4..88f9a8268 100644 --- a/src/share/classes/sun/management/HotspotInternal.java +++ b/src/share/classes/sun/management/HotspotInternal.java @@ -41,7 +41,7 @@ public class HotspotInternal private final static String HOTSPOT_INTERNAL_MBEAN_NAME = "sun.management:type=HotspotInternal"; - private static ObjectName objName = Util.newObjectName(HOTSPOT_INTERNAL_MBEAN_NAME); + private static ObjectName objName = ObjectName.valueOf(HOTSPOT_INTERNAL_MBEAN_NAME); private MBeanServer server = null; /** diff --git a/src/share/classes/sun/management/ManagementFactoryHelper.java b/src/share/classes/sun/management/ManagementFactoryHelper.java index 9bb4785d7..c07acfdbc 100644 --- a/src/share/classes/sun/management/ManagementFactoryHelper.java +++ b/src/share/classes/sun/management/ManagementFactoryHelper.java @@ -220,7 +220,7 @@ public class ManagementFactoryHelper { */ private static void addMBean(MBeanServer mbs, Object mbean, String mbeanName) { try { - final ObjectName objName = Util.newObjectName(mbeanName); + final ObjectName objName = ObjectName.valueOf(mbeanName); // inner class requires these fields to be final final MBeanServer mbs0 = mbs; @@ -280,7 +280,7 @@ public class ManagementFactoryHelper { private static void unregisterMBean(MBeanServer mbs, String mbeanName) { try { - final ObjectName objName = Util.newObjectName(mbeanName); + final ObjectName objName = ObjectName.valueOf(mbeanName); // inner class requires these fields to be final final MBeanServer mbs0 = mbs; diff --git a/src/share/classes/sun/management/MemoryImpl.java b/src/share/classes/sun/management/MemoryImpl.java index 29da467ce..aa56186ae 100644 --- a/src/share/classes/sun/management/MemoryImpl.java +++ b/src/share/classes/sun/management/MemoryImpl.java @@ -177,7 +177,7 @@ class MemoryImpl extends NotificationEmitterSupport } public ObjectName getObjectName() { - return Util.newObjectName(ManagementFactory.MEMORY_MXBEAN_NAME); + return ObjectName.valueOf(ManagementFactory.MEMORY_MXBEAN_NAME); } } diff --git a/src/share/classes/sun/management/OperatingSystemImpl.java b/src/share/classes/sun/management/OperatingSystemImpl.java index cfe729688..9ab8b5695 100644 --- a/src/share/classes/sun/management/OperatingSystemImpl.java +++ b/src/share/classes/sun/management/OperatingSystemImpl.java @@ -74,7 +74,7 @@ public class OperatingSystemImpl implements OperatingSystemMXBean { } } public ObjectName getObjectName() { - return Util.newObjectName(ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME); + return ObjectName.valueOf(ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME); } } diff --git a/src/share/classes/sun/management/RuntimeImpl.java b/src/share/classes/sun/management/RuntimeImpl.java index e58040e83..55bcbdc85 100644 --- a/src/share/classes/sun/management/RuntimeImpl.java +++ b/src/share/classes/sun/management/RuntimeImpl.java @@ -149,7 +149,7 @@ class RuntimeImpl implements RuntimeMXBean { } public ObjectName getObjectName() { - return Util.newObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME); + return ObjectName.valueOf(ManagementFactory.RUNTIME_MXBEAN_NAME); } } diff --git a/src/share/classes/sun/management/ThreadImpl.java b/src/share/classes/sun/management/ThreadImpl.java index d12258b9e..565966e9d 100644 --- a/src/share/classes/sun/management/ThreadImpl.java +++ b/src/share/classes/sun/management/ThreadImpl.java @@ -415,7 +415,7 @@ class ThreadImpl implements ThreadMXBean { private static native void resetContentionTimes0(long tid); public ObjectName getObjectName() { - return Util.newObjectName(ManagementFactory.THREAD_MXBEAN_NAME); + return ObjectName.valueOf(ManagementFactory.THREAD_MXBEAN_NAME); } } -- GitLab From 2bb552ef59f2b4007548fdc2b4e42e6826261539 Mon Sep 17 00:00:00 2001 From: dfuchs Date: Wed, 10 Sep 2008 16:27:13 +0200 Subject: [PATCH 106/139] 6746754: jmx namespace: test for leading separator missing 6669137: RFE: InstanceNotFoundException should have a constructor that takes an ObjectName 6746796: jmx namespaces: Several tests are missing an @bug or @run keyword Summary: Note on 6669137: first implementation of 6669137 was actually pushed with 5072476 - here we only have a small update and a test case. Also re-fixes 6737133: Compilation failure of test/javax/management/eventService/LeaseManagerDeadlockTest.java which had failed. Reviewed-by: emcmanus, yjoan --- .../com/sun/jmx/namespace/RoutingProxy.java | 17 +- .../management/InstanceNotFoundException.java | 2 +- .../InstanceNotFoundExceptionTest.java | 76 ++++++ .../NamedMBeanServerTest.java | 1 + .../LeaseManagerDeadlockTest.java | 1 + .../namespace/DomainCreationTest.java | 1 + .../EventWithNamespaceControlTest.java | 1 + .../namespace/EventWithNamespaceTest.java | 2 +- .../namespace/ExportNamespaceTest.java | 1 + .../management/namespace/JMXDomainTest.java | 1 + .../namespace/JMXNamespaceSecurityTest.java | 1 + .../namespace/JMXNamespaceTest.java | 1 + .../namespace/JMXNamespaceViewTest.java | 1 + .../namespace/JMXNamespacesTest.java | 1 + .../namespace/JMXRemoteNamespaceTest.java | 1 + .../management/namespace/LazyDomainTest.java | 1 + .../namespace/LeadingSeparatorsTest.java | 227 ++++++++++++++++++ .../namespace/NamespaceCreationTest.java | 1 + .../namespace/NamespaceNotificationsTest.java | 1 + .../namespace/NullObjectNameTest.java | 1 + .../management/namespace/QueryNamesTest.java | 1 + .../RemoveNotificationListenerTest.java | 1 + .../namespace/RoutingServerProxyTest.java | 1 + .../namespace/SerialParamProcessorTest.java | 1 + .../namespace/SourceNamespaceTest.java | 1 + .../namespace/VirtualMBeanNotifTest.java | 1 + .../namespace/VirtualMBeanTest.java | 2 +- .../namespace/VirtualNamespaceQueryTest.java | 1 + .../namespace/VirtualPropsTest.java | 2 +- 29 files changed, 344 insertions(+), 6 deletions(-) create mode 100644 test/javax/management/MBeanServer/InstanceNotFoundExceptionTest.java create mode 100644 test/javax/management/namespace/LeadingSeparatorsTest.java diff --git a/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java b/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java index e0635c585..aa35c5bda 100644 --- a/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java +++ b/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java @@ -48,6 +48,19 @@ import javax.management.namespace.JMXNamespaces; *

      {@link RoutingConnectionProxy}: to narrow down into an * MBeanServerConnection.

      *

      {@link RoutingServerProxy}: to narrow down into an MBeanServer.

      + * + *

      This class can also be used to "broaden" from a namespace. The same + * class is used for both purposes because in both cases all that happens + * is that ObjectNames are rewritten in one way on the way in (e.g. the + * parameter of getMBeanInfo) and another way on the way out (e.g. the + * return value of queryNames).

      + * + *

      Specifically, if you narrow into "a//" then you want to add the + * "a//" prefix to ObjectNames on the way in and subtract it on the way + * out. But ClientContext uses this class to subtract the + * "jmx.context//foo=bar//" prefix on the way in and add it back on the + * way out.

      + * *

      * This API is a Sun internal API and is subject to changes without notice. *

      @@ -245,8 +258,8 @@ public abstract class RoutingProxy throw x; } catch (MBeanException ex) { throw new IOException("Failed to get "+attributeName+": "+ - ex, - ex.getTargetException()); + ex.getCause(), + ex.getCause()); } catch (Exception ex) { throw new IOException("Failed to get "+attributeName+": "+ ex,ex); diff --git a/src/share/classes/javax/management/InstanceNotFoundException.java b/src/share/classes/javax/management/InstanceNotFoundException.java index baeaed095..3a8376f7b 100644 --- a/src/share/classes/javax/management/InstanceNotFoundException.java +++ b/src/share/classes/javax/management/InstanceNotFoundException.java @@ -61,6 +61,6 @@ public class InstanceNotFoundException extends OperationsException { * @since 1.7 */ public InstanceNotFoundException(ObjectName name) { - this(name.toString()); + this(String.valueOf(name)); } } diff --git a/test/javax/management/MBeanServer/InstanceNotFoundExceptionTest.java b/test/javax/management/MBeanServer/InstanceNotFoundExceptionTest.java new file mode 100644 index 000000000..300796553 --- /dev/null +++ b/test/javax/management/MBeanServer/InstanceNotFoundExceptionTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6669137 + * @summary Test the constructors of InstanceNotFoundExceptionTest. + * @author Daniel Fuchs + * @compile InstanceNotFoundExceptionTest.java + * @run main InstanceNotFoundExceptionTest + */ + +import javax.management.InstanceNotFoundException; +import javax.management.ObjectName; + +public class InstanceNotFoundExceptionTest { + public static void main(String[] args) throws Exception { + final InstanceNotFoundException x = + new InstanceNotFoundException(); + System.out.println("InstanceNotFoundException(): "+x.getMessage()); + + final String msg = "who is toto?"; + final InstanceNotFoundException x2 = + new InstanceNotFoundException(msg); + if (!msg.equals(x2.getMessage())) + throw new Exception("Bad message: expected "+msg+ + ", got "+x2.getMessage()); + System.out.println("InstanceNotFoundException(" + + msg+"): "+x2.getMessage()); + + final InstanceNotFoundException x3 = + new InstanceNotFoundException((String)null); + if (x3.getMessage() != null) + throw new Exception("Bad message: expected "+null+ + ", got "+x3.getMessage()); + System.out.println("InstanceNotFoundException((String)null): "+ + x3.getMessage()); + + final ObjectName n = new ObjectName("who is toto?:type=msg"); + final InstanceNotFoundException x4 = + new InstanceNotFoundException(n); + if (!String.valueOf(n).equals(x4.getMessage())) + throw new Exception("Bad message: expected "+n+ + ", got "+x4.getMessage()); + System.out.println("InstanceNotFoundException(" + + n+"): "+x4.getMessage()); + + final InstanceNotFoundException x5 = + new InstanceNotFoundException((ObjectName)null); + if (!String.valueOf((ObjectName)null).equals(x5.getMessage())) + throw new Exception("Bad message: expected " + + String.valueOf((ObjectName)null)+" got "+x5.getMessage()); + System.out.println("InstanceNotFoundException((ObjectName)null): "+ + x5.getMessage()); + } +} diff --git a/test/javax/management/MBeanServerFactory/NamedMBeanServerTest.java b/test/javax/management/MBeanServerFactory/NamedMBeanServerTest.java index e26168318..8a8d248cb 100644 --- a/test/javax/management/MBeanServerFactory/NamedMBeanServerTest.java +++ b/test/javax/management/MBeanServerFactory/NamedMBeanServerTest.java @@ -25,6 +25,7 @@ * @test * @summary Test named MBeanServers. * @author Daniel Fuchs + * @bug 6299231 * @run clean NamedMBeanServerTest * @run build NamedMBeanServerTest * @run main NamedMBeanServerTest diff --git a/test/javax/management/eventService/LeaseManagerDeadlockTest.java b/test/javax/management/eventService/LeaseManagerDeadlockTest.java index 453aafe4b..ab7d14d54 100644 --- a/test/javax/management/eventService/LeaseManagerDeadlockTest.java +++ b/test/javax/management/eventService/LeaseManagerDeadlockTest.java @@ -27,6 +27,7 @@ * @summary Check that a lock is not held when a LeaseManager expires. * @author Eamonn McManus * @compile -XDignore.symbol.file=true LeaseManagerDeadlockTest.java + * @run main LeaseManagerDeadlockTest */ import com.sun.jmx.event.LeaseManager; diff --git a/test/javax/management/namespace/DomainCreationTest.java b/test/javax/management/namespace/DomainCreationTest.java index 93a98dc62..02a09868e 100644 --- a/test/javax/management/namespace/DomainCreationTest.java +++ b/test/javax/management/namespace/DomainCreationTest.java @@ -23,6 +23,7 @@ /* * * @test DomainCreationTest.java + * @bug 5072476 * @summary Test the creation and registration of JMXDomain instances. * @author Daniel Fuchs * @run clean DomainCreationTest Wombat WombatMBean diff --git a/test/javax/management/namespace/EventWithNamespaceControlTest.java b/test/javax/management/namespace/EventWithNamespaceControlTest.java index c1e5a9d46..1e3901104 100644 --- a/test/javax/management/namespace/EventWithNamespaceControlTest.java +++ b/test/javax/management/namespace/EventWithNamespaceControlTest.java @@ -27,6 +27,7 @@ * @summary Check -Djmx.remote.use.event.service=true and * -Djmx.remote.delegate.event.service * @author Daniel Fuchs + * @bug 5072476 5108776 * @run clean EventWithNamespaceTest EventWithNamespaceControlTest * Wombat WombatMBean JMXRemoteTargetNamespace * NamespaceController NamespaceControllerMBean diff --git a/test/javax/management/namespace/EventWithNamespaceTest.java b/test/javax/management/namespace/EventWithNamespaceTest.java index 44fca88c7..748fdbeff 100644 --- a/test/javax/management/namespace/EventWithNamespaceTest.java +++ b/test/javax/management/namespace/EventWithNamespaceTest.java @@ -24,7 +24,7 @@ /* * * @test EventWithNamespaceTest.java 1.8 - * @bug 6539857 + * @bug 6539857 5072476 5108776 * @summary General Namespace & Notifications test. * @author Daniel Fuchs * @run clean EventWithNamespaceTest Wombat WombatMBean diff --git a/test/javax/management/namespace/ExportNamespaceTest.java b/test/javax/management/namespace/ExportNamespaceTest.java index e73452714..ec49a4c0c 100644 --- a/test/javax/management/namespace/ExportNamespaceTest.java +++ b/test/javax/management/namespace/ExportNamespaceTest.java @@ -26,6 +26,7 @@ * @summary Test that you can export a single namespace through a * JMXConnectorServer. * @author Daniel Fuchs + * @bug 5072476 * @run clean ExportNamespaceTest Wombat WombatMBean * @run build ExportNamespaceTest Wombat WombatMBean * @run main ExportNamespaceTest diff --git a/test/javax/management/namespace/JMXDomainTest.java b/test/javax/management/namespace/JMXDomainTest.java index 4a1a14e96..258cead1a 100644 --- a/test/javax/management/namespace/JMXDomainTest.java +++ b/test/javax/management/namespace/JMXDomainTest.java @@ -23,6 +23,7 @@ /* * * @test JMXDomainTest.java + * @bug 5072476 * @summary Basic test for JMXDomain. * @author Daniel Fuchs * @run clean JMXDomainTest Wombat WombatMBean diff --git a/test/javax/management/namespace/JMXNamespaceSecurityTest.java b/test/javax/management/namespace/JMXNamespaceSecurityTest.java index b948012f1..213ffbfb6 100644 --- a/test/javax/management/namespace/JMXNamespaceSecurityTest.java +++ b/test/javax/management/namespace/JMXNamespaceSecurityTest.java @@ -26,6 +26,7 @@ * @test JMXNamespaceSecurityTest.java * @summary General JMXNamespaceSecurityTest test. * @author Daniel Fuchs + * @bug 5072476 6299231 * @run clean JMXNamespaceViewTest JMXNamespaceSecurityTest Wombat WombatMBean * LazyDomainTest * @run build JMXNamespaceSecurityTest JMXNamespaceViewTest Wombat WombatMBean diff --git a/test/javax/management/namespace/JMXNamespaceTest.java b/test/javax/management/namespace/JMXNamespaceTest.java index da553f9c8..1c2ffac9d 100644 --- a/test/javax/management/namespace/JMXNamespaceTest.java +++ b/test/javax/management/namespace/JMXNamespaceTest.java @@ -25,6 +25,7 @@ * * @test JMXNamespaceTest.java * @summary General JMXNamespace test. + * @bug 5072476 * @author Daniel Fuchs * @run clean JMXNamespaceTest * Wombat WombatMBean JMXRemoteTargetNamespace diff --git a/test/javax/management/namespace/JMXNamespaceViewTest.java b/test/javax/management/namespace/JMXNamespaceViewTest.java index ad51af301..e13496829 100644 --- a/test/javax/management/namespace/JMXNamespaceViewTest.java +++ b/test/javax/management/namespace/JMXNamespaceViewTest.java @@ -24,6 +24,7 @@ * * @test JMXNamespaceViewTest.java * @summary Test the JMXNamespaceView class. + * @bug 5072476 * @author Daniel Fuchs * @run clean JMXNamespaceViewTest Wombat WombatMBean * @run build JMXNamespaceViewTest Wombat WombatMBean diff --git a/test/javax/management/namespace/JMXNamespacesTest.java b/test/javax/management/namespace/JMXNamespacesTest.java index 4249bf104..4dc7c518a 100644 --- a/test/javax/management/namespace/JMXNamespacesTest.java +++ b/test/javax/management/namespace/JMXNamespacesTest.java @@ -24,6 +24,7 @@ * @test JMXNamespacesTest.java * @summary Test the static method that rewrite ObjectNames in JMXNamespacesTest * @author Daniel Fuchs + * @bug 5072476 * @run clean JMXNamespacesTest * @compile -XDignore.symbol.file=true JMXNamespacesTest.java * @run main JMXNamespacesTest diff --git a/test/javax/management/namespace/JMXRemoteNamespaceTest.java b/test/javax/management/namespace/JMXRemoteNamespaceTest.java index ccc73bfaf..8e5f795a6 100644 --- a/test/javax/management/namespace/JMXRemoteNamespaceTest.java +++ b/test/javax/management/namespace/JMXRemoteNamespaceTest.java @@ -25,6 +25,7 @@ * @test JMXRemoteNamespaceTest.java * @summary Basic tests on a JMXRemoteNamespace. * @author Daniel Fuchs + * @bug 5072476 * @run clean JMXRemoteNamespaceTest Wombat WombatMBean * @run build JMXRemoteNamespaceTest Wombat WombatMBean * @run main JMXRemoteNamespaceTest diff --git a/test/javax/management/namespace/LazyDomainTest.java b/test/javax/management/namespace/LazyDomainTest.java index 83439011e..eda9b6669 100644 --- a/test/javax/management/namespace/LazyDomainTest.java +++ b/test/javax/management/namespace/LazyDomainTest.java @@ -23,6 +23,7 @@ /* * * @test LazyDomainTest.java + * @bug 5072476 * @summary Basic test for Lazy Domains. * @author Daniel Fuchs * @run clean LazyDomainTest Wombat WombatMBean diff --git a/test/javax/management/namespace/LeadingSeparatorsTest.java b/test/javax/management/namespace/LeadingSeparatorsTest.java new file mode 100644 index 000000000..5660b2753 --- /dev/null +++ b/test/javax/management/namespace/LeadingSeparatorsTest.java @@ -0,0 +1,227 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * @test LeadingSeparatorsTest.java + * @summary Test that the semantics of a leading // in ObjectName is respected. + * @author Daniel Fuchs + * @bug 5072476 + * @run clean LeadingSeparatorsTest Wombat WombatMBean + * @compile -XDignore.symbol.file=true LeadingSeparatorsTest.java + * @run build LeadingSeparatorsTest Wombat WombatMBean + * @run main LeadingSeparatorsTest + */ + +import java.lang.management.ManagementFactory; +import java.util.Arrays; +import java.util.Set; +import java.util.HashSet; +import java.util.logging.Logger; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectName; +import javax.management.namespace.JMXNamespaces; +import javax.management.namespace.JMXRemoteNamespace; +import javax.management.namespace.JMXNamespace; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +/** + * Class LeadingSeparatorsTest + * @author Sun Microsystems, 2005 - All rights reserved. + */ +public class LeadingSeparatorsTest { + + /** + * A logger for this class. + **/ + private static final Logger LOG = + Logger.getLogger(LeadingSeparatorsTest.class.getName()); + + /** Creates a new instance of NullObjectNameTest */ + public LeadingSeparatorsTest() { + } + + public static interface MyWombatMBean extends WombatMBean { + public Set untrue(ObjectName pat) throws Exception; + } + public static class MyWombat + extends Wombat implements MyWombatMBean { + public MyWombat() throws NotCompliantMBeanException { + super(MyWombatMBean.class); + } + + public Set untrue(ObjectName pat) throws Exception { + final Set res=listMatching(pat.withDomain("*")); + final Set untrue = new HashSet(); + for (ObjectName a:res) { + untrue.add(a.withDomain(pat.getDomain()+"//"+a.getDomain())); + } + return untrue; + } + } + + static String failure=null; + + public static void testRegister() throws Exception { + final MBeanServer top = ManagementFactory.getPlatformMBeanServer(); + final MBeanServer sub = MBeanServerFactory.createMBeanServer(); + final JMXServiceURL url = new JMXServiceURL("rmi",null,0); + final JMXConnectorServer srv = + JMXConnectorServerFactory.newJMXConnectorServer(url,null,sub); + srv.start(); + + try { + + // Create a namespace rmi// that points to 'sub' and flows through + // a JMXRemoteNamespace connected to 'srv' + // The namespace rmi// will accept createMBean, but not registerMBean. + // + final JMXRemoteNamespace rmiHandler = JMXRemoteNamespace. + newJMXRemoteNamespace(srv.getAddress(),null); + top.registerMBean(rmiHandler, + JMXNamespaces.getNamespaceObjectName("rmi")); + top.invoke(JMXNamespaces.getNamespaceObjectName("rmi"), + "connect", null, null); + + // Create a namespace direct// that points to 'sub' and flows + // through a direct reference to 'sub'. + // The namespace direct// will accept createMBean, and registerMBean. + // + final JMXNamespace directHandler = new JMXNamespace(sub); + top.registerMBean(directHandler, + JMXNamespaces.getNamespaceObjectName("direct")); + + final ObjectName n1 = new ObjectName("//direct//w:type=Wombat"); + final ObjectName n2 = new ObjectName("direct//w:type=Wombat"); + final ObjectName n3 = new ObjectName("//rmi//w:type=Wombat"); + final ObjectName n4 = new ObjectName("rmi//w:type=Wombat"); + + // register wombat using an object name with a leading // + final Object obj = new MyWombat(); + // check that returned object name doesn't have the leading // + assertEquals(n2,top.registerMBean(obj, n1).getObjectName()); + System.out.println(n1+" registered"); + + // check that the registered Wombat can be accessed with all its + // names. + System.out.println(n2+" mood is: "+top.getAttribute(n2, "Mood")); + System.out.println(n1+" mood is: "+top.getAttribute(n1, "Mood")); + System.out.println(n4+" mood is: "+top.getAttribute(n4, "Mood")); + System.out.println(n3+" mood is: "+top.getAttribute(n3, "Mood")); + + // call listMatching. The result should not contain any prefix. + final Set res = (Set) + top.invoke(n3, "listMatching", + // remove rmi// from rmi//*:* + JMXNamespaces.deepReplaceHeadNamespace( + new Object[] {ObjectName.WILDCARD.withDomain("rmi//*")}, + "rmi", ""), new String[] {ObjectName.class.getName()}); + + // add rmi// prefix to all names in res. + final Set res1 = + JMXNamespaces.deepReplaceHeadNamespace(res, "", "rmi"); + System.out.println("got: "+res1); + + // compute expected result + final Set res2 = sub.queryNames(null,null); + final Set res3 = new HashSet(); + for (ObjectName o:res2) { + res3.add(o.withDomain("rmi//"+o.getDomain())); + } + System.out.println("expected: "+res3); + assertEquals(res1, res3); + + // invoke "untrue(//niark//niark:*)" + // should return a set were all ObjectNames begin with + // //niark//niark// + // + final Set res4 = (Set) + top.invoke(n3, "untrue", + // remove niark//niark : should remove nothing since + // our ObjectName begins with a leading // + JMXNamespaces.deepReplaceHeadNamespace( + new Object[] { + ObjectName.WILDCARD.withDomain("//niark//niark")}, + "niark//niark", ""), + new String[] {ObjectName.class.getName()}); + System.out.println("got: "+res4); + + // add rmi// should add nothing since the returned names have a + // leading // + // + final Set res5 = + JMXNamespaces.deepReplaceHeadNamespace(res4, "", "rmi"); + System.out.println("got#2: "+res5); + + // compute expected result + final Set res6 = new HashSet(); + for (ObjectName o:res2) { + res6.add(o.withDomain("//niark//niark//"+o.getDomain())); + } + System.out.println("expected: "+res6); + + // both res4 and res5 should be equals to the expected result. + assertEquals(res4, res6); + assertEquals(res5, res6); + + } finally { + srv.stop(); + } + + if (failure != null) + throw new Exception(failure); + + + } + private static void assertEquals(Object x, Object y) { + if (!equal(x, y)) + failed("expected " + string(x) + "; got " + string(y)); + } + + private static boolean equal(Object x, Object y) { + if (x == y) + return true; + if (x == null || y == null) + return false; + if (x.getClass().isArray()) + return Arrays.deepEquals(new Object[] {x}, new Object[] {y}); + return x.equals(y); + } + + private static String string(Object x) { + String s = Arrays.deepToString(new Object[] {x}); + return s.substring(1, s.length() - 1); + } + + + private static void failed(String why) { + failure = why; + new Throwable("FAILED: " + why).printStackTrace(System.out); + } + + public static void main(String[] args) throws Exception { + testRegister(); + } +} diff --git a/test/javax/management/namespace/NamespaceCreationTest.java b/test/javax/management/namespace/NamespaceCreationTest.java index 981cdda42..871bf0220 100644 --- a/test/javax/management/namespace/NamespaceCreationTest.java +++ b/test/javax/management/namespace/NamespaceCreationTest.java @@ -25,6 +25,7 @@ * @test NamespaceCreationTest.java * @summary General JMXNamespace test. * @author Daniel Fuchs + * @bug 5072476 * @run clean NamespaceCreationTest Wombat WombatMBean * @run build NamespaceCreationTest Wombat WombatMBean * @run main NamespaceCreationTest diff --git a/test/javax/management/namespace/NamespaceNotificationsTest.java b/test/javax/management/namespace/NamespaceNotificationsTest.java index ae5bb3c59..9c5a1a04a 100644 --- a/test/javax/management/namespace/NamespaceNotificationsTest.java +++ b/test/javax/management/namespace/NamespaceNotificationsTest.java @@ -25,6 +25,7 @@ * * @test NamespaceNotificationsTest.java 1.12 * @summary General Namespace & Notifications test. + * @bug 5072476 * @author Daniel Fuchs * @run clean NamespaceNotificationsTest * Wombat WombatMBean JMXRemoteTargetNamespace diff --git a/test/javax/management/namespace/NullObjectNameTest.java b/test/javax/management/namespace/NullObjectNameTest.java index 7624fb9d9..156e7661d 100644 --- a/test/javax/management/namespace/NullObjectNameTest.java +++ b/test/javax/management/namespace/NullObjectNameTest.java @@ -24,6 +24,7 @@ * @test NullObjectNameTest.java * @summary Test that null ObjectName are correctly handled in namespaces. * @author Daniel Fuchs + * @bug 5072476 * @run clean NullObjectNameTest Wombat WombatMBean * @compile -XDignore.symbol.file=true NullObjectNameTest.java * @run build NullObjectNameTest Wombat WombatMBean diff --git a/test/javax/management/namespace/QueryNamesTest.java b/test/javax/management/namespace/QueryNamesTest.java index 6e15c9a75..1af597ace 100644 --- a/test/javax/management/namespace/QueryNamesTest.java +++ b/test/javax/management/namespace/QueryNamesTest.java @@ -25,6 +25,7 @@ * @test QueryNamesTest.java 1.4 * @summary Test how queryNames works with Namespaces. * @author Daniel Fuchs + * @bug 5072476 * @run clean QueryNamesTest Wombat WombatMBean * @run build QueryNamesTest Wombat WombatMBean * @run main QueryNamesTest diff --git a/test/javax/management/namespace/RemoveNotificationListenerTest.java b/test/javax/management/namespace/RemoveNotificationListenerTest.java index 08375c0db..a8ea2aee3 100644 --- a/test/javax/management/namespace/RemoveNotificationListenerTest.java +++ b/test/javax/management/namespace/RemoveNotificationListenerTest.java @@ -25,6 +25,7 @@ * @test RemoveNotificationListenerTest.java 1.8 * @summary General RemoveNotificationListenerTest test. * @author Daniel Fuchs + * @bug 5072476 * @run clean RemoveNotificationListenerTest JMXRemoteTargetNamespace * @compile -XDignore.symbol.file=true JMXRemoteTargetNamespace.java * @run build RemoveNotificationListenerTest JMXRemoteTargetNamespace diff --git a/test/javax/management/namespace/RoutingServerProxyTest.java b/test/javax/management/namespace/RoutingServerProxyTest.java index c0302698c..698021321 100644 --- a/test/javax/management/namespace/RoutingServerProxyTest.java +++ b/test/javax/management/namespace/RoutingServerProxyTest.java @@ -25,6 +25,7 @@ * @test RoutingServerProxyTest.java 1.6 * @summary General RoutingServerProxyTest test. * @author Daniel Fuchs + * @bug 5072476 * @run clean RoutingServerProxyTest Wombat WombatMBean * @compile -XDignore.symbol.file=true RoutingServerProxyTest.java * @run build RoutingServerProxyTest Wombat WombatMBean diff --git a/test/javax/management/namespace/SerialParamProcessorTest.java b/test/javax/management/namespace/SerialParamProcessorTest.java index 26dd77572..20df761d0 100644 --- a/test/javax/management/namespace/SerialParamProcessorTest.java +++ b/test/javax/management/namespace/SerialParamProcessorTest.java @@ -26,6 +26,7 @@ * @test SerialParamProcessorTest.java 1.8 * @summary General SerialParamProcessorTest test. * @author Daniel Fuchs + * @bug 5072476 * @run clean SerialParamProcessorTest Wombat WombatMBean * @compile -XDignore.symbol.file=true SerialParamProcessorTest.java * @run build SerialParamProcessorTest Wombat WombatMBean diff --git a/test/javax/management/namespace/SourceNamespaceTest.java b/test/javax/management/namespace/SourceNamespaceTest.java index 745564b8c..2335eb297 100644 --- a/test/javax/management/namespace/SourceNamespaceTest.java +++ b/test/javax/management/namespace/SourceNamespaceTest.java @@ -24,6 +24,7 @@ * * @test SourceNamespaceTest.java * @summary Test how queryNames works with Namespaces. + * @bug 5072476 * @author Daniel Fuchs * @run clean SourceNamespaceTest Wombat WombatMBean * @run build SourceNamespaceTest Wombat WombatMBean diff --git a/test/javax/management/namespace/VirtualMBeanNotifTest.java b/test/javax/management/namespace/VirtualMBeanNotifTest.java index 301af7acd..cc7bbaf95 100644 --- a/test/javax/management/namespace/VirtualMBeanNotifTest.java +++ b/test/javax/management/namespace/VirtualMBeanNotifTest.java @@ -25,6 +25,7 @@ * @test VirtualMBeanNotifTest.java * @bug 5108776 * @build VirtualMBeanNotifTest Wombat WombatMBean + * @run main VirtualMBeanNotifTest * @summary Test that Virtual MBeans can be implemented and emit notifs. * @author Daniel Fuchs */ diff --git a/test/javax/management/namespace/VirtualMBeanTest.java b/test/javax/management/namespace/VirtualMBeanTest.java index 85860df34..03fd3983e 100644 --- a/test/javax/management/namespace/VirtualMBeanTest.java +++ b/test/javax/management/namespace/VirtualMBeanTest.java @@ -23,7 +23,7 @@ /* * @test VirtualMBeanTest.java - * @bug 5108776 + * @bug 5108776 5072476 * @summary Test that Virtual MBeans can be implemented and emit notifs. * @author Eamonn McManus */ diff --git a/test/javax/management/namespace/VirtualNamespaceQueryTest.java b/test/javax/management/namespace/VirtualNamespaceQueryTest.java index 020c1224f..8af244a7b 100644 --- a/test/javax/management/namespace/VirtualNamespaceQueryTest.java +++ b/test/javax/management/namespace/VirtualNamespaceQueryTest.java @@ -26,6 +26,7 @@ * @test VirtualNamespaceQueryTest.java * @summary General VirtualNamespaceQueryTest test. * @author Daniel Fuchs + * @bug 5072476 * @run clean VirtualNamespaceQueryTest Wombat WombatMBean * NamespaceController NamespaceControllerMBean * JMXRemoteTargetNamespace diff --git a/test/javax/management/namespace/VirtualPropsTest.java b/test/javax/management/namespace/VirtualPropsTest.java index 8bb57edd5..904cc5359 100644 --- a/test/javax/management/namespace/VirtualPropsTest.java +++ b/test/javax/management/namespace/VirtualPropsTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 5108776 + * @bug 5108776 5072476 * @summary Test the properties use case for Virtual MBeans that is documented * in MBeanServerSupport. * @author Eamonn McManus -- GitLab From 0d25bee69340d25fa3749185792c5793982d9051 Mon Sep 17 00:00:00 2001 From: rupashka Date: Wed, 10 Sep 2008 19:16:14 +0400 Subject: [PATCH 107/139] 6587742: filling half of a JSlider's track is no longer optional Summary: now OceanTheme uses the JSlider.isFilled property like other themes Reviewed-by: alexp --- .../javax/swing/plaf/metal/MetalSliderUI.java | 281 +++++++++--------- .../swing/JSlider/6587742/bug6587742.html | 13 + .../swing/JSlider/6587742/bug6587742.java | 130 ++++++++ 3 files changed, 290 insertions(+), 134 deletions(-) create mode 100644 test/javax/swing/JSlider/6587742/bug6587742.html create mode 100644 test/javax/swing/JSlider/6587742/bug6587742.java diff --git a/src/share/classes/javax/swing/plaf/metal/MetalSliderUI.java b/src/share/classes/javax/swing/plaf/metal/MetalSliderUI.java index ce9462737..7a26c46b2 100644 --- a/src/share/classes/javax/swing/plaf/metal/MetalSliderUI.java +++ b/src/share/classes/javax/swing/plaf/metal/MetalSliderUI.java @@ -27,23 +27,13 @@ package javax.swing.plaf.metal; import javax.swing.plaf.basic.BasicSliderUI; -import java.awt.Component; -import java.awt.Container; import java.awt.Graphics; import java.awt.Dimension; import java.awt.Rectangle; -import java.awt.Point; -import java.awt.Insets; import java.awt.Color; -import java.io.Serializable; -import java.awt.IllegalComponentStateException; -import java.awt.Polygon; import java.beans.*; -import javax.swing.border.AbstractBorder; - import javax.swing.*; -import javax.swing.event.*; import javax.swing.plaf.*; /** @@ -131,10 +121,7 @@ public class MetalSliderUI extends BasicSliderUI { scrollListener.setScrollByBlock( false ); - Object sliderFillProp = c.getClientProperty( SLIDER_FILL ); - if ( sliderFillProp != null ) { - filledSlider = ((Boolean)sliderFillProp).booleanValue(); - } + prepareFilledSliderField(); } protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) { @@ -145,18 +132,23 @@ public class MetalSliderUI extends BasicSliderUI { public void propertyChange( PropertyChangeEvent e ) { // listen for slider fill super.propertyChange( e ); - String name = e.getPropertyName(); - if ( name.equals( SLIDER_FILL ) ) { - if ( e.getNewValue() != null ) { - filledSlider = ((Boolean)e.getNewValue()).booleanValue(); - } - else { - filledSlider = false; - } + if (e.getPropertyName().equals(SLIDER_FILL)) { + prepareFilledSliderField(); } } } + private void prepareFilledSliderField() { + // Use true for Ocean theme + filledSlider = MetalLookAndFeel.usingOcean(); + + Object sliderFillProp = slider.getClientProperty(SLIDER_FILL); + + if (sliderFillProp != null) { + filledSlider = ((Boolean) sliderFillProp).booleanValue(); + } + } + public void paintThumb(Graphics g) { Rectangle knobBounds = thumbRect; @@ -172,22 +164,11 @@ public class MetalSliderUI extends BasicSliderUI { g.translate( -knobBounds.x, -knobBounds.y ); } - /** - * If chooseFirstis true, c1 is returned, - * otherwise c2. - */ - private Color chooseColor(boolean chooseFirst, Color c1, Color c2) { - if (chooseFirst) { - return c2; - } - return c1; - } - /** * Returns a rectangle enclosing the track that will be painted. */ private Rectangle getPaintTrackRect() { - int trackLeft = 0, trackRight = 0, trackTop = 0, trackBottom = 0; + int trackLeft = 0, trackRight, trackTop = 0, trackBottom; if (slider.getOrientation() == JSlider.HORIZONTAL) { trackBottom = (trackRect.height - 1) - getThumbOverhang(); trackTop = trackBottom - (getTrackWidth() - 1); @@ -223,8 +204,8 @@ public class MetalSliderUI extends BasicSliderUI { int trackLeft = 0; int trackTop = 0; - int trackRight = 0; - int trackBottom = 0; + int trackRight; + int trackBottom; // Draw the track if ( slider.getOrientation() == JSlider.HORIZONTAL ) { @@ -266,11 +247,11 @@ public class MetalSliderUI extends BasicSliderUI { // Draw the fill if ( filledSlider ) { - int middleOfThumb = 0; - int fillTop = 0; - int fillLeft = 0; - int fillBottom = 0; - int fillRight = 0; + int middleOfThumb; + int fillTop; + int fillLeft; + int fillBottom; + int fillRight; if ( slider.getOrientation() == JSlider.HORIZONTAL ) { middleOfThumb = thumbRect.x + (thumbRect.width / 2); @@ -335,105 +316,137 @@ public class MetalSliderUI extends BasicSliderUI { int w = paintRect.width; int h = paintRect.height; - if (!slider.isEnabled()) { - g.setColor(MetalLookAndFeel.getControlShadow()); - g.drawRect(0, 0, w - 1, h - 1); - } - else if (slider.getOrientation() == JSlider.HORIZONTAL) { - int middleOfThumb = thumbRect.x + (thumbRect.width / 2) - - paintRect.x; - int fillMinX; - int fillMaxX; - - if (middleOfThumb > 0) { - g.setColor(chooseColor(drawInverted, - MetalLookAndFeel.getPrimaryControlDarkShadow(), - MetalLookAndFeel.getControlDarkShadow())); - g.drawRect(0, 0, middleOfThumb - 1, h - 1); - } - if (middleOfThumb < w) { - g.setColor(chooseColor(drawInverted, - MetalLookAndFeel.getControlDarkShadow(), - MetalLookAndFeel.getPrimaryControlDarkShadow())); - g.drawRect(middleOfThumb, 0, w - middleOfThumb - 1, h - 1); - } - g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); - if (drawInverted) { - fillMinX = middleOfThumb; - fillMaxX = w - 2; - g.drawLine(1, 1, middleOfThumb, 1); - } - else { - fillMinX = 1; - fillMaxX = middleOfThumb; - g.drawLine(middleOfThumb, 1, w - 1, 1); - } - if (h == 6) { - g.setColor(MetalLookAndFeel.getWhite()); - g.drawLine(fillMinX, 1, fillMaxX, 1); - g.setColor(sliderAltTrackColor); - g.drawLine(fillMinX, 2, fillMaxX, 2); + if (slider.getOrientation() == JSlider.HORIZONTAL) { + int middleOfThumb = thumbRect.x + thumbRect.width / 2 - paintRect.x; + + if (slider.isEnabled()) { + int fillMinX; + int fillMaxX; + + if (middleOfThumb > 0) { + g.setColor(drawInverted ? MetalLookAndFeel.getControlDarkShadow() : + MetalLookAndFeel.getPrimaryControlDarkShadow()); + + g.drawRect(0, 0, middleOfThumb - 1, h - 1); + } + + if (middleOfThumb < w) { + g.setColor(drawInverted ? MetalLookAndFeel.getPrimaryControlDarkShadow() : + MetalLookAndFeel.getControlDarkShadow()); + + g.drawRect(middleOfThumb, 0, w - middleOfThumb - 1, h - 1); + } + + if (filledSlider) { + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + if (drawInverted) { + fillMinX = middleOfThumb; + fillMaxX = w - 2; + g.drawLine(1, 1, middleOfThumb, 1); + } else { + fillMinX = 1; + fillMaxX = middleOfThumb; + g.drawLine(middleOfThumb, 1, w - 1, 1); + } + if (h == 6) { + g.setColor(MetalLookAndFeel.getWhite()); + g.drawLine(fillMinX, 1, fillMaxX, 1); + g.setColor(sliderAltTrackColor); + g.drawLine(fillMinX, 2, fillMaxX, 2); + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawLine(fillMinX, 3, fillMaxX, 3); + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + g.drawLine(fillMinX, 4, fillMaxX, 4); + } + } + } else { g.setColor(MetalLookAndFeel.getControlShadow()); - g.drawLine(fillMinX, 3, fillMaxX, 3); - g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); - g.drawLine(fillMinX, 4, fillMaxX, 4); - } - } - else { - int middleOfThumb = thumbRect.y + (thumbRect.height / 2) - - paintRect.y; - int fillMinY; - int fillMaxY; - - if (middleOfThumb > 0) { - g.setColor(chooseColor(drawInverted, - MetalLookAndFeel.getControlDarkShadow(), - MetalLookAndFeel.getPrimaryControlDarkShadow())); - g.drawRect(0, 0, w - 1, middleOfThumb - 1); - } - if (middleOfThumb < h) { - g.setColor(chooseColor(drawInverted, - MetalLookAndFeel.getPrimaryControlDarkShadow(), - MetalLookAndFeel.getControlDarkShadow())); - g.drawRect(0, middleOfThumb, w - 1, h - middleOfThumb - 1); - } - g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); - if (drawInverted()) { - fillMinY = 1; - fillMaxY = middleOfThumb; - if (leftToRight) { - g.drawLine(1, middleOfThumb, 1, h - 1); + + if (middleOfThumb > 0) { + if (!drawInverted && filledSlider) { + g.fillRect(0, 0, middleOfThumb - 1, h - 1); + } else { + g.drawRect(0, 0, middleOfThumb - 1, h - 1); + } } - else { - g.drawLine(w - 2, middleOfThumb, w - 2, h - 1); + + if (middleOfThumb < w) { + if (drawInverted && filledSlider) { + g.fillRect(middleOfThumb, 0, w - middleOfThumb - 1, h - 1); + } else { + g.drawRect(middleOfThumb, 0, w - middleOfThumb - 1, h - 1); + } } } - else { - fillMinY = middleOfThumb; - fillMaxY = h - 2; - if (leftToRight) { - g.drawLine(1, 1, 1, middleOfThumb); + } else { + int middleOfThumb = thumbRect.y + (thumbRect.height / 2) - paintRect.y; + + if (slider.isEnabled()) { + int fillMinY; + int fillMaxY; + + if (middleOfThumb > 0) { + g.setColor(drawInverted ? MetalLookAndFeel.getPrimaryControlDarkShadow() : + MetalLookAndFeel.getControlDarkShadow()); + + g.drawRect(0, 0, w - 1, middleOfThumb - 1); } - else { - g.drawLine(w - 2, 1, w - 2, middleOfThumb); + + if (middleOfThumb < h) { + g.setColor(drawInverted ? MetalLookAndFeel.getControlDarkShadow() : + MetalLookAndFeel.getPrimaryControlDarkShadow()); + + g.drawRect(0, middleOfThumb, w - 1, h - middleOfThumb - 1); + } + + if (filledSlider) { + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + if (drawInverted()) { + fillMinY = 1; + fillMaxY = middleOfThumb; + if (leftToRight) { + g.drawLine(1, middleOfThumb, 1, h - 1); + } else { + g.drawLine(w - 2, middleOfThumb, w - 2, h - 1); + } + } else { + fillMinY = middleOfThumb; + fillMaxY = h - 2; + if (leftToRight) { + g.drawLine(1, 1, 1, middleOfThumb); + } else { + g.drawLine(w - 2, 1, w - 2, middleOfThumb); + } + } + if (w == 6) { + g.setColor(leftToRight ? MetalLookAndFeel.getWhite() : MetalLookAndFeel.getPrimaryControlShadow()); + g.drawLine(1, fillMinY, 1, fillMaxY); + g.setColor(leftToRight ? sliderAltTrackColor : MetalLookAndFeel.getControlShadow()); + g.drawLine(2, fillMinY, 2, fillMaxY); + g.setColor(leftToRight ? MetalLookAndFeel.getControlShadow() : sliderAltTrackColor); + g.drawLine(3, fillMinY, 3, fillMaxY); + g.setColor(leftToRight ? MetalLookAndFeel.getPrimaryControlShadow() : MetalLookAndFeel.getWhite()); + g.drawLine(4, fillMinY, 4, fillMaxY); + } + } + } else { + g.setColor(MetalLookAndFeel.getControlShadow()); + + if (middleOfThumb > 0) { + if (drawInverted && filledSlider) { + g.fillRect(0, 0, w - 1, middleOfThumb - 1); + } else { + g.drawRect(0, 0, w - 1, middleOfThumb - 1); + } + } + + if (middleOfThumb < h) { + if (!drawInverted && filledSlider) { + g.fillRect(0, middleOfThumb, w - 1, h - middleOfThumb - 1); + } else { + g.drawRect(0, middleOfThumb, w - 1, h - middleOfThumb - 1); + } } - } - if (w == 6) { - g.setColor(chooseColor(!leftToRight, - MetalLookAndFeel.getWhite(), - MetalLookAndFeel.getPrimaryControlShadow())); - g.drawLine(1, fillMinY, 1, fillMaxY); - g.setColor(chooseColor(!leftToRight, sliderAltTrackColor, - MetalLookAndFeel.getControlShadow())); - g.drawLine(2, fillMinY, 2, fillMaxY); - g.setColor(chooseColor(!leftToRight, - MetalLookAndFeel.getControlShadow(), - sliderAltTrackColor)); - g.drawLine(3, fillMinY, 3, fillMaxY); - g.setColor(chooseColor(!leftToRight, - MetalLookAndFeel.getPrimaryControlShadow(), - MetalLookAndFeel.getWhite())); - g.drawLine(4, fillMinY, 4, fillMaxY); } } diff --git a/test/javax/swing/JSlider/6587742/bug6587742.html b/test/javax/swing/JSlider/6587742/bug6587742.html new file mode 100644 index 000000000..ceda9f921 --- /dev/null +++ b/test/javax/swing/JSlider/6587742/bug6587742.html @@ -0,0 +1,13 @@ + + + +Select every theme and check that all sliders looks good. +Note that every slider has a tooltip text with information about +slider configuration. +There is a small difference in sliders with property "filled = null" (it's +default behaviour when property JSlider.isFilled is not setted) +for themes: +1. OceanTheme - sliders look like filled +2. DefaultMetalTheme - sliders look like NOT filled + + diff --git a/test/javax/swing/JSlider/6587742/bug6587742.java b/test/javax/swing/JSlider/6587742/bug6587742.java new file mode 100644 index 000000000..e5282e3ce --- /dev/null +++ b/test/javax/swing/JSlider/6587742/bug6587742.java @@ -0,0 +1,130 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 6587742 + * @summary filling half of a JSlider's track is no longer optional + * @author Pavel Porvatov + * @run applet/manual=done bug6587742.html + */ + +import javax.swing.*; +import javax.swing.plaf.metal.DefaultMetalTheme; +import javax.swing.plaf.metal.MetalLookAndFeel; +import javax.swing.plaf.metal.MetalTheme; +import javax.swing.plaf.metal.OceanTheme; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +public class bug6587742 extends JApplet { + public void init() { + TestPanel panel = new TestPanel(); + + setContentPane(panel); + } + + private class TestPanel extends JPanel { + private final JComboBox cbThemes = new JComboBox(); + + private TestPanel() { + // Fill cbThemes + cbThemes.addItem(new OceanTheme()); + cbThemes.addItem(new DefaultMetalTheme()); + + cbThemes.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + MetalTheme theme = (MetalTheme) cbThemes.getSelectedItem(); + + if (theme != null) { + MetalLookAndFeel.setCurrentTheme(theme); + + // re-install the Metal Look and Feel + try { + UIManager.setLookAndFeel(new MetalLookAndFeel()); + } catch (UnsupportedLookAndFeelException e1) { + JOptionPane.showMessageDialog(TestPanel.this, "Can't change theme: " + e1.getMessage(), + "Error", JOptionPane.ERROR_MESSAGE); + + return; + } + + SwingUtilities.updateComponentTreeUI(bug6587742.this); + } + } + }); + + JPanel pnVertical = new JPanel(); + + pnVertical.setLayout(new BoxLayout(pnVertical, BoxLayout.Y_AXIS)); + + for (int i = 0; i < 12; i++) { + int filled = i >> 2; + + pnVertical.add(createSlider(false, filled > 1 ? null : Boolean.valueOf(filled == 1), (i & 2) == 0, + (i & 1) != 0)); + } + + JPanel pnHorizontal = new JPanel(); + + pnHorizontal.setLayout(new BoxLayout(pnHorizontal, BoxLayout.X_AXIS)); + + for (int i = 0; i < 12; i++) { + int filled = i >> 2; + + pnHorizontal.add(createSlider(true, filled > 1 ? null : Boolean.valueOf(filled == 1), (i & 2) == 0, + (i & 1) != 0)); + } + + JTabbedPane tpSliders = new JTabbedPane(); + + tpSliders.add("Vertical sliders", pnVertical); + tpSliders.add("Horizontal sliders", pnHorizontal); + + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + add(new JLabel("Select theme:")); + add(cbThemes); + add(tpSliders); + } + } + + private static JSlider createSlider(boolean vertical, Boolean filled, boolean enabled, boolean inverted) { + JSlider result = new JSlider(vertical ? SwingConstants.VERTICAL : SwingConstants.HORIZONTAL, 0, 100, 50); + + result.setMajorTickSpacing(20); + result.setMinorTickSpacing(5); + result.setPaintTicks(true); + result.setPaintLabels(true); + result.setEnabled(enabled); + + if (filled != null) { + result.putClientProperty("JSlider.isFilled", filled); + } + + result.setInverted(inverted); + result.setToolTipText("vertical = " + vertical + "
      enabled = " + enabled + "
      filled = " + filled + + "
      inverted = " + inverted + ""); + + return result; + } +} -- GitLab From b17cc7daa207364057704dcdd6fef7c81a720fa8 Mon Sep 17 00:00:00 2001 From: ohair Date: Wed, 10 Sep 2008 09:31:16 -0700 Subject: [PATCH 108/139] 6746421: Compare images logic needs to be more forgiving Reviewed-by: tbell --- make/common/shared/Defs.gmk | 93 ++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/make/common/shared/Defs.gmk b/make/common/shared/Defs.gmk index b25db9ea4..6dcd968f8 100644 --- a/make/common/shared/Defs.gmk +++ b/make/common/shared/Defs.gmk @@ -352,30 +352,6 @@ else HOTSPOT_DOCS_IMPORT_PATH :=$(call DirExists,$(HOTSPOT_IMPORT_PATH)/docs,$(PROMOTED_BUILD_BASEDIR)/docs,/NO_DOCS_DIR) endif -# PREVIOUS_JDK_FILE: filename of install bundle for previous JDK -ifdef ALT_PREVIOUS_JDK_FILE - PREVIOUS_JDK_FILE =$(ALT_PREVIOUS_JDK_FILE) -else - PREVIOUS_JDK_FILE = jdk-$(PREVIOUS_JDK_UNDERSCORE_VERSION)-$(PLATFORM)-$(ARCH)$(BUNDLE_FILE_SUFFIX) -endif -export PREVIOUS_JDK_FILE -PREVIOUS_JDK_FILE:=$(call AltCheckSpaces,PREVIOUS_JDK_FILE) -PREVIOUS_JDK_FILE:=$(call AltCheckValue,PREVIOUS_JDK_FILE) - -# PREVIOUS_JRE_FILE: filename of install bundle for previous JRE -ifdef ALT_PREVIOUS_JRE_FILE - PREVIOUS_JRE_FILE =$(ALT_PREVIOUS_JRE_FILE) -else - PREVIOUS_JRE_FILE = jre-$(PREVIOUS_JDK_UNDERSCORE_VERSION)-$(PLATFORM)-$(ARCH)$(BUNDLE_FILE_SUFFIX) -endif -export PREVIOUS_JRE_FILE -PREVIOUS_JRE_FILE:=$(call AltCheckSpaces,PREVIOUS_JRE_FILE) -PREVIOUS_JRE_FILE:=$(call AltCheckValue,PREVIOUS_JRE_FILE) - -# Set here as shared variables -PREVIOUS_JRE_BUNDLE = $(PREVIOUS_RELEASE_PATH)/$(PREVIOUS_JRE_FILE) -PREVIOUS_JDK_BUNDLE = $(PREVIOUS_RELEASE_PATH)/$(PREVIOUS_JDK_FILE) - # These are the same on all platforms but require the above platform include 1st # BOOTDIR: Bootstrap JDK, previous released JDK. @@ -389,19 +365,70 @@ export BOOTDIR BOOTDIR:=$(call AltCheckSpaces,BOOTDIR) BOOTDIR:=$(call AltCheckValue,BOOTDIR) -# PREVIOUS_RELEASE_PATH: path to where previous release bundles are -ifdef ALT_PREVIOUS_RELEASE_PATH - PREVIOUS_RELEASE_PATH :=$(call OptFullPath,$(ALT_PREVIOUS_RELEASE_PATH)) -else - PREVIOUS_RELEASE_PATH =$(SLASH_JAVA)/re/jdk/$(PREVIOUS_JDK_VERSION)/archive/fcs/bundles/$(PLATFORM)-$(ARCH) -endif -export PREVIOUS_RELEASE_PATH -PREVIOUS_RELEASE_PATH:=$(call AltCheckSpaces,PREVIOUS_RELEASE_PATH) -PREVIOUS_RELEASE_PATH:=$(call AltCheckValue,PREVIOUS_RELEASE_PATH) +# PREVIOUS_FCS_RE_AREA: re path to where previous release binaries/bundles are +PREVIOUS_FCS_RE_AREA = $(SLASH_JAVA)/re/jdk/$(PREVIOUS_JDK_VERSION)/archive/fcs # PREVIOUS_RELEASE_IMAGE: Previous install image to compare against ifdef ALT_PREVIOUS_RELEASE_IMAGE + + # Explicit image provided, no bundle access needed PREVIOUS_RELEASE_IMAGE :=$(call FullPath,$(ALT_PREVIOUS_RELEASE_IMAGE)) + +else + + # PREVIOUS_RELEASE_PATH: path to where previous release bundles are + ifdef ALT_PREVIOUS_RELEASE_PATH + PREVIOUS_RELEASE_PATH :=$(call OptFullPath,$(ALT_PREVIOUS_RELEASE_PATH)) + else + PREVIOUS_RELEASE_PATH := \ + $(call DirExists,$(PREVIOUS_FCS_RE_AREA)/bundles/$(PLATFORM)-$(ARCH),,) + endif + + # Depending on if we have access to these bundles + ifeq ($(PREVIOUS_RELEASE_PATH),) + # Use images in re area or BOOTDIR (which is normally the previous release) + PREVIOUS_RELEASE_IMAGE := \ + $(call DirExists,$(PREVIOUS_FCS_RE_AREA)/binaries/$(PLATFORM)-$(ARCH),$(BOOTDIR),) + else + # Get names of and paths to bundles + PREVIOUS_RELEASE_PATH:=$(call AltCheckSpaces,PREVIOUS_RELEASE_PATH) + PREVIOUS_RELEASE_PATH:=$(call AltCheckValue,PREVIOUS_RELEASE_PATH) + export PREVIOUS_RELEASE_PATH + + # PREVIOUS_JDK_FILE: filename of install bundle for previous JDK + ifdef ALT_PREVIOUS_JDK_FILE + PREVIOUS_JDK_FILE =$(ALT_PREVIOUS_JDK_FILE) + else + PREVIOUS_JDK_FILE = \ + jdk-$(PREVIOUS_JDK_UNDERSCORE_VERSION)-$(PLATFORM)-$(ARCH)$(BUNDLE_FILE_SUFFIX) + endif + export PREVIOUS_JDK_FILE + PREVIOUS_JDK_FILE:=$(call AltCheckSpaces,PREVIOUS_JDK_FILE) + PREVIOUS_JDK_FILE:=$(call AltCheckValue,PREVIOUS_JDK_FILE) + + # PREVIOUS_JRE_FILE: filename of install bundle for previous JRE + ifdef ALT_PREVIOUS_JRE_FILE + PREVIOUS_JRE_FILE =$(ALT_PREVIOUS_JRE_FILE) + else + PREVIOUS_JRE_FILE = \ + jre-$(PREVIOUS_JDK_UNDERSCORE_VERSION)-$(PLATFORM)-$(ARCH)$(BUNDLE_FILE_SUFFIX) + endif + export PREVIOUS_JRE_FILE + PREVIOUS_JRE_FILE:=$(call AltCheckSpaces,PREVIOUS_JRE_FILE) + PREVIOUS_JRE_FILE:=$(call AltCheckValue,PREVIOUS_JRE_FILE) + + # Paths to these bundles + PREVIOUS_JRE_BUNDLE = $(PREVIOUS_RELEASE_PATH)/$(PREVIOUS_JRE_FILE) + PREVIOUS_JDK_BUNDLE = $(PREVIOUS_RELEASE_PATH)/$(PREVIOUS_JDK_FILE) + endif + +endif + +# Indicate we are using an image comparison +ifneq ($(PREVIOUS_RELEASE_IMAGE),) + PREVIOUS_RELEASE_PATH = USING-PREVIOUS_RELEASE_IMAGE + PREVIOUS_JRE_BUNDLE = USING-PREVIOUS_RELEASE_IMAGE + PREVIOUS_JDK_BUNDLE = USING-PREVIOUS_RELEASE_IMAGE endif # CACERTS_FILE: if OPENJDK is false and the internal version of the file -- GitLab From 9bb1219c2417cf1533ee162b92d1218468a552ba Mon Sep 17 00:00:00 2001 From: ohair Date: Wed, 10 Sep 2008 09:48:41 -0700 Subject: [PATCH 109/139] 6746424: Remove build dependency on findbugs and FINDBUGS_HOME Reviewed-by: tbell --- make/common/Sanity.gmk | 3 +-- make/common/shared/Defs.gmk | 8 -------- make/common/shared/Sanity-Settings.gmk | 4 +--- make/common/shared/Sanity.gmk | 22 ---------------------- make/jprt.config | 12 ++++-------- 5 files changed, 6 insertions(+), 43 deletions(-) diff --git a/make/common/Sanity.gmk b/make/common/Sanity.gmk index a4ae86f47..3388be040 100644 --- a/make/common/Sanity.gmk +++ b/make/common/Sanity.gmk @@ -1,5 +1,5 @@ # -# Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -70,7 +70,6 @@ sanity-base: pre-sanity \ sane-compiler \ sane-cacerts \ sane-ant_version \ - sane-findbugs_version \ sane-zip_version \ sane-msvcrt_path diff --git a/make/common/shared/Defs.gmk b/make/common/shared/Defs.gmk index 6dcd968f8..d7e64a57a 100644 --- a/make/common/shared/Defs.gmk +++ b/make/common/shared/Defs.gmk @@ -549,14 +549,6 @@ ifeq ($(ANT_HOME),) else ANT = $(ANT_HOME)/bin/ant endif -ifeq ($(FINDBUGS_HOME),) - FINDBUGS_HOME := $(call DirExists,/usr/share/findbugs,$(JDK_DEVTOOLS_DIR)/share/findbugs/latest,) -endif -ifeq ($(FINDBUGS_HOME),) - FINDBUGS = findbugs -else - FINDBUGS = $(FINDBUGS_HOME)/bin/findbugs -endif ifdef ALT_COPYRIGHT_YEAR COPYRIGHT_YEAR = $(ALT_COPYRIGHT_YEAR) diff --git a/make/common/shared/Sanity-Settings.gmk b/make/common/shared/Sanity-Settings.gmk index d7ef8fe06..911327f5a 100644 --- a/make/common/shared/Sanity-Settings.gmk +++ b/make/common/shared/Sanity-Settings.gmk @@ -1,5 +1,5 @@ # -# Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -79,7 +79,6 @@ ALL_SETTINGS+=$(call addAltSetting,SLASH_JAVA) ALL_SETTINGS+=$(call addRequiredSetting,VARIANT) ALL_SETTINGS+=$(call addAltSetting,JDK_DEVTOOLS_DIR) ALL_SETTINGS+=$(call addOptionalSetting,ANT_HOME) -ALL_SETTINGS+=$(call addOptionalSetting,FINDBUGS_HOME) ALL_SETTINGS+=$(call addAltSetting,UNIXCOMMAND_PATH) ALL_SETTINGS+=$(call addAltSetting,COMPILER_PATH) ALL_SETTINGS+=$(call addAltSetting,DEVTOOLS_PATH) @@ -119,7 +118,6 @@ ifeq ($(PLATFORM),windows) ALL_SETTINGS+=$(call addRequiredVersionSetting,LINK_VER) endif ALL_SETTINGS+=$(call addRequiredVersionSetting,ANT_VER) -ALL_SETTINGS+=$(call addRequiredVersionSetting,FINDBUGS_VER) ALL_SETTINGS+=$(call addRequiredSetting,TEMPDIR) diff --git a/make/common/shared/Sanity.gmk b/make/common/shared/Sanity.gmk index 3048281df..932a7af2f 100644 --- a/make/common/shared/Sanity.gmk +++ b/make/common/shared/Sanity.gmk @@ -114,14 +114,6 @@ else endif ANT_VER:=$(call GetVersion,"$(_ANT_VER)") -REQUIRED_FINDBUGS_VER := 1.2 -ifeq ($(FINDBUGS_HOME),) - _FINDBUGS_VER:=$(shell $(FINDBUGS) -javahome "$(BOOTDIR)" -textui -version 2>&1 ) -else - _FINDBUGS_VER:=$(shell FINDBUGS_HOME="$(FINDBUGS_HOME)" $(FINDBUGS) -javahome "$(BOOTDIR)" -textui -version 2>&1 ) -endif -FINDBUGS_VER:=$(call GetVersion,"$(_FINDBUGS_VER)") - ifdef ALT_BINDIR ALT_BINDIR_VERSION := $(shell $(ALT_BINDIR)/java$(EXE_SUFFIX) -version 2>&1 | $(NAWK) -F'"' '{ print $$2 }') ALT_BINDIR_OK := $(shell $(ECHO) $(ALT_BINDIR_VERSION) | $(EGREP) -c '^$(JDK_MAJOR_VERSION).$(JDK_MINOR_VERSION)') @@ -182,7 +174,6 @@ include $(JDK_MAKE_SHARED_DIR)/Sanity-Settings.gmk sane-alsa-versioncheck \ sane-alsa-headers \ sane-ant_version \ - sane-findbugs_version \ sane-zip_version \ sane-unzip_version \ sane-msvcrt_path \ @@ -1216,19 +1207,6 @@ sane-ant_version: "" >> $(WARNING_FILE) ; \ fi -###################################################### -# Check the findbugs version -###################################################### -FINDBUGS_CHECK :=$(call CheckVersions,$(FINDBUGS_VER),$(REQUIRED_FINDBUGS_VER)) -sane-findbugs_version: - @if [ "$(FINDBUGS_CHECK)" != "same" \ - -a "$(FINDBUGS_CHECK)" != "newer" ]; then \ - $(ECHO) "WARNING: The version of findbugs being used is older than \n" \ - " the required version of '$(REQUIRED_FINDBUGS_VER)'. \n" \ - " The version of findbugs found was '$(FINDBUGS_VER)'. \n" \ - "" >> $(WARNING_FILE) ; \ - fi - ###################################################### # Check the zip file version ###################################################### diff --git a/make/jprt.config b/make/jprt.config index ee83ee5cc..d720475ce 100644 --- a/make/jprt.config +++ b/make/jprt.config @@ -100,20 +100,16 @@ share="${jdk_devtools}/share" # Needed for langtools, maybe other parts of the build ANT_HOME="${share}/ant/latest" export ANT_HOME -FINDBUGS_HOME="${share}/findbugs/latest" -export FINDBUGS_HOME # The 3 bin directories in common to all platforms sharebin="${share}/bin" antbin="${ANT_HOME}/bin" -findbugsbin="${FINDBUGS_HOME}/bin" # Check input dirMustExist "${bootdir}" ALT_BOOTDIR dirMustExist "${slashjava}" ALT_SLASH_JAVA dirMustExist "${jdk_import}" ALT_JDK_IMPORT_PATH dirMustExist "${ANT_HOME}" ANT_HOME -dirMustExist "${FINDBUGS_HOME}" FINDBUGS_HOME # Use the JDK import for now (FIXME: use the binary plugs?) if [ "${OPENJDK}" = true ] ; then @@ -143,7 +139,7 @@ if [ "${osname}" = SunOS ] ; then ALT_COMPILER_PATH="${compiler_path}" export ALT_COMPILER_PATH dirMustExist "${compiler_path}" ALT_COMPILER_PATH - path4sdk=${compiler_path}:${sharebin}:${antbin}:${findbugsbin} + path4sdk=${compiler_path}:${sharebin}:${antbin} # Add basic solaris system paths path4sdk=${path4sdk}:/usr/ccs/bin:/usr/ccs/lib:/usr/bin:/bin:/usr/sfw/bin @@ -180,7 +176,7 @@ elif [ "${osname}" = Linux ] ; then ALT_COMPILER_PATH="${compiler_path}" export ALT_COMPILER_PATH dirMustExist "${compiler_path}" ALT_COMPILER_PATH - path4sdk=${compiler_path}:${sharebin}:${antbin}:${findbugsbin} + path4sdk=${compiler_path}:${sharebin}:${antbin} # Add basic paths path4sdk=${path4sdk}:/usr/bin:/bin:/usr/sbin:/sbin @@ -228,7 +224,7 @@ else dosname="${mkshome}/mksnt/dosname -s" # Most unix utilities are in the mksnt directory of ROOTDIR unixcommand_path="${mkshome}/mksnt" - path4sdk="${sharebin};${antbin};${findbugsbin};${unixcommand_path}" + path4sdk="${sharebin};${antbin};${unixcommand_path}" dirMustExist "${unixcommand_path}" ALT_UNIXCOMMAND_PATH devtools_path="${jdk_devtools}/win32/bin" path4sdk="${devtools_path};${path4sdk}" @@ -246,7 +242,7 @@ else dosname="/usr/bin/cygpath -a -m -s" # Most unix utilities are in the /usr/bin unixcommand_path="/usr/bin" - path4sdk="${sharebin};${antbin};${findbugsbin};${unixcommand_path}" + path4sdk="${sharebin};${antbin};${unixcommand_path}" dirMustExist "${unixcommand_path}" ALT_UNIXCOMMAND_PATH # Find GNU make make="${unixcommand_path}/make.exe" -- GitLab From 2c16877cd41cccc8d691cd99c6494c903918f29d Mon Sep 17 00:00:00 2001 From: ohair Date: Wed, 10 Sep 2008 10:16:54 -0700 Subject: [PATCH 110/139] 6746430: Fix problems with getting 'ant -version' and the required setting of 'ANT_HOME' Reviewed-by: tbell --- make/common/shared/Defs.gmk | 8 +++++--- make/common/shared/Sanity.gmk | 6 +----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/make/common/shared/Defs.gmk b/make/common/shared/Defs.gmk index d7e64a57a..638cd3636 100644 --- a/make/common/shared/Defs.gmk +++ b/make/common/shared/Defs.gmk @@ -540,9 +540,11 @@ JDK_CUPS_HEADERS_PATH=$(JDK_DEVTOOLS_DIR)/share/cups/include endif endif -# Utilities ant and findbugs -ifeq ($(ANT_HOME),) - ANT_HOME := $(call DirExists,/usr/share/ant,$(JDK_DEVTOOLS_DIR)/share/ant/latest,) +# Utilities ant +ifeq ($(PLATFORM), windows) + ifeq ($(ANT_HOME),) + ANT_HOME := $(call DirExists,$(JDK_DEVTOOLS_DIR)/share/ant/latest,,) + endif endif ifeq ($(ANT_HOME),) ANT = ant diff --git a/make/common/shared/Sanity.gmk b/make/common/shared/Sanity.gmk index 932a7af2f..b2cbab12b 100644 --- a/make/common/shared/Sanity.gmk +++ b/make/common/shared/Sanity.gmk @@ -107,11 +107,7 @@ UNZIP_VER :=$(call GetVersion,"$(_UNZIP_VER)") BOOT_VER :=$(call GetVersion,"$(_BOOT_VER)") REQUIRED_ANT_VER := 1.6.3 -ifeq ($(ANT_HOME),) - _ANT_VER:=$(shell JAVACMD="$(BOOTDIR)/bin/java" $(ANT) -version 2>&1 ) -else - _ANT_VER:=$(shell JAVACMD="$(BOOTDIR)/bin/java" ANT_HOME="$(ANT_HOME)" $(ANT) -version 2>&1 ) -endif +_ANT_VER:=$(shell $(ANT) -version 2>&1 ) ANT_VER:=$(call GetVersion,"$(_ANT_VER)") ifdef ALT_BINDIR -- GitLab From 592e1d4aa69566b6436cdea9ba55a8f2647b3e3b Mon Sep 17 00:00:00 2001 From: ohair Date: Wed, 10 Sep 2008 10:19:02 -0700 Subject: [PATCH 111/139] 6746409: Error: dl failure on line 689: Copies of libjvm.so need to have chcon (selinux) re-applied to them Reviewed-by: tbell --- make/common/Defs.gmk | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/make/common/Defs.gmk b/make/common/Defs.gmk index a0cb9481c..1bdbe97e7 100644 --- a/make/common/Defs.gmk +++ b/make/common/Defs.gmk @@ -704,7 +704,20 @@ endif # Install of imported file (JDK_IMPORT_PATH, or some other external location) define install-import-file @$(ECHO) "ASSEMBLY_IMPORT: $@" -$(install-file) +$(prep-target) +$(CP) $< $@ +@if [ "$(PLATFORM)" = "linux" -a "$(@F)" = "libjvm.so" ] ; then \ + if [ -x /usr/sbin/selinuxenabled ] ; then \ + /usr/sbin/selinuxenabled; \ + if [ $$? = 0 ] ; then \ + $(ECHO) "/usr/bin/chcon -t textrel_shlib_t $@"; \ + /usr/bin/chcon -t textrel_shlib_t $@; \ + if [ $$? != 0 ]; then \ + echo "ERROR: Cannot chcon $@"; \ + fi; \ + fi; \ + fi; \ +fi endef .PHONY: all build clean clobber -- GitLab From 206f09b43cbfef3995e64d1c5d4ba91476b56843 Mon Sep 17 00:00:00 2001 From: michaelm Date: Thu, 11 Sep 2008 17:46:53 +0100 Subject: [PATCH 112/139] 6744329: Exception in light weight http server code Reviewed-by: chegar --- .../net/httpserver/ChunkedOutputStream.java | 7 +- .../com/sun/net/httpserver/bugs/B6744329.java | 106 ++++++++++++++++++ 2 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 test/com/sun/net/httpserver/bugs/B6744329.java diff --git a/src/share/classes/sun/net/httpserver/ChunkedOutputStream.java b/src/share/classes/sun/net/httpserver/ChunkedOutputStream.java index f53d40e5e..bbef17c64 100644 --- a/src/share/classes/sun/net/httpserver/ChunkedOutputStream.java +++ b/src/share/classes/sun/net/httpserver/ChunkedOutputStream.java @@ -73,6 +73,7 @@ class ChunkedOutputStream extends FilterOutputStream if (count == CHUNK_SIZE) { writeChunk(); } + assert count < CHUNK_SIZE; } public void write (byte[]b, int off, int len) throws IOException { @@ -86,20 +87,22 @@ class ChunkedOutputStream extends FilterOutputStream writeChunk(); len -= remain; off += remain; - while (len > CHUNK_SIZE) { + while (len >= CHUNK_SIZE) { System.arraycopy (b,off,buf,OFFSET,CHUNK_SIZE); len -= CHUNK_SIZE; off += CHUNK_SIZE; count = CHUNK_SIZE; writeChunk(); } - pos = OFFSET; } if (len > 0) { System.arraycopy (b,off,buf,pos,len); count += len; pos += len; } + if (count == CHUNK_SIZE) { + writeChunk(); + } } /** diff --git a/test/com/sun/net/httpserver/bugs/B6744329.java b/test/com/sun/net/httpserver/bugs/B6744329.java new file mode 100644 index 000000000..cd23ab9b3 --- /dev/null +++ b/test/com/sun/net/httpserver/bugs/B6744329.java @@ -0,0 +1,106 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * @test + * @bug B6744329 + * @summary Exception in light weight Http server + */ + +import com.sun.net.httpserver.*; + +import java.util.*; +import java.util.concurrent.*; +import java.io.*; +import java.net.*; +import java.security.*; +import java.security.cert.*; +import javax.net.ssl.*; + +public class B6744329 { + + public static void main (String[] args) throws Exception { + Handler handler = new Handler(); + InetSocketAddress addr = new InetSocketAddress (0); + HttpServer server = HttpServer.create (addr, 0); + HttpContext ctx = server.createContext ("/test", handler); + ExecutorService executor = Executors.newCachedThreadPool(); + server.setExecutor (executor); + server.start (); + + URL url = new URL ("http://localhost:"+server.getAddress().getPort()+"/test/foo.html"); + HttpURLConnection urlc = (HttpURLConnection)url.openConnection (); + try { + InputStream is = urlc.getInputStream(); + int c = 0; + while (is.read()!= -1) { + c ++; + } + System.out.println ("OK"); + } catch (IOException e) { + System.out.println ("exception"); + error = true; + } + server.stop(2); + executor.shutdown(); + if (error) { + throw new RuntimeException ("Test failed"); + } + } + + public static boolean error = false; + + /* this must be the same size as in ChunkedOutputStream.java + */ + final static int CHUNK_SIZE = 4096; + + static class Handler implements HttpHandler { + int invocation = 1; + public void handle (HttpExchange t) + throws IOException + { + InputStream is = t.getRequestBody(); + Headers map = t.getRequestHeaders(); + Headers rmap = t.getResponseHeaders(); + while (is.read () != -1) ; + is.close(); + /* chunked response */ + t.sendResponseHeaders (200, 0); + OutputStream os = t.getResponseBody(); + byte[] first = new byte [CHUNK_SIZE * 2]; + byte[] second = new byte [2]; + os.write (first); + os.write ('x'); + os.write ('x'); + /* An index out of bounds exception will be thrown + * below, which is caught by server, and connection + * will be closed. resulting in IOException to client + * - if bug present + */ + os.write ('x'); + os.write ('x'); + os.write ('x'); + t.close(); + } + } +} -- GitLab From 360ee11c4ad21591aca2f69bd62bf90b97737116 Mon Sep 17 00:00:00 2001 From: mullan Date: Thu, 11 Sep 2008 14:05:16 -0400 Subject: [PATCH 113/139] 6465942: Add problem identification facility to the CertPathValidator framework Summary: Add support to the java.security.cert APIs for determining the reason that a certification path is invalid. Reviewed-by: vinnie --- .../cert/CertPathValidatorException.java | 132 ++++++++++++++++-- .../java/security/cert/PKIXReason.java | 77 ++++++++++ .../provider/certpath/BasicChecker.java | 44 +++--- .../provider/certpath/ConstraintsChecker.java | 16 ++- .../certpath/CrlRevocationChecker.java | 29 ++-- .../provider/certpath/ForwardBuilder.java | 6 +- .../provider/certpath/KeyChecker.java | 13 +- .../provider/certpath/OCSPChecker.java | 10 +- .../certpath/PKIXCertPathValidator.java | 39 +++--- .../certpath/PKIXMasterCertPathValidator.java | 15 +- .../provider/certpath/PolicyChecker.java | 21 +-- .../provider/certpath/ReverseBuilder.java | 15 +- .../provider/certpath/SunCertPathBuilder.java | 14 +- .../ValidateCertPath.java | 10 +- .../ReasonTest.java | 67 +++++++++ .../CertPathValidatorException/Serial.java | 113 +++++++++++++++ .../cert/CertPathValidatorException/cert_file | Bin 0 -> 784 bytes .../CertPathValidatorException/jdk6.serial | Bin 0 -> 1519 bytes .../cert/PolicyNode/GetPolicyQualifiers.java | 8 +- 19 files changed, 525 insertions(+), 104 deletions(-) create mode 100644 src/share/classes/java/security/cert/PKIXReason.java create mode 100644 test/java/security/cert/CertPathValidatorException/ReasonTest.java create mode 100644 test/java/security/cert/CertPathValidatorException/Serial.java create mode 100644 test/java/security/cert/CertPathValidatorException/cert_file create mode 100644 test/java/security/cert/CertPathValidatorException/jdk6.serial diff --git a/src/share/classes/java/security/cert/CertPathValidatorException.java b/src/share/classes/java/security/cert/CertPathValidatorException.java index 5fd70c24a..8a04aeff5 100644 --- a/src/share/classes/java/security/cert/CertPathValidatorException.java +++ b/src/share/classes/java/security/cert/CertPathValidatorException.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,9 @@ package java.security.cert; +import java.io.InvalidObjectException; +import java.io.IOException; +import java.io.ObjectInputStream; import java.security.GeneralSecurityException; /** @@ -36,10 +39,11 @@ import java.security.GeneralSecurityException; * if any, that caused this exception to be thrown. *

      * A CertPathValidatorException may also include the - * certification path that was being validated when the exception was thrown - * and the index of the certificate in the certification path that caused the - * exception to be thrown. Use the {@link #getCertPath getCertPath} and - * {@link #getIndex getIndex} methods to retrieve this information. + * certification path that was being validated when the exception was thrown, + * the index of the certificate in the certification path that caused the + * exception to be thrown, and the reason that caused the failure. Use the + * {@link #getCertPath getCertPath}, {@link #getIndex getIndex}, and + * {@link #getReason getReason} methods to retrieve this information. * *

      * Concurrent Access @@ -71,12 +75,17 @@ public class CertPathValidatorException extends GeneralSecurityException { */ private CertPath certPath; + /** + * @serial the reason the validation failed + */ + private Reason reason = BasicReason.UNSPECIFIED; + /** * Creates a CertPathValidatorException with * no detail message. */ public CertPathValidatorException() { - super(); + this(null, null); } /** @@ -87,7 +96,7 @@ public class CertPathValidatorException extends GeneralSecurityException { * @param msg the detail message */ public CertPathValidatorException(String msg) { - super(msg); + this(msg, null); } /** @@ -104,7 +113,7 @@ public class CertPathValidatorException extends GeneralSecurityException { * permitted, and indicates that the cause is nonexistent or unknown.) */ public CertPathValidatorException(Throwable cause) { - super(cause); + this(null, cause); } /** @@ -117,7 +126,7 @@ public class CertPathValidatorException extends GeneralSecurityException { * permitted, and indicates that the cause is nonexistent or unknown.) */ public CertPathValidatorException(String msg, Throwable cause) { - super(msg, cause); + this(msg, cause, null, -1); } /** @@ -139,6 +148,32 @@ public class CertPathValidatorException extends GeneralSecurityException { */ public CertPathValidatorException(String msg, Throwable cause, CertPath certPath, int index) { + this(msg, cause, certPath, index, BasicReason.UNSPECIFIED); + } + + /** + * Creates a CertPathValidatorException with the specified + * detail message, cause, certification path, index, and reason. + * + * @param msg the detail message (or null if none) + * @param cause the cause (or null if none) + * @param certPath the certification path that was in the process of + * being validated when the error was encountered + * @param index the index of the certificate in the certification path + * that caused the error (or -1 if not applicable). Note that + * the list of certificates in a CertPath is zero based. + * @param reason the reason the validation failed + * @throws IndexOutOfBoundsException if the index is out of range + * (index < -1 || (certPath != null && index >= + * certPath.getCertificates().size()) + * @throws IllegalArgumentException if certPath is + * null and index is not -1 + * @throws NullPointerException if reason is null + * + * @since 1.7 + */ + public CertPathValidatorException(String msg, Throwable cause, + CertPath certPath, int index, Reason reason) { super(msg, cause); if (certPath == null && index != -1) { throw new IllegalArgumentException(); @@ -147,8 +182,12 @@ public class CertPathValidatorException extends GeneralSecurityException { (certPath != null && index >= certPath.getCertificates().size())) { throw new IndexOutOfBoundsException(); } + if (reason == null) { + throw new NullPointerException("reason can't be null"); + } this.certPath = certPath; this.index = index; + this.reason = reason; } /** @@ -174,4 +213,79 @@ public class CertPathValidatorException extends GeneralSecurityException { return this.index; } + /** + * Returns the reason that the validation failed. The reason is + * associated with the index of the certificate returned by + * {@link getIndex}. + * + * @return the reason that the validation failed, or + * BasicReason.UNSPECIFIED if a reason has not been + * specified + * + * @since 1.7 + */ + public Reason getReason() { + return this.reason; + } + + private void readObject(ObjectInputStream stream) + throws ClassNotFoundException, IOException { + stream.defaultReadObject(); + if (reason == null) { + reason = BasicReason.UNSPECIFIED; + } + if (certPath == null && index != -1) { + throw new InvalidObjectException("certpath is null and index != -1"); + } + if (index < -1 || + (certPath != null && index >= certPath.getCertificates().size())) { + throw new InvalidObjectException("index out of range"); + } + } + + /** + * The reason the validation algorithm failed. + * + * @since 1.7 + */ + public static interface Reason extends java.io.Serializable { } + + + /** + * The BasicReason enumerates the potential reasons that a certification + * path of any type may be invalid. + * + * @since 1.7 + */ + public static enum BasicReason implements Reason { + /** + * Unspecified reason. + */ + UNSPECIFIED, + + /** + * The certificate is expired. + */ + EXPIRED, + + /** + * The certificate is not yet valid. + */ + NOT_YET_VALID, + + /** + * The certificate is revoked. + */ + REVOKED, + + /** + * The revocation status of the certificate could not be determined. + */ + UNDETERMINED_REVOCATION_STATUS, + + /** + * The signature is invalid. + */ + INVALID_SIGNATURE + } } diff --git a/src/share/classes/java/security/cert/PKIXReason.java b/src/share/classes/java/security/cert/PKIXReason.java new file mode 100644 index 000000000..ed798d334 --- /dev/null +++ b/src/share/classes/java/security/cert/PKIXReason.java @@ -0,0 +1,77 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.security.cert; + +/** + * The PKIXReason enumerates the potential PKIX-specific reasons + * that an X.509 certification path may be invalid according to the PKIX + * (RFC 3280) standard. These reasons are in addition to those of the + * CertPathValidatorException.BasicReason enumeration. + * + * @since 1.7 + */ +public enum PKIXReason implements CertPathValidatorException.Reason { + /** + * The certificate does not chain correctly. + */ + NAME_CHAINING, + + /** + * The certificate's key usage is invalid. + */ + INVALID_KEY_USAGE, + + /** + * The policy constraints have been violated. + */ + INVALID_POLICY, + + /** + * No acceptable trust anchor found. + */ + NO_TRUST_ANCHOR, + + /** + * The certificate contains one or more unrecognized critical + * extensions. + */ + UNRECOGNIZED_CRIT_EXT, + + /** + * The certificate is not a CA certificate. + */ + NOT_CA_CERT, + + /** + * The path length constraint has been violated. + */ + PATH_TOO_LONG, + + /** + * The name constraints have been violated. + */ + INVALID_NAME +} diff --git a/src/share/classes/sun/security/provider/certpath/BasicChecker.java b/src/share/classes/sun/security/provider/certpath/BasicChecker.java index e4f7d1f3d..491dd4711 100644 --- a/src/share/classes/sun/security/provider/certpath/BasicChecker.java +++ b/src/share/classes/sun/security/provider/certpath/BasicChecker.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,12 +29,18 @@ import java.math.BigInteger; import java.util.Collection; import java.util.Date; import java.util.Set; +import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.PublicKey; +import java.security.SignatureException; import java.security.cert.Certificate; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertPathValidatorException.BasicReason; import java.security.cert.X509Certificate; import java.security.cert.PKIXCertPathChecker; -import java.security.cert.CertPathValidatorException; +import java.security.cert.PKIXReason; import java.security.cert.TrustAnchor; import java.security.interfaces.DSAParams; import java.security.interfaces.DSAPublicKey; @@ -152,11 +158,11 @@ class BasicChecker extends PKIXCertPathChecker { try { cert.verify(prevPubKey, sigProvider); - } catch (Exception e) { - if (debug != null) { - debug.println(e.getMessage()); - e.printStackTrace(); - } + } catch (SignatureException e) { + throw new CertPathValidatorException + (msg + " check failed", e, null, -1, + BasicReason.INVALID_SIGNATURE); + } catch (GeneralSecurityException e) { throw new CertPathValidatorException(msg + " check failed", e); } @@ -176,12 +182,12 @@ class BasicChecker extends PKIXCertPathChecker { try { cert.checkValidity(date); - } catch (Exception e) { - if (debug != null) { - debug.println(e.getMessage()); - e.printStackTrace(); - } - throw new CertPathValidatorException(msg + " check failed", e); + } catch (CertificateExpiredException e) { + throw new CertPathValidatorException + (msg + " check failed", e, null, -1, BasicReason.EXPIRED); + } catch (CertificateNotYetValidException e) { + throw new CertPathValidatorException + (msg + " check failed", e, null, -1, BasicReason.NOT_YET_VALID); } if (debug != null) @@ -204,12 +210,16 @@ class BasicChecker extends PKIXCertPathChecker { // reject null or empty issuer DNs if (X500Name.asX500Name(currIssuer).isEmpty()) { - throw new CertPathValidatorException(msg + " check failed: " + - "empty/null issuer DN in certificate is invalid"); + throw new CertPathValidatorException + (msg + " check failed: " + + "empty/null issuer DN in certificate is invalid", null, + null, -1, PKIXReason.NAME_CHAINING); } if (!(currIssuer.equals(prevSubject))) { - throw new CertPathValidatorException(msg + " check failed"); + throw new CertPathValidatorException + (msg + " check failed", null, null, -1, + PKIXReason.NAME_CHAINING); } if (debug != null) @@ -270,7 +280,7 @@ class BasicChecker extends PKIXCertPathChecker { params.getQ(), params.getG()); usableKey = kf.generatePublic(ks); - } catch (Exception e) { + } catch (GeneralSecurityException e) { throw new CertPathValidatorException("Unable to generate key with" + " inherited parameters: " + e.getMessage(), e); diff --git a/src/share/classes/sun/security/provider/certpath/ConstraintsChecker.java b/src/share/classes/sun/security/provider/certpath/ConstraintsChecker.java index 40872d7d6..7e2783cca 100644 --- a/src/share/classes/sun/security/provider/certpath/ConstraintsChecker.java +++ b/src/share/classes/sun/security/provider/certpath/ConstraintsChecker.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,9 +32,10 @@ import java.util.HashSet; import java.io.IOException; import java.security.cert.Certificate; import java.security.cert.CertificateException; +import java.security.cert.CertPathValidatorException; import java.security.cert.X509Certificate; import java.security.cert.PKIXCertPathChecker; -import java.security.cert.CertPathValidatorException; +import java.security.cert.PKIXReason; import sun.security.util.Debug; import sun.security.x509.PKIXExtensions; import sun.security.x509.NameConstraintsExtension; @@ -147,7 +148,8 @@ class ConstraintsChecker extends PKIXCertPathChecker { try { if (!prevNC.verify(currCert)) { - throw new CertPathValidatorException(msg + " check failed"); + throw new CertPathValidatorException(msg + " check failed", + null, null, -1, PKIXReason.INVALID_NAME); } } catch (IOException ioe) { throw new CertPathValidatorException(ioe); @@ -228,8 +230,9 @@ class ConstraintsChecker extends PKIXCertPathChecker { if (i < certPathLength) { int pathLenConstraint = currCert.getBasicConstraints(); if (pathLenConstraint == -1) { - throw new CertPathValidatorException(msg + " check failed: " - + "this is not a CA certificate"); + throw new CertPathValidatorException + (msg + " check failed: this is not a CA certificate", null, + null, -1, PKIXReason.NOT_CA_CERT); } if (!X509CertImpl.isSelfIssued(currCert)) { @@ -237,7 +240,8 @@ class ConstraintsChecker extends PKIXCertPathChecker { throw new CertPathValidatorException (msg + " check failed: pathLenConstraint violated - " + "this cert must be the last cert in the " - + "certification path"); + + "certification path", null, null, -1, + PKIXReason.PATH_TOO_LONG); } maxPathLength--; } diff --git a/src/share/classes/sun/security/provider/certpath/CrlRevocationChecker.java b/src/share/classes/sun/security/provider/certpath/CrlRevocationChecker.java index 747ccba40..63ee34317 100644 --- a/src/share/classes/sun/security/provider/certpath/CrlRevocationChecker.java +++ b/src/share/classes/sun/security/provider/certpath/CrlRevocationChecker.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,6 +39,7 @@ import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.cert.*; +import java.security.cert.CertPathValidatorException.BasicReason; import java.security.interfaces.DSAPublicKey; import javax.security.auth.x500.X500Principal; import sun.security.util.Debug; @@ -268,7 +269,8 @@ class CrlRevocationChecker extends PKIXCertPathChecker { " circular dependency"); } throw new CertPathValidatorException - ("Could not determine revocation status"); + ("Could not determine revocation status", null, null, -1, + BasicReason.UNDETERMINED_REVOCATION_STATUS); } // init the state for this run @@ -324,7 +326,8 @@ class CrlRevocationChecker extends PKIXCertPathChecker { return; } else { throw new CertPathValidatorException - ("Could not determine revocation status"); + ("Could not determine revocation status", null, null, -1, + BasicReason.UNDETERMINED_REVOCATION_STATUS); } } @@ -370,7 +373,8 @@ class CrlRevocationChecker extends PKIXCertPathChecker { + unresCritExts); } throw new CertPathValidatorException - ("Could not determine revocation status"); + ("Could not determine revocation status", null, null, + -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } } @@ -378,10 +382,11 @@ class CrlRevocationChecker extends PKIXCertPathChecker { if (reasonCode == null) { reasonCode = CRLReason.UNSPECIFIED; } - throw new CertPathValidatorException( - new CertificateRevokedException - (entry.getRevocationDate(), reasonCode, - crl.getIssuerX500Principal(), entry.getExtensions())); + Throwable t = new CertificateRevokedException + (entry.getRevocationDate(), reasonCode, + crl.getIssuerX500Principal(), entry.getExtensions()); + throw new CertPathValidatorException(t.getMessage(), t, + null, -1, BasicReason.REVOKED); } } } @@ -428,7 +433,8 @@ class CrlRevocationChecker extends PKIXCertPathChecker { " circular dependency"); } throw new CertPathValidatorException - ("Could not determine revocation status"); + ("Could not determine revocation status", null, null, + -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } // If prevKey wasn't trusted, maybe we just didn't have the right @@ -617,7 +623,7 @@ class CrlRevocationChecker extends PKIXCertPathChecker { return; } catch (CertPathValidatorException cpve) { // If it is revoked, rethrow exception - if (cpve.getCause() instanceof CertificateRevokedException) { + if (cpve.getReason() == BasicReason.REVOKED) { throw cpve; } // Otherwise, ignore the exception and @@ -628,7 +634,8 @@ class CrlRevocationChecker extends PKIXCertPathChecker { throw new CertPathValidatorException(iape); } catch (CertPathBuilderException cpbe) { throw new CertPathValidatorException - ("Could not determine revocation status", cpbe); + ("Could not determine revocation status", null, null, + -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } } } diff --git a/src/share/classes/sun/security/provider/certpath/ForwardBuilder.java b/src/share/classes/sun/security/provider/certpath/ForwardBuilder.java index aa8860373..d8713cdca 100644 --- a/src/share/classes/sun/security/provider/certpath/ForwardBuilder.java +++ b/src/share/classes/sun/security/provider/certpath/ForwardBuilder.java @@ -32,6 +32,7 @@ import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.cert.CertificateException; import java.security.cert.CertPathValidatorException; +import java.security.cert.PKIXReason; import java.security.cert.CertStore; import java.security.cert.CertStoreException; import java.security.cert.PKIXBuilderParameters; @@ -732,8 +733,9 @@ class ForwardBuilder extends Builder { PKIXExtensions.ExtendedKeyUsage_Id.toString()); if (!unresCritExts.isEmpty()) - throw new CertificateException("Unrecognized critical " - + "extension(s)"); + throw new CertPathValidatorException + ("Unrecognized critical extension(s)", null, null, -1, + PKIXReason.UNRECOGNIZED_CRIT_EXT); } } diff --git a/src/share/classes/sun/security/provider/certpath/KeyChecker.java b/src/share/classes/sun/security/provider/certpath/KeyChecker.java index 1ed96c567..d12031955 100644 --- a/src/share/classes/sun/security/provider/certpath/KeyChecker.java +++ b/src/share/classes/sun/security/provider/certpath/KeyChecker.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ package sun.security.provider.certpath; import java.util.*; import java.security.cert.*; +import java.security.cert.PKIXReason; import sun.security.util.Debug; import sun.security.x509.PKIXExtensions; @@ -75,11 +76,12 @@ class KeyChecker extends PKIXCertPathChecker { if (!forward) { remainingCerts = certPathLen; } else { - throw new CertPathValidatorException("forward checking not supported"); + throw new CertPathValidatorException + ("forward checking not supported"); } } - public boolean isForwardCheckingSupported() { + public final boolean isForwardCheckingSupported() { return false; } @@ -155,8 +157,9 @@ class KeyChecker extends PKIXCertPathChecker { // throw an exception if the keyCertSign bit is not set if (!keyUsageBits[keyCertSign]) { - throw new CertPathValidatorException(msg + " check failed: " - + "keyCertSign bit is not set"); + throw new CertPathValidatorException + (msg + " check failed: keyCertSign bit is not set", null, + null, -1, PKIXReason.INVALID_KEY_USAGE); } if (debug != null) { diff --git a/src/share/classes/sun/security/provider/certpath/OCSPChecker.java b/src/share/classes/sun/security/provider/certpath/OCSPChecker.java index adf5ea689..35ed85def 100644 --- a/src/share/classes/sun/security/provider/certpath/OCSPChecker.java +++ b/src/share/classes/sun/security/provider/certpath/OCSPChecker.java @@ -33,6 +33,7 @@ import java.security.Principal; import java.security.PrivilegedAction; import java.security.Security; import java.security.cert.*; +import java.security.cert.CertPathValidatorException.BasicReason; import java.net.*; import javax.security.auth.x500.X500Principal; @@ -381,17 +382,18 @@ class OCSPChecker extends PKIXCertPathChecker { } if (certOCSPStatus == OCSPResponse.CERT_STATUS_REVOKED) { - throw new CertPathValidatorException( - new CertificateRevokedException( + Throwable t = new CertificateRevokedException( ocspResponse.getRevocationTime(), ocspResponse.getRevocationReason(), responderCert.getSubjectX500Principal(), - ocspResponse.getSingleExtensions())); + ocspResponse.getSingleExtensions()); + throw new CertPathValidatorException(t.getMessage(), t, + null, -1, BasicReason.REVOKED); } else if (certOCSPStatus == OCSPResponse.CERT_STATUS_UNKNOWN) { throw new CertPathValidatorException( "Certificate's revocation status is unknown", null, cp, - remainingCerts); + remainingCerts, BasicReason.UNDETERMINED_REVOCATION_STATUS); } } catch (Exception e) { throw new CertPathValidatorException(e); diff --git a/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java b/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java index 73d749465..63335d234 100644 --- a/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java +++ b/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,7 @@ import java.security.cert.CertPathValidatorResult; import java.security.cert.PKIXCertPathChecker; import java.security.cert.PKIXCertPathValidatorResult; import java.security.cert.PKIXParameters; +import java.security.cert.PKIXReason; import java.security.cert.PolicyNode; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; @@ -47,7 +48,6 @@ import java.util.List; import java.util.ArrayList; import java.util.Date; import java.util.Set; -import java.util.HashSet; import javax.security.auth.x500.X500Principal; import sun.security.util.Debug; @@ -67,6 +67,7 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi { private List userCheckers; private String sigProvider; private BasicChecker basicChecker; + private String ocspProperty; /** * Default constructor. @@ -126,7 +127,7 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi { // Must copy elements of certList into a new modifiable List before // calling Collections.reverse(). - List certList = new ArrayList + ArrayList certList = new ArrayList ((List)cp.getCertificates()); if (debug != null) { if (certList.isEmpty()) { @@ -201,7 +202,8 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi { } // (b) otherwise, generate new exception throw new CertPathValidatorException - ("Path does not chain with any of the trust anchors"); + ("Path does not chain with any of the trust anchors", + null, null, -1, PKIXReason.NO_TRUST_ANCHOR); } /** @@ -210,7 +212,6 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi { */ private boolean isWorthTrying(X509Certificate trustedCert, X509Certificate firstCert) - throws CertPathValidatorException { if (debug != null) { debug.println("PKIXCertPathValidator.isWorthTrying() checking " @@ -240,7 +241,6 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi { * Internal method to setup the internal state */ private void populateVariables(PKIXParameters pkixParam) - throws CertPathValidatorException { // default value for testDate is current time testDate = pkixParam.getDate(); @@ -250,6 +250,17 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi { userCheckers = pkixParam.getCertPathCheckers(); sigProvider = pkixParam.getSigProvider(); + + if (pkixParam.isRevocationEnabled()) { + // Examine OCSP security property + ocspProperty = AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + return + Security.getProperty(OCSPChecker.OCSP_ENABLE_PROP); + } + }); + } } /** @@ -259,12 +270,9 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi { */ private PolicyNode doValidate( TrustAnchor anchor, CertPath cpOriginal, - List certList, PKIXParameters pkixParam, + ArrayList certList, PKIXParameters pkixParam, PolicyNodeImpl rootNode) throws CertPathValidatorException { - List certPathCheckers = - new ArrayList(); - int certPathLen = certList.size(); basicChecker = new BasicChecker(anchor, testDate, sigProvider, false); @@ -281,6 +289,8 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi { pkixParam.getPolicyQualifiersRejected(), rootNode); + ArrayList certPathCheckers = + new ArrayList(); // add standard checkers that we will be using certPathCheckers.add(keyChecker); certPathCheckers.add(constraintsChecker); @@ -290,15 +300,6 @@ public class PKIXCertPathValidator extends CertPathValidatorSpi { // only add a revocationChecker if revocation is enabled if (pkixParam.isRevocationEnabled()) { - // Examine OCSP security property - String ocspProperty = AccessController.doPrivileged( - new PrivilegedAction() { - public String run() { - return - Security.getProperty(OCSPChecker.OCSP_ENABLE_PROP); - } - }); - // Use OCSP if it has been enabled if ("true".equalsIgnoreCase(ocspProperty)) { OCSPChecker ocspChecker = diff --git a/src/share/classes/sun/security/provider/certpath/PKIXMasterCertPathValidator.java b/src/share/classes/sun/security/provider/certpath/PKIXMasterCertPathValidator.java index faa472f84..d5f12168d 100644 --- a/src/share/classes/sun/security/provider/certpath/PKIXMasterCertPathValidator.java +++ b/src/share/classes/sun/security/provider/certpath/PKIXMasterCertPathValidator.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,11 +30,12 @@ import sun.security.util.Debug; import java.util.Collections; import java.util.List; import java.util.Set; -import java.util.Iterator; +import java.security.cert.CertificateRevokedException; import java.security.cert.CertPath; import java.security.cert.CertPathValidatorException; -import java.security.cert.CertificateRevokedException; +import java.security.cert.CertPathValidatorException.BasicReason; import java.security.cert.PKIXCertPathChecker; +import java.security.cert.PKIXReason; import java.security.cert.X509Certificate; /** @@ -153,10 +154,11 @@ class PKIXMasterCertPathValidator { */ CertPathValidatorException currentCause = new CertPathValidatorException(cpve.getMessage(), - cpve.getCause(), cpOriginal, cpSize - (i + 1)); + cpve.getCause(), cpOriginal, cpSize - (i + 1), + cpve.getReason()); // Check if OCSP has confirmed that the cert was revoked - if (cpve.getCause() instanceof CertificateRevokedException) { + if (cpve.getReason() == BasicReason.REVOKED) { throw currentCause; } // Check if it is appropriate to failover @@ -184,7 +186,8 @@ class PKIXMasterCertPathValidator { debug.println("checking for unresolvedCritExts"); if (!unresolvedCritExts.isEmpty()) { throw new CertPathValidatorException("unrecognized " + - "critical extension(s)", null, cpOriginal, cpSize-(i+1)); + "critical extension(s)", null, cpOriginal, cpSize-(i+1), + PKIXReason.UNRECOGNIZED_CRIT_EXT); } if (debug != null) diff --git a/src/share/classes/sun/security/provider/certpath/PolicyChecker.java b/src/share/classes/sun/security/provider/certpath/PolicyChecker.java index 3b76f621c..26dc1e52a 100644 --- a/src/share/classes/sun/security/provider/certpath/PolicyChecker.java +++ b/src/share/classes/sun/security/provider/certpath/PolicyChecker.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,11 +30,12 @@ import java.io.IOException; import java.security.cert.Certificate; import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.security.cert.PKIXCertPathChecker; import java.security.cert.CertPathValidatorException; +import java.security.cert.PKIXCertPathChecker; +import java.security.cert.PKIXReason; import java.security.cert.PolicyNode; import java.security.cert.PolicyQualifierInfo; +import java.security.cert.X509Certificate; import sun.security.util.Debug; import sun.security.x509.CertificatePoliciesExtension; @@ -482,8 +483,9 @@ class PolicyChecker extends PKIXCertPathChecker { // the policyQualifiersRejected flag is set in the params if (!pQuals.isEmpty() && rejectPolicyQualifiers && policiesCritical) { - throw new CertPathValidatorException("critical " + - "policy qualifiers present in certificate"); + throw new CertPathValidatorException( + "critical policy qualifiers present in certificate", + null, null, -1, PKIXReason.INVALID_POLICY); } // PKIX: Section 6.1.3: Step (d)(1)(i) @@ -567,7 +569,8 @@ class PolicyChecker extends PKIXCertPathChecker { if ((explicitPolicy == 0) && (rootNode == null)) { throw new CertPathValidatorException - ("non-null policy tree required and policy tree is null"); + ("non-null policy tree required and policy tree is null", + null, null, -1, PKIXReason.INVALID_POLICY); } return rootNode; @@ -776,12 +779,14 @@ class PolicyChecker extends PKIXCertPathChecker { if (issuerDomain.equals(ANY_POLICY)) { throw new CertPathValidatorException - ("encountered an issuerDomainPolicy of ANY_POLICY"); + ("encountered an issuerDomainPolicy of ANY_POLICY", + null, null, -1, PKIXReason.INVALID_POLICY); } if (subjectDomain.equals(ANY_POLICY)) { throw new CertPathValidatorException - ("encountered a subjectDomainPolicy of ANY_POLICY"); + ("encountered a subjectDomainPolicy of ANY_POLICY", + null, null, -1, PKIXReason.INVALID_POLICY); } Set validNodes = diff --git a/src/share/classes/sun/security/provider/certpath/ReverseBuilder.java b/src/share/classes/sun/security/provider/certpath/ReverseBuilder.java index c3f2b678f..6f826026c 100644 --- a/src/share/classes/sun/security/provider/certpath/ReverseBuilder.java +++ b/src/share/classes/sun/security/provider/certpath/ReverseBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,14 +29,15 @@ import java.io.IOException; import java.security.GeneralSecurityException; import java.security.Principal; import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; import java.security.cert.CertPathValidatorException; import java.security.cert.CertStore; import java.security.cert.CertStoreException; import java.security.cert.PKIXBuilderParameters; import java.security.cert.PKIXCertPathChecker; import java.security.cert.PKIXParameters; +import java.security.cert.PKIXReason; import java.security.cert.TrustAnchor; +import java.security.cert.X509Certificate; import java.security.cert.X509CertSelector; import java.util.ArrayList; import java.util.Collection; @@ -402,7 +403,8 @@ class ReverseBuilder extends Builder { */ if ((currentState.remainingCACerts <= 0) && !X509CertImpl.isSelfIssued(cert)) { throw new CertPathValidatorException - ("pathLenConstraint violated, path too long"); + ("pathLenConstraint violated, path too long", null, + null, -1, PKIXReason.PATH_TOO_LONG); } /* @@ -438,7 +440,8 @@ class ReverseBuilder extends Builder { try { if (!currentState.nc.verify(cert)){ throw new CertPathValidatorException - ("name constraints check failed"); + ("name constraints check failed", null, null, -1, + PKIXReason.INVALID_NAME); } } catch (IOException ioe){ throw new CertPathValidatorException(ioe); @@ -483,7 +486,9 @@ class ReverseBuilder extends Builder { unresolvedCritExts.remove(PKIXExtensions.ExtendedKeyUsage_Id.toString()); if (!unresolvedCritExts.isEmpty()) - throw new CertificateException("Unrecognized critical extension(s)"); + throw new CertPathValidatorException + ("Unrecognized critical extension(s)", null, null, -1, + PKIXReason.UNRECOGNIZED_CRIT_EXT); } /* diff --git a/src/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java b/src/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java index 14ed53090..0c439349d 100644 --- a/src/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java +++ b/src/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,9 @@ import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.Principal; import java.security.PublicKey; +import java.security.cert.*; +import java.security.cert.PKIXReason; +import java.security.interfaces.DSAPublicKey; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -39,10 +42,6 @@ import java.util.Iterator; import java.util.List; import java.util.LinkedList; import java.util.Set; - -import java.security.cert.*; -import java.security.interfaces.DSAPublicKey; - import javax.security.auth.x500.X500Principal; import sun.security.x509.X500Name; @@ -565,8 +564,9 @@ public final class SunCertPathBuilder extends CertPathBuilderSpi { (PKIXExtensions.ExtendedKeyUsage_Id.toString()); if (!unresCritExts.isEmpty()) { - throw new CertPathValidatorException("unrecognized " - + "critical extension(s)"); + throw new CertPathValidatorException + ("unrecognized critical extension(s)", null, + null, -1, PKIXReason.UNRECOGNIZED_CRIT_EXT); } } } diff --git a/test/java/security/cert/CertPathValidator/nameConstraintsRFC822/ValidateCertPath.java b/test/java/security/cert/CertPathValidator/nameConstraintsRFC822/ValidateCertPath.java index d61026273..b2666a310 100644 --- a/test/java/security/cert/CertPathValidator/nameConstraintsRFC822/ValidateCertPath.java +++ b/test/java/security/cert/CertPathValidator/nameConstraintsRFC822/ValidateCertPath.java @@ -1,5 +1,5 @@ /* - * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ import java.io.InputStream; import java.io.IOException; import java.security.cert.*; +import java.security.cert.PKIXReason; import java.util.ArrayList; import java.util.Collections; @@ -69,6 +70,9 @@ public final class ValidateCertPath { validate(path, params); throw new Exception("Successfully validated invalid path."); } catch (CertPathValidatorException e) { + if (e.getReason() != PKIXReason.INVALID_NAME) { + throw new Exception("unexpected reason: " + e.getReason()); + } System.out.println("Path rejected as expected: " + e); } } @@ -86,14 +90,14 @@ public final class ValidateCertPath { args = new String[] {"jane2jane.cer", "jane2steve.cer", "steve2tom.cer"}; TrustAnchor anchor = new TrustAnchor(getCertFromFile(args[0]), null); - List list = new ArrayList(); + List list = new ArrayList(); for (int i = 1; i < args.length; i++) { list.add(0, getCertFromFile(args[i])); } CertificateFactory cf = CertificateFactory.getInstance("X509"); path = cf.generateCertPath(list); - Set anchors = Collections.singleton(anchor); + Set anchors = Collections.singleton(anchor); params = new PKIXParameters(anchors); params.setRevocationEnabled(false); } diff --git a/test/java/security/cert/CertPathValidatorException/ReasonTest.java b/test/java/security/cert/CertPathValidatorException/ReasonTest.java new file mode 100644 index 000000000..3702893ea --- /dev/null +++ b/test/java/security/cert/CertPathValidatorException/ReasonTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6465942 + * @summary unit test for CertPathValidatorException.Reason + */ + +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertPathValidatorException.BasicReason; + +public class ReasonTest { + private static volatile boolean failed = false; + public static void main(String[] args) throws Exception { + + // check that getReason returns UNSPECIFIED if reason not specified + CertPathValidatorException cpve = new CertPathValidatorException("abc"); + if (cpve.getReason() != BasicReason.UNSPECIFIED) { + failed = true; + System.err.println("FAILED: unexpected reason: " + cpve.getReason()); + } + + // check that getReason returns specified reason + cpve = new CertPathValidatorException + ("abc", null, null, -1, BasicReason.REVOKED); + if (cpve.getReason() != BasicReason.REVOKED) { + failed = true; + System.err.println("FAILED: unexpected reason: " + cpve.getReason()); + } + + // check that ctor throws NPE when reason is null + try { + cpve = new CertPathValidatorException("abc", null, null, -1, null); + failed = true; + System.err.println("ctor did not throw NPE for null reason"); + } catch (Exception e) { + if (!(e instanceof NullPointerException)) { + failed = true; + System.err.println("FAILED: unexpected exception: " + e); + } + } + if (failed) { + throw new Exception("Some tests FAILED"); + } + } +} diff --git a/test/java/security/cert/CertPathValidatorException/Serial.java b/test/java/security/cert/CertPathValidatorException/Serial.java new file mode 100644 index 000000000..a6ffd3b4c --- /dev/null +++ b/test/java/security/cert/CertPathValidatorException/Serial.java @@ -0,0 +1,113 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6465942 + * @summary Test deserialization of CertPathValidatorException + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +//import java.io.FileOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.CertPath; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertPathValidatorException.BasicReason; +import java.util.Collections; + +/** + * This class tests to see if CertPathValidatorException can be serialized and + * deserialized properly. + */ +public class Serial { + private static volatile boolean failed = false; + public static void main(String[] args) throws Exception { + + File f = new File(System.getProperty("test.src", "."), "cert_file"); + FileInputStream fis = new FileInputStream(f); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + Certificate c = cf.generateCertificate(fis); + fis.close(); + CertPath cp = cf.generateCertPath(Collections.singletonList(c)); + + CertPathValidatorException cpve1 = + new CertPathValidatorException + ("Test", new Exception("Expired"), cp, 0, BasicReason.EXPIRED); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); +// FileOutputStream fos = new FileOutputStream("jdk7.serial"); + ObjectOutputStream oos = new ObjectOutputStream(baos); +// ObjectOutputStream foos = new ObjectOutputStream(fos); + oos.writeObject(cpve1); +// foos.writeObject(cpve1); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bais); + CertPathValidatorException cpve2 = + (CertPathValidatorException) ois.readObject(); + check(!cpve1.getMessage().equals(cpve2.getMessage()), + "CertPathValidatorException messages not equal"); + check(!cpve1.getCause().getMessage().equals(cpve2.getCause().getMessage()), + "CertPathValidatorException causes not equal"); + check(!cpve1.getCertPath().equals(cpve2.getCertPath()), + "CertPathValidatorException certpaths not equal"); + check(cpve1.getIndex() != cpve2.getIndex(), + "CertPathValidatorException indexes not equal"); + check(cpve1.getReason() != cpve2.getReason(), + "CertPathValidatorException reasons not equal"); + oos.close(); + ois.close(); + + f = new File(System.getProperty("test.src", "."), "jdk6.serial"); + fis = new FileInputStream(f); + ois = new ObjectInputStream(fis); + cpve2 = (CertPathValidatorException) ois.readObject(); + check(!cpve1.getMessage().equals(cpve2.getMessage()), + "CertPathValidatorException messages not equal"); + check(!cpve1.getCause().getMessage().equals(cpve2.getCause().getMessage()), + "CertPathValidatorException causes not equal"); + check(!cpve1.getCertPath().equals(cpve2.getCertPath()), + "CertPathValidatorException certpaths not equal"); + check(cpve1.getIndex() != cpve2.getIndex(), + "CertPathValidatorException indexes not equal"); +// System.out.println(cpve2.getReason()); + check(cpve2.getReason() != BasicReason.UNSPECIFIED, + "CertPathValidatorException reasons not equal"); + oos.close(); + ois.close(); + if (failed) { + throw new Exception("Some tests FAILED"); + } + } + + private static void check(boolean expr, String message) { + if (expr) { + failed = true; + System.err.println("FAILED: " + message); + } + } +} diff --git a/test/java/security/cert/CertPathValidatorException/cert_file b/test/java/security/cert/CertPathValidatorException/cert_file new file mode 100644 index 0000000000000000000000000000000000000000..42af97b376221127bc892d2479e53655ac768b8b GIT binary patch literal 784 zcmXqLV&*YuVmigdVlJI^(tw+dU8~LGoCOOrD}zCfp@0D&8*?ZNn=pH5UUpu7c^*uJ z14D!zLxc-Mgd0PI87^WVC(dhWX<%Y#X=rX@Xk-}$=9(K?K)G~wO%vmGgC<5DSa2`` zIr2=6jSPS5gBxW_g*1cid(F{%^W^zA!Ra7Z9gXc!#e!=u@%ci z`B{E9f4h8C?l6;Sh;WqHM5Aw|pVw(8Ue|j6Nr_oKz?hg?%UqMd${%}F-wb{1!PVZSAW40rFXXTXjyMI}|YWf-O!)?pnlpYuv-nZ*Y zr2n%*c7eu*EuLb9$}fC0#Duq>=QQd$vFxFZSXFmOO|Qm-#p~p>H!mxln76o|`ErxtT#3jn@&fmS< hf$B^6@`IT~B7ZJVp2~YvI;6SF_zE}Eo}lFS+5kZ&AXxwa literal 0 HcmV?d00001 diff --git a/test/java/security/cert/CertPathValidatorException/jdk6.serial b/test/java/security/cert/CertPathValidatorException/jdk6.serial new file mode 100644 index 0000000000000000000000000000000000000000..b76d0709c42bf1c9b75821f4434c3dd99aa9d787 GIT binary patch literal 1519 zcmZ4UmVvdnh(R|iu`E%qI5oMnD6^zeFFCcSM9&#W1SFPZgeB%=rX-f+7r9m>rxuiC z=I32Ci;$Vi;+4z9z~srmnwgi9TH(XM0n!9gU&0{k12Rq@W}H4qMjvLJbwv?_8me{f zsd=eIi8;Yg>*4nBWPMZHv_W(!69WTKlQ39QPGVlV9$dwRzw&lc)-p#hRD@&{<(DTW z<)mIcW~y#kzT-4A1G5hUYjR>~acT*JD8zvvv-IIAt$i4HQ&LM3Gjn`Xi;EM}f$D^i z)CHFmW#*+@M>B90mn0@T%GSo3Jg9y$N26oqq zg3O}Sl+q%YMm?~9g5f5@G%~qq+1UJ5vIKdofPsO55g19*hycLQkjQy-mYUh!Rwf1( zPX?}>%)C^;(%hufA|D36l+@&$M3CE^a}tZe&S3+_R$69Gs$XJmD#&F(K38sPNk)DO zTm)>XRS5%IaB5LzVonJIH-w=FazhCNOKxIjUIma2Ni8lZ1)0GOb|i?!3uIY=82lhY z07M8@0L2)9UQkDkcu>NCr!y5;3JFRr(7d_M*X>W9FffHhGq3AmXgW> zh?^@4fT0K!Tk~1`2OCGo187Jwix@O93ji^XK@-y{CKhw)tdj=ZZ0uTX9_K7rm{}PN zats9w_}G|3S=fZxL-Vrp^2_sJA{-bZ>=+_k7$V#lBFu0R137VCOG^V2LrX(*6GJ1* zC@|OD&;rV(vum0dw;MDu>cE175y+8eYHVcqTOZsgTPma(bl+=^-kT@SzX@&+vU%^q za9Ga1VOyYDrfaN&QsD1M+un#@`;UIzW$^9v?9iO+jn)maCi$Pz61Hd03|P8k{`?m# z)dEU~{CBUs{%`v+=^xhN&yTHGCd$w9yZPJYqjHCtOhbgD#3mYjEB(AqJMp^K`%g;D zl5;P5FdkKL;f;X5m* ztl$01>Q&Rva35}4_NMf}$nd^hS0eqN6|xI7Hf-?}D^z~rqah}|{XC~p&xvIZZN#d& zLuz_89xPrbr@eVu>BPLn_1wP{O_M(UPO4&5u|BipQL1bMjz{c=RLKU8(SEd8XNd|_Dbm=$Ys@=)z&6#us!Y0@h>-x_`)4)lMEI=Q2V_% z-r|p0(zi{FT>+x+;>#TuTof*2*l!-Cx~cEOs&{(qHH#XrOwijYoHS$m1y;`cU%$ww zr%K7Qikvh*F2k|@Le-b6Z^fB3{g*wM{l`7^I%@!5rRLVj*&p^^Tf4V+#;=)*PAF-Z zS)ai`he;&3arcvyNNfLZH+KH+ Date: Thu, 11 Sep 2008 11:12:21 -0700 Subject: [PATCH 114/139] 6746415: The include file stropts.h is not available in Fedora 9 (STREAMS Kernel Extension?) Reviewed-by: tbell --- .../native/com/sun/media/sound/PLATFORM_API_SolarisOS_Utils.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/solaris/native/com/sun/media/sound/PLATFORM_API_SolarisOS_Utils.h b/src/solaris/native/com/sun/media/sound/PLATFORM_API_SolarisOS_Utils.h index b0d15f85e..675aa68d6 100644 --- a/src/solaris/native/com/sun/media/sound/PLATFORM_API_SolarisOS_Utils.h +++ b/src/solaris/native/com/sun/media/sound/PLATFORM_API_SolarisOS_Utils.h @@ -31,7 +31,9 @@ #include #include #include +#ifndef __linux__ #include +#endif #include #include #include -- GitLab From 8a22a37048a12312b35d2094a5fd7ba78d96ce30 Mon Sep 17 00:00:00 2001 From: xdono Date: Thu, 11 Sep 2008 11:25:53 -0700 Subject: [PATCH 115/139] Added tag jdk7-b35 for changeset cf4894b78ceb --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 88db46731..5a4391f4d 100644 --- a/.hgtags +++ b/.hgtags @@ -9,3 +9,4 @@ b7474b739d13bacd9972f88ac91f6350b7b0be12 jdk7-b31 c51121419e30eac5f0fbbce45ff1711c4ce0de28 jdk7-b32 fa4c0a6cdd25d97d4e6f5d7aa180bcbb0e0d56af jdk7-b33 434055a0716ee44bca712ebca02fc04b20e6e288 jdk7-b34 +cf4894b78ceb966326e93bf221db0c2d14d59218 jdk7-b35 -- GitLab From 0c38c873c90326f87aa8ad6bedf0317bd38b9bef Mon Sep 17 00:00:00 2001 From: bristor Date: Thu, 11 Sep 2008 14:58:57 -0700 Subject: [PATCH 116/139] 6440786: Cannot create a ZIP file containing zero entries Summary: Allow reading and writing of ZIP files with zero entries. Reviewed-by: alanb --- .../java/util/zip/ZipOutputStream.java | 5 +- src/share/native/java/util/zip/zip_util.c | 26 +++- test/java/util/zip/TestEmptyZip.java | 147 ++++++++++++++++++ 3 files changed, 167 insertions(+), 11 deletions(-) create mode 100644 test/java/util/zip/TestEmptyZip.java diff --git a/src/share/classes/java/util/zip/ZipOutputStream.java b/src/share/classes/java/util/zip/ZipOutputStream.java index 653942682..797a37392 100644 --- a/src/share/classes/java/util/zip/ZipOutputStream.java +++ b/src/share/classes/java/util/zip/ZipOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -317,9 +317,6 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { if (current != null) { closeEntry(); } - if (xentries.size() < 1) { - throw new ZipException("ZIP file must have at least one entry"); - } // write central directory long off = written; for (XEntry xentry : xentries) diff --git a/src/share/native/java/util/zip/zip_util.c b/src/share/native/java/util/zip/zip_util.c index 1f6e04a1f..5d518cf4c 100644 --- a/src/share/native/java/util/zip/zip_util.c +++ b/src/share/native/java/util/zip/zip_util.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1995-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -722,16 +722,22 @@ ZIP_Put_In_Cache(const char *name, ZFILE zfd, char **pmsg, jlong lastModified) } len = zip->len = ZFILE_Lseek(zfd, 0, SEEK_END); - if (len == -1) { - if (pmsg && JVM_GetLastErrorString(errbuf, sizeof(errbuf)) > 0) - *pmsg = errbuf; + if (len <= 0) { + if (len == 0) { /* zip file is empty */ + if (pmsg) { + *pmsg = "zip file is empty"; + } + } else { /* error */ + if (pmsg && JVM_GetLastErrorString(errbuf, sizeof(errbuf)) > 0) + *pmsg = errbuf; + } ZFILE_Close(zfd); freeZip(zip); return NULL; } zip->zfd = zfd; - if (readCEN(zip, -1) <= 0) { + if (readCEN(zip, -1) < 0) { /* An error occurred while trying to read the zip file */ if (pmsg != 0) { /* Set the zip error message */ @@ -947,10 +953,15 @@ jzentry * ZIP_GetEntry(jzfile *zip, char *name, jint ulen) { unsigned int hsh = hash(name); - jint idx = zip->table[hsh % zip->tablelen]; - jzentry *ze; + jint idx; + jzentry *ze = 0; ZIP_Lock(zip); + if (zip->total == 0) { + goto Finally; + } + + idx = zip->table[hsh % zip->tablelen]; /* * This while loop is an optimization where a double lookup @@ -1025,6 +1036,7 @@ ZIP_GetEntry(jzfile *zip, char *name, jint ulen) ulen = 0; } +Finally: ZIP_Unlock(zip); return ze; } diff --git a/test/java/util/zip/TestEmptyZip.java b/test/java/util/zip/TestEmptyZip.java new file mode 100644 index 000000000..d19dee4d4 --- /dev/null +++ b/test/java/util/zip/TestEmptyZip.java @@ -0,0 +1,147 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 6334003 6440786 + * @summary Test ability to write and read zip files that have no entries. + * @author Dave Bristor + */ + +import java.io.*; +import java.util.*; +import java.util.zip.*; + +public class TestEmptyZip { + public static void realMain(String[] args) throws Throwable { + String zipName = "foo.zip"; + File f = new File(System.getProperty("test.scratch", "."), zipName); + if (f.exists() && !f.delete()) { + throw new Exception("failed to delete " + zipName); + } + + // Verify 0-length file cannot be read + f.createNewFile(); + ZipFile zf = null; + try { + zf = new ZipFile(f); + fail(); + } catch (Exception ex) { + check(ex.getMessage().contains("zip file is empty")); + } finally { + if (zf != null) { + zf.close(); + } + } + + ZipInputStream zis = null; + try { + zis = new ZipInputStream(new FileInputStream(f)); + ZipEntry ze = zis.getNextEntry(); + check(ze == null); + } catch (Exception ex) { + unexpected(ex); + } finally { + if (zis != null) { + zis.close(); + } + } + + f.delete(); + + // Verify 0-entries file can be written + write(f); + + // Verify 0-entries file can be read + readFile(f); + readStream(f); + + f.delete(); + } + + static void write(File f) throws Exception { + ZipOutputStream zos = null; + try { + zos = new ZipOutputStream(new FileOutputStream(f)); + zos.finish(); + zos.close(); + pass(); + } catch (Exception ex) { + unexpected(ex); + } finally { + if (zos != null) { + zos.close(); + } + } + } + + static void readFile(File f) throws Exception { + ZipFile zf = null; + try { + zf = new ZipFile(f); + + Enumeration e = zf.entries(); + while (e.hasMoreElements()) { + ZipEntry entry = (ZipEntry) e.nextElement(); + fail(); + } + zf.close(); + pass(); + } catch (Exception ex) { + unexpected(ex); + } finally { + if (zf != null) { + zf.close(); + } + } + } + + static void readStream(File f) throws Exception { + ZipInputStream zis = null; + try { + zis = new ZipInputStream(new FileInputStream(f)); + ZipEntry ze = zis.getNextEntry(); + check(ze == null); + byte[] buf = new byte[1024]; + check(zis.read(buf, 0, 1024) == -1); + } finally { + if (zis != null) { + zis.close(); + } + } + } + + //--------------------- Infrastructure --------------------------- + static volatile int passed = 0, failed = 0; + static boolean pass() {passed++; return true;} + static boolean fail() {failed++; Thread.dumpStack(); return false;} + static boolean fail(String msg) {System.out.println(msg); return fail();} + static void unexpected(Throwable t) {failed++; t.printStackTrace();} + static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;} + static boolean equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) return pass(); + else return fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + try {realMain(args);} catch (Throwable t) {unexpected(t);} + System.out.println("\nPassed = " + passed + " failed = " + failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} -- GitLab From 00a90a93a1f0069a752c4730c9b03ea462748366 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Fri, 12 Sep 2008 15:17:52 +0200 Subject: [PATCH 117/139] 6747411: EventClient causes thread leaks Summary: Reworked thread management in EventClient and related classes. Reviewed-by: sjiang, dfuchs --- .../com/sun/jmx/event/LeaseManager.java | 11 +- .../sun/jmx/event/RepeatedSingletonJob.java | 4 +- .../internal/ClientCommunicatorAdmin.java | 4 +- .../javax/management/event/EventClient.java | 5 +- .../management/event/FetchingEventRelay.java | 52 +++--- .../event/RMIPushEventForwarder.java | 2 +- .../management/remote/rmi/RMIConnector.java | 2 +- .../eventService/EventClientThreadTest.java | 176 ++++++++++++++++++ .../eventService/SharingThreadTest.java | 2 +- 9 files changed, 220 insertions(+), 38 deletions(-) create mode 100644 test/javax/management/eventService/EventClientThreadTest.java diff --git a/src/share/classes/com/sun/jmx/event/LeaseManager.java b/src/share/classes/com/sun/jmx/event/LeaseManager.java index cb1b88bf5..33409a06c 100644 --- a/src/share/classes/com/sun/jmx/event/LeaseManager.java +++ b/src/share/classes/com/sun/jmx/event/LeaseManager.java @@ -27,7 +27,6 @@ package com.sun.jmx.event; import com.sun.jmx.remote.util.ClassLogger; import java.util.concurrent.Executors; -import java.util.concurrent.FutureTask; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -115,6 +114,7 @@ public class LeaseManager { scheduled = null; } callback.run(); + executor.shutdown(); } } @@ -131,6 +131,13 @@ public class LeaseManager { logger.trace("stop", "canceling lease"); scheduled.cancel(false); scheduled = null; + try { + executor.shutdown(); + } catch (SecurityException e) { + // OK: caller doesn't have RuntimePermission("modifyThread") + // which is unlikely in reality but triggers a test failure otherwise + logger.trace("stop", "exception from executor.shutdown", e); + } } private final Runnable callback; @@ -138,7 +145,7 @@ public class LeaseManager { private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, - new DaemonThreadFactory("LeaseManager")); + new DaemonThreadFactory("JMX LeaseManager %d")); private static final ClassLogger logger = new ClassLogger("javax.management.event", "LeaseManager"); diff --git a/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java b/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java index 7de1b40e9..2fe4a3a15 100644 --- a/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java +++ b/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java @@ -95,7 +95,9 @@ public abstract class RepeatedSingletonJob implements Runnable { executor.execute(this); } catch (RejectedExecutionException e) { logger.warning( - "setEventReceiver", "Executor threw exception", e); + "execute", + "Executor threw exception (" + this.getClass().getName() + ")", + e); throw new RejectedExecutionException( "Executor.execute threw exception -" + "should not be possible", e); diff --git a/src/share/classes/com/sun/jmx/remote/internal/ClientCommunicatorAdmin.java b/src/share/classes/com/sun/jmx/remote/internal/ClientCommunicatorAdmin.java index a6635aad8..f90cbc4c5 100644 --- a/src/share/classes/com/sun/jmx/remote/internal/ClientCommunicatorAdmin.java +++ b/src/share/classes/com/sun/jmx/remote/internal/ClientCommunicatorAdmin.java @@ -32,13 +32,15 @@ import com.sun.jmx.remote.util.ClassLogger; import com.sun.jmx.remote.util.EnvHelp; public abstract class ClientCommunicatorAdmin { + private static volatile long threadNo = 1; + public ClientCommunicatorAdmin(long period) { this.period = period; if (period > 0) { checker = new Checker(); - Thread t = new Thread(checker); + Thread t = new Thread(checker, "JMX client heartbeat " + ++threadNo); t.setDaemon(true); t.start(); } else diff --git a/src/share/classes/javax/management/event/EventClient.java b/src/share/classes/javax/management/event/EventClient.java index 4b8101353..10a4df500 100644 --- a/src/share/classes/javax/management/event/EventClient.java +++ b/src/share/classes/javax/management/event/EventClient.java @@ -264,11 +264,12 @@ public class EventClient implements EventConsumer, NotificationManager { new PerThreadGroupPool.Create() { public ScheduledThreadPoolExecutor createThreadPool(ThreadGroup group) { ThreadFactory daemonThreadFactory = new DaemonThreadFactory( - "EventClient lease renewer %d"); + "JMX EventClient lease renewer %d"); ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor( 20, daemonThreadFactory); - exec.setKeepAliveTime(3, TimeUnit.SECONDS); + exec.setKeepAliveTime(1, TimeUnit.SECONDS); exec.allowCoreThreadTimeOut(true); + exec.setRemoveOnCancelPolicy(true); return exec; } }; diff --git a/src/share/classes/javax/management/event/FetchingEventRelay.java b/src/share/classes/javax/management/event/FetchingEventRelay.java index 2b65f9b12..9aa68df0f 100644 --- a/src/share/classes/javax/management/event/FetchingEventRelay.java +++ b/src/share/classes/javax/management/event/FetchingEventRelay.java @@ -31,10 +31,8 @@ import com.sun.jmx.remote.util.ClassLogger; import java.io.IOException; import java.io.NotSerializableException; import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import javax.management.MBeanException; @@ -215,50 +213,47 @@ public class FetchingEventRelay implements EventRelay { this.maxNotifs = maxNotifs; if (executor == null) { - executor = Executors.newSingleThreadScheduledExecutor( + ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(1, daemonThreadFactory); - } + stpe.setKeepAliveTime(1, TimeUnit.SECONDS); + stpe.allowCoreThreadTimeOut(true); + executor = stpe; + this.defaultExecutor = stpe; + } else + this.defaultExecutor = null; this.executor = executor; - if (executor instanceof ScheduledExecutorService) - leaseScheduler = (ScheduledExecutorService) executor; - else { - leaseScheduler = Executors.newSingleThreadScheduledExecutor( - daemonThreadFactory); - } startSequenceNumber = 0; fetchingJob = new MyJob(); } - public void setEventReceiver(EventReceiver eventReceiver) { + public synchronized void setEventReceiver(EventReceiver eventReceiver) { if (logger.traceOn()) { logger.trace("setEventReceiver", ""+eventReceiver); } EventReceiver old = this.eventReceiver; - synchronized(fetchingJob) { - this.eventReceiver = eventReceiver; - if (old == null && eventReceiver != null) - fetchingJob.resume(); - } + this.eventReceiver = eventReceiver; + if (old == null && eventReceiver != null) + fetchingJob.resume(); } public String getClientId() { return clientId; } - public void stop() { + public synchronized void stop() { if (logger.traceOn()) { logger.trace("stop", ""); } - synchronized(fetchingJob) { - if (stopped) { - return; - } - - stopped = true; - clientId = null; + if (stopped) { + return; } + + stopped = true; + clientId = null; + if (defaultExecutor != null) + defaultExecutor.shutdown(); } private class MyJob extends RepeatedSingletonJob { @@ -372,10 +367,9 @@ public class FetchingEventRelay implements EventRelay { private final EventClientDelegateMBean delegate; private String clientId; private boolean stopped = false; - private volatile ScheduledFuture leaseRenewalFuture; private final Executor executor; - private final ScheduledExecutorService leaseScheduler; + private final ExecutorService defaultExecutor; private final MyJob fetchingJob; private final long timeout; @@ -385,5 +379,5 @@ public class FetchingEventRelay implements EventRelay { new ClassLogger("javax.management.event", "FetchingEventRelay"); private static final ThreadFactory daemonThreadFactory = - new DaemonThreadFactory("FetchingEventRelay-executor"); + new DaemonThreadFactory("JMX FetchingEventRelay executor %d"); } diff --git a/src/share/classes/javax/management/event/RMIPushEventForwarder.java b/src/share/classes/javax/management/event/RMIPushEventForwarder.java index 2018f98ad..751300d54 100644 --- a/src/share/classes/javax/management/event/RMIPushEventForwarder.java +++ b/src/share/classes/javax/management/event/RMIPushEventForwarder.java @@ -185,7 +185,7 @@ public class RMIPushEventForwarder implements EventForwarder { private static final ExecutorService executor = Executors.newCachedThreadPool( - new DaemonThreadFactory("RMIEventForwarder Executor")); + new DaemonThreadFactory("JMX RMIEventForwarder Executor")); private final SendingJob sendingJob = new SendingJob(); private final BlockingQueue buffer; diff --git a/src/share/classes/javax/management/remote/rmi/RMIConnector.java b/src/share/classes/javax/management/remote/rmi/RMIConnector.java index bdcbb1568..a620235ac 100644 --- a/src/share/classes/javax/management/remote/rmi/RMIConnector.java +++ b/src/share/classes/javax/management/remote/rmi/RMIConnector.java @@ -420,7 +420,7 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable new PerThreadGroupPool.Create() { public ThreadPoolExecutor createThreadPool(ThreadGroup group) { ThreadFactory daemonThreadFactory = new DaemonThreadFactory( - "RMIConnector listener dispatch %d"); + "JMX RMIConnector listener dispatch %d"); ThreadPoolExecutor exec = new ThreadPoolExecutor( 1, 10, 1, TimeUnit.SECONDS, new LinkedBlockingDeque(), diff --git a/test/javax/management/eventService/EventClientThreadTest.java b/test/javax/management/eventService/EventClientThreadTest.java new file mode 100644 index 000000000..910bc9cc2 --- /dev/null +++ b/test/javax/management/eventService/EventClientThreadTest.java @@ -0,0 +1,176 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6747411 + * @summary Check that EventClient instances don't leak threads. + * @author Eamonn McManus + */ + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.Set; +import java.util.TreeSet; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerDelegate; +import javax.management.MBeanServerNotification; +import javax.management.Notification; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.event.EventClient; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +public class EventClientThreadTest { + private static final int MAX_TIME_SECONDS = 20; + + private static final BlockingQueue queue = + new ArrayBlockingQueue(100); + + private static final NotificationListener queueListener = + new NotificationListener() { + public void handleNotification(Notification notification, + Object handback) { + queue.add(notification); + } + }; + + private static final NotificationFilter dummyFilter = + new NotificationFilter() { + public boolean isNotificationEnabled(Notification notification) { + return true; + } + }; + + public static void main(String[] args) throws Exception { + long start = System.currentTimeMillis(); + long deadline = start + MAX_TIME_SECONDS * 1000; + + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://"); + JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer( + url, null, mbs); + cs.start(); + JMXServiceURL addr = cs.getAddress(); + JMXConnector cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + + ThreadMXBean threads = ManagementFactory.getThreadMXBean(); + + System.out.println("Opening and closing some EventClients..."); + // If we create a connection, then create and destroy EventClients + // over it, then close it, there should be no "JMX *" threads left. + for (int i = 0; i < 5; i++) + test(mbsc); + + cc.close(); + + showTime("opening and closing initial EventClients", start); + + Set jmxThreads = threadsMatching("JMX .*"); + while (!jmxThreads.isEmpty() && System.currentTimeMillis() < deadline) { + Set jmxThreadsNow = threadsMatching("JMX .*"); + Set gone = new TreeSet(jmxThreads); + gone.removeAll(jmxThreadsNow); + for (String s : gone) + showTime("expiry of \"" + s + "\"", start); + jmxThreads = jmxThreadsNow; + Thread.sleep(10); + } + if (System.currentTimeMillis() >= deadline) { + showThreads(threads); + throw new Exception("Timed out waiting for JMX threads to expire"); + } + + showTime("waiting for JMX threads to expire", start); + + System.out.println("TEST PASSED"); + } + + static void showThreads(ThreadMXBean threads) throws Exception { + long[] ids = threads.getAllThreadIds(); + for (long id : ids) { + ThreadInfo ti = threads.getThreadInfo(id); + String name = (ti == null) ? "(defunct)" : ti.getThreadName(); + System.out.printf("%4d %s\n", id, name); + } + } + + static void showTime(String what, long start) { + long elapsed = System.currentTimeMillis() - start; + System.out.printf("Time after %s: %.3f s\n", what, elapsed / 1000.0); + } + + static Set threadsMatching(String pattern) { + Set matching = new TreeSet(); + ThreadMXBean threads = ManagementFactory.getThreadMXBean(); + long[] ids = threads.getAllThreadIds(); + for (long id : ids) { + ThreadInfo ti = threads.getThreadInfo(id); + String name = (ti == null) ? "(defunct)" : ti.getThreadName(); + if (name.matches(pattern)) + matching.add(name); + } + return matching; + } + + static void test(MBeanServerConnection mbsc) throws Exception { + final ObjectName delegateName = MBeanServerDelegate.DELEGATE_NAME; + final ObjectName testName = new ObjectName("test:type=Test"); + EventClient ec = new EventClient(mbsc); + ec.addNotificationListener(delegateName, queueListener, null, null); + mbsc.createMBean(MBeanServerDelegate.class.getName(), testName); + mbsc.unregisterMBean(testName); + final String[] expectedTypes = { + MBeanServerNotification.REGISTRATION_NOTIFICATION, + MBeanServerNotification.UNREGISTRATION_NOTIFICATION, + }; + for (String s : expectedTypes) { + Notification n = queue.poll(3, TimeUnit.SECONDS); + if (n == null) + throw new Exception("Timed out waiting for notif: " + s); + if (!(n instanceof MBeanServerNotification)) + throw new Exception("Got notif of wrong class: " + n.getClass()); + if (!n.getType().equals(s)) { + throw new Exception("Got notif of wrong type: " + n.getType() + + " (expecting " + s + ")"); + } + } + ec.removeNotificationListener(delegateName, queueListener); + + ec.addNotificationListener(delegateName, queueListener, dummyFilter, "foo"); + ec.removeNotificationListener(delegateName, queueListener, dummyFilter, "foo"); + + ec.close(); + } +} \ No newline at end of file diff --git a/test/javax/management/eventService/SharingThreadTest.java b/test/javax/management/eventService/SharingThreadTest.java index a3d7fd37a..7339d0806 100644 --- a/test/javax/management/eventService/SharingThreadTest.java +++ b/test/javax/management/eventService/SharingThreadTest.java @@ -1,4 +1,4 @@ -/*/* +/* * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * -- GitLab From 672a58360c973dce9ee2b9bf33082207325407cf Mon Sep 17 00:00:00 2001 From: dfuchs Date: Fri, 12 Sep 2008 17:58:15 +0200 Subject: [PATCH 118/139] 6747899: jmx namespaces: hooks for permission checks should be defined in HandlerInterceptor Reviewed-by: emcmanus --- .../sun/jmx/namespace/HandlerInterceptor.java | 163 +++++++++++++++++- .../RoutingMBeanServerConnection.java | 162 ++--------------- 2 files changed, 174 insertions(+), 151 deletions(-) diff --git a/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java b/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java index 7c2f39348..566724419 100644 --- a/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java +++ b/src/share/classes/com/sun/jmx/namespace/HandlerInterceptor.java @@ -135,7 +135,11 @@ public abstract class HandlerInterceptor public AttributeList getAttributes(ObjectName name, String[] attributes) throws InstanceNotFoundException, ReflectionException { try { - return super.getAttributes(name, attributes); + final String[] authorized = + checkAttributes(name,attributes,"getAttribute"); + final AttributeList attrList = + super.getAttributes(name,authorized); + return attrList; } catch (IOException ex) { throw handleIOException(ex,"getAttributes",name,attributes); } @@ -185,7 +189,8 @@ public abstract class HandlerInterceptor public void removeNotificationListener(ObjectName name, ObjectName listener) throws InstanceNotFoundException, ListenerNotFoundException { try { - super.removeNotificationListener(name, listener); + check(name,null,"removeNotificationListener"); + super.removeNotificationListener(name,listener); } catch (IOException ex) { throw handleIOException(ex,"removeNotificationListener",name,listener); } @@ -205,7 +210,9 @@ public abstract class HandlerInterceptor @Override public String[] getDomains() { try { - return super.getDomains(); + check(null,null,"getDomains"); + final String[] domains = super.getDomains(); + return checkDomains(domains,"getDomains"); } catch (IOException ex) { throw handleIOException(ex,"getDomains"); } @@ -228,7 +235,10 @@ public abstract class HandlerInterceptor InvalidAttributeValueException, MBeanException, ReflectionException { try { - super.setAttribute(name, attribute); + check(name, + (attribute==null?null:attribute.getName()), + "setAttribute"); + super.setAttribute(name,attribute); } catch (IOException ex) { throw handleIOException(ex,"setAttribute",name, attribute); } @@ -237,8 +247,10 @@ public abstract class HandlerInterceptor // From MBeanServerConnection: catch & handles IOException @Override public Set queryNames(ObjectName name, QueryExp query) { + if (name == null) name=ObjectName.WILDCARD; try { - return super.queryNames(name, query); + checkPattern(name,null,"queryNames"); + return super.queryNames(name,query); } catch (IOException ex) { throw handleIOException(ex,"queryNames",name, query); } @@ -247,8 +259,10 @@ public abstract class HandlerInterceptor // From MBeanServerConnection: catch & handles IOException @Override public Set queryMBeans(ObjectName name, QueryExp query) { + if (name == null) name=ObjectName.WILDCARD; try { - return super.queryMBeans(name, query); + checkPattern(name,null,"queryMBeans"); + return super.queryMBeans(name,query); } catch (IOException ex) { throw handleIOException(ex,"queryMBeans",name, query); } @@ -259,6 +273,7 @@ public abstract class HandlerInterceptor public boolean isInstanceOf(ObjectName name, String className) throws InstanceNotFoundException { try { + check(name, null, "isInstanceOf"); return super.isInstanceOf(name, className); } catch (IOException ex) { throw handleIOException(ex,"isInstanceOf",name, className); @@ -272,6 +287,8 @@ public abstract class HandlerInterceptor MBeanRegistrationException, MBeanException, NotCompliantMBeanException { try { + checkCreate(name, className, "instantiate"); + checkCreate(name, className, "registerMBean"); return super.createMBean(className, name); } catch (IOException ex) { throw handleIOException(ex,"createMBean",className, name); @@ -286,6 +303,8 @@ public abstract class HandlerInterceptor MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException { try { + checkCreate(name, className, "instantiate"); + checkCreate(name, className, "registerMBean"); return super.createMBean(className, name, loaderName); } catch (IOException ex) { throw handleIOException(ex,"createMBean",className, name, loaderName); @@ -298,6 +317,7 @@ public abstract class HandlerInterceptor throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException { try { + check(name, attribute, "getAttribute"); return super.getAttribute(name, attribute); } catch (IOException ex) { throw handleIOException(ex,"getAttribute",name, attribute); @@ -310,6 +330,7 @@ public abstract class HandlerInterceptor NotificationFilter filter, Object handback) throws InstanceNotFoundException, ListenerNotFoundException { try { + check(name,null,"removeNotificationListener"); super.removeNotificationListener(name, listener, filter, handback); } catch (IOException ex) { throw handleIOException(ex,"removeNotificationListener",name, @@ -324,6 +345,7 @@ public abstract class HandlerInterceptor Object handback) throws InstanceNotFoundException, ListenerNotFoundException { try { + check(name,null,"removeNotificationListener"); super.removeNotificationListener(name, listener, filter, handback); } catch (IOException ex) { throw handleIOException(ex,"removeNotificationListener",name, @@ -337,6 +359,7 @@ public abstract class HandlerInterceptor NotificationListener listener) throws InstanceNotFoundException, ListenerNotFoundException { try { + check(name,null,"removeNotificationListener"); super.removeNotificationListener(name, listener); } catch (IOException ex) { throw handleIOException(ex,"removeNotificationListener",name, @@ -350,6 +373,7 @@ public abstract class HandlerInterceptor NotificationListener listener, NotificationFilter filter, Object handback) throws InstanceNotFoundException { try { + check(name,null,"addNotificationListener"); super.addNotificationListener(name, listener, filter, handback); } catch (IOException ex) { throw handleIOException(ex,"addNotificationListener",name, @@ -363,6 +387,7 @@ public abstract class HandlerInterceptor NotificationFilter filter, Object handback) throws InstanceNotFoundException { try { + check(name,null,"addNotificationListener"); super.addNotificationListener(name, listener, filter, handback); } catch (IOException ex) { throw handleIOException(ex,"addNotificationListener",name, @@ -385,6 +410,7 @@ public abstract class HandlerInterceptor public void unregisterMBean(ObjectName name) throws InstanceNotFoundException, MBeanRegistrationException { try { + check(name, null, "unregisterMBean"); super.unregisterMBean(name); } catch (IOException ex) { throw handleIOException(ex,"unregisterMBean",name); @@ -397,6 +423,7 @@ public abstract class HandlerInterceptor throws InstanceNotFoundException, IntrospectionException, ReflectionException { try { + check(name, null, "getMBeanInfo"); return super.getMBeanInfo(name); } catch (IOException ex) { throw handleIOException(ex,"getMBeanInfo",name); @@ -408,6 +435,7 @@ public abstract class HandlerInterceptor public ObjectInstance getObjectInstance(ObjectName name) throws InstanceNotFoundException { try { + check(name, null, "getObjectInstance"); return super.getObjectInstance(name); } catch (IOException ex) { throw handleIOException(ex,"getObjectInstance",name); @@ -422,6 +450,8 @@ public abstract class HandlerInterceptor MBeanRegistrationException, MBeanException, NotCompliantMBeanException { try { + checkCreate(name, className, "instantiate"); + checkCreate(name, className, "registerMBean"); return super.createMBean(className, name, params, signature); } catch (IOException ex) { throw handleIOException(ex,"createMBean",className, name, @@ -437,6 +467,8 @@ public abstract class HandlerInterceptor MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException { try { + checkCreate(name, className, "instantiate"); + checkCreate(name, className, "registerMBean"); return super.createMBean(className, name, loaderName, params, signature); } catch (IOException ex) { @@ -450,7 +482,9 @@ public abstract class HandlerInterceptor public AttributeList setAttributes(ObjectName name,AttributeList attributes) throws InstanceNotFoundException, ReflectionException { try { - return super.setAttributes(name, attributes); + final AttributeList authorized = + checkAttributes(name, attributes, "setAttribute"); + return super.setAttributes(name, authorized); } catch (IOException ex) { throw handleIOException(ex,"setAttributes",name, attributes); } @@ -462,6 +496,7 @@ public abstract class HandlerInterceptor String[] signature) throws InstanceNotFoundException, MBeanException, ReflectionException { try { + check(name, operationName, "invoke"); return super.invoke(name, operationName, params, signature); } catch (IOException ex) { throw handleIOException(ex,"invoke",name, operationName, @@ -582,4 +617,118 @@ public abstract class HandlerInterceptor "Not supported in this namespace: "+namespace)); } + /** + * A result might be excluded for security reasons. + */ + @Override + boolean excludesFromResult(ObjectName targetName, String queryMethod) { + return !checkQuery(targetName, queryMethod); + } + + + //---------------------------------------------------------------------- + // Hooks for checking permissions + //---------------------------------------------------------------------- + + /** + * This method is a hook to implement permission checking in subclasses. + * A subclass may override this method and throw a {@link + * SecurityException} if the permission is denied. + * + * @param routingName The name of the MBean in the enclosing context. + * This is of the form {@code //}. + * @param member The {@link + * javax.management.namespace.JMXNamespacePermission#getMember member} + * name. + * @param action The {@link + * javax.management.namespace.JMXNamespacePermission#getActions action} + * name. + * @throws SecurityException if the caller doesn't have the permission + * to perform the given action on the MBean pointed to + * by routingName. + */ + abstract void check(ObjectName routingName, + String member, String action); + + // called in createMBean and registerMBean + abstract void checkCreate(ObjectName routingName, String className, + String action); + + /** + * This is a hook to implement permission checking in subclasses. + * + * Checks that the caller has sufficient permission for returning + * information about {@code sourceName} in {@code action}. + * + * Subclass may override this method and return false if the caller + * doesn't have sufficient permissions. + * + * @param routingName The name of the MBean to include or exclude from + * the query, expressed in the enclosing context. + * This is of the form {@code //}. + * @param action one of "queryNames" or "queryMBeans" + * @return true if {@code sourceName} can be returned. + */ + abstract boolean checkQuery(ObjectName routingName, String action); + + /** + * This method is a hook to implement permission checking in subclasses. + * + * @param routingName The name of the MBean in the enclosing context. + * This is of the form {@code //}. + * @param attributes The list of attributes to check permission for. + * @param action one of "getAttribute" or "setAttribute" + * @return The list of attributes for which the callers has the + * appropriate {@link + * javax.management.namespace.JMXNamespacePermission}. + * @throws SecurityException if the caller doesn't have the permission + * to perform {@code action} on the MBean pointed to by routingName. + */ + abstract String[] checkAttributes(ObjectName routingName, + String[] attributes, String action); + + /** + * This method is a hook to implement permission checking in subclasses. + * + * @param routingName The name of the MBean in the enclosing context. + * This is of the form {@code //}. + * @param attributes The list of attributes to check permission for. + * @param action one of "getAttribute" or "setAttribute" + * @return The list of attributes for which the callers has the + * appropriate {@link + * javax.management.namespace.JMXNamespacePermission}. + * @throws SecurityException if the caller doesn't have the permission + * to perform {@code action} on the MBean pointed to by routingName. + */ + abstract AttributeList checkAttributes(ObjectName routingName, + AttributeList attributes, String action); + + /** + * This method is a hook to implement permission checking in subclasses. + * Checks that the caller as the necessary permissions to view the + * given domain. If not remove the domains for which the caller doesn't + * have permission from the list. + *

      + * By default, this method always returns {@code domains} + * + * @param domains The domains to return. + * @param action "getDomains" + * @return a filtered list of domains. + */ + String[] checkDomains(String[] domains, String action) { + return domains; + } + + // A priori check for queryNames/queryMBeans/ + void checkPattern(ObjectName routingPattern, + String member, String action) { + // pattern is checked only at posteriori by checkQuery. + // checking it a priori usually doesn't work, because ObjectName.apply + // does not work between two patterns. + // We only check that we have the permission requested for 'action'. + check(null,null,action); + } + + + } diff --git a/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java b/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java index 70df9b504..7022e7e29 100644 --- a/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java +++ b/src/share/classes/com/sun/jmx/namespace/RoutingMBeanServerConnection.java @@ -161,11 +161,7 @@ public abstract class RoutingMBeanServerConnection tmp = source().queryNames(sourceName,query); final Set out = processOutputNames(tmp); //System.err.println("queryNames: out: "+out); @@ -467,7 +442,6 @@ public abstract class RoutingMBeanServerConnection//}. - * @param attributes The list of attributes to check permission for. - * @param action one of "getAttribute" or "setAttribute" - * @return The list of attributes for which the callers has the - * appropriate {@link - * javax.management.namespace.JMXNamespacePermission}. - */ - String[] checkAttributes(ObjectName routingName, - String[] attributes, String action) { - check(routingName,null,action); - return attributes; - } - - /** - * This method is a hook to implement permission checking in subclasses. - * By default, this method does nothing and simply returns - * {@code attribute}. - * - * @param routingName The name of the MBean in the enclosing context. - * This is of the form {@code //}. - * @param attributes The list of attributes to check permission for. - * @param action one of "getAttribute" or "setAttribute" - * @return The list of attributes for which the callers has the - * appropriate {@link - * javax.management.namespace.JMXNamespacePermission}. - */ - AttributeList checkAttributes(ObjectName routingName, - AttributeList attributes, String action) { - check(routingName,null,action); - return attributes; - } - - /** - * This method is a hook to implement permission checking in subclasses. - * By default, this method does nothing. - * A subclass may override this method and throw a {@link - * SecurityException} if the permission is denied. - * - * @param routingName The name of the MBean in the enclosing context. - * This is of the form {@code //}. - * @param member The {@link - * javax.management.namespace.JMXNamespacePermission#getMember member} - * name. - * @param action The {@link - * javax.management.namespace.JMXNamespacePermission#getActions action} - * name. - */ - void check(ObjectName routingName, - String member, String action) { - } - - // called in createMBean and registerMBean - void checkCreate(ObjectName routingName, String className, - String action) { - } - - // A priori check for queryNames/queryMBeans/ - void checkPattern(ObjectName routingPattern, - String member, String action) { - // pattern is checked only at posteriori by checkQuery. - // checking it a priori usually doesn't work, because ObjectName.apply - // does not work between two patterns. - // We only check that we have the permission requested for 'action'. - check(null,null,action); - } - - /** - * This is a hook to implement permission checking in subclasses. + * Returns true if the given targetName must be excluded from the + * query result. + * In this base class, always return {@code false}. + * By default all object names returned by the sources are + * transmitted to the caller - there is no filtering. * - * Checks that the caller has sufficient permission for returning - * information about {@code sourceName} in {@code action}. - * - * By default always return true. Subclass may override this method - * and return false if the caller doesn't have sufficient permissions. - * - * @param routingName The name of the MBean to include or exclude from - * the query, expressed in the enclosing context. - * This is of the form {@code //}. - * @param action one of "queryNames" or "queryMBeans" - * @return true if {@code sourceName} can be returned. + * @param name A target object name expressed in the caller's + * context. In the case of cascading, where the source + * is a sub agent mounted on e.g. namespace "foo", + * that would be a name prefixed by "foo//"... + * @param queryMethod either "queryNames" or "queryMBeans". + * @return true if the name must be excluded. */ - boolean checkQuery(ObjectName routingName, String action) { - return true; + boolean excludesFromResult(ObjectName targetName, String queryMethod) { + return false; } - /** - * This method is a hook to implement permission checking in subclasses. - * Checks that the caller as the necessary permissions to view the - * given domain. If not remove the domains for which the caller doesn't - * have permission from the list. - *

      - * By default, this method always returns {@code domains} - * - * @param domains The domains to return. - * @param action "getDomains" - * @return a filtered list of domains. - */ - String[] checkDomains(String[] domains, String action) { - return domains; - } } -- GitLab From ca1683b0bc47b9d1cb40f54c0466c21188297d90 Mon Sep 17 00:00:00 2001 From: dfuchs Date: Fri, 12 Sep 2008 19:06:38 +0200 Subject: [PATCH 119/139] 6747983: jmx namespace: unspecified self-link detection logic Reviewed-by: emcmanus --- .../jmx/namespace/NamespaceInterceptor.java | 226 +------------- .../namespace/JMXRemoteNamespace.java | 112 +------ .../namespace/JMXNamespaceTest.java | 278 +++--------------- 3 files changed, 42 insertions(+), 574 deletions(-) diff --git a/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java b/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java index 11afeb2af..6862066d2 100644 --- a/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java +++ b/src/share/classes/com/sun/jmx/namespace/NamespaceInterceptor.java @@ -25,22 +25,15 @@ package com.sun.jmx.namespace; import com.sun.jmx.defaults.JmxProperties; -import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.Set; -import java.util.UUID; import java.util.logging.Logger; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.MBeanServer; -import javax.management.MBeanServerConnection; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; -import javax.management.QueryExp; -import javax.management.namespace.JMXNamespaces; import javax.management.namespace.JMXNamespace; import javax.management.namespace.JMXNamespacePermission; @@ -54,8 +47,6 @@ import javax.management.namespace.JMXNamespacePermission; */ public class NamespaceInterceptor extends HandlerInterceptor { - private static final Logger PROBE_LOG = Logger.getLogger( - JmxProperties.NAMESPACE_LOGGER+".probe"); // The target name space in which the NamepsaceHandler is mounted. private final String targetNs; @@ -64,21 +55,6 @@ public class NamespaceInterceptor extends HandlerInterceptor { private final ObjectNameRouter proc; - /** - * Internal hack. The JMXRemoteNamespace can be closed and reconnected. - * Each time the JMXRemoteNamespace connects, a probe should be sent - * to detect cycle. The MBeanServer exposed by JMXRemoteNamespace thus - * implements the DynamicProbe interface, which makes it possible for - * this handler to know that it should send a new probe. - * - * XXX: TODO this probe thing is way too complex and fragile. - * This *must* go away or be replaced by something simpler. - * ideas are welcomed. - **/ - public static interface DynamicProbe { - public boolean isProbeRequested(); - } - /** * Creates a new instance of NamespaceInterceptor */ @@ -100,164 +76,6 @@ public class NamespaceInterceptor extends HandlerInterceptor { ", namespace="+this.targetNs+")"; } - /* - * XXX: TODO this probe thing is way too complex and fragile. - * This *must* go away or be replaced by something simpler. - * ideas are welcomed. - */ - private volatile boolean probed = false; - private volatile ObjectName probe; - - // Query Pattern that we will send through the source server in order - // to detect self-linking namespaces. - // - // XXX: TODO this probe thing is way too complex and fragile. - // This *must* go away or be replaced by something simpler. - // ideas are welcomed. - final ObjectName makeProbePattern(ObjectName probe) - throws MalformedObjectNameException { - - // we could probably link the probe pattern with the probe - e.g. - // using the UUID as key in the pattern - but is it worth it? it - // also has some side effects on the context namespace - because - // such a probe may get rejected by the jmx.context// namespace. - // - // The trick here is to devise a pattern that is not likely to - // be blocked by intermediate levels. Querying for all namespace - // handlers in the source (or source namespace) is more likely to - // achieve this goal. - // - return ObjectName.getInstance("*" + - JMXNamespaces.NAMESPACE_SEPARATOR + ":" + - JMXNamespace.TYPE_ASSIGNMENT); - } - - // tell whether the name pattern corresponds to what might have been - // sent as a probe. - // XXX: TODO this probe thing is way too complex and fragile. - // This *must* go away or be replaced by something simpler. - // ideas are welcomed. - final boolean isProbePattern(ObjectName name) { - final ObjectName p = probe; - if (p == null) return false; - try { - return String.valueOf(name).endsWith(targetNs+ - JMXNamespaces.NAMESPACE_SEPARATOR + "*" + - JMXNamespaces.NAMESPACE_SEPARATOR + ":" + - JMXNamespace.TYPE_ASSIGNMENT); - } catch (RuntimeException x) { - // should not happen. - PROBE_LOG.finest("Ignoring unexpected exception in self link detection: "+ - x); - return false; - } - } - - // The first time a request reaches this NamespaceInterceptor, the - // interceptor will send a probe to detect whether the underlying - // JMXNamespace links to itslef. - // - // One way to create such self-linking namespace would be for instance - // to create a JMXNamespace whose getSourceServer() method would return: - // JMXNamespaces.narrowToNamespace(getMBeanServer(), - // getObjectName().getDomain()) - // - // If such an MBeanServer is returned, then any call to that MBeanServer - // will trigger an infinite loop. - // There can be even trickier configurations if remote connections are - // involved. - // - // In order to prevent this from happening, the NamespaceInterceptor will - // send a probe, in an attempt to detect whether it will receive it at - // the other end. If the probe is received, an exception will be thrown - // in order to break the recursion. The probe is only sent once - when - // the first request to the namespace occurs. The DynamicProbe interface - // can also be used by a Sun JMXNamespace implementation to request the - // emission of a probe at any time (see JMXRemoteNamespace - // implementation). - // - // Probes work this way: the NamespaceInterceptor sets a flag and sends - // a queryNames() request. If a queryNames() request comes in when the flag - // is on, then it deduces that there is a self-linking loop - and instead - // of calling queryNames() on the source MBeanServer of the JMXNamespace - // handler (which would cause the loop to go on) it breaks the recursion - // by returning the probe ObjectName. - // If the NamespaceInterceptor receives the probe ObjectName as result of - // its original sendProbe() request it knows that it has been looping - // back on itslef and throws an IOException... - // - // - // XXX: TODO this probe thing is way too complex and fragile. - // This *must* go away or be replaced by something simpler. - // ideas are welcomed. - // - final void sendProbe(MBeanServerConnection msc) - throws IOException { - try { - PROBE_LOG.fine("Sending probe"); - - // This is just to prevent any other thread to modify - // the probe while the detection cycle is in progress. - // - final ObjectName probePattern; - // we don't want to synchronize on this - we use targetNs - // because it's non null and final. - synchronized (targetNs) { - probed = false; - if (probe != null) { - throw new IOException("concurent connection in progress"); - } - final String uuid = UUID.randomUUID().toString(); - final String endprobe = - JMXNamespaces.NAMESPACE_SEPARATOR + uuid + - ":type=Probe,key="+uuid; - final ObjectName newprobe = - ObjectName.getInstance(endprobe); - probePattern = makeProbePattern(newprobe); - probe = newprobe; - } - - try { - PROBE_LOG.finer("Probe query: "+probePattern+" expecting: "+probe); - final Set res = msc.queryNames(probePattern, null); - final ObjectName expected = probe; - PROBE_LOG.finer("Probe res: "+res); - if (res.contains(expected)) { - throw new IOException("namespace " + - targetNs + " is linking to itself: " + - "cycle detected by probe"); - } - } catch (SecurityException x) { - PROBE_LOG.finer("Can't check for cycles: " + x); - // can't do anything.... - } catch (RuntimeException x) { - PROBE_LOG.finer("Exception raised by queryNames: " + x); - throw x; - } finally { - probe = null; - } - } catch (MalformedObjectNameException x) { - final IOException io = - new IOException("invalid name space: probe failed"); - io.initCause(x); - throw io; - } - PROBE_LOG.fine("Probe returned - no cycles"); - probed = true; - } - - // allows a Sun implementation JMX Namespace, such as the - // JMXRemoteNamespace, to control when a probe should be sent. - // - // XXX: TODO this probe thing is way too complex and fragile. - // This *must* go away or be replaced by something simpler. - // ideas are welcomed. - private boolean isProbeRequested(Object o) { - if (o instanceof DynamicProbe) - return ((DynamicProbe)o).isProbeRequested(); - return false; - } - /** * This method will send a probe to detect self-linking name spaces. * A self linking namespace is a namespace that links back directly @@ -277,29 +95,9 @@ public class NamespaceInterceptor extends HandlerInterceptor { * (see JMXRemoteNamespace implementation). */ private MBeanServer connection() { - try { - final MBeanServer c = super.source(); - if (probe != null) // should not happen - throw new RuntimeException("connection is being probed"); - - if (probed == false || isProbeRequested(c)) { - try { - // Should not happen if class well behaved. - // Never probed. Force it. - //System.err.println("sending probe for " + - // "target="+targetNs+", source="+srcNs); - sendProbe(c); - } catch (IOException io) { - throw new RuntimeException(io.getMessage(), io); - } - } - - if (c != null) { - return c; - } - } catch (RuntimeException x) { - throw x; - } + final MBeanServer c = super.source(); + if (c != null) return c; + // should not come here throw new NullPointerException("getMBeanServerConnection"); } @@ -315,24 +113,6 @@ public class NamespaceInterceptor extends HandlerInterceptor { return super.source(); } - /** - * Calls {@link MBeanServerConnection#queryNames queryNames} - * on the underlying - * {@link #getMBeanServerConnection MBeanServerConnection}. - **/ - @Override - public final Set queryNames(ObjectName name, QueryExp query) { - // XXX: TODO this probe thing is way too complex and fragile. - // This *must* go away or be replaced by something simpler. - // ideas are welcomed. - PROBE_LOG.finer("probe is: "+probe+" pattern is: "+name); - if (probe != null && isProbePattern(name)) { - PROBE_LOG.finer("Return probe: "+probe); - return Collections.singleton(probe); - } - return super.queryNames(name, query); - } - @Override protected ObjectName toSource(ObjectName targetName) throws MalformedObjectNameException { diff --git a/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java b/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java index 1e877e0ce..6958f57f2 100644 --- a/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java +++ b/src/share/classes/javax/management/namespace/JMXRemoteNamespace.java @@ -28,11 +28,9 @@ package javax.management.namespace; import com.sun.jmx.defaults.JmxProperties; import com.sun.jmx.mbeanserver.Util; import com.sun.jmx.namespace.JMXNamespaceUtils; -import com.sun.jmx.namespace.NamespaceInterceptor.DynamicProbe; import com.sun.jmx.remote.util.EnvHelp; import java.io.IOException; -import java.security.AccessControlException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; @@ -44,9 +42,7 @@ import javax.management.AttributeChangeNotification; import javax.management.InstanceNotFoundException; import javax.management.ListenerNotFoundException; import javax.management.MBeanNotificationInfo; -import javax.management.MBeanPermission; import javax.management.MBeanServerConnection; -import javax.management.MalformedObjectNameException; import javax.management.Notification; import javax.management.NotificationBroadcasterSupport; import javax.management.NotificationEmitter; @@ -118,9 +114,6 @@ public class JMXRemoteNamespace */ private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER; - private static final Logger PROBE_LOG = Logger.getLogger( - JmxProperties.NAMESPACE_LOGGER_NAME+".probe"); - // This connection listener is used to listen for connection events from // the underlying JMXConnector. It is used in particular to maintain the @@ -153,8 +146,7 @@ public class JMXRemoteNamespace // because the one that is actually used is the one supplied by the // override of getMBeanServerConnection(). private static class JMXRemoteNamespaceDelegate - extends MBeanServerConnectionWrapper - implements DynamicProbe { + extends MBeanServerConnectionWrapper { private volatile JMXRemoteNamespace parent=null; JMXRemoteNamespaceDelegate() { @@ -180,9 +172,6 @@ public class JMXRemoteNamespace } - public boolean isProbeRequested() { - return this.parent.isProbeRequested(); - } } private static final MBeanNotificationInfo connectNotification = @@ -201,7 +190,6 @@ public class JMXRemoteNamespace private volatile MBeanServerConnection server = null; private volatile JMXConnector conn = null; private volatile ClassLoader defaultClassLoader = null; - private volatile boolean probed; /** * Creates a new instance of {@code JMXRemoteNamespace}. @@ -241,9 +229,6 @@ public class JMXRemoteNamespace // handles (dis)connection events this.listener = new ConnectionListener(); - - // XXX TODO: remove the probe, or simplify it. - this.probed = false; } /** @@ -274,10 +259,6 @@ public class JMXRemoteNamespace return optionsMap; } - boolean isProbeRequested() { - return probed==false; - } - public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) { broadcaster.addNotificationListener(listener, filter, handback); @@ -603,26 +584,7 @@ public class JMXRemoteNamespace } public void connect() throws IOException { - if (conn != null) { - try { - // This is much too fragile. It must go away! - PROBE_LOG.finest("Probing again..."); - triggerProbe(getMBeanServerConnection()); - } catch(Exception x) { - close(); - Throwable cause = x; - // if the cause is a security exception - rethrows it... - while (cause != null) { - if (cause instanceof SecurityException) - throw (SecurityException) cause; - cause = cause.getCause(); - } - throw new IOException("connection failed: cycle?",x); - } - } LOG.fine("connecting..."); - // TODO remove these traces - // System.err.println(getInitParameter()+" connecting"); final Map env = new HashMap(getEnvMap()); try { @@ -652,79 +614,9 @@ public class JMXRemoteNamespace throw x; } - - // XXX Revisit here - // Note from the author: This business of switching connection is - // incredibly complex. Isn't there any means to simplify it? - // switchConnection(conn,aconn,msc); - try { - triggerProbe(msc); - } catch(Exception x) { - close(); - Throwable cause = x; - // if the cause is a security exception - rethrows it... - while (cause != null) { - if (cause instanceof SecurityException) - throw (SecurityException) cause; - cause = cause.getCause(); - } - throw new IOException("connection failed: cycle?",x); - } - LOG.fine("connected."); - } - // If this is a self-linking namespace, this method should trigger - // the emission of a probe in the wrapping NamespaceInterceptor. - // The first call to source() in the wrapping NamespaceInterceptor - // causes the emission of the probe. - // - // Note: the MBeanServer returned by getSourceServer - // (our private JMXRemoteNamespaceDelegate inner class) - // implements a sun private interface (DynamicProbe) which is - // used by the NamespaceInterceptor to determine whether it should - // send a probe or not. - // We needed this interface here because the NamespaceInterceptor - // has otherwise no means to knows that this object has just - // connected, and that a new probe should be sent. - // - // Probes work this way: the NamespaceInterceptor sets a flag and sends - // a queryNames() request. If a queryNames() request comes in when the flag - // is on, then it deduces that there is a self-linking loop - and instead - // of calling queryNames() on the JMXNamespace (which would cause the - // loop to go on) it breaks the recursion by returning the probe ObjectName. - // If the NamespaceInterceptor receives the probe ObjectName as result of - // its original queryNames() it knows that it has been looping back on - // itslef and throws an Exception - which will be raised through this - // method, thus preventing the connection to be established... - // - // More info in the com.sun.jmx.namespace.NamespaceInterceptor class - // - // XXX: TODO this probe thing is way too complex and fragile. - // This *must* go away or be replaced by something simpler. - // ideas are welcomed. - // - private void triggerProbe(final MBeanServerConnection msc) - throws MalformedObjectNameException, IOException { - // Query Pattern that we will send through the source server in order - // to detect self-linking namespaces. - // - // - final ObjectName pattern; - pattern = ObjectName.getInstance("*" + - JMXNamespaces.NAMESPACE_SEPARATOR + ":" + - JMXNamespace.TYPE_ASSIGNMENT); - probed = false; - try { - msc.queryNames(pattern, null); - probed = true; - } catch (AccessControlException x) { - // if we have an MBeanPermission missing then do nothing... - if (!(x.getPermission() instanceof MBeanPermission)) - throw x; - PROBE_LOG.finer("Can't check for cycles: " + x); - probed = false; // no need to do it again... - } + LOG.fine("connected."); } public void close() throws IOException { diff --git a/test/javax/management/namespace/JMXNamespaceTest.java b/test/javax/management/namespace/JMXNamespaceTest.java index 1c2ffac9d..a35377112 100644 --- a/test/javax/management/namespace/JMXNamespaceTest.java +++ b/test/javax/management/namespace/JMXNamespaceTest.java @@ -35,7 +35,6 @@ * NamespaceController.java NamespaceControllerMBean.java * @run main/othervm JMXNamespaceTest */ -import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.reflect.InvocationTargetException; @@ -52,10 +51,10 @@ import javax.management.InvalidAttributeValueException; import javax.management.JMX; import javax.management.MBeanServer; import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; import javax.management.NotificationEmitter; import javax.management.ObjectInstance; import javax.management.ObjectName; -import javax.management.RuntimeOperationsException; import javax.management.StandardMBean; import javax.management.namespace.JMXNamespaces; import javax.management.namespace.JMXNamespace; @@ -155,7 +154,7 @@ public class JMXNamespaceTest { } } - private static class SimpleTestConf { + public static class SimpleTestConf { public final Wombat wombat; public final StandardMBean mbean; public final String dirname; @@ -457,259 +456,56 @@ public class JMXNamespaceTest { } } - /** - * Test cycle detection. - * mkdir test ; cd test ; ln -s . kanga ; ln -s kanga/kanga/roo/kanga roo - * touch kanga/roo/wombat - **/ - public static void probeKangaRooTest(String[] args) { - final SimpleTestConf conf; + public static void verySimpleTest(String[] args) { + System.err.println("verySimpleTest: starting"); try { - conf = new SimpleTestConf(args); - try { - final JMXServiceURL url = - new JMXServiceURL("rmi","localHost",0); - final Map empty = Collections.emptyMap(); - final JMXConnectorServer server = - JMXConnectorServerFactory.newJMXConnectorServer(url, - empty,conf.server); - server.start(); - final JMXServiceURL address = server.getAddress(); - final JMXConnector client = - JMXConnectorFactory.connect(address, - empty); - final String[] signature = { - JMXServiceURL.class.getName(), - Map.class.getName(), - }; - - final Object[] params = { - address, - null, - }; - final MBeanServerConnection c = - client.getMBeanServerConnection(); - - // ln -s . kanga - final ObjectName dirName1 = - new ObjectName("kanga//:type=JMXNamespace"); - c.createMBean(JMXRemoteTargetNamespace.class.getName(), - dirName1, params,signature); - c.invoke(dirName1, "connect", null, null); - try { - // ln -s kanga//kanga//roo//kanga roo - final JMXNamespace local = new JMXNamespace( - new MBeanServerConnectionWrapper(null, - JMXNamespaceTest.class.getClassLoader()){ - - @Override - protected MBeanServerConnection getMBeanServerConnection() { - return JMXNamespaces.narrowToNamespace(c, - "kanga//kanga//roo//kanga" - ); - } - - }); - final ObjectName dirName2 = - new ObjectName("roo//:type=JMXNamespace"); - conf.server.registerMBean(local,dirName2); - System.out.println(dirName2 + " created!"); - try { - // touch kanga/roo/wombat - final ObjectName wombatName1 = - new ObjectName("kanga//roo//"+conf.wombatName); - final WombatMBean wombat1 = - JMX.newMBeanProxy(c,wombatName1,WombatMBean.class); - final String newCaption="I am still the same old wombat"; - Exception x = null; - try { - wombat1.setCaption(newCaption); - } catch (RuntimeOperationsException r) { - x=r.getTargetException(); - System.out.println("Got expected exception: " + x); - // r.printStackTrace(); - } - if (x == null) - throw new RuntimeException("cycle not detected!"); - } finally { - c.unregisterMBean(dirName2); - } - } finally { - c.unregisterMBean(dirName1); - client.close(); - server.stop(); - } - } finally { - conf.close(); - } - System.err.println("probeKangaRooTest PASSED"); + final MBeanServer srv = MBeanServerFactory.createMBeanServer(); + srv.registerMBean(new JMXNamespace( + JMXNamespaces.narrowToNamespace(srv, "foo")), + JMXNamespaces.getNamespaceObjectName("foo")); + throw new Exception("Excpected IllegalArgumentException not raised."); + } catch (IllegalArgumentException x) { + System.err.println("verySimpleTest: got expected exception: "+x); } catch (Exception x) { - System.err.println("probeKangaRooTest FAILED: " +x); + System.err.println("verySimpleTest FAILED: " +x); x.printStackTrace(); throw new RuntimeException(x); } + System.err.println("verySimpleTest: PASSED"); } - /** - * Test cycle detection 2. - * mkdir test ; cd test ; ln -s . roo ; ln -s roo/roo kanga - * touch kanga/roo/wombat ; rm roo ; ln -s kanga roo ; - * touch kanga/roo/wombat - * - **/ - public static void probeKangaRooCycleTest(String[] args) { - final SimpleTestConf conf; - try { - conf = new SimpleTestConf(args); - Exception failed = null; - try { - final JMXServiceURL url = - new JMXServiceURL("rmi","localHost",0); - final Map empty = Collections.emptyMap(); - final JMXConnectorServer server = - JMXConnectorServerFactory.newJMXConnectorServer(url, - empty,conf.server); - server.start(); - final JMXServiceURL address = server.getAddress(); - final JMXConnector client = - JMXConnectorFactory.connect(address, - empty); - final String[] signature = { - JMXServiceURL.class.getName(), - Map.class.getName(), - }; - final String[] signature2 = { - JMXServiceURL.class.getName(), - Map.class.getName(), - String.class.getName() - }; - final Object[] params = { - address, - Collections.emptyMap(), - }; - final Object[] params2 = { - address, - null, - "kanga", - }; - final MBeanServerConnection c = - client.getMBeanServerConnection(); - // ln -s . roo - final ObjectName dirName1 = - new ObjectName("roo//:type=JMXNamespace"); - c.createMBean(JMXRemoteTargetNamespace.class.getName(), - dirName1, params,signature); - c.invoke(dirName1, "connect",null,null); - try { - final Map emptyMap = - Collections.emptyMap(); - final JMXNamespace local = new JMXNamespace( - new MBeanServerConnectionWrapper( - JMXNamespaces.narrowToNamespace(c, - "roo//roo//"), - JMXNamespaceTest.class.getClassLoader())) { - }; - // ln -s roo/roo kanga - final ObjectName dirName2 = - new ObjectName("kanga//:type=JMXNamespace"); - conf.server.registerMBean(local,dirName2); - System.out.println(dirName2 + " created!"); - try { - // touch kanga/roo/wombat - final ObjectName wombatName1 = - new ObjectName("kanga//roo//"+conf.wombatName); - final WombatMBean wombat1 = - JMX.newMBeanProxy(c,wombatName1,WombatMBean.class); - final String newCaption="I am still the same old wombat"; - wombat1.setCaption(newCaption); - // rm roo - c.unregisterMBean(dirName1); - // ln -s kanga roo - System.err.println("**** Creating " + dirName1 + - " ****"); - c.createMBean(JMXRemoteTargetNamespace.class.getName(), - dirName1, params2,signature2); - System.err.println("**** Created " + dirName1 + - " ****"); - Exception x = null; - try { - // touch kanga/roo/wombat - wombat1.setCaption(newCaption+" I hope"); - } catch (RuntimeOperationsException r) { - x=(Exception)r.getCause(); - System.out.println("Got expected exception: " + x); - //r.printStackTrace(); - } - if (x == null) - throw new RuntimeException("should have failed!"); - x = null; - try { - // ls kanga/roo/wombat - System.err.println("**** Connecting " + dirName1 + - " ****"); - JMX.newMBeanProxy(c,dirName1, - JMXRemoteNamespaceMBean.class).connect(); - System.err.println("**** Connected " + dirName1 + - " ****"); - } catch (IOException r) { - x=r; - System.out.println("Got expected exception: " + x); - //r.printStackTrace(); - } - System.err.println("**** Expected Exception Not Raised ****"); - if (x == null) { - System.out.println(dirName1+" contains: "+ - c.queryNames(new ObjectName( - dirName1.getDomain()+"*:*"),null)); - throw new RuntimeException("cycle not detected!"); - } - } catch (Exception t) { - if (failed == null) failed = t; - } finally { - c.unregisterMBean(dirName2); - } - } finally { - try { - c.unregisterMBean(dirName1); - } catch (Exception t) { - if (failed == null) failed = t; - System.err.println("Failed to unregister "+dirName1+ - ": "+t); - } - try { - client.close(); - } catch (Exception t) { - if (failed == null) failed = t; - System.err.println("Failed to close client: "+t); - } - try { - server.stop(); - } catch (Exception t) { - if (failed == null) failed = t; - System.err.println("Failed to stop server: "+t); - } - } - } finally { - try { - conf.close(); - } catch (Exception t) { - if (failed == null) failed = t; - System.err.println("Failed to stop server: "+t); - } - } - if (failed != null) throw failed; - System.err.println("probeKangaRooCycleTest PASSED"); + public static void verySimpleTest2(String[] args) { + System.err.println("verySimpleTest2: starting"); + try { + final MBeanServer srv = MBeanServerFactory.createMBeanServer(); + final JMXConnectorServer cs = JMXConnectorServerFactory. + newJMXConnectorServer(new JMXServiceURL("rmi",null,0), + null, srv); + cs.start(); + final JMXConnector cc = JMXConnectorFactory.connect(cs.getAddress()); + + srv.registerMBean(new JMXNamespace( + new MBeanServerConnectionWrapper( + JMXNamespaces.narrowToNamespace( + cc.getMBeanServerConnection(), + "foo"))), + JMXNamespaces.getNamespaceObjectName("foo")); + throw new Exception("Excpected IllegalArgumentException not raised."); + } catch (IllegalArgumentException x) { + System.err.println("verySimpleTest2: got expected exception: "+x); } catch (Exception x) { - System.err.println("probeKangaRooCycleTest FAILED: " +x); + System.err.println("verySimpleTest2 FAILED: " +x); x.printStackTrace(); throw new RuntimeException(x); } + System.err.println("verySimpleTest2: PASSED"); } + public static void main(String[] args) { simpleTest(args); recursiveTest(args); - probeKangaRooTest(args); - probeKangaRooCycleTest(args); + verySimpleTest(args); + verySimpleTest2(args); } } -- GitLab From 7d65ec62320d79ae0af6e616897e1dc1e01179bc Mon Sep 17 00:00:00 2001 From: dfuchs Date: Wed, 17 Sep 2008 13:40:40 +0200 Subject: [PATCH 120/139] 6748745: JConsole: plotters don't resize well when the window is resized Summary: part of the fix was contributed by jfdenise Reviewed-by: jfdenise --- .../classes/sun/tools/jconsole/Plotter.java | 22 ++++++-- .../jconsole/inspector/XMBeanAttributes.java | 4 +- .../tools/jconsole/inspector/XPlotter.java | 2 +- .../jconsole/inspector/XPlottingViewer.java | 53 +++++++++---------- 4 files changed, 45 insertions(+), 36 deletions(-) diff --git a/src/share/classes/sun/tools/jconsole/Plotter.java b/src/share/classes/sun/tools/jconsole/Plotter.java index ea9d637ed..5c8305bac 100644 --- a/src/share/classes/sun/tools/jconsole/Plotter.java +++ b/src/share/classes/sun/tools/jconsole/Plotter.java @@ -30,18 +30,15 @@ import java.awt.event.*; import java.beans.*; import java.io.*; import java.lang.reflect.Array; -import java.text.*; import java.util.*; import javax.accessibility.*; import javax.swing.*; import javax.swing.border.*; -import javax.swing.event.*; import javax.swing.filechooser.*; import javax.swing.filechooser.FileFilter; import com.sun.tools.jconsole.JConsoleContext; -import com.sun.tools.jconsole.JConsoleContext.ConnectionState; import static com.sun.tools.jconsole.JConsoleContext.ConnectionState.*; @@ -130,6 +127,7 @@ public class Plotter extends JComponent private int bottomMargin = 45; private int leftMargin = 65; private int rightMargin = 70; + private final boolean displayLegend; public Plotter() { this(Unit.NONE, 0); @@ -139,15 +137,21 @@ public class Plotter extends JComponent this(unit, 0); } + public Plotter(Unit unit, int decimals) { + this(unit,decimals,true); + } + // Note: If decimals > 0 then values must be decimally shifted left // that many places, i.e. multiplied by Math.pow(10.0, decimals). - public Plotter(Unit unit, int decimals) { + public Plotter(Unit unit, int decimals, boolean displayLegend) { + this.displayLegend = displayLegend; setUnit(unit); setDecimals(decimals); enableEvents(AWTEvent.MOUSE_EVENT_MASK); addMouseListener(new MouseAdapter() { + @Override public void mousePressed(MouseEvent e) { if (getParent() instanceof PlotterPanel) { getParent().requestFocusInWindow(); @@ -240,6 +244,7 @@ public class Plotter extends JComponent } } + @Override public JPopupMenu getComponentPopupMenu() { if (popupMenu == null) { popupMenu = new JPopupMenu(Resources.getText("Chart:")); @@ -330,6 +335,7 @@ public class Plotter extends JComponent } } + @Override public void paintComponent(Graphics g) { super.paintComponent(g); @@ -670,7 +676,7 @@ public class Plotter extends JComponent curValue += "%"; } int valWidth = fm.stringWidth(curValue); - String legend = seq.name; + String legend = (displayLegend?seq.name:""); int legendWidth = fm.stringWidth(legend); if (checkRightMargin(valWidth) || checkRightMargin(legendWidth)) { // Wait for next repaint @@ -986,10 +992,12 @@ public class Plotter extends JComponent } private static class SaveDataFileChooser extends JFileChooser { + private static final long serialVersionUID = -5182890922369369669L; SaveDataFileChooser() { setFileFilter(new FileNameExtensionFilter("CSV file", "csv")); } + @Override public void approveSelection() { File file = getSelectedFile(); if (file != null) { @@ -1034,6 +1042,7 @@ public class Plotter extends JComponent } } + @Override public AccessibleContext getAccessibleContext() { if (accessibleContext == null) { accessibleContext = new AccessiblePlotter(); @@ -1042,10 +1051,12 @@ public class Plotter extends JComponent } protected class AccessiblePlotter extends AccessibleJComponent { + private static final long serialVersionUID = -3847205410473510922L; protected AccessiblePlotter() { setAccessibleName(getText("Plotter.accessibleName")); } + @Override public String getAccessibleName() { String name = super.getAccessibleName(); @@ -1076,6 +1087,7 @@ public class Plotter extends JComponent return name; } + @Override public AccessibleRole getAccessibleRole() { return AccessibleRole.CANVAS; } diff --git a/src/share/classes/sun/tools/jconsole/inspector/XMBeanAttributes.java b/src/share/classes/sun/tools/jconsole/inspector/XMBeanAttributes.java index 5fca79d37..4fe9e737f 100644 --- a/src/share/classes/sun/tools/jconsole/inspector/XMBeanAttributes.java +++ b/src/share/classes/sun/tools/jconsole/inspector/XMBeanAttributes.java @@ -872,8 +872,8 @@ public class XMBeanAttributes extends XTable { MaximizedCellRenderer(Component comp) { this.comp = comp; Dimension d = comp.getPreferredSize(); - if (d.getHeight() > 200) { - comp.setPreferredSize(new Dimension((int) d.getWidth(), 200)); + if (d.getHeight() > 220) { + comp.setPreferredSize(new Dimension((int) d.getWidth(), 220)); } } @Override diff --git a/src/share/classes/sun/tools/jconsole/inspector/XPlotter.java b/src/share/classes/sun/tools/jconsole/inspector/XPlotter.java index 24b4104b7..8be8c5ca8 100644 --- a/src/share/classes/sun/tools/jconsole/inspector/XPlotter.java +++ b/src/share/classes/sun/tools/jconsole/inspector/XPlotter.java @@ -34,7 +34,7 @@ public class XPlotter extends Plotter { JTable table; public XPlotter(JTable table, Plotter.Unit unit) { - super(unit); + super(unit,0,false); this.table = table; } @Override diff --git a/src/share/classes/sun/tools/jconsole/inspector/XPlottingViewer.java b/src/share/classes/sun/tools/jconsole/inspector/XPlottingViewer.java index 7da7576b5..0c074a559 100644 --- a/src/share/classes/sun/tools/jconsole/inspector/XPlottingViewer.java +++ b/src/share/classes/sun/tools/jconsole/inspector/XPlottingViewer.java @@ -27,14 +27,10 @@ package sun.tools.jconsole.inspector; import java.awt.*; import java.awt.event.*; -import java.io.*; import java.util.*; import java.util.Timer; -import javax.management.*; import javax.swing.*; -import javax.swing.border.*; -import javax.swing.event.*; import sun.tools.jconsole.*; @@ -127,6 +123,7 @@ public class XPlottingViewer extends PlotterPanel implements ActionListener { setBackground(g.getColor()); plotter.paintComponent(g); }*/ + @Override public void actionPerformed(ActionEvent evt) { plotterCache.remove(key); Timer t = timerCache.remove(key); @@ -141,9 +138,11 @@ public class XPlottingViewer extends PlotterPanel implements ActionListener { JTable table) { final Plotter plotter = new XPlotter(table, Plotter.Unit.NONE) { Dimension prefSize = new Dimension(400, 170); + @Override public Dimension getPreferredSize() { return prefSize; } + @Override public Dimension getMinimumSize() { return prefSize; } @@ -183,42 +182,40 @@ public class XPlottingViewer extends PlotterPanel implements ActionListener { return plotter; } - //Create Plotter display private void setupDisplay(Plotter plotter) { - //setLayout(new GridLayout(2,0)); - GridBagLayout gbl = new GridBagLayout(); - setLayout(gbl); + final JPanel buttonPanel = new JPanel(); + final GridBagLayout gbl = new GridBagLayout(); + buttonPanel.setLayout(gbl); + setLayout(new BorderLayout()); plotButton = new JButton(Resources.getText("Discard chart")); plotButton.addActionListener(this); plotButton.setEnabled(true); - // Add the display to the top four cells GridBagConstraints buttonConstraints = new GridBagConstraints(); buttonConstraints.gridx = 0; buttonConstraints.gridy = 0; buttonConstraints.fill = GridBagConstraints.VERTICAL; buttonConstraints.anchor = GridBagConstraints.CENTER; gbl.setConstraints(plotButton, buttonConstraints); - add(plotButton); - - GridBagConstraints plotterConstraints = new GridBagConstraints(); - plotterConstraints.gridx = 0; - plotterConstraints.gridy = 1; - plotterConstraints.weightx = 1; - //plotterConstraints.gridwidth = (int) plotter.getPreferredSize().getWidth(); - //plotterConstraints.gridheight = (int) plotter.getPreferredSize().getHeight(); - plotterConstraints.fill = GridBagConstraints.VERTICAL; - gbl.setConstraints(plotter, plotterConstraints); - - - //bordered = new JPanel(); - //bordered.setPreferredSize(new Dimension(400, 250)); - //bordered.add(plotButton); - //bordered.add(plotter); - - //add(bordered); - + buttonPanel.add(plotButton); + + if (attributeName != null && attributeName.length()!=0) { + final JPanel plotterLabelPanel = new JPanel(); + final JLabel label = new JLabel(attributeName); + final GridBagLayout gbl2 = new GridBagLayout(); + plotterLabelPanel.setLayout(gbl2); + final GridBagConstraints labelConstraints = new GridBagConstraints(); + labelConstraints.gridx = 0; + labelConstraints.gridy = 0; + labelConstraints.fill = GridBagConstraints.VERTICAL; + labelConstraints.anchor = GridBagConstraints.CENTER; + labelConstraints.ipady = 10; + gbl2.setConstraints(label, labelConstraints); + plotterLabelPanel.add(label); + add(plotterLabelPanel, BorderLayout.NORTH); + } setPlotter(plotter); + add(buttonPanel, BorderLayout.SOUTH); repaint(); } -- GitLab From 27b29d0cb0e07daf68faf843f9bd34a838c457b8 Mon Sep 17 00:00:00 2001 From: michaelm Date: Fri, 19 Sep 2008 13:32:36 +0100 Subject: [PATCH 121/139] 6750364: Error in test for 6744329 Reviewed-by: chegar --- test/com/sun/net/httpserver/bugs/B6744329.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/com/sun/net/httpserver/bugs/B6744329.java b/test/com/sun/net/httpserver/bugs/B6744329.java index cd23ab9b3..e09fce5be 100644 --- a/test/com/sun/net/httpserver/bugs/B6744329.java +++ b/test/com/sun/net/httpserver/bugs/B6744329.java @@ -23,7 +23,7 @@ /** * @test - * @bug B6744329 + * @bug 6744329 * @summary Exception in light weight Http server */ -- GitLab From d0615577608d43e2b4f8d5719797a074a5978fe1 Mon Sep 17 00:00:00 2001 From: chegar Date: Fri, 19 Sep 2008 15:14:53 +0100 Subject: [PATCH 122/139] 6746836: java.net exception classes don't specify serialVersionUID Reviewed-by: alanb, jccollet --- make/sun/net/spi/Makefile | 4 ---- make/sun/net/spi/nameservice/Makefile | 4 ---- make/sun/net/spi/nameservice/dns/Makefile | 2 +- src/share/classes/java/net/BindException.java | 3 ++- src/share/classes/java/net/ConnectException.java | 4 +++- src/share/classes/java/net/HttpRetryException.java | 3 ++- src/share/classes/java/net/MalformedURLException.java | 4 +++- src/share/classes/java/net/NoRouteToHostException.java | 4 +++- src/share/classes/java/net/PortUnreachableException.java | 3 ++- src/share/classes/java/net/ProtocolException.java | 4 +++- src/share/classes/java/net/SocketException.java | 4 +++- src/share/classes/java/net/SocketTimeoutException.java | 3 ++- src/share/classes/java/net/URISyntaxException.java | 4 +++- src/share/classes/java/net/UnknownHostException.java | 4 +++- src/share/classes/java/net/UnknownServiceException.java | 4 +++- src/share/classes/sun/net/ConnectionResetException.java | 5 +++-- src/share/classes/sun/net/ProgressEvent.java | 1 + src/share/classes/sun/net/TelnetProtocolException.java | 4 +++- src/share/classes/sun/net/ftp/FtpLoginException.java | 4 +++- src/share/classes/sun/net/ftp/FtpProtocolException.java | 4 +++- src/share/classes/sun/net/httpserver/HttpError.java | 4 +++- .../classes/sun/net/httpserver/StreamClosedException.java | 3 ++- src/share/classes/sun/net/smtp/SmtpProtocolException.java | 4 +++- .../classes/sun/net/www/ApplicationLaunchException.java | 4 +++- .../classes/sun/net/www/http/KeepAliveStreamCleaner.java | 1 + .../sun/net/www/protocol/http/DigestAuthentication.java | 2 ++ .../sun/net/www/protocol/http/NTLMAuthentication.java | 1 + 27 files changed, 61 insertions(+), 30 deletions(-) diff --git a/make/sun/net/spi/Makefile b/make/sun/net/spi/Makefile index 20583bc4e..f969b8799 100644 --- a/make/sun/net/spi/Makefile +++ b/make/sun/net/spi/Makefile @@ -23,10 +23,6 @@ # have any questions. # -# -# Makefile for building com/sun -# - BUILDDIR = ../../.. include $(BUILDDIR)/common/Defs.gmk diff --git a/make/sun/net/spi/nameservice/Makefile b/make/sun/net/spi/nameservice/Makefile index b6593c71d..b0ff374de 100644 --- a/make/sun/net/spi/nameservice/Makefile +++ b/make/sun/net/spi/nameservice/Makefile @@ -23,10 +23,6 @@ # have any questions. # -# -# Makefile for building com/sun -# - BUILDDIR = ../../../.. include $(BUILDDIR)/common/Defs.gmk diff --git a/make/sun/net/spi/nameservice/dns/Makefile b/make/sun/net/spi/nameservice/dns/Makefile index a882eada9..daf35125d 100644 --- a/make/sun/net/spi/nameservice/dns/Makefile +++ b/make/sun/net/spi/nameservice/dns/Makefile @@ -24,7 +24,7 @@ # # -# Makefile for building JNDI service provider toolkit +# Makefile for building JNDI DNS name service provider # BUILDDIR = ../../../../.. diff --git a/src/share/classes/java/net/BindException.java b/src/share/classes/java/net/BindException.java index 9ea95d6f7..b2975e52d 100644 --- a/src/share/classes/java/net/BindException.java +++ b/src/share/classes/java/net/BindException.java @@ -1,5 +1,5 @@ /* - * Copyright 1996-1997 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ package java.net; */ public class BindException extends SocketException { + private static final long serialVersionUID = -5945005768251722951L; /** * Constructs a new BindException with the specified detail diff --git a/src/share/classes/java/net/ConnectException.java b/src/share/classes/java/net/ConnectException.java index 29b46ab66..ed97e6b91 100644 --- a/src/share/classes/java/net/ConnectException.java +++ b/src/share/classes/java/net/ConnectException.java @@ -1,5 +1,5 @@ /* - * Copyright 1996-1997 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,8 @@ package java.net; * @since JDK1.1 */ public class ConnectException extends SocketException { + private static final long serialVersionUID = 3831404271622369215L; + /** * Constructs a new ConnectException with the specified detail * message as to why the connect error occurred. diff --git a/src/share/classes/java/net/HttpRetryException.java b/src/share/classes/java/net/HttpRetryException.java index d6f336e3e..5e75dc94a 100644 --- a/src/share/classes/java/net/HttpRetryException.java +++ b/src/share/classes/java/net/HttpRetryException.java @@ -1,5 +1,5 @@ /* - * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,7 @@ import java.io.IOException; */ public class HttpRetryException extends IOException { + private static final long serialVersionUID = -9186022286469111381L; private int responseCode; private String location; diff --git a/src/share/classes/java/net/MalformedURLException.java b/src/share/classes/java/net/MalformedURLException.java index c38cd542c..b8f3b6fb5 100644 --- a/src/share/classes/java/net/MalformedURLException.java +++ b/src/share/classes/java/net/MalformedURLException.java @@ -1,5 +1,5 @@ /* - * Copyright 1995-1997 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1995-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,6 +36,8 @@ import java.io.IOException; * @since JDK1.0 */ public class MalformedURLException extends IOException { + private static final long serialVersionUID = -182787522200415866L; + /** * Constructs a MalformedURLException with no detail message. */ diff --git a/src/share/classes/java/net/NoRouteToHostException.java b/src/share/classes/java/net/NoRouteToHostException.java index 8bfc3de72..e6db581a2 100644 --- a/src/share/classes/java/net/NoRouteToHostException.java +++ b/src/share/classes/java/net/NoRouteToHostException.java @@ -1,5 +1,5 @@ /* - * Copyright 1996-1997 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,8 @@ package java.net; * @since JDK1.1 */ public class NoRouteToHostException extends SocketException { + private static final long serialVersionUID = -1897550894873493790L; + /** * Constructs a new NoRouteToHostException with the specified detail * message as to why the remote host cannot be reached. diff --git a/src/share/classes/java/net/PortUnreachableException.java b/src/share/classes/java/net/PortUnreachableException.java index cf8f24d23..e4491d891 100644 --- a/src/share/classes/java/net/PortUnreachableException.java +++ b/src/share/classes/java/net/PortUnreachableException.java @@ -1,5 +1,5 @@ /* - * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ package java.net; */ public class PortUnreachableException extends SocketException { + private static final long serialVersionUID = 8462541992376507323L; /** * Constructs a new PortUnreachableException with a diff --git a/src/share/classes/java/net/ProtocolException.java b/src/share/classes/java/net/ProtocolException.java index 3eef7d538..b0567f2e5 100644 --- a/src/share/classes/java/net/ProtocolException.java +++ b/src/share/classes/java/net/ProtocolException.java @@ -1,5 +1,5 @@ /* - * Copyright 1995-1997 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1995-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,6 +36,8 @@ import java.io.IOException; */ public class ProtocolException extends IOException { + private static final long serialVersionUID = -6098449442062388080L; + /** * Constructs a new ProtocolException with the * specified detail message. diff --git a/src/share/classes/java/net/SocketException.java b/src/share/classes/java/net/SocketException.java index 6b40c3c83..6e6f60d34 100644 --- a/src/share/classes/java/net/SocketException.java +++ b/src/share/classes/java/net/SocketException.java @@ -1,5 +1,5 @@ /* - * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1995-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,8 @@ import java.io.IOException; */ public class SocketException extends IOException { + private static final long serialVersionUID = -5935874303556886934L; + /** * Constructs a new SocketException with the * specified detail message. diff --git a/src/share/classes/java/net/SocketTimeoutException.java b/src/share/classes/java/net/SocketTimeoutException.java index b44995959..fab3a5810 100644 --- a/src/share/classes/java/net/SocketTimeoutException.java +++ b/src/share/classes/java/net/SocketTimeoutException.java @@ -1,5 +1,5 @@ /* - * Copyright 2000 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,7 @@ package java.net; */ public class SocketTimeoutException extends java.io.InterruptedIOException { + private static final long serialVersionUID = -8846654841826352300L; /** * Constructs a new SocketTimeoutException with a detail diff --git a/src/share/classes/java/net/URISyntaxException.java b/src/share/classes/java/net/URISyntaxException.java index e1882be40..17a6bbb72 100644 --- a/src/share/classes/java/net/URISyntaxException.java +++ b/src/share/classes/java/net/URISyntaxException.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,8 @@ package java.net; public class URISyntaxException extends Exception { + private static final long serialVersionUID = 2137979680897488891L; + private String input; private int index; diff --git a/src/share/classes/java/net/UnknownHostException.java b/src/share/classes/java/net/UnknownHostException.java index b6df3be5d..860b847d6 100644 --- a/src/share/classes/java/net/UnknownHostException.java +++ b/src/share/classes/java/net/UnknownHostException.java @@ -1,5 +1,5 @@ /* - * Copyright 1995-1997 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1995-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,8 @@ import java.io.IOException; */ public class UnknownHostException extends IOException { + private static final long serialVersionUID = -4639126076052875403L; + /** * Constructs a new UnknownHostException with the * specified detail message. diff --git a/src/share/classes/java/net/UnknownServiceException.java b/src/share/classes/java/net/UnknownServiceException.java index 9e3e7fd35..d006e3af2 100644 --- a/src/share/classes/java/net/UnknownServiceException.java +++ b/src/share/classes/java/net/UnknownServiceException.java @@ -1,5 +1,5 @@ /* - * Copyright 1995-1997 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1995-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,8 @@ import java.io.IOException; * @since JDK1.0 */ public class UnknownServiceException extends IOException { + private static final long serialVersionUID = -4169033248853639508L; + /** * Constructs a new UnknownServiceException with no * detail message. diff --git a/src/share/classes/sun/net/ConnectionResetException.java b/src/share/classes/sun/net/ConnectionResetException.java index 095ba9133..69202945d 100644 --- a/src/share/classes/sun/net/ConnectionResetException.java +++ b/src/share/classes/sun/net/ConnectionResetException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,10 +30,11 @@ import java.net.SocketException; /** * Thrown to indicate a connection reset. * - * @since 1.4 + * @since 1.4.1 */ public class ConnectionResetException extends SocketException { + private static final long serialVersionUID = -7633185991801851556L; public ConnectionResetException(String msg) { super(msg); diff --git a/src/share/classes/sun/net/ProgressEvent.java b/src/share/classes/sun/net/ProgressEvent.java index ee4ec018d..cba1007e3 100644 --- a/src/share/classes/sun/net/ProgressEvent.java +++ b/src/share/classes/sun/net/ProgressEvent.java @@ -32,6 +32,7 @@ import java.net.URL; * * @author Stanley Man-Kit Ho */ +@SuppressWarnings("serial") // never serialized public class ProgressEvent extends EventObject { // URL of the stream private URL url; diff --git a/src/share/classes/sun/net/TelnetProtocolException.java b/src/share/classes/sun/net/TelnetProtocolException.java index 4cf4e72db..8400d13ba 100644 --- a/src/share/classes/sun/net/TelnetProtocolException.java +++ b/src/share/classes/sun/net/TelnetProtocolException.java @@ -1,5 +1,5 @@ /* - * Copyright 1994-1995 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,8 @@ import java.io.*; */ public class TelnetProtocolException extends IOException { + private static final long serialVersionUID = 8509127047257111343L; + public TelnetProtocolException(String s) { super(s); } diff --git a/src/share/classes/sun/net/ftp/FtpLoginException.java b/src/share/classes/sun/net/ftp/FtpLoginException.java index 55cf34ffe..0d37a8706 100644 --- a/src/share/classes/sun/net/ftp/FtpLoginException.java +++ b/src/share/classes/sun/net/ftp/FtpLoginException.java @@ -1,5 +1,5 @@ /* - * Copyright 1994-1995 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,8 @@ import java.io.*; * @author Jonathan Payne */ public class FtpLoginException extends FtpProtocolException { + private static final long serialVersionUID = 2218162403237941536L; + FtpLoginException(String s) { super(s); } diff --git a/src/share/classes/sun/net/ftp/FtpProtocolException.java b/src/share/classes/sun/net/ftp/FtpProtocolException.java index 9745f9eb9..6afbe215e 100644 --- a/src/share/classes/sun/net/ftp/FtpProtocolException.java +++ b/src/share/classes/sun/net/ftp/FtpProtocolException.java @@ -1,5 +1,5 @@ /* - * Copyright 1994-1995 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,8 @@ import java.io.*; * @author Jonathan Payne */ public class FtpProtocolException extends IOException { + private static final long serialVersionUID = 5978077070276545054L; + FtpProtocolException(String s) { super(s); } diff --git a/src/share/classes/sun/net/httpserver/HttpError.java b/src/share/classes/sun/net/httpserver/HttpError.java index a6dd066aa..77dca7ed5 100644 --- a/src/share/classes/sun/net/httpserver/HttpError.java +++ b/src/share/classes/sun/net/httpserver/HttpError.java @@ -1,5 +1,5 @@ /* - * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,8 @@ package sun.net.httpserver; * A Http error */ class HttpError extends RuntimeException { + private static final long serialVersionUID = 8769596371344178179L; + public HttpError (String msg) { super (msg); } diff --git a/src/share/classes/sun/net/httpserver/StreamClosedException.java b/src/share/classes/sun/net/httpserver/StreamClosedException.java index 0fc2c6d50..09295fdc1 100644 --- a/src/share/classes/sun/net/httpserver/StreamClosedException.java +++ b/src/share/classes/sun/net/httpserver/StreamClosedException.java @@ -1,5 +1,5 @@ /* - * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,4 +28,5 @@ package sun.net.httpserver; import java.io.*; class StreamClosedException extends IOException { + private static final long serialVersionUID = -4485921499356327937L; } diff --git a/src/share/classes/sun/net/smtp/SmtpProtocolException.java b/src/share/classes/sun/net/smtp/SmtpProtocolException.java index 0550eea73..e52563dbd 100644 --- a/src/share/classes/sun/net/smtp/SmtpProtocolException.java +++ b/src/share/classes/sun/net/smtp/SmtpProtocolException.java @@ -1,5 +1,5 @@ /* - * Copyright 1995 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1995-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,8 @@ import java.io.IOException; * an SMTP session. */ public class SmtpProtocolException extends IOException { + private static final long serialVersionUID = -7547136771133814908L; + SmtpProtocolException(String s) { super(s); } diff --git a/src/share/classes/sun/net/www/ApplicationLaunchException.java b/src/share/classes/sun/net/www/ApplicationLaunchException.java index 3b346d5be..fe4370690 100644 --- a/src/share/classes/sun/net/www/ApplicationLaunchException.java +++ b/src/share/classes/sun/net/www/ApplicationLaunchException.java @@ -1,5 +1,5 @@ /* - * Copyright 1996 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,8 @@ package sun.net.www; */ public class ApplicationLaunchException extends Exception { + private static final long serialVersionUID = -4782286141289536883L; + public ApplicationLaunchException(String reason) { super(reason); } diff --git a/src/share/classes/sun/net/www/http/KeepAliveStreamCleaner.java b/src/share/classes/sun/net/www/http/KeepAliveStreamCleaner.java index 7eea3194c..ec149197f 100644 --- a/src/share/classes/sun/net/www/http/KeepAliveStreamCleaner.java +++ b/src/share/classes/sun/net/www/http/KeepAliveStreamCleaner.java @@ -43,6 +43,7 @@ import java.security.PrivilegedAction; * @author Chris Hegarty */ +@SuppressWarnings("serial") // never serialized public class KeepAliveStreamCleaner extends LinkedBlockingQueue implements Runnable { // maximum amount of remaining data that we will try to cleanup diff --git a/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java b/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java index 7720640a0..8ddaa4d87 100644 --- a/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java +++ b/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java @@ -59,6 +59,8 @@ class DigestAuthentication extends AuthenticationInfo { // instances as a result of a single authorization (for multiple domains) static class Parameters implements java.io.Serializable { + private static final long serialVersionUID = -3584543755194526252L; + private boolean serverQop; // server proposed qop=auth private String opaque; private String cnonce; diff --git a/src/solaris/classes/sun/net/www/protocol/http/NTLMAuthentication.java b/src/solaris/classes/sun/net/www/protocol/http/NTLMAuthentication.java index 9130be50d..4c5c6c847 100644 --- a/src/solaris/classes/sun/net/www/protocol/http/NTLMAuthentication.java +++ b/src/solaris/classes/sun/net/www/protocol/http/NTLMAuthentication.java @@ -64,6 +64,7 @@ import java.net.*; */ class NTLMAuthentication extends AuthenticationInfo { + private static final long serialVersionUID = -2403849171106437142L; static char NTLM_AUTH = 'N'; -- GitLab From d001e0efab6f6feee26fa661add340914c601578 Mon Sep 17 00:00:00 2001 From: sjiang Date: Mon, 22 Sep 2008 15:43:12 +0200 Subject: [PATCH 123/139] 6697180: JMX query results in java.io.IOException: Illegal state - also a deadlock can also be seen Reviewed-by: emcmanus --- .../remote/internal/ClientNotifForwarder.java | 59 ++-- .../connection/MultiThreadDeadLockTest.java | 256 ++++++++++++++++++ 2 files changed, 285 insertions(+), 30 deletions(-) create mode 100644 test/javax/management/remote/mandatory/connection/MultiThreadDeadLockTest.java diff --git a/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java b/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java index b2ceb2fc1..ab6bd60ca 100644 --- a/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java +++ b/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java @@ -290,28 +290,6 @@ public abstract class ClientNotifForwarder { infoList.clear(); - if (currentFetchThread == Thread.currentThread()) { - /* we do not need to stop the fetching thread, because this thread is - used to do restarting and it will not be used to do fetching during - the re-registering the listeners.*/ - return tmp; - } - - while (state == STARTING) { - try { - wait(); - } catch (InterruptedException ire) { - IOException ioe = new IOException(ire.toString()); - EnvHelp.initCause(ioe, ire); - - throw ioe; - } - } - - if (state == STARTED) { - setState(STOPPING); - } - return tmp; } @@ -353,8 +331,9 @@ public abstract class ClientNotifForwarder { beingReconnected = false; notifyAll(); - if (currentFetchThread == Thread.currentThread()) { - // no need to init, simply get the id + if (currentFetchThread == Thread.currentThread() || + state == STARTING || state == STARTED) { // doing or waiting reconnection + // only update mbeanRemovedNotifID try { mbeanRemovedNotifID = addListenerForMBeanRemovedNotif(); } catch (Exception e) { @@ -366,12 +345,23 @@ public abstract class ClientNotifForwarder { logger.trace("init", msg, e); } } - } else if (listenerInfos.length > 0) { // old listeners re-registered - init(true); - } else if (infoList.size() > 0) { - // but new listeners registered during reconnection - init(false); - } + } else { + while (state == STOPPING) { + try { + wait(); + } catch (InterruptedException ire) { + IOException ioe = new IOException(ire.toString()); + EnvHelp.initCause(ioe, ire); + throw ioe; + } + } + + if (listenerInfos.length > 0) { // old listeners are re-added + init(true); // not update clientSequenceNumber + } else if (infoList.size() > 0) { // only new listeners added during reconnection + init(false); // need update clientSequenceNumber + } + } } public synchronized void terminate() { @@ -486,6 +476,15 @@ public abstract class ClientNotifForwarder { if (nr == null || shouldStop()) { // tell that the thread is REALLY stopped setState(STOPPED); + + try { + removeListenerForMBeanRemovedNotif(mbeanRemovedNotifID); + } catch (Exception e) { + if (logger.traceOn()) { + logger.trace("NotifFetcher-run", + "removeListenerForMBeanRemovedNotif", e); + } + } } else { executor.execute(this); } diff --git a/test/javax/management/remote/mandatory/connection/MultiThreadDeadLockTest.java b/test/javax/management/remote/mandatory/connection/MultiThreadDeadLockTest.java new file mode 100644 index 000000000..0204afb16 --- /dev/null +++ b/test/javax/management/remote/mandatory/connection/MultiThreadDeadLockTest.java @@ -0,0 +1,256 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +import java.io.IOException; +import java.io.Serializable; +import java.net.Socket; +import java.rmi.server.RMIClientSocketFactory; +import java.util.HashMap; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; +import javax.management.remote.rmi.RMIConnectorServer; + +/* + * @test + * @bug 6697180 + * @summary test on a client notification deadlock. + * @author Shanliang JIANG + * @run clean MultiThreadDeadLockTest + * @run build MultiThreadDeadLockTest + * @run main MultiThreadDeadLockTest + */ + +public class MultiThreadDeadLockTest { + + private static long serverTimeout = 500L; + + public static void main(String[] args) throws Exception { + print("Create the MBean server"); + MBeanServer mbs = MBeanServerFactory.createMBeanServer(); + + print("Initialize environment map"); + HashMap env = new HashMap(); + + print("Specify a client socket factory to control socket creation."); + env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, + clientFactory); + + print("Specify a server idle timeout to make a server close an idle connection."); + env.put("jmx.remote.x.server.connection.timeout", serverTimeout); + + print("Disable client heartbeat."); + env.put("jmx.remote.x.client.connection.check.period", 0); + + env.put("jmx.remote.x.notification.fetch.timeout", serverTimeout); + + print("Create an RMI server"); + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + JMXConnectorServer server = + JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); + server.start(); + + url = server.getAddress(); + + print("Create jmx client on "+url); + StateMachine.setState(CREATE_SOCKET); // allow to create client socket + client = JMXConnectorFactory.connect(url, env); + Thread.sleep(100); + + totoName = new ObjectName("default:name=toto"); + mbs.registerMBean(toto, totoName); + print("Register the mbean: " + totoName); + + print("Add listener to toto MBean"); + client.getMBeanServerConnection().addNotificationListener( + totoName, myListener, null, null); + Thread.sleep(10); + + print("send notif, listener will block the fetcher"); + toto.sendNotif(); + Thread.sleep(100); + + StateMachine.setState(NO_OP); + + print("Sleep 3 times of server idle timeout: "+serverTimeout+ + ", the sever should close the idle connection."); + Thread.sleep(serverTimeout*3); + + print("start the user thread to call mbean method, it will get IOexception" + + " and start the reconnection, the socket factory will block the" + + " socket creation."); + UserThread ut = new UserThread(); + ut.start(); + Thread.sleep(10); + + print("Free the listener, the fetcher will get IO and makes " + + "a deadlock if the bug is not fixed."); + StateMachine.setState(FREE_LISTENER); + Thread.sleep(100); + + print("Allow to create new socket for the reconnection"); + StateMachine.setState(CREATE_SOCKET); + + print("Check whether the user thread gets free to call the mbean."); + if (!ut.waitDone(5000)) { + throw new RuntimeException("Possible deadlock!"); + } + + print("Remove the listener."); + client.getMBeanServerConnection().removeNotificationListener( + totoName, myListener, null, null); + Thread.sleep(serverTimeout*3); + + print("\nWell passed, bye!"); + + client.close(); + Thread.sleep(10); + server.stop(); + } + + private static ObjectName totoName = null; + private static JMXConnector client; + + public static class UserThread extends Thread { + public UserThread() { + setDaemon(true); + } + + public void run() { + try { + client.getMBeanServerConnection().invoke( + totoName, "allowReturn", null, null); + } catch (Exception e) { + throw new Error(e); + } + + synchronized(UserThread.class) { + done = true; + UserThread.class.notify(); + } + } + + public boolean waitDone(long timeout) { + synchronized(UserThread.class) { + if(!done) { + try { + UserThread.class.wait(timeout); + } catch (Exception e) { + throw new Error(e); + } + } + } + return done; + } + + private boolean done = false; + } + + public static interface TotoMBean { + public void allowReturn(); + } + + public static class Toto extends NotificationBroadcasterSupport + implements TotoMBean { + + public void allowReturn() { + enter("allowReturn"); + + leave("allowReturn"); + } + + public void sendNotif() { + enter("sendNotif"); + + sendNotification(new Notification("Toto", totoName, 0)); + + leave("sendNotif"); + } + } + private static Toto toto = new Toto(); + + public static NotificationListener myListener = new NotificationListener() { + public void handleNotification(Notification notification, Object handback) { + enter("handleNotification"); + + StateMachine.waitState(FREE_LISTENER); + + leave("handleNotification"); + } + }; + + public static class RMIClientFactory + implements RMIClientSocketFactory, Serializable { + + public Socket createSocket(String host, int port) throws IOException { + enter("createSocket"); + //print("Calling createSocket(" + host + " " + port + ")"); + + StateMachine.waitState(CREATE_SOCKET); + Socket s = new Socket(host, port); + leave("createSocket"); + + return s; + } + } + private static RMIClientFactory clientFactory = new RMIClientFactory(); + + private static int CREATE_SOCKET = 1; + private static int FREE_LISTENER = 3; + private static int NO_OP = 0; + + public static class StateMachine { + + private static int state = NO_OP; + private static int[] lock = new int[0]; + + public static void waitState(int s) { + synchronized (lock) { + while (state != s) { + try { + lock.wait(); + } catch (InterruptedException ire) { + // should not + throw new Error(ire); + } + } + } + } + + public static int getState() { + synchronized (lock) { + return state; + } + } + + public static void setState(int s) { + synchronized (lock) { + state = s; + lock.notifyAll(); + } + } + } + + private static void print(String m) { + System.out.println(m); + } + + private static void enter(String m) { + System.out.println("\n---Enter the method " + m); + } + + private static void leave(String m) { + System.out.println("===Leave the method: " + m); + } +} + -- GitLab From 571d9660ab85e5a6f50c6ad569757585a301fb8c Mon Sep 17 00:00:00 2001 From: mullan Date: Mon, 22 Sep 2008 10:43:17 -0400 Subject: [PATCH 124/139] 6469266: Integrate Apache XMLSec 1.4.2 into JDK 7 Reviewed-by: valeriep --- .../apache/xml/internal/security/Init.java | 90 +- .../security/algorithms/Algorithm.java | 10 +- .../security/algorithms/JCEMapper.java | 17 +- .../algorithms/MessageDigestAlgorithm.java | 29 +- .../algorithms/SignatureAlgorithm.java | 146 ++- .../algorithms/SignatureAlgorithmSpi.java | 27 +- .../implementations/IntegrityHmac.java | 45 +- .../implementations/SignatureBaseRSA.java | 635 ++++++------ .../implementations/SignatureDSA.java | 625 ++++++------ .../implementations/SignatureECDSA.java | 384 ++++++++ .../c14n/CanonicalizationException.java | 1 - .../internal/security/c14n/Canonicalizer.java | 566 +++++------ .../c14n/InvalidCanonicalizerException.java | 1 - .../security/c14n/helper/AttrCompare.java | 166 ++-- .../c14n/implementations/Canonicalizer11.java | 684 +++++++++++++ .../Canonicalizer11_OmitComments.java | 41 + .../Canonicalizer11_WithComments.java | 41 + .../Canonicalizer20010315.java | 300 +++--- .../Canonicalizer20010315Excl.java | 90 +- ...Canonicalizer20010315ExclWithComments.java | 1 + .../Canonicalizer20010315WithComments.java | 1 - .../implementations/CanonicalizerBase.java | 702 +++++++------ .../implementations/NameSpaceSymbTable.java | 226 ++--- .../c14n/implementations/UtfHelpper.java | 155 +++ .../security/encryption/XMLCipher.java | 611 +++++------- .../security/encryption/XMLCipherInput.java | 124 ++- ...tentHandlerAlreadyRegisteredException.java | 3 +- .../xml/internal/security/keys/KeyInfo.java | 408 +++----- .../xml/internal/security/keys/KeyUtils.java | 3 +- .../security/keys/content/KeyInfoContent.java | 3 +- .../security/keys/content/KeyName.java | 9 +- .../security/keys/content/KeyValue.java | 234 +++-- .../security/keys/content/MgmtData.java | 9 +- .../security/keys/content/PGPData.java | 9 +- .../keys/content/RetrievalMethod.java | 11 +- .../security/keys/content/SPKIData.java | 9 +- .../security/keys/content/X509Data.java | 72 +- .../keys/content/keyvalues/DSAKeyValue.java | 9 +- .../content/keyvalues/KeyValueContent.java | 3 +- .../keys/content/keyvalues/RSAKeyValue.java | 10 +- .../keys/content/x509/XMLX509CRL.java | 12 +- .../keys/content/x509/XMLX509Certificate.java | 41 +- .../keys/content/x509/XMLX509DataContent.java | 3 +- .../content/x509/XMLX509IssuerSerial.java | 259 +++-- .../keys/content/x509/XMLX509SKI.java | 285 +++--- .../keys/content/x509/XMLX509SubjectName.java | 34 +- .../InvalidKeyResolverException.java | 3 +- .../keys/keyresolver/KeyResolver.java | 179 ++-- .../keyresolver/KeyResolverException.java | 3 +- .../keys/keyresolver/KeyResolverSpi.java | 169 ++-- .../implementations/DSAKeyValueResolver.java | 70 +- .../implementations/EncryptedKeyResolver.java | 69 +- .../implementations/RSAKeyValueResolver.java | 67 +- .../RetrievalMethodResolver.java | 349 +++---- .../X509CertificateResolver.java | 91 +- .../X509IssuerSerialResolver.java | 86 +- .../implementations/X509SKIResolver.java | 104 +- .../X509SubjectNameResolver.java | 107 +- .../keys/storage/StorageResolver.java | 29 +- .../storage/StorageResolverException.java | 3 +- .../keys/storage/StorageResolverSpi.java | 3 +- .../CertsInFilesystemDirectoryResolver.java | 25 +- .../implementations/KeyStoreResolver.java | 10 +- .../SingleCertificateResolver.java | 10 +- .../xml/internal/security/resource/config.dtd | 146 +-- .../xml/internal/security/resource/config.xml | 779 ++++++++------- .../security/resource/schema/etsi.xsd | 694 ++++++------- .../resource/schema/xmldsig-core-schema.dtd | 2 +- .../resource/schema/xmldsig-core-schema.xsd | 2 +- .../resource/xmlsecurity_en.properties | 246 ++--- .../InvalidDigestValueException.java | 1 - .../InvalidSignatureValueException.java | 1 - .../internal/security/signature/Manifest.java | 52 +- .../MissingResourceFailureException.java | 1 - .../security/signature/NodeFilter.java | 19 +- .../security/signature/ObjectContainer.java | 21 +- .../security/signature/Reference.java | 129 ++- .../ReferenceNotInitializedException.java | 1 - .../signature/SignatureProperties.java | 8 +- .../security/signature/SignatureProperty.java | 11 +- .../security/signature/SignedInfo.java | 305 +++--- .../security/signature/XMLSignature.java | 168 ++-- .../signature/XMLSignatureException.java | 1 - .../security/signature/XMLSignatureInput.java | 931 +++++++++--------- .../signature/XMLSignatureInputDebugger.java | 27 +- .../transforms/InvalidTransformException.java | 1 - .../security/transforms/Transform.java | 593 ++++++----- .../security/transforms/TransformParam.java | 2 +- .../security/transforms/TransformSpi.java | 159 +-- .../transforms/TransformationException.java | 1 - .../security/transforms/Transforms.java | 491 ++++----- .../implementations/FuncHereContext.java | 2 +- .../TransformBase64Decode.java | 13 +- .../implementations/TransformC14N.java | 13 +- .../implementations/TransformC14N11.java | 65 ++ .../TransformC14N11_WithComments.java | 67 ++ .../TransformC14NExclusive.java | 23 +- .../TransformC14NExclusiveWithComments.java | 20 +- .../TransformC14NWithComments.java | 17 +- .../TransformEnvelopedSignature.java | 41 +- .../implementations/TransformXPath.java | 76 +- .../TransformXPath2Filter.java | 151 ++- .../implementations/TransformXPointer.java | 4 +- .../implementations/TransformXSLT.java | 73 +- .../params/XPath2FilterContainer.java | 7 +- .../params/XPath2FilterContainer04.java | 8 +- .../params/XPathFilterCHGPContainer.java | 10 +- .../xml/internal/security/utils/Base64.java | 166 ++-- .../security/utils/CachedXPathAPIHolder.java | 26 +- .../utils/CachedXPathFuncHereAPI.java | 16 +- .../internal/security/utils/Constants.java | 9 +- .../security/utils/DigesterOutputStream.java | 46 +- .../security/utils/ElementChecker.java | 17 + .../security/utils/ElementCheckerImpl.java | 60 ++ .../internal/security/utils/ElementProxy.java | 185 ++-- .../security/utils/EncryptionConstants.java | 2 +- .../utils/EncryptionElementProxy.java | 2 +- .../security/utils/HelperNodeList.java | 12 +- .../xml/internal/security/utils/I18n.java | 9 +- .../internal/security/utils/IdResolver.java | 377 +++---- .../internal/security/utils/JavaUtils.java | 176 ++-- .../security/utils/RFC2253Parser.java | 12 +- .../security/utils/SignatureElementProxy.java | 16 +- .../security/utils/SignerOutputStream.java | 31 +- .../utils/UnsyncBufferedOutputStream.java | 9 +- .../utils/UnsyncByteArrayOutputStream.java | 18 +- .../xml/internal/security/utils/XMLUtils.java | 153 ++- .../security/utils/XPathFuncHereAPI.java | 3 +- .../utils/resolver/ResourceResolver.java | 119 ++- .../resolver/ResourceResolverException.java | 3 +- .../utils/resolver/ResourceResolverSpi.java | 59 +- .../implementations/ResolverAnonymous.java | 6 +- .../implementations/ResolverDirectHTTP.java | 54 +- .../implementations/ResolverFragment.java | 33 +- .../ResolverLocalFilesystem.java | 35 +- .../implementations/ResolverXPointer.java | 13 +- .../dsig/internal/DigesterOutputStream.java | 8 +- .../xml/dsig/internal/MacOutputStream.java | 19 +- .../xml/dsig/internal/SignerOutputStream.java | 8 +- .../internal/dom/ApacheCanonicalizer.java | 51 +- .../jcp/xml/dsig/internal/dom/ApacheData.java | 37 +- .../dsig/internal/dom/ApacheNodeSetData.java | 39 +- .../internal/dom/ApacheOctetStreamData.java | 37 +- .../dsig/internal/dom/ApacheTransform.java | 41 +- .../dsig/internal/dom/DOMBase64Transform.java | 37 +- .../dom/DOMCanonicalXMLC14N11Method.java | 79 ++ .../dom/DOMCanonicalXMLC14NMethod.java | 37 +- .../dom/DOMCanonicalizationMethod.java | 46 +- .../dsig/internal/dom/DOMCryptoBinary.java | 37 +- .../dsig/internal/dom/DOMDigestMethod.java | 39 +- .../internal/dom/DOMEnvelopedTransform.java | 37 +- .../dsig/internal/dom/DOMExcC14NMethod.java | 39 +- .../internal/dom/DOMHMACSignatureMethod.java | 37 +- .../jcp/xml/dsig/internal/dom/DOMKeyInfo.java | 51 +- .../dsig/internal/dom/DOMKeyInfoFactory.java | 43 +- .../jcp/xml/dsig/internal/dom/DOMKeyName.java | 37 +- .../xml/dsig/internal/dom/DOMKeyValue.java | 37 +- .../xml/dsig/internal/dom/DOMManifest.java | 44 +- .../jcp/xml/dsig/internal/dom/DOMPGPData.java | 37 +- .../xml/dsig/internal/dom/DOMReference.java | 167 ++-- .../dsig/internal/dom/DOMRetrievalMethod.java | 46 +- .../dsig/internal/dom/DOMSignatureMethod.java | 52 +- .../internal/dom/DOMSignatureProperties.java | 37 +- .../internal/dom/DOMSignatureProperty.java | 37 +- .../xml/dsig/internal/dom/DOMSignedInfo.java | 58 +- .../xml/dsig/internal/dom/DOMStructure.java | 37 +- .../xml/dsig/internal/dom/DOMSubTreeData.java | 37 +- .../xml/dsig/internal/dom/DOMTransform.java | 51 +- .../dsig/internal/dom/DOMURIDereferencer.java | 45 +- .../jcp/xml/dsig/internal/dom/DOMUtils.java | 45 +- .../xml/dsig/internal/dom/DOMX509Data.java | 39 +- .../internal/dom/DOMX509IssuerSerial.java | 37 +- .../xml/dsig/internal/dom/DOMXMLObject.java | 44 +- .../dsig/internal/dom/DOMXMLSignature.java | 56 +- .../internal/dom/DOMXMLSignatureFactory.java | 76 +- .../dom/DOMXPathFilter2Transform.java | 41 +- .../dsig/internal/dom/DOMXPathTransform.java | 37 +- .../dsig/internal/dom/DOMXSLTTransform.java | 37 +- .../org/jcp/xml/dsig/internal/dom/Utils.java | 37 +- .../jcp/xml/dsig/internal/dom/XMLDSigRI.java | 54 +- .../org/jcp/xml/dsig/internal/package.html | 5 - .../xml/crypto/dsig/GenerationTests.java | 4 + 182 files changed, 9492 insertions(+), 8034 deletions(-) create mode 100644 src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/SignatureECDSA.java create mode 100644 src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer11.java create mode 100644 src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer11_OmitComments.java create mode 100644 src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer11_WithComments.java create mode 100644 src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/UtfHelpper.java create mode 100644 src/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformC14N11.java create mode 100644 src/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformC14N11_WithComments.java create mode 100644 src/share/classes/com/sun/org/apache/xml/internal/security/utils/ElementChecker.java create mode 100644 src/share/classes/com/sun/org/apache/xml/internal/security/utils/ElementCheckerImpl.java create mode 100644 src/share/classes/org/jcp/xml/dsig/internal/dom/DOMCanonicalXMLC14N11Method.java delete mode 100644 src/share/classes/org/jcp/xml/dsig/internal/package.html diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/Init.java b/src/share/classes/com/sun/org/apache/xml/internal/security/Init.java index 845c36a5f..5b1acfd6a 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/Init.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/Init.java @@ -20,12 +20,9 @@ */ package com.sun.org.apache.xml.internal.security; - - import java.io.InputStream; import java.security.AccessController; import java.security.PrivilegedAction; - import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -51,7 +48,7 @@ import org.w3c.dom.Node; * done by calling {@link Init#init} which should be done in any static block * of the files of this library. We ensure that this call is only executed once. * - * @author $Author: raul $ + * @author $Author: mullan $ */ public final class Init { @@ -113,20 +110,19 @@ public final class Init { dbf.setValidating(false); DocumentBuilder db = dbf.newDocumentBuilder(); - // We don't allow users to override the Apache XML Security // configuration in the JRE. Users should use the standard security // provider mechanism instead if implementing their own // transform or canonicalization algorithms. - // String cfile = System.getProperty("com.sun.org.apache.xml.internal.security.resource.config"); - // InputStream is = - // Class.forName("com.sun.org.apache.xml.internal.security.Init") - // .getResourceAsStream(cfile != null ? cfile : "resource/config.xml"); + // InputStream is = Class.forName("com.sun.org.apache.xml.internal.security.Init").getResourceAsStream("resource/config.xml"); InputStream is = (InputStream) AccessController.doPrivileged( new PrivilegedAction() { public Object run() { +// String cfile = System.getProperty +// ("com.sun.org.apache.xml.internal.security.resource.config"); return getClass().getResourceAsStream - ("resource/config.xml"); +// (cfile != null ? cfile : "resource/config.xml"); + ("resource/config.xml"); } }); @@ -167,7 +163,7 @@ public final class Init { // // if (tag.equals("ResourceBundles")){ // XX_configure_i18n_start = System.currentTimeMillis(); -// Element resource=(Element)el; +// Element resource=(Element)el; // /* configure internationalization */ // Attr langAttr = resource.getAttributeNode("defaultLanguageCode"); // Attr countryAttr = resource.getAttributeNode("defaultCountryCode"); @@ -202,11 +198,11 @@ public final class Init { if (currMeth.getDeclaringClass().getName() .equals(JAVACLASS)) { - log.log(java.util.logging.Level.FINE, currMeth.getDeclaringClass().toString()); + log.log(java.util.logging.Level.FINE, currMeth.getDe claringClass().toString()); } }*/ - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Canonicalizer.register(" + URI + ", " + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Canonicalizer.register(" + URI + ", " + JAVACLASS + ")"); Canonicalizer.register(URI, JAVACLASS); } catch (ClassNotFoundException e) { @@ -233,9 +229,8 @@ public final class Init { "JAVACLASS"); try { Class.forName(JAVACLASS); - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Transform.register(" + URI + ", " + JAVACLASS - + ")"); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Transform.register(" + URI + ", " + JAVACLASS + ")"); Transform.register(URI, JAVACLASS); } catch (ClassNotFoundException e) { Object exArgs[] = { URI, JAVACLASS }; @@ -284,12 +279,11 @@ public final class Init { // // if (currMeth.getDeclaringClass().getName() // .equals(JAVACLASS)) { -// log.log(java.util.logging.Level.FINE, currMeth.getDeclaringClass().toString()); +// log.log(java.util.logging.Level.FINE, currMeth.getDe claringClass().toString()); // } // } - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "SignatureAlgorithm.register(" + URI + ", " - + JAVACLASS + ")"); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "SignatureAlgorithm.register(" + URI + ", " + JAVACLASS + ")"); SignatureAlgorithm.register(URI, JAVACLASS); } catch (ClassNotFoundException e) { Object exArgs[] = { URI, JAVACLASS }; @@ -320,13 +314,11 @@ public final class Init { "DESCRIPTION"); if ((Description != null) && (Description.length() > 0)) { - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Register Resolver: " + JAVACLASS + ": " - + Description); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Register Resolver: " + JAVACLASS + ": " + Description); } else { - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Register Resolver: " + JAVACLASS - + ": For unknown purposes"); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Register Resolver: " + JAVACLASS + ": For unknown purposes"); } try { ResourceResolver.register(JAVACLASS); @@ -359,13 +351,11 @@ public final class Init { "DESCRIPTION"); if ((Description != null) && (Description.length() > 0)) { - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Register Resolver: " + JAVACLASS + ": " - + Description); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Register Resolver: " + JAVACLASS + ": " + Description); } else { - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Register Resolver: " + JAVACLASS - + ": For unknown purposes"); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Register Resolver: " + JAVACLASS + ": For unknown purposes"); } KeyResolver.register(JAVACLASS); @@ -376,8 +366,8 @@ public final class Init { if (tag.equals("PrefixMappings")){ XX_configure_reg_prefixes_start = System.currentTimeMillis(); - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Now I try to bind prefixes:"); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Now I try to bind prefixes:"); Element[] nl = XMLUtils.selectNodes(el.getFirstChild(), CONF_NS,"PrefixMapping"); @@ -386,8 +376,8 @@ public final class Init { "namespace"); String prefix = nl[i].getAttributeNS(null, "prefix"); - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Now I try to bind " + prefix + " to " + namespace); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Now I try to bind " + prefix + " to " + namespace); com.sun.org.apache.xml.internal.security.utils.ElementProxy .setDefaultPrefix(namespace, prefix); } @@ -398,19 +388,19 @@ public final class Init { long XX_init_end = System.currentTimeMillis(); //J- - if (true) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "XX_init " + ((int)(XX_init_end - XX_init_start)) + " ms"); - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, " XX_prng " + ((int)(XX_prng_end - XX_prng_start)) + " ms"); - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, " XX_parsing " + ((int)(XX_parsing_end - XX_parsing_start)) + " ms"); - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, " XX_configure_i18n " + ((int)(XX_configure_i18n_end- XX_configure_i18n_start)) + " ms"); - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, " XX_configure_reg_c14n " + ((int)(XX_configure_reg_c14n_end- XX_configure_reg_c14n_start)) + " ms"); - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, " XX_configure_reg_jcemapper " + ((int)(XX_configure_reg_jcemapper_end- XX_configure_reg_jcemapper_start)) + " ms"); - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, " XX_configure_reg_keyInfo " + ((int)(XX_configure_reg_keyInfo_end- XX_configure_reg_keyInfo_start)) + " ms"); - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, " XX_configure_reg_keyResolver " + ((int)(XX_configure_reg_keyResolver_end- XX_configure_reg_keyResolver_start)) + " ms"); - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, " XX_configure_reg_prefixes " + ((int)(XX_configure_reg_prefixes_end- XX_configure_reg_prefixes_start)) + " ms"); - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, " XX_configure_reg_resourceresolver " + ((int)(XX_configure_reg_resourceresolver_end- XX_configure_reg_resourceresolver_start)) + " ms"); - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, " XX_configure_reg_sigalgos " + ((int)(XX_configure_reg_sigalgos_end- XX_configure_reg_sigalgos_start)) + " ms"); - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, " XX_configure_reg_transforms " + ((int)(XX_configure_reg_transforms_end- XX_configure_reg_transforms_start)) + " ms"); + if (log.isLoggable(java.util.logging.Level.FINE)) { + log.log(java.util.logging.Level.FINE, "XX_init " + ((int)(XX_init_end - XX_init_start)) + " ms"); + log.log(java.util.logging.Level.FINE, " XX_prng " + ((int)(XX_prng_end - XX_prng_start)) + " ms"); + log.log(java.util.logging.Level.FINE, " XX_parsing " + ((int)(XX_parsing_end - XX_parsing_start)) + " ms"); + log.log(java.util.logging.Level.FINE, " XX_configure_i18n " + ((int)(XX_configure_i18n_end- XX_configure_i18n_start)) + " ms"); + log.log(java.util.logging.Level.FINE, " XX_configure_reg_c14n " + ((int)(XX_configure_reg_c14n_end- XX_configure_reg_c14n_start)) + " ms"); + log.log(java.util.logging.Level.FINE, " XX_configure_reg_jcemapper " + ((int)(XX_configure_reg_jcemapper_end- XX_configure_reg_jcemapper_start)) + " ms"); + log.log(java.util.logging.Level.FINE, " XX_configure_reg_keyInfo " + ((int)(XX_configure_reg_keyInfo_end- XX_configure_reg_keyInfo_start)) + " ms"); + log.log(java.util.logging.Level.FINE, " XX_configure_reg_keyResolver " + ((int)(XX_configure_reg_keyResolver_end- XX_configure_reg_keyResolver_start)) + " ms"); + log.log(java.util.logging.Level.FINE, " XX_configure_reg_prefixes " + ((int)(XX_configure_reg_prefixes_end- XX_configure_reg_prefixes_start)) + " ms"); + log.log(java.util.logging.Level.FINE, " XX_configure_reg_resourceresolver " + ((int)(XX_configure_reg_resourceresolver_end- XX_configure_reg_resourceresolver_start)) + " ms"); + log.log(java.util.logging.Level.FINE, " XX_configure_reg_sigalgos " + ((int)(XX_configure_reg_sigalgos_end- XX_configure_reg_sigalgos_start)) + " ms"); + log.log(java.util.logging.Level.FINE, " XX_configure_reg_transforms " + ((int)(XX_configure_reg_transforms_end- XX_configure_reg_transforms_start)) + " ms"); } } catch (Exception e) { log.log(java.util.logging.Level.SEVERE, "Bad: ", e); diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/Algorithm.java b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/Algorithm.java index 2f326f0a7..62fd0fe49 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/Algorithm.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/Algorithm.java @@ -24,7 +24,7 @@ package com.sun.org.apache.xml.internal.security.algorithms; import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; import com.sun.org.apache.xml.internal.security.utils.Constants; -import com.sun.org.apache.xml.internal.security.utils.ElementProxy; +import com.sun.org.apache.xml.internal.security.utils.SignatureElementProxy; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -33,11 +33,7 @@ import org.w3c.dom.Element; * The Algorithm class which stores the Algorithm URI as a string. * */ -public abstract class Algorithm extends ElementProxy { - - /** {@link java.util.logging} logging facility */ - static java.util.logging.Logger log = - java.util.logging.Logger.getLogger(Algorithm.class.getName()); +public abstract class Algorithm extends SignatureElementProxy { /** * @@ -79,7 +75,7 @@ public abstract class Algorithm extends ElementProxy { */ protected void setAlgorithmURI(String algorithmURI) { - if ((this._state == MODE_CREATE) && (algorithmURI != null)) { + if ( (algorithmURI != null)) { this._constructionElement.setAttributeNS(null, Constants._ATT_ALGORITHM, algorithmURI); } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/JCEMapper.java b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/JCEMapper.java index 475d4591b..7a2391359 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/JCEMapper.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/JCEMapper.java @@ -35,7 +35,7 @@ import org.w3c.dom.Element; /** * This class maps algorithm identifier URIs to JAVA JCE class names. * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class JCEMapper { @@ -45,9 +45,9 @@ public class JCEMapper { - private static Map uriToJCEName = new HashMap(); + private static Map uriToJCEName; - private static Map algorithmsMap = new HashMap(); + private static Map algorithmsMap; private static String providerName = null; /** @@ -63,6 +63,8 @@ public class JCEMapper { static void loadAlgorithms( Element algorithmsEl) { Element[] algorithms = XMLUtils.selectNodes(algorithmsEl.getFirstChild(),Init.CONF_NS,"Algorithm"); + uriToJCEName = new HashMap( algorithms.length * 2); + algorithmsMap = new HashMap( algorithms.length * 2); for (int i = 0 ;i < algorithms.length ;i ++) { Element el = algorithms[i]; String id = el.getAttribute("URI"); @@ -70,6 +72,7 @@ public class JCEMapper { uriToJCEName.put(id, jceName); algorithmsMap.put(id, new Algorithm(el)); } + } static Algorithm getAlgorithmMapping(String algoURI) { @@ -84,8 +87,8 @@ public class JCEMapper { * */ public static String translateURItoJCEID(String AlgorithmURI) { - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Request for URI " + AlgorithmURI); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Request for URI " + AlgorithmURI); String jceName = (String) uriToJCEName.get(AlgorithmURI); return jceName; @@ -100,8 +103,8 @@ public class JCEMapper { * */ public static String getAlgorithmClassFromURI(String AlgorithmURI) { - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Request for URI " + AlgorithmURI); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Request for URI " + AlgorithmURI); return ((Algorithm) algorithmsMap.get(AlgorithmURI)).algorithmClass; } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/MessageDigestAlgorithm.java b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/MessageDigestAlgorithm.java index 805d061a2..fa62ef351 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/MessageDigestAlgorithm.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/MessageDigestAlgorithm.java @@ -20,10 +20,10 @@ */ package com.sun.org.apache.xml.internal.security.algorithms; - - import java.security.MessageDigest; import java.security.NoSuchProviderException; +import java.util.HashMap; +import java.util.Map; import com.sun.org.apache.xml.internal.security.signature.XMLSignatureException; import com.sun.org.apache.xml.internal.security.utils.Constants; @@ -41,11 +41,6 @@ import org.w3c.dom.Document; */ public class MessageDigestAlgorithm extends Algorithm { - /** {@link java.util.logging} logging facility */ - static java.util.logging.Logger log = - java.util.logging.Logger.getLogger( - MessageDigestAlgorithm.class.getName()); - /** Message Digest - NOT RECOMMENDED MD5*/ public static final String ALGO_ID_DIGEST_NOT_RECOMMENDED_MD5 = Constants.MoreAlgorithmsSpecNS + "md5"; /** Digest - Required SHA1*/ @@ -76,6 +71,12 @@ public class MessageDigestAlgorithm extends Algorithm { this.algorithm = messageDigest; } + static ThreadLocal instances=new ThreadLocal() { + protected Object initialValue() { + return new HashMap(); + }; + }; + /** * Factory method for constructing a message digest algorithm by name. * @@ -86,8 +87,15 @@ public class MessageDigestAlgorithm extends Algorithm { */ public static MessageDigestAlgorithm getInstance( Document doc, String algorithmURI) throws XMLSignatureException { + MessageDigest md = getDigestInstance(algorithmURI); + return new MessageDigestAlgorithm(doc, md, algorithmURI); + } - String algorithmID = JCEMapper.translateURItoJCEID(algorithmURI); +private static MessageDigest getDigestInstance(String algorithmURI) throws XMLSignatureException { + MessageDigest result=(MessageDigest) ((Map)instances.get()).get(algorithmURI); + if (result!=null) + return result; + String algorithmID = JCEMapper.translateURItoJCEID(algorithmURI); if (algorithmID == null) { Object[] exArgs = { algorithmURI }; @@ -113,8 +121,9 @@ public class MessageDigestAlgorithm extends Algorithm { throw new XMLSignatureException("algorithms.NoSuchAlgorithm", exArgs); } - return new MessageDigestAlgorithm(doc, md, algorithmURI); - } + ((Map)instances.get()).put(algorithmURI, md); + return md; +} /** * Returns the actual {@link java.security.MessageDigest} algorithm object diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/SignatureAlgorithm.java b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/SignatureAlgorithm.java index 3ebb782a3..567df3cbb 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/SignatureAlgorithm.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/SignatureAlgorithm.java @@ -25,6 +25,7 @@ import java.security.Key; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.util.HashMap; +import java.util.Map; import com.sun.org.apache.xml.internal.security.algorithms.implementations.IntegrityHmac; import com.sun.org.apache.xml.internal.security.exceptions.AlgorithmAlreadyRegisteredException; @@ -52,9 +53,35 @@ public class SignatureAlgorithm extends Algorithm { /** All available algorithm classes are registered here */ static HashMap _algorithmHash = null; + static ThreadLocal instancesSigning=new ThreadLocal() { + protected Object initialValue() { + return new HashMap(); + }; + }; + + static ThreadLocal instancesVerify=new ThreadLocal() { + protected Object initialValue() { + return new HashMap(); + }; + }; + + static ThreadLocal keysSigning=new ThreadLocal() { + protected Object initialValue() { + return new HashMap(); + }; + }; + static ThreadLocal keysVerify=new ThreadLocal() { + protected Object initialValue() { + return new HashMap(); + }; + }; +// boolean isForSigning=false; + /** Field _signatureAlgorithm */ protected SignatureAlgorithmSpi _signatureAlgorithm = null; + private String algorithmURI; + /** * Constructor SignatureAlgorithm * @@ -64,18 +91,49 @@ public class SignatureAlgorithm extends Algorithm { */ public SignatureAlgorithm(Document doc, String algorithmURI) throws XMLSecurityException { - super(doc, algorithmURI); + this.algorithmURI = algorithmURI; + } - try { + + private void initializeAlgorithm(boolean isForSigning) throws XMLSignatureException { + if (_signatureAlgorithm!=null) { + return; + } + _signatureAlgorithm=isForSigning ? getInstanceForSigning(algorithmURI) : getInstanceForVerify(algorithmURI); + this._signatureAlgorithm + .engineGetContextFromElement(this._constructionElement); + } + private static SignatureAlgorithmSpi getInstanceForSigning(String algorithmURI) throws XMLSignatureException { + SignatureAlgorithmSpi result=(SignatureAlgorithmSpi) ((Map)instancesSigning.get()).get(algorithmURI); + if (result!=null) { + result.reset(); + return result; + } + result=buildSigner(algorithmURI, result); + ((Map)instancesSigning.get()).put(algorithmURI,result); + return result; + } + private static SignatureAlgorithmSpi getInstanceForVerify(String algorithmURI) throws XMLSignatureException { + SignatureAlgorithmSpi result=(SignatureAlgorithmSpi) ((Map)instancesVerify.get()).get(algorithmURI); + if (result!=null) { + result.reset(); + return result; + } + result=buildSigner(algorithmURI, result); + ((Map)instancesVerify.get()).put(algorithmURI,result); + return result; + } + + private static SignatureAlgorithmSpi buildSigner(String algorithmURI, SignatureAlgorithmSpi result) throws XMLSignatureException { + try { Class implementingClass = SignatureAlgorithm.getImplementingClass(algorithmURI); - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Create URI \"" + algorithmURI + "\" class \"" + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Create URI \"" + algorithmURI + "\" class \"" + implementingClass + "\""); - - this._signatureAlgorithm = - (SignatureAlgorithmSpi) implementingClass.newInstance(); + result=(SignatureAlgorithmSpi) implementingClass.newInstance(); + return result; } catch (IllegalAccessException ex) { Object exArgs[] = { algorithmURI, ex.getMessage() }; @@ -92,7 +150,7 @@ public class SignatureAlgorithm extends Algorithm { throw new XMLSignatureException("algorithms.NoSuchAlgorithm", exArgs, ex); } - } +} /** * Constructor SignatureAlgorithm @@ -107,7 +165,8 @@ public class SignatureAlgorithm extends Algorithm { throws XMLSecurityException { this(doc, algorithmURI); - + this.algorithmURI=algorithmURI; + initializeAlgorithm(true); this._signatureAlgorithm.engineSetHMACOutputLength(HMACOutputLength); ((IntegrityHmac)this._signatureAlgorithm) .engineAddContextToElement(this._constructionElement); @@ -124,37 +183,7 @@ public class SignatureAlgorithm extends Algorithm { throws XMLSecurityException { super(element, BaseURI); - - String algorithmURI = this.getURI(); - - try { - Class implementingClass = - SignatureAlgorithm.getImplementingClass(algorithmURI); - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Create URI \"" + algorithmURI + "\" class \"" - + implementingClass + "\""); - - this._signatureAlgorithm = - (SignatureAlgorithmSpi) implementingClass.newInstance(); - - this._signatureAlgorithm - .engineGetContextFromElement(this._constructionElement); - } catch (IllegalAccessException ex) { - Object exArgs[] = { algorithmURI, ex.getMessage() }; - - throw new XMLSignatureException("algorithms.NoSuchAlgorithm", exArgs, - ex); - } catch (InstantiationException ex) { - Object exArgs[] = { algorithmURI, ex.getMessage() }; - - throw new XMLSignatureException("algorithms.NoSuchAlgorithm", exArgs, - ex); - } catch (NullPointerException ex) { - Object exArgs[] = { algorithmURI, ex.getMessage() }; - - throw new XMLSignatureException("algorithms.NoSuchAlgorithm", exArgs, - ex); - } + algorithmURI = this.getURI(); } /** @@ -175,7 +204,12 @@ public class SignatureAlgorithm extends Algorithm { * @return the result of the {@link java.security.Signature#getAlgorithm} method */ public String getJCEAlgorithmString() { - return this._signatureAlgorithm.engineGetJCEAlgorithmString(); + try { + return getInstanceForVerify(algorithmURI).engineGetJCEAlgorithmString(); + } catch (XMLSignatureException e) { + //Ignore. + return null; + } } /** @@ -184,7 +218,11 @@ public class SignatureAlgorithm extends Algorithm { * @return The Provider of this Signature Alogrithm */ public String getJCEProviderName() { - return this._signatureAlgorithm.engineGetJCEProviderName(); + try { + return getInstanceForVerify(algorithmURI).engineGetJCEProviderName(); + } catch (XMLSignatureException e) { + return null; + } } /** @@ -231,7 +269,13 @@ public class SignatureAlgorithm extends Algorithm { * @throws XMLSignatureException */ public void initSign(Key signingKey) throws XMLSignatureException { - this._signatureAlgorithm.engineInitSign(signingKey); + initializeAlgorithm(true); + Map map=(Map)keysSigning.get(); + if (map.get(this.algorithmURI)==signingKey) { + return; + } + map.put(this.algorithmURI,signingKey); + this._signatureAlgorithm.engineInitSign(signingKey); } /** @@ -244,6 +288,7 @@ public class SignatureAlgorithm extends Algorithm { */ public void initSign(Key signingKey, SecureRandom secureRandom) throws XMLSignatureException { + initializeAlgorithm(true); this._signatureAlgorithm.engineInitSign(signingKey, secureRandom); } @@ -258,6 +303,7 @@ public class SignatureAlgorithm extends Algorithm { public void initSign( Key signingKey, AlgorithmParameterSpec algorithmParameterSpec) throws XMLSignatureException { + initializeAlgorithm(true); this._signatureAlgorithm.engineInitSign(signingKey, algorithmParameterSpec); } @@ -282,7 +328,13 @@ public class SignatureAlgorithm extends Algorithm { * @throws XMLSignatureException */ public void initVerify(Key verificationKey) throws XMLSignatureException { - this._signatureAlgorithm.engineInitVerify(verificationKey); + initializeAlgorithm(false); + Map map=(Map)keysVerify.get(); + if (map.get(this.algorithmURI)==verificationKey) { + return; + } + map.put(this.algorithmURI,verificationKey); + this._signatureAlgorithm.engineInitVerify(verificationKey); } /** @@ -320,7 +372,7 @@ public class SignatureAlgorithm extends Algorithm { .getLogger(SignatureAlgorithm.class.getName()); } - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Init() called"); + log.log(java.util.logging.Level.FINE, "Init() called"); if (!SignatureAlgorithm._alreadyInitialized) { SignatureAlgorithm._algorithmHash = new HashMap(10); @@ -340,8 +392,8 @@ public class SignatureAlgorithm extends Algorithm { throws AlgorithmAlreadyRegisteredException,XMLSignatureException { { - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Try to register " + algorithmURI + " " + implementingClass); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Try to register " + algorithmURI + " " + implementingClass); // are we already registered? Class registeredClassClass = diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/SignatureAlgorithmSpi.java b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/SignatureAlgorithmSpi.java index 1ae46db4b..c47be7e2c 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/SignatureAlgorithmSpi.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/SignatureAlgorithmSpi.java @@ -20,27 +20,20 @@ */ package com.sun.org.apache.xml.internal.security.algorithms; - - import java.security.Key; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import com.sun.org.apache.xml.internal.security.signature.XMLSignatureException; -import org.w3c.dom.Document; import org.w3c.dom.Element; /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public abstract class SignatureAlgorithmSpi { - /** {@link java.util.logging} logging facility */ - static java.util.logging.Logger log = - java.util.logging.Logger.getLogger(SignatureAlgorithmSpi.class.getName()); - /** * Returns the URI representation of Transformation algorithm * @@ -167,20 +160,6 @@ public abstract class SignatureAlgorithmSpi { protected abstract void engineSetParameter(AlgorithmParameterSpec params) throws XMLSignatureException; - /** Field _doc */ - Document _doc = null; - - /** - * Method engineSetDocument - * - * @param doc - */ - protected void engineSetDocument(Document doc) { - this._doc = doc; - } - - /** Field _constructionElement */ - Element _constructionElement = null; /** * Method engineGetContextFromElement @@ -188,7 +167,6 @@ public abstract class SignatureAlgorithmSpi { * @param element */ protected void engineGetContextFromElement(Element element) { - this._constructionElement = element; } /** @@ -199,4 +177,7 @@ public abstract class SignatureAlgorithmSpi { */ protected abstract void engineSetHMACOutputLength(int HMACOutputLength) throws XMLSignatureException; + + public void reset() { + } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/IntegrityHmac.java b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/IntegrityHmac.java index 0e89024bf..d3495bb56 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/IntegrityHmac.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/IntegrityHmac.java @@ -45,7 +45,7 @@ import org.w3c.dom.Text; /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public abstract class IntegrityHmac extends SignatureAlgorithmSpi { @@ -74,8 +74,8 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi { public IntegrityHmac() throws XMLSignatureException { String algorithmID = JCEMapper.translateURItoJCEID(this.engineGetURI()); - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Created IntegrityHmacSHA1 using " + algorithmID); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Created IntegrityHmacSHA1 using " + algorithmID); try { this._macAlgorithm = Mac.getInstance(algorithmID); @@ -99,6 +99,10 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi { throw new XMLSignatureException("empty"); } + public void reset() { + _HMACOutputLength=0; + } + /** * Proxy method for {@link java.security.Signature#verify(byte[])} * which is executed on the internal {@link java.security.Signature} object. @@ -145,7 +149,20 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi { try { this._macAlgorithm.init(secretKey); } catch (InvalidKeyException ex) { - throw new XMLSignatureException("empty", ex); + // reinstantiate Mac object to work around bug in JDK + // see: http://bugs.sun.com/view_bug.do?bug_id=4953555 + Mac mac = this._macAlgorithm; + try { + this._macAlgorithm = Mac.getInstance + (_macAlgorithm.getAlgorithm()); + } catch (Exception e) { + // this shouldn't occur, but if it does, restore previous Mac + if (log.isLoggable(java.util.logging.Level.FINE)) { + log.log(java.util.logging.Level.FINE, "Exception when reinstantiating Mac:" + e); + } + this._macAlgorithm = mac; + } + throw new XMLSignatureException("empty", ex); } } @@ -323,7 +340,7 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi { */ protected String engineGetJCEAlgorithmString() { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "engineGetJCEAlgorithmString()"); + log.log(java.util.logging.Level.FINE, "engineGetJCEAlgorithmString()"); return this._macAlgorithm.getAlgorithm(); } @@ -397,7 +414,8 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi { /** * Class IntegrityHmacSHA1 * - * @author $Author: raul $ + * @author $Author: mullan $ + * @version $Revision: 1.5 $ */ public static class IntegrityHmacSHA1 extends IntegrityHmac { @@ -423,7 +441,8 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi { /** * Class IntegrityHmacSHA256 * - * @author $Author: raul $ + * @author $Author: mullan $ + * @version $Revision: 1.5 $ */ public static class IntegrityHmacSHA256 extends IntegrityHmac { @@ -449,7 +468,8 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi { /** * Class IntegrityHmacSHA384 * - * @author $Author: raul $ + * @author $Author: mullan $ + * @version $Revision: 1.5 $ */ public static class IntegrityHmacSHA384 extends IntegrityHmac { @@ -475,7 +495,8 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi { /** * Class IntegrityHmacSHA512 * - * @author $Author: raul $ + * @author $Author: mullan $ + * @version $Revision: 1.5 $ */ public static class IntegrityHmacSHA512 extends IntegrityHmac { @@ -501,7 +522,8 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi { /** * Class IntegrityHmacRIPEMD160 * - * @author $Author: raul $ + * @author $Author: mullan $ + * @version $Revision: 1.5 $ */ public static class IntegrityHmacRIPEMD160 extends IntegrityHmac { @@ -527,7 +549,8 @@ public abstract class IntegrityHmac extends SignatureAlgorithmSpi { /** * Class IntegrityHmacMD5 * - * @author $Author: raul $ + * @author $Author: mullan $ + * @version $Revision: 1.5 $ */ public static class IntegrityHmacMD5 extends IntegrityHmac { diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/SignatureBaseRSA.java b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/SignatureBaseRSA.java index fde23e8fc..ccc01b01c 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/SignatureBaseRSA.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/SignatureBaseRSA.java @@ -3,7 +3,7 @@ * DO NOT REMOVE OR ALTER! */ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2007 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,6 @@ */ package com.sun.org.apache.xml.internal.security.algorithms.implementations; - - import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; @@ -38,329 +36,344 @@ import com.sun.org.apache.xml.internal.security.algorithms.SignatureAlgorithmSpi import com.sun.org.apache.xml.internal.security.signature.XMLSignature; import com.sun.org.apache.xml.internal.security.signature.XMLSignatureException; - /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public abstract class SignatureBaseRSA extends SignatureAlgorithmSpi { - /** {@link java.util.logging} logging facility */ + /** {@link java.util.logging} logging facility */ static java.util.logging.Logger log = - java.util.logging.Logger.getLogger(SignatureBaseRSA.class.getName()); + java.util.logging.Logger.getLogger + (SignatureBaseRSA.class.getName()); /** @inheritDoc */ - public abstract String engineGetURI(); + public abstract String engineGetURI(); - /** Field algorithm */ - private java.security.Signature _signatureAlgorithm = null; + /** Field algorithm */ + private java.security.Signature _signatureAlgorithm = null; - /** - * Constructor SignatureRSA - * - * @throws XMLSignatureException - */ - public SignatureBaseRSA() throws XMLSignatureException { + /** + * Constructor SignatureRSA + * + * @throws XMLSignatureException + */ + public SignatureBaseRSA() throws XMLSignatureException { - String algorithmID = JCEMapper.translateURItoJCEID(this.engineGetURI()); + String algorithmID = JCEMapper.translateURItoJCEID(this.engineGetURI()); - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Created SignatureDSA using " + algorithmID); - String provider=JCEMapper.getProviderId(); - try { - if (provider==null) { + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Created SignatureRSA using " + algorithmID); + String provider=JCEMapper.getProviderId(); + try { + if (provider==null) { this._signatureAlgorithm = Signature.getInstance(algorithmID); - } else { + } else { this._signatureAlgorithm = Signature.getInstance(algorithmID,provider); - } - } catch (java.security.NoSuchAlgorithmException ex) { - Object[] exArgs = { algorithmID, - ex.getLocalizedMessage() }; + } + } catch (java.security.NoSuchAlgorithmException ex) { + Object[] exArgs = { algorithmID, ex.getLocalizedMessage() }; + + throw new XMLSignatureException("algorithms.NoSuchAlgorithm", exArgs); + } catch (NoSuchProviderException ex) { + Object[] exArgs = { algorithmID, ex.getLocalizedMessage() }; + + throw new XMLSignatureException("algorithms.NoSuchAlgorithm", exArgs); + } + } + + /** @inheritDoc */ + protected void engineSetParameter(AlgorithmParameterSpec params) + throws XMLSignatureException { + + try { + this._signatureAlgorithm.setParameter(params); + } catch (InvalidAlgorithmParameterException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected boolean engineVerify(byte[] signature) + throws XMLSignatureException { + + try { + return this._signatureAlgorithm.verify(signature); + } catch (SignatureException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected void engineInitVerify(Key publicKey) throws XMLSignatureException { + + if (!(publicKey instanceof PublicKey)) { + String supplied = publicKey.getClass().getName(); + String needed = PublicKey.class.getName(); + Object exArgs[] = { supplied, needed }; + + throw new XMLSignatureException + ("algorithms.WrongKeyForThisOperation", exArgs); + } + + try { + this._signatureAlgorithm.initVerify((PublicKey) publicKey); + } catch (InvalidKeyException ex) { + // reinstantiate Signature object to work around bug in JDK + // see: http://bugs.sun.com/view_bug.do?bug_id=4953555 + Signature sig = this._signatureAlgorithm; + try { + this._signatureAlgorithm = Signature.getInstance + (_signatureAlgorithm.getAlgorithm()); + } catch (Exception e) { + // this shouldn't occur, but if it does, restore previous + // Signature + if (log.isLoggable(java.util.logging.Level.FINE)) { + log.log(java.util.logging.Level.FINE, "Exception when reinstantiating Signature:" + e); + } + this._signatureAlgorithm = sig; + } + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected byte[] engineSign() throws XMLSignatureException { + try { + return this._signatureAlgorithm.sign(); + } catch (SignatureException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected void engineInitSign(Key privateKey, SecureRandom secureRandom) + throws XMLSignatureException { + + if (!(privateKey instanceof PrivateKey)) { + String supplied = privateKey.getClass().getName(); + String needed = PrivateKey.class.getName(); + Object exArgs[] = { supplied, needed }; + + throw new XMLSignatureException + ("algorithms.WrongKeyForThisOperation", exArgs); + } + + try { + this._signatureAlgorithm.initSign + ((PrivateKey) privateKey, secureRandom); + } catch (InvalidKeyException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected void engineInitSign(Key privateKey) throws XMLSignatureException { + + if (!(privateKey instanceof PrivateKey)) { + String supplied = privateKey.getClass().getName(); + String needed = PrivateKey.class.getName(); + Object exArgs[] = { supplied, needed }; + + throw new XMLSignatureException + ("algorithms.WrongKeyForThisOperation", exArgs); + } + + try { + this._signatureAlgorithm.initSign((PrivateKey) privateKey); + } catch (InvalidKeyException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected void engineUpdate(byte[] input) throws XMLSignatureException { + try { + this._signatureAlgorithm.update(input); + } catch (SignatureException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected void engineUpdate(byte input) throws XMLSignatureException { + try { + this._signatureAlgorithm.update(input); + } catch (SignatureException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected void engineUpdate(byte buf[], int offset, int len) + throws XMLSignatureException { + try { + this._signatureAlgorithm.update(buf, offset, len); + } catch (SignatureException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected String engineGetJCEAlgorithmString() { + return this._signatureAlgorithm.getAlgorithm(); + } + + /** @inheritDoc */ + protected String engineGetJCEProviderName() { + return this._signatureAlgorithm.getProvider().getName(); + } + + /** @inheritDoc */ + protected void engineSetHMACOutputLength(int HMACOutputLength) + throws XMLSignatureException { + throw new XMLSignatureException + ("algorithms.HMACOutputLengthOnlyForHMAC"); + } - throw new XMLSignatureException("algorithms.NoSuchAlgorithm", exArgs); - } catch (NoSuchProviderException ex) { - Object[] exArgs = { algorithmID, - ex.getLocalizedMessage() }; + /** @inheritDoc */ + protected void engineInitSign( + Key signingKey, AlgorithmParameterSpec algorithmParameterSpec) + throws XMLSignatureException { + throw new XMLSignatureException( + "algorithms.CannotUseAlgorithmParameterSpecOnRSA"); + } + + /** + * Class SignatureRSASHA1 + * + * @author $Author: mullan $ + * @version $Revision: 1.5 $ + */ + public static class SignatureRSASHA1 extends SignatureBaseRSA { + + /** + * Constructor SignatureRSASHA1 + * + * @throws XMLSignatureException + */ + public SignatureRSASHA1() throws XMLSignatureException { + super(); + } + + /** @inheritDoc */ + public String engineGetURI() { + return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1; + } + } + + /** + * Class SignatureRSASHA256 + * + * @author $Author: mullan $ + * @version $Revision: 1.5 $ + */ + public static class SignatureRSASHA256 extends SignatureBaseRSA { + + /** + * Constructor SignatureRSASHA256 + * + * @throws XMLSignatureException + */ + public SignatureRSASHA256() throws XMLSignatureException { + super(); + } + + /** @inheritDoc */ + public String engineGetURI() { + return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256; + } + } + + /** + * Class SignatureRSASHA384 + * + * @author $Author: mullan $ + * @version $Revision: 1.5 $ + */ + public static class SignatureRSASHA384 extends SignatureBaseRSA { + + /** + * Constructor SignatureRSASHA384 + * + * @throws XMLSignatureException + */ + public SignatureRSASHA384() throws XMLSignatureException { + super(); + } + + /** @inheritDoc */ + public String engineGetURI() { + return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA384; + } + } + + /** + * Class SignatureRSASHA512 + * + * @author $Author: mullan $ + * @version $Revision: 1.5 $ + */ + public static class SignatureRSASHA512 extends SignatureBaseRSA { + + /** + * Constructor SignatureRSASHA512 + * + * @throws XMLSignatureException + */ + public SignatureRSASHA512() throws XMLSignatureException { + super(); + } + + /** @inheritDoc */ + public String engineGetURI() { + return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512; + } + } + + /** + * Class SignatureRSARIPEMD160 + * + * @author $Author: mullan $ + * @version $Revision: 1.5 $ + */ + public static class SignatureRSARIPEMD160 extends SignatureBaseRSA { + + /** + * Constructor SignatureRSARIPEMD160 + * + * @throws XMLSignatureException + */ + public SignatureRSARIPEMD160() throws XMLSignatureException { + super(); + } + + /** @inheritDoc */ + public String engineGetURI() { + return XMLSignature.ALGO_ID_SIGNATURE_RSA_RIPEMD160; + } + } + + /** + * Class SignatureRSAMD5 + * + * @author $Author: mullan $ + * @version $Revision: 1.5 $ + */ + public static class SignatureRSAMD5 extends SignatureBaseRSA { + + /** + * Constructor SignatureRSAMD5 + * + * @throws XMLSignatureException + */ + public SignatureRSAMD5() throws XMLSignatureException { + super(); + } - throw new XMLSignatureException("algorithms.NoSuchAlgorithm", exArgs); + /** @inheritDoc */ + public String engineGetURI() { + return XMLSignature.ALGO_ID_SIGNATURE_NOT_RECOMMENDED_RSA_MD5; } - } - - /** @inheritDoc */ - protected void engineSetParameter(AlgorithmParameterSpec params) - throws XMLSignatureException { - - try { - this._signatureAlgorithm.setParameter(params); - } catch (InvalidAlgorithmParameterException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** @inheritDoc */ - protected boolean engineVerify(byte[] signature) - throws XMLSignatureException { - - try { - return this._signatureAlgorithm.verify(signature); - } catch (SignatureException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** @inheritDoc */ - protected void engineInitVerify(Key publicKey) throws XMLSignatureException { - - if (!(publicKey instanceof PublicKey)) { - String supplied = publicKey.getClass().getName(); - String needed = PublicKey.class.getName(); - Object exArgs[] = { supplied, needed }; - - throw new XMLSignatureException("algorithms.WrongKeyForThisOperation", - exArgs); - } - - try { - this._signatureAlgorithm.initVerify((PublicKey) publicKey); - } catch (InvalidKeyException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** @inheritDoc */ - protected byte[] engineSign() throws XMLSignatureException { - - try { - return this._signatureAlgorithm.sign(); - } catch (SignatureException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** @inheritDoc */ - protected void engineInitSign(Key privateKey, SecureRandom secureRandom) - throws XMLSignatureException { - - if (!(privateKey instanceof PrivateKey)) { - String supplied = privateKey.getClass().getName(); - String needed = PrivateKey.class.getName(); - Object exArgs[] = { supplied, needed }; - - throw new XMLSignatureException("algorithms.WrongKeyForThisOperation", - exArgs); - } - - try { - this._signatureAlgorithm.initSign((PrivateKey) privateKey, - secureRandom); - } catch (InvalidKeyException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** @inheritDoc */ - protected void engineInitSign(Key privateKey) throws XMLSignatureException { - - if (!(privateKey instanceof PrivateKey)) { - String supplied = privateKey.getClass().getName(); - String needed = PrivateKey.class.getName(); - Object exArgs[] = { supplied, needed }; - - throw new XMLSignatureException("algorithms.WrongKeyForThisOperation", - exArgs); - } - - try { - this._signatureAlgorithm.initSign((PrivateKey) privateKey); - } catch (InvalidKeyException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** @inheritDoc */ - protected void engineUpdate(byte[] input) throws XMLSignatureException { - - try { - this._signatureAlgorithm.update(input); - } catch (SignatureException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** @inheritDoc */ - protected void engineUpdate(byte input) throws XMLSignatureException { - - try { - this._signatureAlgorithm.update(input); - } catch (SignatureException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** @inheritDoc */ - protected void engineUpdate(byte buf[], int offset, int len) - throws XMLSignatureException { - - try { - this._signatureAlgorithm.update(buf, offset, len); - } catch (SignatureException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** @inheritDoc */ - protected String engineGetJCEAlgorithmString() { - return this._signatureAlgorithm.getAlgorithm(); - } - - /** @inheritDoc */ - protected String engineGetJCEProviderName() { - return this._signatureAlgorithm.getProvider().getName(); - } - - /** @inheritDoc */ - protected void engineSetHMACOutputLength(int HMACOutputLength) - throws XMLSignatureException { - throw new XMLSignatureException("algorithms.HMACOutputLengthOnlyForHMAC"); - } - - /** @inheritDoc */ - protected void engineInitSign( - Key signingKey, AlgorithmParameterSpec algorithmParameterSpec) - throws XMLSignatureException { - throw new XMLSignatureException( - "algorithms.CannotUseAlgorithmParameterSpecOnRSA"); - } - - /** - * Class SignatureRSASHA1 - * - * @author $Author: raul $ - */ - public static class SignatureRSASHA1 extends SignatureBaseRSA { - - /** - * Constructor SignatureRSASHA1 - * - * @throws XMLSignatureException - */ - public SignatureRSASHA1() throws XMLSignatureException { - super(); - } - - /** @inheritDoc */ - public String engineGetURI() { - return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1; - } - } - - /** - * Class SignatureRSASHA256 - * - * @author $Author: raul $ - */ - public static class SignatureRSASHA256 extends SignatureBaseRSA { - - /** - * Constructor SignatureRSASHA256 - * - * @throws XMLSignatureException - */ - public SignatureRSASHA256() throws XMLSignatureException { - super(); - } - - /** @inheritDoc */ - public String engineGetURI() { - return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256; - } - } - - /** - * Class SignatureRSASHA384 - * - * @author $Author: raul $ - */ - public static class SignatureRSASHA384 extends SignatureBaseRSA { - - /** - * Constructor SignatureRSASHA384 - * - * @throws XMLSignatureException - */ - public SignatureRSASHA384() throws XMLSignatureException { - super(); - } - - /** @inheritDoc */ - public String engineGetURI() { - return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA384; - } - } - - /** - * Class SignatureRSASHA512 - * - * @author $Author: raul $ - */ - public static class SignatureRSASHA512 extends SignatureBaseRSA { - - /** - * Constructor SignatureRSASHA512 - * - * @throws XMLSignatureException - */ - public SignatureRSASHA512() throws XMLSignatureException { - super(); - } - - /** @inheritDoc */ - public String engineGetURI() { - return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512; - } - } - - /** - * Class SignatureRSARIPEMD160 - * - * @author $Author: raul $ - */ - public static class SignatureRSARIPEMD160 extends SignatureBaseRSA { - - /** - * Constructor SignatureRSARIPEMD160 - * - * @throws XMLSignatureException - */ - public SignatureRSARIPEMD160() throws XMLSignatureException { - super(); - } - - /** @inheritDoc */ - public String engineGetURI() { - return XMLSignature.ALGO_ID_SIGNATURE_RSA_RIPEMD160; - } - } - - /** - * Class SignatureRSAMD5 - * - * @author $Author: raul $ - */ - public static class SignatureRSAMD5 extends SignatureBaseRSA { - - /** - * Constructor SignatureRSAMD5 - * - * @throws XMLSignatureException - */ - public SignatureRSAMD5() throws XMLSignatureException { - super(); - } - - /** @inheritDoc */ - public String engineGetURI() { - return XMLSignature.ALGO_ID_SIGNATURE_NOT_RECOMMENDED_RSA_MD5; - } - } + } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/SignatureDSA.java b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/SignatureDSA.java index 355579b7e..615aa436e 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/SignatureDSA.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/SignatureDSA.java @@ -20,8 +20,6 @@ */ package com.sun.org.apache.xml.internal.security.algorithms.implementations; - - import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -39,342 +37,359 @@ import com.sun.org.apache.xml.internal.security.signature.XMLSignatureException; import com.sun.org.apache.xml.internal.security.utils.Base64; import com.sun.org.apache.xml.internal.security.utils.Constants; - /** * - * @author $Author: vishal $ + * @author $Author: mullan $ */ public class SignatureDSA extends SignatureAlgorithmSpi { - /** {@link java.util.logging} logging facility */ + /** {@link java.util.logging} logging facility */ static java.util.logging.Logger log = java.util.logging.Logger.getLogger(SignatureDSA.class.getName()); - /** Field _URI */ - public static final String _URI = Constants.SignatureSpecNS + "dsa-sha1"; - - /** Field algorithm */ - private java.security.Signature _signatureAlgorithm = null; - - /** - * Method engineGetURI - * - * @inheritDoc - */ - protected String engineGetURI() { - return SignatureDSA._URI; - } - - /** - * Constructor SignatureDSA - * - * @throws XMLSignatureException - */ - public SignatureDSA() throws XMLSignatureException { - - String algorithmID = JCEMapper.translateURItoJCEID(SignatureDSA._URI); - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Created SignatureDSA using " + algorithmID); - - try { - this._signatureAlgorithm = Signature.getInstance(algorithmID); - } catch (java.security.NoSuchAlgorithmException ex) { - Object[] exArgs = { algorithmID, - ex.getLocalizedMessage() }; - - throw new XMLSignatureException("algorithms.NoSuchAlgorithm", exArgs); - } - } - - /** - * @inheritDoc - */ - protected void engineSetParameter(AlgorithmParameterSpec params) - throws XMLSignatureException { - - try { - this._signatureAlgorithm.setParameter(params); - } catch (InvalidAlgorithmParameterException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** - * @inheritDoc - */ - protected boolean engineVerify(byte[] signature) + /** Field _URI */ + public static final String _URI = Constants.SignatureSpecNS + "dsa-sha1"; + + /** Field algorithm */ + private java.security.Signature _signatureAlgorithm = null; + + /** + * Method engineGetURI + * + * @inheritDoc + */ + protected String engineGetURI() { + return SignatureDSA._URI; + } + + /** + * Constructor SignatureDSA + * + * @throws XMLSignatureException + */ + public SignatureDSA() throws XMLSignatureException { + + String algorithmID = JCEMapper.translateURItoJCEID(SignatureDSA._URI); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Created SignatureDSA using " + algorithmID); + + String provider = JCEMapper.getProviderId(); + try { + if (provider == null) { + this._signatureAlgorithm = Signature.getInstance(algorithmID); + } else { + this._signatureAlgorithm = + Signature.getInstance(algorithmID, provider); + } + } catch (java.security.NoSuchAlgorithmException ex) { + Object[] exArgs = { algorithmID, ex.getLocalizedMessage() }; + throw new XMLSignatureException("algorithms.NoSuchAlgorithm", exArgs); + } catch (java.security.NoSuchProviderException ex) { + Object[] exArgs = { algorithmID, ex.getLocalizedMessage() }; + throw new XMLSignatureException("algorithms.NoSuchAlgorithm", exArgs); + } + } + + /** + * @inheritDoc + */ + protected void engineSetParameter(AlgorithmParameterSpec params) + throws XMLSignatureException { + + try { + this._signatureAlgorithm.setParameter(params); + } catch (InvalidAlgorithmParameterException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** + * @inheritDoc + */ + protected boolean engineVerify(byte[] signature) throws XMLSignatureException { - try { - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Called DSA.verify() on " + Base64.encode(signature)); - - byte[] jcebytes = SignatureDSA.convertXMLDSIGtoASN1(signature); - - return this._signatureAlgorithm.verify(jcebytes); - } catch (SignatureException ex) { - throw new XMLSignatureException("empty", ex); - } catch (IOException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** - * @inheritDoc - */ - protected void engineInitVerify(Key publicKey) throws XMLSignatureException { - - if (!(publicKey instanceof PublicKey)) { - String supplied = publicKey.getClass().getName(); - String needed = PublicKey.class.getName(); - Object exArgs[] = { supplied, needed }; - - throw new XMLSignatureException("algorithms.WrongKeyForThisOperation", - exArgs); - } - - try { - this._signatureAlgorithm.initVerify((PublicKey) publicKey); - } catch (InvalidKeyException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** - * @inheritDoc - */ - protected byte[] engineSign() throws XMLSignatureException { - - try { - byte jcebytes[] = this._signatureAlgorithm.sign(); - - return SignatureDSA.convertASN1toXMLDSIG(jcebytes); - } catch (IOException ex) { - throw new XMLSignatureException("empty", ex); - } catch (SignatureException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** - * @inheritDoc - */ - protected void engineInitSign(Key privateKey, SecureRandom secureRandom) + try { + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Called DSA.verify() on " + Base64.encode(signature)); + + byte[] jcebytes = SignatureDSA.convertXMLDSIGtoASN1(signature); + + return this._signatureAlgorithm.verify(jcebytes); + } catch (SignatureException ex) { + throw new XMLSignatureException("empty", ex); + } catch (IOException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** + * @inheritDoc + */ + protected void engineInitVerify(Key publicKey) throws XMLSignatureException { + + if (!(publicKey instanceof PublicKey)) { + String supplied = publicKey.getClass().getName(); + String needed = PublicKey.class.getName(); + Object exArgs[] = { supplied, needed }; + + throw new XMLSignatureException + ("algorithms.WrongKeyForThisOperation", exArgs); + } + + try { + this._signatureAlgorithm.initVerify((PublicKey) publicKey); + } catch (InvalidKeyException ex) { + // reinstantiate Signature object to work around bug in JDK + // see: http://bugs.sun.com/view_bug.do?bug_id=4953555 + Signature sig = this._signatureAlgorithm; + try { + this._signatureAlgorithm = Signature.getInstance + (_signatureAlgorithm.getAlgorithm()); + } catch (Exception e) { + // this shouldn't occur, but if it does, restore previous + // Signature + if (log.isLoggable(java.util.logging.Level.FINE)) { + log.log(java.util.logging.Level.FINE, "Exception when reinstantiating Signature:" + e); + } + this._signatureAlgorithm = sig; + } + throw new XMLSignatureException("empty", ex); + } + } + + /** + * @inheritDoc + */ + protected byte[] engineSign() throws XMLSignatureException { + + try { + byte jcebytes[] = this._signatureAlgorithm.sign(); + + return SignatureDSA.convertASN1toXMLDSIG(jcebytes); + } catch (IOException ex) { + throw new XMLSignatureException("empty", ex); + } catch (SignatureException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** + * @inheritDoc + */ + protected void engineInitSign(Key privateKey, SecureRandom secureRandom) throws XMLSignatureException { - if (!(privateKey instanceof PrivateKey)) { - String supplied = privateKey.getClass().getName(); - String needed = PrivateKey.class.getName(); - Object exArgs[] = { supplied, needed }; + if (!(privateKey instanceof PrivateKey)) { + String supplied = privateKey.getClass().getName(); + String needed = PrivateKey.class.getName(); + Object exArgs[] = { supplied, needed }; - throw new XMLSignatureException("algorithms.WrongKeyForThisOperation", - exArgs); - } + throw new XMLSignatureException + ("algorithms.WrongKeyForThisOperation", exArgs); + } - try { - this._signatureAlgorithm.initSign((PrivateKey) privateKey, + try { + this._signatureAlgorithm.initSign((PrivateKey) privateKey, secureRandom); - } catch (InvalidKeyException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** - * @inheritDoc - */ - protected void engineInitSign(Key privateKey) throws XMLSignatureException { - - if (!(privateKey instanceof PrivateKey)) { - String supplied = privateKey.getClass().getName(); - String needed = PrivateKey.class.getName(); - Object exArgs[] = { supplied, needed }; - - throw new XMLSignatureException("algorithms.WrongKeyForThisOperation", - exArgs); - } - - try { - this._signatureAlgorithm.initSign((PrivateKey) privateKey); - } catch (InvalidKeyException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** - * @inheritDoc - */ - protected void engineUpdate(byte[] input) throws XMLSignatureException { - - try { - this._signatureAlgorithm.update(input); - } catch (SignatureException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** - * @inheritDoc - */ - protected void engineUpdate(byte input) throws XMLSignatureException { - - try { - this._signatureAlgorithm.update(input); - } catch (SignatureException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** - * @inheritDoc - */ - protected void engineUpdate(byte buf[], int offset, int len) - throws XMLSignatureException { - - try { - this._signatureAlgorithm.update(buf, offset, len); - } catch (SignatureException ex) { - throw new XMLSignatureException("empty", ex); - } - } - - /** - * Method engineGetJCEAlgorithmString - * - * @inheritDoc - */ - protected String engineGetJCEAlgorithmString() { - return this._signatureAlgorithm.getAlgorithm(); - } - - /** - * Method engineGetJCEProviderName - * - * @inheritDoc - */ - protected String engineGetJCEProviderName() { - return this._signatureAlgorithm.getProvider().getName(); - } - - - /** - * Converts an ASN.1 DSA value to a XML Signature DSA Value. - * - * The JAVA JCE DSA Signature algorithm creates ASN.1 encoded (r,s) value - * pairs; the XML Signature requires the core BigInteger values. - * - * @param asn1Bytes - * @return the decode bytes - * - * @throws IOException - * @see 6.4.1 DSA - */ - private static byte[] convertASN1toXMLDSIG(byte asn1Bytes[]) + } catch (InvalidKeyException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** + * @inheritDoc + */ + protected void engineInitSign(Key privateKey) throws XMLSignatureException { + + if (!(privateKey instanceof PrivateKey)) { + String supplied = privateKey.getClass().getName(); + String needed = PrivateKey.class.getName(); + Object exArgs[] = { supplied, needed }; + + throw new XMLSignatureException + ("algorithms.WrongKeyForThisOperation", exArgs); + } + + try { + this._signatureAlgorithm.initSign((PrivateKey) privateKey); + } catch (InvalidKeyException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** + * @inheritDoc + */ + protected void engineUpdate(byte[] input) throws XMLSignatureException { + try { + this._signatureAlgorithm.update(input); + } catch (SignatureException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** + * @inheritDoc + */ + protected void engineUpdate(byte input) throws XMLSignatureException { + try { + this._signatureAlgorithm.update(input); + } catch (SignatureException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** + * @inheritDoc + */ + protected void engineUpdate(byte buf[], int offset, int len) + throws XMLSignatureException { + try { + this._signatureAlgorithm.update(buf, offset, len); + } catch (SignatureException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** + * Method engineGetJCEAlgorithmString + * + * @inheritDoc + */ + protected String engineGetJCEAlgorithmString() { + return this._signatureAlgorithm.getAlgorithm(); + } + + /** + * Method engineGetJCEProviderName + * + * @inheritDoc + */ + protected String engineGetJCEProviderName() { + return this._signatureAlgorithm.getProvider().getName(); + } + + /** + * Converts an ASN.1 DSA value to a XML Signature DSA Value. + * + * The JAVA JCE DSA Signature algorithm creates ASN.1 encoded (r,s) value + * pairs; the XML Signature requires the core BigInteger values. + * + * @param asn1Bytes + * @return the decode bytes + * + * @throws IOException + * @see 6.4.1 DSA + */ + private static byte[] convertASN1toXMLDSIG(byte asn1Bytes[]) throws IOException { - byte rLength = asn1Bytes[3]; - int i; + byte rLength = asn1Bytes[3]; + int i; - for (i = rLength; (i > 0) && (asn1Bytes[(4 + rLength) - i] == 0); i--); + for (i = rLength; (i > 0) && (asn1Bytes[(4 + rLength) - i] == 0); i--); - byte sLength = asn1Bytes[5 + rLength]; - int j; + byte sLength = asn1Bytes[5 + rLength]; + int j; - for (j = sLength; + for (j = sLength; (j > 0) && (asn1Bytes[(6 + rLength + sLength) - j] == 0); j--); - if ((asn1Bytes[0] != 48) || (asn1Bytes[1] != asn1Bytes.length - 2) + if ((asn1Bytes[0] != 48) || (asn1Bytes[1] != asn1Bytes.length - 2) || (asn1Bytes[2] != 2) || (i > 20) || (asn1Bytes[4 + rLength] != 2) || (j > 20)) { - throw new IOException("Invalid ASN.1 format of DSA signature"); - } - byte xmldsigBytes[] = new byte[40]; + throw new IOException("Invalid ASN.1 format of DSA signature"); + } + byte xmldsigBytes[] = new byte[40]; - System.arraycopy(asn1Bytes, (4 + rLength) - i, xmldsigBytes, 20 - i, + System.arraycopy(asn1Bytes, (4 + rLength) - i, xmldsigBytes, 20 - i, i); - System.arraycopy(asn1Bytes, (6 + rLength + sLength) - j, xmldsigBytes, + System.arraycopy(asn1Bytes, (6 + rLength + sLength) - j, xmldsigBytes, 40 - j, j); - return xmldsigBytes; - } - - /** - * Converts a XML Signature DSA Value to an ASN.1 DSA value. - * - * The JAVA JCE DSA Signature algorithm creates ASN.1 encoded (r,s) value - * pairs; the XML Signature requires the core BigInteger values. - * - * @param xmldsigBytes - * @return the encoded ASN.1 bytes - * - * @throws IOException - * @see 6.4.1 DSA - */ - private static byte[] convertXMLDSIGtoASN1(byte xmldsigBytes[]) + return xmldsigBytes; + } + + /** + * Converts a XML Signature DSA Value to an ASN.1 DSA value. + * + * The JAVA JCE DSA Signature algorithm creates ASN.1 encoded (r,s) value + * pairs; the XML Signature requires the core BigInteger values. + * + * @param xmldsigBytes + * @return the encoded ASN.1 bytes + * + * @throws IOException + * @see 6.4.1 DSA + */ + private static byte[] convertXMLDSIGtoASN1(byte xmldsigBytes[]) throws IOException { - if (xmldsigBytes.length != 40) { - throw new IOException("Invalid XMLDSIG format of DSA signature"); - } + if (xmldsigBytes.length != 40) { + throw new IOException("Invalid XMLDSIG format of DSA signature"); + } - int i; + int i; - for (i = 20; (i > 0) && (xmldsigBytes[20 - i] == 0); i--); + for (i = 20; (i > 0) && (xmldsigBytes[20 - i] == 0); i--); - int j = i; + int j = i; - if (xmldsigBytes[20 - i] < 0) { + if (xmldsigBytes[20 - i] < 0) { j += 1; - } - - int k; - - for (k = 20; (k > 0) && (xmldsigBytes[40 - k] == 0); k--); - - int l = k; - - if (xmldsigBytes[40 - k] < 0) { - l += 1; - } - - byte asn1Bytes[] = new byte[6 + j + l]; - - asn1Bytes[0] = 48; - asn1Bytes[1] = (byte) (4 + j + l); - asn1Bytes[2] = 2; - asn1Bytes[3] = (byte) j; - - System.arraycopy(xmldsigBytes, 20 - i, asn1Bytes, (4 + j) - i, i); - - asn1Bytes[4 + j] = 2; - asn1Bytes[5 + j] = (byte) l; - - System.arraycopy(xmldsigBytes, 40 - k, asn1Bytes, (6 + j + l) - k, k); - - return asn1Bytes; - } - - /** - * Method engineSetHMACOutputLength - * - * @param HMACOutputLength - * @throws XMLSignatureException - */ - protected void engineSetHMACOutputLength(int HMACOutputLength) - throws XMLSignatureException { - throw new XMLSignatureException("algorithms.HMACOutputLengthOnlyForHMAC"); - } - - /** - * Method engineInitSign - * - * @param signingKey - * @param algorithmParameterSpec - * @throws XMLSignatureException - */ - protected void engineInitSign( - Key signingKey, AlgorithmParameterSpec algorithmParameterSpec) - throws XMLSignatureException { - throw new XMLSignatureException( - "algorithms.CannotUseAlgorithmParameterSpecOnDSA"); - } + } + + int k; + + for (k = 20; (k > 0) && (xmldsigBytes[40 - k] == 0); k--); + + int l = k; + + if (xmldsigBytes[40 - k] < 0) { + l += 1; + } + + byte asn1Bytes[] = new byte[6 + j + l]; + + asn1Bytes[0] = 48; + asn1Bytes[1] = (byte) (4 + j + l); + asn1Bytes[2] = 2; + asn1Bytes[3] = (byte) j; + + System.arraycopy(xmldsigBytes, 20 - i, asn1Bytes, (4 + j) - i, i); + + asn1Bytes[4 + j] = 2; + asn1Bytes[5 + j] = (byte) l; + + System.arraycopy(xmldsigBytes, 40 - k, asn1Bytes, (6 + j + l) - k, k); + + return asn1Bytes; + } + + /** + * Method engineSetHMACOutputLength + * + * @param HMACOutputLength + * @throws XMLSignatureException + */ + protected void engineSetHMACOutputLength(int HMACOutputLength) + throws XMLSignatureException { + throw new XMLSignatureException( + "algorithms.HMACOutputLengthOnlyForHMAC"); + } + + /** + * Method engineInitSign + * + * @param signingKey + * @param algorithmParameterSpec + * @throws XMLSignatureException + */ + protected void engineInitSign( + Key signingKey, AlgorithmParameterSpec algorithmParameterSpec) + throws XMLSignatureException { + throw new XMLSignatureException( + "algorithms.CannotUseAlgorithmParameterSpecOnDSA"); + } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/SignatureECDSA.java b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/SignatureECDSA.java new file mode 100644 index 000000000..18fdffe28 --- /dev/null +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/SignatureECDSA.java @@ -0,0 +1,384 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* + * Copyright 1999-2004 The Apache Software Foundation. + * + * Licensed 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 com.sun.org.apache.xml.internal.security.algorithms.implementations; + + + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.AlgorithmParameterSpec; + +import com.sun.org.apache.xml.internal.security.algorithms.JCEMapper; +import com.sun.org.apache.xml.internal.security.algorithms.SignatureAlgorithmSpi; +import com.sun.org.apache.xml.internal.security.signature.XMLSignature; +import com.sun.org.apache.xml.internal.security.signature.XMLSignatureException; +import com.sun.org.apache.xml.internal.security.utils.Base64; + + +/** + * + * @author $Author: mullan $ + */ +public abstract class SignatureECDSA extends SignatureAlgorithmSpi { + + /** {@link java.util.logging} logging facility */ + static java.util.logging.Logger log = + java.util.logging.Logger.getLogger(SignatureECDSA.class.getName()); + + /** @inheritDoc */ + public abstract String engineGetURI(); + + /** Field algorithm */ + private java.security.Signature _signatureAlgorithm = null; + + /** + * Converts an ASN.1 ECDSA value to a XML Signature ECDSA Value. + * + * The JAVA JCE ECDSA Signature algorithm creates ASN.1 encoded (r,s) value + * pairs; the XML Signature requires the core BigInteger values. + * + * @param asn1Bytes + * @return the decode bytes + * + * @throws IOException + * @see 6.4.1 DSA + * @see 3.3. ECDSA Signatures + */ + private static byte[] convertASN1toXMLDSIG(byte asn1Bytes[]) + throws IOException { + + byte rLength = asn1Bytes[3]; + int i; + + for (i = rLength; (i > 0) && (asn1Bytes[(4 + rLength) - i] == 0); i--); + + byte sLength = asn1Bytes[5 + rLength]; + int j; + + for (j = sLength; + (j > 0) && (asn1Bytes[(6 + rLength + sLength) - j] == 0); j--); + + if ((asn1Bytes[0] != 48) || (asn1Bytes[1] != asn1Bytes.length - 2) + || (asn1Bytes[2] != 2) || (i > 24) + || (asn1Bytes[4 + rLength] != 2) || (j > 24)) { + throw new IOException("Invalid ASN.1 format of ECDSA signature"); + } + byte xmldsigBytes[] = new byte[48]; + + System.arraycopy(asn1Bytes, (4 + rLength) - i, xmldsigBytes, 24 - i, + i); + System.arraycopy(asn1Bytes, (6 + rLength + sLength) - j, xmldsigBytes, + 48 - j, j); + + return xmldsigBytes; + } + + /** + * Converts a XML Signature ECDSA Value to an ASN.1 DSA value. + * + * The JAVA JCE ECDSA Signature algorithm creates ASN.1 encoded (r,s) value + * pairs; the XML Signature requires the core BigInteger values. + * + * @param xmldsigBytes + * @return the encoded ASN.1 bytes + * + * @throws IOException + * @see 6.4.1 DSA + * @see 3.3. ECDSA Signatures + */ + private static byte[] convertXMLDSIGtoASN1(byte xmldsigBytes[]) + throws IOException { + + if (xmldsigBytes.length != 48) { + throw new IOException("Invalid XMLDSIG format of ECDSA signature"); + } + + int i; + + for (i = 24; (i > 0) && (xmldsigBytes[24 - i] == 0); i--); + + int j = i; + + if (xmldsigBytes[24 - i] < 0) { + j += 1; + } + + int k; + + for (k = 24; (k > 0) && (xmldsigBytes[48 - k] == 0); k--); + + int l = k; + + if (xmldsigBytes[48 - k] < 0) { + l += 1; + } + + byte asn1Bytes[] = new byte[6 + j + l]; + + asn1Bytes[0] = 48; + asn1Bytes[1] = (byte) (4 + j + l); + asn1Bytes[2] = 2; + asn1Bytes[3] = (byte) j; + + System.arraycopy(xmldsigBytes, 24 - i, asn1Bytes, (4 + j) - i, i); + + asn1Bytes[4 + j] = 2; + asn1Bytes[5 + j] = (byte) l; + + System.arraycopy(xmldsigBytes, 48 - k, asn1Bytes, (6 + j + l) - k, k); + + return asn1Bytes; + } + + /** + * Constructor SignatureRSA + * + * @throws XMLSignatureException + */ + public SignatureECDSA() throws XMLSignatureException { + + String algorithmID = JCEMapper.translateURItoJCEID(this.engineGetURI()); + + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Created SignatureECDSA using " + algorithmID); + String provider=JCEMapper.getProviderId(); + try { + if (provider==null) { + this._signatureAlgorithm = Signature.getInstance(algorithmID); + } else { + this._signatureAlgorithm = Signature.getInstance(algorithmID,provider); + } + } catch (java.security.NoSuchAlgorithmException ex) { + Object[] exArgs = { algorithmID, + ex.getLocalizedMessage() }; + + throw new XMLSignatureException("algorithms.NoSuchAlgorithm", exArgs); + } catch (NoSuchProviderException ex) { + Object[] exArgs = { algorithmID, + ex.getLocalizedMessage() }; + + throw new XMLSignatureException("algorithms.NoSuchAlgorithm", exArgs); + } + } + + /** @inheritDoc */ + protected void engineSetParameter(AlgorithmParameterSpec params) + throws XMLSignatureException { + + try { + this._signatureAlgorithm.setParameter(params); + } catch (InvalidAlgorithmParameterException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected boolean engineVerify(byte[] signature) + throws XMLSignatureException { + + try { + byte[] jcebytes = SignatureECDSA.convertXMLDSIGtoASN1(signature); + + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Called ECDSA.verify() on " + Base64.encode(signature)); + + return this._signatureAlgorithm.verify(jcebytes); + } catch (SignatureException ex) { + throw new XMLSignatureException("empty", ex); + } catch (IOException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected void engineInitVerify(Key publicKey) throws XMLSignatureException { + + if (!(publicKey instanceof PublicKey)) { + String supplied = publicKey.getClass().getName(); + String needed = PublicKey.class.getName(); + Object exArgs[] = { supplied, needed }; + + throw new XMLSignatureException("algorithms.WrongKeyForThisOperation", + exArgs); + } + + try { + this._signatureAlgorithm.initVerify((PublicKey) publicKey); + } catch (InvalidKeyException ex) { + // reinstantiate Signature object to work around bug in JDK + // see: http://bugs.sun.com/view_bug.do?bug_id=4953555 + Signature sig = this._signatureAlgorithm; + try { + this._signatureAlgorithm = Signature.getInstance + (_signatureAlgorithm.getAlgorithm()); + } catch (Exception e) { + // this shouldn't occur, but if it does, restore previous + // Signature + if (log.isLoggable(java.util.logging.Level.FINE)) { + log.log(java.util.logging.Level.FINE, "Exception when reinstantiating Signature:" + e); + } + this._signatureAlgorithm = sig; + } + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected byte[] engineSign() throws XMLSignatureException { + + try { + byte jcebytes[] = this._signatureAlgorithm.sign(); + + return SignatureECDSA.convertASN1toXMLDSIG(jcebytes); + } catch (SignatureException ex) { + throw new XMLSignatureException("empty", ex); + } catch (IOException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected void engineInitSign(Key privateKey, SecureRandom secureRandom) + throws XMLSignatureException { + + if (!(privateKey instanceof PrivateKey)) { + String supplied = privateKey.getClass().getName(); + String needed = PrivateKey.class.getName(); + Object exArgs[] = { supplied, needed }; + + throw new XMLSignatureException("algorithms.WrongKeyForThisOperation", + exArgs); + } + + try { + this._signatureAlgorithm.initSign((PrivateKey) privateKey, + secureRandom); + } catch (InvalidKeyException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected void engineInitSign(Key privateKey) throws XMLSignatureException { + + if (!(privateKey instanceof PrivateKey)) { + String supplied = privateKey.getClass().getName(); + String needed = PrivateKey.class.getName(); + Object exArgs[] = { supplied, needed }; + + throw new XMLSignatureException("algorithms.WrongKeyForThisOperation", + exArgs); + } + + try { + this._signatureAlgorithm.initSign((PrivateKey) privateKey); + } catch (InvalidKeyException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected void engineUpdate(byte[] input) throws XMLSignatureException { + + try { + this._signatureAlgorithm.update(input); + } catch (SignatureException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected void engineUpdate(byte input) throws XMLSignatureException { + + try { + this._signatureAlgorithm.update(input); + } catch (SignatureException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected void engineUpdate(byte buf[], int offset, int len) + throws XMLSignatureException { + + try { + this._signatureAlgorithm.update(buf, offset, len); + } catch (SignatureException ex) { + throw new XMLSignatureException("empty", ex); + } + } + + /** @inheritDoc */ + protected String engineGetJCEAlgorithmString() { + return this._signatureAlgorithm.getAlgorithm(); + } + + /** @inheritDoc */ + protected String engineGetJCEProviderName() { + return this._signatureAlgorithm.getProvider().getName(); + } + + /** @inheritDoc */ + protected void engineSetHMACOutputLength(int HMACOutputLength) + throws XMLSignatureException { + throw new XMLSignatureException("algorithms.HMACOutputLengthOnlyForHMAC"); + } + + /** @inheritDoc */ + protected void engineInitSign( + Key signingKey, AlgorithmParameterSpec algorithmParameterSpec) + throws XMLSignatureException { + throw new XMLSignatureException( + "algorithms.CannotUseAlgorithmParameterSpecOnRSA"); + } + + /** + * Class SignatureRSASHA1 + * + * @author $Author: mullan $ + * @version $Revision: 1.2 $ + */ + public static class SignatureECDSASHA1 extends SignatureECDSA { + + /** + * Constructor SignatureRSASHA1 + * + * @throws XMLSignatureException + */ + public SignatureECDSASHA1() throws XMLSignatureException { + super(); + } + + /** @inheritDoc */ + public String engineGetURI() { + return XMLSignature.ALGO_ID_SIGNATURE_ECDSA_SHA1; + } + } + +} diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/CanonicalizationException.java b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/CanonicalizationException.java index 732334c36..36c98cfe7 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/CanonicalizationException.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/CanonicalizationException.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/Canonicalizer.java b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/Canonicalizer.java index eb7cb6cad..a4181233d 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/Canonicalizer.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/Canonicalizer.java @@ -3,7 +3,7 @@ * DO NOT REMOVE OR ALTER! */ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2008 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,6 @@ */ package com.sun.org.apache.xml.internal.security.c14n; - - import java.io.ByteArrayInputStream; import java.io.OutputStream; import java.util.HashMap; @@ -37,318 +35,326 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; - /** - * * * @author Christian Geuer-Pollmann */ public class Canonicalizer { - //J- - /** The output encoding of canonicalized data */ - public static final String ENCODING = "UTF8"; + /** The output encoding of canonicalized data */ + public static final String ENCODING = "UTF8"; + + /** + * XPath Expresion for selecting every node and continuous comments joined + * in only one node + */ + public static final String XPATH_C14N_WITH_COMMENTS_SINGLE_NODE = + "(.//. | .//@* | .//namespace::*)"; + + /** + * The URL defined in XML-SEC Rec for inclusive c14n without comments. + */ + public static final String ALGO_ID_C14N_OMIT_COMMENTS = + "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"; + /** + * The URL defined in XML-SEC Rec for inclusive c14n with comments. + */ + public static final String ALGO_ID_C14N_WITH_COMMENTS = + ALGO_ID_C14N_OMIT_COMMENTS + "#WithComments"; + /** + * The URL defined in XML-SEC Rec for exclusive c14n without comments. + */ + public static final String ALGO_ID_C14N_EXCL_OMIT_COMMENTS = + "http://www.w3.org/2001/10/xml-exc-c14n#"; + /** + * The URL defined in XML-SEC Rec for exclusive c14n with comments. + */ + public static final String ALGO_ID_C14N_EXCL_WITH_COMMENTS = + ALGO_ID_C14N_EXCL_OMIT_COMMENTS + "WithComments"; + /** + * The URI for inclusive c14n 1.1 without comments. + */ + public static final String ALGO_ID_C14N11_OMIT_COMMENTS = + "http://www.w3.org/2006/12/xml-c14n11"; + /** + * The URI for inclusive c14n 1.1 with comments. + */ + public static final String ALGO_ID_C14N11_WITH_COMMENTS = + ALGO_ID_C14N11_OMIT_COMMENTS + "#WithComments"; + + static boolean _alreadyInitialized = false; + static Map _canonicalizerHash = null; + protected CanonicalizerSpi canonicalizerSpi = null; - /** - * XPath Expresion for selecting every node and continuos comments joined in only one node + /** + * Method init + * */ - public static final String XPATH_C14N_WITH_COMMENTS_SINGLE_NODE = "(.//. | .//@* | .//namespace::*)"; + public static void init() { + if (!Canonicalizer._alreadyInitialized) { + Canonicalizer._canonicalizerHash = new HashMap(10); + Canonicalizer._alreadyInitialized = true; + } + } - /** - * The URL defined in XML-SEC Rec for inclusive c14n without comments. + /** + * Constructor Canonicalizer + * + * @param algorithmURI + * @throws InvalidCanonicalizerException */ - public static final String ALGO_ID_C14N_OMIT_COMMENTS = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"; - /** - * The URL defined in XML-SEC Rec for inclusive c14n with comments. - */ - public static final String ALGO_ID_C14N_WITH_COMMENTS = ALGO_ID_C14N_OMIT_COMMENTS + "#WithComments"; - /** - * The URL defined in XML-SEC Rec for exclusive c14n without comments. - */ - public static final String ALGO_ID_C14N_EXCL_OMIT_COMMENTS = "http://www.w3.org/2001/10/xml-exc-c14n#"; - /** - * The URL defined in XML-SEC Rec for exclusive c14n with comments. - */ - public static final String ALGO_ID_C14N_EXCL_WITH_COMMENTS = ALGO_ID_C14N_EXCL_OMIT_COMMENTS + "WithComments"; - - static boolean _alreadyInitialized = false; - static Map _canonicalizerHash = null; - - protected CanonicalizerSpi canonicalizerSpi = null; - //J+ - - /** - * Method init - * - */ - public static void init() { - - if (!Canonicalizer._alreadyInitialized) { - Canonicalizer._canonicalizerHash = new HashMap(10); - Canonicalizer._alreadyInitialized = true; - } - } - - /** - * Constructor Canonicalizer - * - * @param algorithmURI - * @throws InvalidCanonicalizerException - */ - private Canonicalizer(String algorithmURI) + private Canonicalizer(String algorithmURI) throws InvalidCanonicalizerException { - try { - Class implementingClass = getImplementingClass(algorithmURI); - - this.canonicalizerSpi = - (CanonicalizerSpi) implementingClass.newInstance(); - this.canonicalizerSpi.reset=true; - } catch (Exception e) { - Object exArgs[] = { algorithmURI }; - - throw new InvalidCanonicalizerException( - "signature.Canonicalizer.UnknownCanonicalizer", exArgs); - } - } - - /** - * Method getInstance - * - * @param algorithmURI - * @return a Conicicalizer instance ready for the job - * @throws InvalidCanonicalizerException - */ - public static final Canonicalizer getInstance(String algorithmURI) + try { + Class implementingClass = getImplementingClass(algorithmURI); + + this.canonicalizerSpi = + (CanonicalizerSpi) implementingClass.newInstance(); + this.canonicalizerSpi.reset=true; + } catch (Exception e) { + Object exArgs[] = { algorithmURI }; + + throw new InvalidCanonicalizerException( + "signature.Canonicalizer.UnknownCanonicalizer", exArgs); + } + } + + /** + * Method getInstance + * + * @param algorithmURI + * @return a Conicicalizer instance ready for the job + * @throws InvalidCanonicalizerException + */ + public static final Canonicalizer getInstance(String algorithmURI) throws InvalidCanonicalizerException { - Canonicalizer c14nizer = new Canonicalizer(algorithmURI); + Canonicalizer c14nizer = new Canonicalizer(algorithmURI); - return c14nizer; - } + return c14nizer; + } - /** - * Method register - * - * @param algorithmURI - * @param implementingClass - * @throws AlgorithmAlreadyRegisteredException - */ - public static void register(String algorithmURI, String implementingClass) + /** + * Method register + * + * @param algorithmURI + * @param implementingClass + * @throws AlgorithmAlreadyRegisteredException + */ + public static void register(String algorithmURI, String implementingClass) throws AlgorithmAlreadyRegisteredException { - // check whether URI is already registered - Class registeredClass = getImplementingClass(algorithmURI); + // check whether URI is already registered + Class registeredClass = getImplementingClass(algorithmURI); - if (registeredClass != null) { - Object exArgs[] = { algorithmURI, registeredClass }; + if (registeredClass != null) { + Object exArgs[] = { algorithmURI, registeredClass }; - throw new AlgorithmAlreadyRegisteredException( - "algorithm.alreadyRegistered", exArgs); - } + throw new AlgorithmAlreadyRegisteredException( + "algorithm.alreadyRegistered", exArgs); + } - try { - _canonicalizerHash.put(algorithmURI, Class.forName(implementingClass)); + try { + _canonicalizerHash.put(algorithmURI, Class.forName(implementingClass)); } catch (ClassNotFoundException e) { - throw new RuntimeException("c14n class not found"); + throw new RuntimeException("c14n class not found"); } - } - - /** - * Method getURI - * - * @return the URI defined for this c14n instance. - */ - public final String getURI() { - return this.canonicalizerSpi.engineGetURI(); - } - - /** - * Method getIncludeComments - * - * @return true if the c14n respect the comments. - */ - public boolean getIncludeComments() { - return this.canonicalizerSpi.engineGetIncludeComments(); - } - - /** - * This method tries to canonicalize the given bytes. It's possible to even - * canonicalize non-wellformed sequences if they are well-formed after being - * wrapped with a >a<...>/a<. - * - * @param inputBytes - * @return the result of the conicalization. - * @throws CanonicalizationException - * @throws java.io.IOException - * @throws javax.xml.parsers.ParserConfigurationException - * @throws org.xml.sax.SAXException - */ - public byte[] canonicalize(byte[] inputBytes) + } + + /** + * Method getURI + * + * @return the URI defined for this c14n instance. + */ + public final String getURI() { + return this.canonicalizerSpi.engineGetURI(); + } + + /** + * Method getIncludeComments + * + * @return true if the c14n respect the comments. + */ + public boolean getIncludeComments() { + return this.canonicalizerSpi.engineGetIncludeComments(); + } + + /** + * This method tries to canonicalize the given bytes. It's possible to even + * canonicalize non-wellformed sequences if they are well-formed after being + * wrapped with a >a<...>/a<. + * + * @param inputBytes + * @return the result of the conicalization. + * @throws CanonicalizationException + * @throws java.io.IOException + * @throws javax.xml.parsers.ParserConfigurationException + * @throws org.xml.sax.SAXException + */ + public byte[] canonicalize(byte[] inputBytes) throws javax.xml.parsers.ParserConfigurationException, java.io.IOException, org.xml.sax.SAXException, CanonicalizationException { - ByteArrayInputStream bais = new ByteArrayInputStream(inputBytes); - InputSource in = new InputSource(bais); - DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance(); - - dfactory.setNamespaceAware(true); - - // needs to validate for ID attribute nomalization - dfactory.setValidating(true); - - DocumentBuilder db = dfactory.newDocumentBuilder(); - - /* - * for some of the test vectors from the specification, - * there has to be a validatin parser for ID attributes, default - * attribute values, NMTOKENS, etc. - * Unfortunaltely, the test vectors do use different DTDs or - * even no DTD. So Xerces 1.3.1 fires many warnings about using - * ErrorHandlers. - * - * Text from the spec: - * - * The input octet stream MUST contain a well-formed XML document, - * but the input need not be validated. However, the attribute - * value normalization and entity reference resolution MUST be - * performed in accordance with the behaviors of a validating - * XML processor. As well, nodes for default attributes (declared - * in the ATTLIST with an AttValue but not specified) are created - * in each element. Thus, the declarations in the document type - * declaration are used to help create the canonical form, even - * though the document type declaration is not retained in the - * canonical form. - * - */ - db.setErrorHandler(new com.sun.org.apache.xml.internal.security.utils - .IgnoreAllErrorHandler()); - - Document document = db.parse(in); - byte result[] = this.canonicalizeSubtree(document); - - return result; - } - - /** - * Canonicalizes the subtree rooted by node. - * - * @param node The node to canicalize - * @return the result of the c14n. - * - * @throws CanonicalizationException - */ - public byte[] canonicalizeSubtree(Node node) + ByteArrayInputStream bais = new ByteArrayInputStream(inputBytes); + InputSource in = new InputSource(bais); + DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance(); + + dfactory.setNamespaceAware(true); + + // needs to validate for ID attribute nomalization + dfactory.setValidating(true); + + DocumentBuilder db = dfactory.newDocumentBuilder(); + + /* + * for some of the test vectors from the specification, + * there has to be a validatin parser for ID attributes, default + * attribute values, NMTOKENS, etc. + * Unfortunaltely, the test vectors do use different DTDs or + * even no DTD. So Xerces 1.3.1 fires many warnings about using + * ErrorHandlers. + * + * Text from the spec: + * + * The input octet stream MUST contain a well-formed XML document, + * but the input need not be validated. However, the attribute + * value normalization and entity reference resolution MUST be + * performed in accordance with the behaviors of a validating + * XML processor. As well, nodes for default attributes (declared + * in the ATTLIST with an AttValue but not specified) are created + * in each element. Thus, the declarations in the document type + * declaration are used to help create the canonical form, even + * though the document type declaration is not retained in the + * canonical form. + * + */ + db.setErrorHandler(new com.sun.org.apache.xml.internal.security.utils + .IgnoreAllErrorHandler()); + + Document document = db.parse(in); + byte result[] = this.canonicalizeSubtree(document); + + return result; + } + + /** + * Canonicalizes the subtree rooted by node. + * + * @param node The node to canicalize + * @return the result of the c14n. + * + * @throws CanonicalizationException + */ + public byte[] canonicalizeSubtree(Node node) throws CanonicalizationException { - return this.canonicalizerSpi.engineCanonicalizeSubTree(node); - } - - /** - * Canonicalizes the subtree rooted by node. - * - * @param node - * @param inclusiveNamespaces - * @return the result of the c14n. - * @throws CanonicalizationException - */ - public byte[] canonicalizeSubtree(Node node, String inclusiveNamespaces) + return this.canonicalizerSpi.engineCanonicalizeSubTree(node); + } + + /** + * Canonicalizes the subtree rooted by node. + * + * @param node + * @param inclusiveNamespaces + * @return the result of the c14n. + * @throws CanonicalizationException + */ + public byte[] canonicalizeSubtree(Node node, String inclusiveNamespaces) throws CanonicalizationException { - return this.canonicalizerSpi.engineCanonicalizeSubTree(node, + return this.canonicalizerSpi.engineCanonicalizeSubTree(node, inclusiveNamespaces); - } - - /** - * Canonicalizes an XPath node set. The xpathNodeSet is treated - * as a list of XPath nodes, not as a list of subtrees. - * - * @param xpathNodeSet - * @return the result of the c14n. - * @throws CanonicalizationException - */ - public byte[] canonicalizeXPathNodeSet(NodeList xpathNodeSet) + } + + /** + * Canonicalizes an XPath node set. The xpathNodeSet is treated + * as a list of XPath nodes, not as a list of subtrees. + * + * @param xpathNodeSet + * @return the result of the c14n. + * @throws CanonicalizationException + */ + public byte[] canonicalizeXPathNodeSet(NodeList xpathNodeSet) throws CanonicalizationException { - return this.canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet); - } - - /** - * Canonicalizes an XPath node set. The xpathNodeSet is treated - * as a list of XPath nodes, not as a list of subtrees. - * - * @param xpathNodeSet - * @param inclusiveNamespaces - * @return the result of the c14n. - * @throws CanonicalizationException - */ - public byte[] canonicalizeXPathNodeSet( + return this.canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet); + } + + /** + * Canonicalizes an XPath node set. The xpathNodeSet is treated + * as a list of XPath nodes, not as a list of subtrees. + * + * @param xpathNodeSet + * @param inclusiveNamespaces + * @return the result of the c14n. + * @throws CanonicalizationException + */ + public byte[] canonicalizeXPathNodeSet( NodeList xpathNodeSet, String inclusiveNamespaces) throws CanonicalizationException { - return this.canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet, + return this.canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet, inclusiveNamespaces); - } - - /** - * Canonicalizes an XPath node set. - * - * @param xpathNodeSet - * @return the result of the c14n. - * @throws CanonicalizationException - */ - public byte[] canonicalizeXPathNodeSet(Set xpathNodeSet) + } + + /** + * Canonicalizes an XPath node set. + * + * @param xpathNodeSet + * @return the result of the c14n. + * @throws CanonicalizationException + */ + public byte[] canonicalizeXPathNodeSet(Set xpathNodeSet) throws CanonicalizationException { - return this.canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet); - } - - /** - * Canonicalizes an XPath node set. - * - * @param xpathNodeSet - * @param inclusiveNamespaces - * @return the result of the c14n. - * @throws CanonicalizationException - */ - public byte[] canonicalizeXPathNodeSet( - Set xpathNodeSet, String inclusiveNamespaces) - throws CanonicalizationException { - return this.canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet, - inclusiveNamespaces); - } - - /** - * Sets the writter where the cannocalization ends. ByteArrayOutputStream if - * none is setted. - * @param os - */ - public void setWriter(OutputStream os) { - this.canonicalizerSpi.setWriter(os); - } - - /** - * Returns the name of the implementing {@link CanonicalizerSpi} class - * - * @return the name of the implementing {@link CanonicalizerSpi} class - */ - public String getImplementingCanonicalizerClass() { - return this.canonicalizerSpi.getClass().getName(); - } - - /** - * Method getImplementingClass - * - * @param URI - * @return the name of the class that implements the give URI - */ - private static Class getImplementingClass(String URI) { - return (Class) _canonicalizerHash.get(URI); - } - - /** - * Set the canonicalizator behaviour to not reset. - * - */ - public void notReset() { - this.canonicalizerSpi.reset=false; - } + return this.canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet); + } + + /** + * Canonicalizes an XPath node set. + * + * @param xpathNodeSet + * @param inclusiveNamespaces + * @return the result of the c14n. + * @throws CanonicalizationException + */ + public byte[] canonicalizeXPathNodeSet(Set xpathNodeSet, + String inclusiveNamespaces) throws CanonicalizationException { + return this.canonicalizerSpi.engineCanonicalizeXPathNodeSet(xpathNodeSet, + inclusiveNamespaces); + } + + /** + * Sets the writer where the canonicalization ends. ByteArrayOutputStream + * if none is set. + * @param os + */ + public void setWriter(OutputStream os) { + this.canonicalizerSpi.setWriter(os); + } + + /** + * Returns the name of the implementing {@link CanonicalizerSpi} class + * + * @return the name of the implementing {@link CanonicalizerSpi} class + */ + public String getImplementingCanonicalizerClass() { + return this.canonicalizerSpi.getClass().getName(); + } + + /** + * Method getImplementingClass + * + * @param URI + * @return the name of the class that implements the given URI + */ + private static Class getImplementingClass(String URI) { + return (Class) _canonicalizerHash.get(URI); + } + + /** + * Set the canonicalizer behaviour to not reset. + */ + public void notReset() { + this.canonicalizerSpi.reset = false; + } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/InvalidCanonicalizerException.java b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/InvalidCanonicalizerException.java index 8af3ed880..9fb1531b7 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/InvalidCanonicalizerException.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/InvalidCanonicalizerException.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/helper/AttrCompare.java b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/helper/AttrCompare.java index 46fdc6603..802abda28 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/helper/AttrCompare.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/helper/AttrCompare.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -21,17 +20,17 @@ */ package com.sun.org.apache.xml.internal.security.c14n.helper; - - import com.sun.org.apache.xml.internal.security.utils.Constants; import org.w3c.dom.Attr; - +import java.io.Serializable; +import java.util.Comparator; /** * Compares two attributes based on the C14n specification. * *

        - *
      • Namespace nodes have a lesser document order position than attribute nodes. + *
      • Namespace nodes have a lesser document order position than attribute + * nodes. *
      • An element's namespace nodes are sorted lexicographically by * local name (the default namespace node, if one exists, has no * local name and is therefore lexicographically least). @@ -40,104 +39,89 @@ import org.w3c.dom.Attr; * key (an empty namespace URI is lexicographically least). *
      * - * $todo$ Should we implement java.util.Comparator and import java.util.Arrays to use Arrays.sort(intarray); * @author Christian Geuer-Pollmann */ -public class AttrCompare implements java.util.Comparator { - - private final int ATTR0_BEFORE_ATTR1 = -1; - private final int ATTR1_BEFORE_ATTR0 = 1; - - private final static String XMLNS=Constants.NamespaceSpecNS; - /** - * Compares two attributes based on the C14n specification. - * - *
        - *
      • Namespace nodes have a lesser document order position than attribute nodes. - *
      • An element's namespace nodes are sorted lexicographically by - * local name (the default namespace node, if one exists, has no - * local name and is therefore lexicographically least). - *
      • An element's attribute nodes are sorted lexicographically with - * namespace URI as the primary key and local name as the secondary - * key (an empty namespace URI is lexicographically least). - *
      - * - * @param obj0 casted Attr - * @param obj1 casted Attr - * @return returns a negative integer, zero, or a positive integer as obj0 is less than, equal to, or greater than obj1 - * - */ - public int compare(Object obj0, Object obj1) { - - Attr attr0 = (Attr) obj0; - Attr attr1 = (Attr) obj1; - String namespaceURI0 = attr0.getNamespaceURI(); - String namespaceURI1 = attr1.getNamespaceURI(); - - boolean isNamespaceAttr0 = - XMLNS.equals(namespaceURI0); - boolean isNamespaceAttr1 = - XMLNS.equals(namespaceURI1); - - if (isNamespaceAttr0) { - if (isNamespaceAttr1) { - - // both are namespaces - String localname0 = attr0.getLocalName(); - String localname1 = attr1.getLocalName(); - - if (localname0.equals("xmlns")) { - localname0 = ""; +public class AttrCompare implements Comparator, Serializable { + + private final static long serialVersionUID = -7113259629930576230L; + private final static int ATTR0_BEFORE_ATTR1 = -1; + private final static int ATTR1_BEFORE_ATTR0 = 1; + private final static String XMLNS=Constants.NamespaceSpecNS; + + /** + * Compares two attributes based on the C14n specification. + * + *
        + *
      • Namespace nodes have a lesser document order position than + * attribute nodes. + *
      • An element's namespace nodes are sorted lexicographically by + * local name (the default namespace node, if one exists, has no + * local name and is therefore lexicographically least). + *
      • An element's attribute nodes are sorted lexicographically with + * namespace URI as the primary key and local name as the secondary + * key (an empty namespace URI is lexicographically least). + *
      + * + * @param obj0 casted Attr + * @param obj1 casted Attr + * @return returns a negative integer, zero, or a positive integer as + * obj0 is less than, equal to, or greater than obj1 + * + */ + public int compare(Object obj0, Object obj1) { + + Attr attr0 = (Attr) obj0; + Attr attr1 = (Attr) obj1; + String namespaceURI0 = attr0.getNamespaceURI(); + String namespaceURI1 = attr1.getNamespaceURI(); + + boolean isNamespaceAttr0 = XMLNS==namespaceURI0; + boolean isNamespaceAttr1 = XMLNS==namespaceURI1; + + if (isNamespaceAttr0) { + if (isNamespaceAttr1) { + // both are namespaces + String localname0 = attr0.getLocalName(); + String localname1 = attr1.getLocalName(); + + if (localname0.equals("xmlns")) { + localname0 = ""; + } + + if (localname1.equals("xmlns")) { + localname1 = ""; + } + + return localname0.compareTo(localname1); } + // attr0 is a namespace, attr1 is not + return ATTR0_BEFORE_ATTR1; + } - if (localname1.equals("xmlns")) { - localname1 = ""; - } - - return localname0.compareTo(localname1); - } - // attr0 is a namespace, attr1 is not - return ATTR0_BEFORE_ATTR1; - - } - if (isNamespaceAttr1) { - + if (isNamespaceAttr1) { // attr1 is a namespace, attr0 is not return ATTR1_BEFORE_ATTR0; - } - - // none is a namespae - - if (namespaceURI0 == null) { - if (namespaceURI1 == null) { - /* - String localName0 = attr0.getLocalName(); - String localName1 = attr1.getLocalName(); - return localName0.compareTo(localName1); - */ + } + // none is a namespace + if (namespaceURI0 == null) { + if (namespaceURI1 == null) { String name0 = attr0.getName(); String name1 = attr1.getName(); return name0.compareTo(name1); + } + return ATTR0_BEFORE_ATTR1; } - return ATTR0_BEFORE_ATTR1; - } - if (namespaceURI1 == null) { - return ATTR1_BEFORE_ATTR0; - } - int a = namespaceURI0.compareTo(namespaceURI1); - - if (a != 0) { - return a; - } - /* - String localName0 = ; - String localName1 =;*/ - - return (attr0.getLocalName()) - .compareTo( attr1.getLocalName()); + if (namespaceURI1 == null) { + return ATTR1_BEFORE_ATTR0; + } - } + int a = namespaceURI0.compareTo(namespaceURI1); + if (a != 0) { + return a; + } + return (attr0.getLocalName()).compareTo(attr1.getLocalName()); + } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer11.java b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer11.java new file mode 100644 index 000000000..4790fd890 --- /dev/null +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer11.java @@ -0,0 +1,684 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* + * Copyright 2008 The Apache Software Foundation. + * + * Licensed 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 com.sun.org.apache.xml.internal.security.c14n.implementations; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import javax.xml.parsers.ParserConfigurationException; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; + +import java.util.logging.Logger; +import java.util.logging.Logger; +import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException; +import com.sun.org.apache.xml.internal.security.c14n.helper.C14nHelper; +import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput; +import com.sun.org.apache.xml.internal.security.utils.Constants; +import com.sun.org.apache.xml.internal.security.utils.XMLUtils; + +/** + * Implements + * Canonical XML Version 1.1, a W3C Proposed Recommendation from 29 + * January 2008. + * + * @author Sean Mullan + * @author Raul Benito + * @version $Revision: 1.2 $ + */ +public abstract class Canonicalizer11 extends CanonicalizerBase { + boolean firstCall = true; + final SortedSet result = new TreeSet(COMPARE); + static final String XMLNS_URI = Constants.NamespaceSpecNS; + static final String XML_LANG_URI = Constants.XML_LANG_SPACE_SpecNS; + + static Logger log = Logger.getLogger(Canonicalizer11.class.getName()); + + static class XmlAttrStack { + int currentLevel = 0; + int lastlevel = 0; + XmlsStackElement cur; + static class XmlsStackElement { + int level; + boolean rendered = false; + List nodes = new ArrayList(); + }; + List levels = new ArrayList(); + void push(int level) { + currentLevel = level; + if (currentLevel == -1) + return; + cur = null; + while (lastlevel >= currentLevel) { + levels.remove(levels.size() - 1); + if (levels.size() == 0) { + lastlevel = 0; + return; + } + lastlevel=((XmlsStackElement)levels.get(levels.size()-1)).level; + } + } + void addXmlnsAttr(Attr n) { + if (cur == null) { + cur = new XmlsStackElement(); + cur.level = currentLevel; + levels.add(cur); + lastlevel = currentLevel; + } + cur.nodes.add(n); + } + void getXmlnsAttr(Collection col) { + if (cur == null) { + cur = new XmlsStackElement(); + cur.level = currentLevel; + lastlevel = currentLevel; + levels.add(cur); + } + int size = levels.size() - 2; + boolean parentRendered = false; + XmlsStackElement e = null; + if (size == -1) { + parentRendered = true; + } else { + e = (XmlsStackElement) levels.get(size); + if (e.rendered && e.level+1 == currentLevel) + parentRendered = true; + } + if (parentRendered) { + col.addAll(cur.nodes); + cur.rendered = true; + return; + } + + Map loa = new HashMap(); + List baseAttrs = new ArrayList(); + boolean successiveOmitted = true; + for (;size>=0;size--) { + e = (XmlsStackElement) levels.get(size); + if (e.rendered) { + successiveOmitted = false; + } + Iterator it = e.nodes.iterator(); + while (it.hasNext() && successiveOmitted) { + Attr n = (Attr) it.next(); + if (n.getLocalName().equals("base")) { + if (!e.rendered) { + baseAttrs.add(n); + } + } else if (!loa.containsKey(n.getName())) + loa.put(n.getName(), n); + } + } + if (!baseAttrs.isEmpty()) { + Iterator it = cur.nodes.iterator(); + String base = null; + Attr baseAttr = null; + while (it.hasNext()) { + Attr n = (Attr) it.next(); + if (n.getLocalName().equals("base")) { + base = n.getValue(); + baseAttr = n; + break; + } + } + it = baseAttrs.iterator(); + while (it.hasNext()) { + Attr n = (Attr) it.next(); + if (base == null) { + base = n.getValue(); + baseAttr = n; + } else { + try { + base = joinURI(n.getValue(), base); + } catch (URISyntaxException ue) { + ue.printStackTrace(); + } + } + } + if (base != null && base.length() != 0) { + baseAttr.setValue(base); + col.add(baseAttr); + } + } + + cur.rendered = true; + col.addAll(loa.values()); + } + }; + XmlAttrStack xmlattrStack = new XmlAttrStack(); + + /** + * Constructor Canonicalizer11 + * + * @param includeComments + */ + public Canonicalizer11(boolean includeComments) { + super(includeComments); + } + + /** + * Returns the Attr[]s to be outputted for the given element. + *
      + * The code of this method is a copy of {@link #handleAttributes(Element, + * NameSpaceSymbTable)}, + * whereas it takes into account that subtree-c14n is -- well -- + * subtree-based. + * So if the element in question isRoot of c14n, it's parent is not in the + * node set, as well as all other ancestors. + * + * @param E + * @param ns + * @return the Attr[]s to be outputted + * @throws CanonicalizationException + */ + Iterator handleAttributesSubtree(Element E, NameSpaceSymbTable ns) + throws CanonicalizationException { + if (!E.hasAttributes() && !firstCall) { + return null; + } + // result will contain the attrs which have to be outputted + final SortedSet result = this.result; + result.clear(); + NamedNodeMap attrs = E.getAttributes(); + int attrsLength = attrs.getLength(); + + for (int i = 0; i < attrsLength; i++) { + Attr N = (Attr) attrs.item(i); + String NUri = N.getNamespaceURI(); + + if (XMLNS_URI != NUri) { + // It's not a namespace attr node. Add to the result and + // continue. + result.add(N); + continue; + } + + String NName = N.getLocalName(); + String NValue = N.getValue(); + if (XML.equals(NName) + && XML_LANG_URI.equals(NValue)) { + // The default mapping for xml must not be output. + continue; + } + + Node n = ns.addMappingAndRender(NName, NValue, N); + + if (n != null) { + // Render the ns definition + result.add(n); + if (C14nHelper.namespaceIsRelative(N)) { + Object exArgs[] = {E.getTagName(), NName, N.getNodeValue()}; + throw new CanonicalizationException( + "c14n.Canonicalizer.RelativeNamespace", exArgs); + } + } + } + + if (firstCall) { + // It is the first node of the subtree + // Obtain all the namespaces defined in the parents, and added + // to the output. + ns.getUnrenderedNodes(result); + // output the attributes in the xml namespace. + xmlattrStack.getXmlnsAttr(result); + firstCall = false; + } + + return result.iterator(); + } + + /** + * Returns the Attr[]s to be outputted for the given element. + *
      + * IMPORTANT: This method expects to work on a modified DOM tree, i.e. a + * DOM which has been prepared using + * {@link com.sun.org.apache.xml.internal.security.utils.XMLUtils#circumventBug2650( + * org.w3c.dom.Document)}. + * + * @param E + * @param ns + * @return the Attr[]s to be outputted + * @throws CanonicalizationException + */ + Iterator handleAttributes(Element E, NameSpaceSymbTable ns) + throws CanonicalizationException { + // result will contain the attrs which have to be output + xmlattrStack.push(ns.getLevel()); + boolean isRealVisible = isVisibleDO(E, ns.getLevel()) == 1; + NamedNodeMap attrs = null; + int attrsLength = 0; + if (E.hasAttributes()) { + attrs = E.getAttributes(); + attrsLength = attrs.getLength(); + } + + SortedSet result = this.result; + result.clear(); + + for (int i = 0; i < attrsLength; i++) { + Attr N = (Attr) attrs.item(i); + String NUri = N.getNamespaceURI(); + + if (XMLNS_URI != NUri) { + // A non namespace definition node. + if (XML_LANG_URI == NUri) { + if (N.getLocalName().equals("id")) { + if (isRealVisible) { + // treat xml:id like any other attribute + // (emit it, but don't inherit it) + result.add(N); + } + } else { + xmlattrStack.addXmlnsAttr(N); + } + } else if (isRealVisible) { + // The node is visible add the attribute to the list of + // output attributes. + result.add(N); + } + // keep working + continue; + } + + String NName = N.getLocalName(); + String NValue = N.getValue(); + if ("xml".equals(NName) + && XML_LANG_URI.equals(NValue)) { + /* except omit namespace node with local name xml, which defines + * the xml prefix, if its string value is + * http://www.w3.org/XML/1998/namespace. + */ + continue; + } + // add the prefix binding to the ns symb table. + // ns.addInclusiveMapping(NName,NValue,N,isRealVisible); + if (isVisible(N)) { + if (!isRealVisible && ns.removeMappingIfRender(NName)) { + continue; + } + // The xpath select this node output it if needed. + // Node n = ns.addMappingAndRenderXNodeSet + // (NName, NValue, N, isRealVisible); + Node n = ns.addMappingAndRender(NName, NValue, N); + if (n != null) { + result.add(n); + if (C14nHelper.namespaceIsRelative(N)) { + Object exArgs[] = + { E.getTagName(), NName, N.getNodeValue() }; + throw new CanonicalizationException( + "c14n.Canonicalizer.RelativeNamespace", exArgs); + } + } + } else { + if (isRealVisible && NName != XMLNS) { + ns.removeMapping(NName); + } else { + ns.addMapping(NName, NValue, N); + } + } + } + if (isRealVisible) { + // The element is visible, handle the xmlns definition + Attr xmlns = E.getAttributeNodeNS(XMLNS_URI, XMLNS); + Node n = null; + if (xmlns == null) { + // No xmlns def just get the already defined. + n = ns.getMapping(XMLNS); + } else if (!isVisible(xmlns)) { + // There is a defn but the xmlns is not selected by the xpath. + // then xmlns="" + n = ns.addMappingAndRender(XMLNS, "", nullNode); + } + // output the xmlns def if needed. + if (n != null) { + result.add(n); + } + // Float all xml:* attributes of the unselected parent elements to + // this one. addXmlAttributes(E,result); + xmlattrStack.getXmlnsAttr(result); + ns.getUnrenderedNodes(result); + } + + return result.iterator(); + } + + /** + * Always throws a CanonicalizationException because this is inclusive c14n. + * + * @param xpathNodeSet + * @param inclusiveNamespaces + * @return none it always fails + * @throws CanonicalizationException always + */ + public byte[] engineCanonicalizeXPathNodeSet(Set xpathNodeSet, + String inclusiveNamespaces) throws CanonicalizationException { + throw new CanonicalizationException( + "c14n.Canonicalizer.UnsupportedOperation"); + } + + /** + * Always throws a CanonicalizationException because this is inclusive c14n. + * + * @param rootNode + * @param inclusiveNamespaces + * @return none it always fails + * @throws CanonicalizationException + */ + public byte[] engineCanonicalizeSubTree(Node rootNode, + String inclusiveNamespaces) throws CanonicalizationException { + throw new CanonicalizationException( + "c14n.Canonicalizer.UnsupportedOperation"); + } + + void circumventBugIfNeeded(XMLSignatureInput input) + throws CanonicalizationException, ParserConfigurationException, + IOException, SAXException { + if (!input.isNeedsToBeExpanded()) + return; + Document doc = null; + if (input.getSubNode() != null) { + doc = XMLUtils.getOwnerDocument(input.getSubNode()); + } else { + doc = XMLUtils.getOwnerDocument(input.getNodeSet()); + } + XMLUtils.circumventBug2650(doc); + } + + void handleParent(Element e, NameSpaceSymbTable ns) { + if (!e.hasAttributes()) { + return; + } + xmlattrStack.push(-1); + NamedNodeMap attrs = e.getAttributes(); + int attrsLength = attrs.getLength(); + for (int i = 0; i < attrsLength; i++) { + Attr N = (Attr) attrs.item(i); + if (Constants.NamespaceSpecNS != N.getNamespaceURI()) { + // Not a namespace definition, ignore. + if (XML_LANG_URI == N.getNamespaceURI()) { + xmlattrStack.addXmlnsAttr(N); + } + continue; + } + + String NName = N.getLocalName(); + String NValue = N.getNodeValue(); + if (XML.equals(NName) + && Constants.XML_LANG_SPACE_SpecNS.equals(NValue)) { + continue; + } + ns.addMapping(NName,NValue,N); + } + } + + private static String joinURI(String baseURI, String relativeURI) + throws URISyntaxException { + String bscheme = null; + String bauthority = null; + String bpath = ""; + String bquery = null; + String bfragment = null; // Is this correct? + + // pre-parse the baseURI + if (baseURI != null) { + if (baseURI.endsWith("..")) { + baseURI = baseURI + "/"; + } + URI base = new URI(baseURI); + bscheme = base.getScheme(); + bauthority = base.getAuthority(); + bpath = base.getPath(); + bquery = base.getQuery(); + bfragment = base.getFragment(); + } + + URI r = new URI(relativeURI); + String rscheme = r.getScheme(); + String rauthority = r.getAuthority(); + String rpath = r.getPath(); + String rquery = r.getQuery(); + String rfragment = null; + + String tscheme, tauthority, tpath, tquery, tfragment; + if (rscheme != null && rscheme.equals(bscheme)) { + rscheme = null; + } + if (rscheme != null) { + tscheme = rscheme; + tauthority = rauthority; + tpath = removeDotSegments(rpath); + tquery = rquery; + } else { + if (rauthority != null) { + tauthority = rauthority; + tpath = removeDotSegments(rpath); + tquery = rquery; + } else { + if (rpath.length() == 0) { + tpath = bpath; + if (rquery != null) { + tquery = rquery; + } else { + tquery = bquery; + } + } else { + if (rpath.startsWith("/")) { + tpath = removeDotSegments(rpath); + } else { + if (bauthority != null && bpath.length() == 0) { + tpath = "/" + rpath; + } else { + int last = bpath.lastIndexOf('/'); + if (last == -1) { + tpath = rpath; + } else { + tpath = bpath.substring(0, last+1) + rpath; + } + } + tpath = removeDotSegments(tpath); + } + tquery = rquery; + } + tauthority = bauthority; + } + tscheme = bscheme; + } + tfragment = rfragment; + return new URI(tscheme, tauthority, tpath, tquery, tfragment).toString(); + } + + private static String removeDotSegments(String path) { + + log.log(java.util.logging.Level.FINE, "STEP OUTPUT BUFFER\t\tINPUT BUFFER"); + + // 1. The input buffer is initialized with the now-appended path + // components then replace occurrences of "//" in the input buffer + // with "/" until no more occurrences of "//" are in the input buffer. + String input = path; + while (input.indexOf("//") > -1) { + input = input.replaceAll("//", "/"); + } + + // Initialize the output buffer with the empty string. + StringBuffer output = new StringBuffer(); + + // If the input buffer starts with a root slash "/" then move this + // character to the output buffer. + if (input.charAt(0) == '/') { + output.append("/"); + input = input.substring(1); + } + + printStep("1 ", output.toString(), input); + + // While the input buffer is not empty, loop as follows + while (input.length() != 0) { + // 2A. If the input buffer begins with a prefix of "./", + // then remove that prefix from the input buffer + // else if the input buffer begins with a prefix of "../", then + // if also the output does not contain the root slash "/" only, + // then move this prefix to the end of the output buffer else + // remove that prefix + if (input.startsWith("./")) { + input = input.substring(2); + printStep("2A", output.toString(), input); + } else if (input.startsWith("../")) { + input = input.substring(3); + if (!output.toString().equals("/")) { + output.append("../"); + } + printStep("2A", output.toString(), input); + // 2B. if the input buffer begins with a prefix of "/./" or "/.", + // where "." is a complete path segment, then replace that prefix + // with "/" in the input buffer; otherwise, + } else if (input.startsWith("/./")) { + input = input.substring(2); + printStep("2B", output.toString(), input); + } else if (input.equals("/.")) { + // FIXME: what is complete path segment? + input = input.replaceFirst("/.", "/"); + printStep("2B", output.toString(), input); + // 2C. if the input buffer begins with a prefix of "/../" or "/..", + // where ".." is a complete path segment, then replace that prefix + // with "/" in the input buffer and if also the output buffer is + // empty, last segment in the output buffer equals "../" or "..", + // where ".." is a complete path segment, then append ".." or "/.." + // for the latter case respectively to the output buffer else + // remove the last segment and its preceding "/" (if any) from the + // output buffer and if hereby the first character in the output + // buffer was removed and it was not the root slash then delete a + // leading slash from the input buffer; otherwise, + } else if (input.startsWith("/../")) { + input = input.substring(3); + if (output.length() == 0) { + output.append("/"); + } else if (output.toString().endsWith("../")) { + output.append(".."); + } else if (output.toString().endsWith("..")) { + output.append("/.."); + } else { + int index = output.lastIndexOf("/"); + if (index == -1) { + output = new StringBuffer(); + if (input.charAt(0) == '/') { + input = input.substring(1); + } + } else { + output = output.delete(index, output.length()); + } + } + printStep("2C", output.toString(), input); + } else if (input.equals("/..")) { + // FIXME: what is complete path segment? + input = input.replaceFirst("/..", "/"); + if (output.length() == 0) { + output.append("/"); + } else if (output.toString().endsWith("../")) { + output.append(".."); + } else if (output.toString().endsWith("..")) { + output.append("/.."); + } else { + int index = output.lastIndexOf("/"); + if (index == -1) { + output = new StringBuffer(); + if (input.charAt(0) == '/') { + input = input.substring(1); + } + } else { + output = output.delete(index, output.length()); + } + } + printStep("2C", output.toString(), input); + // 2D. if the input buffer consists only of ".", then remove + // that from the input buffer else if the input buffer consists + // only of ".." and if the output buffer does not contain only + // the root slash "/", then move the ".." to the output buffer + // else delte it.; otherwise, + } else if (input.equals(".")) { + input = ""; + printStep("2D", output.toString(), input); + } else if (input.equals("..")) { + if (!output.toString().equals("/")) + output.append(".."); + input = ""; + printStep("2D", output.toString(), input); + // 2E. move the first path segment (if any) in the input buffer + // to the end of the output buffer, including the initial "/" + // character (if any) and any subsequent characters up to, but not + // including, the next "/" character or the end of the input buffer. + } else { + int end = -1; + int begin = input.indexOf('/'); + if (begin == 0) { + end = input.indexOf('/', 1); + } else { + end = begin; + begin = 0; + } + String segment; + if (end == -1) { + segment = input.substring(begin); + input = ""; + } else { + segment = input.substring(begin, end); + input = input.substring(end); + } + output.append(segment); + printStep("2E", output.toString(), input); + } + } + + // 3. Finally, if the only or last segment of the output buffer is + // "..", where ".." is a complete path segment not followed by a slash + // then append a slash "/". The output buffer is returned as the result + // of remove_dot_segments + if (output.toString().endsWith("..")) { + output.append("/"); + printStep("3 ", output.toString(), input); + } + + return output.toString(); + } + + private static void printStep(String step, String output, String input) { + if (log.isLoggable(java.util.logging.Level.FINE)) { + log.log(java.util.logging.Level.FINE, " " + step + ": " + output); + if (output.length() == 0) { + log.log(java.util.logging.Level.FINE, "\t\t\t\t" + input); + } else { + log.log(java.util.logging.Level.FINE, "\t\t\t" + input); + } + } + } +} diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer11_OmitComments.java b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer11_OmitComments.java new file mode 100644 index 000000000..31903667f --- /dev/null +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer11_OmitComments.java @@ -0,0 +1,41 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* + * Copyright 2008 The Apache Software Foundation. + * + * Licensed 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 com.sun.org.apache.xml.internal.security.c14n.implementations; + +import com.sun.org.apache.xml.internal.security.c14n.Canonicalizer; + +/** + * @author Sean Mullan + */ +public class Canonicalizer11_OmitComments extends Canonicalizer11 { + + public Canonicalizer11_OmitComments() { + super(false); + } + + public final String engineGetURI() { + return Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS; + } + + public final boolean engineGetIncludeComments() { + return false; + } +} diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer11_WithComments.java b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer11_WithComments.java new file mode 100644 index 000000000..ba650c108 --- /dev/null +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer11_WithComments.java @@ -0,0 +1,41 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* + * Copyright 2008 The Apache Software Foundation. + * + * Licensed 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 com.sun.org.apache.xml.internal.security.c14n.implementations; + +import com.sun.org.apache.xml.internal.security.c14n.Canonicalizer; + +/** + * @author Sean Mullan + */ +public class Canonicalizer11_WithComments extends Canonicalizer11 { + + public Canonicalizer11_WithComments() { + super(true); + } + + public final String engineGetURI() { + return Canonicalizer.ALGO_ID_C14N11_WITH_COMMENTS; + } + + public final boolean engineGetIncludeComments() { + return true; + } +} diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315.java b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315.java index 5dbeb60f9..541c2d63c 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -23,20 +22,30 @@ package com.sun.org.apache.xml.internal.security.c14n.implementations; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import javax.xml.parsers.ParserConfigurationException; + import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException; import com.sun.org.apache.xml.internal.security.c14n.helper.C14nHelper; +import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput; import com.sun.org.apache.xml.internal.security.utils.Constants; +import com.sun.org.apache.xml.internal.security.utils.XMLUtils; import org.w3c.dom.Attr; +import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; +import org.xml.sax.SAXException; /** @@ -44,13 +53,92 @@ import org.w3c.dom.Node; * XML Version 1.0, a W3C Recommendation from 15 March 2001. * * @author Christian Geuer-Pollmann + * @version $Revision: 1.5 $ */ public abstract class Canonicalizer20010315 extends CanonicalizerBase { boolean firstCall=true; final SortedSet result= new TreeSet(COMPARE); static final String XMLNS_URI=Constants.NamespaceSpecNS; static final String XML_LANG_URI=Constants.XML_LANG_SPACE_SpecNS; - /** + static class XmlAttrStack { + int currentLevel=0; + int lastlevel=0; + XmlsStackElement cur; + static class XmlsStackElement { + int level; + boolean rendered=false; + List nodes=new ArrayList(); + }; + List levels=new ArrayList(); + void push(int level) { + currentLevel=level; + if (currentLevel==-1) + return; + cur=null; + while (lastlevel>=currentLevel) { + levels.remove(levels.size()-1); + if (levels.size()==0) { + lastlevel=0; + return; + } + lastlevel=((XmlsStackElement)levels.get(levels.size()-1)).level; + } + } + void addXmlnsAttr(Attr n) { + if (cur==null) { + cur=new XmlsStackElement(); + cur.level=currentLevel; + levels.add(cur); + lastlevel=currentLevel; + } + cur.nodes.add(n); + } + void getXmlnsAttr(Collection col) { + int size=levels.size()-1; + if (cur==null) { + cur=new XmlsStackElement(); + cur.level=currentLevel; + lastlevel=currentLevel; + levels.add(cur); + } + boolean parentRendered=false; + XmlsStackElement e=null; + if (size==-1) { + parentRendered=true; + } else { + e=(XmlsStackElement)levels.get(size); + if (e.rendered && e.level+1==currentLevel) + parentRendered=true; + + } + if (parentRendered) { + col.addAll(cur.nodes); + cur.rendered=true; + return; + } + + Map loa = new HashMap(); + for (;size>=0;size--) { + e=(XmlsStackElement)levels.get(size); + Iterator it=e.nodes.iterator(); + while (it.hasNext()) { + Attr n=(Attr)it.next(); + if (!loa.containsKey(n.getName())) + loa.put(n.getName(),n); + } + //if (e.rendered) + //break; + + }; + //cur.nodes.clear(); + //cur.nodes.addAll(loa.values()); + cur.rendered=true; + col.addAll(loa.values()); + } + + } + XmlAttrStack xmlattrStack=new XmlAttrStack(); + /** * Constructor Canonicalizer20010315 * * @param includeComments @@ -86,16 +174,16 @@ public abstract class Canonicalizer20010315 extends CanonicalizerBase { for (int i = 0; i < attrsLength; i++) { Attr N = (Attr) attrs.item(i); - String NName=N.getLocalName(); - String NValue=N.getValue(); String NUri =N.getNamespaceURI(); - if (!XMLNS_URI.equals(NUri)) { + if (XMLNS_URI!=NUri) { //It's not a namespace attr node. Add to the result and continue. result.add(N); continue; } + String NName=N.getLocalName(); + String NValue=N.getValue(); if (XML.equals(NName) && XML_LANG_URI.equals(NValue)) { //The default mapping for xml must not be output. @@ -120,64 +208,13 @@ public abstract class Canonicalizer20010315 extends CanonicalizerBase { //Obtain all the namespaces defined in the parents, and added to the output. ns.getUnrenderedNodes(result); //output the attributes in the xml namespace. - addXmlAttributesSubtree(E, result); - firstCall=false; + xmlattrStack.getXmlnsAttr(result); + firstCall=false; } return result.iterator(); } - /** - * Float the xml:* attributes of the parent nodes to the root node of c14n - * @param E the root node. - * @param result the xml:* attributes to output. - */ - private void addXmlAttributesSubtree(Element E, SortedSet result) { - // E is in the node-set - Node parent = E.getParentNode(); - Map loa = new HashMap(); - - if ((parent != null) && (parent.getNodeType() == Node.ELEMENT_NODE)) { - - // parent element is not in node set - for (Node ancestor = parent; - (ancestor != null) - && (ancestor.getNodeType() == Node.ELEMENT_NODE); - ancestor = ancestor.getParentNode()) { - Element el=((Element) ancestor); - if (!el.hasAttributes()) { - continue; - } - // for all ancestor elements - NamedNodeMap ancestorAttrs = el.getAttributes(); - - for (int i = 0; i < ancestorAttrs.getLength(); i++) { - // for all attributes in the ancestor element - Attr currentAncestorAttr = (Attr) ancestorAttrs.item(i); - - if (XML_LANG_URI.equals( - currentAncestorAttr.getNamespaceURI())) { - - // do we have an xml:* ? - if (!E.hasAttributeNS( - XML_LANG_URI, - currentAncestorAttr.getLocalName())) { - - // the xml:* attr is not in E - if (!loa.containsKey(currentAncestorAttr.getName())) { - loa.put(currentAncestorAttr.getName(), - currentAncestorAttr); - } - } - } - } - } - } - - result.addAll( loa.values()); - - } - /** * Returns the Attr[]s to be outputted for the given element. *
      @@ -192,7 +229,8 @@ public abstract class Canonicalizer20010315 extends CanonicalizerBase { */ Iterator handleAttributes(Element E, NameSpaceSymbTable ns ) throws CanonicalizationException { // result will contain the attrs which have to be outputted - boolean isRealVisible=isVisible(E); + xmlattrStack.push(ns.getLevel()); + boolean isRealVisible=isVisibleDO(E,ns.getLevel())==1; NamedNodeMap attrs = null; int attrsLength = 0; if (E.hasAttributes()) { @@ -204,16 +242,15 @@ public abstract class Canonicalizer20010315 extends CanonicalizerBase { SortedSet result = this.result; result.clear(); - for (int i = 0; i < attrsLength; i++) { Attr N = (Attr) attrs.item(i); - String NName=N.getLocalName(); - String NValue=N.getValue(); String NUri =N.getNamespaceURI(); - if (!XMLNS_URI.equals(NUri)) { + if (XMLNS_URI!=NUri) { //A non namespace definition node. - if (isRealVisible){ + if (XML_LANG_URI==NUri) { + xmlattrStack.addXmlnsAttr(N); + } else if (isRealVisible){ //The node is visible add the attribute to the list of output attributes. result.add(N); } @@ -221,7 +258,8 @@ public abstract class Canonicalizer20010315 extends CanonicalizerBase { continue; } - + String NName=N.getLocalName(); + String NValue=N.getValue(); if ("xml".equals(NName) && XML_LANG_URI.equals(NValue)) { /* except omit namespace node with local name xml, which defines @@ -232,16 +270,26 @@ public abstract class Canonicalizer20010315 extends CanonicalizerBase { //add the prefix binding to the ns symb table. //ns.addInclusiveMapping(NName,NValue,N,isRealVisible); if (isVisible(N)) { - //The xpath select this node output it if needed. - Node n=ns.addMappingAndRenderXNodeSet(NName,NValue,N,isRealVisible); - if (n!=null) { + if (!isRealVisible && ns.removeMappingIfRender(NName)) { + continue; + } + //The xpath select this node output it if needed. + //Node n=ns.addMappingAndRenderXNodeSet(NName,NValue,N,isRealVisible); + Node n=ns.addMappingAndRender(NName,NValue,N); + if (n!=null) { result.add(n); if (C14nHelper.namespaceIsRelative(N)) { Object exArgs[] = { E.getTagName(), NName, N.getNodeValue() }; throw new CanonicalizationException( "c14n.Canonicalizer.RelativeNamespace", exArgs); - } - } + } + } + } else { + if (isRealVisible && NName!=XMLNS) { + ns.removeMapping(NName); + } else { + ns.addMapping(NName,NValue,N); + } } } if (isRealVisible) { @@ -254,84 +302,21 @@ public abstract class Canonicalizer20010315 extends CanonicalizerBase { } else if ( !isVisible(xmlns)) { //There is a definition but the xmlns is not selected by the xpath. //then xmlns="" - n=ns.addMappingAndRenderXNodeSet(XMLNS,"",nullNode,true); + n=ns.addMappingAndRender(XMLNS,"",nullNode); } //output the xmlns def if needed. if (n!=null) { result.add(n); } //Float all xml:* attributes of the unselected parent elements to this one. - addXmlAttributes(E,result); + //addXmlAttributes(E,result); + xmlattrStack.getXmlnsAttr(result); + ns.getUnrenderedNodes(result); + } return result.iterator(); } - /** - * Float the xml:* attributes of the unselected parent nodes to the ciurrent node. - * @param E - * @param result - */ - private void addXmlAttributes(Element E, SortedSet result) { - /* The processing of an element node E MUST be modified slightly when an - * XPath node-set is given as input and the element's parent is omitted - * from the node-set. The method for processing the attribute axis of an - * element E in the node-set is enhanced. All element nodes along E's - * ancestor axis are examined for nearest occurrences of attributes in - * the xml namespace, such as xml:lang and xml:space (whether or not they - * are in the node-set). From this list of attributes, remove any that are - * in E's attribute axis (whether or not they are in the node-set). Then, - * lexicographically merge this attribute list with the nodes of E's - * attribute axis that are in the node-set. The result of visiting the - * attribute axis is computed by processing the attribute nodes in this - * merged attribute list. - */ - - // E is in the node-set - Node parent = E.getParentNode(); - Map loa = new HashMap(); - - if ((parent != null) && (parent.getNodeType() == Node.ELEMENT_NODE) - &&!isVisible(parent)) { - - // parent element is not in node set - for (Node ancestor = parent; - (ancestor != null) - && (ancestor.getNodeType() == Node.ELEMENT_NODE); - ancestor = ancestor.getParentNode()) { - Element el=((Element) ancestor); - if (!el.hasAttributes()) { - continue; - } - // for all ancestor elements - NamedNodeMap ancestorAttrs =el.getAttributes(); - - for (int i = 0; i < ancestorAttrs.getLength(); i++) { - - // for all attributes in the ancestor element - Attr currentAncestorAttr = (Attr) ancestorAttrs.item(i); - - if (XML_LANG_URI.equals( - currentAncestorAttr.getNamespaceURI())) { - - // do we have an xml:* ? - if (!E.hasAttributeNS( - XML_LANG_URI, - currentAncestorAttr.getLocalName())) { - - // the xml:* attr is not in E - if (!loa.containsKey(currentAncestorAttr.getName())) { - loa.put(currentAncestorAttr.getName(), - currentAncestorAttr); - } - } - } - } - } - } - result.addAll(loa.values()); - -} - /** * Always throws a CanonicalizationException because this is inclusive c14n. * @@ -363,4 +348,43 @@ public abstract class Canonicalizer20010315 extends CanonicalizerBase { throw new CanonicalizationException( "c14n.Canonicalizer.UnsupportedOperation"); } + void circumventBugIfNeeded(XMLSignatureInput input) throws CanonicalizationException, ParserConfigurationException, IOException, SAXException { + if (!input.isNeedsToBeExpanded()) + return; + Document doc = null; + if (input.getSubNode() != null) { + doc=XMLUtils.getOwnerDocument(input.getSubNode()); + } else { + doc=XMLUtils.getOwnerDocument(input.getNodeSet()); + } + XMLUtils.circumventBug2650(doc); + + } + + void handleParent(Element e, NameSpaceSymbTable ns) { + if (!e.hasAttributes()) { + return; + } + xmlattrStack.push(-1); + NamedNodeMap attrs = e.getAttributes(); + int attrsLength = attrs.getLength(); + for (int i = 0; i < attrsLength; i++) { + Attr N = (Attr) attrs.item(i); + if (Constants.NamespaceSpecNS!=N.getNamespaceURI()) { + //Not a namespace definition, ignore. + if (XML_LANG_URI==N.getNamespaceURI()) { + xmlattrStack.addXmlnsAttr(N); + } + continue; + } + + String NName=N.getLocalName(); + String NValue=N.getNodeValue(); + if (XML.equals(NName) + && Constants.XML_LANG_SPACE_SpecNS.equals(NValue)) { + continue; + } + ns.addMapping(NName,NValue,N); + } + } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315Excl.java b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315Excl.java index 18f697366..679c7b508 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315Excl.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315Excl.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -21,20 +20,26 @@ */ package com.sun.org.apache.xml.internal.security.c14n.implementations; +import java.io.IOException; import java.util.Iterator; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import javax.xml.parsers.ParserConfigurationException; + import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException; import com.sun.org.apache.xml.internal.security.c14n.helper.C14nHelper; import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput; import com.sun.org.apache.xml.internal.security.transforms.params.InclusiveNamespaces; import com.sun.org.apache.xml.internal.security.utils.Constants; +import com.sun.org.apache.xml.internal.security.utils.XMLUtils; import org.w3c.dom.Attr; +import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; +import org.xml.sax.SAXException; /** * Implements " Exclusive XML @@ -47,6 +52,7 @@ import org.w3c.dom.Node; * THIS implementation is a complete rewrite of the algorithm. * * @author Christian Geuer-Pollmann + * @version $Revision: 1.5 $ * @see * XML Canonicalization, Version 1.0 */ @@ -55,7 +61,7 @@ public abstract class Canonicalizer20010315Excl extends CanonicalizerBase { * This Set contains the names (Strings like "xmlns" or "xmlns:foo") of * the inclusive namespaces. */ - TreeSet _inclusiveNSSet = null; + TreeSet _inclusiveNSSet = new TreeSet(); static final String XMLNS_URI=Constants.NamespaceSpecNS; final SortedSet result = new TreeSet(COMPARE); /** @@ -143,10 +149,8 @@ public abstract class Canonicalizer20010315Excl extends CanonicalizerBase { for (int i = 0; i < attrsLength; i++) { Attr N = (Attr) attrs.item(i); - String NName=N.getLocalName(); - String NNodeValue=N.getNodeValue(); - if (!XMLNS_URI.equals(N.getNamespaceURI())) { + if (XMLNS_URI!=N.getNamespaceURI()) { //Not a namespace definition. //The Element is output element, add his prefix(if used) to visibyUtilized String prefix = N.getPrefix(); @@ -157,6 +161,8 @@ public abstract class Canonicalizer20010315Excl extends CanonicalizerBase { result.add(N); continue; } + String NName=N.getLocalName(); + String NNodeValue=N.getNodeValue(); if (ns.addMapping(NName, NNodeValue,N)) { //New definition check if it is relative. @@ -168,17 +174,17 @@ public abstract class Canonicalizer20010315Excl extends CanonicalizerBase { } } } - + String prefix; if (E.getNamespaceURI() != null) { - String prefix = E.getPrefix(); + prefix = E.getPrefix(); if ((prefix == null) || (prefix.length() == 0)) { - visiblyUtilized.add(XMLNS); - } else { - visiblyUtilized.add(prefix); + prefix=XMLNS; } + } else { - visiblyUtilized.add(XMLNS); + prefix=XMLNS; } + visiblyUtilized.add(prefix); //This can be optimezed by I don't have time Iterator it=visiblyUtilized.iterator(); @@ -211,12 +217,6 @@ public abstract class Canonicalizer20010315Excl extends CanonicalizerBase { } - /** @inheritDoc */ - public byte[] engineCanonicalizeXPathNodeSet(Set xpathNodeSet - ) throws CanonicalizationException { - return engineCanonicalizeXPathNodeSet(xpathNodeSet,""); - } - /** * @inheritDoc * @param E @@ -236,21 +236,20 @@ public abstract class Canonicalizer20010315Excl extends CanonicalizerBase { //The prefix visibly utilized(in the attribute or in the name) in the element Set visiblyUtilized =null; //It's the output selected. - boolean isOutputElement = isVisible(E); + boolean isOutputElement=isVisibleDO(E,ns.getLevel())==1; if (isOutputElement) { visiblyUtilized = (Set) this._inclusiveNSSet.clone(); } for (int i = 0; i < attrsLength; i++) { Attr N = (Attr) attrs.item(i); - String NName=N.getLocalName(); - String NNodeValue=N.getNodeValue(); - if ( !isVisible(N) ) { - //The node is not in the nodeset(if there is a nodeset) - continue; - } - if (!XMLNS_URI.equals(N.getNamespaceURI())) { + + if (XMLNS_URI!=N.getNamespaceURI()) { + if ( !isVisible(N) ) { + //The node is not in the nodeset(if there is a nodeset) + continue; + } //Not a namespace definition. if (isOutputElement) { //The Element is output element, add his prefix(if used) to visibyUtilized @@ -263,6 +262,25 @@ public abstract class Canonicalizer20010315Excl extends CanonicalizerBase { } continue; } + String NName=N.getLocalName(); + if (isOutputElement && !isVisible(N) && NName!=XMLNS) { + ns.removeMappingIfNotRender(NName); + continue; + } + String NNodeValue=N.getNodeValue(); + + if (!isOutputElement && isVisible(N) && _inclusiveNSSet.contains(NName) && !ns.removeMappingIfRender(NName)) { + Node n=ns.addMappingAndRender(NName,NNodeValue,N); + if (n!=null) { + result.add(n); + if (C14nHelper.namespaceIsRelative(N)) { + Object exArgs[] = { E.getTagName(), NName, N.getNodeValue() }; + throw new CanonicalizationException( + "c14n.Canonicalizer.RelativeNamespace", exArgs); + } + } + } + if (ns.addMapping(NName, NNodeValue,N)) { @@ -306,18 +324,20 @@ public abstract class Canonicalizer20010315Excl extends CanonicalizerBase { } result.add(key); } - } else /*if (_circunvented)*/ { - Iterator it=this._inclusiveNSSet.iterator(); - while (it.hasNext()) { - String s=(String)it.next(); - Attr key=ns.getMappingWithoutRendered(s); - if (key==null) { - continue; - } - result.add(key); - } } return result.iterator(); } + void circumventBugIfNeeded(XMLSignatureInput input) throws CanonicalizationException, ParserConfigurationException, IOException, SAXException { + if (!input.isNeedsToBeExpanded() || _inclusiveNSSet.isEmpty()) + return; + Document doc = null; + if (input.getSubNode() != null) { + doc=XMLUtils.getOwnerDocument(input.getSubNode()); + } else { + doc=XMLUtils.getOwnerDocument(input.getNodeSet()); + } + + XMLUtils.circumventBug2650(doc); + } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315ExclWithComments.java b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315ExclWithComments.java index 025502bd5..375501248 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315ExclWithComments.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315ExclWithComments.java @@ -28,6 +28,7 @@ import com.sun.org.apache.xml.internal.security.c14n.Canonicalizer; /** * Class Canonicalizer20010315ExclWithComments * + * @version $Revision: 1.5 $ */ public class Canonicalizer20010315ExclWithComments extends Canonicalizer20010315Excl { diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315WithComments.java b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315WithComments.java index 539bb3a39..4714e165b 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315WithComments.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315WithComments.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerBase.java b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerBase.java index 810876375..05f22d8c1 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerBase.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerBase.java @@ -27,9 +27,11 @@ import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.ListIterator; +import java.util.Map; import java.util.Set; import javax.xml.parsers.DocumentBuilderFactory; @@ -45,7 +47,6 @@ import com.sun.org.apache.xml.internal.security.utils.UnsyncByteArrayOutputStrea import com.sun.org.apache.xml.internal.security.utils.XMLUtils; import org.w3c.dom.Attr; import org.w3c.dom.Comment; -import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; @@ -57,6 +58,7 @@ import org.xml.sax.SAXException; * Abstract base class for canonicalization algorithms. * * @author Christian Geuer-Pollmann + * @version $Revision: 1.5 $ */ public abstract class CanonicalizerBase extends CanonicalizerSpi { //Constants to be outputed, In char array form, so @@ -122,6 +124,18 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { throws CanonicalizationException { return engineCanonicalizeSubTree(rootNode,(Node)null); } + /** + * Method engineCanonicalizeXPathNodeSet + * @inheritDoc + * @param xpathNodeSet + * @throws CanonicalizationException + */ + public byte[] engineCanonicalizeXPathNodeSet(Set xpathNodeSet) + throws CanonicalizationException { + this._xpathNodeSet = xpathNodeSet; + return engineCanonicalizeXPathNodeSetInternal(XMLUtils.getOwnerDocument(this._xpathNodeSet)); + } + /** * Canonicalizes a Subtree node. * @param input the root of the subtree to canicalize @@ -143,15 +157,8 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { return bytes; } else if (input.isNodeSet()) { nodeFilter=input.getNodeFilters(); - Document doc = null; - if (input.getSubNode() != null) { - doc=XMLUtils.getOwnerDocument(input.getSubNode()); - } else { - doc=XMLUtils.getOwnerDocument(input.getNodeSet()); - } - if (input.isNeedsToBeExpanded()) { - XMLUtils.circumventBug2650(doc); - } + + circumventBugIfNeeded(input); if (input.getSubNode() != null) { bytes = engineCanonicalizeXPathNodeSetInternal(input.getSubNode()); @@ -173,6 +180,13 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { } } /** + * @param _writer The _writer to set. + */ + public void setWriter(OutputStream _writer) { + this._writer = _writer; + } + + /** * Canonicalizes a Subtree node. * * @param rootNode @@ -187,11 +201,13 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { this._excludeNode = excludeNode; try { NameSpaceSymbTable ns=new NameSpaceSymbTable(); + int nodeLevel=NODE_BEFORE_DOCUMENT_ELEMENT; if (rootNode instanceof Element) { //Fills the nssymbtable with the definitions of the parent of the root subnode getParentNameSpaces((Element)rootNode,ns); + nodeLevel=NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; } - this.canonicalizeSubTree(rootNode,ns,rootNode); + this.canonicalizeSubTree(rootNode,ns,rootNode,nodeLevel); this._writer.close(); if (this._writer instanceof ByteArrayOutputStream) { byte []result=((ByteArrayOutputStream)this._writer).toByteArray(); @@ -199,6 +215,12 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { ((ByteArrayOutputStream)this._writer).reset(); } return result; + } else if (this._writer instanceof UnsyncByteArrayOutputStream) { + byte []result=((UnsyncByteArrayOutputStream)this._writer).toByteArray(); + if (reset) { + ((UnsyncByteArrayOutputStream)this._writer).reset(); + } + return result; } return null; @@ -219,13 +241,17 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { * @throws CanonicalizationException * @throws IOException */ - final void canonicalizeSubTree(Node currentNode, NameSpaceSymbTable ns,Node endnode) + final void canonicalizeSubTree(Node currentNode, NameSpaceSymbTable ns,Node endnode, + int documentLevel) throws CanonicalizationException, IOException { + if (isVisibleInt(currentNode)==-1) + return; Node sibling=null; Node parentNode=null; final OutputStream writer=this._writer; final Node excludeNode=this._excludeNode; final boolean includeComments=this._includeComments; + Map cache=new HashMap(); do { switch (currentNode.getNodeType()) { @@ -242,18 +268,17 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { case Node.DOCUMENT_FRAGMENT_NODE : case Node.DOCUMENT_NODE : ns.outputNodePush(); - //currentNode = currentNode.getFirstChild(); sibling= currentNode.getFirstChild(); break; case Node.COMMENT_NODE : if (includeComments) { - outputCommentToWriter((Comment) currentNode, writer); + outputCommentToWriter((Comment) currentNode, writer, documentLevel); } break; case Node.PROCESSING_INSTRUCTION_NODE : - outputPItoWriter((ProcessingInstruction) currentNode, writer); + outputPItoWriter((ProcessingInstruction) currentNode, writer, documentLevel); break; case Node.TEXT_NODE : @@ -262,6 +287,7 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { break; case Node.ELEMENT_NODE : + documentLevel=NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; if (currentNode==excludeNode) { break; } @@ -270,27 +296,27 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { ns.outputNodePush(); writer.write('<'); String name=currentElement.getTagName(); - writeStringToUtf8(name,writer); + UtfHelpper.writeByte(name,writer,cache); Iterator attrs = this.handleAttributesSubtree(currentElement,ns); if (attrs!=null) { //we output all Attrs which are available while (attrs.hasNext()) { Attr attr = (Attr) attrs.next(); - outputAttrToWriter(attr.getNodeName(),attr.getNodeValue(), writer); + outputAttrToWriter(attr.getNodeName(),attr.getNodeValue(), writer,cache); } } writer.write('>'); sibling= currentNode.getFirstChild(); if (sibling==null) { writer.write(_END_TAG); - writeStringToUtf8(name,writer); + UtfHelpper.writeStringToUtf8(name,writer); writer.write('>'); //We fineshed with this level, pop to the previous definitions. ns.outputNodePop(); - if (parentNode != null) { + if (parentNode != null) { sibling= currentNode.getNextSibling(); - } + } } else { parentNode=currentElement; } @@ -298,7 +324,7 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { } while (sibling==null && parentNode!=null) { writer.write(_END_TAG); - writeStringToUtf8(((Element)parentNode).getTagName(),writer); + UtfHelpper.writeByte(((Element)parentNode).getTagName(),writer,cache); writer.write('>'); //We fineshed with this level, pop to the previous definitions. ns.outputNodePop(); @@ -307,6 +333,7 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { sibling=parentNode.getNextSibling(); parentNode=parentNode.getParentNode(); if (!(parentNode instanceof Element)) { + documentLevel=NODE_AFTER_DOCUMENT_ELEMENT; parentNode=null; } } @@ -317,47 +344,8 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { } while(true); } - /** - * Checks whether a Comment or ProcessingInstruction is before or after the - * document element. This is needed for prepending or appending "\n"s. - * - * @param currentNode comment or pi to check - * @return NODE_BEFORE_DOCUMENT_ELEMENT, NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT or NODE_AFTER_DOCUMENT_ELEMENT - * @see #NODE_BEFORE_DOCUMENT_ELEMENT - * @see #NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT - * @see #NODE_AFTER_DOCUMENT_ELEMENT - */ - final static int getPositionRelativeToDocumentElement(Node currentNode) { - if ((currentNode == null) || - (currentNode.getParentNode().getNodeType() != Node.DOCUMENT_NODE) ) { - return CanonicalizerBase.NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; - } - Element documentElement = currentNode.getOwnerDocument().getDocumentElement(); - if ( (documentElement == null) || (documentElement == currentNode) ){ - return CanonicalizerBase.NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; - } - for (Node x = currentNode; x != null; x = x.getNextSibling()) { - if (x == documentElement) { - return CanonicalizerBase.NODE_BEFORE_DOCUMENT_ELEMENT; - } - } - - return CanonicalizerBase.NODE_AFTER_DOCUMENT_ELEMENT; - } - - /** - * Method engineCanonicalizeXPathNodeSet - * @inheritDoc - * @param xpathNodeSet - * @throws CanonicalizationException - */ - public byte[] engineCanonicalizeXPathNodeSet(Set xpathNodeSet) - throws CanonicalizationException { - this._xpathNodeSet = xpathNodeSet; - return engineCanonicalizeXPathNodeSetInternal(XMLUtils.getOwnerDocument(this._xpathNodeSet)); - } private byte[] engineCanonicalizeXPathNodeSetInternal(Node doc) throws CanonicalizationException { @@ -370,6 +358,12 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { ((ByteArrayOutputStream)this._writer).reset(); } return sol; + } else if (this._writer instanceof UnsyncByteArrayOutputStream) { + byte []result=((UnsyncByteArrayOutputStream)this._writer).toByteArray(); + if (reset) { + ((UnsyncByteArrayOutputStream)this._writer).reset(); + } + return result; } return null; } catch (UnsupportedEncodingException ex) { @@ -390,11 +384,17 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { */ final void canonicalizeXPathNodeSet(Node currentNode,Node endnode ) throws CanonicalizationException, IOException { - boolean currentNodeIsVisible = false; - NameSpaceSymbTable ns=new NameSpaceSymbTable(); + if (isVisibleInt(currentNode)==-1) + return; + boolean currentNodeIsVisible = false; + NameSpaceSymbTable ns=new NameSpaceSymbTable(); + if (currentNode instanceof Element) + getParentNameSpaces((Element)currentNode,ns); Node sibling=null; Node parentNode=null; OutputStream writer=this._writer; + int documentLevel=NODE_BEFORE_DOCUMENT_ELEMENT; + Map cache=new HashMap(); do { switch (currentNode.getNodeType()) { @@ -416,14 +416,14 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { break; case Node.COMMENT_NODE : - if (this._includeComments && isVisible(currentNode)) { - outputCommentToWriter((Comment) currentNode, writer); + if (this._includeComments && (isVisibleDO(currentNode,ns.getLevel())==1)) { + outputCommentToWriter((Comment) currentNode, writer, documentLevel); } break; case Node.PROCESSING_INSTRUCTION_NODE : if (isVisible(currentNode)) - outputPItoWriter((ProcessingInstruction) currentNode, writer); + outputPItoWriter((ProcessingInstruction) currentNode, writer, documentLevel); break; case Node.TEXT_NODE : @@ -436,12 +436,6 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { || (nextSibling.getNodeType() == Node.CDATA_SECTION_NODE)); nextSibling = nextSibling.getNextSibling()) { - /* The XPath data model allows to select only the first of a - * sequence of mixed text and CDATA nodes. But we must output - * them all, so we must search: - * - * @see http://nagoya.apache.org/bugzilla/show_bug.cgi?id=6329 - */ outputTextToWriter(nextSibling.getNodeValue(), writer); currentNode=nextSibling; sibling=currentNode.getNextSibling(); @@ -451,15 +445,21 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { break; case Node.ELEMENT_NODE : + documentLevel=NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; Element currentElement = (Element) currentNode; //Add a level to the nssymbtable. So latter can be pop-back. String name=null; - currentNodeIsVisible=isVisible(currentNode); + int i=isVisibleDO(currentNode,ns.getLevel()); + if (i==-1) { + sibling= currentNode.getNextSibling(); + break; + } + currentNodeIsVisible=(i==1); if (currentNodeIsVisible) { ns.outputNodePush(); writer.write('<'); name=currentElement.getTagName(); - writeStringToUtf8(name,writer); + UtfHelpper.writeByte(name,writer,cache); } else { ns.push(); } @@ -469,7 +469,7 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { //we output all Attrs which are available while (attrs.hasNext()) { Attr attr = (Attr) attrs.next(); - outputAttrToWriter(attr.getNodeName(),attr.getNodeValue(), writer); + outputAttrToWriter(attr.getNodeName(),attr.getNodeValue(), writer,cache); } } if (currentNodeIsVisible) { @@ -480,7 +480,7 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { if (sibling==null) { if (currentNodeIsVisible) { writer.write(_END_TAG); - writeStringToUtf8(name,writer); + UtfHelpper.writeByte(name,writer,cache); writer.write('>'); //We fineshed with this level, pop to the previous definitions. ns.outputNodePop(); @@ -498,7 +498,7 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { while (sibling==null && parentNode!=null) { if (isVisible(parentNode)) { writer.write(_END_TAG); - writeStringToUtf8(((Element)parentNode).getTagName(),writer); + UtfHelpper.writeByte(((Element)parentNode).getTagName(),writer,cache); writer.write('>'); //We fineshed with this level, pop to the previous definitions. ns.outputNodePop(); @@ -511,6 +511,7 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { parentNode=parentNode.getParentNode(); if (!(parentNode instanceof Element)) { parentNode=null; + documentLevel=NODE_AFTER_DOCUMENT_ELEMENT; } } if (sibling==null) @@ -519,12 +520,38 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { sibling=currentNode.getNextSibling(); } while(true); } + int isVisibleDO(Node currentNode,int level) { + if (nodeFilter!=null) { + Iterator it=nodeFilter.iterator(); + while (it.hasNext()) { + int i=((NodeFilter)it.next()).isNodeIncludeDO(currentNode,level); + if (i!=1) + return i; + } + } + if ((this._xpathNodeSet!=null) && !this._xpathNodeSet.contains(currentNode)) + return 0; + return 1; + } + int isVisibleInt(Node currentNode) { + if (nodeFilter!=null) { + Iterator it=nodeFilter.iterator(); + while (it.hasNext()) { + int i=((NodeFilter)it.next()).isNodeInclude(currentNode); + if (i!=1) + return i; + } + } + if ((this._xpathNodeSet!=null) && !this._xpathNodeSet.contains(currentNode)) + return 0; + return 1; + } boolean isVisible(Node currentNode) { if (nodeFilter!=null) { Iterator it=nodeFilter.iterator(); while (it.hasNext()) { - if (!((NodeFilter)it.next()).isNodeInclude(currentNode)) + if (((NodeFilter)it.next()).isNodeInclude(currentNode)!=1) return false; } } @@ -533,19 +560,42 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { return true; } + void handleParent(Element e,NameSpaceSymbTable ns) { + if (!e.hasAttributes()) { + return; + } + NamedNodeMap attrs = e.getAttributes(); + int attrsLength = attrs.getLength(); + for (int i = 0; i < attrsLength; i++) { + Attr N = (Attr) attrs.item(i); + if (Constants.NamespaceSpecNS!=N.getNamespaceURI()) { + //Not a namespace definition, ignore. + continue; + } + + String NName=N.getLocalName(); + String NValue=N.getNodeValue(); + if (XML.equals(NName) + && Constants.XML_LANG_SPACE_SpecNS.equals(NValue)) { + continue; + } + ns.addMapping(NName,NValue,N); + } + } + /** * Adds to ns the definitons from the parent elements of el * @param el * @param ns */ - final static void getParentNameSpaces(Element el,NameSpaceSymbTable ns) { - List parents=new ArrayList(); + final void getParentNameSpaces(Element el,NameSpaceSymbTable ns) { + List parents=new ArrayList(10); Node n1=el.getParentNode(); if (!(n1 instanceof Element)) { return; } //Obtain all the parents of the elemnt - Element parent=(Element) el.getParentNode(); + Element parent=(Element) n1; while (parent!=null) { parents.add(parent); Node n=parent.getParentNode(); @@ -557,297 +607,15 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { //Visit them in reverse order. ListIterator it=parents.listIterator(parents.size()); while (it.hasPrevious()) { - Element ele=(Element)it.previous(); - if (!ele.hasAttributes()) { - continue; + Element ele=(Element)it.previous(); + handleParent(ele, ns); } - NamedNodeMap attrs = ele.getAttributes(); - int attrsLength = attrs.getLength(); - for (int i = 0; i < attrsLength; i++) { - Attr N = (Attr) attrs.item(i); - if (!Constants.NamespaceSpecNS.equals(N.getNamespaceURI())) { - //Not a namespace definition, ignore. - continue; - } - - String NName=N.getLocalName(); - String NValue=N.getNodeValue(); - if (XML.equals(NName) - && Constants.XML_LANG_SPACE_SpecNS.equals(NValue)) { - continue; - } - ns.addMapping(NName,NValue,N); - } - } Attr nsprefix; if (((nsprefix=ns.getMappingWithoutRendered("xmlns"))!=null) && "".equals(nsprefix.getValue())) { ns.addMappingAndRender("xmlns","",nullNode); } } - /** - * Outputs an Attribute to the internal Writer. - * - * The string value of the node is modified by replacing - *
        - *
      • all ampersands (&) with &amp;
      • - *
      • all open angle brackets (<) with &lt;
      • - *
      • all quotation mark characters with &quot;
      • - *
      • and the whitespace characters #x9, #xA, and #xD, with character - * references. The character references are written in uppercase - * hexadecimal with no leading zeroes (for example, #xD is represented - * by the character reference &#xD;)
      • - *
      - * - * @param name - * @param value - * @param writer - * @throws IOException - */ - static final void outputAttrToWriter(final String name, final String value, final OutputStream writer) throws IOException { - writer.write(' '); - writeStringToUtf8(name,writer); - writer.write(equalsStr); - byte []toWrite; - final int length = value.length(); - for (int i=0;i < length; i++) { - char c = value.charAt(i); - - switch (c) { - - case '&' : - toWrite=_AMP_; - //writer.write(_AMP_); - break; - - case '<' : - toWrite=_LT_; - //writer.write(_LT_); - break; - - case '"' : - toWrite=_QUOT_; - //writer.write(_QUOT_); - break; - - case 0x09 : // '\t' - toWrite=__X9_; - //writer.write(__X9_); - break; - - case 0x0A : // '\n' - toWrite=__XA_; - //writer.write(__XA_); - break; - - case 0x0D : // '\r' - toWrite=__XD_; - //writer.write(__XD_); - break; - - default : - writeCharToUtf8(c,writer); - //this._writer.write(c); - continue; - } - writer.write(toWrite); - } - - writer.write('\"'); - } - - final static void writeCharToUtf8(final char c,final OutputStream out) throws IOException{ - char ch; - if (/*(c >= 0x0001) &&*/ (c <= 0x007F)) { - out.write(c); - return; - } - int bias; - int write; - if (c > 0x07FF) { - ch=(char)(c>>>12); - write=0xE0; - if (ch>0) { - write |= ( ch & 0x0F); - } - out.write(write); - write=0x80; - bias=0x3F; - } else { - write=0xC0; - bias=0x1F; - } - ch=(char)(c>>>6); - if (ch>0) { - write|= (ch & bias); - } - out.write(write); - out.write(0x80 | ((c) & 0x3F)); - - } - - final static void writeStringToUtf8(final String str,final OutputStream out) throws IOException{ - final int length=str.length(); - int i=0; - char c; - while (i= 0x0001) &&*/ (c <= 0x007F)) { - out.write(c); - continue; - } - char ch; - int bias; - int write; - if (c > 0x07FF) { - ch=(char)(c>>>12); - write=0xE0; - if (ch>0) { - write |= ( ch & 0x0F); - } - out.write(write); - write=0x80; - bias=0x3F; - } else { - write=0xC0; - bias=0x1F; - } - ch=(char)(c>>>6); - if (ch>0) { - write|= (ch & bias); - } - out.write(write); - out.write(0x80 | ((c) & 0x3F)); - continue; - - } - - } - /** - * Outputs a PI to the internal Writer. - * - * @param currentPI - * @param writer where to write the things - * @throws IOException - */ - static final void outputPItoWriter(ProcessingInstruction currentPI, OutputStream writer) throws IOException { - final int position = getPositionRelativeToDocumentElement(currentPI); - - if (position == NODE_AFTER_DOCUMENT_ELEMENT) { - writer.write('\n'); - } - writer.write(_BEGIN_PI); - - final String target = currentPI.getTarget(); - int length = target.length(); - - for (int i = 0; i < length; i++) { - char c=target.charAt(i); - if (c==0x0D) { - writer.write(__XD_); - } else { - writeCharToUtf8(c,writer); - } - } - - final String data = currentPI.getData(); - - length = data.length(); - - if (length > 0) { - writer.write(' '); - - for (int i = 0; i < length; i++) { - char c=data.charAt(i); - if (c==0x0D) { - writer.write(__XD_); - } else { - writeCharToUtf8(c,writer); - } - } - } - - writer.write(_END_PI); - if (position == NODE_BEFORE_DOCUMENT_ELEMENT) { - writer.write('\n'); - } - } - - /** - * Method outputCommentToWriter - * - * @param currentComment - * @param writer writer where to write the things - * @throws IOException - */ - static final void outputCommentToWriter(Comment currentComment, OutputStream writer) throws IOException { - final int position = getPositionRelativeToDocumentElement(currentComment); - if (position == NODE_AFTER_DOCUMENT_ELEMENT) { - writer.write('\n'); - } - writer.write(_BEGIN_COMM); - - final String data = currentComment.getData(); - final int length = data.length(); - - for (int i = 0; i < length; i++) { - char c=data.charAt(i); - if (c==0x0D) { - writer.write(__XD_); - } else { - writeCharToUtf8(c,writer); - } - } - - writer.write(_END_COMM); - if (position == NODE_BEFORE_DOCUMENT_ELEMENT) { - writer.write('\n'); - } - } - - /** - * Outputs a Text of CDATA section to the internal Writer. - * - * @param text - * @param writer writer where to write the things - * @throws IOException - */ - static final void outputTextToWriter(final String text, final OutputStream writer) throws IOException { - final int length = text.length(); - byte []toWrite; - for (int i = 0; i < length; i++) { - char c = text.charAt(i); - - switch (c) { - - case '&' : - toWrite=_AMP_; - //writer.write(_AMP_); - break; - - case '<' : - toWrite=_LT_; - //writer.write(_LT_); - break; - - case '>' : - toWrite=_GT_; - //writer.write(_GT_); - break; - - case 0xD : - toWrite=__XD_; - //writer.write(__XD_); - break; - - default : - writeCharToUtf8(c,writer); - continue; - } - writer.write(toWrite); - } - } - /** * Obtain the attributes to output for this node in XPathNodeSet c14n. * @@ -870,13 +638,207 @@ public abstract class CanonicalizerBase extends CanonicalizerSpi { abstract Iterator handleAttributesSubtree(Element E, NameSpaceSymbTable ns) throws CanonicalizationException; + abstract void circumventBugIfNeeded(XMLSignatureInput input) throws CanonicalizationException, ParserConfigurationException, IOException, SAXException; + /** + * Outputs an Attribute to the internal Writer. + * + * The string value of the node is modified by replacing + *
        + *
      • all ampersands (&) with &amp;
      • + *
      • all open angle brackets (<) with &lt;
      • + *
      • all quotation mark characters with &quot;
      • + *
      • and the whitespace characters #x9, #xA, and #xD, with character + * references. The character references are written in uppercase + * hexadecimal with no leading zeroes (for example, #xD is represented + * by the character reference &#xD;)
      • + *
      + * + * @param name + * @param value + * @param writer + * @throws IOException + */ + static final void outputAttrToWriter(final String name, final String value, final OutputStream writer, + final Map cache) throws IOException { + writer.write(' '); + UtfHelpper.writeByte(name,writer,cache); + writer.write(equalsStr); + byte []toWrite; + final int length = value.length(); + int i=0; + while (i < length) { + char c = value.charAt(i++); + + switch (c) { + + case '&' : + toWrite=_AMP_; + break; + + case '<' : + toWrite=_LT_; + break; + + case '"' : + toWrite=_QUOT_; + break; + + case 0x09 : // '\t' + toWrite=__X9_; + break; + + case 0x0A : // '\n' + toWrite=__XA_; + break; + + case 0x0D : // '\r' + toWrite=__XD_; + break; + + default : + if (c < 0x80 ) { + writer.write(c); + } else { + UtfHelpper.writeCharToUtf8(c,writer); + }; + continue; + } + writer.write(toWrite); + } - /** - * @param _writer The _writer to set. - */ - public void setWriter(OutputStream _writer) { - this._writer = _writer; - } + writer.write('\"'); + } + + /** + * Outputs a PI to the internal Writer. + * + * @param currentPI + * @param writer where to write the things + * @throws IOException + */ + static final void outputPItoWriter(ProcessingInstruction currentPI, OutputStream writer,int position) throws IOException { + + if (position == NODE_AFTER_DOCUMENT_ELEMENT) { + writer.write('\n'); + } + writer.write(_BEGIN_PI); + + final String target = currentPI.getTarget(); + int length = target.length(); + + for (int i = 0; i < length; i++) { + char c=target.charAt(i); + if (c==0x0D) { + writer.write(__XD_); + } else { + if (c < 0x80) { + writer.write(c); + } else { + UtfHelpper.writeCharToUtf8(c,writer); + }; + } + } + + final String data = currentPI.getData(); + + length = data.length(); + + if (length > 0) { + writer.write(' '); + + for (int i = 0; i < length; i++) { + char c=data.charAt(i); + if (c==0x0D) { + writer.write(__XD_); + } else { + UtfHelpper.writeCharToUtf8(c,writer); + } + } + } + + writer.write(_END_PI); + if (position == NODE_BEFORE_DOCUMENT_ELEMENT) { + writer.write('\n'); + } + } + + /** + * Method outputCommentToWriter + * + * @param currentComment + * @param writer writer where to write the things + * @throws IOException + */ + static final void outputCommentToWriter(Comment currentComment, OutputStream writer,int position) throws IOException { + if (position == NODE_AFTER_DOCUMENT_ELEMENT) { + writer.write('\n'); + } + writer.write(_BEGIN_COMM); + + final String data = currentComment.getData(); + final int length = data.length(); + + for (int i = 0; i < length; i++) { + char c=data.charAt(i); + if (c==0x0D) { + writer.write(__XD_); + } else { + if (c < 0x80) { + writer.write(c); + } else { + UtfHelpper.writeCharToUtf8(c,writer); + }; + } + } + + writer.write(_END_COMM); + if (position == NODE_BEFORE_DOCUMENT_ELEMENT) { + writer.write('\n'); + } + } + + /** + * Outputs a Text of CDATA section to the internal Writer. + * + * @param text + * @param writer writer where to write the things + * @throws IOException + */ + static final void outputTextToWriter(final String text, final OutputStream writer) throws IOException { + final int length = text.length(); + byte []toWrite; + for (int i = 0; i < length; i++) { + char c = text.charAt(i); + + switch (c) { + + case '&' : + toWrite=_AMP_; + break; + + case '<' : + toWrite=_LT_; + break; + + case '>' : + toWrite=_GT_; + break; + + case 0xD : + toWrite=__XD_; + break; + + default : + if (c < 0x80) { + writer.write(c); + } else { + UtfHelpper.writeCharToUtf8(c,writer); + }; + continue; + } + writer.write(toWrite); + } + } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/NameSpaceSymbTable.java b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/NameSpaceSymbTable.java index bad23010f..538d36974 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/NameSpaceSymbTable.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/NameSpaceSymbTable.java @@ -20,16 +20,10 @@ */ package com.sun.org.apache.xml.internal.security.c14n.implementations; -import java.lang.reflect.Array; -import java.util.AbstractList; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.Iterator; import java.util.List; -import java.util.Map; - import org.w3c.dom.Attr; @@ -46,21 +40,26 @@ import org.w3c.dom.Node; public class NameSpaceSymbTable { /**The map betwen prefix-> entry table. */ - SymbMap symb = new SymbMap(); + SymbMap symb; /**The level of nameSpaces (for Inclusive visibility).*/ int nameSpaces=0; /**The stacks for removing the definitions when doing pop.*/ - List level = new ArrayList(); + List level; boolean cloned=true; static final String XMLNS="xmlns"; + final static SymbMap initialMap=new SymbMap(); + static { + NameSpaceSymbEntry ne=new NameSpaceSymbEntry("",null,true,XMLNS); + ne.lastrendered=""; + initialMap.put(XMLNS,ne); + } /** * Default constractor **/ public NameSpaceSymbTable() { + level = new ArrayList(10); //Insert the default binding for xmlns. - NameSpaceSymbEntry ne=new NameSpaceSymbEntry("",null,true); - ne.lastrendered=""; - symb.put(XMLNS,ne); + symb=(SymbMap) initialMap.clone(); } /** @@ -75,8 +74,14 @@ public class NameSpaceSymbTable { NameSpaceSymbEntry n=(NameSpaceSymbEntry)(it.next()); //put them rendered? if ((!n.rendered) && (n.n!=null)) { + n=(NameSpaceSymbEntry) n.clone(); + needsClone(); + symb.put(n.prefix,n); + n.lastrendered=n.uri; + n.rendered=true; + result.add(n.n); - n.rendered=true; + } } } @@ -104,10 +109,6 @@ public class NameSpaceSymbTable { **/ public void push() { //Put the number of namespace definitions in the stack. - /**if (cloned) { - Object ob[]= {symb,cloned ? symb : null}; - level.add(ob); - } **/ level.add(null); cloned=false; } @@ -124,7 +125,7 @@ public class NameSpaceSymbTable { if (size==0) { cloned=false; } else - cloned=(level.get(size-1)!=symb); + cloned=(level.get(size-1)!=symb); } else { cloned=false; } @@ -134,8 +135,7 @@ public class NameSpaceSymbTable { final void needsClone() { if (!cloned) { - level.remove(level.size()-1); - level.add(symb); + level.set(level.size()-1,symb); symb=(SymbMap) symb.clone(); cloned=true; } @@ -200,7 +200,7 @@ public class NameSpaceSymbTable { return false; } //Creates and entry in the table for this new definition. - NameSpaceSymbEntry ne=new NameSpaceSymbEntry(uri,n,false); + NameSpaceSymbEntry ne=new NameSpaceSymbEntry(uri,n,false,prefix); needsClone(); symb.put(prefix, ne); if (ob != null) { @@ -238,7 +238,7 @@ public class NameSpaceSymbTable { return null; } - NameSpaceSymbEntry ne=new NameSpaceSymbEntry(uri,n,true); + NameSpaceSymbEntry ne=new NameSpaceSymbEntry(uri,n,true,prefix); ne.lastrendered=uri; needsClone(); symb.put(prefix, ne); @@ -251,53 +251,38 @@ public class NameSpaceSymbTable { } return ne.n; } - /** - * Adds & gets(if needed) the attribute node that defines the binding for the prefix. - * Take on account if the rules of rendering in the inclusive c14n. - * For inclusive c14n. - * @param prefix the prefix to obtain the attribute. - * @param outputNode the container element is an output element. - * @param uri the Uri of the definition - * @param n the attribute that have the definition - * @return null if there is no need to render the prefix. Otherwise the node of - * definition. - **/ - public Node addMappingAndRenderXNodeSet(String prefix, String uri,Attr n,boolean outputNode) { + + public int getLevel() { + // TODO Auto-generated method stub + return level.size(); + } + + public void removeMapping(String prefix) { NameSpaceSymbEntry ob = symb.get(prefix); - int visibleNameSpaces=nameSpaces; - if ((ob!=null) && uri.equals(ob.uri)) { - if (!ob.rendered) { - ob=(NameSpaceSymbEntry)ob.clone(); - needsClone(); - symb.put(prefix,ob); - ob.rendered=true; - ob.level=visibleNameSpaces; - return ob.n; - } - ob=(NameSpaceSymbEntry)ob.clone(); + + if (ob!=null) { needsClone(); - symb.put(prefix,ob); - if (outputNode && (((visibleNameSpaces-ob.level)<2) || XMLNS.equals(prefix)) ) { - ob.level=visibleNameSpaces; - return null; //Already rendered, just return nulll - } - ob.level=visibleNameSpaces; - return ob.n; - } + symb.put(prefix,null); + } + } - NameSpaceSymbEntry ne=new NameSpaceSymbEntry(uri,n,true); - ne.level=nameSpaces; - ne.rendered=true; - needsClone(); - symb.put(prefix, ne); - if (ob != null) { - ne.lastrendered=ob.lastrendered; + public void removeMappingIfNotRender(String prefix) { + NameSpaceSymbEntry ob = symb.get(prefix); - if ((ob.lastrendered!=null)&& (ob.lastrendered.equals(uri))) { - ne.rendered=true; - } - } - return ne.n; + if (ob!=null && !ob.rendered) { + needsClone(); + symb.put(prefix,null); + } + } + + public boolean removeMappingIfRender(String prefix) { + NameSpaceSymbEntry ob = symb.get(prefix); + + if (ob!=null && ob.rendered) { + needsClone(); + symb.put(prefix,null); + } + return false; } } @@ -305,10 +290,11 @@ public class NameSpaceSymbTable { * The internal structure of NameSpaceSymbTable. **/ class NameSpaceSymbEntry implements Cloneable { - NameSpaceSymbEntry(String name,Attr n,boolean rendered) { + NameSpaceSymbEntry(String name,Attr n,boolean rendered,String prefix) { this.uri=name; this.rendered=rendered; this.n=n; + this.prefix=prefix; } /** @inheritDoc */ public Object clone() { @@ -320,6 +306,7 @@ class NameSpaceSymbEntry implements Cloneable { } /** The level where the definition was rendered(Only for inclusive) */ int level=0; + String prefix; /**The URI that the prefix defines */ String uri; /**The last output in the URI for this prefix (This for speed reason).*/ @@ -330,53 +317,57 @@ class NameSpaceSymbEntry implements Cloneable { Attr n; }; -class SymbMap implements Cloneable{ - int free=23; - NameSpaceSymbEntry[] entries=new NameSpaceSymbEntry[free]; - String[] keys=new String[free]; - - void put(String key, NameSpaceSymbEntry value) { +class SymbMap implements Cloneable { + int free=23; + NameSpaceSymbEntry[] entries; + String[] keys; + SymbMap() { + entries=new NameSpaceSymbEntry[free]; + keys=new String[free]; + } + void put(String key, NameSpaceSymbEntry value) { int index = index(key); - Object oldKey = keys[index]; - keys[index] = key; - entries[index] = value; + Object oldKey = keys[index]; + keys[index] = key; + entries[index] = value; if (oldKey==null || !oldKey.equals(key)) { - if (--free == 0) { - free=entries.length; - int newCapacity = free<<2; - rehash(newCapacity); - } + if (--free == 0) { + free=entries.length; + int newCapacity = free<<2; + rehash(newCapacity); + } } } List entrySet() { - List a=new ArrayList(); - for (int i=0;iint value @@ -384,37 +375,38 @@ class SymbMap implements Cloneable{ protected void rehash(int newCapacity) { int oldCapacity = keys.length; String oldKeys[] = keys; - NameSpaceSymbEntry oldVals[] = entries; + NameSpaceSymbEntry oldVals[] = entries; - keys = new String[newCapacity]; - entries = new NameSpaceSymbEntry[newCapacity]; + keys = new String[newCapacity]; + entries = new NameSpaceSymbEntry[newCapacity]; for (int i = oldCapacity; i-- > 0;) { if(oldKeys[i] != null) { String o = oldKeys[i]; int index = index(o); - keys[index] = o; - entries[index] = oldVals[i]; + keys[index] = o; + entries[index] = oldVals[i]; } } } - NameSpaceSymbEntry get(String key) { - return entries[index(key)]; - } - protected Object clone() { - // TODO Auto-generated method stub - try { - SymbMap copy=(SymbMap) super.clone(); - copy.entries=new NameSpaceSymbEntry[entries.length]; - System.arraycopy(entries,0,copy.entries,0,entries.length); - copy.keys=new String[keys.length]; - System.arraycopy(keys,0,copy.keys,0,keys.length); - - return copy; - } catch (CloneNotSupportedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - return null; + + NameSpaceSymbEntry get(String key) { + return entries[index(key)]; + } + + protected Object clone() { + try { + SymbMap copy=(SymbMap) super.clone(); + copy.entries=new NameSpaceSymbEntry[entries.length]; + System.arraycopy(entries,0,copy.entries,0,entries.length); + copy.keys=new String[keys.length]; + System.arraycopy(keys,0,copy.keys,0,keys.length); + + return copy; + } catch (CloneNotSupportedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); } + return null; + } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/UtfHelpper.java b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/UtfHelpper.java new file mode 100644 index 000000000..cfcc06dd2 --- /dev/null +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/UtfHelpper.java @@ -0,0 +1,155 @@ +package com.sun.org.apache.xml.internal.security.c14n.implementations; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; + +public class UtfHelpper { + + final static void writeByte(final String str,final OutputStream out,Map cache) throws IOException { + byte []result=(byte[]) cache.get(str); + if (result==null) { + result=getStringInUtf8(str); + cache.put(str,result); + } + + out.write(result); + + } + + final static void writeCharToUtf8(final char c,final OutputStream out) throws IOException{ + if (c < 0x80) { + out.write(c); + return; + } + if ((c >= 0xD800 && c <= 0xDBFF) || (c >= 0xDC00 && c <= 0xDFFF) ){ + //No Surrogates in sun java + out.write(0x3f); + return; + } + int bias; + int write; + char ch; + if (c > 0x07FF) { + ch=(char)(c>>>12); + write=0xE0; + if (ch>0) { + write |= ( ch & 0x0F); + } + out.write(write); + write=0x80; + bias=0x3F; + } else { + write=0xC0; + bias=0x1F; + } + ch=(char)(c>>>6); + if (ch>0) { + write|= (ch & bias); + } + out.write(write); + out.write(0x80 | ((c) & 0x3F)); + + } + + final static void writeStringToUtf8(final String str,final OutputStream out) throws IOException{ + final int length=str.length(); + int i=0; + char c; + while (i= 0xD800 && c <= 0xDBFF) || (c >= 0xDC00 && c <= 0xDFFF) ){ + //No Surrogates in sun java + out.write(0x3f); + continue; + } + char ch; + int bias; + int write; + if (c > 0x07FF) { + ch=(char)(c>>>12); + write=0xE0; + if (ch>0) { + write |= ( ch & 0x0F); + } + out.write(write); + write=0x80; + bias=0x3F; + } else { + write=0xC0; + bias=0x1F; + } + ch=(char)(c>>>6); + if (ch>0) { + write|= (ch & bias); + } + out.write(write); + out.write(0x80 | ((c) & 0x3F)); + + } + + } + public final static byte[] getStringInUtf8(final String str) { + final int length=str.length(); + boolean expanded=false; + byte []result=new byte[length]; + int i=0; + int out=0; + char c; + while (i= 0xD800 && c <= 0xDBFF) || (c >= 0xDC00 && c <= 0xDFFF) ){ + //No Surrogates in sun java + result[out++]=0x3f; + + continue; + } + if (!expanded) { + byte newResult[]=new byte[3*length]; + System.arraycopy(result, 0, newResult, 0, out); + result=newResult; + expanded=true; + } + char ch; + int bias; + byte write; + if (c > 0x07FF) { + ch=(char)(c>>>12); + write=(byte)0xE0; + if (ch>0) { + write |= ( ch & 0x0F); + } + result[out++]=write; + write=(byte)0x80; + bias=0x3F; + } else { + write=(byte)0xC0; + bias=0x1F; + } + ch=(char)(c>>>6); + if (ch>0) { + write|= (ch & bias); + } + result[out++]=write; + result[out++]=(byte)(0x80 | ((c) & 0x3F));/**/ + + } + if (expanded) { + byte newResult[]=new byte[out]; + System.arraycopy(result, 0, newResult, 0, out); + result=newResult; + } + return result; + } + + + +} diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/encryption/XMLCipher.java b/src/share/classes/com/sun/org/apache/xml/internal/security/encryption/XMLCipher.java index 781421649..683acdbf0 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/encryption/XMLCipher.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/encryption/XMLCipher.java @@ -22,6 +22,7 @@ package com.sun.org.apache.xml.internal.security.encryption; import java.io.ByteArrayOutputStream; +import java.io.InputStream; import java.io.IOException; import java.io.StringReader; import java.io.UnsupportedEncodingException; @@ -30,6 +31,7 @@ import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -204,7 +206,7 @@ public class XMLCipher { * @since 1.0. */ private XMLCipher() { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Constructing XMLCipher..."); + logger.log(java.util.logging.Level.FINE, "Constructing XMLCipher..."); _factory = new Factory(); _serializer = new Serializer(); @@ -266,7 +268,7 @@ public class XMLCipher { public static XMLCipher getInstance(String transformation) throws XMLEncryptionException { // sanity checks - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Getting XMLCipher..."); + logger.log(java.util.logging.Level.FINE, "Getting XMLCipher..."); if (null == transformation) logger.log(java.util.logging.Level.SEVERE, "Transformation unexpectedly null..."); if(!isValidEncryptionAlgorithm(transformation)) @@ -294,7 +296,7 @@ public class XMLCipher { try { instance._contextCipher = Cipher.getInstance(jceAlgorithm); - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "cihper.algoritm = " + + logger.log(java.util.logging.Level.FINE, "cihper.algoritm = " + instance._contextCipher.getAlgorithm()); } catch (NoSuchAlgorithmException nsae) { throw new XMLEncryptionException("empty", nsae); @@ -305,8 +307,39 @@ public class XMLCipher { return (instance); } - public static XMLCipher getInstance(String transformation,Cipher cipher) throws - XMLEncryptionException { + /** + * Returns an XMLCipher that implements the specified + * transformation, operates on the specified context document and serializes + * the document with the specified canonicalization algorithm before it + * encrypts the document. + *

      + * + * @param transformation the name of the transformation, e.g., + * XMLCipher.TRIPLEDES which is + * shorthand for + * "http://www.w3.org/2001/04/xmlenc#tripledes-cbc" + * @param canon the name of the c14n algorithm, if + * null use standard serializer + * @return + * @throws XMLEncryptionException + */ + + public static XMLCipher getInstance(String transformation, String canon) + throws XMLEncryptionException { + XMLCipher instance = XMLCipher.getInstance(transformation); + + if (canon != null) { + try { + instance._canon = Canonicalizer.getInstance(canon); + } catch (InvalidCanonicalizerException ice) { + throw new XMLEncryptionException("empty", ice); + } + } + + return instance; + } + + public static XMLCipher getInstance(String transformation,Cipher cipher) throws XMLEncryptionException { // sanity checks logger.log(java.util.logging.Level.FINE, "Getting XMLCipher..."); if (null == transformation) @@ -321,8 +354,8 @@ public class XMLCipher { instance._kek = null; - /* Create a canonicaliser - used when serialising DOM to octets - * prior to encryption (and for the reverse) */ + /* Create a canonicaliser - used when serialising DOM to octets + * prior to encryption (and for the reverse) */ try { instance._canon = Canonicalizer.getInstance @@ -346,41 +379,6 @@ public class XMLCipher { return (instance); } - - - /** - * Returns an XMLCipher that implements the specified - * transformation, operates on the specified context document and serializes - * the document with the specified canonicalization algorithm before it - * encrypts the document. - *

      - * - * @param transformation the name of the transformation, e.g., - * XMLCipher.TRIPLEDES which is - * shorthand for - * "http://www.w3.org/2001/04/xmlenc#tripledes-cbc" - * @param canon the name of the c14n algorithm, if - * null use standard serializer - * @return - * @throws XMLEncryptionException - */ - - public static XMLCipher getInstance(String transformation, String canon) - throws XMLEncryptionException { - XMLCipher instance = XMLCipher.getInstance(transformation); - - if (canon != null) { - try { - instance._canon = Canonicalizer.getInstance(canon); - } catch (InvalidCanonicalizerException ice) { - throw new XMLEncryptionException("empty", ice); - } - } - - return instance; - } - - /** * Returns an XMLCipher that implements the specified * transformation and operates on the specified context document. @@ -396,7 +394,7 @@ public class XMLCipher { public static XMLCipher getProviderInstance(String transformation, String provider) throws XMLEncryptionException { // sanity checks - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Getting XMLCipher..."); + logger.log(java.util.logging.Level.FINE, "Getting XMLCipher..."); if (null == transformation) logger.log(java.util.logging.Level.SEVERE, "Transformation unexpectedly null..."); if(null == provider) @@ -429,9 +427,9 @@ public class XMLCipher { instance._contextCipher = Cipher.getInstance(jceAlgorithm, provider); - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "cipher._algorithm = " + + logger.log(java.util.logging.Level.FINE, "cipher._algorithm = " + instance._contextCipher.getAlgorithm()); - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "provider.name = " + provider); + logger.log(java.util.logging.Level.FINE, "provider.name = " + provider); } catch (NoSuchAlgorithmException nsae) { throw new XMLEncryptionException("empty", nsae); } catch (NoSuchProviderException nspre) { @@ -490,7 +488,7 @@ public class XMLCipher { public static XMLCipher getInstance() throws XMLEncryptionException { // sanity checks - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Getting XMLCipher for no transformation..."); + logger.log(java.util.logging.Level.FINE, "Getting XMLCipher for no transformation..."); XMLCipher instance = new XMLCipher(); @@ -532,7 +530,7 @@ public class XMLCipher { throws XMLEncryptionException { // sanity checks - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Getting XMLCipher, provider but no transformation"); + logger.log(java.util.logging.Level.FINE, "Getting XMLCipher, provider but no transformation"); if(null == provider) logger.log(java.util.logging.Level.SEVERE, "Provider unexpectedly null.."); if("" == provider) @@ -578,7 +576,7 @@ public class XMLCipher { */ public void init(int opmode, Key key) throws XMLEncryptionException { // sanity checks - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Initializing XMLCipher..."); + logger.log(java.util.logging.Level.FINE, "Initializing XMLCipher..."); _ek = null; _ed = null; @@ -586,18 +584,18 @@ public class XMLCipher { switch (opmode) { case ENCRYPT_MODE : - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "opmode = ENCRYPT_MODE"); + logger.log(java.util.logging.Level.FINE, "opmode = ENCRYPT_MODE"); _ed = createEncryptedData(CipherData.VALUE_TYPE, "NO VALUE YET"); break; case DECRYPT_MODE : - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "opmode = DECRYPT_MODE"); + logger.log(java.util.logging.Level.FINE, "opmode = DECRYPT_MODE"); break; case WRAP_MODE : - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "opmode = WRAP_MODE"); + logger.log(java.util.logging.Level.FINE, "opmode = WRAP_MODE"); _ek = createEncryptedKey(CipherData.VALUE_TYPE, "NO VALUE YET"); break; case UNWRAP_MODE : - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "opmode = UNWRAP_MODE"); + logger.log(java.util.logging.Level.FINE, "opmode = UNWRAP_MODE"); break; default : logger.log(java.util.logging.Level.SEVERE, "Mode unexpectedly invalid"); @@ -622,7 +620,7 @@ public class XMLCipher { public EncryptedData getEncryptedData() { // Sanity checks - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Returning EncryptedData"); + logger.log(java.util.logging.Level.FINE, "Returning EncryptedData"); return _ed; } @@ -640,7 +638,7 @@ public class XMLCipher { public EncryptedKey getEncryptedKey() { // Sanity checks - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Returning EncryptedKey"); + logger.log(java.util.logging.Level.FINE, "Returning EncryptedKey"); return _ek; } @@ -750,11 +748,11 @@ public class XMLCipher { */ private Document encryptElement(Element element) throws Exception{ - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Encrypting element..."); + logger.log(java.util.logging.Level.FINE, "Encrypting element..."); if(null == element) logger.log(java.util.logging.Level.SEVERE, "Element unexpectedly null..."); if(_cipherMode != ENCRYPT_MODE) - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "XMLCipher unexpectedly not in ENCRYPT_MODE..."); + logger.log(java.util.logging.Level.FINE, "XMLCipher unexpectedly not in ENCRYPT_MODE..."); if (_algorithm == null) { throw new XMLEncryptionException("XMLCipher instance without transformation specified"); @@ -785,11 +783,11 @@ public class XMLCipher { */ private Document encryptElementContent(Element element) throws /* XMLEncryption */Exception { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Encrypting element content..."); + logger.log(java.util.logging.Level.FINE, "Encrypting element content..."); if(null == element) logger.log(java.util.logging.Level.SEVERE, "Element unexpectedly null..."); if(_cipherMode != ENCRYPT_MODE) - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "XMLCipher unexpectedly not in ENCRYPT_MODE..."); + logger.log(java.util.logging.Level.FINE, "XMLCipher unexpectedly not in ENCRYPT_MODE..."); if (_algorithm == null) { throw new XMLEncryptionException("XMLCipher instance without transformation specified"); @@ -815,7 +813,7 @@ public class XMLCipher { */ public Document doFinal(Document context, Document source) throws /* XMLEncryption */Exception { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Processing source document..."); + logger.log(java.util.logging.Level.FINE, "Processing source document..."); if(null == context) logger.log(java.util.logging.Level.SEVERE, "Context document unexpectedly null..."); if(null == source) @@ -855,7 +853,7 @@ public class XMLCipher { */ public Document doFinal(Document context, Element element) throws /* XMLEncryption */Exception { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Processing source element..."); + logger.log(java.util.logging.Level.FINE, "Processing source element..."); if(null == context) logger.log(java.util.logging.Level.SEVERE, "Context document unexpectedly null..."); if(null == element) @@ -898,7 +896,7 @@ public class XMLCipher { */ public Document doFinal(Document context, Element element, boolean content) throws /* XMLEncryption*/ Exception { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Processing source element..."); + logger.log(java.util.logging.Level.FINE, "Processing source element..."); if(null == context) logger.log(java.util.logging.Level.SEVERE, "Context document unexpectedly null..."); if(null == element) @@ -952,6 +950,34 @@ public class XMLCipher { return encryptData(context, element, false); } + /** + * Returns an EncryptedData interface. Use this operation if + * you want to have full control over the serialization of the element + * or element content. + * + * This does not change the source document in any way. + * + * @param context the context Document. + * @param type a URI identifying type information about the plaintext form + * of the encrypted content (may be null) + * @param serializedData the serialized data + * @return the EncryptedData + * @throws Exception + */ + public EncryptedData encryptData(Document context, String type, + InputStream serializedData) throws Exception { + + logger.log(java.util.logging.Level.FINE, "Encrypting element..."); + if (null == context) + logger.log(java.util.logging.Level.SEVERE, "Context document unexpectedly null..."); + if (null == serializedData) + logger.log(java.util.logging.Level.SEVERE, "Serialized data unexpectedly null..."); + if (_cipherMode != ENCRYPT_MODE) + logger.log(java.util.logging.Level.FINE, "XMLCipher unexpectedly not in ENCRYPT_MODE..."); + + return encryptData(context, null, type, serializedData); + } + /** * Returns an EncryptedData interface. Use this operation if * you want to have full control over the contents of the @@ -966,160 +992,60 @@ public class XMLCipher { * @return the EncryptedData * @throws Exception */ - public EncryptedData encryptData(Document context, Element element, boolean contentMode) throws - /* XMLEncryption */ Exception { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Encrypting element..."); - if (null == context) - logger.log(java.util.logging.Level.SEVERE, "Context document unexpectedly null..."); - if (null == element) - logger.log(java.util.logging.Level.SEVERE, "Element unexpectedly null..."); - if (_cipherMode != ENCRYPT_MODE) - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "XMLCipher unexpectedly not in ENCRYPT_MODE..."); + public EncryptedData encryptData( + Document context, Element element, boolean contentMode) + throws /* XMLEncryption */ Exception { - _contextDocument = context; - - if (_algorithm == null) { - throw new XMLEncryptionException("XMLCipher instance without transformation specified"); - } - - String serializedOctets = null; - if (contentMode) { - NodeList children = element.getChildNodes(); - if ((null != children)) { - serializedOctets = _serializer.serialize(children); - } else { - Object exArgs[] = { "Element has no content." }; - throw new XMLEncryptionException("empty", exArgs); - } - } else { - serializedOctets = _serializer.serialize(element); - } - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Serialized octets:\n" + serializedOctets); - - byte[] encryptedBytes = null; - - // Now create the working cipher if none was created already - Cipher c; - if (_contextCipher == null) { - String jceAlgorithm = - JCEMapper.translateURItoJCEID(_algorithm); - - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "alg = " + jceAlgorithm); - - try { - if (_requestedJCEProvider == null) - c = Cipher.getInstance(jceAlgorithm); - else - c = Cipher.getInstance(jceAlgorithm, _requestedJCEProvider); - } catch (NoSuchAlgorithmException nsae) { - throw new XMLEncryptionException("empty", nsae); - } catch (NoSuchProviderException nspre) { - throw new XMLEncryptionException("empty", nspre); - } catch (NoSuchPaddingException nspae) { - throw new XMLEncryptionException("empty", nspae); - } - } - else { - c = _contextCipher; - } - // Now perform the encryption - - try { - // Should internally generate an IV - // todo - allow user to set an IV - c.init(_cipherMode, _key); - } catch (InvalidKeyException ike) { - throw new XMLEncryptionException("empty", ike); - } - - try { - encryptedBytes = - c.doFinal(serializedOctets.getBytes("UTF-8")); - - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Expected cipher.outputSize = " + - Integer.toString(c.getOutputSize( - serializedOctets.getBytes().length))); - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Actual cipher.outputSize = " + - Integer.toString(encryptedBytes.length)); - } catch (IllegalStateException ise) { - throw new XMLEncryptionException("empty", ise); - } catch (IllegalBlockSizeException ibse) { - throw new XMLEncryptionException("empty", ibse); - } catch (BadPaddingException bpe) { - throw new XMLEncryptionException("empty", bpe); - } catch (UnsupportedEncodingException uee) { - throw new XMLEncryptionException("empty", uee); - } - - // Now build up to a properly XML Encryption encoded octet stream - // IvParameterSpec iv; - - byte[] iv = c.getIV(); - byte[] finalEncryptedBytes = - new byte[iv.length + encryptedBytes.length]; - System.arraycopy(iv, 0, finalEncryptedBytes, 0, - iv.length); - System.arraycopy(encryptedBytes, 0, finalEncryptedBytes, - iv.length, - encryptedBytes.length); - - String base64EncodedEncryptedOctets = Base64.encode(finalEncryptedBytes); - - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Encrypted octets:\n" + base64EncodedEncryptedOctets); - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Encrypted octets length = " + - base64EncodedEncryptedOctets.length()); - - try { - CipherData cd = _ed.getCipherData(); - CipherValue cv = cd.getCipherValue(); - // cv.setValue(base64EncodedEncryptedOctets.getBytes()); - cv.setValue(base64EncodedEncryptedOctets); - - if (contentMode) { - _ed.setType( - new URI(EncryptionConstants.TYPE_CONTENT).toString()); - } else { - _ed.setType( - new URI(EncryptionConstants.TYPE_ELEMENT).toString()); - } - EncryptionMethod method = - _factory.newEncryptionMethod(new URI(_algorithm).toString()); - _ed.setEncryptionMethod(method); - } catch (URI.MalformedURIException mfue) { - throw new XMLEncryptionException("empty", mfue); - } - return (_ed); - } - - - - public EncryptedData encryptData(Document context, byte [] serializedOctets, boolean contentMode) throws - /* XMLEncryption */ Exception { logger.log(java.util.logging.Level.FINE, "Encrypting element..."); if (null == context) logger.log(java.util.logging.Level.SEVERE, "Context document unexpectedly null..."); - if (null == serializedOctets) - logger.log(java.util.logging.Level.SEVERE, "Canonicalized Data is unexpectedly null..."); + if (null == element) + logger.log(java.util.logging.Level.SEVERE, "Element unexpectedly null..."); if (_cipherMode != ENCRYPT_MODE) logger.log(java.util.logging.Level.FINE, "XMLCipher unexpectedly not in ENCRYPT_MODE..."); + if (contentMode) { + return encryptData + (context, element, EncryptionConstants.TYPE_CONTENT, null); + } else { + return encryptData + (context, element, EncryptionConstants.TYPE_ELEMENT, null); + } + } + + private EncryptedData encryptData( + Document context, Element element, String type, + InputStream serializedData) throws /* XMLEncryption */ Exception { + _contextDocument = context; if (_algorithm == null) { - throw new XMLEncryptionException("XMLCipher instance without transformation specified"); + throw new XMLEncryptionException + ("XMLCipher instance without transformation specified"); } - - logger.log(java.util.logging.Level.FINE, "Serialized octets:\n" + serializedOctets); + String serializedOctets = null; + if (serializedData == null) { + if (type == EncryptionConstants.TYPE_CONTENT) { + NodeList children = element.getChildNodes(); + if (null != children) { + serializedOctets = _serializer.serialize(children); + } else { + Object exArgs[] = { "Element has no content." }; + throw new XMLEncryptionException("empty", exArgs); + } + } else { + serializedOctets = _serializer.serialize(element); + } + logger.log(java.util.logging.Level.FINE, "Serialized octets:\n" + serializedOctets); + } byte[] encryptedBytes = null; // Now create the working cipher if none was created already Cipher c; if (_contextCipher == null) { - String jceAlgorithm = - JCEMapper.translateURItoJCEID(_algorithm); - + String jceAlgorithm = JCEMapper.translateURItoJCEID(_algorithm); logger.log(java.util.logging.Level.FINE, "alg = " + jceAlgorithm); try { @@ -1148,41 +1074,47 @@ public class XMLCipher { } try { - encryptedBytes = - c.doFinal(serializedOctets); - - logger.log(java.util.logging.Level.FINE, "Expected cipher.outputSize = " + + if (serializedData != null) { + int numBytes; + byte[] buf = new byte[8192]; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + while ((numBytes = serializedData.read(buf)) != -1) { + byte[] data = c.update(buf, 0, numBytes); + baos.write(data); + } + baos.write(c.doFinal()); + encryptedBytes = baos.toByteArray(); + } else { + encryptedBytes = c.doFinal(serializedOctets.getBytes("UTF-8")); + logger.log(java.util.logging.Level.FINE, "Expected cipher.outputSize = " + Integer.toString(c.getOutputSize( - serializedOctets.length))); + serializedOctets.getBytes().length))); + } logger.log(java.util.logging.Level.FINE, "Actual cipher.outputSize = " + - Integer.toString(encryptedBytes.length)); + Integer.toString(encryptedBytes.length)); } catch (IllegalStateException ise) { throw new XMLEncryptionException("empty", ise); } catch (IllegalBlockSizeException ibse) { throw new XMLEncryptionException("empty", ibse); } catch (BadPaddingException bpe) { throw new XMLEncryptionException("empty", bpe); - } catch (Exception uee) { + } catch (UnsupportedEncodingException uee) { throw new XMLEncryptionException("empty", uee); } // Now build up to a properly XML Encryption encoded octet stream // IvParameterSpec iv; - byte[] iv = c.getIV(); byte[] finalEncryptedBytes = new byte[iv.length + encryptedBytes.length]; - System.arraycopy(iv, 0, finalEncryptedBytes, 0, - iv.length); - System.arraycopy(encryptedBytes, 0, finalEncryptedBytes, - iv.length, - encryptedBytes.length); - + System.arraycopy(iv, 0, finalEncryptedBytes, 0, iv.length); + System.arraycopy(encryptedBytes, 0, finalEncryptedBytes, iv.length, + encryptedBytes.length); String base64EncodedEncryptedOctets = Base64.encode(finalEncryptedBytes); logger.log(java.util.logging.Level.FINE, "Encrypted octets:\n" + base64EncodedEncryptedOctets); logger.log(java.util.logging.Level.FINE, "Encrypted octets length = " + - base64EncodedEncryptedOctets.length()); + base64EncodedEncryptedOctets.length()); try { CipherData cd = _ed.getCipherData(); @@ -1190,15 +1122,11 @@ public class XMLCipher { // cv.setValue(base64EncodedEncryptedOctets.getBytes()); cv.setValue(base64EncodedEncryptedOctets); - if (contentMode) { - _ed.setType( - new URI(EncryptionConstants.TYPE_CONTENT).toString()); - } else { - _ed.setType( - new URI(EncryptionConstants.TYPE_ELEMENT).toString()); + if (type != null) { + _ed.setType(new URI(type).toString()); } EncryptionMethod method = - _factory.newEncryptionMethod(new URI(_algorithm).toString()); + _factory.newEncryptionMethod(new URI(_algorithm).toString()); _ed.setEncryptionMethod(method); } catch (URI.MalformedURIException mfue) { throw new XMLEncryptionException("empty", mfue); @@ -1206,7 +1134,6 @@ public class XMLCipher { return (_ed); } - /** * Returns an EncryptedData interface. Use this operation if * you want to load an EncryptedData structure from a DOM @@ -1219,7 +1146,7 @@ public class XMLCipher { */ public EncryptedData loadEncryptedData(Document context, Element element) throws XMLEncryptionException { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Loading encrypted element..."); + logger.log(java.util.logging.Level.FINE, "Loading encrypted element..."); if(null == context) logger.log(java.util.logging.Level.SEVERE, "Context document unexpectedly null..."); if(null == element) @@ -1246,13 +1173,13 @@ public class XMLCipher { public EncryptedKey loadEncryptedKey(Document context, Element element) throws XMLEncryptionException { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Loading encrypted key..."); + logger.log(java.util.logging.Level.FINE, "Loading encrypted key..."); if(null == context) logger.log(java.util.logging.Level.SEVERE, "Context document unexpectedly null..."); if(null == element) logger.log(java.util.logging.Level.SEVERE, "Element unexpectedly null..."); if(_cipherMode != UNWRAP_MODE && _cipherMode != DECRYPT_MODE) - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "XMLCipher unexpectedly not in UNWRAP_MODE or DECRYPT_MODE..."); + logger.log(java.util.logging.Level.FINE, "XMLCipher unexpectedly not in UNWRAP_MODE or DECRYPT_MODE..."); _contextDocument = context; _ek = _factory.newEncryptedKey(element); @@ -1290,12 +1217,12 @@ public class XMLCipher { public EncryptedKey encryptKey(Document doc, Key key) throws XMLEncryptionException { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Encrypting key ..."); + logger.log(java.util.logging.Level.FINE, "Encrypting key ..."); if(null == key) logger.log(java.util.logging.Level.SEVERE, "Key unexpectedly null..."); if(_cipherMode != WRAP_MODE) - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "XMLCipher unexpectedly not in WRAP_MODE..."); + logger.log(java.util.logging.Level.FINE, "XMLCipher unexpectedly not in WRAP_MODE..."); if (_algorithm == null) { @@ -1313,7 +1240,7 @@ public class XMLCipher { String jceAlgorithm = JCEMapper.translateURItoJCEID(_algorithm); - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "alg = " + jceAlgorithm); + logger.log(java.util.logging.Level.FINE, "alg = " + jceAlgorithm); try { if (_requestedJCEProvider == null) @@ -1345,8 +1272,8 @@ public class XMLCipher { String base64EncodedEncryptedOctets = Base64.encode(encryptedBytes); - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Encrypted key octets:\n" + base64EncodedEncryptedOctets); - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Encrypted key octets length = " + + logger.log(java.util.logging.Level.FINE, "Encrypted key octets:\n" + base64EncodedEncryptedOctets); + logger.log(java.util.logging.Level.FINE, "Encrypted key octets length = " + base64EncodedEncryptedOctets.length()); CipherValue cv = _ek.getCipherData().getCipherValue(); @@ -1376,10 +1303,10 @@ public class XMLCipher { public Key decryptKey(EncryptedKey encryptedKey, String algorithm) throws XMLEncryptionException { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Decrypting key from previously loaded EncryptedKey..."); + logger.log(java.util.logging.Level.FINE, "Decrypting key from previously loaded EncryptedKey..."); if(_cipherMode != UNWRAP_MODE) - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "XMLCipher unexpectedly not in UNWRAP_MODE..."); + logger.log(java.util.logging.Level.FINE, "XMLCipher unexpectedly not in UNWRAP_MODE..."); if (algorithm == null) { throw new XMLEncryptionException("Cannot decrypt a key without knowing the algorithm"); @@ -1387,7 +1314,7 @@ public class XMLCipher { if (_key == null) { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Trying to find a KEK via key resolvers"); + logger.log(java.util.logging.Level.FINE, "Trying to find a KEK via key resolvers"); KeyInfo ki = encryptedKey.getKeyInfo(); if (ki != null) { @@ -1418,7 +1345,7 @@ public class XMLCipher { JCEMapper.translateURItoJCEID( encryptedKey.getEncryptionMethod().getAlgorithm()); - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "JCE Algorithm = " + jceAlgorithm); + logger.log(java.util.logging.Level.FINE, "JCE Algorithm = " + jceAlgorithm); try { if (_requestedJCEProvider == null) @@ -1448,7 +1375,7 @@ public class XMLCipher { throw new XMLEncryptionException("empty", nsae); } - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Decryption of key type " + algorithm + " OK"); + logger.log(java.util.logging.Level.FINE, "Decryption of key type " + algorithm + " OK"); return ret; @@ -1478,14 +1405,9 @@ public class XMLCipher { * * @param node the Node to clear. */ - private void removeContent(Node node) { - NodeList list = node.getChildNodes(); - if (list.getLength() > 0) { - Node n = list.item(0); - if (null != n) { - n.getParentNode().removeChild(n); - } - removeContent(node); + private static void removeContent(Node node) { + while (node.hasChildNodes()) { + node.removeChild(node.getFirstChild()); } } @@ -1499,7 +1421,7 @@ public class XMLCipher { private Document decryptElement(Element element) throws XMLEncryptionException { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Decrypting element..."); + logger.log(java.util.logging.Level.FINE, "Decrypting element..."); if(_cipherMode != DECRYPT_MODE) logger.log(java.util.logging.Level.SEVERE, "XMLCipher unexpectedly not in DECRYPT_MODE..."); @@ -1512,7 +1434,7 @@ public class XMLCipher { } - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Decrypted octets:\n" + octets); + logger.log(java.util.logging.Level.FINE, "Decrypted octets:\n" + octets); Node sourceParent = element.getParentNode(); @@ -1573,7 +1495,7 @@ public class XMLCipher { public byte[] decryptToByteArray(Element element) throws XMLEncryptionException { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Decrypting to ByteArray..."); + logger.log(java.util.logging.Level.FINE, "Decrypting to ByteArray..."); if(_cipherMode != DECRYPT_MODE) logger.log(java.util.logging.Level.SEVERE, "XMLCipher unexpectedly not in DECRYPT_MODE..."); @@ -2226,7 +2148,7 @@ public class XMLCipher { AgreementMethod newAgreementMethod(Element element) throws XMLEncryptionException { if (null == element) { - //complain + throw new NullPointerException("element is null"); } String algorithm = element.getAttributeNS(null, @@ -2292,7 +2214,7 @@ public class XMLCipher { CipherData newCipherData(Element element) throws XMLEncryptionException { if (null == element) { - // complain + throw new NullPointerException("element is null"); } int type = 0; @@ -2352,7 +2274,7 @@ public class XMLCipher { (Element) transformsElements.item(0); if (transformsElement != null) { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Creating a DSIG based Transforms element"); + logger.log(java.util.logging.Level.FINE, "Creating a DSIG based Transforms element"); try { result.setTransforms(new TransformsImpl(transformsElement)); } @@ -2411,34 +2333,28 @@ public class XMLCipher { XMLEncryptionException { EncryptedData result = null; - NodeList dataElements = element.getElementsByTagNameNS( - EncryptionConstants.EncryptionSpecNS, - EncryptionConstants._TAG_CIPHERDATA); + NodeList dataElements = element.getElementsByTagNameNS( + EncryptionConstants.EncryptionSpecNS, + EncryptionConstants._TAG_CIPHERDATA); - // Need to get the last CipherData found, as earlier ones will - // be for elements in the KeyInfo lists + // Need to get the last CipherData found, as earlier ones will + // be for elements in the KeyInfo lists Element dataElement = - (Element) dataElements.item(dataElements.getLength() - 1); + (Element) dataElements.item(dataElements.getLength() - 1); CipherData data = newCipherData(dataElement); result = newEncryptedData(data); - try { - result.setId(element.getAttributeNS( - null, EncryptionConstants._ATT_ID)); - result.setType(new URI( - element.getAttributeNS( - null, EncryptionConstants._ATT_TYPE)).toString()); - result.setMimeType(element.getAttributeNS( - null, EncryptionConstants._ATT_MIMETYPE)); - result.setEncoding(new URI( - element.getAttributeNS( - null, Constants._ATT_ENCODING)).toString()); - } catch (URI.MalformedURIException mfue) { - // do nothing - } + result.setId(element.getAttributeNS( + null, EncryptionConstants._ATT_ID)); + result.setType( + element.getAttributeNS(null, EncryptionConstants._ATT_TYPE)); + result.setMimeType(element.getAttributeNS( + null, EncryptionConstants._ATT_MIMETYPE)); + result.setEncoding( + element.getAttributeNS(null, Constants._ATT_ENCODING)); Element encryptionMethodElement = (Element) element.getElementsByTagNameNS( @@ -2450,18 +2366,18 @@ public class XMLCipher { } // BFL 16/7/03 - simple implementation - // TODO: Work out how to handle relative URI + // TODO: Work out how to handle relative URI Element keyInfoElement = (Element) element.getElementsByTagNameNS( Constants.SignatureSpecNS, Constants._TAG_KEYINFO).item(0); if (null != keyInfoElement) { - try { - result.setKeyInfo(new KeyInfo(keyInfoElement, null)); - } catch (XMLSecurityException xse) { - throw new XMLEncryptionException("Error loading Key Info", - xse); - } + try { + result.setKeyInfo(new KeyInfo(keyInfoElement, null)); + } catch (XMLSecurityException xse) { + throw new XMLEncryptionException("Error loading Key Info", + xse); + } } // TODO: Implement @@ -2511,31 +2427,25 @@ public class XMLCipher { EncryptedKey newEncryptedKey(Element element) throws XMLEncryptionException { EncryptedKey result = null; - NodeList dataElements = element.getElementsByTagNameNS( - EncryptionConstants.EncryptionSpecNS, - EncryptionConstants._TAG_CIPHERDATA); + NodeList dataElements = element.getElementsByTagNameNS( + EncryptionConstants.EncryptionSpecNS, + EncryptionConstants._TAG_CIPHERDATA); Element dataElement = - (Element) dataElements.item(dataElements.getLength() - 1); + (Element) dataElements.item(dataElements.getLength() - 1); CipherData data = newCipherData(dataElement); result = newEncryptedKey(data); - try { - result.setId(element.getAttributeNS( - null, EncryptionConstants._ATT_ID)); - result.setType(new URI( - element.getAttributeNS( - null, EncryptionConstants._ATT_TYPE)).toString()); - result.setMimeType(element.getAttributeNS( - null, EncryptionConstants._ATT_MIMETYPE)); - result.setEncoding(new URI( - element.getAttributeNS( - null, Constants._ATT_ENCODING)).toString()); - result.setRecipient(element.getAttributeNS( - null, EncryptionConstants._ATT_RECIPIENT)); - } catch (URI.MalformedURIException mfue) { - // do nothing - } + result.setId(element.getAttributeNS( + null, EncryptionConstants._ATT_ID)); + result.setType( + element.getAttributeNS(null, EncryptionConstants._ATT_TYPE)); + result.setMimeType(element.getAttributeNS( + null, EncryptionConstants._ATT_MIMETYPE)); + result.setEncoding( + element.getAttributeNS(null, Constants._ATT_ENCODING)); + result.setRecipient(element.getAttributeNS( + null, EncryptionConstants._ATT_RECIPIENT)); Element encryptionMethodElement = (Element) element.getElementsByTagNameNS( @@ -2550,12 +2460,12 @@ public class XMLCipher { (Element) element.getElementsByTagNameNS( Constants.SignatureSpecNS, Constants._TAG_KEYINFO).item(0); if (null != keyInfoElement) { - try { - result.setKeyInfo(new KeyInfo(keyInfoElement, null)); - } catch (XMLSecurityException xse) { - throw new XMLEncryptionException("Error loading Key Info", - xse); - } + try { + result.setKeyInfo(new KeyInfo(keyInfoElement, null)); + } catch (XMLSecurityException xse) { + throw new XMLEncryptionException + ("Error loading Key Info", xse); + } } // TODO: Implement @@ -2581,7 +2491,8 @@ public class XMLCipher { EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_CARRIEDKEYNAME).item(0); if (null != carriedNameElement) { - result.setCarriedName(carriedNameElement.getNodeValue()); + result.setCarriedName + (carriedNameElement.getFirstChild().getNodeValue()); } return (result); @@ -2680,13 +2591,8 @@ public class XMLCipher { EncryptionProperty newEncryptionProperty(Element element) { EncryptionProperty result = newEncryptionProperty(); - try { - result.setTarget(new URI( - element.getAttributeNS( - null, EncryptionConstants._ATT_TARGET)).toString()); - } catch (URI.MalformedURIException mfue) { - // do nothing - } + result.setTarget( + element.getAttributeNS(null, EncryptionConstants._ATT_TARGET)); result.setId(element.getAttributeNS( null, EncryptionConstants._ATT_ID)); // TODO: Make this lot work... @@ -2943,7 +2849,7 @@ public class XMLCipher { } catch (URI.MalformedURIException mfue) { //complain } - algorithm = tmpAlgorithm.toString(); + algorithmURI = tmpAlgorithm.toString(); } // @@ -3183,7 +3089,7 @@ public class XMLCipher { _contextDocument, EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_CIPHERVALUE); result.appendChild(_contextDocument.createTextNode( - new String(cipherValue))); + cipherValue)); return (result); } @@ -3247,8 +3153,7 @@ public class XMLCipher { } if (null != super.getType()) { result.setAttributeNS( - null, EncryptionConstants._ATT_TYPE, - super.getType().toString()); + null, EncryptionConstants._ATT_TYPE, super.getType()); } if (null != super.getMimeType()) { result.setAttributeNS( @@ -3258,7 +3163,7 @@ public class XMLCipher { if (null != super.getEncoding()) { result.setAttributeNS( null, EncryptionConstants._ATT_ENCODING, - super.getEncoding().toString()); + super.getEncoding()); } if (null != super.getEncryptionMethod()) { result.appendChild(((EncryptionMethodImpl) @@ -3383,8 +3288,7 @@ public class XMLCipher { } if (null != super.getType()) { result.setAttributeNS( - null, EncryptionConstants._ATT_TYPE, - super.getType().toString()); + null, EncryptionConstants._ATT_TYPE, super.getType()); } if (null != super.getMimeType()) { result.setAttributeNS(null, @@ -3392,7 +3296,7 @@ public class XMLCipher { } if (null != super.getEncoding()) { result.setAttributeNS(null, Constants._ATT_ENCODING, - super.getEncoding().toString()); + super.getEncoding()); } if (null != getRecipient()) { result.setAttributeNS(null, @@ -3468,13 +3372,17 @@ public class XMLCipher { * @param type */ public void setType(String type) { - URI tmpType = null; - try { - tmpType = new URI(type); - } catch (URI.MalformedURIException mfue) { - // complain + if (type == null || type.length() == 0) { + this.type = null; + } else { + URI tmpType = null; + try { + tmpType = new URI(type); + } catch (URI.MalformedURIException mfue) { + // complain + } + this.type = tmpType.toString(); } - this.type = tmpType.toString(); } /** * @@ -3502,13 +3410,17 @@ public class XMLCipher { * @param encoding */ public void setEncoding(String encoding) { - URI tmpEncoding = null; - try { - tmpEncoding = new URI(encoding); - } catch (URI.MalformedURIException mfue) { - // complain + if (encoding == null || encoding.length() == 0) { + this.encoding = null; + } else { + URI tmpEncoding = null; + try { + tmpEncoding = new URI(encoding); + } catch (URI.MalformedURIException mfue) { + // complain + } + this.encoding = tmpEncoding.toString(); } - this.encoding = tmpEncoding.toString(); } /** * @@ -3635,7 +3547,7 @@ public class XMLCipher { _contextDocument, EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_ENCRYPTIONMETHOD); result.setAttributeNS(null, EncryptionConstants._ATT_ALGORITHM, - algorithm.toString()); + algorithm); if (keySize > 0) { result.appendChild( ElementProxy.createElementForFamily(_contextDocument, @@ -3735,8 +3647,7 @@ public class XMLCipher { private class EncryptionPropertyImpl implements EncryptionProperty { private String target = null; private String id = null; - private String attributeName = null; - private String attributeValue = null; + private HashMap attributeMap = new HashMap(); private List encryptionInformation = null; /** @@ -3752,13 +3663,24 @@ public class XMLCipher { } /** @inheritDoc */ public void setTarget(String target) { - URI tmpTarget = null; - try { - tmpTarget = new URI(target); - } catch (URI.MalformedURIException mfue) { - // complain + if (target == null || target.length() == 0) { + this.target = null; + } else if (target.startsWith("#")) { + /* + * This is a same document URI reference. Do not parse, + * because com.sun.org.apache.xml.internal.utils.URI considers this an + * illegal URI because it has no scheme. + */ + this.target = target; + } else { + URI tmpTarget = null; + try { + tmpTarget = new URI(target); + } catch (URI.MalformedURIException mfue) { + // complain + } + this.target = tmpTarget.toString(); } - this.target = tmpTarget.toString(); } /** @inheritDoc */ public String getId() { @@ -3770,12 +3692,11 @@ public class XMLCipher { } /** @inheritDoc */ public String getAttribute(String attribute) { - return (attributeValue); + return (String) attributeMap.get(attribute); } /** @inheritDoc */ public void setAttribute(String attribute, String value) { - attributeName = attribute; - attributeValue = value; + attributeMap.put(attribute, value); } /** @inheritDoc */ public Iterator getEncryptionInformation() { @@ -3805,7 +3726,7 @@ public class XMLCipher { EncryptionConstants._TAG_ENCRYPTIONPROPERTY); if (null != target) { result.setAttributeNS(null, EncryptionConstants._ATT_TARGET, - target.toString()); + target); } if (null != id) { result.setAttributeNS(null, EncryptionConstants._ATT_ID, @@ -3839,7 +3760,13 @@ public class XMLCipher { * @param doc */ public TransformsImpl(Document doc) { - super(doc); + if (doc == null) { + throw new RuntimeException("Document is null"); + } + + this._doc = doc; + this._constructionElement = createElementForFamilyLocal(this._doc, + this.getBaseNamespace(), this.getBaseLocalName()); } /** * diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/encryption/XMLCipherInput.java b/src/share/classes/com/sun/org/apache/xml/internal/security/encryption/XMLCipherInput.java index 9d1db588e..65b9a604b 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/encryption/XMLCipherInput.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/encryption/XMLCipherInput.java @@ -108,84 +108,78 @@ public class XMLCipherInput { return null; } - /** - * Internal method to get bytes in decryption mode + /** + * Internal method to get bytes in decryption mode * @return the decripted bytes * @throws XMLEncryptionException - */ - - private byte[] getDecryptBytes() throws XMLEncryptionException { + */ + private byte[] getDecryptBytes() throws XMLEncryptionException { - String base64EncodedEncryptedOctets = null; + String base64EncodedEncryptedOctets = null; if (_cipherData.getDataType() == CipherData.REFERENCE_TYPE) { - // Fun time! - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Found a reference type CipherData"); - CipherReference cr = _cipherData.getCipherReference(); - - // Need to wrap the uri in an Attribute node so that we can - // Pass to the resource resolvers - - Attr uriAttr = cr.getURIAsAttr(); - XMLSignatureInput input = null; - - try { - ResourceResolver resolver = - ResourceResolver.getInstance(uriAttr, null); - input = resolver.resolve(uriAttr, null); - } catch (ResourceResolverException ex) { - throw new XMLEncryptionException("empty", ex); - } - - if (input != null) { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Managed to resolve URI \"" + cr.getURI() + "\""); - } - else { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Failed to resolve URI \"" + cr.getURI() + "\""); - } - - // Lets see if there are any transforms - Transforms transforms = cr.getTransforms(); - if (transforms != null) { - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Have transforms in cipher reference"); - try { - com.sun.org.apache.xml.internal.security.transforms.Transforms dsTransforms = - transforms.getDSTransforms(); - input = dsTransforms.performTransforms(input); - } catch (TransformationException ex) { - throw new XMLEncryptionException("empty", ex); - } - } - - try { - return input.getBytes(); - } - catch (IOException ex) { - throw new XMLEncryptionException("empty", ex); - } catch (CanonicalizationException ex) { - throw new XMLEncryptionException("empty", ex); - } - - // retrieve the cipher text + // Fun time! + logger.log(java.util.logging.Level.FINE, "Found a reference type CipherData"); + CipherReference cr = _cipherData.getCipherReference(); + + // Need to wrap the uri in an Attribute node so that we can + // Pass to the resource resolvers + + Attr uriAttr = cr.getURIAsAttr(); + XMLSignatureInput input = null; + + try { + ResourceResolver resolver = + ResourceResolver.getInstance(uriAttr, null); + input = resolver.resolve(uriAttr, null); + } catch (ResourceResolverException ex) { + throw new XMLEncryptionException("empty", ex); + } + + if (input != null) { + logger.log(java.util.logging.Level.FINE, "Managed to resolve URI \"" + cr.getURI() + "\""); + } else { + logger.log(java.util.logging.Level.FINE, "Failed to resolve URI \"" + cr.getURI() + "\""); + } + + // Lets see if there are any transforms + Transforms transforms = cr.getTransforms(); + if (transforms != null) { + logger.log(java.util.logging.Level.FINE, "Have transforms in cipher reference"); + try { + com.sun.org.apache.xml.internal.security.transforms.Transforms dsTransforms = + transforms.getDSTransforms(); + input = dsTransforms.performTransforms(input); + } catch (TransformationException ex) { + throw new XMLEncryptionException("empty", ex); + } + } + + try { + return input.getBytes(); + } catch (IOException ex) { + throw new XMLEncryptionException("empty", ex); + } catch (CanonicalizationException ex) { + throw new XMLEncryptionException("empty", ex); + } + + // retrieve the cipher text } else if (_cipherData.getDataType() == CipherData.VALUE_TYPE) { - CipherValue cv = _cipherData.getCipherValue(); - base64EncodedEncryptedOctets = new String(cv.getValue()); + base64EncodedEncryptedOctets = + _cipherData.getCipherValue().getValue(); } else { - throw new XMLEncryptionException("CipherData.getDataType() returned unexpected value"); - } + throw new XMLEncryptionException("CipherData.getDataType() returned unexpected value"); + } - if (logger.isLoggable(java.util.logging.Level.FINE)) logger.log(java.util.logging.Level.FINE, "Encrypted octets:\n" + base64EncodedEncryptedOctets); + logger.log(java.util.logging.Level.FINE, "Encrypted octets:\n" + base64EncodedEncryptedOctets); byte[] encryptedBytes = null; - try { - encryptedBytes = Base64.decode(base64EncodedEncryptedOctets); + encryptedBytes = Base64.decode(base64EncodedEncryptedOctets); } catch (Base64DecodingException bde) { throw new XMLEncryptionException("empty", bde); } - return (encryptedBytes); - - } - + return (encryptedBytes); + } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/ContentHandlerAlreadyRegisteredException.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/ContentHandlerAlreadyRegisteredException.java index e071b1474..6477d9bba 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/ContentHandlerAlreadyRegisteredException.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/ContentHandlerAlreadyRegisteredException.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -28,7 +27,7 @@ import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class ContentHandlerAlreadyRegisteredException extends XMLSecurityException { diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/KeyInfo.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/KeyInfo.java index 1750257f6..cf588b8d4 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/KeyInfo.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/KeyInfo.java @@ -25,6 +25,8 @@ package com.sun.org.apache.xml.internal.security.keys; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; import java.util.List; import javax.crypto.SecretKey; @@ -88,15 +90,22 @@ import org.w3c.dom.NodeList; * The containsXXX() methods return whether the KeyInfo * contains the corresponding type. * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class KeyInfo extends SignatureElementProxy { /** {@link java.util.logging} logging facility */ static java.util.logging.Logger log = java.util.logging.Logger.getLogger(KeyInfo.class.getName()); + List x509Datas=null; + List encryptedKeys=null; - + static final List nullList; + static { + List list = new ArrayList(); + list.add(null); + nullList = Collections.unmodifiableList(list); + } /** * Constructor KeyInfo @@ -108,7 +117,6 @@ public class KeyInfo extends SignatureElementProxy { XMLUtils.addReturnToElement(this._constructionElement); - } /** @@ -119,8 +127,8 @@ public class KeyInfo extends SignatureElementProxy { * @throws XMLSecurityException */ public KeyInfo(Element element, String BaseURI) throws XMLSecurityException { - super(element, BaseURI); + // _storageResolvers.add(null); } @@ -131,7 +139,7 @@ public class KeyInfo extends SignatureElementProxy { */ public void setId(String Id) { - if ((this._state == MODE_SIGN) && (Id != null)) { + if ((Id != null)) { this._constructionElement.setAttributeNS(null, Constants._ATT_ID, Id); IdResolver.registerElementById(this._constructionElement, Id); } @@ -162,10 +170,8 @@ public class KeyInfo extends SignatureElementProxy { */ public void add(KeyName keyname) { - if (this._state == MODE_SIGN) { this._constructionElement.appendChild(keyname.getElement()); XMLUtils.addReturnToElement(this._constructionElement); - } } /** @@ -219,11 +225,8 @@ public class KeyInfo extends SignatureElementProxy { * @param keyvalue */ public void add(KeyValue keyvalue) { - - if (this._state == MODE_SIGN) { this._constructionElement.appendChild(keyvalue.getElement()); XMLUtils.addReturnToElement(this._constructionElement); - } } /** @@ -241,11 +244,8 @@ public class KeyInfo extends SignatureElementProxy { * @param mgmtdata */ public void add(MgmtData mgmtdata) { - - if (this._state == MODE_SIGN) { this._constructionElement.appendChild(mgmtdata.getElement()); XMLUtils.addReturnToElement(this._constructionElement); - } } /** @@ -254,11 +254,8 @@ public class KeyInfo extends SignatureElementProxy { * @param pgpdata */ public void add(PGPData pgpdata) { - - if (this._state == MODE_SIGN) { this._constructionElement.appendChild(pgpdata.getElement()); XMLUtils.addReturnToElement(this._constructionElement); - } } /** @@ -279,11 +276,8 @@ public class KeyInfo extends SignatureElementProxy { * @param retrievalmethod */ public void add(RetrievalMethod retrievalmethod) { - - if (this._state == MODE_SIGN) { this._constructionElement.appendChild(retrievalmethod.getElement()); XMLUtils.addReturnToElement(this._constructionElement); - } } /** @@ -292,11 +286,8 @@ public class KeyInfo extends SignatureElementProxy { * @param spkidata */ public void add(SPKIData spkidata) { - - if (this._state == MODE_SIGN) { this._constructionElement.appendChild(spkidata.getElement()); XMLUtils.addReturnToElement(this._constructionElement); - } } /** @@ -305,11 +296,11 @@ public class KeyInfo extends SignatureElementProxy { * @param x509data */ public void add(X509Data x509data) { - - if (this._state == MODE_SIGN) { + if (x509Datas==null) + x509Datas=new ArrayList(); + x509Datas.add(x509data); this._constructionElement.appendChild(x509data.getElement()); XMLUtils.addReturnToElement(this._constructionElement); - } } /** @@ -321,12 +312,11 @@ public class KeyInfo extends SignatureElementProxy { public void add(EncryptedKey encryptedKey) throws XMLEncryptionException { - - if (this._state == MODE_SIGN) { + if (encryptedKeys==null) + encryptedKeys=new ArrayList(); + encryptedKeys.add(encryptedKey); XMLCipher cipher = XMLCipher.getInstance(); this._constructionElement.appendChild(cipher.martial(encryptedKey)); - } - } /** @@ -335,11 +325,8 @@ public class KeyInfo extends SignatureElementProxy { * @param element */ public void addUnknownElement(Element element) { - - if (this._state == MODE_SIGN) { this._constructionElement.appendChild(element); XMLUtils.addReturnToElement(this._constructionElement); - } } /** @@ -403,6 +390,9 @@ public class KeyInfo extends SignatureElementProxy { *@return the number of the X509Data tags */ public int lengthX509Data() { + if (x509Datas!=null) { + return x509Datas.size(); + } return this.length(Constants.SignatureSpecNS, Constants._TAG_X509DATA); } @@ -550,7 +540,9 @@ public class KeyInfo extends SignatureElementProxy { * @throws XMLSecurityException */ public X509Data itemX509Data(int i) throws XMLSecurityException { - + if (x509Datas!=null) { + return (X509Data) x509Datas.get(i); + } Element e = XMLUtils.selectDsNode(this._constructionElement.getFirstChild(), Constants._TAG_X509DATA,i); @@ -569,7 +561,9 @@ public class KeyInfo extends SignatureElementProxy { */ public EncryptedKey itemEncryptedKey(int i) throws XMLSecurityException { - + if (encryptedKeys!=null) { + return (EncryptedKey) encryptedKeys.get(i); + } Element e = XMLUtils.selectXencNode(this._constructionElement.getFirstChild(), EncryptionConstants._TAG_ENCRYPTEDKEY,i); @@ -707,20 +701,20 @@ public class KeyInfo extends SignatureElementProxy { PublicKey pk = this.getPublicKeyFromInternalResolvers(); if (pk != null) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "I could find a key using the per-KeyInfo key resolvers"); + log.log(java.util.logging.Level.FINE, "I could find a key using the per-KeyInfo key resolvers"); return pk; } - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "I couldn't find a key using the per-KeyInfo key resolvers"); + log.log(java.util.logging.Level.FINE, "I couldn't find a key using the per-KeyInfo key resolvers"); pk = this.getPublicKeyFromStaticResolvers(); if (pk != null) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "I could find a key using the system-wide key resolvers"); + log.log(java.util.logging.Level.FINE, "I could find a key using the system-wide key resolvers"); return pk; } - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "I couldn't find a key using the system-wide key resolvers"); + log.log(java.util.logging.Level.FINE, "I couldn't find a key using the system-wide key resolvers"); return null; } @@ -732,46 +726,29 @@ public class KeyInfo extends SignatureElementProxy { * @throws KeyResolverException */ PublicKey getPublicKeyFromStaticResolvers() throws KeyResolverException { - - for (int i = 0; i < KeyResolver.length(); i++) { - KeyResolver keyResolver = KeyResolver.item(i); + int length=KeyResolver.length(); + int storageLength=this._storageResolvers.size(); + Iterator it= KeyResolver.iterator(); + for (int i = 0; i < length; i++) { + KeyResolverSpi keyResolver = (KeyResolverSpi) it.next(); Node currentChild=this._constructionElement.getFirstChild(); + String uri= this.getBaseURI(); while (currentChild!=null) { if (currentChild.getNodeType() == Node.ELEMENT_NODE) { - if (this._storageResolvers.size() == 0) { - - // if we do not have storage resolvers, we verify with null - StorageResolver storage = null; - - if (keyResolver.canResolve((Element) currentChild, - this.getBaseURI(), storage)) { - PublicKey pk = - keyResolver.resolvePublicKey((Element) currentChild, - this.getBaseURI(), - storage); - - if (pk != null) { - return pk; - } - } - } else { - for (int k = 0; k < this._storageResolvers.size(); k++) { + for (int k = 0; k < storageLength; k++) { StorageResolver storage = (StorageResolver) this._storageResolvers.get(k); - if (keyResolver.canResolve((Element) currentChild, - this.getBaseURI(), storage)) { - PublicKey pk = - keyResolver.resolvePublicKey((Element) currentChild, - this.getBaseURI(), + PublicKey pk = + keyResolver.engineLookupAndResolvePublicKey((Element) currentChild, + uri, storage); - if (pk != null) { - return pk; - } + if (pk != null) { + KeyResolver.hit(it); + return pk; } } - } } currentChild=currentChild.getNextSibling(); } @@ -786,50 +763,27 @@ public class KeyInfo extends SignatureElementProxy { * @throws KeyResolverException */ PublicKey getPublicKeyFromInternalResolvers() throws KeyResolverException { - - for (int i = 0; i < this.lengthInternalKeyResolver(); i++) { + int length=lengthInternalKeyResolver(); + int storageLength=this._storageResolvers.size(); + for (int i = 0; i < length; i++) { KeyResolverSpi keyResolver = this.itemInternalKeyResolver(i); - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Try " + keyResolver.getClass().getName()); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Try " + keyResolver.getClass().getName()); Node currentChild=this._constructionElement.getFirstChild(); + String uri=this.getBaseURI(); while (currentChild!=null) { if (currentChild.getNodeType() == Node.ELEMENT_NODE) { - if (this._storageResolvers.size() == 0) { - - // if we do not have storage resolvers, we verify with null - StorageResolver storage = null; - - if (keyResolver.engineCanResolve((Element) currentChild, - this.getBaseURI(), - storage)) { - PublicKey pk = - keyResolver - .engineResolvePublicKey((Element) currentChild, this - .getBaseURI(), storage); + for (int k = 0; k < storageLength; k++) { + StorageResolver storage = + (StorageResolver) this._storageResolvers.get(k); + PublicKey pk = keyResolver + .engineLookupAndResolvePublicKey((Element) currentChild, uri, storage); if (pk != null) { - return pk; + return pk; } } - } else { - for (int k = 0; k < this._storageResolvers.size(); k++) { - StorageResolver storage = - (StorageResolver) this._storageResolvers.get(k); - - if (keyResolver.engineCanResolve((Element) currentChild, - this.getBaseURI(), - storage)) { - PublicKey pk = keyResolver - .engineResolvePublicKey((Element) currentChild, this - .getBaseURI(), storage); - - if (pk != null) { - return pk; - } - } - } - } } currentChild=currentChild.getNextSibling(); } @@ -850,12 +804,12 @@ public class KeyInfo extends SignatureElementProxy { X509Certificate cert = this.getX509CertificateFromInternalResolvers(); if (cert != null) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, + log.log(java.util.logging.Level.FINE, "I could find a X509Certificate using the per-KeyInfo key resolvers"); return cert; } - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, + log.log(java.util.logging.Level.FINE, "I couldn't find a X509Certificate using the per-KeyInfo key resolvers"); @@ -863,12 +817,12 @@ public class KeyInfo extends SignatureElementProxy { cert = this.getX509CertificateFromStaticResolvers(); if (cert != null) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, + log.log(java.util.logging.Level.FINE, "I could find a X509Certificate using the system-wide key resolvers"); return cert; } - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, + log.log(java.util.logging.Level.FINE, "I couldn't find a X509Certificate using the system-wide key resolvers"); @@ -885,53 +839,44 @@ public class KeyInfo extends SignatureElementProxy { */ X509Certificate getX509CertificateFromStaticResolvers() throws KeyResolverException { - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Start getX509CertificateFromStaticResolvers() with " + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Start getX509CertificateFromStaticResolvers() with " + KeyResolver.length() + " resolvers"); + String uri=this.getBaseURI(); + int length= KeyResolver.length(); + int storageLength=this._storageResolvers.size(); + Iterator it = KeyResolver.iterator(); + for (int i = 0; i com.sun.org.apache.xml.internal.security.keys package. * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class KeyUtils { diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/KeyInfoContent.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/KeyInfoContent.java index ba9769043..4d5a7a6b9 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/KeyInfoContent.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/KeyInfoContent.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -28,7 +27,7 @@ package com.sun.org.apache.xml.internal.security.keys.content; /** * Empty interface just to identify Elements that can be cildren of ds:KeyInfo. * - * @author $Author: blautenb $ + * @author $Author: mullan $ */ public interface KeyInfoContent { } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/KeyName.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/KeyName.java index dfff3c322..6794ea675 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/KeyName.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/KeyName.java @@ -20,25 +20,18 @@ */ package com.sun.org.apache.xml.internal.security.keys.content; - - import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; import com.sun.org.apache.xml.internal.security.utils.Constants; import com.sun.org.apache.xml.internal.security.utils.SignatureElementProxy; import org.w3c.dom.Document; import org.w3c.dom.Element; - /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class KeyName extends SignatureElementProxy implements KeyInfoContent { - /** {@link java.util.logging} logging facility */ - static java.util.logging.Logger log = - java.util.logging.Logger.getLogger(KeyName.class.getName()); - /** * Constructor KeyName * diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/KeyValue.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/KeyValue.java index ab8b959f7..0d3ee810d 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/KeyValue.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/KeyValue.java @@ -20,11 +20,8 @@ */ package com.sun.org.apache.xml.internal.security.keys.content; - - import java.security.PublicKey; - import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; import com.sun.org.apache.xml.internal.security.keys.content.keyvalues.DSAKeyValue; import com.sun.org.apache.xml.internal.security.keys.content.keyvalues.RSAKeyValue; @@ -34,140 +31,131 @@ import com.sun.org.apache.xml.internal.security.utils.XMLUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; - /** * The KeyValue element contains a single public key that may be useful in * validating the signature. Structured formats for defining DSA (REQUIRED) * and RSA (RECOMMENDED) public keys are defined in Signature Algorithms * (section 6.4). The KeyValue element may include externally defined public - * keys values represented as PCDATA or element types from an external namespace. + * keys values represented as PCDATA or element types from an external + * namespace. * - * @author $Author: vishal $ + * @author $Author: mullan $ */ public class KeyValue extends SignatureElementProxy implements KeyInfoContent { - /** {@link java.util.logging} logging facility */ - static java.util.logging.Logger log = - java.util.logging.Logger.getLogger(KeyValue.class.getName()); - - /** - * Constructor KeyValue - * - * @param doc - * @param dsaKeyValue - */ - public KeyValue(Document doc, DSAKeyValue dsaKeyValue) { - - super(doc); - - XMLUtils.addReturnToElement(this._constructionElement); - this._constructionElement.appendChild(dsaKeyValue.getElement()); - XMLUtils.addReturnToElement(this._constructionElement); - } - - /** - * Constructor KeyValue - * - * @param doc - * @param rsaKeyValue - */ - public KeyValue(Document doc, RSAKeyValue rsaKeyValue) { - - super(doc); - - XMLUtils.addReturnToElement(this._constructionElement); - this._constructionElement.appendChild(rsaKeyValue.getElement()); - XMLUtils.addReturnToElement(this._constructionElement); - } - - /** - * Constructor KeyValue - * - * @param doc - * @param unknownKeyValue - */ - public KeyValue(Document doc, Element unknownKeyValue) { - - super(doc); - - XMLUtils.addReturnToElement(this._constructionElement); - this._constructionElement.appendChild(unknownKeyValue); - XMLUtils.addReturnToElement(this._constructionElement); - } - - /** - * Constructor KeyValue - * - * @param doc - * @param pk - */ - public KeyValue(Document doc, PublicKey pk) { - - super(doc); - - XMLUtils.addReturnToElement(this._constructionElement); - - if (pk instanceof java.security.interfaces.DSAPublicKey) { - DSAKeyValue dsa = new DSAKeyValue(this._doc, pk); - - this._constructionElement.appendChild(dsa.getElement()); - XMLUtils.addReturnToElement(this._constructionElement); - } else if (pk instanceof java.security.interfaces.RSAPublicKey) { - RSAKeyValue rsa = new RSAKeyValue(this._doc, pk); - - this._constructionElement.appendChild(rsa.getElement()); - XMLUtils.addReturnToElement(this._constructionElement); - } - } - - /** - * Constructor KeyValue - * - * @param element - * @param BaseURI - * @throws XMLSecurityException - */ - public KeyValue(Element element, String BaseURI) + /** + * Constructor KeyValue + * + * @param doc + * @param dsaKeyValue + */ + public KeyValue(Document doc, DSAKeyValue dsaKeyValue) { + + super(doc); + + XMLUtils.addReturnToElement(this._constructionElement); + this._constructionElement.appendChild(dsaKeyValue.getElement()); + XMLUtils.addReturnToElement(this._constructionElement); + } + + /** + * Constructor KeyValue + * + * @param doc + * @param rsaKeyValue + */ + public KeyValue(Document doc, RSAKeyValue rsaKeyValue) { + + super(doc); + + XMLUtils.addReturnToElement(this._constructionElement); + this._constructionElement.appendChild(rsaKeyValue.getElement()); + XMLUtils.addReturnToElement(this._constructionElement); + } + + /** + * Constructor KeyValue + * + * @param doc + * @param unknownKeyValue + */ + public KeyValue(Document doc, Element unknownKeyValue) { + + super(doc); + + XMLUtils.addReturnToElement(this._constructionElement); + this._constructionElement.appendChild(unknownKeyValue); + XMLUtils.addReturnToElement(this._constructionElement); + } + + /** + * Constructor KeyValue + * + * @param doc + * @param pk + */ + public KeyValue(Document doc, PublicKey pk) { + + super(doc); + + XMLUtils.addReturnToElement(this._constructionElement); + + if (pk instanceof java.security.interfaces.DSAPublicKey) { + DSAKeyValue dsa = new DSAKeyValue(this._doc, pk); + + this._constructionElement.appendChild(dsa.getElement()); + XMLUtils.addReturnToElement(this._constructionElement); + } else if (pk instanceof java.security.interfaces.RSAPublicKey) { + RSAKeyValue rsa = new RSAKeyValue(this._doc, pk); + + this._constructionElement.appendChild(rsa.getElement()); + XMLUtils.addReturnToElement(this._constructionElement); + } + } + + /** + * Constructor KeyValue + * + * @param element + * @param BaseURI + * @throws XMLSecurityException + */ + public KeyValue(Element element, String BaseURI) throws XMLSecurityException { - super(element, BaseURI); - } - - /** - * Method getPublicKey - * - * @return the public key - * @throws XMLSecurityException - */ - public PublicKey getPublicKey() throws XMLSecurityException { - - - Element rsa = XMLUtils.selectDsNode(this._constructionElement.getFirstChild(), - Constants._TAG_RSAKEYVALUE,0); - - if (rsa != null) { - RSAKeyValue kv = new RSAKeyValue(rsa, - this._baseURI); - + super(element, BaseURI); + } + + /** + * Method getPublicKey + * + * @return the public key + * @throws XMLSecurityException + */ + public PublicKey getPublicKey() throws XMLSecurityException { + + Element rsa = XMLUtils.selectDsNode + (this._constructionElement.getFirstChild(), + Constants._TAG_RSAKEYVALUE,0); + + if (rsa != null) { + RSAKeyValue kv = new RSAKeyValue(rsa, this._baseURI); return kv.getPublicKey(); - } + } - Element dsa = XMLUtils.selectDsNode(this._constructionElement, - Constants._TAG_DSAKEYVALUE,0); - - - if (dsa != null) { - DSAKeyValue kv = new DSAKeyValue(dsa, - this._baseURI); + Element dsa = XMLUtils.selectDsNode + (this._constructionElement.getFirstChild(), + Constants._TAG_DSAKEYVALUE,0); + if (dsa != null) { + DSAKeyValue kv = new DSAKeyValue(dsa, this._baseURI); return kv.getPublicKey(); - } - + } - return null; - } + return null; + } - /** @inheritDoc */ - public String getBaseLocalName() { - return Constants._TAG_KEYVALUE; - } + /** @inheritDoc */ + public String getBaseLocalName() { + return Constants._TAG_KEYVALUE; + } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/MgmtData.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/MgmtData.java index 52662d912..185e35571 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/MgmtData.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/MgmtData.java @@ -20,25 +20,18 @@ */ package com.sun.org.apache.xml.internal.security.keys.content; - - import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; import com.sun.org.apache.xml.internal.security.utils.Constants; import com.sun.org.apache.xml.internal.security.utils.SignatureElementProxy; import org.w3c.dom.Document; import org.w3c.dom.Element; - /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class MgmtData extends SignatureElementProxy implements KeyInfoContent { - /** {@link java.util.logging} logging facility */ - static java.util.logging.Logger log = - java.util.logging.Logger.getLogger(MgmtData.class.getName()); - /** * Constructor MgmtData * diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/PGPData.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/PGPData.java index 7829c3652..010c907a8 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/PGPData.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/PGPData.java @@ -20,25 +20,18 @@ */ package com.sun.org.apache.xml.internal.security.keys.content; - - import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; import com.sun.org.apache.xml.internal.security.utils.Constants; import com.sun.org.apache.xml.internal.security.utils.SignatureElementProxy; import org.w3c.dom.Element; - /** * - * @author $Author: raul $ + * @author $Author: mullan $ * $todo$ Implement */ public class PGPData extends SignatureElementProxy implements KeyInfoContent { - /** {@link java.util.logging} logging facility */ - static java.util.logging.Logger log = - java.util.logging.Logger.getLogger(PGPData.class.getName()); - /** * Constructor PGPData * diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/RetrievalMethod.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/RetrievalMethod.java index feb8b26d3..3c4956b77 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/RetrievalMethod.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/RetrievalMethod.java @@ -20,9 +20,6 @@ */ package com.sun.org.apache.xml.internal.security.keys.content; - - - import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; import com.sun.org.apache.xml.internal.security.signature.XMLSignatureException; import com.sun.org.apache.xml.internal.security.transforms.Transforms; @@ -33,17 +30,13 @@ import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; - /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class RetrievalMethod extends SignatureElementProxy implements KeyInfoContent { - /** {@link java.util.logging} logging facility */ - static java.util.logging.Logger log = - java.util.logging.Logger.getLogger(RetrievalMethod.class.getName()); //J- /** DSA retrieval */ public static final String TYPE_DSA = Constants.SignatureSpecNS + "DSAKeyValue"; @@ -133,7 +126,7 @@ public class RetrievalMethod extends SignatureElementProxy try { Element transformsElem = - XMLUtils.selectDsNode(this._constructionElement, + XMLUtils.selectDsNode(this._constructionElement.getFirstChild(), Constants ._TAG_TRANSFORMS, 0); diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/SPKIData.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/SPKIData.java index 64bb23c62..95cef8d54 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/SPKIData.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/SPKIData.java @@ -20,25 +20,18 @@ */ package com.sun.org.apache.xml.internal.security.keys.content; - - import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; import com.sun.org.apache.xml.internal.security.utils.Constants; import com.sun.org.apache.xml.internal.security.utils.SignatureElementProxy; import org.w3c.dom.Element; - /** * - * @author $Author: raul $ + * @author $Author: mullan $ * $todo$ implement */ public class SPKIData extends SignatureElementProxy implements KeyInfoContent { - /** {@link java.util.logging} logging facility */ - static java.util.logging.Logger log = - java.util.logging.Logger.getLogger(SPKIData.class.getName()); - /** * Constructor SPKIData * diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/X509Data.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/X509Data.java index d60f3ad65..199b1dcb0 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/X509Data.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/X509Data.java @@ -41,7 +41,7 @@ import org.w3c.dom.Node; /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class X509Data extends SignatureElementProxy implements KeyInfoContent { @@ -72,60 +72,17 @@ public class X509Data extends SignatureElementProxy implements KeyInfoContent { throws XMLSecurityException { super(element, BaseURI); - - boolean noElements=true; Node sibling=this._constructionElement.getFirstChild(); while (sibling!=null) { if (sibling.getNodeType()!=Node.ELEMENT_NODE) { sibling=sibling.getNextSibling(); continue; } - noElements=false; - Element currentElem = (Element) sibling; - sibling=sibling.getNextSibling(); - String localname = currentElem.getLocalName(); - - if (currentElem.getNamespaceURI().equals(Constants.SignatureSpecNS)) { - if (localname.equals(Constants._TAG_X509ISSUERSERIAL)) { - XMLX509IssuerSerial is = new XMLX509IssuerSerial(currentElem, - BaseURI); - - this.add(is); - } else if (localname.equals(Constants._TAG_X509SKI)) { - XMLX509SKI ski = new XMLX509SKI(currentElem, BaseURI); - - this.add(ski); - } else if (localname.equals(Constants._TAG_X509SUBJECTNAME)) { - XMLX509SubjectName sn = new XMLX509SubjectName(currentElem, - BaseURI); - - this.add(sn); - } else if (localname.equals(Constants._TAG_X509CERTIFICATE)) { - XMLX509Certificate cert = new XMLX509Certificate(currentElem, - BaseURI); - - this.add(cert); - } else if (localname.equals(Constants._TAG_X509CRL)) { - XMLX509CRL crl = new XMLX509CRL(currentElem, BaseURI); - - this.add(crl); - } else { - log.log(java.util.logging.Level.WARNING, "Found a " + currentElem.getTagName() + " element in " - + Constants._TAG_X509DATA); - this.addUnknownElement(currentElem); - } - } else { - log.log(java.util.logging.Level.WARNING, "Found a " + currentElem.getTagName() + " element in " - + Constants._TAG_X509DATA); - this.addUnknownElement(currentElem); - } + return; } - if (noElements) { - Object exArgs[] = { "Elements", Constants._TAG_X509DATA }; - - throw new XMLSecurityException("xml.WrongContent", exArgs); - } - + /* No Elements found */ + Object exArgs[] = { "Elements", Constants._TAG_X509DATA }; + throw new XMLSecurityException("xml.WrongContent", exArgs); } /** @@ -169,11 +126,9 @@ public class X509Data extends SignatureElementProxy implements KeyInfoContent { */ public void add(XMLX509IssuerSerial xmlX509IssuerSerial) { - if (this._state == MODE_SIGN) { this._constructionElement .appendChild(xmlX509IssuerSerial.getElement()); XMLUtils.addReturnToElement(this._constructionElement); - } } /** @@ -202,11 +157,8 @@ public class X509Data extends SignatureElementProxy implements KeyInfoContent { * @param xmlX509SKI */ public void add(XMLX509SKI xmlX509SKI) { - - if (this._state == MODE_SIGN) { this._constructionElement.appendChild(xmlX509SKI.getElement()); XMLUtils.addReturnToElement(this._constructionElement); - } } /** @@ -233,11 +185,8 @@ public class X509Data extends SignatureElementProxy implements KeyInfoContent { * @param xmlX509SubjectName */ public void add(XMLX509SubjectName xmlX509SubjectName) { - - if (this._state == MODE_SIGN) { this._constructionElement.appendChild(xmlX509SubjectName.getElement()); XMLUtils.addReturnToElement(this._constructionElement); - } } /** @@ -266,11 +215,8 @@ public class X509Data extends SignatureElementProxy implements KeyInfoContent { * @param xmlX509Certificate */ public void add(XMLX509Certificate xmlX509Certificate) { - - if (this._state == MODE_SIGN) { this._constructionElement.appendChild(xmlX509Certificate.getElement()); XMLUtils.addReturnToElement(this._constructionElement); - } } /** @@ -288,11 +234,8 @@ public class X509Data extends SignatureElementProxy implements KeyInfoContent { * @param xmlX509CRL */ public void add(XMLX509CRL xmlX509CRL) { - - if (this._state == MODE_SIGN) { this._constructionElement.appendChild(xmlX509CRL.getElement()); XMLUtils.addReturnToElement(this._constructionElement); - } } /** @@ -301,11 +244,8 @@ public class X509Data extends SignatureElementProxy implements KeyInfoContent { * @param element */ public void addUnknownElement(Element element) { - - if (this._state == MODE_SIGN) { this._constructionElement.appendChild(element); XMLUtils.addReturnToElement(this._constructionElement); - } } /** @@ -479,7 +419,7 @@ public class X509Data extends SignatureElementProxy implements KeyInfoContent { * TODO implement **/ public Element itemUnknownElement(int i) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "itemUnknownElement not implemented:"+i); + log.log(java.util.logging.Level.FINE, "itemUnknownElement not implemented:"+i); return null; } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/keyvalues/DSAKeyValue.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/keyvalues/DSAKeyValue.java index 19fb7758f..ef735c3da 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/keyvalues/DSAKeyValue.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/keyvalues/DSAKeyValue.java @@ -20,8 +20,6 @@ */ package com.sun.org.apache.xml.internal.security.keys.content.keyvalues; - - import java.math.BigInteger; import java.security.Key; import java.security.KeyFactory; @@ -39,18 +37,13 @@ import com.sun.org.apache.xml.internal.security.utils.XMLUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; - /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class DSAKeyValue extends SignatureElementProxy implements KeyValueContent { - /** {@link java.util.logging} logging facility */ - static java.util.logging.Logger log = - java.util.logging.Logger.getLogger(DSAKeyValue.class.getName()); - /** * Constructor DSAKeyValue * diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/keyvalues/KeyValueContent.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/keyvalues/KeyValueContent.java index 608758e16..31e761443 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/keyvalues/KeyValueContent.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/keyvalues/KeyValueContent.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -32,7 +31,7 @@ import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; * * * - * @author $Author: raul $ + * @author $Author: mullan $ * */ public interface KeyValueContent { diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/keyvalues/RSAKeyValue.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/keyvalues/RSAKeyValue.java index 6fc33aded..71b23cda5 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/keyvalues/RSAKeyValue.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/keyvalues/RSAKeyValue.java @@ -20,8 +20,6 @@ */ package com.sun.org.apache.xml.internal.security.keys.content.keyvalues; - - import java.math.BigInteger; import java.security.Key; import java.security.KeyFactory; @@ -39,19 +37,13 @@ import com.sun.org.apache.xml.internal.security.utils.XMLUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; - /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class RSAKeyValue extends SignatureElementProxy implements KeyValueContent { - /** {@link java.util.logging} logging facility */ - static java.util.logging.Logger log = - java.util.logging.Logger.getLogger( - RSAKeyValue.class.getName()); - /** * Constructor RSAKeyValue * diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509CRL.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509CRL.java index 7c9f5be1f..b68c444dc 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509CRL.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509CRL.java @@ -20,30 +20,20 @@ */ package com.sun.org.apache.xml.internal.security.keys.content.x509; - - import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; import com.sun.org.apache.xml.internal.security.utils.Constants; import com.sun.org.apache.xml.internal.security.utils.SignatureElementProxy; import org.w3c.dom.Document; import org.w3c.dom.Element; - /** * - * - * - * - * @author $Author: raul $ + * @author $Author: mullan $ * */ public class XMLX509CRL extends SignatureElementProxy implements XMLX509DataContent { - /** {@link java.util.logging} logging facility */ - static java.util.logging.Logger log = - java.util.logging.Logger.getLogger(XMLX509CRL.class.getName()); - /** * Constructor XMLX509CRL * diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509Certificate.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509Certificate.java index 51f81e529..630d9ccc2 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509Certificate.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509Certificate.java @@ -20,8 +20,6 @@ */ package com.sun.org.apache.xml.internal.security.keys.content.x509; - - import java.io.ByteArrayInputStream; import java.security.PublicKey; import java.security.cert.CertificateException; @@ -34,18 +32,13 @@ import com.sun.org.apache.xml.internal.security.utils.SignatureElementProxy; import org.w3c.dom.Document; import org.w3c.dom.Element; - /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class XMLX509Certificate extends SignatureElementProxy implements XMLX509DataContent { - /** {@link java.util.logging} logging facility */ - static java.util.logging.Logger log = - java.util.logging.Logger.getLogger(XMLX509Certificate.class.getName()); - /** Field JCA_CERT_ID */ public static final String JCA_CERT_ID = "X.509"; @@ -146,23 +139,25 @@ public class XMLX509Certificate extends SignatureElementProxy return null; } - /** @inheritDoc */ - public boolean equals(Object obj) { + /** @inheritDoc */ + public boolean equals(Object obj) { - try { - if (!obj.getClass().getName().equals(this.getClass().getName())) { + if (obj == null) { return false; - } - - XMLX509Certificate other = (XMLX509Certificate) obj; - - /** $todo$ or should be create X509Certificates and use the equals() from the Certs */ - return java.security.MessageDigest.isEqual(other.getCertificateBytes(), - this.getCertificateBytes()); - } catch (XMLSecurityException ex) { - return false; - } - } + } + if (!this.getClass().getName().equals(obj.getClass().getName())) { + return false; + } + XMLX509Certificate other = (XMLX509Certificate) obj; + try { + + /** $todo$ or should be create X509Certificates and use the equals() from the Certs */ + return java.security.MessageDigest.isEqual + (other.getCertificateBytes(), this.getCertificateBytes()); + } catch (XMLSecurityException ex) { + return false; + } + } /** @inheritDoc */ public String getBaseLocalName() { diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509DataContent.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509DataContent.java index adbf4978f..02bf9f82d 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509DataContent.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509DataContent.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -28,7 +27,7 @@ package com.sun.org.apache.xml.internal.security.keys.content.x509; /** * Just used for tagging contents that are allowed inside a ds:X509Data Element. * - * @author $Author: blautenb $ + * @author $Author: mullan $ */ public interface XMLX509DataContent { } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509IssuerSerial.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509IssuerSerial.java index d0701592d..1d16b2b62 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509IssuerSerial.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509IssuerSerial.java @@ -20,8 +20,6 @@ */ package com.sun.org.apache.xml.internal.security.keys.content.x509; - - import java.math.BigInteger; import java.security.cert.X509Certificate; @@ -33,148 +31,139 @@ import com.sun.org.apache.xml.internal.security.utils.XMLUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; - /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class XMLX509IssuerSerial extends SignatureElementProxy implements XMLX509DataContent { - /** {@link java.util.logging} logging facility */ + /** {@link java.util.logging} logging facility */ static java.util.logging.Logger log = java.util.logging.Logger.getLogger( XMLX509IssuerSerial.class.getName()); - /** - * Constructor XMLX509IssuerSerial - * - * @param element - * @param BaseURI - * @throws XMLSecurityException - */ - public XMLX509IssuerSerial(Element element, String BaseURI) + /** + * Constructor XMLX509IssuerSerial + * + * @param element + * @param baseURI + * @throws XMLSecurityException + */ + public XMLX509IssuerSerial(Element element, String baseURI) throws XMLSecurityException { - super(element, BaseURI); - } - - /** - * Constructor XMLX509IssuerSerial - * - * @param doc - * @param X509IssuerName - * @param X509SerialNumber - */ - public XMLX509IssuerSerial(Document doc, String X509IssuerName, - BigInteger X509SerialNumber) { - - super(doc); - - XMLUtils.addReturnToElement(this._constructionElement); - this.addTextElement(X509IssuerName, Constants._TAG_X509ISSUERNAME); - XMLUtils.addReturnToElement(this._constructionElement); - this.addTextElement(X509SerialNumber.toString(), Constants._TAG_X509SERIALNUMBER); - } - - /** - * Constructor XMLX509IssuerSerial - * - * @param doc - * @param X509IssuerName - * @param X509SerialNumber - */ - public XMLX509IssuerSerial(Document doc, String X509IssuerName, - String X509SerialNumber) { - this(doc, X509IssuerName, new BigInteger(X509SerialNumber)); - } - - /** - * Constructor XMLX509IssuerSerial - * - * @param doc - * @param X509IssuerName - * @param X509SerialNumber - */ - public XMLX509IssuerSerial(Document doc, String X509IssuerName, - int X509SerialNumber) { - this(doc, X509IssuerName, - new BigInteger(Integer.toString(X509SerialNumber))); - } - - /** - * Constructor XMLX509IssuerSerial - * - * @param doc - * @param x509certificate - */ - public XMLX509IssuerSerial(Document doc, X509Certificate x509certificate) { - - this(doc, - RFC2253Parser.normalize(x509certificate.getIssuerDN().getName()), - x509certificate.getSerialNumber()); - } - - /** - * Method getSerialNumber - * - * - * @return the serial number - */ - public BigInteger getSerialNumber() { - - String text = - this.getTextFromChildElement(Constants._TAG_X509SERIALNUMBER, - Constants.SignatureSpecNS); - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "In dem X509SerialNumber wurde gefunden: " + text); - - return new BigInteger(text); - } - - /** - * Method getSerialNumberInteger - * - * - * @return the serial number as plain int - */ - public int getSerialNumberInteger() { - return this.getSerialNumber().intValue(); - } - - /** - * Method getIssuerName - * - * - * @return the issuer name - */ - public String getIssuerName() { - - return RFC2253Parser - .normalize(this - .getTextFromChildElement(Constants._TAG_X509ISSUERNAME, - Constants.SignatureSpecNS)); - } - - /** @inheritDoc */ - public boolean equals(Object obj) { - - if (!obj.getClass().getName().equals(this.getClass().getName())) { - return false; - } - - XMLX509IssuerSerial other = (XMLX509IssuerSerial) obj; - - - if (other.getSerialNumber().equals(this.getSerialNumber()) - && other.getIssuerName().equals(this.getIssuerName())) { - return true; - } - - return false; - } - - /** @inheritDoc */ - public String getBaseLocalName() { - return Constants._TAG_X509ISSUERSERIAL; - } + super(element, baseURI); + } + + /** + * Constructor XMLX509IssuerSerial + * + * @param doc + * @param x509IssuerName + * @param x509SerialNumber + */ + public XMLX509IssuerSerial(Document doc, String x509IssuerName, + BigInteger x509SerialNumber) { + + super(doc); + XMLUtils.addReturnToElement(this._constructionElement); + addTextElement(x509IssuerName, Constants._TAG_X509ISSUERNAME); + addTextElement(x509SerialNumber.toString(), Constants._TAG_X509SERIALNUMBER); + } + + /** + * Constructor XMLX509IssuerSerial + * + * @param doc + * @param x509IssuerName + * @param x509SerialNumber + */ + public XMLX509IssuerSerial(Document doc, String x509IssuerName, + String x509SerialNumber) { + this(doc, x509IssuerName, new BigInteger(x509SerialNumber)); + } + + /** + * Constructor XMLX509IssuerSerial + * + * @param doc + * @param x509IssuerName + * @param x509SerialNumber + */ + public XMLX509IssuerSerial(Document doc, String x509IssuerName, + int x509SerialNumber) { + this(doc, x509IssuerName, + new BigInteger(Integer.toString(x509SerialNumber))); + } + + /** + * Constructor XMLX509IssuerSerial + * + * @param doc + * @param x509certificate + */ + public XMLX509IssuerSerial(Document doc, X509Certificate x509certificate) { + + this(doc, + RFC2253Parser.normalize(x509certificate.getIssuerDN().getName()), + x509certificate.getSerialNumber()); + } + + /** + * Method getSerialNumber + * + * @return the serial number + */ + public BigInteger getSerialNumber() { + + String text = this.getTextFromChildElement + (Constants._TAG_X509SERIALNUMBER, Constants.SignatureSpecNS); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "X509SerialNumber text: " + text); + + return new BigInteger(text); + } + + /** + * Method getSerialNumberInteger + * + * @return the serial number as plain int + */ + public int getSerialNumberInteger() { + return this.getSerialNumber().intValue(); + } + + /** + * Method getIssuerName + * + * @return the issuer name + */ + public String getIssuerName() { + + return RFC2253Parser + .normalize(this + .getTextFromChildElement(Constants._TAG_X509ISSUERNAME, + Constants.SignatureSpecNS)); + } + + /** @inheritDoc */ + public boolean equals(Object obj) { + + if (obj == null) { + return false; + } + if (!this.getClass().getName().equals(obj.getClass().getName())) { + return false; + } + + XMLX509IssuerSerial other = (XMLX509IssuerSerial) obj; + + return this.getSerialNumber().equals(other.getSerialNumber()) + && this.getIssuerName().equals(other.getIssuerName()); + } + + /** @inheritDoc */ + public String getBaseLocalName() { + return Constants._TAG_X509ISSUERSERIAL; + } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509SKI.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509SKI.java index fcbb19e24..fbbb17e6a 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509SKI.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509SKI.java @@ -20,8 +20,6 @@ */ package com.sun.org.apache.xml.internal.security.keys.content.x509; - - import java.io.IOException; import java.io.ByteArrayInputStream; import java.io.InputStream; @@ -36,192 +34,143 @@ import com.sun.org.apache.xml.internal.security.utils.SignatureElementProxy; import org.w3c.dom.Document; import org.w3c.dom.Element; -import sun.security.util.DerValue; - - /** * Handles SubjectKeyIdentifier (SKI) for X.509v3. * - * @author $Author: raul $ - * @see Interface X509Extension + * @author $Author: mullan $ + * @see Interface X509Extension */ public class XMLX509SKI extends SignatureElementProxy implements XMLX509DataContent { - /** {@link java.util.logging} logging facility */ + /** {@link java.util.logging} logging facility */ static java.util.logging.Logger log = java.util.logging.Logger.getLogger(XMLX509SKI.class.getName()); - /** - * SubjectKeyIdentifier (id-ce-subjectKeyIdentifier) (2.5.29.14): - * This extension identifies the public key being certified. It enables - * distinct keys used by the same subject to be differentiated - * (e.g., as key updating occurs). - *
      - * A key identifer shall be unique with respect to all key identifiers - * for the subject with which it is used. This extension is always non-critical. - */ - public static final String SKI_OID = "2.5.29.14"; - - /** - * Constructor X509SKI - * - * @param doc - * @param skiBytes - */ - public XMLX509SKI(Document doc, byte[] skiBytes) { - - super(doc); - - this.addBase64Text(skiBytes); - } - - /** - * Constructor XMLX509SKI - * - * @param doc - * @param x509certificate - * @throws XMLSecurityException - */ - public XMLX509SKI(Document doc, X509Certificate x509certificate) - throws XMLSecurityException { - - super(doc); - - this.addBase64Text(XMLX509SKI.getSKIBytesFromCert(x509certificate)); - } - - /** - * Constructor XMLX509SKI - * - * @param element - * @param BaseURI - * @throws XMLSecurityException - */ - public XMLX509SKI(Element element, String BaseURI) + /** + * SubjectKeyIdentifier (id-ce-subjectKeyIdentifier) (2.5.29.14): + * This extension identifies the public key being certified. It enables + * distinct keys used by the same subject to be differentiated + * (e.g., as key updating occurs). + *
      + * A key identifer shall be unique with respect to all key identifiers + * for the subject with which it is used. This extension is always non-critical. + */ + public static final String SKI_OID = "2.5.29.14"; + + /** + * Constructor X509SKI + * + * @param doc + * @param skiBytes + */ + public XMLX509SKI(Document doc, byte[] skiBytes) { + super(doc); + this.addBase64Text(skiBytes); + } + + /** + * Constructor XMLX509SKI + * + * @param doc + * @param x509certificate + * @throws XMLSecurityException + */ + public XMLX509SKI(Document doc, X509Certificate x509certificate) throws XMLSecurityException { - super(element, BaseURI); - } - - /** - * Method getSKIBytes - * - * @return the skibytes - * @throws XMLSecurityException - */ - public byte[] getSKIBytes() throws XMLSecurityException { - return this.getBytesFromTextChild(); - } - - /** - * Method getSKIBytesFromCert - * - * @param cert - * @return sky bytes from the given certificate - * - * @throws XMLSecurityException - * @see java.security.cert.X509Extension#getExtensionValue(java.lang.String) - */ - public static byte[] getSKIBytesFromCert(X509Certificate cert) + super(doc); + this.addBase64Text(XMLX509SKI.getSKIBytesFromCert(x509certificate)); + } + + /** + * Constructor XMLX509SKI + * + * @param element + * @param BaseURI + * @throws XMLSecurityException + */ + public XMLX509SKI(Element element, String BaseURI) throws XMLSecurityException { - - try { - - /* - * Gets the DER-encoded OCTET string for the extension value (extnValue) - * identified by the passed-in oid String. The oid string is - * represented by a set of positive whole numbers separated by periods. - */ - byte[] derEncodedValue = cert.getExtensionValue(XMLX509SKI.SKI_OID); - - if (cert.getVersion() < 3) { + super(element, BaseURI); + } + + /** + * Method getSKIBytes + * + * @return the skibytes + * @throws XMLSecurityException + */ + public byte[] getSKIBytes() throws XMLSecurityException { + return this.getBytesFromTextChild(); + } + + /** + * Method getSKIBytesFromCert + * + * @param cert + * @return ski bytes from the given certificate + * + * @throws XMLSecurityException + * @see java.security.cert.X509Extension#getExtensionValue(java.lang.String) + */ + public static byte[] getSKIBytesFromCert(X509Certificate cert) + throws XMLSecurityException { + + if (cert.getVersion() < 3) { Object exArgs[] = { new Integer(cert.getVersion()) }; - throw new XMLSecurityException("certificate.noSki.lowVersion", exArgs); - } - - byte[] extensionValue = null; - - /** - * Use sun.security.util.DerValue if it is present. - */ - try { - DerValue dervalue = new DerValue(derEncodedValue); - if (dervalue == null) { - throw new XMLSecurityException("certificate.noSki.null"); - } - if (dervalue.tag != DerValue.tag_OctetString) { - throw new XMLSecurityException("certificate.noSki.notOctetString"); - } - extensionValue = dervalue.getOctetString(); - } catch (NoClassDefFoundError e) { - } - - /** - * Fall back to org.bouncycastle.asn1.DERInputStream - */ - if (extensionValue == null) { - try { - Class clazz = Class.forName("org.bouncycastle.asn1.DERInputStream"); - if (clazz != null) { - Constructor constructor = clazz.getConstructor(new Class[]{InputStream.class}); - InputStream is = (InputStream) constructor.newInstance(new Object[]{new ByteArrayInputStream(derEncodedValue)}); - Method method = clazz.getMethod("readObject", new Class[]{}); - Object obj = method.invoke(is, new Object[]{}); - if (obj == null) { - throw new XMLSecurityException("certificate.noSki.null"); - } - Class clazz2 = Class.forName("org.bouncycastle.asn1.ASN1OctetString"); - if (!clazz2.isInstance(obj)) { - throw new XMLSecurityException("certificate.noSki.notOctetString"); - } - Method method2 = clazz2.getMethod("getOctets", new Class[]{}); - extensionValue = (byte[]) method2.invoke(obj, new Object[]{}); - } - } catch (Throwable t) { - } - } - - /** - * Strip away first two bytes from the DerValue (tag and length) - */ - byte abyte0[] = new byte[extensionValue.length - 2]; - - System.arraycopy(extensionValue, 2, abyte0, 0, abyte0.length); - - /* - byte abyte0[] = new byte[derEncodedValue.length - 4]; - System.arraycopy(derEncodedValue, 4, abyte0, 0, abyte0.length); + } + + /* + * Gets the DER-encoded OCTET string for the extension value + * (extnValue) identified by the passed-in oid String. The oid + * string is represented by a set of positive whole numbers + * separated by periods. + */ + byte[] extensionValue = cert.getExtensionValue(XMLX509SKI.SKI_OID); + if (extensionValue == null) { + throw new XMLSecurityException("certificate.noSki.null"); + } + + /** + * Strip away first four bytes from the extensionValue + * The first two bytes are the tag and length of the extensionValue + * OCTET STRING, and the next two bytes are the tag and length of + * the skid OCTET STRING. */ - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Base64 of SKI is " + Base64.encode(abyte0)); + byte skidValue[] = new byte[extensionValue.length - 4]; + + System.arraycopy(extensionValue, 4, skidValue, 0, skidValue.length); - return abyte0; - } catch (IOException ex) { - throw new XMLSecurityException("generic.EmptyMessage", ex); - } - } + if (log.isLoggable(java.util.logging.Level.FINE)) { + log.log(java.util.logging.Level.FINE, "Base64 of SKI is " + Base64.encode(skidValue)); + } - /** @inheritDoc */ - public boolean equals(Object obj) { + return skidValue; + } - if (!obj.getClass().getName().equals(this.getClass().getName())) { - return false; - } + /** @inheritDoc */ + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (!this.getClass().getName().equals(obj.getClass().getName())) { + return false; + } - XMLX509SKI other = (XMLX509SKI) obj; + XMLX509SKI other = (XMLX509SKI) obj; - try { - return java.security.MessageDigest.isEqual(other.getSKIBytes(), + try { + return java.security.MessageDigest.isEqual(other.getSKIBytes(), this.getSKIBytes()); - } catch (XMLSecurityException ex) { - return false; - } - } - - /** @inheritDoc */ - public String getBaseLocalName() { - return Constants._TAG_X509SKI; - } + } catch (XMLSecurityException ex) { + return false; + } + } + + /** @inheritDoc */ + public String getBaseLocalName() { + return Constants._TAG_X509SKI; + } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509SubjectName.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509SubjectName.java index 548ec7ba5..8d51da2e2 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509SubjectName.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/content/x509/XMLX509SubjectName.java @@ -20,8 +20,6 @@ */ package com.sun.org.apache.xml.internal.security.keys.content.x509; - - import java.security.cert.X509Certificate; import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; @@ -33,15 +31,11 @@ import org.w3c.dom.Element; /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class XMLX509SubjectName extends SignatureElementProxy implements XMLX509DataContent { - /** {@link java.util.logging} logging facility */ - static java.util.logging.Logger log = - java.util.logging.Logger.getLogger(XMLX509SubjectName.class.getName()); - /** * Constructor X509SubjectName * @@ -88,23 +82,21 @@ public class XMLX509SubjectName extends SignatureElementProxy return RFC2253Parser.normalize(this.getTextFromTextChild()); } - /** @inheritDoc */ - public boolean equals(Object obj) { - - if (!obj.getClass().getName().equals(this.getClass().getName())) { - return false; - } - - XMLX509SubjectName other = (XMLX509SubjectName) obj; - String otherSubject = other.getSubjectName(); - String thisSubject = this.getSubjectName(); + /** @inheritDoc */ + public boolean equals(Object obj) { + if (obj == null) { + return false; + } - if (otherSubject.equals(thisSubject)) { - return true; - } + if (!this.getClass().getName().equals(obj.getClass().getName())) { + return false; + } - return false; + XMLX509SubjectName other = (XMLX509SubjectName) obj; + String otherSubject = other.getSubjectName(); + String thisSubject = this.getSubjectName(); + return thisSubject.equals(otherSubject); } /** @inheritDoc */ diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/InvalidKeyResolverException.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/InvalidKeyResolverException.java index 6e3d408d4..3b3508005 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/InvalidKeyResolverException.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/InvalidKeyResolverException.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -29,7 +28,7 @@ import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; /** * * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class InvalidKeyResolverException extends XMLSecurityException { diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolver.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolver.java index 488a59731..1da9dbb36 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolver.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolver.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -26,6 +25,7 @@ package com.sun.org.apache.xml.internal.security.keys.keyresolver; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import javax.crypto.SecretKey; @@ -39,7 +39,8 @@ import org.w3c.dom.Node; * KeyResolver is factory class for subclass of KeyResolverSpi that * represent child element of KeyInfo. * - * @author $Author: raul $ + * @author $Author: mullan $ + * @version %I%, %G% */ public class KeyResolver { @@ -72,6 +73,7 @@ public class KeyResolver { InstantiationException { this._resolverSpi = (KeyResolverSpi) Class.forName(className).newInstance(); + this._resolverSpi.setGlobalResolver(true); } /** @@ -83,40 +85,82 @@ public class KeyResolver { return KeyResolver._resolverVector.size(); } + public static void hit(Iterator hintI) { + ResolverIterator hint = (ResolverIterator) hintI; + int i = hint.i; + if (i!=1 && hint.res ==_resolverVector) { + List resolverVector=(List)((ArrayList)_resolverVector).clone(); + Object ob=resolverVector.remove(i-1); + resolverVector.add(0,ob); + _resolverVector=resolverVector; + } else { + //System.out.println("KeyResolver hitting"); + } + } + /** - * Method item + * Method getInstance + * + * @param element + * @param BaseURI + * @param storage + * @return The certificate represented by the element. * - * @param i - * @return the number i resolver registerd * @throws KeyResolverException */ - public static KeyResolver item(int i) throws KeyResolverException { + public static final X509Certificate getX509Certificate( + Element element, String BaseURI, StorageResolver storage) + throws KeyResolverException { + + // use the old vector to not be hit by updates + List resolverVector = KeyResolver._resolverVector; + for (int i = 0; i < resolverVector.size(); i++) { + KeyResolver resolver= + (KeyResolver) resolverVector.get(i); - KeyResolver resolver = (KeyResolver) KeyResolver._resolverVector.get(i); - if (resolver==null) { - throw new KeyResolverException("utils.resolver.noClass"); + if (resolver==null) { + Object exArgs[] = { + (((element != null) + && (element.getNodeType() == Node.ELEMENT_NODE)) + ? element.getTagName() + : "null") }; + + throw new KeyResolverException("utils.resolver.noClass", exArgs); + } + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "check resolvability by class " + resolver.getClass()); + + X509Certificate cert=resolver.resolveX509Certificate(element, BaseURI, storage); + if (cert!=null) { + return cert; + } } - return resolver; - } + Object exArgs[] = { + (((element != null) && (element.getNodeType() == Node.ELEMENT_NODE)) + ? element.getTagName() + : "null") }; + throw new KeyResolverException("utils.resolver.noClass", exArgs); + } /** * Method getInstance * * @param element * @param BaseURI * @param storage - * @return the instance that happends to implement the thing. + * @return the public key contained in the element * * @throws KeyResolverException */ - public static final KeyResolver getInstance( + public static final PublicKey getPublicKey( Element element, String BaseURI, StorageResolver storage) throws KeyResolverException { - for (int i = 0; i < KeyResolver._resolverVector.size(); i++) { + List resolverVector = KeyResolver._resolverVector; + for (int i = 0; i < resolverVector.size(); i++) { KeyResolver resolver= - (KeyResolver) KeyResolver._resolverVector.get(i); + (KeyResolver) resolverVector.get(i); if (resolver==null) { Object exArgs[] = { @@ -127,11 +171,19 @@ public class KeyResolver { throw new KeyResolverException("utils.resolver.noClass", exArgs); } - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "check resolvability by class " + resolver.getClass()); - - if (resolver.canResolve(element, BaseURI, storage)) { - return resolver; + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "check resolvability by class " + resolver.getClass()); + + PublicKey cert=resolver.resolvePublicKey(element, BaseURI, storage); + if (cert!=null) { + if (i!=0 && resolverVector==_resolverVector) { + //update resolver. + resolverVector=(List)((ArrayList)_resolverVector).clone(); + Object ob=resolverVector.remove(i); + resolverVector.add(0,ob); + _resolverVector=resolverVector; + } + return cert; } } @@ -182,34 +234,6 @@ public class KeyResolver { KeyResolver._resolverVector.add(0, className); } - /* - * Method resolve - * - * @param element - * - * @throws KeyResolverException - */ - - /** - * Method resolveStatic - * - * @param element - * @param BaseURI - * @param storage - * @return resolve from the static register an element - * - * @throws KeyResolverException - */ - public static PublicKey resolveStatic( - Element element, String BaseURI, StorageResolver storage) - throws KeyResolverException { - - KeyResolver myResolver = KeyResolver.getInstance(element, BaseURI, - storage); - - return myResolver.resolvePublicKey(element, BaseURI, storage); - } - /** * Method resolve * @@ -223,7 +247,7 @@ public class KeyResolver { public PublicKey resolvePublicKey( Element element, String BaseURI, StorageResolver storage) throws KeyResolverException { - return this._resolverSpi.engineResolvePublicKey(element, BaseURI, storage); + return this._resolverSpi.engineLookupAndResolvePublicKey(element, BaseURI, storage); } /** @@ -239,7 +263,7 @@ public class KeyResolver { public X509Certificate resolveX509Certificate( Element element, String BaseURI, StorageResolver storage) throws KeyResolverException { - return this._resolverSpi.engineResolveX509Certificate(element, BaseURI, + return this._resolverSpi.engineLookupResolveX509Certificate(element, BaseURI, storage); } @@ -253,7 +277,7 @@ public class KeyResolver { public SecretKey resolveSecretKey( Element element, String BaseURI, StorageResolver storage) throws KeyResolverException { - return this._resolverSpi.engineResolveSecretKey(element, BaseURI, + return this._resolverSpi.engineLookupAndResolveSecretKey(element, BaseURI, storage); } @@ -277,14 +301,6 @@ public class KeyResolver { return this._resolverSpi.engineGetProperty(key); } - /** - * Method getPropertyKeys - * - * @return the properties key registerd in this resolver - */ - public String[] getPropertyKeys() { - return this._resolverSpi.engineGetPropertyKeys(); - } /** * Method understandsProperty @@ -296,18 +312,6 @@ public class KeyResolver { return this._resolverSpi.understandsProperty(propertyToTest); } - /** - * Method canResolve - * - * @param element - * @param BaseURI - * @param storage - * @return true if can resolve the key in the element - */ - public boolean canResolve(Element element, String BaseURI, - StorageResolver storage) { - return this._resolverSpi.engineCanResolve(element, BaseURI, storage); - } /** * Method resolverClassName @@ -317,4 +321,37 @@ public class KeyResolver { public String resolverClassName() { return this._resolverSpi.getClass().getName(); } + + static class ResolverIterator implements Iterator { + List res; + Iterator it; + int i; + public ResolverIterator(List list) { + res = list; + it = res.iterator(); + } + public boolean hasNext() { + // TODO Auto-generated method stub + return it.hasNext(); + } + + public Object next() { + i++; + KeyResolver resolver = (KeyResolver) it.next(); + if (resolver==null) { + throw new RuntimeException("utils.resolver.noClass"); + } + + return resolver._resolverSpi; + } + + public void remove() { + // TODO Auto-generated method stub + + } + + }; + public static Iterator iterator() { + return new ResolverIterator(_resolverVector); + } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolverException.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolverException.java index 4248c8dfa..f0069949b 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolverException.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolverException.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -31,7 +30,7 @@ import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; * * * - * @author $Author: raul $ + * @author $Author: mullan $ * */ public class KeyResolverException extends XMLSecurityException { diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolverSpi.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolverSpi.java index a6b99825e..dc2865bcf 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolverSpi.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolverSpi.java @@ -20,17 +20,15 @@ */ package com.sun.org.apache.xml.internal.security.keys.keyresolver; - - import java.security.PublicKey; import java.security.cert.X509Certificate; +import java.util.HashMap; import javax.crypto.SecretKey; import com.sun.org.apache.xml.internal.security.keys.storage.StorageResolver; import org.w3c.dom.Element; - /** * This class is abstract class for a child KeyInfo Elemnet. * @@ -41,14 +39,10 @@ import org.w3c.dom.Element; * JAVACLASS="MyPackage.MyKeyValueImpl"//gt; * * - * @author $Author: raul $ + * @author $Author: mullan $ + * @version $Revision: 1.5 $ */ public abstract class KeyResolverSpi { - - /** {@link java.util.logging} logging facility */ - static java.util.logging.Logger log = - java.util.logging.Logger.getLogger(KeyResolverSpi.class.getName()); - /** * This method helps the {@link com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver} to decide whether a * {@link com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverSpi} is able to perform the requested action. @@ -56,10 +50,12 @@ public abstract class KeyResolverSpi { * @param element * @param BaseURI * @param storage - * @return true if can resolve the key in the element + * @return */ - abstract public boolean engineCanResolve(Element element, String BaseURI, - StorageResolver storage); + public boolean engineCanResolve(Element element, String BaseURI, + StorageResolver storage) { + throw new UnsupportedOperationException(); + } /** * Method engineResolvePublicKey @@ -71,9 +67,60 @@ public abstract class KeyResolverSpi { * * @throws KeyResolverException */ - abstract public PublicKey engineResolvePublicKey( + public PublicKey engineResolvePublicKey( Element element, String BaseURI, StorageResolver storage) - throws KeyResolverException; + throws KeyResolverException { + throw new UnsupportedOperationException(); + }; + + /** + * Method engineResolvePublicKey + * + * @param element + * @param BaseURI + * @param storage + * @return resolved public key from the registered from the element. + * + * @throws KeyResolverException + */ + public PublicKey engineLookupAndResolvePublicKey( + Element element, String BaseURI, StorageResolver storage) + throws KeyResolverException { + KeyResolverSpi tmp = cloneIfNeeded(); + if (!tmp.engineCanResolve(element, BaseURI, storage)) + return null; + return tmp.engineResolvePublicKey(element, BaseURI, storage); + } + + private KeyResolverSpi cloneIfNeeded() throws KeyResolverException { + KeyResolverSpi tmp=this; + if (globalResolver) { + try { + tmp = (KeyResolverSpi) getClass().newInstance(); + } catch (InstantiationException e) { + throw new KeyResolverException("",e); + } catch (IllegalAccessException e) { + throw new KeyResolverException("",e); + } + } + return tmp; + } + + /** + * Method engineResolveCertificate + * + * @param element + * @param BaseURI + * @param storage + * @return resolved X509Certificate key from the registered from the elements + * + * @throws KeyResolverException + */ + public X509Certificate engineResolveX509Certificate( + Element element, String BaseURI, StorageResolver storage) + throws KeyResolverException{ + throw new UnsupportedOperationException(); + }; /** * Method engineResolveCertificate @@ -85,9 +132,30 @@ public abstract class KeyResolverSpi { * * @throws KeyResolverException */ - abstract public X509Certificate engineResolveX509Certificate( + public X509Certificate engineLookupResolveX509Certificate( Element element, String BaseURI, StorageResolver storage) - throws KeyResolverException; + throws KeyResolverException { + KeyResolverSpi tmp = cloneIfNeeded(); + if (!tmp.engineCanResolve(element, BaseURI, storage)) + return null; + return tmp.engineResolveX509Certificate(element, BaseURI, storage); + + } + /** + * Method engineResolveSecretKey + * + * @param element + * @param BaseURI + * @param storage + * @return resolved SecretKey key from the registered from the elements + * + * @throws KeyResolverException + */ + public SecretKey engineResolveSecretKey( + Element element, String BaseURI, StorageResolver storage) + throws KeyResolverException{ + throw new UnsupportedOperationException(); + }; /** * Method engineResolveSecretKey @@ -99,12 +167,19 @@ public abstract class KeyResolverSpi { * * @throws KeyResolverException */ - abstract public SecretKey engineResolveSecretKey( + public SecretKey engineLookupAndResolveSecretKey( Element element, String BaseURI, StorageResolver storage) - throws KeyResolverException; + throws KeyResolverException { + KeyResolverSpi tmp = cloneIfNeeded(); + if (!tmp.engineCanResolve(element, BaseURI, storage)) + return null; + return tmp.engineResolveSecretKey(element, BaseURI, storage); + } /** Field _properties */ - protected java.util.Map _properties = new java.util.HashMap(10); + protected java.util.Map _properties = null; + + protected boolean globalResolver=false; /** * Method engineSetProperty @@ -113,19 +188,8 @@ public abstract class KeyResolverSpi { * @param value */ public void engineSetProperty(String key, String value) { - - java.util.Iterator i = this._properties.keySet().iterator(); - - while (i.hasNext()) { - String c = (String) i.next(); - - if (c.equals(key)) { - key = c; - - break; - } - } - + if (_properties==null) + _properties=new HashMap(); this._properties.put(key, value); } @@ -136,31 +200,12 @@ public abstract class KeyResolverSpi { * @return obtain the property appointed by key */ public String engineGetProperty(String key) { - - java.util.Iterator i = this._properties.keySet().iterator(); - - while (i.hasNext()) { - String c = (String) i.next(); - - if (c.equals(key)) { - key = c; - - break; - } - } + if (_properties==null) + return null; return (String) this._properties.get(key); } - /** - * Method engineGetPropertyKeys - * - * @return the keys of properties known by this resolver - */ - public String[] engineGetPropertyKeys() { - return new String[0]; - } - /** * Method understandsProperty * @@ -168,17 +213,13 @@ public abstract class KeyResolverSpi { * @return true if understood the property */ public boolean understandsProperty(String propertyToTest) { + if (_properties==null) + return false; - String[] understood = this.engineGetPropertyKeys(); - - if (understood != null) { - for (int i = 0; i < understood.length; i++) { - if (understood[i].equals(propertyToTest)) { - return true; - } - } - } - - return false; + return this._properties.get(propertyToTest)!=null; } + public void setGlobalResolver(boolean globalResolver) { + this.globalResolver = globalResolver; + } + } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/DSAKeyValueResolver.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/DSAKeyValueResolver.java index efda14c1a..20bf7bad7 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/DSAKeyValueResolver.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/DSAKeyValueResolver.java @@ -37,46 +37,10 @@ import org.w3c.dom.Element; /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class DSAKeyValueResolver extends KeyResolverSpi { - /** Field _dsaKeyElement */ - private Element _dsaKeyElement = null; - - /** @inheritDoc */ - public boolean engineCanResolve(Element element, String BaseURI, - StorageResolver storage) { - - if (element == null) { - return false; - } - - boolean isKeyValue = XMLUtils.elementIsInSignatureSpace(element, - Constants._TAG_KEYVALUE); - boolean isDSAKeyValue = XMLUtils.elementIsInSignatureSpace(element, - Constants._TAG_DSAKEYVALUE); - - if (isKeyValue) { - - this._dsaKeyElement = - XMLUtils.selectDsNode(element.getFirstChild(),Constants._TAG_DSAKEYVALUE,0); - - if (this._dsaKeyElement != null) { - return true; - } - } else if (isDSAKeyValue) { - - // this trick is needed to allow the RetrievalMethodResolver to eat a - // ds:DSAKeyValue directly (without KeyValue) - this._dsaKeyElement = element; - - return true; - } - - return false; - } - /** * Method engineResolvePublicKey * @@ -85,20 +49,30 @@ public class DSAKeyValueResolver extends KeyResolverSpi { * @param storage * @return null if no {@link PublicKey} could be obtained */ - public PublicKey engineResolvePublicKey( + public PublicKey engineLookupAndResolvePublicKey( Element element, String BaseURI, StorageResolver storage) { + if (element == null) { + return null; + } + Element dsaKeyElement=null; + boolean isKeyValue = XMLUtils.elementIsInSignatureSpace(element, + Constants._TAG_KEYVALUE); + if (isKeyValue) { + dsaKeyElement = + XMLUtils.selectDsNode(element.getFirstChild(),Constants._TAG_DSAKEYVALUE,0); + } else if (XMLUtils.elementIsInSignatureSpace(element, + Constants._TAG_DSAKEYVALUE)) { + // this trick is needed to allow the RetrievalMethodResolver to eat a + // ds:DSAKeyValue directly (without KeyValue) + dsaKeyElement = element; + } - if (this._dsaKeyElement == null) { - boolean weCanResolve = this.engineCanResolve(element, BaseURI, - storage); - - if (!weCanResolve || (this._dsaKeyElement == null)) { - return null; - } + if (dsaKeyElement == null) { + return null; } try { - DSAKeyValue dsaKeyValue = new DSAKeyValue(this._dsaKeyElement, + DSAKeyValue dsaKeyValue = new DSAKeyValue(dsaKeyElement, BaseURI); PublicKey pk = dsaKeyValue.getPublicKey(); @@ -112,13 +86,13 @@ public class DSAKeyValueResolver extends KeyResolverSpi { /** @inheritDoc */ - public X509Certificate engineResolveX509Certificate( + public X509Certificate engineLookupResolveX509Certificate( Element element, String BaseURI, StorageResolver storage) { return null; } /** @inheritDoc */ - public javax.crypto.SecretKey engineResolveSecretKey( + public javax.crypto.SecretKey engineLookupAndResolveSecretKey( Element element, String BaseURI, StorageResolver storage){ return null; } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/EncryptedKeyResolver.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/EncryptedKeyResolver.java index 4b44f1c50..6adc050e8 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/EncryptedKeyResolver.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/EncryptedKeyResolver.java @@ -56,7 +56,6 @@ public class EncryptedKeyResolver extends KeyResolverSpi { RSAKeyValueResolver.class.getName()); - Key _key; Key _kek; String _algorithm; @@ -66,7 +65,6 @@ public class EncryptedKeyResolver extends KeyResolverSpi { * @param algorithm */ public EncryptedKeyResolver(String algorithm) { - _key = null; _kek = null; _algorithm=algorithm; } @@ -78,64 +76,49 @@ public class EncryptedKeyResolver extends KeyResolverSpi { */ public EncryptedKeyResolver(String algorithm, Key kek) { - _key = null; _algorithm = algorithm; _kek = kek; } - /** - * Method engineCanResolve - * - * @param element - * @param BaseURI - * @param storage - * @return true if can resolve the key in the element - * - */ - - public boolean engineCanResolve(Element element, String BaseURI, - StorageResolver storage) { - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "EncryptedKeyResolver - Can I resolve " + element.getTagName()); - - if (element == null) { - return false; - } - - boolean isEncryptedKey = XMLUtils.elementIsInEncryptionSpace(element, - EncryptionConstants._TAG_ENCRYPTEDKEY); - - if (isEncryptedKey) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Passed an Encrypted Key"); - try { - XMLCipher cipher = XMLCipher.getInstance(); - cipher.init(XMLCipher.UNWRAP_MODE, _kek); - EncryptedKey ek = cipher.loadEncryptedKey(element); - _key = cipher.decryptKey(ek, _algorithm); - } - catch (Exception e) {} - } - - return (_key != null); - } - /** @inheritDoc */ - public PublicKey engineResolvePublicKey( + public PublicKey engineLookupAndResolvePublicKey( Element element, String BaseURI, StorageResolver storage) { return null; } /** @inheritDoc */ - public X509Certificate engineResolveX509Certificate( + public X509Certificate engineLookupResolveX509Certificate( Element element, String BaseURI, StorageResolver storage) { return null; } /** @inheritDoc */ - public javax.crypto.SecretKey engineResolveSecretKey( + public javax.crypto.SecretKey engineLookupAndResolveSecretKey( Element element, String BaseURI, StorageResolver storage) { - return (SecretKey) _key; + SecretKey key=null; + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "EncryptedKeyResolver - Can I resolve " + element.getTagName()); + + if (element == null) { + return null; + } + + boolean isEncryptedKey = XMLUtils.elementIsInEncryptionSpace(element, + EncryptionConstants._TAG_ENCRYPTEDKEY); + + if (isEncryptedKey) { + log.log(java.util.logging.Level.FINE, "Passed an Encrypted Key"); + try { + XMLCipher cipher = XMLCipher.getInstance(); + cipher.init(XMLCipher.UNWRAP_MODE, _kek); + EncryptedKey ek = cipher.loadEncryptedKey(element); + key = (SecretKey) cipher.decryptKey(ek, _algorithm); + } + catch (Exception e) {} + } + + return key; } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/RSAKeyValueResolver.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/RSAKeyValueResolver.java index 541de7307..fb38e8725 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/RSAKeyValueResolver.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/RSAKeyValueResolver.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -38,7 +37,7 @@ import org.w3c.dom.Element; /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class RSAKeyValueResolver extends KeyResolverSpi { @@ -48,75 +47,55 @@ public class RSAKeyValueResolver extends KeyResolverSpi { RSAKeyValueResolver.class.getName()); /** Field _rsaKeyElement */ - private Element _rsaKeyElement = null; - /** @inheritDoc */ - public boolean engineCanResolve(Element element, String BaseURI, - StorageResolver storage) { - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Can I resolve " + element.getTagName()); + /** @inheritDoc */ + public PublicKey engineLookupAndResolvePublicKey( + Element element, String BaseURI, StorageResolver storage) { + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Can I resolve " + element.getTagName()); if (element == null) { - return false; + return null; } - boolean isKeyValue = XMLUtils.elementIsInSignatureSpace(element, - Constants._TAG_KEYVALUE); - boolean isRSAKeyValue = XMLUtils.elementIsInSignatureSpace(element, - Constants._TAG_RSAKEYVALUE); - - if (isKeyValue) { - this._rsaKeyElement = XMLUtils.selectDsNode(element.getFirstChild(), - Constants._TAG_RSAKEYVALUE, 0); - - if (this._rsaKeyElement != null) { - return true; - } - } else if (isRSAKeyValue) { - + boolean isKeyValue = XMLUtils.elementIsInSignatureSpace(element, + Constants._TAG_KEYVALUE); + Element rsaKeyElement=null; + if (isKeyValue) { + rsaKeyElement = XMLUtils.selectDsNode(element.getFirstChild(), + Constants._TAG_RSAKEYVALUE, 0); + } else if (XMLUtils.elementIsInSignatureSpace(element, + Constants._TAG_RSAKEYVALUE)) { // this trick is needed to allow the RetrievalMethodResolver to eat a // ds:RSAKeyValue directly (without KeyValue) - this._rsaKeyElement = element; - - return true; - } - - return false; - } - - /** @inheritDoc */ - public PublicKey engineResolvePublicKey( - Element element, String BaseURI, StorageResolver storage) { + rsaKeyElement = element; + } - if (this._rsaKeyElement == null) { - boolean weCanResolve = this.engineCanResolve(element, BaseURI, - storage); - if (!weCanResolve || (this._rsaKeyElement == null)) { - return null; - } + if (rsaKeyElement == null) { + return null; } try { - RSAKeyValue rsaKeyValue = new RSAKeyValue(this._rsaKeyElement, + RSAKeyValue rsaKeyValue = new RSAKeyValue(rsaKeyElement, BaseURI); return rsaKeyValue.getPublicKey(); } catch (XMLSecurityException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "XMLSecurityException", ex); + log.log(java.util.logging.Level.FINE, "XMLSecurityException", ex); } return null; } /** @inheritDoc */ - public X509Certificate engineResolveX509Certificate( + public X509Certificate engineLookupResolveX509Certificate( Element element, String BaseURI, StorageResolver storage) { return null; } /** @inheritDoc */ - public javax.crypto.SecretKey engineResolveSecretKey( + public javax.crypto.SecretKey engineLookupAndResolveSecretKey( Element element, String BaseURI, StorageResolver storage) { return null; } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/RetrievalMethodResolver.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/RetrievalMethodResolver.java index d8d98bdb9..3dff21824 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/RetrievalMethodResolver.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/RetrievalMethodResolver.java @@ -28,7 +28,15 @@ import java.security.PublicKey; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Set; +import javax.xml.parsers.ParserConfigurationException; + +import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException; import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; import com.sun.org.apache.xml.internal.security.keys.content.RetrievalMethod; import com.sun.org.apache.xml.internal.security.keys.content.x509.XMLX509Certificate; @@ -44,6 +52,7 @@ import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.Node; +import org.xml.sax.SAXException; /** @@ -55,7 +64,7 @@ import org.w3c.dom.Node; * RetrievalMethodResolver cannot handle itself, resolving of the extracted * element is delegated back to the KeyResolver mechanism. * - * @author $Author: raul $ + * @author $Author: mullan $ modified by Dave Garcia */ public class RetrievalMethodResolver extends KeyResolverSpi { @@ -65,198 +74,170 @@ public class RetrievalMethodResolver extends KeyResolverSpi { RetrievalMethodResolver.class.getName()); /** - * Method engineCanResolve + * Method engineResolvePublicKey * @inheritDoc * @param element * @param BaseURI * @param storage * */ - public boolean engineCanResolve(Element element, String BaseURI, - StorageResolver storage) { + public PublicKey engineLookupAndResolvePublicKey( + Element element, String BaseURI, StorageResolver storage) + { + if (!XMLUtils.elementIsInSignatureSpace(element, + Constants._TAG_RETRIEVALMETHOD)) { + return null; + } - if - (!XMLUtils.elementIsInSignatureSpace(element, - Constants._TAG_RETRIEVALMETHOD)) { - return false; - } + try { + //Create a retrieval method over the given element + RetrievalMethod rm = new RetrievalMethod(element, BaseURI); + String type = rm.getType(); + XMLSignatureInput resource=resolveInput(rm,BaseURI); + if (RetrievalMethod.TYPE_RAWX509.equals(type)) { + //a raw certificate, direct parsing is done! + X509Certificate cert=getRawCertificate(resource); + if (cert != null) { + return cert.getPublicKey(); + } + return null; + }; + Element e = obtainRefrenceElement(resource); + return resolveKey(e,BaseURI,storage); + } catch (XMLSecurityException ex) { + log.log(java.util.logging.Level.FINE, "XMLSecurityException", ex); + } catch (CertificateException ex) { + log.log(java.util.logging.Level.FINE, "CertificateException", ex); + } catch (IOException ex) { + log.log(java.util.logging.Level.FINE, "IOException", ex); + } catch (ParserConfigurationException e) { + log.log(java.util.logging.Level.FINE, "ParserConfigurationException", e); + } catch (SAXException e) { + log.log(java.util.logging.Level.FINE, "SAXException", e); + } + return null; + } - return true; + static private Element obtainRefrenceElement(XMLSignatureInput resource) throws CanonicalizationException, ParserConfigurationException, IOException, SAXException, KeyResolverException { + Element e; + if (resource.isElement()){ + e=(Element) resource.getSubNode(); + } else if (resource.isNodeSet()) { + //Retrieved resource is a nodeSet + e=getDocumentElement(resource.getNodeSet()); + } else { + //Retrieved resource is an inputStream + byte inputBytes[] = resource.getBytes(); + e = getDocFromBytes(inputBytes); + //otherwise, we parse the resource, create an Element and delegate + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "we have to parse " + inputBytes.length + " bytes"); + } + return e; } /** - * Method engineResolvePublicKey + * Method engineResolveX509Certificate * @inheritDoc * @param element * @param BaseURI * @param storage * */ - public PublicKey engineResolvePublicKey( + public X509Certificate engineLookupResolveX509Certificate( Element element, String BaseURI, StorageResolver storage) { + if (!XMLUtils.elementIsInSignatureSpace(element, + Constants._TAG_RETRIEVALMETHOD)) { + return null; + } - try { + try { RetrievalMethod rm = new RetrievalMethod(element, BaseURI); - Attr uri = rm.getURIAttr(); - - // type can be null because it's optional - String type = rm.getType(); - Transforms transforms = rm.getTransforms(); - ResourceResolver resRes = ResourceResolver.getInstance(uri, BaseURI); - - if (resRes != null) { - XMLSignatureInput resource = resRes.resolve(uri, BaseURI); - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Before applying Transforms, resource has " - + resource.getBytes().length + "bytes"); - - if (transforms != null) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "We have Transforms"); - - resource = transforms.performTransforms(resource); - } - if (true) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "After applying Transforms, resource has " - + resource.getBytes().length + "bytes"); - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Resolved to resource " + resource.getSourceURI()); - } - - byte inputBytes[] = resource.getBytes(); - - if ((type != null) && type.equals(RetrievalMethod.TYPE_RAWX509)) { - - // if the resource stores a raw certificate, we have to handle it - CertificateFactory certFact = - CertificateFactory - .getInstance(XMLX509Certificate.JCA_CERT_ID); - X509Certificate cert = - (X509Certificate) certFact - .generateCertificate(new ByteArrayInputStream(inputBytes)); - - if (cert != null) { - return cert.getPublicKey(); - } - } else { - - // otherwise, we parse the resource, create an Element and delegate - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "we have to parse " + inputBytes.length + " bytes"); - - Element e = this.getDocFromBytes(inputBytes); - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Now we have a {" + e.getNamespaceURI() + "}" - + e.getLocalName() + " Element"); - - if (e != null) { - KeyResolver newKeyResolver = KeyResolver.getInstance(getFirstElementChild(e), - BaseURI, storage); - - if (newKeyResolver != null) { - return newKeyResolver.resolvePublicKey(getFirstElementChild(e), BaseURI, - storage); - } - } - } - } + String type = rm.getType(); + XMLSignatureInput resource=resolveInput(rm,BaseURI); + if (RetrievalMethod.TYPE_RAWX509.equals(type)) { + X509Certificate cert=getRawCertificate(resource); + return cert; + } + Element e = obtainRefrenceElement(resource); + return resolveCertificate(e,BaseURI,storage); } catch (XMLSecurityException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "XMLSecurityException", ex); + log.log(java.util.logging.Level.FINE, "XMLSecurityException", ex); } catch (CertificateException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "CertificateException", ex); + log.log(java.util.logging.Level.FINE, "CertificateException", ex); } catch (IOException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "IOException", ex); - } - + log.log(java.util.logging.Level.FINE, "IOException", ex); + } catch (ParserConfigurationException e) { + log.log(java.util.logging.Level.FINE, "ParserConfigurationException", e); + } catch (SAXException e) { + log.log(java.util.logging.Level.FINE, "SAXException", e); + } return null; } /** - * Method engineResolveX509Certificate - * @inheritDoc - * @param element + * Retrieves a x509Certificate from the given information + * @param e * @param BaseURI * @param storage - * + * @return + * @throws KeyResolverException */ - public X509Certificate engineResolveX509Certificate( - Element element, String BaseURI, StorageResolver storage) - { - - try { - RetrievalMethod rm = new RetrievalMethod(element, BaseURI); - Attr uri = rm.getURIAttr(); - Transforms transforms = rm.getTransforms(); - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Asked to resolve URI " + uri); - - ResourceResolver resRes = ResourceResolver.getInstance(uri, BaseURI); - - if (resRes != null) { - XMLSignatureInput resource = resRes.resolve(uri, BaseURI); - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Before applying Transforms, resource has " - + resource.getBytes().length + "bytes"); - - if (transforms != null) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "We have Transforms"); - - resource = transforms.performTransforms(resource); - } - - if (true) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "After applying Transforms, resource has " - + resource.getBytes().length + "bytes"); - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Resolved to resource " + resource.getSourceURI()); - } - - byte inputBytes[] = resource.getBytes(); - - if ((rm.getType() != null) - && rm.getType().equals(RetrievalMethod.TYPE_RAWX509)) { - - // if the resource stores a raw certificate, we have to handle it - CertificateFactory certFact = - CertificateFactory - .getInstance(XMLX509Certificate.JCA_CERT_ID); - X509Certificate cert = - (X509Certificate) certFact - .generateCertificate(new ByteArrayInputStream(inputBytes)); - - if (cert != null) { - return cert; - } - } else { - - // otherwise, we parse the resource, create an Element and delegate - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "we have to parse " + inputBytes.length + " bytes"); - - Element e = this.getDocFromBytes(inputBytes); - - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Now we have a {" + e.getNamespaceURI() + "}" - + e.getLocalName() + " Element"); - - if (e != null) { - KeyResolver newKeyResolver = KeyResolver.getInstance(getFirstElementChild(e), - BaseURI, storage); + static private X509Certificate resolveCertificate(Element e,String BaseURI,StorageResolver storage) throws KeyResolverException{ + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Now we have a {" + e.getNamespaceURI() + "}"+ e.getLocalName() + " Element"); + //An element has been provided + if (e != null) { + return KeyResolver.getX509Certificate(e,BaseURI, storage); + } + return null; + } - if (newKeyResolver != null) { - return newKeyResolver.resolveX509Certificate(getFirstElementChild(e), BaseURI, - storage); + /** + * Retrieves a x509Certificate from the given information + * @param e + * @param BaseURI + * @param storage + * @return + * @throws KeyResolverException + */ + static private PublicKey resolveKey(Element e,String BaseURI,StorageResolver storage) throws KeyResolverException{ + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Now we have a {" + e.getNamespaceURI() + "}"+ e.getLocalName() + " Element"); + //An element has been provided + if (e != null) { + return KeyResolver.getPublicKey(e,BaseURI, storage); } - } - } - } - } catch (XMLSecurityException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "XMLSecurityException", ex); - } catch (CertificateException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "CertificateException", ex); - } catch (IOException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "IOException", ex); - } + return null; + } - return null; + static private X509Certificate getRawCertificate(XMLSignatureInput resource) throws CanonicalizationException, IOException, CertificateException{ + byte inputBytes[] = resource.getBytes(); + // if the resource stores a raw certificate, we have to handle it + CertificateFactory certFact =CertificateFactory.getInstance(XMLX509Certificate.JCA_CERT_ID); + X509Certificate cert =(X509Certificate) certFact.generateCertificate(new ByteArrayInputStream(inputBytes)); + return cert; + } + /** + * Resolves the input from the given retrieval method + * @return + * @throws XMLSecurityException + */ + static private XMLSignatureInput resolveInput(RetrievalMethod rm,String BaseURI) throws XMLSecurityException{ + Attr uri = rm.getURIAttr(); + //Apply the trnasforms + Transforms transforms = rm.getTransforms(); + ResourceResolver resRes = ResourceResolver.getInstance(uri, BaseURI); + if (resRes != null) { + XMLSignatureInput resource = resRes.resolve(uri, BaseURI); + if (transforms != null) { + log.log(java.util.logging.Level.FINE, "We have Transforms"); + resource = transforms.performTransforms(resource); + } + return resource; + } + return null; } /** @@ -266,18 +247,13 @@ public class RetrievalMethodResolver extends KeyResolverSpi { * @return the Document Element after parsing bytes * @throws KeyResolverException if something goes wrong */ - Element getDocFromBytes(byte[] bytes) throws KeyResolverException { - + static Element getDocFromBytes(byte[] bytes) throws KeyResolverException { try { - javax.xml.parsers.DocumentBuilderFactory dbf = - javax.xml.parsers.DocumentBuilderFactory.newInstance(); - + javax.xml.parsers.DocumentBuilderFactory dbf =javax.xml.parsers.DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); - javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder(); org.w3c.dom.Document doc = db.parse(new java.io.ByteArrayInputStream(bytes)); - return doc.getDocumentElement(); } catch (org.xml.sax.SAXException ex) { throw new KeyResolverException("empty", ex); @@ -296,16 +272,43 @@ public class RetrievalMethodResolver extends KeyResolverSpi { * @param storage * */ - public javax.crypto.SecretKey engineResolveSecretKey( + public javax.crypto.SecretKey engineLookupAndResolveSecretKey( Element element, String BaseURI, StorageResolver storage) { return null; } - static Element getFirstElementChild(Element e){ - Node n=e.getFirstChild(); - while (n!=null && n.getNodeType()!=Node.ELEMENT_NODE) { - n=n.getNextSibling(); - } - return (Element)n; + + static Element getDocumentElement(Set set) { + Iterator it=set.iterator(); + Element e=null; + while (it.hasNext()) { + Node currentNode=(Node)it.next(); + if (currentNode instanceof Element) { + e=(Element)currentNode; + break; + } + + } + List parents=new ArrayList(10); + + //Obtain all the parents of the elemnt + do { + parents.add(e); + Node n=e.getParentNode(); + if (!(n instanceof Element )) { + break; + } + e=(Element)n; + } while (e!=null); + //Visit them in reverse order. + ListIterator it2=parents.listIterator(parents.size()-1); + Element ele=null; + while (it2.hasPrevious()) { + ele=(Element)it2.previous(); + if (set.contains(ele)) { + return ele; + } + } + return null; } } diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/X509CertificateResolver.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/X509CertificateResolver.java index 16264c998..06a49c670 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/X509CertificateResolver.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/X509CertificateResolver.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -41,7 +40,7 @@ import org.w3c.dom.Element; * Resolves Certificates which are directly contained inside a * ds:X509Certificate Element. * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class X509CertificateResolver extends KeyResolverSpi { @@ -49,47 +48,7 @@ public class X509CertificateResolver extends KeyResolverSpi { static java.util.logging.Logger log = java.util.logging.Logger.getLogger(X509CertificateResolver.class.getName()); - /** Field _dsaKeyElement */ - Element[] _x509CertKeyElements = null; - - /** - * Method engineCanResolve - * @inheritDoc - * @param element - * @param BaseURI - * @param storage - * - */ - public boolean engineCanResolve(Element element, String BaseURI, - StorageResolver storage) { - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Can I resolve " + element.getTagName() + "?"); - - if (!XMLUtils.elementIsInSignatureSpace(element, - Constants._TAG_X509DATA)) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "I can't"); - - return false; - } - - this._x509CertKeyElements = XMLUtils.selectDsNodes(element.getFirstChild(), - Constants._TAG_X509CERTIFICATE); - - if ((this._x509CertKeyElements != null) - && (this._x509CertKeyElements.length > 0)) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Yes Sir, I can"); - - return true; - } - - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "I can't"); - - return false; - } - - /** Field _x509certObject[] */ - XMLX509Certificate _x509certObject[] = null; /** * Method engineResolvePublicKey @@ -100,11 +59,11 @@ public class X509CertificateResolver extends KeyResolverSpi { * * @throws KeyResolverException */ - public PublicKey engineResolvePublicKey( + public PublicKey engineLookupAndResolvePublicKey( Element element, String BaseURI, StorageResolver storage) throws KeyResolverException { - X509Certificate cert = this.engineResolveX509Certificate(element, + X509Certificate cert = this.engineLookupResolveX509Certificate(element, BaseURI, storage); if (cert != null) { @@ -123,43 +82,33 @@ public class X509CertificateResolver extends KeyResolverSpi { * * @throws KeyResolverException */ - public X509Certificate engineResolveX509Certificate( + public X509Certificate engineLookupResolveX509Certificate( Element element, String BaseURI, StorageResolver storage) throws KeyResolverException { try { - if ((this._x509CertKeyElements == null) - || (this._x509CertKeyElements.length == 0)) { - boolean weCanResolve = this.engineCanResolve(element, BaseURI, - storage); - - if (!weCanResolve || (this._x509CertKeyElements == null) - || (this._x509CertKeyElements.length == 0)) { - return null; - } + Element[] els=XMLUtils.selectDsNodes(element.getFirstChild(), + Constants._TAG_X509CERTIFICATE); + if ((els == null) || (els.length == 0)) { + Element el=XMLUtils.selectDsNode(element.getFirstChild(), + Constants._TAG_X509DATA,0); + if (el!=null) { + return engineLookupResolveX509Certificate(el, BaseURI, storage); + } + return null; } - this._x509certObject = - new XMLX509Certificate[this._x509CertKeyElements.length]; - // populate Object array - for (int i = 0; i < this._x509CertKeyElements.length; i++) { - this._x509certObject[i] = - new XMLX509Certificate(this._x509CertKeyElements[i] - , BaseURI); - } - - for (int i = 0; i < this._x509certObject.length; i++) { - X509Certificate cert = this._x509certObject[i].getX509Certificate(); - - if (cert != null) { - return cert; + for (int i = 0; i < els.length; i++) { + XMLX509Certificate xmlCert=new XMLX509Certificate(els[i], BaseURI); + X509Certificate cert = xmlCert.getX509Certificate(); + if (cert!=null) { + return cert; } } - return null; } catch (XMLSecurityException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "XMLSecurityException", ex); + log.log(java.util.logging.Level.FINE, "XMLSecurityException", ex); throw new KeyResolverException("generic.EmptyMessage", ex); } @@ -173,7 +122,7 @@ public class X509CertificateResolver extends KeyResolverSpi { * @param storage * */ - public javax.crypto.SecretKey engineResolveSecretKey( + public javax.crypto.SecretKey engineLookupAndResolveSecretKey( Element element, String BaseURI, StorageResolver storage) { return null; diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/X509IssuerSerialResolver.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/X509IssuerSerialResolver.java index c4e824747..8f717e716 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/X509IssuerSerialResolver.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/X509IssuerSerialResolver.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -39,7 +38,7 @@ import org.w3c.dom.Element; /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class X509IssuerSerialResolver extends KeyResolverSpi { @@ -48,44 +47,13 @@ public class X509IssuerSerialResolver extends KeyResolverSpi { java.util.logging.Logger.getLogger( X509IssuerSerialResolver.class.getName()); - /** @inheritDoc */ - public boolean engineCanResolve(Element element, String BaseURI, - StorageResolver storage) { - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Can I resolve " + element.getTagName() + "?"); - - X509Data x509data = null; - try { - x509data = new X509Data(element, BaseURI); - } catch (XMLSignatureException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "I can't"); - - return false; - } catch (XMLSecurityException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "I can't"); - - return false; - } - - if (x509data == null) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "I can't"); - return false; - } - - if (x509data.containsIssuerSerial()) { - return true; - } - - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "I can't"); - return false; - } /** @inheritDoc */ - public PublicKey engineResolvePublicKey( + public PublicKey engineLookupAndResolvePublicKey( Element element, String BaseURI, StorageResolver storage) throws KeyResolverException { - X509Certificate cert = this.engineResolveX509Certificate(element, + X509Certificate cert = this.engineLookupResolveX509Certificate(element, BaseURI, storage); if (cert != null) { @@ -96,10 +64,31 @@ public class X509IssuerSerialResolver extends KeyResolverSpi { } /** @inheritDoc */ - public X509Certificate engineResolveX509Certificate( + public X509Certificate engineLookupResolveX509Certificate( Element element, String BaseURI, StorageResolver storage) throws KeyResolverException { - + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Can I resolve " + element.getTagName() + "?"); + + X509Data x509data = null; + try { + x509data = new X509Data(element, BaseURI); + } catch (XMLSignatureException ex) { + log.log(java.util.logging.Level.FINE, "I can't"); + return null; + } catch (XMLSecurityException ex) { + log.log(java.util.logging.Level.FINE, "I can't"); + return null; + } + + if (x509data == null) { + log.log(java.util.logging.Level.FINE, "I can't"); + return null; + } + + if (!x509data.containsIssuerSerial()) { + return null; + } try { if (storage == null) { Object exArgs[] = { Constants._TAG_X509ISSUERSERIAL }; @@ -107,53 +96,52 @@ public class X509IssuerSerialResolver extends KeyResolverSpi { new KeyResolverException("KeyResolver.needStorageResolver", exArgs); - if (log.isLoggable(java.util.logging.Level.INFO)) log.log(java.util.logging.Level.INFO, "", ex); + log.log(java.util.logging.Level.INFO, "", ex); throw ex; } - X509Data x509data = new X509Data(element, BaseURI); int noOfISS = x509data.lengthIssuerSerial(); while (storage.hasNext()) { X509Certificate cert = storage.next(); XMLX509IssuerSerial certSerial = new XMLX509IssuerSerial(element.getOwnerDocument(), cert); - if (true) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Found Certificate Issuer: " + if (log.isLoggable(java.util.logging.Level.FINE)) { + log.log(java.util.logging.Level.FINE, "Found Certificate Issuer: " + certSerial.getIssuerName()); - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Found Certificate Serial: " + log.log(java.util.logging.Level.FINE, "Found Certificate Serial: " + certSerial.getSerialNumber().toString()); } for (int i=0; i 0)) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Yes Sir, I can"); - - return true; - } - - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "I can't"); - - return false; - } /** * Method engineResolvePublicKey @@ -102,11 +57,11 @@ public class X509SKIResolver extends KeyResolverSpi { * @return null if no {@link PublicKey} could be obtained * @throws KeyResolverException */ - public PublicKey engineResolvePublicKey( + public PublicKey engineLookupAndResolvePublicKey( Element element, String BaseURI, StorageResolver storage) throws KeyResolverException { - X509Certificate cert = this.engineResolveX509Certificate(element, + X509Certificate cert = this.engineLookupResolveX509Certificate(element, BaseURI, storage); if (cert != null) { @@ -125,46 +80,55 @@ public class X509SKIResolver extends KeyResolverSpi { * * @throws KeyResolverException */ - public X509Certificate engineResolveX509Certificate( + public X509Certificate engineLookupResolveX509Certificate( Element element, String BaseURI, StorageResolver storage) throws KeyResolverException { - - try { - if (this._x509childNodes == null) { - boolean weCanResolve = this.engineCanResolve(element, BaseURI, - storage); - - if (!weCanResolve || (this._x509childNodes == null)) { - return null; - } - } - + if (log.isLoggable(java.util.logging.Level.FINE)) { + log.log(java.util.logging.Level.FINE, "Can I resolve " + element.getTagName() + "?"); + } + if (!XMLUtils.elementIsInSignatureSpace(element, + Constants._TAG_X509DATA)) { + log.log(java.util.logging.Level.FINE, "I can't"); + return null; + } + /** Field _x509childObject[] */ + XMLX509SKI x509childObject[] = null; + + Element x509childNodes[] = null; + x509childNodes = XMLUtils.selectDsNodes(element.getFirstChild(), + Constants._TAG_X509SKI); + + if (!((x509childNodes != null) + && (x509childNodes.length > 0))) { + log.log(java.util.logging.Level.FINE, "I can't"); + return null; + } + try { if (storage == null) { Object exArgs[] = { Constants._TAG_X509SKI }; KeyResolverException ex = new KeyResolverException("KeyResolver.needStorageResolver", exArgs); - if (log.isLoggable(java.util.logging.Level.INFO)) log.log(java.util.logging.Level.INFO, "", ex); + log.log(java.util.logging.Level.INFO, "", ex); throw ex; } - this._x509childObject = - new XMLX509SKI[this._x509childNodes.length]; + x509childObject = new XMLX509SKI[x509childNodes.length]; - for (int i = 0; i < this._x509childNodes.length; i++) { - this._x509childObject[i] = - new XMLX509SKI(this._x509childNodes[i], BaseURI); + for (int i = 0; i < x509childNodes.length; i++) { + x509childObject[i] = + new XMLX509SKI(x509childNodes[i], BaseURI); } while (storage.hasNext()) { X509Certificate cert = storage.next(); XMLX509SKI certSKI = new XMLX509SKI(element.getOwnerDocument(), cert); - for (int i = 0; i < this._x509childObject.length; i++) { - if (certSKI.equals(this._x509childObject[i])) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Return PublicKey from " + for (int i = 0; i < x509childObject.length; i++) { + if (certSKI.equals(x509childObject[i])) { + log.log(java.util.logging.Level.FINE, "Return PublicKey from " + cert.getSubjectDN().getName()); return cert; @@ -186,7 +150,7 @@ public class X509SKIResolver extends KeyResolverSpi { * @param storage * */ - public javax.crypto.SecretKey engineResolveSecretKey( + public javax.crypto.SecretKey engineLookupAndResolveSecretKey( Element element, String BaseURI, StorageResolver storage) { return null; diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/X509SubjectNameResolver.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/X509SubjectNameResolver.java index 5da6a7d8f..05e82226c 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/X509SubjectNameResolver.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/X509SubjectNameResolver.java @@ -38,7 +38,7 @@ import org.w3c.dom.Element; /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class X509SubjectNameResolver extends KeyResolverSpi { @@ -47,50 +47,6 @@ public class X509SubjectNameResolver extends KeyResolverSpi { java.util.logging.Logger.getLogger( X509SubjectNameResolver.class.getName()); - /** Field _x509childNodes */ - private Element[] _x509childNodes = null; - - /** Field _x509childObject[] */ - private XMLX509SubjectName _x509childObject[] = null; - - /** - * Method engineCanResolve - * @inheritDoc - * @param element - * @param BaseURI - * @param storage - * - */ - public boolean engineCanResolve(Element element, String BaseURI, - StorageResolver storage) { - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Can I resolve " + element.getTagName() + "?"); - - - if (!XMLUtils.elementIsInSignatureSpace(element, - Constants._TAG_X509DATA) ) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "I can't"); - - return false; - } - - - - this._x509childNodes = XMLUtils.selectDsNodes(element, - Constants._TAG_X509SUBJECTNAME); - - if ((this._x509childNodes != null) - && (this._x509childNodes.length > 0)) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Yes Sir, I can"); - - return true; - } - - - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "I can't"); - - return false; - } /** * Method engineResolvePublicKey @@ -101,11 +57,11 @@ public class X509SubjectNameResolver extends KeyResolverSpi { * @return null if no {@link PublicKey} could be obtained * @throws KeyResolverException */ - public PublicKey engineResolvePublicKey( + public PublicKey engineLookupAndResolvePublicKey( Element element, String BaseURI, StorageResolver storage) throws KeyResolverException { - X509Certificate cert = this.engineResolveX509Certificate(element, + X509Certificate cert = this.engineLookupResolveX509Certificate(element, BaseURI, storage); if (cert != null) { @@ -124,37 +80,46 @@ public class X509SubjectNameResolver extends KeyResolverSpi { * * @throws KeyResolverException */ - public X509Certificate engineResolveX509Certificate( + public X509Certificate engineLookupResolveX509Certificate( Element element, String BaseURI, StorageResolver storage) throws KeyResolverException { + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Can I resolve " + element.getTagName() + "?"); + Element[] x509childNodes = null; + XMLX509SubjectName x509childObject[] = null; + + if (!XMLUtils.elementIsInSignatureSpace(element, + Constants._TAG_X509DATA) ) { + log.log(java.util.logging.Level.FINE, "I can't"); + return null; + } + x509childNodes = XMLUtils.selectDsNodes(element.getFirstChild(), + Constants._TAG_X509SUBJECTNAME); - try { - if (this._x509childNodes == null) { - boolean weCanResolve = this.engineCanResolve(element, BaseURI, - storage); - - if (!weCanResolve || (this._x509childNodes == null)) { - return null; + if (!((x509childNodes != null) + && (x509childNodes.length > 0))) { + log.log(java.util.logging.Level.FINE, "I can't"); + return null; } - } + try { if (storage == null) { Object exArgs[] = { Constants._TAG_X509SUBJECTNAME }; KeyResolverException ex = new KeyResolverException("KeyResolver.needStorageResolver", exArgs); - if (log.isLoggable(java.util.logging.Level.INFO)) log.log(java.util.logging.Level.INFO, "", ex); + log.log(java.util.logging.Level.INFO, "", ex); throw ex; } - this._x509childObject = - new XMLX509SubjectName[this._x509childNodes.length]; + x509childObject = + new XMLX509SubjectName[x509childNodes.length]; - for (int i = 0; i < this._x509childNodes.length; i++) { - this._x509childObject[i] = - new XMLX509SubjectName(this._x509childNodes[i], + for (int i = 0; i < x509childNodes.length; i++) { + x509childObject[i] = + new XMLX509SubjectName(x509childNodes[i], BaseURI); } @@ -163,24 +128,24 @@ public class X509SubjectNameResolver extends KeyResolverSpi { XMLX509SubjectName certSN = new XMLX509SubjectName(element.getOwnerDocument(), cert); - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Found Certificate SN: " + certSN.getSubjectName()); + log.log(java.util.logging.Level.FINE, "Found Certificate SN: " + certSN.getSubjectName()); - for (int i = 0; i < this._x509childObject.length; i++) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Found Element SN: " - + this._x509childObject[i].getSubjectName()); + for (int i = 0; i < x509childObject.length; i++) { + log.log(java.util.logging.Level.FINE, "Found Element SN: " + + x509childObject[i].getSubjectName()); - if (certSN.equals(this._x509childObject[i])) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "match !!! "); + if (certSN.equals(x509childObject[i])) { + log.log(java.util.logging.Level.FINE, "match !!! "); return cert; } - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "no match..."); + log.log(java.util.logging.Level.FINE, "no match..."); } } return null; } catch (XMLSecurityException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "XMLSecurityException", ex); + log.log(java.util.logging.Level.FINE, "XMLSecurityException", ex); throw new KeyResolverException("generic.EmptyMessage", ex); } @@ -194,7 +159,7 @@ public class X509SubjectNameResolver extends KeyResolverSpi { * @param storage * */ - public javax.crypto.SecretKey engineResolveSecretKey( + public javax.crypto.SecretKey engineLookupAndResolveSecretKey( Element element, String BaseURI, StorageResolver storage) { return null; diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/StorageResolver.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/StorageResolver.java index 46fee3d42..f1a8dd340 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/StorageResolver.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/StorageResolver.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -21,8 +20,6 @@ */ package com.sun.org.apache.xml.internal.security.keys.storage; - - import java.security.KeyStore; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -36,7 +33,7 @@ import com.sun.org.apache.xml.internal.security.keys.storage.implementations.Sin /** * This class collects customized resolvers for Certificates. * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class StorageResolver { @@ -45,7 +42,7 @@ public class StorageResolver { java.util.logging.Logger.getLogger(StorageResolver.class.getName()); /** Field _storageResolvers */ - List _storageResolvers = new ArrayList(); + List _storageResolvers = null; /** Field _iterator */ Iterator _iterator = null; @@ -71,7 +68,8 @@ public class StorageResolver { * @param resolver */ public void add(StorageResolverSpi resolver) { - + if (_storageResolvers==null) + _storageResolvers=new ArrayList(); this._storageResolvers.add(resolver); this._iterator = null; @@ -126,6 +124,8 @@ public class StorageResolver { public Iterator getIterator() { if (this._iterator == null) { + if (_storageResolvers==null) + _storageResolvers=new ArrayList(); this._iterator = new StorageResolverIterator(this._storageResolvers.iterator()); } @@ -140,6 +140,8 @@ public class StorageResolver { public boolean hasNext() { if (this._iterator == null) { + if (_storageResolvers==null) + _storageResolvers=new ArrayList(); this._iterator = new StorageResolverIterator(this._storageResolvers.iterator()); } @@ -158,15 +160,13 @@ public class StorageResolver { /** * Class StorageResolverIterator * - * @author $Author: raul $ + * @author $Author: mullan $ + * @version $Revision: 1.5 $ */ - class StorageResolverIterator implements Iterator { + static class StorageResolverIterator implements Iterator { /** Field _resolvers */ - Iterator _resolvers = null; - - /** Field _currentResolver */ - int _currentResolver = 0; + Iterator _resolvers = null; /** * Constructor FilesystemIterator @@ -179,17 +179,16 @@ public class StorageResolver { /** @inheritDoc */ public boolean hasNext() { - return _resolvers.hasNext(); + return _resolvers.hasNext(); } /** @inheritDoc */ public Object next() { - return _resolvers.next(); + return _resolvers.next(); } /** * Method remove - * */ public void remove() { throw new UnsupportedOperationException( diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/StorageResolverException.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/StorageResolverException.java index 2388ef19b..29dff030f 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/StorageResolverException.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/StorageResolverException.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -28,7 +27,7 @@ import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class StorageResolverException extends XMLSecurityException { diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/StorageResolverSpi.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/StorageResolverSpi.java index dc95e6040..25f3e2828 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/StorageResolverSpi.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/StorageResolverSpi.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -28,7 +27,7 @@ import java.util.Iterator; /** * - * @author $Author: raul $ + * @author $Author: mullan $ */ public abstract class StorageResolverSpi { diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/implementations/CertsInFilesystemDirectoryResolver.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/implementations/CertsInFilesystemDirectoryResolver.java index 6c6909665..06fb5694b 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/implementations/CertsInFilesystemDirectoryResolver.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/implementations/CertsInFilesystemDirectoryResolver.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -21,8 +20,6 @@ */ package com.sun.org.apache.xml.internal.security.keys.storage.implementations; - - import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -40,12 +37,11 @@ import com.sun.org.apache.xml.internal.security.keys.storage.StorageResolverExce import com.sun.org.apache.xml.internal.security.keys.storage.StorageResolverSpi; import com.sun.org.apache.xml.internal.security.utils.Base64; - /** * This {@link StorageResolverSpi} makes all raw (binary) {@link X509Certificate}s * which reside as files in a single directory available to the {@link com.sun.org.apache.xml.internal.security.keys.storage.StorageResolver}. * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class CertsInFilesystemDirectoryResolver extends StorageResolverSpi { @@ -131,20 +127,20 @@ public class CertsInFilesystemDirectoryResolver extends StorageResolverSpi { dn = cert.getSubjectDN().getName(); added = true; } catch (FileNotFoundException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Could not add certificate from file " + filename, ex); + log.log(java.util.logging.Level.FINE, "Could not add certificate from file " + filename, ex); } catch (IOException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Could not add certificate from file " + filename, ex); + log.log(java.util.logging.Level.FINE, "Could not add certificate from file " + filename, ex); } catch (CertificateNotYetValidException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Could not add certificate from file " + filename, ex); + log.log(java.util.logging.Level.FINE, "Could not add certificate from file " + filename, ex); } catch (CertificateExpiredException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Could not add certificate from file " + filename, ex); + log.log(java.util.logging.Level.FINE, "Could not add certificate from file " + filename, ex); } catch (CertificateException ex) { - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Could not add certificate from file " + filename, ex); + log.log(java.util.logging.Level.FINE, "Could not add certificate from file " + filename, ex); } if (added) { - if (true) - if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Added certificate: " + dn); + if (log.isLoggable(java.util.logging.Level.FINE)) + log.log(java.util.logging.Level.FINE, "Added certificate: " + dn); } } } @@ -157,9 +153,10 @@ public class CertsInFilesystemDirectoryResolver extends StorageResolverSpi { /** * Class FilesystemIterator * - * @author $Author: raul $ + * @author $Author: mullan $ + * @version $Revision: 1.5 $ */ - class FilesystemIterator implements Iterator { + private static class FilesystemIterator implements Iterator { /** Field _certs */ List _certs = null; diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/implementations/KeyStoreResolver.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/implementations/KeyStoreResolver.java index ad1eb6b14..18632433c 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/implementations/KeyStoreResolver.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/implementations/KeyStoreResolver.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -21,8 +20,6 @@ */ package com.sun.org.apache.xml.internal.security.keys.storage.implementations; - - import java.security.KeyStore; import java.security.KeyStoreException; import java.security.cert.X509Certificate; @@ -37,7 +34,7 @@ import com.sun.org.apache.xml.internal.security.keys.storage.StorageResolverSpi; * Makes the Certificates from a JAVA {@link KeyStore} object available to the * {@link com.sun.org.apache.xml.internal.security.keys.storage.StorageResolver}. * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class KeyStoreResolver extends StorageResolverSpi { @@ -66,9 +63,10 @@ public class KeyStoreResolver extends StorageResolverSpi { /** * Class KeyStoreIterator * - * @author $Author: raul $ + * @author $Author: mullan $ + * @version $Revision: 1.5 $ */ - class KeyStoreIterator implements Iterator { + static class KeyStoreIterator implements Iterator { /** Field _keyStore */ KeyStore _keyStore = null; diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/implementations/SingleCertificateResolver.java b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/implementations/SingleCertificateResolver.java index 6c4f260b9..7e61b2a24 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/implementations/SingleCertificateResolver.java +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/keys/storage/implementations/SingleCertificateResolver.java @@ -2,7 +2,6 @@ * reserved comment block * DO NOT REMOVE OR ALTER! */ - /* * Copyright 1999-2004 The Apache Software Foundation. * @@ -21,8 +20,6 @@ */ package com.sun.org.apache.xml.internal.security.keys.storage.implementations; - - import java.security.cert.X509Certificate; import java.util.Iterator; @@ -33,7 +30,7 @@ import com.sun.org.apache.xml.internal.security.keys.storage.StorageResolverSpi; * This {@link StorageResolverSpi} makes a single {@link X509Certificate} * available to the {@link com.sun.org.apache.xml.internal.security.keys.storage.StorageResolver}. * - * @author $Author: raul $ + * @author $Author: mullan $ */ public class SingleCertificateResolver extends StorageResolverSpi { @@ -61,9 +58,10 @@ public class SingleCertificateResolver extends StorageResolverSpi { /** * Class InternalIterator * - * @author $Author: raul $ + * @author $Author: mullan $ + * @version $Revision: 1.5 $ */ - class InternalIterator implements Iterator { + static class InternalIterator implements Iterator { /** Field _alreadyReturned */ boolean _alreadyReturned = false; diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/resource/config.dtd b/src/share/classes/com/sun/org/apache/xml/internal/security/resource/config.dtd index 1e886bf17..f57b9fabe 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/resource/config.dtd +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/resource/config.dtd @@ -1,73 +1,73 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/resource/config.xml b/src/share/classes/com/sun/org/apache/xml/internal/security/resource/config.xml index d0d6edcee..aea159574 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/resource/config.xml +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/resource/config.xml @@ -1,380 +1,399 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/resource/schema/etsi.xsd b/src/share/classes/com/sun/org/apache/xml/internal/security/resource/schema/etsi.xsd index 3a08c64f4..d69852ff8 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/resource/schema/etsi.xsd +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/resource/schema/etsi.xsd @@ -1,347 +1,347 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/resource/schema/xmldsig-core-schema.dtd b/src/share/classes/com/sun/org/apache/xml/internal/security/resource/schema/xmldsig-core-schema.dtd index 969dbb18c..b2cc19f63 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/resource/schema/xmldsig-core-schema.dtd +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/resource/schema/xmldsig-core-schema.dtd @@ -3,7 +3,7 @@ Joseph Reagle $last changed 20001215$ http://www.w3.org/2000/09/xmldsig# - $Revision: 1.1 $ on $Date: 2002/02/08 20:32:26 $ by $Author: reagle $ + $Revision: 1.6 $ on $Date: 2008/07/24 16:15:03 $ by $Author: mullan $ Copyright 2001 The Internet Society and W3C (Massachusetts Institute of Technology, Institut National de Recherche en Informatique et en diff --git a/src/share/classes/com/sun/org/apache/xml/internal/security/resource/schema/xmldsig-core-schema.xsd b/src/share/classes/com/sun/org/apache/xml/internal/security/resource/schema/xmldsig-core-schema.xsd index df126b30e..e8288a526 100644 --- a/src/share/classes/com/sun/org/apache/xml/internal/security/resource/schema/xmldsig-core-schema.xsd +++ b/src/share/classes/com/sun/org/apache/xml/internal/security/resource/schema/xmldsig-core-schema.xsd @@ -11,7 +11,7 @@ + + + + +This package contains classes that allow the creation +and manipulation of service tags. +This com.sun.servicetag package is intended for +Sun internal use only. +

      +

      +
      Service Tag
      +
      A service tag is an XML-based data structure that contains identifying +information about an instance of a product or component on a system. +
      +
      +
      +
      Service Tag Registry
      +
      A service tag registry is a XML-based registry that contains +the service tags of all the tagged components on a system. The +service tag registry is present on systems that have the +Service Tags software installed. +
      +
      +
      +
      Registration Data
      +
      A registration data is a container of one or more +service tags that identify the +components for product registration and will be used to interface +with the Sun Connection registration services. +
      +
      + +This package contains the methods to create service tags, set up the +registration data for product registration, add service tags to and +remove them from the system service tag registry. +

      +All methods defined in this package will throw {@code NullPointerException} +if {@code null} is passed in any input parameter unless it is stated otherwise. +In addition, they are multi-thread safe. + + + + diff --git a/src/share/classes/com/sun/servicetag/resources/Putback-Notes.txt b/src/share/classes/com/sun/servicetag/resources/Putback-Notes.txt new file mode 100644 index 000000000..70e8cffbd --- /dev/null +++ b/src/share/classes/com/sun/servicetag/resources/Putback-Notes.txt @@ -0,0 +1,25 @@ +README for auto-generating of the offline registration page. + +1. register.html is defined by the xDesign team. + +2. Before putback in the workspace, we need to modify the + register.html to contain the following: + + (a) replace the pathname of the jdk_header.png image to + + + (b) replace the product name from: + Java Development Kit Version 6 Update 5 (e.g.) + to: + Java Development Kit @@JDK_VERSION@@ + + (c) replace the form action for the "Register My JDK" button with: + +

      + + (d) Add this input in the form for posting data after + the line: + + + +3. The jdk_header.png is located under /lib/servicetag directory. diff --git a/src/share/classes/com/sun/servicetag/resources/javase_5_swordfish.properties b/src/share/classes/com/sun/servicetag/resources/javase_5_swordfish.properties new file mode 100644 index 000000000..8b58c32f0 --- /dev/null +++ b/src/share/classes/com/sun/servicetag/resources/javase_5_swordfish.properties @@ -0,0 +1,29 @@ +# Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code 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 General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. + +servicetag.jdk.urn = urn:uuid:d5bed446-05f2-42ed-ba0a-153105a52413 +servicetag.jdk.name = J2SE 5.0 Development Kit +servicetag.jre.urn = urn:uuid:5c6686aa-fd05-46a6-ba3e-700e2d5f7043 +servicetag.jre.name = J2SE 5.0 Runtime Environment +servicetag.parent.urn = urn:uuid:f3c20172-557a-11d7-93d0-d6a41ea318df +servicetag.parent.name = Java 2 Platform, Standard Edition 5.0 diff --git a/src/share/classes/com/sun/servicetag/resources/javase_6_swordfish.properties b/src/share/classes/com/sun/servicetag/resources/javase_6_swordfish.properties new file mode 100644 index 000000000..982c9666c --- /dev/null +++ b/src/share/classes/com/sun/servicetag/resources/javase_6_swordfish.properties @@ -0,0 +1,29 @@ +# Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code 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 General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. + +servicetag.jdk.urn = urn:uuid:b58ef9a8-5ae8-11db-a023-080020a9ed93 +servicetag.jdk.name = Java SE 6 Development Kit +servicetag.jre.urn = urn:uuid:92d1de8c-1e59-42c6-a280-1c379526bcbc +servicetag.jre.name = Java SE 6 Runtime Environment +servicetag.parent.urn = urn:uuid:fdc90b21-018d-4cab-b866-612c7c119ed3 +servicetag.parent.name = Java Platform Standard Edition 6 (Java SE 6) diff --git a/src/share/classes/com/sun/servicetag/resources/javase_7_swordfish.properties b/src/share/classes/com/sun/servicetag/resources/javase_7_swordfish.properties new file mode 100644 index 000000000..45eebd2d3 --- /dev/null +++ b/src/share/classes/com/sun/servicetag/resources/javase_7_swordfish.properties @@ -0,0 +1,29 @@ +# Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code 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 General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. + +servicetag.jdk.urn = JSEZ9-007-ZZZZ +servicetag.jdk.name = Java SE 7 Development Kit +servicetag.jre.urn = JSERE-007-ZZZZ +servicetag.jre.name = Java SE 7 Runtime Environment +servicetag.parent.urn = urn:uuid:dc1704fe-264f-11dc-9482-080020a9ed93 +servicetag.parent.name = Java Platform Standard Edition 7 (Java SE 7) diff --git a/src/share/classes/com/sun/servicetag/resources/jdk_header.png b/src/share/classes/com/sun/servicetag/resources/jdk_header.png new file mode 100644 index 0000000000000000000000000000000000000000..011cfd4cd8aad0697b25257240ac338021f531c0 GIT binary patch literal 19005 zcmZ5{18^l#*X@mMV{YtZVoq$^wry+TNhY>!+qP|IV%vH7-uvqP^yNHPZ-269zzu*fCEYy$n*z8K{GU>e%kWQvazX4P0woF%g4s&adAmaPp^FGQLj<6 zd{3S(f-)?}ADY@As~;YQXdos?`f2M<&#T&LY1OQa^XU*Wkmsmf&oQKPS=Z{K@$vIQ zV~Iruwrb~p#?NReR}rb`?(0JKo+#*w5b!px zRztfey5J?etLCPB#9WTfmAlppJybyv2V1^CN%{(S3zG!P!U|$!EhP#wG|iCDNz*dL znv|&4DM$W$Ev!fR?QCO_H3f&`!9jv82Cs-0#rcNOulwKPq-p3&;jWf2IVA?Tl;?H7 zevGJs!+6i(wFoqw47pA-T7wUwpR~ZHfBY4bg9%nfks!)ksB>23w-QnC zPTRne*I~7*i~FiweZWs$~zKXimoCW4hNf~3{W;$1G&alZfDD3ODQ-`aREYZ<2Q=lf^ z+!eSgTez`0AP44SmWtzU{=eUm0Fn%GxYp6s^HRt7gXc@z8v(C-16Q#1YMQ15e3gJ8 z{MkqyA}+EKXPQ`M!BWtR8=3iFFd-r_@WX`IzmCM=7qBxQou!h-G&Oy+HVFOS`M=gb zo}oxh)0R`CJG}xEa{pb%<+~ZujRL*wcZlxSE?JAc$){{qP`Db2VMSn0*>70OVv32h zAlia|gOM4^(VAm0uBek!BQz4Ps^gbKj6a&!$2Qiq^&H z4^*%qfV4aRk5$OY4ngvSCREs6^8|#kl3aV1InkWNm>4)y)YnitcEpeeTWWT6lVKr1 z1`HvpQ9>00n?`Zrc!;KR5R@8ASl$kMEQ3wo||6c^Xge+jOdTFVOc^~5e=M%BkA#lkoi3D0pL zrsMl?UeELhfgxS=0oVR^gw&2E1hFc-?5+D9hqCO`LaHU$BH#BI(c*$A9s5k>=x)~U zPTsT)m4SI()954eDF)>ApWsMureR7-EkE>cLWWaVn$?PfA2cZJaEJ+vF{PgljN%fB zXlPdo7f_+@CgBQ=L5_rLI26z6#wUne5&p-vV5le{@EWJYtKy%Zs6;4ED&q%k=#`64 z>+8eafIig+phyVZEY~wtC%L{Ii#TWf#h+4FT87oiaO|=_Iho{Zn#28r^GW~)gbais z1ip7g;}nrS*X3*~?+2r0ONZn^sEjL-lJ_Aw20yf)N8L zHzV+I(65ZlQ0wGg*J|o2IY&Pp9~ARPFnJo`#Sah6NzdYbchR?NFTqEJhm#8vNQ5U) zvFofN{^#L)gxV`YUvp2=x;+c|^fy?91^WtTqTR&H;^21(2VQkjqUxSrjmdqtyPXG+ zsy8iJK~%o_%>>O4+dXH7~Z3WM1qKEIWoDeAydjK&}Ppu*S{hFHJNhs zXfqb)HiXwu#9OshEnzjWMc@f|l`{}P8Jq#%O4GxpySz-7j(o3OuEPb`kTLiAf; zXIi1V$KSW0Le`Xkg8mli)r)-zO7K5UVxba!Qf#NA6T)z=?l<8p;~z>%EuCN$w7`jJ zvc|Tb!^IRLrnnzjqnM?fPYIMffE z?>HtUO126~vrW&5Y7H3>OvMW45-^SUFF>e0%m{BH53}24|I&d{ROk8-=tM$F0y4IzL`KszPFIDC)w6pNxjEjzW|X?nES%Fa0B zvKR6H^DHv+5|#0B{X@1C3YW@FzvSq|ssC%4X~0v-?LSO~pS{_@h1t0VyLZyRcl!l= z1=zAuSO%~g@2{V~05jZm`Wn@?tAM!gX{|lk)*pW=bi{RHP?J*~bk;y3g6AVV2koni z#&uQOt@V`!ZB@lcmR(QNo8jrMT5u{34Z)^Zj>7Vw1#u}jI~B?Se_TYGy1mFLTr+(1 zHeXaYVd)Vz_oFx=>#}@U3y~M?pBk})UPXYZNX)xNU*uA{J3l@`3a|K-3%ZWIQ z(oo^WLI01pldfERahM~eW%%!7jRDhQ2#sH^zCQV!8(2Ql`Tn3WuPsFo{&u_3SV?p4 zZSks3Q(R%j;-4F*SxUyl(&n~g$isR5vs2_f9ZSITsi-C8gf9;YAD%$a@kUhalRT=Gw_%={${cF$-RM};41?17SKB3t9C{qtL@b3W?C~H z_9Jrr=9nrshMJhFAYN0Es(1+p{8zZ?QS62CSf()d|J*J9he`BT`={?vGf-G9)&1#*b-u1B_hlAW*$&no_el(`dFGHUHbiw z=&#bvYG2T2_W4TYePIEr(`Tus*|9E7lF^^oiUq?1|pKLO+&P#NI%P`>ue1tt3GUww_k z<@U$a8Ckur$w7EzJdsXA%ymw7sV)Hn#gvt+O>#k!JMlJjlPR8qvHQeSn+ie(5*xBg z?=%vRA4%3vQc8J%AGJqBN)S3+#J4flnv(2C$x{uM#C zfX&#Ks-Z*`V|zv)gue^UDBxe7KuxTWEbB^*5{T$q4_u|~Ey>$xMeSaz1B`Y(Ui)xr z;P}11-6kjd>`jo|2wKH@pu*}Q8Nl65M0eNfpR6^j`ifeVdVd5R>~JHh$u>4v0iQM* z+U=@4pn!V(8bjn4A#-ak#Ib1UWbRpm zHP6KZlniKZ`JOc*vs|to*s|*_IbnV79mh>z4bcP^pOi7m_w_$*f!q0hjNKA!a+LdB zb(E7wd;0t%2bXZ7$8EHKAV*=~sq@d|SJO=RYFyRx``&zKR#w(KAr6>-h&}`cJK?+z z4gDh<7kSN$dXdgqQpNR1+>aFz7)l0 z{b&O;C^S|?q#%trQ%|bj-`DNmF2u4nU20%UiHtn;t#UM@j`^+_FM&{!n z7|Cwr<+`xNFcr}eOiovJ@9eUzn1dm%DnIP|r*xpG9Rfk&EUu~WTC2^Hc78^xWMLA3 zA*3IzhOhe$85(b6s~f8iRS5UexzgpA5>6Y>7#5{0gh>q2-exG+qvV$&Z$JiOmEIWU zqDFrFqw+}`aVU2)5`#A=O#p5UZh~JJpjaHx8o7+6TrnU)0kXW-u>nZOYAs;^#Z=k4!12Ni6jsv zD~&=C0QnGa3kY}~Ie}W{FEQbBq8BlXo#D{+xLczaMoQ8QsaEh6IUg-6@SW5A2 zes*quUF7~cLZqTjVfQ(YxAP@^WH2!^;b#EIA@Mf1R1`e7Ss2wb&sSx*Zv~Q-Km4o~ z58A=Hn2;&u*AHu~H|)m2&Qa9m-LP9#8^uL9!FjhM2UHv`HGzfxO(9wZT|>T=j(#Vx zT_!!k!i*+ZyPD4ol~qPWx!)6b03z;Pe5#(28ZE9TPQ>bCM3`dQxw2_R5Y6@3YFW8Z$2@-av6>&J> z$Z#74f2Px|fBDkY?aRBCu8kk$EQU0BHpZOv)W}f^A(f^&1_mR9cBY{7QB_=^_qB6) z*0Z7;st3okk{BZ27vXwU$7?g{il(!CkQG?5{`F@ggHvSa%pJx(C(49C+Pqs=+XhR} zA9ef9p7(o)bi?h#1i(aXb?SR}iyFan#g|3sqj+CE#o=eUT64wy+OgS#?`Z@N z7C1VQ!fe4Juls&u+QkEfwp5~K99!pfm0K$W)q|Ax^}gEd3gn-}s5QgBb34+B zP<($(d~y$!#6}g_2X8nqj5ijy^$6&aIOk+8)G@0)YDa-!R;ciupswi$;!dL@5ODqm zT0VvOVIxJ|SdhBfw0M;KF}bC6GFX`;=P3suAvGQ($gH%$3|1%7Qa|j(uhLmeERwhX z4aF4~*W0wqmw0Fqvu#t$*bDx%hD^QNP|&<=d$vf2=ey8q4{YEu<|64_Q%BA*Z{m&&S=Wk5CrgJh%OSO0n5PMXEWX zSHoHC@MaYxH*Y{M1iseWa1Gfgai2Sik~Ik73S0~r8QLCcN4R`3Qm&idtwO~UJe*Gw>T<6SPejVoG)Z*1 z^)p#)R{-s`9(dTu-S2s`J$N~o#Xel9hV;F@{s=9jqte~BLoa|S*t_sn>Z$xe$3IPn~UMUpqq@IXCj2To7BWPd%j} z@%CU78+>-;{AB03v~)jbkgS9Pb7xGBkigggA3b{E*?&MK0IK;m(1f_mSyPbaY$}Un*&yi($rN(I`F_B@C@JFG`GZC1d{SrcxOr230##ZC+nmIsF7GYWoN1c$mA*gZsscTo5a1u0Tzu zu*}_-W-Kt6QpSHar4#S3>ep@RR}JiT)tEJNOmIk#8~R?i+aA7LL;IUPBz5Ldt-b7K zDW+@-9SoQ1rjIBMB}iI_pAn_gwpnrA$}+KefLY&f{3=Hw1<`+aRFPI|*($qaOKS-OG0;pSQ}+H-HUEdU|s3 z{3fF9fr_z7!<-s%79`^*60Vw|ugT!hSM;vf%@+q+kQUUK)#V`!)FEO~28lOV>IFN=P{tc=PD8%C z7NwFCY_p`M;{zQQmz?=6ZdqJ@fzcY*%-3XY7biP4u=B9b09 z8^x!(zI!xZzjhcwf%Hk5YHnEsdg6^gmWt0_<5Nw!h2$#3evN1{C5PPvi#Z8h`leFu zj&6oStOQGS!}WO@bVni`$)#s@HZt~IUusD?nr9+!9up3q;N`w7CZogO;xaowc>0^_ zp!S5(h^Sg~Et+49%JJbG^SL!()Emi<^aXut#*p0+k~R4EBkd+#Tt4qHds~SVvY(fY zPbb$>J5anlo>v|01YK@(C5m-bT@XAtOz~vKc#9H=jdCh_!wxuJSrE%n_VAW7Br8MnX!AlNo&rL% zOp#1%!{mpuL*tMlhECoiaBRvE<;!__7Ffl^F%QNX=W=5ja@lH0Jt^mA>!YV`8a*j- zjMX0HnY?a}P6-dy7Fmjs_u%CNPD2LUT&{eVGvGu@Se!QG0it-WKh(RAj&=0D?I5|` z{bH#Yt2&j{m!k)zhj^TKozr1}|9nv3T|gd2f3jPfxv`;S`BEsc+uOyYe>A@7K=(5T zf4=YR7KVgyu-m65YfGEQg|;K(YauU!WS9b;z@L8n3I8xSVe5i?3iHc+o!8y(VWPGN zZ>Z0Q{z)y9LsRG1)!ZLKPJPsPO1hst>S9)>*p1IC3(v3s*i!_VIklFsGpsPOV3|Uu z=!y1=I=I|d93{obXnCa0dfT)5t7G-b8ndnVZ6l=_hn|RD16!?P%e-vLUmcw*SoJz z*1fOL<|(3+&xac|5tE2ian6zwO9cU74p|FS8e7z?wgQGU1Kp1b2nFMkmCDLfBRC!{v^)v!3zfbd-V*sm!B+0gOHP zd~a)shT866e1JdE9W4vw%LlP!;BfL8?bT_p;JI0tgy8L;N0(NEL<}*Yilr4i64=ma zfyvum?<#-oXz6uaPy-LuSa2~s{ei7-xLVYF|G*K<7q?=oN$|)CB!&Z&N8qY$I1~A5=mmq5L`gppYt=Hqt)vgjr zf`p+J)V!c?eJ7bvvXXBPAplcHZV7jI8cXSUYee&Q6#8{3Z)u5pjzNoe>xbJJ4((P9 zjyg4m19XZqi;FQUvOJFyQliBdXeuS~QFg-Xe7Re~^w0s zxw?=$Cbcjo0nLohZZTDRxe`{9kduiV3%U2pEEhesnteR~y#6NO$;0f33pp_dwR-o3 zb<=7g4Qg=Ug9<}rvt5s-Fb^XHQ?5`2V?&GoJ2DmWC;ET|`CwsX9W-PNTU^5I?fy?# zEA|=|Z#S9gUGjPdrb1zx)~o+*uS^?ku3>*sF{`%FWu-`nPb@6JJ#$>DLIh372HT^R zzbt0OPH;5%amu6S_5ydoU;`LWTiVXw4Ombt`?shIV4}rUJ8C!YY{@aaf78i^1!f1$ z9Pg63?3Ko43_%NOkbIr5xw{}l}8@>qyT zPO6N??f=vN_B4o7JuJr-%u(fc-0F5S(M72hk32a!+Gce*7|&6<7br(WN-6+yfUU(f zQIs3JP{U9O6yKHRS49^sEStu24w@|&+mD4FbNNE3_n%(Wdi+G%Mg`?Zx`wxyNO$e$`5WDQ zc^04Swe7;}e^g$aME7D{j3CMn(Q!ZjW19Zs;Gp!doFcmG-y9rrkbs~R?<%4(5sW@+ zT$a)qy7_CDeB{^yW)NJmGVG9;BDsw3gD$A43Jo z`T1NiKg$3)pY^KJK@_^a4)D-Z5Re=o@=oFmg#1yrjy%g+qaGr}@=tbIGSSkY@exNW z2#t$~=Y@G>Pskw`;)1fxWsn%YNdnG!*12`7co zc;hfQ{>>;DC%T6#mStHgHL(yg)Mw->q;dGDXC>H0ZezeL08Y3I6CckYpH3iG#U#w(%h-`qsn6$%ju&178|p9xLx7=mHlL zi17FRY*JcsDLBy5SAW|Z9?1_1>!HsVmo1;4;3|f-$Pp(VQt|8U)G<@p0mM7JPD7QN zoVs2HUJ!SjnE1zmk{+sA1r7LCRVowY zp5Sxn=PhE`XkaPzF~Syqiqtg0Uz8hf2VQIE_u}cFy6rxE1C{R`pQFL$F`cVekvX*% z(l<|VY9BY1iwKi+*N0Jy>GECxH=wsI;~^-r#_K8~Tc6g#AJ|xl5ZWBH0!S*}=~&{0 z*K6!Ocl3r(#(U*7O@6i+j54^wqCoX+>WMUy9=e zl8rJKC#J5w`~jp)3doVxfn!>cpHvai3(A*$;;Nyz76$M={E@M+rm4xHaXjmFb0@jV z^77ONxzvXA8fK7S0Iz#d)-mFlTnzjmVSazAIBjARV<4YF)0$zfe~DHc^i~W^>|rK~ z%s+mee%9z#q1nL+7wRZYVN+~#rR&lO^B2!XGqS?B_O7=OT2D%I7(piW-)qceHc^w2 zv%*M)a$Vdk_@;iXSo+xXNQJlp&Rk3tv~eF)~Gz_WQ+OgJj%;8JGt1wF@VyA zMIjgMpT^E=ck*#q5GXVzhVVG#`@?uT-YAGhEESR(nYi;=8t;s*R78>Ap*$roDEGIS zC7M%J_w9bhFaG1;G#>;(L=~-? zE|%S$Ky``ju6&x%R(=zlPG+!^ob_I#sDsWx9v8?>YsX>nu{Jg+Xpe-+ctr=wN91BA z%3-g1-Ge^@XJr|L+}*~-v?;Jrpq1^9A!i3WFqLwx*Tx%@<5Q<1@HG6f&6zP(YEXHL zG(zSEtU(|C;wrOPz1Og-+BG2}9CO-Mow1-Bsw523jwXc(x{7MLLgLwQ4;nO37tl!! zZP|(zl|XXzZ|(`(dI*E*o*20v#!$u^+Ru{hhjeyX)?-UG7zj^y-Z^Gs(m0a+B@NqK zAqZwqJIK>@OXE(Wwp_sl!hul3YV)AiZNTsE=9+J!8slLGmierORyqqJ zJ;bG1Xx1~(OB!2W8=QiW)#0E<@CWCLjMh#0>x8ycg^+WWJhnvCo`$ga1Iz5#@+5x# z)DN@!L+J>YZM|yzqnM5A&!AU3kU9;#bo?Zj?JmY-^$_%ir{PDt%b&C5oywp4gFq7{ zQ0+`kBn+D&3w^d$>yr@l`q*RCi^}L`K{&5(F6uj)*~`I3>;d8KQ`!Y2eAhEgzDt`c zUd}H;C_Z=Xe4Xmqgz$jizso$DN>G$gGZ^wcFw1~Mz{mBepg_xS`^09ZC;C_SvkbUt zGSL9^95S}9`EF#lk%VXRR85w?Ghez1cElXs;%{F7Lm_~=dk^2IZq4-)@z97M)3Qv1 z{YJXB4B+40qV7>2!I)7N%oW$7$z$1=ki=g?s1-xFF=4?)2<@|8zBZ3cM#pv07xuV= z7%>Et1RToZddzx&c|DCZwvYh~5cWa{f0q2%85B7})P9~dB?KElvEg_52}Fe`sQPwB z15QwaWHvhQ<#_3)nS_Ds!V16VJ#y@u7*UGLntisgXBqb{CF$2FC^` zHUj?{7pyYHRxPX9nX0qVW8K3ja=@%PDqXk4$bc(Ys{J6xTA|?Kn#Mk7K>&q9s!%X)=-frh|FA;aUe%%;gY>Y}-{Wes5LuNJb0 z>Xxn-=HP*ImeqZb>kB_rtPq~@+}!^R#C7{w-<~Qls%usn+E%wtjG%GqV`c)&v!?a5(#$xjK^ zd|Gf%jE#+*$Kn6X_d8%CN({1i@C&&0sefk%-OagyG|&y0V6$V98uH%~m4>D!!meEt z)yM~4k-S~|6oS-nI1hC!M=dESN_-~7h56?+wR6)PJcl`g@hpaHYnGD@f$dJyvSbuP zGZ8Ogtlj(pPUKr>(XX5eq=aF z+vRB#mCOB9-0lAmGz;N9T__gQ)?i}h4@d#G;TvG=1ZHyS3m(D)bN7Q?TMp4@nh|_w z8YVoXp#Cm`WIG&jveTg8Zd?l^eNVOX0t#kU2M^pwd!i&CxD4ib1BhXI#rJ2mk**TAT<&;YD%{#OP0q~=cBU-J_1r}wYz)JR2C}iDo6Bw zE*r2Wv!brPK93FQ!7j84m?m!;{(~Q#olMx;xveNv6KBTzFpVRhJkpL)T8nh7UB#FN zG-g~f0)%=O5(GNYo}Mf`8hMeuv4*#J?RC(qc?zH1`8;M%Z^MH#2wZz^W+6l#rWcmK zP|gh)ci&66)YfURc3he-*>!UaN;br9Brhb%f;uK%Ze;Bt?75yTf~D* z&O<^nG^L<$`Fe}acwp8ZeT{;~=3=^CJ3VO7vaDts(a?$uS4LS=Q-}x@UDb7>S z4Wt=rmPHEge}Bi4DRkO1Cd$c`J2)8-zNOu6Bu}*O$9$$9LBv{;=%VNq-32K4 zyOa~m<2;-p;B&W+-PsQ^bNs-tLIl2G-K(qqh_hgZuG=9{3=srgOL!td0`=wdU}}uL z^TPtH{8tXxp15tfjyC6OnXhZEEMBwYxCd7$1d6pYBarnw*zx8*E! zVb}BYhf9o=I=Yb9H@PQz|kn z|K#3+)LfRAvHr@fR9rcns%OY$5eo1IuM|UkCpAJxJy2;(X&F5GGd|wCKr|7>JoNb% zV9EGXw?zRhCH1$;XxYzpTH(;HGT>pcrxM}o@d%qvD&$04vLPb0Npx)O( zF59x|`n1nz(i@QNN_O~O{+6nJwAG^|6eW*QoqIvUp@nq!B-epyTq^!~^f34DL0?%Y zoVtdJHgn0iXNg)>t7T8pev6dKN(x+YQLV=Gxk96@?c+Dj-eqgv-$pzDQ~}hi%_3P^ zw-Q$!8{aYpt0{*_b1h&D}itk87?J!#f=SAN-P z3A;lY@)&8r0kFq=R1L^NHWI)N)cb~(C6)%~X<4WqkXmA!N^I$B&?q#h7vC&b43g{| zR<>%@Iph7jETFgF?)9hq4S)F8Hhl-wBBTgP_)Meg8%PEmJc`)eWa`o-_k`9nQ5e%n zhk*Dg7JX;;*UAxWc5!tHwTz#@7%Wosp~-oBp<+4^OnzhIT)L!IUGNto?(E_>WwOFZ zXuV#jDEw~V90ox&jW+|8GE6mb<_hflfF<&CppBeGxhK)}&QY(o5IPDNo|J?VCz@)B zgNw>yP^MP~JKX&fM}9as5@3BbyiXy==#Lt;a-0I6LJ_B#Lh7>XbpUmq{gP;H`zuIK zgxOw3Bx6Q2ptSCfokG*~^KIe3f7U=xgK0c^9C~t@6wxM0bUv7MFZ?}Y`9$-vSIjoq z6KTI1)C|rh^xCNQmvA$qYu#2Rw9BJI7r z)@c*zo)@KPxLxhVp|4=;xfs71l1*}pU*lWCGR0)jz>{2)&Y0PTP&~Qd1sWNz6E==F zyk^orFGMhP5c~R=N+8!N4-MEQJ#cVIdY5fAUMv||pPKFxfgd*1(H?-98&`_^SMYiS z<)f2#VUPkr11WmlrGR{H36c=`($&$wY+o79Q zy^Wr)ELR_ENG;oKEnCwQrBx0;git#Mt$G(Qu7{U8tkB-P^i0eySnRP&p3A&BI5=HJyu^o$r%iTG_)7FFc&%4P);P}A5&l- zotl%g<7i7R)6WzJVUB1cLTR;V8s*)4tTM|PA_g!b1w2r$TGf{!`#77a7MWVoKYbKE zL$i@|%-lyHDb_73A(K5ptzxfIJ*6c{5K<>;zz*qkNw43Jmb-%yx`FYv0Hv9%i9xUf7gS+EP(WC-ih3K zctFJu1JJwd$e0|~Y~IgF0iQxAS493CkDq$mjY?h-Uu}WC`Qd=>R0{1^I+7eo7u*8i z#ZA}B_IN@=g|c0`iB%GZ22DVJa3uTV7ry(;wBF<*`<#RF)a=KV2PhXI{X(#z@IeHs z$RX^Vqj>E&yDBmgpqg>Xtt!j$8|QkiBcd>hKBHg;g$WiMhfT10#+!M!$j8X=GRqAg zQxR!v%&NQ?1mkFXkVf`c0A|#NQ>XRG@PW^e%+98($n+0u6Tcmi{rr5N(9XfzoF2g$ z+sUB7CcR;?Rz@lLy&ze&Fk=jQwmL`MTS3$cofQ>+3eZ%S{`)$ps_cLtNij;8 zttI6f;>iX6wDlG)gzV=Zi4@uxhIhY?j54?*grXLRaVVH@;3|wgjgr<7$0QigC`bmr z^%g141Fo&7Ew{h2{U1aIA(MG2A)Fv5y=onP?%)=!)*+-vbKS3 zZ}Qun9_m!#ahh{*8*9ai(!9S}YF@GiX#k%wUMzwI$l3FGIKzPALMLt0SuMw=eHSt@ zx~L!!6NFh{pt9~9nPQgTGw#dQAgR#j2IC8KTT|7fxsfjMWE;8b-4XPYJ1QESU_Fmp zE{u%|DiL}RF6TeK9idQ!myjm%r@M|&^+x2k1C+|1{4o!0WV5QKu|9)tYA$V>*=bo~ z=j*0CRz)&Aj6n6e_76=U@4K>pBlNcF*=Ma8j}F;NJexuc$B&V0ij8nt0sn?GGR}UU z%2)(G_Te!<)FXFf>4%|AiWZW&ul(|c_sCy!2YC;I9=NYfTzX<83CSs8!^dFY?w2iv z2mG31-P^C7#sj{+S)NPJ-DCWon?-w|uUxU`G21V0_8GnUxr zghh_Jsu#=!auaN+FV-|kdtHadw#*_$ zIFH9dVbVO`bp9C#2DxKadvLoc*I7SxNN(sX-I?3ENDh$(zT+;A=nRK>xe#*T?)>QG za;<1BCJGx&y1p~JkL;2TvzuS8;~|qz_H;vS*6`RR$@_oQwy>;M+d}cGc7l!;Fpx>8 zNZHy61x7BUhEV%iC`gSf;f?X@j#o8#G355NvfEMlVQC>r-_Z2Fv@z;03&5k(?u4*V7st%3QtjMlHm7VqN)>Nce-vCKf7amWOqV)eguA&k6TnI^WGL!b> zWvG4LVI_gafbPU)rUtk}gHGV`y_b#VhoEEdM@)CzF#-tFhBv$2w!XHUWd4P}is@=X znw!4IbQy)BlPGW0U73rDhVWj|CP0?X1XkC@5kFUM`#cX~;Mt|HdG=j{v?I}1SVfF@ zXHwXL2S>7su%H%brRDL#zLNPoj*c^88aK{vjO%sQ?LA6Ocl#UV`=~UYx#D|UT{NwC zJM+4rhppB>@F)I_VpCn=t@Y3kkEyHXtJRfqf1zR>}Hz&3d+K`1AwxHV`Ke;=%tI} z68^1kU$p!3vcx z0?{ja=S+rqtSZGli+A+ox_0~HLWtibB5wk_kl+T+`}1J>54?Ro$)=v)-T1fE%jmnB z-mP8Jp%vnJRsk1+M49nLAuNT<&qEWEQ29hW)leat%InFXk5yvvc@b>~gQ6qYMzk0N z>UJ|uE|teL_L&dxZZ*3kElKyg-H0ndl}r<&Ds*UHav2Ol@vj0H5oL*@_&!9@z_pq2 z-Ca_6XEgj)Y>pq~Ykg4ck?Z#KcuD#g_Lk?^cLlna$bms~-`^XTEUW_42!rb-yryyc z(TR{^*FtqdEl>U4UPGZ%OyxMRk_SQbL38c~`Vv*%Q$Rb_B$^%vpndcji!}+nKRa>q zAwdLje-#j1zHV+{uDTq!zf}GHW_i(WV8MMo247S6L$XTj0;@TCpyX)>O!+O!o6hk( zWd&qKA?*cGK2W4Br*)fFz>ncIjlDbvmP5UvpgeObNy=PWr>HFFJRQ3I zm5y3Yv=BK0<`sUaH4m^?fU>2vtxoe@j(&4mJ_1!-p|Wvw^<|}ekHj??xOK2(T60up zsT3SHi$*;XcY_e{G!w2)>lfc3@U-aeyEW>Q^?cx;x7%0&&At#Tg-FlB%tTUwiCNLZ zY0}452y8M?D*Eh0=6t0BOU?Q?x!O*k^hsPoQImsIIe-a(3m;imSXiuZMZf^oLqku4 z(4O%8!fXwWu+@(wHe>z%Ep=4wID|m> z{q;V~(;ubeg&dZtydjgn5BO#7%qiWdlwkhT^fZ5wdm(bJjT8kZJ&lQEL+}!$-O6ru z)IbplokAKu31Vq&3Gtq*{`X?`BHC?w#DVJ)aq+mA7yzHfXgO0w!uk!f&la}0h|R3R z7LY0v7`<8M`oA284Xs=8vtlx`;}M@rsfg>9RF%h#rP1pjmCF&tVOPYZG&i*#)dXL< zyoE#pAu}7Y-B2i=(zG%fLfr~>&K8!m9AT~1C>+)1!ae9T+kN_Koa&+;Vf>3L-^zFF zBAi$LqJRIqvcAeO3{T`T?g#h(?jL-_HJBrJ!1OTpYLoKZigSPXw)xG5y1KYLuwl#Z zri|`%B%@Y?YIk(f??$7Q-r(#@ZWdqjZjJrW?vA{Uy^)()j*RmOc=vgp zUq0VNxhO?EizWZnTF7Bqo2SEy?Sen3JEzA%4op83cc*(4whl}%tRwR)#yx=duKmcc zjYvg+i$zBAmVI>F>`OzQ%P+T@e=(p5tRBXiT>b$|etp{aOH^xHG4z>3+39w6nRjGd z5-p@byWLXzXOALZ+iD%;TStITxopP7C_FnGtDfs;&40qEb|1fQg1(+2LrX)e`#X^b z6nsaUG5`SL#~1J96%{xo@p7<0V&;04eD z0fJ&X*OmUu2n6yM6jgP3FZXVWl`UJZLuE_;@dSZzg_m*w*&!8&E)J4_25*F-`!LT?{xYEqp~&TMHM^|)kiPj?Vs`5wtCQ)k`!L?^BzTLR z7VdOeGEr@R5t*`k3%`p0`SxBednUaa#lqr?kMbmRFS@>uRgwz)Z&xi_jniS+rT}`Z z3wg9@grwc^5t-9Q7&epXzfomdUDcM_(ud9{FFZNcD^6{lt|JEDej{l>?>U!t6myIv z9`5#swHg~Ax3S&g5y0nfw)Oi9oD>M#rqjQHZ#f_h4b|9`xZmTZOSi8HXHe_cYbvt8 zaHXAPz!Oq;4fp~OnIL35WHuUk0(#gVT6l8-W ziISEX&=$+jR_1{1c51Od`Eu)OYMYv1=DdB+q>1)oEfLD6R#S)7^9`< zWM-yf2y5E+f#7r!?rI-2ZpXFg7 zrbb`AAd29%aLfyg9Q$J^19|J zX}vLNwL+cbtIY-?L&AtQOVxOtMKG-vM+BXM%(B3}_gpltg#O3SL22Pn8FI4S^IyLa zh>2kqXn$oo601+0X0uqyerEtyX(gJgrHqr*4W`1w2t%x)wW%r%5-CbDyO5&RW=oYv<<_fgC<`eSkJV{IY7j>gSqfxQ>8|9W`Y5 zT~{-=wzRc1;o-;5aeJcuUqzgGI27C$$CXHAiLwis8O9njq6rn#FqUDaVTfXE6J{_N zB`Rz7ZR`;lS;ELtvSeSfuh}LkV-5AzPh{)$)AQ)&U~!hxud)5E>vD>bu8Z#9bT6^5h6r2Nio|yI%>n574+yqkEt6I^ zU|ogQ(e@bmH^(Qw`0MYLars#svf&mJ_o2PkjT^H<)J3%dBucoQw|0F@oth91Kc`yW zVoCQWO?SR zbYlq1(}^!9BUBj_s$%tHRQNN7i{Zk{NNcurV{ndzz)m9y64Kjf)2vgw=ZuzKE-j!XR3_h#-rBklS z&#B5MQ2Z&$1BobQi^86nsrICC^S5)p@YzEB_0GUhr&~eJ+)}lQ9}>#N^V<48$KG|o zgAQ5B;Is16@0n5Y)qw2%+5WqEac|~D#aI$!c>s9FWctz3+k2MyHo~_kM6%1ICB6&& z*Hnwub>bIoJLK7H(>i;!{jf044)e?c8tQOM(j;21p&CKs&w&U=BUGEz^~e?JVLVRR z&L`#TXC@z@b<_%rTtlPRlHVD#sb;h?Tvd;-oNG!>^Q}kE7g0EiK*q-eAm(a37(s8` zzCv$sq!_}Z?-azuCc>>si9C>zom%W^1x!0~BSyiXP`H%g7$f4`tZj1YCpVD^cU@~0cT_{S~|C>qCz40725MYtetc;%x)J!)6mszJyNqMRZUMXzggo@JfH4Z zBH|~%BBK92OzYLaeZKI4g}brnOT6#g1&9bO^GriYv70}1jM@f}}@=HFkHCg&C9WlC< zC;qXEy%Qq%up7Yg5&S+IN!&;;Mto1wyeZxRiHebX3!;v-_j|8((BRC`ciW=SXIFf- zbUrBiY+b77iqsHQ?uV!e`!`@wk{9*C#OITMd@P)hM0W;qEuD{Ec;aMc!=g8Q;evvK z;H+6vZnxYZeNgTEfNFaxK*iwzJAvM0xlLZ%~? z?0ak(6Ig;U9Czax=Ka8)NfL*pJlcqjkm|Sbv?KZF1Gkp+gCd^7_!M zIC+;~$;3^5by;M&w|4w7eF5(rX6N(@rN_2{trYUf4l@eKEbh>ltn`u>PY0|gec=dX zxZNuiZm5XLiy)D6Yd8+k{mO8LT}Wfipcx3JN~h?5av%roX5Y=LEI@IGJuTn~!|M3; zGjPu4B7A39W#*rjUDUb>$Q`INu+D6C_OC*Ltw;LqX;Nw92*nXl3BEghISxi0<7=qc zK56E=sK7tpD{#Q^k24&c8EG>av|HI60 z4#AOUHv8`XC{tOrwY~fB*J_D|e?cxU^l{R0X{5OYG#+dWhL&n-6~)vzJ)jhVglru* z$8K|*r3Dy!h?YO$;6Sz9H@1X*c5_XhN7_&^2uYX1D6Fdp#7nm0KjhkE4?1*N8`*Bk zb_w_2=m+F8udxmpkMvv4aevSJFS-dqs|W>Y`o{{&R1x^v(1xg)&*ykJ$37=y+tliA#8Bkr;|FE3+ck+Qq1L0|7(W}cQ0eTHrtJBCcuh+q2q!*#~ zk;Ha!ph10BJtEQXPJn)Ci$b|~h+=o;>w-sf$5i8E+|o#N)>BF2DJr|)PiX^BW3B)uKE^^JfM6%w?U85 z&z6H{)~ENEC`m6II^Y8bF`YoO2>KZmru~q-BpyvGf_$!zZ$evHd-zts;FOO=2R+z> zzL)|135{$OfzvZ_7K&udJm7}CwXKy@;&kqBXkPamA&+&p(zV-tL;_23B~8|qYo%-^ zc&!?0UwsZq$&ezO9Hgwj<*hFZ-vRKrACM-yne|QfX;|@lyDd303~N7H2uHKIt&YY8 zl7~%IwAy%!93d@`px#CR4-!<~6V#WwRVDuo>qtT>Jo0PX-=5%V;4e+E_YS_K2x$RP zLx2A;%I1^%jBPJQE{Z}gXADzHWu+xx{!&c|u>+$-NMT;sN@|vGs!%|8#@CFIs!=DX z(&4Swon3kB_R|T5wanU!%&cq(&BuGAQH)jM@^E2N zk3_F!p!Ajq?voYFNGiVf}g`$KW;iZ6nZKlf-x%bTc`Ox + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/share/classes/com/sun/servicetag/resources/register.html b/src/share/classes/com/sun/servicetag/resources/register.html new file mode 100644 index 000000000..23e9d717a --- /dev/null +++ b/src/share/classes/com/sun/servicetag/resources/register.html @@ -0,0 +1,105 @@ + + + + +Register your JDK + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        +
       

      Thank you for installing the + Java Development Kit @@JDK_VERSION@@ + from Sun Microsystems.

      +

      Registering your product will give you the following benefits:

      +
        +
      • Notification of new versions, patches, and updates
      • +
      • Special offers on Sun developer products, services and training
      • +
      • Access to early releases and documentation
      • +
      +

      Product registration is FREE, quick and easy!

      +
      +

      All you need is a Sun Developer Network or other Sun Online account. If you don't already have one, you will be prompted to create one.

      + + + + + +
      + + + + You need to be connected to the Internet to register this Sun product.
      +
      +
        +

      Sun Microsystems, Inc. respects your privacy. + We will use your personal information for communications + and management of your Sun Online Account, the services + and applications you access using your Sun Online Account, + and the products and systems you register with your Sun Online Account.

      +

      For more information on the data that will be collected as + part of the registration process and how it will be managed
      + see http://java.sun.com/javase/registration/JDKRegistrationPrivacy.html.
      +
      + For more information on Sun's Privacy Policy see http://www.sun.com/privacy/ or contact privacy@sun.com.

        
        
      + + diff --git a/src/share/classes/com/sun/servicetag/resources/register_ja.html b/src/share/classes/com/sun/servicetag/resources/register_ja.html new file mode 100644 index 000000000..56ee84f54 --- /dev/null +++ b/src/share/classes/com/sun/servicetag/resources/register_ja.html @@ -0,0 +1,91 @@ + + + + +JDK 製å“登録 + + + + + + + + + + + + + + + + + + + + + + + + + + +
       
       

      Sun Microsystems ã® Java Development Kit @@JDK_VERSION@@ をインストールã—ã¦ã„ãŸã ãã€ã‚りãŒã¨ã†ã”ã–ã„ã¾ã™ã€‚

      +

      製å“登録をã™ã‚‹ã¨ã€æ¬¡ã®ã‚ˆã†ãªç‰¹å…¸ã‚’å—ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚

      +
        +
      • 最新ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã€ãƒ‘ッãƒã€ãŠã‚ˆã³æ›´æ–°ã«ã¤ã„ã¦ã®é€šçŸ¥
      • +
      • Sun ã®é–‹ç™ºè€…å‘ã‘製å“ã€ã‚µãƒ¼ãƒ“スã€ãŠã‚ˆã³ãƒˆãƒ¬ãƒ¼ãƒ‹ãƒ³ã‚°ã®ç‰¹åˆ¥è²©å£²
      • +
      • アーリーリリースãŠã‚ˆã³ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆã¸ã®ã‚¢ã‚¯ã‚»ã‚¹
      • +
      +

      製å“登録ã¯ç„¡æ–™ã§ã‚りã€è¿…速ã§ç°¡å˜ã§ã™ã€‚

      +
      +

      å¿…è¦ã«ãªã‚‹ã®ã¯ã€Sun 開発者å‘ã‘ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¾ãŸã¯ãã®ä»–ã® Sun オンラインアカウントã ã‘ã§ã™ã€‚ ã¾ã ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒãªã„å ´åˆã¯ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ä½œæˆãŒæ±‚ã‚られã¾ã™ã€‚

      + + + + + +
      + + +
      ã“ã® Sun 製å“を登録ã™ã‚‹ã«ã¯ã€ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒƒãƒˆã«æŽ¥ç¶šã—ã¦ã„ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚
      +
      +
        +

      Sun Microsystems, Inc. ã¯ã€ãŠå®¢æ§˜ã®ãƒ—ライãƒã‚·ãƒ¼ã‚’å°Šé‡ã—ã¾ã™ã€‚ ãŠå®¢æ§˜ã®å€‹äººæƒ…å ±ã¯ã€ãŠå®¢æ§˜ã® Sun オンラインアカウントã€ãŠå®¢æ§˜ãŒ Sun オンラインアカウントを使用ã—ã¦ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã‚µãƒ¼ãƒ“スã¨ã‚¢ãƒ—リケーションã€ãŠã‚ˆã³ãŠå®¢æ§˜ãŒ Sun オンラインアカウントã§ç™»éŒ²ã™ã‚‹è£½å“ã¨ã‚·ã‚¹ãƒ†ãƒ ã®é€šä¿¡ã¨ç®¡ç†ã«ä½¿ç”¨ã—ã¾ã™ã€‚

      +

      登録ã®éš›ã«åŽé›†ã•れるデータやã€ãれらãŒã©ã®ã‚ˆã†ã«ç®¡ç†ã•れるã‹ã«ã¤ã„ã¦ã®è©³ç´°ã¯ã€
      http://java.sun.com/javase/ja/registration/JDKRegistrationPrivacy.html ã‚’å‚ç…§ã—ã¦ãã ã•ã„。

      Sun ã®ãƒ—ライãƒã‚·ãƒ¼ãƒãƒªã‚·ãƒ¼ã«ã¤ã„ã¦ã®è©³ç´°ã¯ã€http://jp.sun.com/privacy/ ã‚’å‚ç…§ã™ã‚‹ã‹ã€ãŠå•ã„åˆã‚ã›ãƒ•ォームã‹ã‚‰ãŠå•ã„åˆã‚ã›ãã ã•ã„。

        
        
      + + diff --git a/src/share/classes/com/sun/servicetag/resources/register_zh_CN.html b/src/share/classes/com/sun/servicetag/resources/register_zh_CN.html new file mode 100644 index 000000000..c47929fb7 --- /dev/null +++ b/src/share/classes/com/sun/servicetag/resources/register_zh_CN.html @@ -0,0 +1,92 @@ + + + + +注册您的 JDK + + + + + + + + + + + + + + + + + + + + + + + + + + + +
       
       

      感谢您安装 Sun Microsystems 的 Java Development Kit @@JDK_VERSION@@。

      +

      注册产å“åŽæ‚¨å°†èŽ·å¾—å¦‚ä¸‹å¢žå€¼æœåŠ¡ï¼š

      +
        +
      • 获得新版本ã€ä¿®è¡¥ç¨‹åºå’Œæ›´æ–°çš„通知æœåŠ¡
      • +
      • 获得有关 Sun å¼€å‘者产å“ã€æœåŠ¡å’ŒåŸ¹è®­çš„ä¼˜æƒ 
      • +
      • 获得对早期版本和文档的访问æƒé™
      • +
      +

      äº§å“æ³¨å†Œæ˜¯å…费的,å³å¿«é€Ÿåˆè½»æ¾ï¼

      +
      +

      您需è¦å…·æœ‰ Sun å¼€å‘者网络或其他 Sun è”æœºå¸æˆ·ã€‚如果您没有,系统将æç¤ºæ‚¨åˆ›å»ºä¸€ä¸ªã€‚

      + + + + + +
      + + +
      您需è¦è¿žæŽ¥åˆ° Internet æ¥æ³¨å†Œæ­¤ Sun 产å“。
      +
      +
        +

      Sun Microsystems, Inc. å°Šé‡æ‚¨çš„éšç§ã€‚我们会将您的个人信æ¯ç”¨äºŽé€šä¿¡å’Œ Sun è”æœºå¸æˆ·çš„管ç†ã€Sun è”æœºå¸æˆ·è®¿é—®çš„æœåŠ¡å’Œåº”ç”¨ç¨‹åºä»¥åŠç”¨äºŽä½¿ç”¨ Sun è”æœºå¸æˆ·æ³¨å†Œçš„产å“和系统。

      +

      有关注册过程中收集的数æ®ä»¥åŠè¿™äº›æ•°æ®çš„ç®¡ç†æ–¹å¼çš„æ›´å¤šä¿¡æ¯ï¼Œ
      请访问 http://java.sun.com/javase/registration/JDKRegistrationPrivacy.html。

      有关 Sun éšç§æ”¿ç­–的更多信æ¯ï¼Œè¯·è®¿é—® http://www.sun.com/privacy/ 或与 privacy@sun.com è”系。

        
        
      + + diff --git a/test/com/sun/servicetag/DeleteServiceTag.java b/test/com/sun/servicetag/DeleteServiceTag.java new file mode 100644 index 000000000..6dda06b61 --- /dev/null +++ b/test/com/sun/servicetag/DeleteServiceTag.java @@ -0,0 +1,129 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6622366 + * @summary Basic Test for deleting a service tag in a product registration + * @author Mandy Chung + * + * @run build DeleteServiceTag Util + * @run main DeleteServiceTag + */ + +import com.sun.servicetag.*; +import java.io.*; +import java.util.*; + +public class DeleteServiceTag { + private static RegistrationData registration; + private static File regFile; + private static Map stMap = + new LinkedHashMap(); + private static String[] files = new String[] { + "servicetag1.properties", + "servicetag2.properties", + "servicetag3.properties" + }; + + public static void main(String[] argv) throws Exception { + String registrationDir = System.getProperty("test.classes"); + String servicetagDir = System.getProperty("test.src"); + + File original = new File(servicetagDir, "registration.xml"); + regFile = new File(registrationDir, "registration.xml"); + copyRegistrationXML(original, regFile); + + // loads all the service tags + for (String f : files) { + File stfile = new File(servicetagDir, f); + ServiceTag svcTag = Util.newServiceTag(stfile); + stMap.put(svcTag.getInstanceURN(), svcTag); + } + + // load the registration data with all service tags + BufferedInputStream in = new BufferedInputStream(new FileInputStream(regFile)); + registration = RegistrationData.loadFromXML(in); + + if (stMap.size() != files.length) { + throw new RuntimeException("Invalid service tag count= " + + stMap.size() + " expected=" + files.length); + } + // check the service tags + Util.checkRegistrationData(regFile.getCanonicalPath(), stMap); + + // delete a service tag + deleteServiceTag(servicetagDir, files[0]); + + System.out.println("Test passed: service tags deleted."); + } + + private static void copyRegistrationXML(File from, File to) throws IOException { + + to.delete(); + BufferedReader reader = new BufferedReader(new FileReader(from)); + PrintWriter writer = new PrintWriter(to); + try { + String line = null; + while ((line = reader.readLine()) != null) { + writer.println(line); + } + writer.flush(); + } finally { + writer.close(); + } + } + + private static void deleteServiceTag(String parent, String filename) throws Exception { + File f = new File(parent, filename); + ServiceTag svcTag = Util.newServiceTag(f); + + ServiceTag st = registration.removeServiceTag(svcTag.getInstanceURN()); + if (st == null) { + throw new RuntimeException("RegistrationData.remove method" + + " returns null"); + } + if (!Util.matches(st, svcTag)) { + throw new RuntimeException("ServiceTag added in the registration " + + " doesn't match."); + } + // check the service tags before storing the updated data + Util.checkRegistrationData(regFile.getCanonicalPath(), stMap); + + ServiceTag st1 = registration.getServiceTag(svcTag.getInstanceURN()); + if (st1 != null) { + throw new RuntimeException("RegistrationData.get method returns " + + "non-null."); + } + // Now remove the service tag from the map and store to the XML file + stMap.remove(svcTag.getInstanceURN()); + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(regFile)); + try { + registration.storeToXML(out); + } finally { + out.close(); + } + } +} diff --git a/test/com/sun/servicetag/DuplicateNotFound.java b/test/com/sun/servicetag/DuplicateNotFound.java new file mode 100644 index 000000000..d84352085 --- /dev/null +++ b/test/com/sun/servicetag/DuplicateNotFound.java @@ -0,0 +1,97 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6622366 + * @summary Basic Test for RegistrationData.removeServiceTag and + * updateServiceTag. + * @author Mandy Chung + * + * @run build DuplicateNotFound Util + * @run main DuplicateNotFound + */ + +import com.sun.servicetag.*; +import java.io.*; +import java.util.*; + +public class DuplicateNotFound { + private static String servicetagDir = System.getProperty("test.src"); + private static String[] files = new String[] { + "servicetag1.properties", + "servicetag2.properties", + "servicetag3.properties" + }; + + private static RegistrationData registration = new RegistrationData(); + + public static void main(String[] argv) throws Exception { + ServiceTag svcTag; + registration.addServiceTag(loadServiceTag(files[0])); + registration.addServiceTag(loadServiceTag(files[1])); + testDuplicate(files[0]); + testDuplicate(files[1]); + testNotFound(files[2]); + } + + private static void testDuplicate(String filename) throws Exception { + boolean dup = false; + try { + registration.addServiceTag(loadServiceTag(filename)); + } catch (IllegalArgumentException e) { + dup = true; + } + if (!dup) { + throw new RuntimeException(filename + + " added successfully but expected to be a duplicated."); + } + } + private static void testNotFound(String filename) throws Exception { + ServiceTag st = loadServiceTag(filename); + ServiceTag svctag = registration.getServiceTag(st.getInstanceURN()); + if (svctag != null) { + throw new RuntimeException(st.getInstanceURN() + + " exists but expected not found"); + } + + svctag = registration.removeServiceTag(st.getInstanceURN()); + if (svctag != null) { + throw new RuntimeException(st.getInstanceURN() + + " exists but expected not found"); + } + + svctag = registration.updateServiceTag(st.getInstanceURN(), "testing"); + if (svctag != null) { + throw new RuntimeException(st.getInstanceURN() + + " updated successfully but expected not found."); + } + } + + private static ServiceTag loadServiceTag(String filename) throws Exception { + File f = new File(servicetagDir, filename); + return Util.newServiceTag(f); + } +} diff --git a/test/com/sun/servicetag/FindServiceTags.java b/test/com/sun/servicetag/FindServiceTags.java new file mode 100644 index 000000000..e9bc09479 --- /dev/null +++ b/test/com/sun/servicetag/FindServiceTags.java @@ -0,0 +1,133 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6622366 + * @summary Basic Test for Registry.findServiceTags() + * @author Mandy Chung + * + * @run build FindServiceTags SvcTagClient Util + * @run main FindServiceTags + */ + +import com.sun.servicetag.*; +import java.io.*; +import java.util.*; + +// This test creates a few service tags in the Registry. +// Check if the findServiceTags method returns the expected ones. +public class FindServiceTags { + private static String registryDir = System.getProperty("test.classes"); + private static String servicetagDir = System.getProperty("test.src"); + private static String[] files = new String[] { + "servicetag1.properties", + "servicetag2.properties", + "servicetag3.properties", + "servicetag4.properties", + "servicetag5.properties" + }; + + private static Registry registry; + private static Set set = new HashSet(); + private static Set productUrns = new HashSet(); + private static int expectedUrnCount = 3; + + public static void main(String[] argv) throws Exception { + registry = Util.getSvcTagClientRegistry(); + + for (String filename : files) { + File f = new File(servicetagDir, filename); + ServiceTag svcTag = Util.newServiceTag(f); + ServiceTag st = registry.addServiceTag(svcTag); + + set.add(st); + productUrns.add(st.getProductURN()); + } + if (productUrns.size() != expectedUrnCount) { + throw new RuntimeException("Unexpected number of product URNs = " + + productUrns.size() + " expected " + expectedUrnCount); + } + if (set.size() != files.length) { + throw new RuntimeException("Unexpected number of service tags = " + + set.size() + " expected " + files.length); + } + String purn = null; + for (String urn : productUrns) { + if (purn == null) { + // save the first product_urn for later use + purn = urn; + } + findServiceTags(urn); + } + + // remove all service tags of purn + Set tags = registry.findServiceTags(purn); + for (ServiceTag st : tags) { + System.out.println("Removing service tag " + st.getInstanceURN()); + registry.removeServiceTag(st.getInstanceURN()); + } + tags = registry.findServiceTags(purn); + if (tags.size() != 0) { + throw new RuntimeException("Unexpected service tag count = " + + tags.size()); + } + + System.out.println("Test passed."); + } + + private static void findServiceTags(String productUrn) throws Exception { + Set found = registry.findServiceTags(productUrn); + Set matched = new HashSet(); + System.out.println("Finding service tags of product_urn=" + + productUrn); + for (ServiceTag st : set) { + if (st.getProductURN().equals(productUrn)) { + System.out.println(st.getInstanceURN()); + matched.add(st); + } + } + if (found.size() != matched.size()) { + throw new RuntimeException("Unmatched service tag count = " + + found.size() + " expected " + matched.size()); + } + + for (ServiceTag st0 : found) { + ServiceTag st = null; + for (ServiceTag st1 : matched) { + if (Util.matches(st0, st1)) { + st = st1; + break; + } + } + if (st == null) { + System.out.println("product_urn=" + st0.getProductURN()); + System.out.println("instance_urn=" + st0.getInstanceURN() ); + throw new RuntimeException(st0.getInstanceURN() + + " not expected in the returned list"); + } + } + } +} diff --git a/test/com/sun/servicetag/InstanceUrnCheck.java b/test/com/sun/servicetag/InstanceUrnCheck.java new file mode 100644 index 000000000..b0736dd59 --- /dev/null +++ b/test/com/sun/servicetag/InstanceUrnCheck.java @@ -0,0 +1,76 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6622366 + * @summary Basic Test for checking instance_urn + * @author Mandy Chung + * + * @run build InstanceUrnCheck Util + * @run main InstanceUrnCheck + */ + +import com.sun.servicetag.*; +import java.io.*; +import java.util.*; + +public class InstanceUrnCheck { + private static String servicetagDir = System.getProperty("test.src"); + private static String[] files = new String[] { + "servicetag1.properties", + "servicetag2.properties", + "servicetag3.properties" + }; + private static RegistrationData registration = new RegistrationData(); + + public static void main(String[] argv) throws Exception { + for (String f : files) { + addServiceTag(f); + } + } + + private static void addServiceTag(String filename) throws Exception { + File f = new File(servicetagDir, filename); + ServiceTag svcTag = Util.newServiceTag(f, true /* no instance_urn */); + ServiceTag st = registration.addServiceTag(svcTag); + if (!Util.matchesNoInstanceUrn(svcTag, st)) { + throw new RuntimeException("ServiceTag " + + " doesn't match."); + } + System.out.println("New service tag instance_urn=" + st.getInstanceURN()); + if (!st.getInstanceURN().startsWith("urn:st:")) { + throw new RuntimeException("Invalid generated instance_urn " + + st.getInstanceURN()); + } + if (st.getInstallerUID() != -1) { + throw new RuntimeException("Invalid installer_uid " + + st.getInstallerUID()); + } + if (st.getTimestamp() == null) { + throw new RuntimeException("null timestamp "); + } + } +} diff --git a/test/com/sun/servicetag/InvalidRegistrationData.java b/test/com/sun/servicetag/InvalidRegistrationData.java new file mode 100644 index 000000000..b68f5ee04 --- /dev/null +++ b/test/com/sun/servicetag/InvalidRegistrationData.java @@ -0,0 +1,65 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6622366 + * @summary Basic Test for invalid product registry + * @author Mandy Chung + * + * @run build InvalidRegistrationData + * @run main InvalidRegistrationData + */ + +import com.sun.servicetag.*; +import java.io.*; +import java.util.*; + +public class InvalidRegistrationData { + public static void main(String[] argv) throws Exception { + String servicetagDir = System.getProperty("test.src"); + + checkRegistrationData(servicetagDir, "missing-environ-field.xml"); + checkRegistrationData(servicetagDir, "newer-registry-version.xml"); + } + + private static void checkRegistrationData(String parent, String filename) + throws Exception { + boolean thrown = false; + File f = new File(parent, filename); + BufferedInputStream in = new BufferedInputStream(new FileInputStream(f)); + try { + RegistrationData regData = RegistrationData.loadFromXML(in); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage() + " thrown expected"); + thrown = true; + } + + if (!thrown) { + throw new RuntimeException("ERROR: No IllegalArgumentException thrown"); + } + } + +} diff --git a/test/com/sun/servicetag/InvalidServiceTag.java b/test/com/sun/servicetag/InvalidServiceTag.java new file mode 100644 index 000000000..70b406a50 --- /dev/null +++ b/test/com/sun/servicetag/InvalidServiceTag.java @@ -0,0 +1,96 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6622366 + * @summary Basic Test for ServiceTag.newServiceTag() to test invalid fields. + * @author Mandy Chung + * + * @run build InvalidServiceTag + * @run main InvalidServiceTag + */ + +import com.sun.servicetag.*; +import java.io.*; +import java.util.*; + +public class InvalidServiceTag { + private final static int MAX_CONTAINER_LEN = 64 - 1; + public static void main(String[] argv) throws Exception { + // all fields valid + ServiceTag st1 = ServiceTag.newInstance("product name", + "product version", + "product urn", + "product parent", + "product parent urn", + "product defined instance ID", + "product vendor", + "platform arch", + "container", + "source"); + // empty optional field + ServiceTag st2 = ServiceTag.newInstance("product name", + "product version", + "product urn", + "product parent", + "", + "", + "product vendor", + "platform arch", + "container", + "source"); + // Invalid - empty required field + setInvalidContainer(""); + // Invalid - required field exceeds max length. + StringBuilder sb = new StringBuilder(); + for (int i = 0; i <= MAX_CONTAINER_LEN; i++) { + sb.append('x'); + } + setInvalidContainer(sb.toString()); + System.out.println("Test passed."); + } + private static void setInvalidContainer(String container) { + boolean exceptionThrown = false; + try { + ServiceTag st2 = ServiceTag.newInstance("product name", + "product version", + "product urn", + "product parent", + "product parent urn", + "product defined instance ID", + "product vendor", + "platform arch", + container, + "source"); + } catch (IllegalArgumentException iae) { + iae.printStackTrace(); + exceptionThrown = true; + } + if (!exceptionThrown) { + throw new RuntimeException("IllegalArgumentException not thrown"); + } + } +} diff --git a/test/com/sun/servicetag/JavaServiceTagTest.java b/test/com/sun/servicetag/JavaServiceTagTest.java new file mode 100644 index 000000000..1647f0119 --- /dev/null +++ b/test/com/sun/servicetag/JavaServiceTagTest.java @@ -0,0 +1,176 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6622366 + * @summary Basic Test for ServiceTag.getJavaServiceTag() + * Disable creating the service tag in the system registry. + * Verify the existence of registration.xml file and the + * content of the service tag. + * @author Mandy Chung + * + * @run build JavaServiceTagTest + * @run main JavaServiceTagTest + */ + +import com.sun.servicetag.*; +import java.io.*; +import java.util.*; + +public class JavaServiceTagTest { + public static void main(String[] argv) throws Exception { + String registrationDir = System.getProperty("test.classes"); + + // disable calling to stclient + System.setProperty("servicetag.sthelper.supported", "false"); + + if (Registry.isSupported()) { + throw new RuntimeException("Registry.isSupported() should " + + "return false"); + } + // For debugging + // System.setProperty("servicetag.verbose", ""); + + // cleanup the registration.xml and servicetag file in the test directory + System.setProperty("servicetag.dir.path", registrationDir); + File regFile = new File(registrationDir, "registration.xml"); + regFile.delete(); + File svcTagFile = new File(registrationDir, "servicetag"); + svcTagFile.delete(); + + ServiceTag svctag = ServiceTag.getJavaServiceTag("JavaServiceTagTest"); + checkServiceTag(svctag); + + if (svcTagFile.exists()) { + throw new RuntimeException(svcTagFile + " should not exist."); + } + + // registration.xml should be created + if (!regFile.exists()) { + throw new RuntimeException(regFile + " not created."); + } + BufferedInputStream in = new BufferedInputStream(new FileInputStream(regFile)); + RegistrationData registration = RegistrationData.loadFromXML(in); + Set c = registration.getServiceTags(); + if (c.size() != 1) { + throw new RuntimeException(regFile + " has " + c.size() + + " service tags. Expected 1."); + } + ServiceTag st = registration.getServiceTag(svctag.getInstanceURN()); + if (!Util.matches(st, svctag)) { + throw new RuntimeException("ServiceTag " + + " doesn't match."); + } + } + + private static void checkServiceTag(ServiceTag st) throws IOException { + Properties props = loadSwordfishEntries(); + if (st.getProductURN(). + equals(props.getProperty("servicetag.jdk.urn"))) { + if (!st.getProductName(). + equals(props.getProperty("servicetag.jdk.name"))) { + throw new RuntimeException("Product URN and name don't match."); + } + } else if (st.getProductURN(). + equals(props.getProperty("servicetag.jre.urn"))) { + if (!st.getProductName(). + equals(props.getProperty("servicetag.jre.name"))) { + throw new RuntimeException("Product URN and name don't match."); + } + } else { + throw new RuntimeException("Unexpected product_urn: " + + st.getProductURN()); + } + if (!st.getProductVersion(). + equals(System.getProperty("java.version"))) { + throw new RuntimeException("Unexpected product_version: " + + st.getProductVersion()); + } + if (!st.getProductParent(). + equals(props.getProperty("servicetag.parent.name"))) { + throw new RuntimeException("Unexpected product_parent: " + + st.getProductParent()); + } + if (!st.getProductParentURN(). + equals(props.getProperty("servicetag.parent.urn"))) { + throw new RuntimeException("Unexpected product_parent_urn: " + + st.getProductParentURN()); + } + if (!st.getPlatformArch(). + equals(System.getProperty("os.arch"))) { + throw new RuntimeException("Unexpected platform_arch: " + + st.getPlatformArch()); + } + if (!st.getProductVendor(). + equals("Sun Microsystems")) { + throw new RuntimeException("Unexpected product_vendor: " + + st.getProductVendor()); + } + if (!st.getSource(). + equals("JavaServiceTagTest")) { + throw new RuntimeException("Unexpected source: " + + st.getSource()); + } + String[] ss = st.getProductDefinedInstanceID().split(","); + boolean id = false; + boolean dir = false; + for (String s : ss) { + String[] values = s.split("="); + if (values[0].equals("id")) { + id = true; + String[] sss = values[1].split(" "); + if (!sss[0].equals(System.getProperty("java.runtime.version"))) { + throw new RuntimeException("Unexpected version in id: " + + sss[0]); + } + if (sss.length < 2) { + throw new RuntimeException("Unexpected id=" + values[1]); + } + } else if (values[0].equals("dir")) { + dir = true; + } + } + if (!id || !dir) { + throw new RuntimeException("Unexpected product_defined_instance_id: " + + st.getProductDefinedInstanceID()); + } + } + + private static Properties loadSwordfishEntries() + throws IOException { + int version = sun.misc.Version.jdkMinorVersion(); + String filename = "/com/sun/servicetag/resources/javase_" + + version + "_swordfish.properties"; + InputStream in = Installer.class.getClass().getResourceAsStream(filename); + Properties props = new Properties(); + try { + props.load(in); + } finally { + in.close(); + } + return props; + } +} diff --git a/test/com/sun/servicetag/JavaServiceTagTest1.java b/test/com/sun/servicetag/JavaServiceTagTest1.java new file mode 100644 index 000000000..3d5a8789b --- /dev/null +++ b/test/com/sun/servicetag/JavaServiceTagTest1.java @@ -0,0 +1,240 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6622366 + * @summary Basic Test for ServiceTag.getJavaServiceTag(String) + * to verify that the registration.xml and servicetag files + * are both created correctly. + * @author Mandy Chung + * + * @run build JavaServiceTagTest1 + * @run main JavaServiceTagTest1 + */ + +import com.sun.servicetag.*; +import java.io.*; +import java.util.*; + +public class JavaServiceTagTest1 { + private static String registrationDir = System.getProperty("test.classes"); + private static String servicetagDir = System.getProperty("test.src"); + private static File regFile; + private static File svcTagFile; + private static Registry registry; + public static void main(String[] argv) throws Exception { + // cleanup the registration.xml and servicetag file in the test directory + System.setProperty("servicetag.dir.path", registrationDir); + regFile = new File(registrationDir, "registration.xml"); + regFile.delete(); + + svcTagFile = new File(registrationDir, "servicetag"); + svcTagFile.delete(); + + registry = Util.getSvcTagClientRegistry(); + + // verify that only one service tag is created + ServiceTag st1 = testJavaServiceTag("Test1"); + + // getJavaServiceTag method should create a new service tag + // and delete the old one + ServiceTag st2 = testJavaServiceTag("Test2"); + if (registry.getServiceTag(st1.getInstanceURN()) != null) { + throw new RuntimeException("instance_urn: " + st1.getInstanceURN() + + " exists but expected to be removed"); + } + + // expected to have different instance_urn + if (st1.getInstanceURN().equals(st2.getInstanceURN())) { + throw new RuntimeException("instance_urn: " + st1.getInstanceURN() + + " == " + st2.getInstanceURN()); + } + + // Delete the service tag from the Registry and the servicetag file + if (registry.removeServiceTag(st2.getInstanceURN()) == null) { + throw new RuntimeException("Failed to remove " + + st1.getInstanceURN() + " from the registry"); + } + svcTagFile.delete(); + + // call the getJavaServiceTag(String) method again + // should create the servicetag file. + ServiceTag st3 = testJavaServiceTag("Test2"); + if (!Util.matches(st2, st3)) { + System.out.println(st2); + System.out.println(st3); + throw new RuntimeException("Test Failed: Expected to be the same"); + } + + } + + private static ServiceTag testJavaServiceTag(String source) throws Exception { + ServiceTag svctag = ServiceTag.getJavaServiceTag(source); + checkServiceTag(svctag, source); + + // verify if registration.xml is created + if (!regFile.exists()) { + throw new RuntimeException(regFile + " not created."); + } + + // verify the registration.xml content is the expected service tag + BufferedInputStream in = new BufferedInputStream(new FileInputStream(regFile)); + RegistrationData registration = RegistrationData.loadFromXML(in); + Set c = registration.getServiceTags(); + if (c.size() != 1) { + throw new RuntimeException(regFile + " has " + c.size() + + " service tags. Expected 1."); + } + ServiceTag st = registration.getServiceTag(svctag.getInstanceURN()); + if (!Util.matches(st, svctag)) { + throw new RuntimeException("RegistrationData ServiceTag " + + " doesn't match."); + } + + // verify the service tag added in the registry + st = registry.getServiceTag(svctag.getInstanceURN()); + if (!Util.matches(st, svctag)) { + throw new RuntimeException("Registry ServiceTag " + + " doesn't match."); + } + + // verify if servicetag file is created + if (!svcTagFile.exists()) { + throw new RuntimeException(svcTagFile + " not created."); + } + + // verify that the servicetag file only contains one instance_urn + BufferedReader reader = new BufferedReader(new FileReader(svcTagFile)); + int count = 0; + try { + String line; + while ((line = reader.readLine()) != null) { + if (line.equals(svctag.getInstanceURN())) { + count++; + } else { + throw new RuntimeException("servicetag contains " + + " unexpected instance_urn " + line); + } + } + } finally { + reader.close(); + } + if (count != 1) { + throw new RuntimeException("servicetag contains unexpected " + + "number of instance_urn = " + count); + } + return svctag; + } + + private static void checkServiceTag(ServiceTag st, String source) + throws IOException { + Properties props = loadSwordfishEntries(); + if (st.getProductURN(). + equals(props.getProperty("servicetag.jdk.urn"))) { + if (!st.getProductName(). + equals(props.getProperty("servicetag.jdk.name"))) { + throw new RuntimeException("Product URN and name don't match."); + } + } else if (st.getProductURN(). + equals(props.getProperty("servicetag.jre.urn"))) { + if (!st.getProductName(). + equals(props.getProperty("servicetag.jre.name"))) { + throw new RuntimeException("Product URN and name don't match."); + } + } else { + throw new RuntimeException("Unexpected product_urn: " + + st.getProductURN()); + } + if (!st.getProductVersion(). + equals(System.getProperty("java.version"))) { + throw new RuntimeException("Unexpected product_version: " + + st.getProductVersion()); + } + if (!st.getProductParent(). + equals(props.getProperty("servicetag.parent.name"))) { + throw new RuntimeException("Unexpected product_parent: " + + st.getProductParent()); + } + if (!st.getProductParentURN(). + equals(props.getProperty("servicetag.parent.urn"))) { + throw new RuntimeException("Unexpected product_parent_urn: " + + st.getProductParentURN()); + } + if (!st.getPlatformArch(). + equals(System.getProperty("os.arch"))) { + throw new RuntimeException("Unexpected platform_arch: " + + st.getPlatformArch()); + } + if (!st.getProductVendor(). + equals("Sun Microsystems")) { + throw new RuntimeException("Unexpected product_vendor: " + + st.getProductVendor()); + } + if (!st.getSource(). + equals(source)) { + throw new RuntimeException("Unexpected source: " + + st.getSource() + " expected: " + source); + } + String[] ss = st.getProductDefinedInstanceID().split(","); + boolean id = false; + boolean dir = false; + for (String s : ss) { + String[] values = s.split("="); + if (values[0].equals("id")) { + id = true; + String[] sss = values[1].split(" "); + if (!sss[0].equals(System.getProperty("java.runtime.version"))) { + throw new RuntimeException("Unexpected version in id: " + + sss[0]); + } + if (sss.length < 2) { + throw new RuntimeException("Unexpected id=" + values[1]); + } + } else if (values[0].equals("dir")) { + dir = true; + } + } + if (!id || !dir) { + throw new RuntimeException("Unexpected product_defined_instance_id: " + + st.getProductDefinedInstanceID()); + } + } + + private static Properties loadSwordfishEntries() + throws IOException { + int version = sun.misc.Version.jdkMinorVersion(); + String filename = "/com/sun/servicetag/resources/javase_" + + version + "_swordfish.properties"; + InputStream in = Installer.class.getClass().getResourceAsStream(filename); + Properties props = new Properties(); + try { + props.load(in); + } finally { + in.close(); + } + return props; + } +} diff --git a/test/com/sun/servicetag/NewRegistrationData.java b/test/com/sun/servicetag/NewRegistrationData.java new file mode 100644 index 000000000..4a91e578e --- /dev/null +++ b/test/com/sun/servicetag/NewRegistrationData.java @@ -0,0 +1,105 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6622366 + * @summary Basic Test for Registration Data + * @author Mandy Chung + * + * @run build NewRegistrationData Util + * @run main NewRegistrationData + */ + +import com.sun.servicetag.*; +import java.io.*; +import java.util.*; + +public class NewRegistrationData { + private static RegistrationData regData; + private static Map stMap = new LinkedHashMap(); + private static String[] files = new String[] { + "servicetag1.properties", + "servicetag2.properties", + "servicetag3.properties" + }; + + public static void main(String[] argv) throws Exception { + String regDataDir = System.getProperty("test.classes"); + String servicetagDir = System.getProperty("test.src"); + + File reg = new File(regDataDir, "registration.xml"); + // Make sure a brand new file is created + reg.delete(); + + regData = new RegistrationData(); + if (regData.getRegistrationURN().isEmpty()) { + throw new RuntimeException("Empty registration urn"); + } + + int count = 0; + for (String f : files) { + addServiceTag(servicetagDir, f, ++count); + } + + // check if the registration data contains all service tags + Set c = regData.getServiceTags(); + for (ServiceTag st : c) { + if (!Util.matches(st, regData.getServiceTag(st.getInstanceURN()))) { + throw new RuntimeException("ServiceTag added in the regData " + + " doesn't match."); + } + } + + // store the service tag to a file + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(reg)); + try { + regData.storeToXML(out); + } finally { + out.close(); + } + + Util.checkRegistrationData(reg.getCanonicalPath(), stMap); + System.out.println("Test passed: " + count + " service tags added"); + } + + private static void addServiceTag(String parent, String filename, int count) throws Exception { + File f = new File(parent, filename); + ServiceTag svcTag = Util.newServiceTag(f); + regData.addServiceTag(svcTag); + stMap.put(svcTag.getInstanceURN(), svcTag); + + Set c = regData.getServiceTags(); + if (c.size() != count) { + throw new RuntimeException("Invalid service tag count= " + + c.size() + " expected=" + count); + } + ServiceTag st = regData.getServiceTag(svcTag.getInstanceURN()); + if (!Util.matches(st, svcTag)) { + throw new RuntimeException("ServiceTag added in the regData " + + " doesn't match."); + } + } +} diff --git a/test/com/sun/servicetag/SvcTagClient.java b/test/com/sun/servicetag/SvcTagClient.java new file mode 100644 index 000000000..c63d1deee --- /dev/null +++ b/test/com/sun/servicetag/SvcTagClient.java @@ -0,0 +1,200 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @bug 6622366 + */ + +import com.sun.servicetag.*; +import java.io.*; +import java.util.*; + +/** + * A utility class simulating stclient for testing purpose. + */ +public class SvcTagClient { + private static File xmlFile; + private static RegistrationData registration; + private static String instanceURN; + private static String productName; + private static String productVersion; + private static String productURN; + private static String productParent; + private static String productParentURN = ""; // optional + private static String productDefinedInstanceID = ""; //optional + private static String productVendor; + private static String platformArch; + private static String container; + private static String source; + + // stclient exit value + private static final int ST_ERR_REC_NOT_FOUND = 225; + + public static void main(String[] args) throws Exception { + String path = System.getProperty("stclient.registry.path"); + if (path == null) { + System.err.println("stclient registry path missing"); + System.exit(-1); + } + xmlFile = new File(path); + if (xmlFile.exists()) { + BufferedInputStream in = new BufferedInputStream(new FileInputStream(xmlFile)); + registration = RegistrationData.loadFromXML(in); + } else { + registration = new RegistrationData(); + } + boolean add = false; + boolean delete = false; + boolean update = false; + boolean get = false; + boolean find = false; + + int count = 0; + while (count < args.length) { + String arg = args[count]; + if (!arg.startsWith("-")) { + System.err.println("Invalid option:" + arg); + System.exit(-1); + } + if (arg.equals("-a")) { + add = true; + } else if (arg.equals("-d")) { + delete = true; + } else if (arg.equals("-u")) { + update = true; + } else if (arg.equals("-g")) { + get = true; + } else if (arg.equals("-f")) { + find = true; + productURN = ""; + } else if (arg.equals("-t")) { + productURN = args[++count]; + } else if (arg.equals("-i")) { + instanceURN = args[++count]; + } else if (arg.equals("-p")) { + productName = args[++count]; + } else if (arg.equals("-e")) { + productVersion = args[++count]; + } else if (arg.equals("-t")) { + productURN = args[++count]; + } else if (arg.equals("-F")) { + productParentURN = args[++count]; + } else if (arg.equals("-P")) { + productParent = args[++count]; + } else if (arg.equals("-I")) { + productDefinedInstanceID = args[++count]; + } else if (arg.equals("-m")) { + productVendor = args[++count]; + } else if (arg.equals("-A")) { + platformArch = args[++count]; + } else if (arg.equals("-z")) { + container = args[++count]; + } else if (arg.equals("-S")) { + source = args[++count]; + } else { + System.err.println("Invalid option:" + arg); + System.exit(-1); + } + count++; + } + + if (add) { + addServiceTag(); + } else if (delete) { + deleteServiceTag(); + } else if (update) { + updateServiceTag(); + } else if (get) { + getServiceTag(); + } else if (find) { + findServiceTags(); + } else { + System.err.println("Error"); + System.exit(-1); + } + updateXmlFile(); + } + private static String OUTPUT = "Product instance URN="; + + private static void addServiceTag() { + if (instanceURN == null) { + instanceURN = ServiceTag.generateInstanceURN(); + } + ServiceTag st = ServiceTag.newInstance(instanceURN, + productName, + productVersion, + productURN, + productParent, + productParentURN, + productDefinedInstanceID, + productVendor, + platformArch, + container, + source); + registration.addServiceTag(st); + System.out.println(OUTPUT + st.getInstanceURN()); + } + + private static void deleteServiceTag() { + registration.removeServiceTag(instanceURN); + System.out.println("instance_urn=" + instanceURN + " deleted"); + } + + private static void updateServiceTag() { + registration.updateServiceTag(instanceURN, productDefinedInstanceID); + System.out.println("instance_urn=" + instanceURN + " updated"); + } + + private static void getServiceTag() { + ServiceTag st = registration.getServiceTag(instanceURN); + if (st == null) { + System.err.println("instance_urn=" + instanceURN + " not found"); + System.exit(ST_ERR_REC_NOT_FOUND); + } else { + System.out.println(st); + } + } + + private static void findServiceTags() { + Set set = registration.getServiceTags(); + for (ServiceTag st : set) { + if (st.getProductURN().equals(productURN)) { + System.out.println(st.getInstanceURN()); + } + } + if (set.size() == 0) { + System.out.println("No records found"); + System.exit(ST_ERR_REC_NOT_FOUND); + } + } + private static void updateXmlFile() throws IOException { + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(xmlFile)); + try { + registration.storeToXML(out); + } finally { + out.close(); + } + } +} diff --git a/test/com/sun/servicetag/SystemRegistryTest.java b/test/com/sun/servicetag/SystemRegistryTest.java new file mode 100644 index 000000000..0cabcedfc --- /dev/null +++ b/test/com/sun/servicetag/SystemRegistryTest.java @@ -0,0 +1,131 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6622366 + * @summary Basic Test for registry class + * by replacing stclient with SvcTagClient utility + * @author Mandy Chung + * + * @run build SvcTagClient SystemRegistryTest Util + * @run main SystemRegistryTest + */ + +import com.sun.servicetag.*; +import java.io.*; +import java.util.*; + +public class SystemRegistryTest { + private static String registryDir = System.getProperty("test.classes"); + private static String servicetagDir = System.getProperty("test.src"); + private static List list = new ArrayList(); + private static String[] files = new String[] { + "servicetag1.properties", + "servicetag2.properties", + "servicetag3.properties" + }; + + private static Registry registry; + public static void main(String[] argv) throws Exception { + registry = Util.getSvcTagClientRegistry(); + + for (String filename : files) { + File f = new File(servicetagDir, filename); + ServiceTag svcTag = Util.newServiceTag(f); + ServiceTag st = registry.addServiceTag(svcTag); + list.add(st); + System.out.println(st); + } + + testDuplicate(list.get(0)); + testNotFound(); + + // remove a service tag + String urn = list.get(0).getInstanceURN(); + ServiceTag svcTag = registry.removeServiceTag(urn); + if (!Util.matches(svcTag, list.get(0))) { + throw new RuntimeException(urn + + " deleted but does not match."); + } + + // get a service tag + svcTag = list.get(1); + urn = svcTag.getInstanceURN(); + ServiceTag st = registry.getServiceTag(urn); + if (!Util.matches(svcTag, st)) { + throw new RuntimeException(urn + + " returned from getServiceTag but does not match."); + } + // update the service tag + registry.updateServiceTag(urn, "My new defined ID"); + st = registry.getServiceTag(urn); + if (Util.matches(svcTag, st)) { + throw new RuntimeException(urn + + " updated but expected to be different."); + } + + if (!st.getProductDefinedInstanceID().equals("My new defined ID")) { + throw new RuntimeException("Invalid product_defined_instance_id " + + st.getProductDefinedInstanceID()); + } + if (st.getInstallerUID() != -1) { + throw new RuntimeException("Invalid installer_uid " + + st.getInstallerUID()); + } + if (st.getTimestamp().equals(svcTag.getTimestamp())) { + throw new RuntimeException("Timestamp " + + st.getTimestamp() + " == " + svcTag.getTimestamp()); + } + + } + private static void testDuplicate(ServiceTag st) throws IOException { + boolean dup = false; + try { + registry.addServiceTag(st); + } catch (IllegalArgumentException e) { + dup = true; + } + if (!dup) { + throw new RuntimeException(st.getInstanceURN() + + " added successfully but expected to be a duplicated."); + } + } + + private static void testNotFound() throws Exception { + String instanceURN = "urn:st:721cf98a-f4d7-6231-bb1d-f2f5aa903ef7"; + ServiceTag svctag = registry.removeServiceTag(instanceURN); + if (svctag != null) { + throw new RuntimeException(instanceURN + + " exists but expected not found"); + } + + svctag = registry.updateServiceTag(instanceURN, "testing"); + if (svctag != null) { + throw new RuntimeException(instanceURN + + " exists but expected not found"); + } + } +} diff --git a/test/com/sun/servicetag/TestLoadFromXML.java b/test/com/sun/servicetag/TestLoadFromXML.java new file mode 100644 index 000000000..dcc19f355 --- /dev/null +++ b/test/com/sun/servicetag/TestLoadFromXML.java @@ -0,0 +1,69 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6622366 + * @summary Basic Test for RegistrationData.loadFromXML + * @author Mandy Chung + * + * @run build TestLoadFromXML + * @run main TestLoadFromXML + */ + +import com.sun.servicetag.*; +import java.io.*; +import java.util.*; + +public class TestLoadFromXML { + public static void main(String[] argv) throws Exception { + String registrationDir = System.getProperty("test.classes"); + String servicetagDir = System.getProperty("test.src"); + + File inFile = new File(servicetagDir, "registration.xml"); + File outFile = new File(registrationDir, "out.xml"); + BufferedInputStream in = new BufferedInputStream(new FileInputStream(inFile)); + RegistrationData regData = RegistrationData.loadFromXML(in); + boolean closed = false; + try { + in.read(); + } catch (IOException e) { + // expect the InputStream is closed + closed = true; + System.out.println("*** Expected IOException ***"); + e.printStackTrace(); + } + if (!closed) { + throw new RuntimeException("InputStream not closed after " + + "RegistrationData.loadFromXML() call"); + } + + BufferedOutputStream out = + new BufferedOutputStream(new FileOutputStream(outFile)); + regData.storeToXML(out); + // should be able to write to the OutputStream + out.write(0); + } +} diff --git a/test/com/sun/servicetag/UpdateServiceTagTest.java b/test/com/sun/servicetag/UpdateServiceTagTest.java new file mode 100644 index 000000000..1d61f3f96 --- /dev/null +++ b/test/com/sun/servicetag/UpdateServiceTagTest.java @@ -0,0 +1,109 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6622366 + * @summary Basic Test for RegistrationData.updateServiceTag + * @author Mandy Chung + * + * @run build UpdateServiceTagTest Util + * @run main UpdateServiceTagTest + */ + +import com.sun.servicetag.*; +import java.io.*; +import java.util.*; + +public class UpdateServiceTagTest { + private static String servicetagDir = System.getProperty("test.src"); + private static String[] files = new String[] { + "servicetag1.properties", + "servicetag2.properties", + "servicetag3.properties" + }; + private static RegistrationData registration = new RegistrationData(); + private static Set set = new HashSet(); + + public static void main(String[] argv) throws Exception { + for (String f : files) { + ServiceTag st = addServiceTag(f); + set.add(st); + } + Thread.sleep(1000); + for (ServiceTag st : set) { + updateServiceTag(st); + } + } + + private static ServiceTag addServiceTag(String filename) throws Exception { + File f = new File(servicetagDir, filename); + ServiceTag svcTag = Util.newServiceTag(f, true /* no instance_urn */); + ServiceTag st = registration.addServiceTag(svcTag); + if (!Util.matchesNoInstanceUrn(svcTag, st)) { + throw new RuntimeException("ServiceTag " + + " doesn't match."); + } + String urn = st.getInstanceURN(); + if (!urn.startsWith("urn:st:")) { + throw new RuntimeException("Invalid generated instance_urn " + + urn); + } + if (st.getInstallerUID() != -1) { + throw new RuntimeException("Invalid installer_uid " + + st.getInstallerUID()); + } + if (st.getTimestamp() == null) { + throw new RuntimeException("null timestamp "); + } + return st; + } + + private static String newID = "New product defined instance ID"; + private static void updateServiceTag(ServiceTag svcTag) throws Exception { + // update the service tag + String urn = svcTag.getInstanceURN(); + registration.updateServiceTag(urn, newID); + + // get the updated service tag + ServiceTag st = registration.getServiceTag(urn); + if (Util.matches(svcTag, st)) { + throw new RuntimeException("ServiceTag " + + " should not match."); + } + if (!st.getProductDefinedInstanceID().equals(newID)) { + throw new RuntimeException("Invalid product_defined_instance_id " + + st.getProductDefinedInstanceID()); + } + if (st.getInstallerUID() != -1) { + throw new RuntimeException("Invalid installer_uid " + + st.getInstallerUID()); + } + if (st.getTimestamp().equals(svcTag.getTimestamp())) { + throw new RuntimeException("Timestamp " + + st.getTimestamp() + " == " + svcTag.getTimestamp()); + } + } +} diff --git a/test/com/sun/servicetag/Util.java b/test/com/sun/servicetag/Util.java new file mode 100644 index 000000000..367486dba --- /dev/null +++ b/test/com/sun/servicetag/Util.java @@ -0,0 +1,250 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @bug 6622366 + * @summary Utility class used by other jtreg tests + */ + +import com.sun.servicetag.RegistrationData; +import com.sun.servicetag.ServiceTag; +import com.sun.servicetag.Registry; + +import java.util.Set; +import java.util.Date; +import java.util.Map; +import java.util.Properties; +import java.util.TimeZone; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.io.*; + +public class Util { + public static ServiceTag newServiceTag(File f) + throws FileNotFoundException, IOException, NumberFormatException { + return newServiceTag(f, false /* with instance_urn */); + } + + public static ServiceTag newServiceTag(File f, boolean noInstanceURN) + throws FileNotFoundException, IOException, NumberFormatException { + Properties props = new Properties(); + FileReader reader = new FileReader(f); + try { + props.load(reader); + } finally { + reader.close(); + } + if (noInstanceURN) { + return ServiceTag.newInstance( + props.getProperty("product_name"), + props.getProperty("product_version"), + props.getProperty("product_urn"), + props.getProperty("product_parent"), + props.getProperty("product_parent_urn"), + props.getProperty("product_defined_inst_id"), + props.getProperty("product_vendor"), + props.getProperty("platform_arch"), + props.getProperty("container"), + props.getProperty("source")); + } else { + return ServiceTag.newInstance( + props.getProperty("instance_urn"), + props.getProperty("product_name"), + props.getProperty("product_version"), + props.getProperty("product_urn"), + props.getProperty("product_parent"), + props.getProperty("product_parent_urn"), + props.getProperty("product_defined_inst_id"), + props.getProperty("product_vendor"), + props.getProperty("platform_arch"), + props.getProperty("container"), + props.getProperty("source")); + } + } + + public static boolean matches(ServiceTag st1, ServiceTag st2) { + if (!st1.getInstanceURN().equals(st2.getInstanceURN())) { + System.out.println("instance_urn: " + st1.getInstanceURN() + + " != " + st2.getInstanceURN()); + return false; + } + return matchesNoInstanceUrn(st1, st2); + } + + public static boolean matchesNoInstanceUrn(ServiceTag st1, ServiceTag st2) { + if (!st1.getProductName().equals(st2.getProductName())) { + System.out.println("product_name: " + st1.getProductName() + + " != " + st2.getProductName()); + return false; + } + + if (!st1.getProductVersion().equals(st2.getProductVersion())) { + System.out.println("product_version: " + st1.getProductVersion() + + " != " + st2.getProductVersion()); + return false; + } + if (!st1.getProductURN().equals(st2.getProductURN())) { + System.out.println("product_urn: " + st1.getProductURN() + + " != " + st2.getProductURN()); + return false; + } + if (!st1.getProductParentURN().equals(st2.getProductParentURN())) { + System.out.println("product_parent_urn: " + st1.getProductParentURN() + + " != " + st2.getProductParentURN()); + return false; + } + if (!st1.getProductParent().equals(st2.getProductParent())) { + System.out.println("product_parent: " + st1.getProductParent() + + " != " + st2.getProductParent()); + return false; + } + if (!st1.getProductDefinedInstanceID().equals(st2.getProductDefinedInstanceID())) { + System.out.println("product_defined_inst_id: " + + st1.getProductDefinedInstanceID() + + " != " + st2.getProductDefinedInstanceID()); + return false; + } + if (!st1.getProductVendor().equals(st2.getProductVendor())) { + System.out.println("product_vendor: " + st1.getProductVendor() + + " != " + st2.getProductVendor()); + return false; + } + if (!st1.getPlatformArch().equals(st2.getPlatformArch())) { + System.out.println("platform_arch: " + st1.getPlatformArch() + + " != " + st2.getPlatformArch()); + return false; + } + if (!st1.getContainer().equals(st2.getContainer())) { + System.out.println("container: " + st1.getContainer() + + " != " + st2.getContainer()); + return false; + } + if (!st1.getSource().equals(st2.getSource())) { + System.out.println("source: " + st1.getSource() + + " != " + st2.getSource()); + return false; + } + return true; + } + + public static void checkRegistrationData(String regFile, + Map stMap) + throws IOException { + BufferedInputStream in = new BufferedInputStream(new FileInputStream(regFile)); + RegistrationData registration = RegistrationData.loadFromXML(in); + Set svcTags = registration.getServiceTags(); + if (svcTags.size() != stMap.size()) { + throw new RuntimeException("Invalid service tag count= " + + svcTags.size() + " expected=" + stMap.size()); + } + for (ServiceTag st : svcTags) { + ServiceTag st1 = stMap.get(st.getInstanceURN()); + if (!matches(st, st1)) { + throw new RuntimeException("ServiceTag in the registry " + + "does not match the one in the map"); + } + } + } + + + /** + * Formats the Date into a timestamp string in YYYY-MM-dd HH:mm:ss GMT. + * @param timestamp Date + * @return a string representation of the timestamp in the YYYY-MM-dd HH:mm:ss GMT format. + */ + static String formatTimestamp(Date timestamp) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); + df.setTimeZone(TimeZone.getTimeZone("GMT")); + return df.format(timestamp); + } + + /** + * Parses a timestamp string in YYYY-MM-dd HH:mm:ss GMT format. + * @param timestamp Timestamp in the YYYY-MM-dd HH:mm:ss GMT format. + * @return Date + */ + static Date parseTimestamp(String timestamp) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); + df.setTimeZone(TimeZone.getTimeZone("GMT")); + try { + return df.parse(timestamp); + } catch (ParseException e) { + // should not reach here + e.printStackTrace(); + return new Date(); + } + } + + /** + * Returns the command simulating stclient behavior. + */ + static String getSvcClientCommand(String stclientRegistry) { + String regDir = System.getProperty("test.classes"); + + StringBuilder sb = new StringBuilder(); + // wrap each argument to the command with double quotes + sb.append("\""); + sb.append(System.getProperty("java.home")); + sb.append(File.separator).append("bin"); + sb.append(File.separator).append("java"); + sb.append("\""); + sb.append(" -cp "); + sb.append("\"").append(regDir).append("\""); + sb.append(" \"-Dstclient.registry.path="); + sb.append(stclientRegistry).append("\""); + sb.append(" SvcTagClient"); + return sb.toString(); + } + + private static Registry registry = null; + /** + * Returns the Registry processed by SvcTagClient that simulates + * stclient. + */ + static synchronized Registry getSvcTagClientRegistry() throws IOException { + if (registry != null) { + return registry; + } + + // System.setProperty("servicetag.verbose", "true"); + // enable the helper class + System.setProperty("servicetag.sthelper.supported", "true"); + + // clean up registry.xml + String regDir = System.getProperty("test.classes"); + File registryFile = new File(regDir, "registry.xml"); + if (registryFile.exists()) { + registryFile.delete(); + } + + String stclientCmd = Util.getSvcClientCommand(registryFile.getCanonicalPath()); + System.out.println("stclient cmd: " + stclientCmd); + System.setProperty("servicetag.stclient.cmd", stclientCmd); + + // get the Registry object after the system properties are set + registry = Registry.getSystemRegistry(); + return registry; + } +} diff --git a/test/com/sun/servicetag/ValidRegistrationData.java b/test/com/sun/servicetag/ValidRegistrationData.java new file mode 100644 index 000000000..ec6929398 --- /dev/null +++ b/test/com/sun/servicetag/ValidRegistrationData.java @@ -0,0 +1,112 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6622366 + * @summary Basic Test for reading a valid registration + * @author Mandy Chung + * + * @run build ValidRegistrationData + * @run main ValidRegistrationData + */ + +import com.sun.servicetag.*; +import java.io.*; +import java.util.*; + +public class ValidRegistrationData { + private static String registrationDir = System.getProperty("test.classes"); + private static String servicetagDir = System.getProperty("test.src"); + private static RegistrationData registration; + private static Map stMap = + new LinkedHashMap(); + private static String[] files = new String[] { + "servicetag1.properties", + "servicetag2.properties", + "servicetag3.properties" + }; + private static String URN = "urn:st:9543ffaa-a4f1-4f77-b2d1-f561922d4e4a"; + + public static void main(String[] argv) throws Exception { + File f = new File(servicetagDir, "registration.xml"); + + // load the registration data with all service tags + BufferedInputStream in = new BufferedInputStream(new FileInputStream(f)); + registration = RegistrationData.loadFromXML(in); + if (!registration.getRegistrationURN().equals(URN)){ + throw new RuntimeException("Invalid URN=" + + registration.getRegistrationURN()); + } + Map environMap = registration.getEnvironmentMap(); + checkEnvironmentMap(environMap); + + // set environment + setInvalidEnvironment("hostname", ""); + setInvalidEnvironment("osName", ""); + setInvalidEnvironment("invalid", ""); + } + + private static void checkEnvironmentMap(Map envMap) + throws Exception { + Properties props = new Properties(); + File f = new File(servicetagDir, "environ.properties"); + FileReader reader = new FileReader(f); + try { + props.load(reader); + } finally { + reader.close(); + } + for (Map.Entry entry : envMap.entrySet()) { + String name = entry.getKey(); + String value = entry.getValue(); + String expected = props.getProperty(name); + if (expected == null || !value.equals(expected)) { + throw new RuntimeException("Invalid environment " + + name + "=" + value); + } + props.remove(name); + } + if (!props.isEmpty()) { + System.out.println("Environment missing: "); + for (String s : props.stringPropertyNames()) { + System.out.println(" " + s + "=" + props.getProperty(s)); + } + throw new RuntimeException("Invalid environment read"); + } + } + private static void setInvalidEnvironment(String name, String value) { + boolean invalid = false; + try { + registration.setEnvironment(name, value); + } catch (IllegalArgumentException e) { + invalid = true; + } + if (!invalid) { + throw new RuntimeException(name + "=" + value + + " set but expected to fail."); + } + } +} diff --git a/test/com/sun/servicetag/environ.properties b/test/com/sun/servicetag/environ.properties new file mode 100644 index 000000000..0881061b0 --- /dev/null +++ b/test/com/sun/servicetag/environ.properties @@ -0,0 +1,9 @@ +hostname=ko +hostId=83abc1ab +osName=SunOS +osVersion=5.10 +osArchitecture=sparc +systemModel=Sun-Fire-V440 +systemManufacturer=Sun Microsystems +cpuManufacturer=Sun Microsystems +serialNumber=BEL078932 diff --git a/test/com/sun/servicetag/missing-environ-field.xml b/test/com/sun/servicetag/missing-environ-field.xml new file mode 100644 index 000000000..727288dd1 --- /dev/null +++ b/test/com/sun/servicetag/missing-environ-field.xml @@ -0,0 +1,45 @@ + + + +ko + +SunOS +5.10 +sparc + + + + + + +urn:st:9efff93a-767c-4714-fcfa-c48988293110 +Java SE 6 Runtime Environment +1.6.0-internal +urn:uuid:92d1de8c-1e59-42c6-a280-1c379526bcbc +urn:uuid:fdc90b21-018d-4cab-b866-612c7c119ed3 +Java Platform Standard Edition 6 (Java SE 6) +id=1.6.0-internal-b00 sparc,dir=/myjdk/solaris-sparc +Sun Microsystems +sparc +2007-11-12 06:15:11 GMT +global +servicetag1.properties +121937 + + +urn:st:0e9712bf-4832-e315-8e40-c4b17ffac9a9 +Java SE 6 Development Kit +1.6.0 +urn:uuid:b58ef9a8-5ae8-11db-a023-080020a9ed93 +urn:uuid:fdc90b21-018d-4cab-b866-612c7c119ed3 +Java Platform Standard Edition 6 (Java SE 6) +id=1.6.0_05-b01 sparc,dir=/myjdk/solaris-i586 +Sun Microsystems +i386 +2007-11-12 06:15:11 GMT +global +servicetag2.properties +121937 + + + diff --git a/test/com/sun/servicetag/newer-registry-version.xml b/test/com/sun/servicetag/newer-registry-version.xml new file mode 100644 index 000000000..9ee68ac0a --- /dev/null +++ b/test/com/sun/servicetag/newer-registry-version.xml @@ -0,0 +1,32 @@ + + + +ko + +SunOS +5.10 +sparc + + + + + + + +urn:st:9efff93a-767c-4714-fcfa-c48988293110 +Java SE 6 Runtime Environment +1.6.0-internal +urn:uuid:92d1de8c-1e59-42c6-a280-1c379526bcbc +urn:uuid:fdc90b21-018d-4cab-b866-612c7c119ed3 +Java Platform Standard Edition 6 (Java SE 6) +id=1.6.0-internal-b00 sparc,dir=/myjdk/solaris-sparc +Sun Microsystems +sparc +2007-11-13 00:49:01 GMT +global +servicetag1.properties +121937 + + + + diff --git a/test/com/sun/servicetag/registration.xml b/test/com/sun/servicetag/registration.xml new file mode 100644 index 000000000..e7a431a0f --- /dev/null +++ b/test/com/sun/servicetag/registration.xml @@ -0,0 +1,61 @@ + + + +ko +83abc1ab +SunOS +5.10 +sparc +Sun-Fire-V440 +Sun Microsystems +Sun Microsystems +BEL078932 + + + +urn:st:9efff93a-767c-4714-fcfa-c48988293110 +Java SE 6 Runtime Environment +1.6.0-internal +urn:uuid:92d1de8c-1e59-42c6-a280-1c379526bcbc +urn:uuid:fdc90b21-018d-4cab-b866-612c7c119ed3 +Java Platform Standard Edition 6 (Java SE 6) +id=1.6.0-internal-b00 sparc,dir=/myjdk/solaris-sparc +Sun Microsystems +sparc +2007-11-13 00:49:01 GMT +global +servicetag1.properties +121937 + + +urn:st:0e9712bf-4832-e315-8e40-c4b17ffac9a9 +Java SE 6 Development Kit +1.6.0 +urn:uuid:b58ef9a8-5ae8-11db-a023-080020a9ed93 +urn:uuid:fdc90b21-018d-4cab-b866-612c7c119ed3 +Java Platform Standard Edition 6 (Java SE 6) +id=1.6.0_05-b01 i386,dir=/myjdk/solaris-i586 +Sun Microsystems +i386 +2007-11-13 00:49:01 GMT +global +servicetag2.properties +121937 + + +urn:st:8a6ff75e-21a4-c8d7-bbda-de2c971bd67d +Solaris 10 Operating System +10 +urn:uuid:5005588c-36f3-11d6-9cec-fc96f718e113 +urn:uuid:596ffcfa-63d5-11d7-9886-ac816a682f92 +Solaris Operating System + +Sun Microsystems +sparc +2007-11-13 00:49:01 GMT +global +servicetag3.properties +212883 + + + diff --git a/test/com/sun/servicetag/servicetag1.properties b/test/com/sun/servicetag/servicetag1.properties new file mode 100644 index 000000000..3a1e8acc3 --- /dev/null +++ b/test/com/sun/servicetag/servicetag1.properties @@ -0,0 +1,13 @@ +instance_urn=urn:st:9efff93a-767c-4714-fcfa-c48988293110 +product_name=Java SE 6 Runtime Environment +product_version=1.6.0-internal +product_urn=urn:uuid:92d1de8c-1e59-42c6-a280-1c379526bcbc +product_parent_urn=urn:uuid:fdc90b21-018d-4cab-b866-612c7c119ed3 +product_parent=Java Platform Standard Edition 6 (Java SE 6) +product_defined_inst_id=id=1.6.0-internal-b00 sparc,dir=/myjdk/solaris-sparc +product_vendor=Sun Microsystems +platform_arch=sparc +timestamp=2007-11-12 05:19:40 GMT +container=global +source=servicetag1.properties +installer_uid=121937 diff --git a/test/com/sun/servicetag/servicetag2.properties b/test/com/sun/servicetag/servicetag2.properties new file mode 100644 index 000000000..d5dbe14f8 --- /dev/null +++ b/test/com/sun/servicetag/servicetag2.properties @@ -0,0 +1,13 @@ +instance_urn=urn:st:0e9712bf-4832-e315-8e40-c4b17ffac9a9 +product_name=Java SE 6 Development Kit +product_version=1.6.0 +product_urn=urn:uuid:b58ef9a8-5ae8-11db-a023-080020a9ed93 +product_parent_urn=urn:uuid:fdc90b21-018d-4cab-b866-612c7c119ed3 +product_parent=Java Platform Standard Edition 6 (Java SE 6) +product_defined_inst_id=id=1.6.0_05-b01 i386,dir=/myjdk/solaris-i586 +product_vendor=Sun Microsystems +platform_arch=i386 +timestamp=2007-11-12 06:12:21 GMT +container=global +source=servicetag2.properties +installer_uid=121937 diff --git a/test/com/sun/servicetag/servicetag3.properties b/test/com/sun/servicetag/servicetag3.properties new file mode 100644 index 000000000..6ca5dc79d --- /dev/null +++ b/test/com/sun/servicetag/servicetag3.properties @@ -0,0 +1,13 @@ +instance_urn=urn:st:8a6ff75e-21a4-c8d7-bbda-de2c971bd67d +product_name=Solaris 10 Operating System +product_version=10 +product_urn=urn:uuid:5005588c-36f3-11d6-9cec-fc96f718e113 +product_parent_urn=urn:uuid:596ffcfa-63d5-11d7-9886-ac816a682f92 +product_parent=Solaris Operating System +product_defined_inst_id= +product_vendor=Sun Microsystems +platform_arch=sparc +timestamp=2007-06-20 22:07:11 GMT +container=global +source=servicetag3.properties +installer_uid=212883 diff --git a/test/com/sun/servicetag/servicetag4.properties b/test/com/sun/servicetag/servicetag4.properties new file mode 100644 index 000000000..ba7c3c526 --- /dev/null +++ b/test/com/sun/servicetag/servicetag4.properties @@ -0,0 +1,13 @@ +instance_urn=urn:st:b8171b45-a087-47b3-92c8-f2d9fb34e8c2 +product_name=Java SE 6 Runtime Environment +product_version=1.6.0_05 +product_urn=urn:uuid:92d1de8c-1e59-42c6-a280-1c379526bcbc +product_parent_urn=urn:uuid:fdc90b21-018d-4cab-b866-612c7c119ed3 +product_parent=Java Platform Standard Edition 6 (Java SE 6) +product_defined_inst_id=id=1.6.0_05-b01 amd64,dir=/myjdk/linux-amd64 +product_vendor=Sun Microsystems +platform_arch=x64 +timestamp=2007-12-12 05:19:40 GMT +container=global +source=servicetag4.properties +installer_uid=121937 diff --git a/test/com/sun/servicetag/servicetag5.properties b/test/com/sun/servicetag/servicetag5.properties new file mode 100644 index 000000000..84e44c324 --- /dev/null +++ b/test/com/sun/servicetag/servicetag5.properties @@ -0,0 +1,13 @@ +instance_urn=urn:st:1d4269a1-71e3-4e44-bc8f-3793da7928ed +product_name=Java SE 6 Runtime Environment +product_version=1.6.0_06 +product_urn=urn:uuid:92d1de8c-1e59-42c6-a280-1c379526bcbc +product_parent_urn=urn:uuid:fdc90b21-018d-4cab-b866-612c7c119ed3 +product_parent=Java Platform Standard Edition 6 (Java SE 6) +product_defined_inst_id=id=1.6.0_06-b06 i386,dir=/w/mchung/bundles/jdk1.6.0_05/jre +product_vendor=Sun Microsystems +platform_arch=x86 +timestamp=2007-11-29 17:59:42 GMT +container=global +source=servicetag5.properties +installer_uid=-1 -- GitLab From 1ac113c601c020f052848a697f4a6f6a6c62e3a4 Mon Sep 17 00:00:00 2001 From: jjh Date: Thu, 2 Oct 2008 18:23:23 -0700 Subject: [PATCH 134/139] 6751643: ThreadReference.ownedMonitors() can return null Summary: Make a local copy of the cache so it doesn't get modified by a racy resume Reviewed-by: dcubed, swamyv --- .../sun/tools/jdi/ThreadReferenceImpl.java | 114 ++++--- test/com/sun/jdi/SimulResumerTest.java | 278 ++++++++++++++++++ 2 files changed, 350 insertions(+), 42 deletions(-) create mode 100644 test/com/sun/jdi/SimulResumerTest.java diff --git a/src/share/classes/com/sun/tools/jdi/ThreadReferenceImpl.java b/src/share/classes/com/sun/tools/jdi/ThreadReferenceImpl.java index 7af01561e..0f18120f1 100644 --- a/src/share/classes/com/sun/tools/jdi/ThreadReferenceImpl.java +++ b/src/share/classes/com/sun/tools/jdi/ThreadReferenceImpl.java @@ -61,7 +61,8 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl private ThreadGroupReference threadGroup; // This is cached only while this one thread is suspended. Each time - // the thread is resumed, we clear this and start with a fresh one. + // the thread is resumed, we abandon the current cache object and + // create a new intialized one. private static class LocalCache { JDWP.ThreadReference.Status status = null; List frames = null; @@ -74,6 +75,28 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl boolean triedCurrentContended = false; } + /* + * The localCache instance var is set by resetLocalCache to an initialized + * object as shown above. This occurs when the ThreadReference + * object is created, and when the mirrored thread is resumed. + * The fields are then filled in by the relevant methods as they + * are called. A problem can occur if resetLocalCache is called + * (ie, a resume() is executed) at certain points in the execution + * of some of these methods - see 6751643. To avoid this, each + * method that wants to use this cache must make a local copy of + * this variable and use that. This means that each invocation of + * these methods will use a copy of the cache object that was in + * effect at the point that the copy was made; if a racy resume + * occurs, it won't affect the method's local copy. This means that + * the values returned by these calls may not match the state of + * the debuggee at the time the caller gets the values. EG, + * frameCount() is called and comes up with 5 frames. But before + * it returns this, a resume of the debuggee thread is executed in a + * different debugger thread. The thread is resumed and running at + * the time that the value 5 is returned. Or even worse, the thread + * could be suspended again and have a different number of frames, eg, 24, + * but this call will still return 5. + */ private LocalCache localCache; private void resetLocalCache() { @@ -129,8 +152,9 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl } /** - * Note that we only cache the name string while suspended because - * it can change via Thread.setName arbitrarily + * Note that we only cache the name string while the entire VM is suspended + * because the name can change via Thread.setName arbitrarily while this + * thread is running. */ public String name() { String name = null; @@ -240,19 +264,20 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl } private JDWP.ThreadReference.Status jdwpStatus() { - JDWP.ThreadReference.Status myStatus = localCache.status; + LocalCache snapshot = localCache; + JDWP.ThreadReference.Status myStatus = snapshot.status; try { if (myStatus == null) { myStatus = JDWP.ThreadReference.Status.process(vm, this); if ((myStatus.suspendStatus & SUSPEND_STATUS_SUSPENDED) != 0) { // thread is suspended, we can cache the status. - localCache.status = myStatus; + snapshot.status = myStatus; } } } catch (JDWPException exc) { throw exc.toJDIException(); } - return myStatus; + return myStatus; } public int status() { @@ -304,9 +329,10 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl } public int frameCount() throws IncompatibleThreadStateException { + LocalCache snapshot = localCache; try { - if (localCache.frameCount == -1) { - localCache.frameCount = JDWP.ThreadReference.FrameCount + if (snapshot.frameCount == -1) { + snapshot.frameCount = JDWP.ThreadReference.FrameCount .process(vm, this).frameCount; } } catch (JDWPException exc) { @@ -318,7 +344,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl throw exc.toJDIException(); } } - return localCache.frameCount; + return snapshot.frameCount; } public List frames() throws IncompatibleThreadStateException { @@ -335,22 +361,22 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl * local is known to be non-null. Should only be called from * a sync method. */ - private boolean isSubrange(LocalCache localCache, + private boolean isSubrange(LocalCache snapshot, int start, int length) { - if (start < localCache.framesStart) { + if (start < snapshot.framesStart) { return false; } if (length == -1) { - return (localCache.framesLength == -1); + return (snapshot.framesLength == -1); } - if (localCache.framesLength == -1) { - if ((start + length) > (localCache.framesStart + - localCache.frames.size())) { + if (snapshot.framesLength == -1) { + if ((start + length) > (snapshot.framesStart + + snapshot.frames.size())) { throw new IndexOutOfBoundsException(); } return true; } - return ((start + length) <= (localCache.framesStart + localCache.framesLength)); + return ((start + length) <= (snapshot.framesStart + snapshot.framesLength)); } public List frames(int start, int length) @@ -371,14 +397,14 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl // Lock must be held while creating stack frames so if that two threads // do this at the same time, one won't clobber the subset created by the other. - + LocalCache snapshot = localCache; try { - if (localCache.frames == null || !isSubrange(localCache, start, length)) { + if (snapshot.frames == null || !isSubrange(snapshot, start, length)) { JDWP.ThreadReference.Frames.Frame[] jdwpFrames = JDWP.ThreadReference.Frames. process(vm, this, start, length).frames; int count = jdwpFrames.length; - localCache.frames = new ArrayList(count); + snapshot.frames = new ArrayList(count); for (int i = 0; i ownedMonitors() throws IncompatibleThreadStateException { + LocalCache snapshot = localCache; try { - if (localCache.ownedMonitors == null) { - localCache.ownedMonitors = Arrays.asList( + if (snapshot.ownedMonitors == null) { + snapshot.ownedMonitors = Arrays.asList( (ObjectReference[])JDWP.ThreadReference.OwnedMonitors. process(vm, this).owned); if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) { vm.printTrace(description() + " temporarily caching owned monitors"+ - " (count = " + localCache.ownedMonitors.size() + ")"); + " (count = " + snapshot.ownedMonitors.size() + ")"); } } } catch (JDWPException exc) { @@ -435,54 +462,57 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl throw exc.toJDIException(); } } - return localCache.ownedMonitors; + return snapshot.ownedMonitors; } public ObjectReference currentContendedMonitor() throws IncompatibleThreadStateException { + LocalCache snapshot = localCache; try { - if (localCache.contendedMonitor == null && - !localCache.triedCurrentContended) { - localCache.contendedMonitor = JDWP.ThreadReference.CurrentContendedMonitor. + if (snapshot.contendedMonitor == null && + !snapshot.triedCurrentContended) { + snapshot.contendedMonitor = JDWP.ThreadReference.CurrentContendedMonitor. process(vm, this).monitor; - localCache.triedCurrentContended = true; - if ((localCache.contendedMonitor != null) && + snapshot.triedCurrentContended = true; + if ((snapshot.contendedMonitor != null) && ((vm.traceFlags & vm.TRACE_OBJREFS) != 0)) { vm.printTrace(description() + " temporarily caching contended monitor"+ - " (id = " + localCache.contendedMonitor.uniqueID() + ")"); + " (id = " + snapshot.contendedMonitor.uniqueID() + ")"); } } } catch (JDWPException exc) { switch (exc.errorCode()) { + case JDWP.Error.THREAD_NOT_SUSPENDED: case JDWP.Error.INVALID_THREAD: /* zombie */ throw new IncompatibleThreadStateException(); default: throw exc.toJDIException(); } } - return localCache.contendedMonitor; + return snapshot.contendedMonitor; } public List ownedMonitorsAndFrames() throws IncompatibleThreadStateException { + LocalCache snapshot = localCache; try { - if (localCache.ownedMonitorsInfo == null) { + if (snapshot.ownedMonitorsInfo == null) { JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor[] minfo; minfo = JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.process(vm, this).owned; - localCache.ownedMonitorsInfo = new ArrayList(minfo.length); + snapshot.ownedMonitorsInfo = new ArrayList(minfo.length); for (int i=0; i < minfo.length; i++) { JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor mi = minfo[i]; MonitorInfo mon = new MonitorInfoImpl(vm, minfo[i].monitor, this, minfo[i].stack_depth); - localCache.ownedMonitorsInfo.add(mon); + snapshot.ownedMonitorsInfo.add(mon); } if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) { vm.printTrace(description() + " temporarily caching owned monitors"+ - " (count = " + localCache.ownedMonitorsInfo.size() + ")"); + " (count = " + snapshot.ownedMonitorsInfo.size() + ")"); } } @@ -495,7 +525,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl throw exc.toJDIException(); } } - return localCache.ownedMonitorsInfo; + return snapshot.ownedMonitorsInfo; } public void popFrames(StackFrame frame) throws IncompatibleThreadStateException { diff --git a/test/com/sun/jdi/SimulResumerTest.java b/test/com/sun/jdi/SimulResumerTest.java new file mode 100644 index 000000000..cf7568e8b --- /dev/null +++ b/test/com/sun/jdi/SimulResumerTest.java @@ -0,0 +1,278 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * @test + * @bug 6751643 + * @summary ThreadReference.ownedMonitors() can return null + * + * @author jjh + * + * @run build TestScaffold VMConnection TargetListener TargetAdapter + * @run compile -g SimulResumerTest.java + * @run main SimulResumerTest + */ +import com.sun.jdi.*; +import com.sun.jdi.event.*; +import com.sun.jdi.request.*; + +import java.util.*; + +/* + * This debuggee basically runs two threads each of + * which loop, hitting a bkpt in each iteration. + * + */ +class SimulResumerTarg extends Thread { + static boolean one = false; + static String name1 = "Thread 1"; + static String name2 = "Thread 2"; + static int count = 10000; + public static void main(String[] args) { + System.out.println("Howdy!"); + SimulResumerTarg t1 = new SimulResumerTarg(name1); + SimulResumerTarg t2 = new SimulResumerTarg(name2); + + t1.start(); + t2.start(); + } + + public SimulResumerTarg(String name) { + super(name); + } + + public void run() { + if (getName().equals(name1)) { + run1(); + } else { + run2(); + } + } + + public void bkpt1(int i) { + synchronized(name1) { + yield(); + } + } + + public void run1() { + int i = 0; + while (i < count) { + i++; + bkpt1(i); + } + } + + public void bkpt2(int i) { + synchronized(name2) { + yield(); + } + } + + public void run2() { + int i = 0; + while (i < count) { + i++; + bkpt2(i); + } + } +} + +/********** test program **********/ + +public class SimulResumerTest extends TestScaffold { + ReferenceType targetClass; + ThreadReference mainThread; + BreakpointRequest request1; + BreakpointRequest request2; + static volatile int bkpts = 0; + static int iters = 0; + Thread resumerThread; + static int waitTime = 100; + ThreadReference debuggeeThread1 = null; + ThreadReference debuggeeThread2 = null; + + SimulResumerTest (String args[]) { + super(args); + } + + public static void main(String[] args) throws Exception { + new SimulResumerTest(args).startTests(); + } + + /* BreakpointEvent handler */ + + public void breakpointReached(BreakpointEvent event) { + // save ThreadRefs for the two debuggee threads + ThreadReference thr = event.thread(); + if (bkpts == 0) { + resumerThread.start(); + debuggeeThread1 = thr; + System.out.println("thr1 = " + debuggeeThread1); + } + + if (debuggeeThread2 == null && thr != debuggeeThread1) { + debuggeeThread2 = thr; + System.out.println("thr2 = " + debuggeeThread2); + } + + synchronized("abc") { + bkpts++; + } + /** + if (bkpts >= SimulResumerTarg.count * 2) { + resumerThread.interrupt(); + } + *****/ + + } + + /********** test core **********/ + + void check(ThreadReference thr) { + // This calls each ThreadReference method that could fail due to the bug + // that occurs if a resume is done while a call to the method is in process. + String kind = ""; + if (thr != null) { + try { + kind = "ownedMonitors()"; + System.out.println("kind = " + kind); + if (thr.ownedMonitors() == null) { + failure("failure: ownedMonitors = null"); + } + + kind = "ownedMonitorsAndFrames()"; + System.out.println("kind = " + kind); + if (thr.ownedMonitorsAndFrames() == null) { + failure("failure: ownedMonitorsAndFrames = null"); + } + + kind = "currentContendedMonitor()"; + System.out.println("kind = " + kind); + thr.currentContendedMonitor(); + // no failure return value here; could cause an NPE + + kind = "frames()"; + System.out.println("kind = " + kind); + List frames = thr.frames(); + // no failure return value here; could cause an NPE + + int nframes = frames.size(); + if (nframes > 0) { + // hmm, how could it ever be 0? + kind = "frames(0, size - 1)"; + System.out.println("kind = " + kind); + thr.frames(0, frames.size() - 1); + } + + kind = "frameCount()"; + System.out.println("kind = " + kind); + if (thr.frameCount() == -1) { + failure("failure: frameCount = -1"); + } + + kind = "name()"; + System.out.println("kind = " + kind); + if (thr.name() == null) { + failure("failure: name = null"); + } + + kind = "status()"; + System.out.println("kind = " + kind); + if (thr.status() < 0) { + failure("failure: status < 0"); + } + + } catch (IncompatibleThreadStateException ee) { + // ignore + } catch (VMDisconnectedException ee) { + // This is how we stop. The debuggee runs to completion + // and we get this exception. + throw ee; + } catch (Exception ee) { + failure("failure: Got exception from " + kind + ": " + ee ); + } + } + } + + protected void runTests() throws Exception { + /* + * Get to the top of main() + * to determine targetClass and mainThread + */ + BreakpointEvent bpe = startToMain("SimulResumerTarg"); + targetClass = bpe.location().declaringType(); + mainThread = bpe.thread(); + EventRequestManager erm = vm().eventRequestManager(); + final Thread mainThread = Thread.currentThread(); + + /* + * Set event requests + */ + Location loc1 = findMethod(targetClass, "bkpt1", "(I)V").location(); + Location loc2 = findMethod(targetClass, "bkpt2", "(I)V").location(); + request1 = erm.createBreakpointRequest(loc1); + request2 = erm.createBreakpointRequest(loc2); + request1.enable(); + request2.enable(); + + /* + * This thread will be started when we get the first bkpt. + */ + resumerThread = new Thread("test resumer") { + public void run() { + while (true) { + iters++; + System.out.println("bkpts = " + bkpts + ", iters = " + iters); + try { + Thread.sleep(waitTime); + check(debuggeeThread1); + check(debuggeeThread2); + } catch (InterruptedException ee) { + // If the test completes, this occurs. + println("resumer Interrupted"); + break; + } catch (VMDisconnectedException ee) { + println("VMDisconnectedException"); + break; + } + } + } + }; + + /* + * resume the target, listening for events + */ + listenUntilVMDisconnect(); + resumerThread.interrupt(); + /* + * deal with results of test + * if anything has called failure("foo") testFailed will be true + */ + if (!testFailed) { + println("SimulResumerTest: passed; bkpts = " + bkpts + ", iters = " + iters); + } else { + throw new Exception("SimulResumerTest: failed; bkpts = " + bkpts + ", iters = " + iters); + } + } +} -- GitLab From d2b279b058bd90637b6809df1dc97c4ea4d0ea8a Mon Sep 17 00:00:00 2001 From: ksrini Date: Wed, 1 Oct 2008 09:04:42 -0700 Subject: [PATCH 135/139] 4459600: java -jar fails to run Main-Class if classname followed by whitespace. Summary: Fixed whitespace trimming in the manifest as well as post review comments on CR: 6742159 Reviewed-by: darcy, dholmes --- .../classes/sun/launcher/LauncherHelper.java | 15 ++++--------- test/tools/launcher/Arrrghs.java | 18 +++++++++++----- test/tools/launcher/TestHelper.java | 21 +++++++++---------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/share/classes/sun/launcher/LauncherHelper.java b/src/share/classes/sun/launcher/LauncherHelper.java index 96ad48446..8cca710da 100644 --- a/src/share/classes/sun/launcher/LauncherHelper.java +++ b/src/share/classes/sun/launcher/LauncherHelper.java @@ -151,7 +151,7 @@ public enum LauncherHelper { throw new IOException("no main mainifest attributes, in " + jarname); } - return mainAttrs.getValue(MAIN_CLASS); + return mainAttrs.getValue(MAIN_CLASS).trim(); } finally { if (jarFile != null) { jarFile.close(); @@ -207,10 +207,9 @@ public enum LauncherHelper { throw new RuntimeException("Main method not found in " + classname); } /* - * Usually the getMethod (above) will choose the correct method, based - * on its modifiers and parameter types, the only check required is the - * getReturnType check as getMethod does not check for this, all the - * other modifier tests are redundant, and are simply here for safety. + * getMethod (above) will choose the correct method, based + * on its name and parameter type, however, we still have to + * ensure that the method is static and returns a void. */ int mod = method.getModifiers(); if (!Modifier.isStatic(mod)) { @@ -219,12 +218,6 @@ public enum LauncherHelper { throw new RuntimeException("Main method is not static in class " + classname); } - if (!Modifier.isPublic(mod)) { - ostream.println(getLocalizedMessage("java.launcher.cls.error2", - "public", classname)); - throw new RuntimeException("Main method is not public in class " + - classname); - } Class rType = method.getReturnType(); if (!rType.isPrimitive() || !rType.getName().equals("void")) { ostream.println(getLocalizedMessage("java.launcher.cls.error3", diff --git a/test/tools/launcher/Arrrghs.java b/test/tools/launcher/Arrrghs.java index 6b14abe50..df9e993a1 100644 --- a/test/tools/launcher/Arrrghs.java +++ b/test/tools/launcher/Arrrghs.java @@ -24,7 +24,7 @@ /** * @test * @compile -XDignore.symbol.file Arrrghs.java TestHelper.java - * @bug 5030233 6214916 6356475 6571029 6684582 + * @bug 5030233 6214916 6356475 6571029 6684582 6742159 4459600 * @run main Arrrghs * @summary Argument parsing validation. */ @@ -232,7 +232,8 @@ public class Arrrghs { TestHelper.TestResult tr = null; // a missing class - TestHelper.createJar(new File("some.jar"), new File("Foo"), (String[])null); + TestHelper.createJar("MIA", new File("some.jar"), new File("Foo"), + (String[])null); tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); tr.contains("MIA"); System.out.println(tr); @@ -276,13 +277,13 @@ public class Arrrghs { // incorrect method type - non-static TestHelper.createJar(new File("some.jar"), new File("Foo"), - "public void main(Object[] args){}"); + "public void main(String[] args){}"); tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); - tr.contains("Error: Main method not found in class Foo"); + tr.contains("Error: Main method is not static in class Foo"); System.out.println(tr); // use classpath to check tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo"); - tr.contains("Error: Main method not found in class Foo"); + tr.contains("Error: Main method is not static in class Foo"); System.out.println(tr); // amongst a potpourri of kindred main methods, is the right one chosen ? @@ -300,6 +301,13 @@ public class Arrrghs { tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo"); tr.contains("THE_CHOSEN_ONE"); System.out.println(tr); + + // test for extraneous whitespace in the Main-Class attribute + TestHelper.createJar(" Foo ", new File("some.jar"), new File("Foo"), + "public static void main(String... args){}"); + tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); + tr.checkPositive(); + System.out.println(tr); } /** diff --git a/test/tools/launcher/TestHelper.java b/test/tools/launcher/TestHelper.java index bd9009dc5..fb63114b6 100644 --- a/test/tools/launcher/TestHelper.java +++ b/test/tools/launcher/TestHelper.java @@ -72,8 +72,8 @@ public enum TestHelper { } /* - * A generic jar file creator which creates the java file, compiles it - * and jar's it up for use. + * A convenience method to create a java file, compile and jar it up, using + * the sole class file name in the jar, as the Main-Class attribute value. */ static void createJar(File jarName, File mainClass, String... mainDefs) throws FileNotFoundException { @@ -81,11 +81,13 @@ public enum TestHelper { } /* - * A method which takes manifest entry to specify a specific manifest - * Main-Class name. + * A generic jar file creator to create a java file, compile it + * and jar it up, a specific Main-Class entry name in the + * manifest can be specified or a null to use the sole class file name + * as the Main-Class attribute value. */ - static void createJar(String mEntry, File jarName, File mainClass, String... mainDefs) - throws FileNotFoundException { + static void createJar(String mEntry, File jarName, File mainClass, + String... mainDefs) throws FileNotFoundException { if (jarName.exists()) { jarName.delete(); } @@ -105,10 +107,7 @@ public enum TestHelper { if (compiler.run(null, null, null, compileArgs) != 0) { throw new RuntimeException("compilation failed " + mainClass + ".java"); } - - if (mEntry == null && mainDefs == null) { - mEntry = "MIA"; - } else { + if (mEntry == null) { mEntry = mainClass.getName(); } String jarArgs[] = { @@ -125,7 +124,7 @@ public enum TestHelper { } /* - * A method which executes a java cmd and returs the results in a container + * A method which executes a java cmd and returns the results in a container */ static TestResult doExec(String...cmds) { String cmdStr = ""; -- GitLab From 5fdbd9856eaa5b332f8bee5fb50886fe798aa6fb Mon Sep 17 00:00:00 2001 From: xdono Date: Thu, 2 Oct 2008 19:58:32 -0700 Subject: [PATCH 136/139] 6754988: Update copyright year Summary: Update for files that have been modified starting July 2008 Reviewed-by: ohair, tbell --- make/com/sun/inputmethods/indicim/Makefile | 2 +- make/com/sun/inputmethods/thaiim/Makefile | 2 +- make/com/sun/java/pack/Makefile | 2 +- make/com/sun/security/auth/module/Makefile | 2 +- make/common/BuildToolJar.gmk | 2 +- make/common/Demo.gmk | 2 +- make/common/Library.gmk | 2 +- make/common/internal/ImportComponents.gmk | 2 +- make/common/shared/Defs-java.gmk | 2 +- make/common/shared/Defs-windows.gmk | 2 +- make/java/fdlibm/Makefile | 2 +- make/java/hpi/windows/Makefile | 2 +- make/java/java/FILES_java.gmk | 2 +- make/java/java_crw_demo/Makefile | 2 +- make/java/java_hprof_demo/Makefile | 2 +- make/java/management/Makefile | 2 +- make/java/net/Makefile | 2 +- make/java/net/mapfile-vers | 2 +- make/java/nio/FILES_java.gmk | 2 +- make/java/nio/genCoder.sh | 2 +- make/java/npt/Makefile | 2 +- make/java/zip/Makefile | 2 +- make/javax/swing/beaninfo/SwingBeans.gmk | 2 +- make/jpda/back/Makefile | 2 +- make/jpda/transport/shmem/Makefile | 2 +- make/jpda/transport/socket/Makefile | 2 +- make/jprt.properties | 2 +- make/mksample/nio/Makefile | 2 +- make/mksample/nio/multicast/Makefile | 2 +- make/netbeans/jconsole/build.properties | 2 +- make/netbeans/jconsole/build.xml | 2 +- make/sun/cmm/kcms/Makefile | 2 +- make/sun/font/t2k/Makefile | 2 +- make/sun/image/generic/Makefile | 2 +- make/sun/image/vis/Makefile | 2 +- make/sun/jconsole/Makefile | 2 +- make/sun/jdbc/Makefile | 2 +- make/sun/jpeg/Makefile | 2 +- make/sun/net/spi/nameservice/dns/Makefile | 2 +- make/sun/text/Makefile | 2 +- src/share/back/ThreadReferenceImpl.c | 2 +- src/share/back/eventFilter.c | 2 +- src/share/back/transport.c | 2 +- src/share/classes/com/sun/jmx/defaults/JmxProperties.java | 2 +- src/share/classes/com/sun/jmx/event/DaemonThreadFactory.java | 2 +- src/share/classes/com/sun/jmx/event/EventBuffer.java | 2 +- src/share/classes/com/sun/jmx/event/EventClientFactory.java | 2 +- src/share/classes/com/sun/jmx/event/EventConnection.java | 2 +- src/share/classes/com/sun/jmx/event/EventParams.java | 2 +- src/share/classes/com/sun/jmx/event/LeaseManager.java | 2 +- src/share/classes/com/sun/jmx/event/LeaseRenewer.java | 2 +- src/share/classes/com/sun/jmx/event/ReceiverBuffer.java | 2 +- src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java | 2 +- src/share/classes/com/sun/jmx/mbeanserver/DynamicMBean2.java | 2 +- src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java | 2 +- src/share/classes/com/sun/jmx/mbeanserver/NotifySupport.java | 2 +- .../classes/com/sun/jmx/mbeanserver/PerThreadGroupPool.java | 2 +- .../com/sun/jmx/mbeanserver/StandardMBeanIntrospector.java | 2 +- .../classes/com/sun/jmx/mbeanserver/SunJmxMBeanServer.java | 2 +- .../com/sun/jmx/remote/internal/ClientCommunicatorAdmin.java | 2 +- .../classes/com/sun/jmx/remote/internal/ProxyInputStream.java | 2 +- src/share/classes/com/sun/jmx/remote/internal/ProxyRef.java | 2 +- src/share/classes/com/sun/jmx/remote/util/EnvHelp.java | 2 +- .../classes/com/sun/jmx/remote/util/EventClientConnection.java | 2 +- src/share/classes/com/sun/jmx/snmp/tasks/ThreadService.java | 2 +- src/share/classes/com/sun/tools/jdi/MonitorInfoImpl.java | 2 +- src/share/classes/com/sun/tools/jdi/ThreadReferenceImpl.java | 2 +- src/share/classes/com/sun/tools/jdi/VMAction.java | 2 +- src/share/classes/com/sun/tools/jdi/VMState.java | 2 +- src/share/classes/java/awt/EventQueue.java | 2 +- src/share/classes/java/lang/AbstractStringBuilder.java | 2 +- src/share/classes/java/lang/StringBuffer.java | 2 +- src/share/classes/java/lang/StringBuilder.java | 2 +- src/share/classes/java/net/Inet6Address.java | 2 +- src/share/classes/java/nio/Bits.java | 2 +- src/share/classes/java/nio/ByteBufferAs-X-Buffer.java | 2 +- src/share/classes/java/nio/Direct-X-Buffer.java | 2 +- src/share/classes/java/nio/Heap-X-Buffer.java | 2 +- src/share/classes/java/nio/X-Buffer.java | 2 +- src/share/classes/java/nio/channels/DatagramChannel.java | 2 +- src/share/classes/java/nio/channels/SelectionKey.java | 2 +- src/share/classes/java/nio/channels/ServerSocketChannel.java | 2 +- src/share/classes/java/nio/channels/SocketChannel.java | 2 +- src/share/classes/java/nio/channels/exceptions | 2 +- src/share/classes/java/nio/channels/package-info.java | 2 +- src/share/classes/java/nio/channels/spi/AbstractSelector.java | 2 +- src/share/classes/java/nio/charset/Charset-X-Coder.java | 2 +- src/share/classes/java/nio/charset/CoderResult.java | 2 +- src/share/classes/java/util/CurrencyData.properties | 2 +- src/share/classes/java/util/EnumSet.java | 2 +- src/share/classes/java/util/Timer.java | 2 +- src/share/classes/javax/management/Description.java | 2 +- src/share/classes/javax/management/Descriptor.java | 2 +- src/share/classes/javax/management/DescriptorFields.java | 2 +- src/share/classes/javax/management/DescriptorKey.java | 2 +- src/share/classes/javax/management/DynamicWrapperMBean.java | 2 +- src/share/classes/javax/management/Impact.java | 2 +- .../classes/javax/management/InstanceNotFoundException.java | 2 +- src/share/classes/javax/management/MBean.java | 2 +- src/share/classes/javax/management/MBeanOperationInfo.java | 2 +- src/share/classes/javax/management/MBeanPermission.java | 2 +- src/share/classes/javax/management/MBeanRegistration.java | 2 +- src/share/classes/javax/management/MBeanServerConnection.java | 2 +- src/share/classes/javax/management/MBeanServerDelegate.java | 2 +- src/share/classes/javax/management/MBeanServerFactory.java | 2 +- src/share/classes/javax/management/MBeanServerNotification.java | 2 +- src/share/classes/javax/management/ManagedAttribute.java | 2 +- src/share/classes/javax/management/ManagedOperation.java | 2 +- .../javax/management/NotificationBroadcasterSupport.java | 2 +- src/share/classes/javax/management/NotificationInfo.java | 2 +- src/share/classes/javax/management/NotificationInfos.java | 2 +- src/share/classes/javax/management/SendNotification.java | 2 +- src/share/classes/javax/management/StandardEmitterMBean.java | 2 +- src/share/classes/javax/management/event/EventClient.java | 2 +- .../classes/javax/management/event/EventClientDelegate.java | 2 +- .../javax/management/event/EventClientDelegateMBean.java | 2 +- .../javax/management/event/EventClientNotFoundException.java | 2 +- src/share/classes/javax/management/event/EventConsumer.java | 2 +- src/share/classes/javax/management/event/EventForwarder.java | 2 +- src/share/classes/javax/management/event/EventReceiver.java | 2 +- src/share/classes/javax/management/event/EventRelay.java | 2 +- .../classes/javax/management/event/FetchingEventForwarder.java | 2 +- .../classes/javax/management/event/FetchingEventRelay.java | 2 +- src/share/classes/javax/management/event/ListenerInfo.java | 2 +- .../classes/javax/management/event/NotificationManager.java | 2 +- .../classes/javax/management/event/RMIPushEventForwarder.java | 2 +- src/share/classes/javax/management/event/RMIPushEventRelay.java | 2 +- src/share/classes/javax/management/event/RMIPushServer.java | 2 +- .../javax/management/openmbean/CompositeDataSupport.java | 2 +- .../classes/javax/management/openmbean/TabularDataSupport.java | 2 +- src/share/classes/javax/management/remote/JMXConnector.java | 2 +- .../classes/javax/management/remote/JMXConnectorServer.java | 2 +- .../javax/management/remote/JMXConnectorServerMBean.java | 2 +- .../classes/javax/management/remote/rmi/RMIConnectionImpl.java | 2 +- src/share/classes/javax/net/ssl/SSLServerSocket.java | 2 +- src/share/classes/sun/font/NullFontScaler.java | 2 +- .../perfdata/monitor/protocol/local/LocalMonitoredVm.java | 2 +- .../perfdata/monitor/protocol/local/MonitoredHostProvider.java | 2 +- src/share/classes/sun/misc/SharedSecrets.java | 2 +- src/share/classes/sun/net/httpserver/ChunkedOutputStream.java | 2 +- src/share/classes/sun/net/httpserver/ServerImpl.java | 2 +- src/share/classes/sun/nio/ch/DatagramChannelImpl.java | 2 +- src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java | 2 +- src/share/classes/sun/nio/ch/Net.java | 2 +- src/share/classes/sun/nio/ch/SelectorImpl.java | 2 +- src/share/classes/sun/nio/ch/SelectorProviderImpl.java | 2 +- src/share/classes/sun/nio/ch/ServerSocketAdaptor.java | 2 +- src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java | 2 +- src/share/classes/sun/nio/ch/SocketChannelImpl.java | 2 +- src/share/classes/sun/nio/cs/standard-charsets | 2 +- .../sun/reflect/generics/factory/CoreReflectionFactory.java | 2 +- src/share/classes/sun/text/resources/FormatData_sv.java | 2 +- src/share/classes/sun/tools/jconsole/Plotter.java | 2 +- src/share/classes/sun/tools/jmap/JMap.java | 2 +- src/share/demo/jvmti/hprof/hprof_io.c | 2 +- src/share/demo/jvmti/hprof/hprof_util.c | 2 +- src/share/native/java/net/net_util.h | 2 +- src/share/native/java/nio/Bits.c | 2 +- src/share/native/sun/nio/ch/genSocketOptionRegistry.c | 2 +- src/share/transport/shmem/shmemBack.c | 2 +- src/share/transport/shmem/shmemBase.c | 2 +- src/share/transport/socket/socketTransport.c | 2 +- src/share/transport/socket/sysSocket.h | 2 +- src/solaris/classes/sun/nio/ch/DevPollSelectorImpl.java | 2 +- src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java | 2 +- src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java | 2 +- .../native/com/sun/media/sound/PLATFORM_API_SolarisOS_Utils.h | 2 +- src/solaris/native/java/net/PlainDatagramSocketImpl.c | 2 +- src/solaris/native/java/net/SocketInputStream.c | 2 +- src/solaris/native/java/net/SocketOutputStream.c | 2 +- src/solaris/native/java/net/linux_close.c | 2 +- src/solaris/native/java/net/net_util_md.c | 2 +- src/solaris/native/java/nio/MappedByteBuffer.c | 2 +- src/solaris/native/sun/nio/ch/DatagramChannelImpl.c | 2 +- src/solaris/native/sun/nio/ch/EPollArrayWrapper.c | 2 +- src/solaris/native/sun/nio/ch/FileKey.c | 2 +- src/solaris/native/sun/nio/ch/InheritedChannel.c | 2 +- src/solaris/native/sun/nio/ch/Net.c | 2 +- src/solaris/native/sun/nio/ch/ServerSocketChannelImpl.c | 2 +- src/solaris/native/sun/nio/ch/SocketChannelImpl.c | 2 +- src/solaris/native/sun/nio/ch/nio_util.h | 2 +- src/solaris/transport/socket/socket_md.c | 2 +- src/windows/classes/sun/nio/ch/PipeImpl.java | 2 +- src/windows/classes/sun/nio/ch/WindowsSelectorImpl.java | 2 +- src/windows/javavm/export/jvm_md.h | 2 +- src/windows/native/java/net/NetworkInterface.c | 2 +- src/windows/native/java/net/NetworkInterface.h | 2 +- src/windows/native/java/net/NetworkInterface_win9x.c | 2 +- src/windows/native/java/net/NetworkInterface_winXP.c | 2 +- src/windows/native/java/net/SocketOutputStream.c | 2 +- src/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c | 2 +- src/windows/native/java/net/TwoStacksPlainSocketImpl.c | 2 +- src/windows/native/java/net/net_util_md.h | 2 +- src/windows/native/sun/net/dns/ResolverConfigurationImpl.c | 2 +- src/windows/native/sun/nio/ch/DatagramChannelImpl.c | 2 +- src/windows/native/sun/nio/ch/Net.c | 2 +- src/windows/native/sun/nio/ch/ServerSocketChannelImpl.c | 2 +- src/windows/native/sun/nio/ch/SocketChannelImpl.c | 2 +- src/windows/native/sun/nio/ch/WindowsSelectorImpl.c | 2 +- src/windows/native/sun/windows/ComCtl32Util.cpp | 2 +- src/windows/native/sun/windows/ComCtl32Util.h | 2 +- src/windows/native/sun/windows/awt_TextArea.cpp | 2 +- src/windows/transport/socket/socket_md.c | 2 +- src/windows/transport/socket/socket_md.h | 2 +- test/com/sun/jdi/ClassesByName2Test.java | 2 +- test/com/sun/jdi/ConnectedVMs.java | 2 +- test/com/sun/jdi/MonitorFrameInfo.java | 2 +- test/com/sun/jdi/Solaris32AndSolaris64Test.sh | 2 +- test/com/sun/jdi/SourceNameFilterTest.java | 2 +- test/com/sun/jdi/VMConnection.java | 2 +- test/com/sun/net/httpserver/bugs/B6744329.java | 2 +- .../lang/management/ManagementFactory/ThreadMXBeanProxy.java | 2 +- test/java/lang/management/ThreadMXBean/Locks.java | 2 +- test/java/net/CookieHandler/TestHttpCookie.java | 2 +- test/java/net/Inet6Address/serialize/Serialize.java | 2 +- test/java/nio/channels/FileChannel/ExpandingMap.java | 2 +- test/java/nio/channels/Selector/Wakeup.java | 2 +- test/javax/management/Introspector/AnnotatedMBeanTest.java | 2 +- .../management/Introspector/AnnotatedNotificationInfoTest.java | 2 +- test/javax/management/Introspector/MBeanDescriptionTest.java | 2 +- test/javax/management/Introspector/ParameterNameTest.java | 2 +- test/javax/management/Introspector/ResourceInjectionTest.java | 2 +- test/javax/management/Introspector/annot/Name.java | 2 +- test/javax/management/MBeanInfo/NotificationInfoTest.java | 2 +- test/javax/management/MBeanServer/DynamicWrapperMBeanTest.java | 2 +- test/javax/management/MBeanServer/OldMBeanServerTest.java | 2 +- .../management/MBeanServerFactory/NamedMBeanServerTest.java | 2 +- test/javax/management/ObjectName/ApplyWildcardTest.java | 2 +- test/javax/management/ObjectName/SerialCompatTest.java | 2 +- test/javax/management/eventService/AddRemoveListenerTest.java | 2 +- test/javax/management/eventService/CustomForwarderTest.java | 2 +- test/javax/management/eventService/EventManagerTest.java | 2 +- test/javax/management/eventService/FetchingTest.java | 2 +- test/javax/management/eventService/LeaseTest.java | 2 +- test/javax/management/eventService/ListenerTest.java | 2 +- .../javax/management/eventService/NotSerializableNotifTest.java | 2 +- test/javax/management/eventService/PublishTest.java | 2 +- .../management/eventService/ReconnectableConnectorTest.java | 2 +- test/javax/management/eventService/SharingThreadTest.java | 2 +- test/javax/management/mxbean/ComparatorExceptionTest.java | 2 +- test/javax/management/mxbean/GenericArrayTypeTest.java | 2 +- test/javax/management/mxbean/LeakTest.java | 2 +- test/javax/management/mxbean/MBeanOperationInfoTest.java | 2 +- test/javax/management/mxbean/MXBeanTest.java | 2 +- test/javax/management/mxbean/SameObjectTwoNamesTest.java | 2 +- test/javax/management/mxbean/ThreadMXBeanTest.java | 2 +- test/javax/management/mxbean/TigerMXBean.java | 2 +- .../management/remote/mandatory/connection/CloseServerTest.java | 2 +- .../management/remote/mandatory/connection/DeadLockTest.java | 2 +- .../management/remote/mandatory/connection/IdleTimeoutTest.java | 2 +- .../management/remote/mandatory/connection/RMIExitTest.java | 2 +- .../management/remote/mandatory/connection/ReconnectTest.java | 2 +- .../management/remote/mandatory/loading/MissingClassTest.java | 2 +- test/javax/management/remote/mandatory/notif/AddRemoveTest.java | 2 +- test/javax/management/remote/mandatory/notif/DiffHBTest.java | 2 +- .../remote/mandatory/notif/EmptyDomainNotificationTest.java | 2 +- .../management/remote/mandatory/notif/ListenerScaleTest.java | 2 +- .../remote/mandatory/notif/NotifBufferSizePropertyNameTest.java | 2 +- .../remote/mandatory/notif/NotifReconnectDeadlockTest.java | 2 +- .../mandatory/notif/NotificationAccessControllerTest.java | 2 +- .../remote/mandatory/notif/NotificationBufferCreationTest.java | 2 +- .../remote/mandatory/notif/NotificationBufferDeadlockTest.java | 2 +- .../remote/mandatory/notif/NotificationEmissionTest.java | 2 +- test/javax/management/remote/mandatory/notif/RMINotifTest.java | 2 +- .../management/remote/mandatory/notif/UnexpectedNotifTest.java | 2 +- test/javax/script/E4XErrorTest.java | 2 +- test/javax/script/JavaScriptScopeTest.java | 2 +- test/javax/script/NullUndefinedVarTest.java | 2 +- test/javax/script/PluggableContextTest.java | 2 +- test/javax/script/ProviderTest.java | 2 +- test/javax/script/RhinoExceptionTest.java | 2 +- test/javax/script/Test1.java | 2 +- test/javax/script/Test2.java | 2 +- test/javax/script/Test3.java | 2 +- test/javax/script/Test4.java | 2 +- test/javax/script/Test5.java | 2 +- test/javax/script/Test6.java | 2 +- test/javax/script/Test7.java | 2 +- test/javax/script/Test8.java | 2 +- test/javax/script/VersionTest.java | 2 +- test/sun/tools/jrunscript/common.sh | 2 +- test/sun/tools/jrunscript/jrunscript-DTest.sh | 2 +- test/sun/tools/jrunscript/jrunscript-argsTest.sh | 2 +- test/sun/tools/jrunscript/jrunscript-cpTest.sh | 2 +- test/sun/tools/jrunscript/jrunscript-eTest.sh | 2 +- test/sun/tools/jrunscript/jrunscript-fTest.sh | 2 +- test/sun/tools/jrunscript/jrunscriptTest.sh | 2 +- 287 files changed, 287 insertions(+), 287 deletions(-) diff --git a/make/com/sun/inputmethods/indicim/Makefile b/make/com/sun/inputmethods/indicim/Makefile index 4c7dc97ee..e6e7c0dfd 100644 --- a/make/com/sun/inputmethods/indicim/Makefile +++ b/make/com/sun/inputmethods/indicim/Makefile @@ -1,5 +1,5 @@ # -# Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/com/sun/inputmethods/thaiim/Makefile b/make/com/sun/inputmethods/thaiim/Makefile index 9b709d7a9..d4f47a69d 100644 --- a/make/com/sun/inputmethods/thaiim/Makefile +++ b/make/com/sun/inputmethods/thaiim/Makefile @@ -1,5 +1,5 @@ # -# Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/com/sun/java/pack/Makefile b/make/com/sun/java/pack/Makefile index 9229e3f4c..c8b4e9909 100644 --- a/make/com/sun/java/pack/Makefile +++ b/make/com/sun/java/pack/Makefile @@ -1,5 +1,5 @@ # -# Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/com/sun/security/auth/module/Makefile b/make/com/sun/security/auth/module/Makefile index d9f705d4d..d7a4abdf1 100644 --- a/make/com/sun/security/auth/module/Makefile +++ b/make/com/sun/security/auth/module/Makefile @@ -1,5 +1,5 @@ # -# Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/common/BuildToolJar.gmk b/make/common/BuildToolJar.gmk index 638e56b42..2c5ad8e3f 100644 --- a/make/common/BuildToolJar.gmk +++ b/make/common/BuildToolJar.gmk @@ -1,5 +1,5 @@ # -# Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/common/Demo.gmk b/make/common/Demo.gmk index 221bee186..12a3ce105 100644 --- a/make/common/Demo.gmk +++ b/make/common/Demo.gmk @@ -1,5 +1,5 @@ # -# Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/common/Library.gmk b/make/common/Library.gmk index 2aa247f81..3e4318ecd 100644 --- a/make/common/Library.gmk +++ b/make/common/Library.gmk @@ -1,5 +1,5 @@ # -# Copyright 1995-2007 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 1995-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/common/internal/ImportComponents.gmk b/make/common/internal/ImportComponents.gmk index d22783d65..743659407 100644 --- a/make/common/internal/ImportComponents.gmk +++ b/make/common/internal/ImportComponents.gmk @@ -1,5 +1,5 @@ # -# Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/common/shared/Defs-java.gmk b/make/common/shared/Defs-java.gmk index f6484f492..9bfb96da4 100644 --- a/make/common/shared/Defs-java.gmk +++ b/make/common/shared/Defs-java.gmk @@ -1,5 +1,5 @@ # -# Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/common/shared/Defs-windows.gmk b/make/common/shared/Defs-windows.gmk index d0be243d8..35fa8ce34 100644 --- a/make/common/shared/Defs-windows.gmk +++ b/make/common/shared/Defs-windows.gmk @@ -1,5 +1,5 @@ # -# Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/java/fdlibm/Makefile b/make/java/fdlibm/Makefile index b2d90ecfc..eeb5e744f 100644 --- a/make/java/fdlibm/Makefile +++ b/make/java/fdlibm/Makefile @@ -1,5 +1,5 @@ # -# Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/java/hpi/windows/Makefile b/make/java/hpi/windows/Makefile index fa04ec6a3..b82d8853a 100644 --- a/make/java/hpi/windows/Makefile +++ b/make/java/hpi/windows/Makefile @@ -1,5 +1,5 @@ # -# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/java/java/FILES_java.gmk b/make/java/java/FILES_java.gmk index 2531ce723..577c58d9b 100644 --- a/make/java/java/FILES_java.gmk +++ b/make/java/java/FILES_java.gmk @@ -1,5 +1,5 @@ # -# Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/java/java_crw_demo/Makefile b/make/java/java_crw_demo/Makefile index c65a84df1..6c45bcbd4 100644 --- a/make/java/java_crw_demo/Makefile +++ b/make/java/java_crw_demo/Makefile @@ -1,5 +1,5 @@ # -# Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/java/java_hprof_demo/Makefile b/make/java/java_hprof_demo/Makefile index 58805eeb6..4e1ef2a31 100644 --- a/make/java/java_hprof_demo/Makefile +++ b/make/java/java_hprof_demo/Makefile @@ -1,5 +1,5 @@ # -# Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/java/management/Makefile b/make/java/management/Makefile index c1c4edbdf..466d2212a 100644 --- a/make/java/management/Makefile +++ b/make/java/management/Makefile @@ -1,5 +1,5 @@ # -# Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/java/net/Makefile b/make/java/net/Makefile index 4141294c0..b9a3defdb 100644 --- a/make/java/net/Makefile +++ b/make/java/net/Makefile @@ -1,5 +1,5 @@ # -# Copyright 1995-2007 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 1995-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/java/net/mapfile-vers b/make/java/net/mapfile-vers index e7da6186e..d9803f8f9 100644 --- a/make/java/net/mapfile-vers +++ b/make/java/net/mapfile-vers @@ -1,5 +1,5 @@ # -# Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/java/nio/FILES_java.gmk b/make/java/nio/FILES_java.gmk index 8a6177b0a..0e8c70906 100644 --- a/make/java/nio/FILES_java.gmk +++ b/make/java/nio/FILES_java.gmk @@ -1,5 +1,5 @@ # -# Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/java/nio/genCoder.sh b/make/java/nio/genCoder.sh index 769b98cd5..b5f4faaa7 100644 --- a/make/java/nio/genCoder.sh +++ b/make/java/nio/genCoder.sh @@ -1,7 +1,7 @@ #! /bin/sh # -# Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/java/npt/Makefile b/make/java/npt/Makefile index fe9eb0ad4..57e6a6638 100644 --- a/make/java/npt/Makefile +++ b/make/java/npt/Makefile @@ -1,5 +1,5 @@ # -# Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/java/zip/Makefile b/make/java/zip/Makefile index 00b381d50..48e8c3904 100644 --- a/make/java/zip/Makefile +++ b/make/java/zip/Makefile @@ -1,5 +1,5 @@ # -# Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/javax/swing/beaninfo/SwingBeans.gmk b/make/javax/swing/beaninfo/SwingBeans.gmk index 462c2509c..66f16aae1 100644 --- a/make/javax/swing/beaninfo/SwingBeans.gmk +++ b/make/javax/swing/beaninfo/SwingBeans.gmk @@ -1,5 +1,5 @@ # -# Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/jpda/back/Makefile b/make/jpda/back/Makefile index bd9818365..5237d9c79 100644 --- a/make/jpda/back/Makefile +++ b/make/jpda/back/Makefile @@ -1,5 +1,5 @@ # -# Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/jpda/transport/shmem/Makefile b/make/jpda/transport/shmem/Makefile index a1e238250..fcc8c632d 100644 --- a/make/jpda/transport/shmem/Makefile +++ b/make/jpda/transport/shmem/Makefile @@ -1,5 +1,5 @@ # -# Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/jpda/transport/socket/Makefile b/make/jpda/transport/socket/Makefile index 828613d49..65f801847 100644 --- a/make/jpda/transport/socket/Makefile +++ b/make/jpda/transport/socket/Makefile @@ -1,5 +1,5 @@ # -# Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/jprt.properties b/make/jprt.properties index e6666cb1d..c909f36aa 100644 --- a/make/jprt.properties +++ b/make/jprt.properties @@ -1,5 +1,5 @@ # -# Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2006-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/mksample/nio/Makefile b/make/mksample/nio/Makefile index 1f17a4cce..e05106293 100644 --- a/make/mksample/nio/Makefile +++ b/make/mksample/nio/Makefile @@ -1,5 +1,5 @@ # -# Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/mksample/nio/multicast/Makefile b/make/mksample/nio/multicast/Makefile index 05a153a13..179d3d4a9 100644 --- a/make/mksample/nio/multicast/Makefile +++ b/make/mksample/nio/multicast/Makefile @@ -1,5 +1,5 @@ # -# Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it diff --git a/make/netbeans/jconsole/build.properties b/make/netbeans/jconsole/build.properties index 189b528ee..dd442d93d 100644 --- a/make/netbeans/jconsole/build.properties +++ b/make/netbeans/jconsole/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2007 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/make/netbeans/jconsole/build.xml b/make/netbeans/jconsole/build.xml index b546fb7de..8d56df7c7 100644 --- a/make/netbeans/jconsole/build.xml +++ b/make/netbeans/jconsole/build.xml @@ -1,5 +1,5 @@