From e3cd9f9e06f0f98d652e6d4b15fb6e47f4c4cb9a Mon Sep 17 00:00:00 2001 From: Igor Nekrestyanov Date: Wed, 28 May 2008 20:06:09 +0400 Subject: [PATCH 001/105] 6587560: OpenJDK problem handling bitmaps returned when LCD text is requested Reviewed-by: bae, prr --- jdk/src/share/native/sun/font/freetypeScaler.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/jdk/src/share/native/sun/font/freetypeScaler.c b/jdk/src/share/native/sun/font/freetypeScaler.c index a3229df5dd1..5f6385b2868 100644 --- a/jdk/src/share/native/sun/font/freetypeScaler.c +++ b/jdk/src/share/native/sun/font/freetypeScaler.c @@ -770,11 +770,9 @@ Java_sun_font_FreetypeFontScaler_getGlyphImageNative( glyphInfo->topLeftX = (float) ftglyph->bitmap_left; glyphInfo->topLeftY = (float) -ftglyph->bitmap_top; - if (context->aaType == TEXT_AA_LCD_HRGB || - context->aaType == TEXT_AA_LCD_HBGR) { + if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) { glyphInfo->width = width/3; - } else if (context->aaType == TEXT_AA_LCD_VRGB || - context->aaType == TEXT_AA_LCD_VBGR) { + } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) { glyphInfo->height = glyphInfo->height/3; } From 64cefa94d14d83af717e797a0fa3135f50198882 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 12 Jun 2008 13:17:33 -0700 Subject: [PATCH 002/105] 6378099: RFE: Use libfontconfig to create/synthesise a fontconfig.properties Reviewed-by: tdv, igor --- jdk/make/sun/headless/mapfile-vers | 1 + jdk/make/sun/xawt/mapfile-vers | 1 + .../classes/sun/awt/FontConfiguration.java | 80 ++- .../share/classes/sun/font/FontManager.java | 157 ++++-- .../sun/java2d/SunGraphicsEnvironment.java | 31 +- .../sun/awt/X11GraphicsEnvironment.java | 50 +- .../classes/sun/font/FcFontConfiguration.java | 517 ++++++++++++++++++ jdk/src/solaris/native/sun/awt/fontconfig.h | 480 ++++++++++------ jdk/src/solaris/native/sun/awt/fontpath.c | 313 ++++++++++- .../sun/awt/Win32GraphicsEnvironment.java | 4 +- 10 files changed, 1360 insertions(+), 274 deletions(-) create mode 100644 jdk/src/solaris/classes/sun/font/FcFontConfiguration.java diff --git a/jdk/make/sun/headless/mapfile-vers b/jdk/make/sun/headless/mapfile-vers index f27f007d75d..d3107ecee84 100644 --- a/jdk/make/sun/headless/mapfile-vers +++ b/jdk/make/sun/headless/mapfile-vers @@ -60,6 +60,7 @@ SUNWprivate_1.1 { X11SurfaceData_GetOps; Java_java_awt_Font_initIDs; Java_sun_font_FontManager_getFontConfig; + Java_sun_font_FontManager_getFontConfigVersion; Java_sun_font_FontManager_getFontConfigAASettings; Java_sun_font_FontManager_getFontPath; Java_sun_font_FontManager_setNativeFontPath; diff --git a/jdk/make/sun/xawt/mapfile-vers b/jdk/make/sun/xawt/mapfile-vers index 7828481bd2d..aee563e2dec 100644 --- a/jdk/make/sun/xawt/mapfile-vers +++ b/jdk/make/sun/xawt/mapfile-vers @@ -177,6 +177,7 @@ SUNWprivate_1.1 { Java_java_awt_TextField_initIDs; Java_java_awt_TrayIcon_initIDs; Java_sun_font_FontManager_getFontConfig; + Java_sun_font_FontManager_getFontConfigVersion; Java_sun_font_FontManager_getFontConfigAASettings; Java_sun_font_FontManager_getFontPath; Java_sun_font_FontManager_setNativeFontPath; diff --git a/jdk/src/share/classes/sun/awt/FontConfiguration.java b/jdk/src/share/classes/sun/awt/FontConfiguration.java index a6b94870b3f..d3d6ed8194f 100644 --- a/jdk/src/share/classes/sun/awt/FontConfiguration.java +++ b/jdk/src/share/classes/sun/awt/FontConfiguration.java @@ -72,6 +72,11 @@ public abstract class FontConfiguration { protected boolean preferLocaleFonts; protected boolean preferPropFonts; + private File fontConfigFile; + private boolean foundOsSpecificFile; + private boolean inited; + private String javaLib; + /* A default FontConfiguration must be created before an alternate * one to ensure proper static initialisation takes place. */ @@ -80,14 +85,25 @@ public abstract class FontConfiguration { logger = Logger.getLogger("sun.awt.FontConfiguration"); } this.environment = environment; - this.preferLocaleFonts = false; - this.preferPropFonts = false; setOsNameAndVersion(); /* static initialization */ setEncoding(); /* static initialization */ - fontConfig = this; /* static initialization */ + /* Separating out the file location from the rest of the + * initialisation, so the caller has the option of doing + * something else if a suitable file isn't found. + */ + findFontConfigFile(); + } - readFontConfigFile(); - initFontConfig(); + public synchronized boolean init() { + if (!inited) { + this.preferLocaleFonts = false; + this.preferPropFonts = false; + fontConfig = this; /* static initialization */ + readFontConfigFile(fontConfigFile); + initFontConfig(); + inited = true; + } + return true; } public FontConfiguration(SunGraphicsEnvironment environment, @@ -121,21 +137,51 @@ public abstract class FontConfiguration { ///////////////////////////////////////////////////////////////////// // methods for loading the FontConfig file // ///////////////////////////////////////////////////////////////////// - private void readFontConfigFile() { - // Find fontconfig file - File f = null; + + public boolean foundOsSpecificFile() { + return foundOsSpecificFile; + } + + /* Smoke test to see if we can trust this configuration by testing if + * the first slot of a composite font maps to an installed file. + */ + public boolean fontFilesArePresent() { + init(); + short fontNameID = compFontNameIDs[0][0][0]; + short fileNameID = getComponentFileID(fontNameID); + final String fileName = mapFileName(getComponentFileName(fileNameID)); + Boolean exists = (Boolean)java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Object run() { + try { + File f = new File(fileName); + return Boolean.valueOf(f.exists()); + } + catch (Exception e) { + return false; + } + } + }); + return exists.booleanValue(); + } + + private void findFontConfigFile() { + + foundOsSpecificFile = true; // default assumption. String javaHome = System.getProperty("java.home"); if (javaHome == null) { throw new Error("java.home property not set"); } - String javaLib = javaHome + File.separator + "lib"; + javaLib = javaHome + File.separator + "lib"; String userConfigFile = System.getProperty("sun.awt.fontconfig"); if (userConfigFile != null) { - f = new File(userConfigFile); + fontConfigFile = new File(userConfigFile); } else { - f = findFontConfigFile(javaLib); + fontConfigFile = findFontConfigFile(javaLib); } + } + private void readFontConfigFile(File f) { /* This is invoked here as readFontConfigFile is only invoked * once per VM, and always in a privileged context, thus the * directory containing installed fall back fonts is accessed @@ -167,7 +213,7 @@ public abstract class FontConfiguration { } } - private void getInstalledFallbackFonts(String javaLib) { + protected void getInstalledFallbackFonts(String javaLib) { String fallbackDirName = javaLib + File.separator + "fonts" + File.separator + "fallback"; @@ -229,6 +275,8 @@ public abstract class FontConfiguration { return configFile; } } + foundOsSpecificFile = false; + configFile = findImpl(baseName); if (configFile != null) { return configFile; @@ -506,12 +554,12 @@ public abstract class FontConfiguration { ///////////////////////////////////////////////////////////////////// protected static final int NUM_FONTS = 5; protected static final int NUM_STYLES = 4; - private static final String[] fontNames + protected static final String[] fontNames = {"serif", "sansserif", "monospaced", "dialog", "dialoginput"}; - private static final String[] publicFontNames + protected static final String[] publicFontNames = {Font.SERIF, Font.SANS_SERIF, Font.MONOSPACED, Font.DIALOG, Font.DIALOG_INPUT}; - private static final String[] styleNames + protected static final String[] styleNames = {"plain", "bold", "italic", "bolditalic"}; /** @@ -656,7 +704,7 @@ public abstract class FontConfiguration { return null; } - private static String[] installedFallbackFontFiles = null; + protected static String[] installedFallbackFontFiles = null; /** * Maps a file name given in the font configuration file diff --git a/jdk/src/share/classes/sun/font/FontManager.java b/jdk/src/share/classes/sun/font/FontManager.java index 09f181f12c2..a9a7257e42a 100644 --- a/jdk/src/share/classes/sun/font/FontManager.java +++ b/jdk/src/share/classes/sun/font/FontManager.java @@ -148,6 +148,7 @@ public final class FontManager { static HashSet jreLucidaFontFiles; static String[] jreOtherFontFiles; static boolean noOtherJREFontFiles = false; // initial assumption. + static boolean fontConfigFailed = false; /* Used to indicate required return type from toArray(..); */ private static String[] STR_ARRAY = new String[0]; @@ -3100,19 +3101,30 @@ public final class FontManager { "monospace:bold:italic", }; - /* This class is just a data structure. + /* These next three classes are just data structures. */ - private static class FontConfigInfo { + static class FontConfigFont { + String familyName; // eg Bitstream Vera Sans + String styleStr; // eg Bold + String fullName; // eg Bitstream Vera Sans Bold + String fontFile; // eg /usr/X11/lib/fonts/foo.ttf + } + + static class FcCompFont { String fcName; // eg sans String fcFamily; // eg sans String jdkName; // eg sansserif int style; // eg 0=PLAIN - String familyName; // eg Bitstream Vera Sans - String fontFile; // eg /usr/X11/lib/fonts/foo.ttf + FontConfigFont firstFont; + FontConfigFont[] allFonts; //boolean preferBitmaps; // if embedded bitmaps preferred over AA CompositeFont compFont; // null if not yet created/known. } + static class FontConfigInfo { + int fcVersion; + String[] cacheDirs = new String[4]; + } private static String getFCLocaleStr() { Locale l = SunToolkit.getStartupLocale(); @@ -3124,6 +3136,12 @@ public final class FontManager { return localeStr; } + /* This does cause the native libfontconfig to be loaded and unloaded, + * but it does not incur the overhead of initialisation of its + * data structures, so shouldn't have a measurable impact. + */ + public static native int getFontConfigVersion(); + private static native int getFontConfigAASettings(String locale, String fcFamily); @@ -3157,17 +3175,35 @@ public final class FontManager { return getFontConfigAAHint("sans"); } + /* This is populated by native */ + private static final FontConfigInfo fcInfo = new FontConfigInfo(); + /* This array has the array elements created in Java code and is * passed down to native to be filled in. */ - private static FontConfigInfo[] fontConfigFonts; + private static FcCompFont[] fontConfigFonts; - /* Return an array of FontConfigInfo structs describing the primary + /* Return an array of FcCompFont structs describing the primary * font located for each of fontconfig/GTK/Pango's logical font names. */ private static native void getFontConfig(String locale, - FontConfigInfo[] fonts); + FontConfigInfo fcInfo, + FcCompFont[] fonts, + boolean includeFallbacks); + static void populateFontConfig(FcCompFont[] fcInfo) { + fontConfigFonts = fcInfo; + } + + static FcCompFont[] loadFontConfig() { + initFontConfigFonts(true); + return fontConfigFonts; + } + + static FontConfigInfo getFontConfigInfo() { + initFontConfigFonts(true); + return fcInfo; + } /* This can be made public if it's needed to force a re-read * rather than using the cached values. The re-read would be needed @@ -3175,54 +3211,80 @@ public final class FontManager { * In that event this method would need to return directly the array * to be used by the caller in case it subsequently changed. */ - private static void initFontConfigFonts() { + private static synchronized void + initFontConfigFonts(boolean includeFallbacks) { if (fontConfigFonts != null) { - return; + if (!includeFallbacks || (fontConfigFonts[0].allFonts != null)) { + return; + } } - if (isWindows) { + if (isWindows || fontConfigFailed) { return; } long t0 = 0; if (logging) { - t0 = System.currentTimeMillis(); + t0 = System.nanoTime(); } - FontConfigInfo[] fontArr = new FontConfigInfo[fontConfigNames.length]; + FcCompFont[] fontArr = new FcCompFont[fontConfigNames.length]; for (int i = 0; i< fontArr.length; i++) { - fontArr[i] = new FontConfigInfo(); + fontArr[i] = new FcCompFont(); fontArr[i].fcName = fontConfigNames[i]; int colonPos = fontArr[i].fcName.indexOf(':'); fontArr[i].fcFamily = fontArr[i].fcName.substring(0, colonPos); fontArr[i].jdkName = mapFcName(fontArr[i].fcFamily); fontArr[i].style = i % 4; // depends on array order. } - getFontConfig(getFCLocaleStr(), fontArr); + getFontConfig(getFCLocaleStr(), fcInfo, fontArr, includeFallbacks); + /* If don't find anything (eg no libfontconfig), then just return */ + for (int i = 0; i< fontArr.length; i++) { + FcCompFont fci = fontArr[i]; + if (fci.firstFont == null) { + if (logging) { + logger.info("Fontconfig returned no fonts."); + } + fontConfigFailed = true; + return; + } + } fontConfigFonts = fontArr; if (logging) { - long t1 = System.currentTimeMillis(); - logger.info("Time spent accessing fontconfig="+(t1-t0)+"ms."); + long t1 = System.nanoTime(); + logger.info("Time spent accessing fontconfig="+ + (t1-t0)/1000000+"ms."); for (int i = 0; i< fontConfigFonts.length; i++) { - FontConfigInfo fci = fontConfigFonts[i]; + FcCompFont fci = fontConfigFonts[i]; logger.info("FC font " + fci.fcName+" maps to family " + - fci.familyName + " in file " + fci.fontFile); + fci.firstFont.familyName + + " in file " + fci.firstFont.fontFile); + if (fci.allFonts != null) { + for (int f=0;f 0 && - fontConfigFonts[0].fontFile != null) { - info[0] = fontConfigFonts[0].familyName; - info[1] = fontConfigFonts[0].fontFile; + fontConfigFonts[0].firstFont.fontFile != null) { + info[0] = fontConfigFonts[0].firstFont.familyName; + info[1] = fontConfigFonts[0].firstFont.fontFile; } else { info[0] = "Dialog"; info[1] = "/dialog.ttf"; @@ -3373,8 +3437,8 @@ public final class FontManager { return defaultPlatformFont; } - private FontConfigInfo getFontConfigInfo() { - initFontConfigFonts(); + private FcCompFont getFcCompFont() { + initFontConfigFonts(false); for (int i=0; i 5.10f) { + File f = new File("/etc/release"); + FileInputStream fis = new FileInputStream(f); + InputStreamReader isr + = new InputStreamReader(fis, "ISO-8859-1"); + BufferedReader br = new BufferedReader(isr); + String line = br.readLine(); + if (line.indexOf("OpenSolaris") >= 0) { + isOpenSolaris = true; + } + fis.close(); + } + } catch (Exception e) { + } } else if ("Windows".equals(osName)) { isWindows = true; } @@ -174,11 +192,7 @@ public abstract class SunGraphicsEnvironment extends GraphicsEnvironment noType1Font = "true". equals(System.getProperty("sun.java2d.noType1Font")); - if (isOpenJDK()) { - String[] fontInfo = FontManager.getDefaultPlatformFont(); - defaultFontName = fontInfo[0]; - defaultFontFileName = fontInfo[1]; - } else { + if (!isOpenJDK()) { defaultFontName = lucidaFontName; if (useAbsoluteFontFileNames()) { defaultFontFileName = @@ -244,6 +258,11 @@ public abstract class SunGraphicsEnvironment extends GraphicsEnvironment * that might be specified. */ fontConfig = createFontConfiguration(); + if (isOpenJDK()) { + String[] fontInfo = FontManager.getDefaultPlatformFont(); + defaultFontName = fontInfo[0]; + defaultFontFileName = fontInfo[1]; + } getPlatformFontPathFromFontConfig(); String extraFontPath = fontConfig.getExtraFontPath(); @@ -1069,7 +1088,7 @@ public abstract class SunGraphicsEnvironment extends GraphicsEnvironment String fontFileName = getFileNameFromPlatformName(platformFontName); String[] nativeNames = null; - if (fontFileName == null) { + if (fontFileName == null || fontFileName.equals(platformFontName)){ /* No file located, so register using the platform name, * i.e. as a native font. */ diff --git a/jdk/src/solaris/classes/sun/awt/X11GraphicsEnvironment.java b/jdk/src/solaris/classes/sun/awt/X11GraphicsEnvironment.java index b531fa739b9..4c52788f82e 100644 --- a/jdk/src/solaris/classes/sun/awt/X11GraphicsEnvironment.java +++ b/jdk/src/solaris/classes/sun/awt/X11GraphicsEnvironment.java @@ -44,6 +44,7 @@ import java.util.*; import java.util.logging.*; import sun.awt.motif.MFontConfiguration; +import sun.font.FcFontConfiguration; import sun.font.Font2D; import sun.font.FontManager; import sun.font.NativeFont; @@ -350,6 +351,14 @@ public class X11GraphicsEnvironment * only to get called for these fonts. */ public String getFileNameFromPlatformName(String platName) { + + /* If the FontConfig file doesn't use xlfds, or its + * FcFontConfiguration, this may be already a file name. + */ + if (platName.startsWith("/")) { + return platName; + } + String fileName = null; String fontID = specificFontIDForName(platName); @@ -905,12 +914,50 @@ public class X11GraphicsEnvironment // Implements SunGraphicsEnvironment.createFontConfiguration. protected FontConfiguration createFontConfiguration() { - return new MFontConfiguration(this); + + /* The logic here decides whether to use a preconfigured + * fontconfig.properties file, or synthesise one using platform APIs. + * On Solaris (as opposed to OpenSolaris) we try to use the + * pre-configured ones, but if the files it specifies are missing + * we fail-safe to synthesising one. This might happen if Solaris + * changes its fonts. + * For OpenSolaris I don't expect us to ever create fontconfig files, + * so it will always synthesise. Note that if we misidentify + * OpenSolaris as Solaris, then the test for the presence of + * Solaris-only font files will correct this. + * For Linux we require an exact match of distro and version to + * use the preconfigured file, and also that it points to + * existent fonts. + * If synthesising fails, we fall back to any preconfigured file + * and do the best we can. For the commercial JDK this will be + * fine as it includes the Lucida fonts. OpenJDK should not hit + * this as the synthesis should always work on its platforms. + */ + FontConfiguration mFontConfig = new MFontConfiguration(this); + if (isOpenSolaris || + (isLinux && + (!mFontConfig.foundOsSpecificFile() || + !mFontConfig.fontFilesArePresent()) || + (isSolaris && !mFontConfig.fontFilesArePresent()))) { + FcFontConfiguration fcFontConfig = + new FcFontConfiguration(this); + if (fcFontConfig.init()) { + return fcFontConfig; + } + } + mFontConfig.init(); + return mFontConfig; } public FontConfiguration createFontConfiguration(boolean preferLocaleFonts, boolean preferPropFonts) { + FontConfiguration config = getFontConfiguration(); + if (config instanceof FcFontConfiguration) { + // Doesn't need to implement the alternate support. + return config; + } + return new MFontConfiguration(this, preferLocaleFonts, preferPropFonts); } @@ -921,6 +968,7 @@ public class X11GraphicsEnvironment * for this platform. */ public String getDefaultFontFaceName() { + return null; } diff --git a/jdk/src/solaris/classes/sun/font/FcFontConfiguration.java b/jdk/src/solaris/classes/sun/font/FcFontConfiguration.java new file mode 100644 index 00000000000..fe2e5dbf836 --- /dev/null +++ b/jdk/src/solaris/classes/sun/font/FcFontConfiguration.java @@ -0,0 +1,517 @@ +/* + * 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 + * 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.font; + +import java.awt.Font; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.HashSet; +import java.util.logging.Logger; +import java.util.Properties; +import java.util.Scanner; +import sun.awt.FontConfiguration; +import sun.awt.FontDescriptor; +import sun.awt.SunToolkit; +import sun.font.CompositeFontDescriptor; +import sun.font.FontManager; +import sun.font.FontManager.FontConfigInfo; +import sun.font.FontManager.FcCompFont; +import sun.font.FontManager.FontConfigFont; +import sun.java2d.SunGraphicsEnvironment; + +public class FcFontConfiguration extends FontConfiguration { + + /** Version of the cache file format understood by this code. + * Its part of the file name so that we can rev this at + * any time, even in a minor JDK update. + * It is stored as the value of the "version" property. + * This is distinct from the version of "libfontconfig" that generated + * the cached results, and which is the "fcversion" property in the file. + * {@code FontConfiguration.getVersion()} also returns a version string, + * and has meant the version of the fontconfiguration.properties file + * that was read. Since this class doesn't use such files, then what + * that really means is whether the methods on this class return + * values that are compatible with the classes that do directly read + * from such files. It is a compatible subset of version "1". + */ + private static final String fileVersion = "1"; + private String fcInfoFileName = null; + + private FcCompFont[] fcCompFonts = null; + + public FcFontConfiguration(SunGraphicsEnvironment environment) { + super(environment); + init(); + } + + /* This isn't called but is needed to satisfy super-class contract. */ + public FcFontConfiguration(SunGraphicsEnvironment environment, + boolean preferLocaleFonts, + boolean preferPropFonts) { + super(environment, preferLocaleFonts, preferPropFonts); + init(); + } + + @Override + public synchronized boolean init() { + if (fcCompFonts != null) { + return true; + } + + readFcInfo(); + if (fcCompFonts == null) { + fcCompFonts = FontManager.loadFontConfig(); + if (fcCompFonts != null) { + try { + writeFcInfo(); + } catch (Exception e) { + if (SunGraphicsEnvironment.debugFonts) { + Logger logger = + Logger.getLogger("sun.awt.FontConfiguration"); + logger.warning("Exception writing fcInfo " + e); + } + } + } else if (SunGraphicsEnvironment.debugFonts) { + Logger logger = Logger.getLogger("sun.awt.FontConfiguration"); + logger.warning("Failed to get info from libfontconfig"); + } + } else { + FontManager.populateFontConfig(fcCompFonts); + } + + if (fcCompFonts == null) { + return false; // couldn't load fontconfig. + } + + // NB already in a privileged block from SGE + String javaHome = System.getProperty("java.home"); + if (javaHome == null) { + throw new Error("java.home property not set"); + } + String javaLib = javaHome + File.separator + "lib"; + getInstalledFallbackFonts(javaLib); + + return true; + } + + @Override + public String getFallbackFamilyName(String fontName, + String defaultFallback) { + // maintain compatibility with old font.properties files, which either + // had aliases for TimesRoman & Co. or defined mappings for them. + String compatibilityName = getCompatibilityFamilyName(fontName); + if (compatibilityName != null) { + return compatibilityName; + } + return defaultFallback; + } + + @Override + protected String + getFaceNameFromComponentFontName(String componentFontName) { + return null; + } + + @Override + protected String + getFileNameFromComponentFontName(String componentFontName) { + return null; + } + + @Override + public String getFileNameFromPlatformName(String platformName) { + /* Platform name is the file name, but rather than returning + * the arg, return null*/ + return null; + } + + @Override + protected Charset getDefaultFontCharset(String fontName) { + return Charset.forName("ISO8859_1"); + } + + @Override + protected String getEncoding(String awtFontName, + String characterSubsetName) { + return "default"; + } + + @Override + protected void initReorderMap() { + reorderMap = new HashMap(); + } + + @Override + public FontDescriptor[] getFontDescriptors(String fontName, int style) { + throw new InternalError("Not implemented"); + } + + @Override + public int getNumberCoreFonts() { + return 1; + } + + @Override + public String[] getPlatformFontNames() { + HashSet nameSet = new HashSet(); + FcCompFont[] fcCompFonts = FontManager.loadFontConfig(); + for (int i=0; i lastModified) { + return; + } + cacheDirIndex++; + } + + String[] names = { "sansserif", "serif", "monospaced" }; + String[] fcnames = { "sans", "serif", "monospace" }; + int namesLen = names.length; + int numStyles = 4; + FcCompFont[] fci = new FcCompFont[namesLen*numStyles]; + + try { + for (int i=0; i +#include +#include #include +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define FC_ATTRIBUTE_SENTINEL(x) __attribute__((__sentinel__(0))) +#else +#define FC_ATTRIBUTE_SENTINEL(x) +#endif + +#ifndef FcPublic +#define FcPublic +#endif + typedef unsigned char FcChar8; typedef unsigned short FcChar16; typedef unsigned int FcChar32; @@ -43,7 +52,7 @@ typedef int FcBool; */ #define FC_MAJOR 2 -#define FC_MINOR 2 +#define FC_MINOR 5 #define FC_REVISION 0 #define FC_VERSION ((FC_MAJOR * 10000) + (FC_MINOR * 100) + (FC_REVISION)) @@ -58,7 +67,7 @@ typedef int FcBool; * it means multiple copies of the font information. */ -#define FC_CACHE_VERSION "1" +#define FC_CACHE_VERSION "2" #define FcTrue 1 #define FcFalse 0 @@ -74,6 +83,7 @@ typedef int FcBool; #define FC_FOUNDRY "foundry" /* String */ #define FC_ANTIALIAS "antialias" /* Bool (depends) */ #define FC_HINTING "hinting" /* Bool (true) */ +#define FC_HINT_STYLE "hintstyle" /* Int */ #define FC_VERTICAL_LAYOUT "verticallayout" /* Bool (false) */ #define FC_AUTOHINT "autohint" /* Bool (false) */ #define FC_GLOBAL_ADVANCE "globaladvance" /* Bool (true) */ @@ -88,11 +98,21 @@ typedef int FcBool; #define FC_DPI "dpi" /* double */ #define FC_RGBA "rgba" /* Int */ #define FC_MINSPACE "minspace" /* Bool use minimum line spacing */ -#define FC_SOURCE "source" /* String (X11, freetype) */ +#define FC_SOURCE "source" /* String (deprecated) */ #define FC_CHARSET "charset" /* CharSet */ #define FC_LANG "lang" /* String RFC 3066 langs */ #define FC_FONTVERSION "fontversion" /* Int from 'head' table */ +#define FC_FULLNAME "fullname" /* String */ +#define FC_FAMILYLANG "familylang" /* String RFC 3066 langs */ +#define FC_STYLELANG "stylelang" /* String RFC 3066 langs */ +#define FC_FULLNAMELANG "fullnamelang" /* String RFC 3066 langs */ +#define FC_CAPABILITY "capability" /* String */ +#define FC_FONTFORMAT "fontformat" /* String */ +#define FC_EMBOLDEN "embolden" /* Bool - true if emboldening needed*/ +#define FC_EMBEDDED_BITMAP "embeddedbitmap" /* Bool - true to enable embedded bitmaps */ +#define FC_DECORATIVE "decorative" /* Bool - true if style is a decorative variant */ +#define FC_CACHE_SUFFIX ".cache-"FC_CACHE_VERSION #define FC_DIR_CACHE_FILE "fonts.cache-"FC_CACHE_VERSION #define FC_USER_CACHE_FILE ".fonts.cache-"FC_CACHE_VERSION @@ -105,6 +125,7 @@ typedef int FcBool; #define FC_WEIGHT_EXTRALIGHT 40 #define FC_WEIGHT_ULTRALIGHT FC_WEIGHT_EXTRALIGHT #define FC_WEIGHT_LIGHT 50 +#define FC_WEIGHT_BOOK 75 #define FC_WEIGHT_REGULAR 80 #define FC_WEIGHT_NORMAL FC_WEIGHT_REGULAR #define FC_WEIGHT_MEDIUM 100 @@ -115,6 +136,8 @@ typedef int FcBool; #define FC_WEIGHT_ULTRABOLD FC_WEIGHT_EXTRABOLD #define FC_WEIGHT_BLACK 210 #define FC_WEIGHT_HEAVY FC_WEIGHT_BLACK +#define FC_WEIGHT_EXTRABLACK 215 +#define FC_WEIGHT_ULTRABLACK FC_WEIGHT_EXTRABLACK #define FC_SLANT_ROMAN 0 #define FC_SLANT_ITALIC 100 @@ -131,6 +154,7 @@ typedef int FcBool; #define FC_WIDTH_ULTRAEXPANDED 200 #define FC_PROPORTIONAL 0 +#define FC_DUAL 90 #define FC_MONO 100 #define FC_CHARCELL 110 @@ -142,6 +166,12 @@ typedef int FcBool; #define FC_RGBA_VBGR 4 #define FC_RGBA_NONE 5 +/* hinting style */ +#define FC_HINT_NONE 0 +#define FC_HINT_SLIGHT 1 +#define FC_HINT_MEDIUM 2 +#define FC_HINT_FULL 3 + typedef enum _FcType { FcTypeVoid, FcTypeInteger, @@ -180,7 +210,8 @@ typedef struct _FcConstant { } FcConstant; typedef enum _FcResult { - FcResultMatch, FcResultNoMatch, FcResultTypeMismatch, FcResultNoId + FcResultMatch, FcResultNoMatch, FcResultTypeMismatch, FcResultNoId, + FcResultOutOfMemory } FcResult; typedef struct _FcPattern FcPattern; @@ -197,7 +228,6 @@ typedef struct _FcValue { const FcMatrix *m; const FcCharSet *c; void *f; - const FcPattern *p; const FcLangSet *l; } u; } FcValue; @@ -215,11 +245,14 @@ typedef struct _FcObjectSet { } FcObjectSet; typedef enum _FcMatchKind { - FcMatchPattern, FcMatchFont + FcMatchPattern, FcMatchFont, FcMatchScan } FcMatchKind; typedef enum _FcLangResult { - FcLangEqual, FcLangDifferentCountry, FcLangDifferentLang + FcLangEqual = 0, + FcLangDifferentCountry = 1, + FcLangDifferentTerritory = 1, + FcLangDifferentLang = 2 } FcLangResult; typedef enum _FcSetName { @@ -249,169 +282,207 @@ typedef struct _FcStrList FcStrList; typedef struct _FcStrSet FcStrSet; +typedef struct _FcCache FcCache; + _FCFUNCPROTOBEGIN -FcBool -FcDirCacheValid (const FcChar8 *cache_file); - /* fcblanks.c */ -FcBlanks * +FcPublic FcBlanks * FcBlanksCreate (void); -void +FcPublic void FcBlanksDestroy (FcBlanks *b); -FcBool +FcPublic FcBool FcBlanksAdd (FcBlanks *b, FcChar32 ucs4); -FcBool +FcPublic FcBool FcBlanksIsMember (FcBlanks *b, FcChar32 ucs4); +/* fccache.c */ + +FcPublic const FcChar8 * +FcCacheDir(const FcCache *c); + +FcPublic FcFontSet * +FcCacheCopySet(const FcCache *c); + +FcPublic const FcChar8 * +FcCacheSubdir (const FcCache *c, int i); + +FcPublic int +FcCacheNumSubdir (const FcCache *c); + +FcPublic int +FcCacheNumFont (const FcCache *c); + +FcPublic FcBool +FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config); + +FcPublic FcBool +FcDirCacheValid (const FcChar8 *cache_file); + /* fccfg.c */ -FcChar8 * +FcPublic FcChar8 * FcConfigHome (void); -FcBool +FcPublic FcBool FcConfigEnableHome (FcBool enable); -FcChar8 * +FcPublic FcChar8 * FcConfigFilename (const FcChar8 *url); -FcConfig * +FcPublic FcConfig * FcConfigCreate (void); -void +FcPublic void FcConfigDestroy (FcConfig *config); -FcBool +FcPublic FcBool FcConfigSetCurrent (FcConfig *config); -FcConfig * +FcPublic FcConfig * FcConfigGetCurrent (void); -FcBool +FcPublic FcBool FcConfigUptoDate (FcConfig *config); -FcBool +FcPublic FcBool FcConfigBuildFonts (FcConfig *config); -FcStrList * +FcPublic FcStrList * FcConfigGetFontDirs (FcConfig *config); -FcStrList * +FcPublic FcStrList * FcConfigGetConfigDirs (FcConfig *config); -FcStrList * +FcPublic FcStrList * FcConfigGetConfigFiles (FcConfig *config); -FcChar8 * +FcPublic FcChar8 * FcConfigGetCache (FcConfig *config); -FcBlanks * +FcPublic FcBlanks * FcConfigGetBlanks (FcConfig *config); -int -FcConfigGetRescanInverval (FcConfig *config); +FcPublic FcStrList * +FcConfigGetCacheDirs (FcConfig *config); -FcBool -FcConfigSetRescanInverval (FcConfig *config, int rescanInterval); +FcPublic int +FcConfigGetRescanInterval (FcConfig *config); -FcFontSet * +FcPublic FcBool +FcConfigSetRescanInterval (FcConfig *config, int rescanInterval); + +FcPublic FcFontSet * FcConfigGetFonts (FcConfig *config, FcSetName set); -FcBool +FcPublic FcBool FcConfigAppFontAddFile (FcConfig *config, const FcChar8 *file); -FcBool +FcPublic FcBool FcConfigAppFontAddDir (FcConfig *config, const FcChar8 *dir); -void +FcPublic void FcConfigAppFontClear (FcConfig *config); -FcBool +FcPublic FcBool FcConfigSubstituteWithPat (FcConfig *config, FcPattern *p, FcPattern *p_pat, FcMatchKind kind); -FcBool +FcPublic FcBool FcConfigSubstitute (FcConfig *config, FcPattern *p, FcMatchKind kind); /* fccharset.c */ -FcCharSet * +FcPublic FcCharSet* FcCharSetCreate (void); -void +/* deprecated alias for FcCharSetCreate */ +FcPublic FcCharSet * +FcCharSetNew (void); + +FcPublic void FcCharSetDestroy (FcCharSet *fcs); -FcBool +FcPublic FcBool FcCharSetAddChar (FcCharSet *fcs, FcChar32 ucs4); -FcCharSet * +FcPublic FcCharSet* FcCharSetCopy (FcCharSet *src); -FcBool +FcPublic FcBool FcCharSetEqual (const FcCharSet *a, const FcCharSet *b); -FcCharSet * +FcPublic FcCharSet* FcCharSetIntersect (const FcCharSet *a, const FcCharSet *b); -FcCharSet * +FcPublic FcCharSet* FcCharSetUnion (const FcCharSet *a, const FcCharSet *b); -FcCharSet * +FcPublic FcCharSet* FcCharSetSubtract (const FcCharSet *a, const FcCharSet *b); -FcBool +FcPublic FcBool FcCharSetHasChar (const FcCharSet *fcs, FcChar32 ucs4); -FcChar32 +FcPublic FcChar32 FcCharSetCount (const FcCharSet *a); -FcChar32 +FcPublic FcChar32 FcCharSetIntersectCount (const FcCharSet *a, const FcCharSet *b); -FcChar32 +FcPublic FcChar32 FcCharSetSubtractCount (const FcCharSet *a, const FcCharSet *b); -FcBool +FcPublic FcBool FcCharSetIsSubset (const FcCharSet *a, const FcCharSet *b); #define FC_CHARSET_MAP_SIZE (256/32) #define FC_CHARSET_DONE ((FcChar32) -1) -FcChar32 +FcPublic FcChar32 FcCharSetFirstPage (const FcCharSet *a, FcChar32 map[FC_CHARSET_MAP_SIZE], FcChar32 *next); -FcChar32 +FcPublic FcChar32 FcCharSetNextPage (const FcCharSet *a, FcChar32 map[FC_CHARSET_MAP_SIZE], FcChar32 *next); +/* + * old coverage API, rather hard to use correctly + */ + +FcPublic FcChar32 +FcCharSetCoverage (const FcCharSet *a, FcChar32 page, FcChar32 *result); /* fcdbg.c */ -void +FcPublic void FcValuePrint (const FcValue v); -void +FcPublic void FcPatternPrint (const FcPattern *p); -void +FcPublic void FcFontSetPrint (const FcFontSet *s); /* fcdefault.c */ -void +FcPublic void FcDefaultSubstitute (FcPattern *pattern); /* fcdir.c */ -FcBool +FcPublic FcBool +FcFileIsDir (const FcChar8 *file); + +FcPublic FcBool FcFileScan (FcFontSet *set, FcStrSet *dirs, FcFileCache *cache, @@ -419,7 +490,7 @@ FcFileScan (FcFontSet *set, const FcChar8 *file, FcBool force); -FcBool +FcPublic FcBool FcDirScan (FcFontSet *set, FcStrSet *dirs, FcFileCache *cache, @@ -427,144 +498,165 @@ FcDirScan (FcFontSet *set, const FcChar8 *dir, FcBool force); -FcBool +FcPublic FcBool FcDirSave (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir); +FcPublic FcCache * +FcDirCacheLoad (const FcChar8 *dir, FcConfig *config, FcChar8 **cache_file); + +FcPublic FcCache * +FcDirCacheRead (const FcChar8 *dir, FcBool force, FcConfig *config); + +FcPublic FcCache * +FcDirCacheLoadFile (const FcChar8 *cache_file, struct stat *file_stat); + +FcPublic void +FcDirCacheUnload (FcCache *cache); + /* fcfreetype.c */ -FcPattern * +FcPublic FcPattern * FcFreeTypeQuery (const FcChar8 *file, int id, FcBlanks *blanks, int *count); /* fcfs.c */ -FcFontSet * +FcPublic FcFontSet * FcFontSetCreate (void); -void +FcPublic void FcFontSetDestroy (FcFontSet *s); -FcBool +FcPublic FcBool FcFontSetAdd (FcFontSet *s, FcPattern *font); /* fcinit.c */ -FcConfig * +FcPublic FcConfig * FcInitLoadConfig (void); -FcConfig * +FcPublic FcConfig * FcInitLoadConfigAndFonts (void); -FcBool +FcPublic FcBool FcInit (void); -int +FcPublic void +FcFini (void); + +FcPublic int FcGetVersion (void); -FcBool +FcPublic FcBool FcInitReinitialize (void); -FcBool +FcPublic FcBool FcInitBringUptoDate (void); /* fclang.c */ -FcLangSet * +FcPublic FcStrSet * +FcGetLangs (void); + +FcPublic const FcCharSet * +FcLangGetCharSet (const FcChar8 *lang); + +FcPublic FcLangSet* FcLangSetCreate (void); -void +FcPublic void FcLangSetDestroy (FcLangSet *ls); -FcLangSet * +FcPublic FcLangSet* FcLangSetCopy (const FcLangSet *ls); -FcBool +FcPublic FcBool FcLangSetAdd (FcLangSet *ls, const FcChar8 *lang); -FcLangResult +FcPublic FcLangResult FcLangSetHasLang (const FcLangSet *ls, const FcChar8 *lang); -FcLangResult +FcPublic FcLangResult FcLangSetCompare (const FcLangSet *lsa, const FcLangSet *lsb); -FcBool +FcPublic FcBool FcLangSetContains (const FcLangSet *lsa, const FcLangSet *lsb); -FcBool +FcPublic FcBool FcLangSetEqual (const FcLangSet *lsa, const FcLangSet *lsb); -FcChar32 +FcPublic FcChar32 FcLangSetHash (const FcLangSet *ls); /* fclist.c */ -FcObjectSet * +FcPublic FcObjectSet * FcObjectSetCreate (void); -FcBool +FcPublic FcBool FcObjectSetAdd (FcObjectSet *os, const char *object); -void +FcPublic void FcObjectSetDestroy (FcObjectSet *os); -FcObjectSet * +FcPublic FcObjectSet * FcObjectSetVaBuild (const char *first, va_list va); -FcObjectSet * -FcObjectSetBuild (const char *first, ...); +FcPublic FcObjectSet * +FcObjectSetBuild (const char *first, ...) FC_ATTRIBUTE_SENTINEL(0); -FcFontSet * +FcPublic FcFontSet * FcFontSetList (FcConfig *config, FcFontSet **sets, int nsets, FcPattern *p, FcObjectSet *os); -FcFontSet * +FcPublic FcFontSet * FcFontList (FcConfig *config, FcPattern *p, FcObjectSet *os); /* fcatomic.c */ -FcAtomic * +FcPublic FcAtomic * FcAtomicCreate (const FcChar8 *file); -FcBool +FcPublic FcBool FcAtomicLock (FcAtomic *atomic); -FcChar8 * +FcPublic FcChar8 * FcAtomicNewFile (FcAtomic *atomic); -FcChar8 * +FcPublic FcChar8 * FcAtomicOrigFile (FcAtomic *atomic); -FcBool +FcPublic FcBool FcAtomicReplaceOrig (FcAtomic *atomic); -void +FcPublic void FcAtomicDeleteNew (FcAtomic *atomic); -void +FcPublic void FcAtomicUnlock (FcAtomic *atomic); -void +FcPublic void FcAtomicDestroy (FcAtomic *atomic); /* fcmatch.c */ -FcPattern * +FcPublic FcPattern * FcFontSetMatch (FcConfig *config, FcFontSet **sets, int nsets, FcPattern *p, FcResult *result); -FcPattern * +FcPublic FcPattern * FcFontMatch (FcConfig *config, FcPattern *p, FcResult *result); -FcPattern * +FcPublic FcPattern * FcFontRenderPrepare (FcConfig *config, FcPattern *pat, FcPattern *font); -FcFontSet * +FcPublic FcFontSet * FcFontSetSort (FcConfig *config, FcFontSet **sets, int nsets, @@ -573,179 +665,198 @@ FcFontSetSort (FcConfig *config, FcCharSet **csp, FcResult *result); -FcFontSet * +FcPublic FcFontSet * FcFontSort (FcConfig *config, FcPattern *p, FcBool trim, FcCharSet **csp, FcResult *result); -void +FcPublic void FcFontSetSortDestroy (FcFontSet *fs); /* fcmatrix.c */ -FcMatrix * +FcPublic FcMatrix * FcMatrixCopy (const FcMatrix *mat); -FcBool +FcPublic FcBool FcMatrixEqual (const FcMatrix *mat1, const FcMatrix *mat2); -void +FcPublic void FcMatrixMultiply (FcMatrix *result, const FcMatrix *a, const FcMatrix *b); -void +FcPublic void FcMatrixRotate (FcMatrix *m, double c, double s); -void +FcPublic void FcMatrixScale (FcMatrix *m, double sx, double sy); -void +FcPublic void FcMatrixShear (FcMatrix *m, double sh, double sv); /* fcname.c */ -FcBool +FcPublic FcBool FcNameRegisterObjectTypes (const FcObjectType *types, int ntype); -FcBool +FcPublic FcBool FcNameUnregisterObjectTypes (const FcObjectType *types, int ntype); -const FcObjectType * +FcPublic const FcObjectType * FcNameGetObjectType (const char *object); -FcBool +FcPublic FcBool FcNameRegisterConstants (const FcConstant *consts, int nconsts); -FcBool +FcPublic FcBool FcNameUnregisterConstants (const FcConstant *consts, int nconsts); -const FcConstant * +FcPublic const FcConstant * FcNameGetConstant (FcChar8 *string); -FcBool +FcPublic FcBool FcNameConstant (FcChar8 *string, int *result); -FcPattern * +FcPublic FcPattern * FcNameParse (const FcChar8 *name); -FcChar8 * +FcPublic FcChar8 * FcNameUnparse (FcPattern *pat); /* fcpat.c */ -FcPattern * +FcPublic FcPattern * FcPatternCreate (void); -FcPattern * +FcPublic FcPattern * FcPatternDuplicate (const FcPattern *p); -void +FcPublic void FcPatternReference (FcPattern *p); -void +FcPublic void FcValueDestroy (FcValue v); -FcBool +FcPublic FcBool FcValueEqual (FcValue va, FcValue vb); -FcValue +FcPublic FcValue FcValueSave (FcValue v); -void +FcPublic void FcPatternDestroy (FcPattern *p); -FcBool +FcPublic FcBool FcPatternEqual (const FcPattern *pa, const FcPattern *pb); -FcBool +FcPublic FcBool FcPatternEqualSubset (const FcPattern *pa, const FcPattern *pb, const FcObjectSet *os); -FcChar32 +FcPublic FcChar32 FcPatternHash (const FcPattern *p); -FcBool +FcPublic FcBool FcPatternAdd (FcPattern *p, const char *object, FcValue value, FcBool append); -FcBool +FcPublic FcBool FcPatternAddWeak (FcPattern *p, const char *object, FcValue value, FcBool append); -FcResult +FcPublic FcResult FcPatternGet (const FcPattern *p, const char *object, int id, FcValue *v); -FcBool +FcPublic FcBool FcPatternDel (FcPattern *p, const char *object); -FcBool +FcPublic FcBool +FcPatternRemove (FcPattern *p, const char *object, int id); + +FcPublic FcBool FcPatternAddInteger (FcPattern *p, const char *object, int i); -FcBool +FcPublic FcBool FcPatternAddDouble (FcPattern *p, const char *object, double d); -FcBool +FcPublic FcBool FcPatternAddString (FcPattern *p, const char *object, const FcChar8 *s); -FcBool +FcPublic FcBool FcPatternAddMatrix (FcPattern *p, const char *object, const FcMatrix *s); -FcBool +FcPublic FcBool FcPatternAddCharSet (FcPattern *p, const char *object, const FcCharSet *c); -FcBool +FcPublic FcBool FcPatternAddBool (FcPattern *p, const char *object, FcBool b); -FcBool +FcPublic FcBool FcPatternAddLangSet (FcPattern *p, const char *object, const FcLangSet *ls); -FcResult +FcPublic FcResult FcPatternGetInteger (const FcPattern *p, const char *object, int n, int *i); -FcResult +FcPublic FcResult FcPatternGetDouble (const FcPattern *p, const char *object, int n, double *d); -FcResult +FcPublic FcResult FcPatternGetString (const FcPattern *p, const char *object, int n, FcChar8 ** s); -FcResult +FcPublic FcResult FcPatternGetMatrix (const FcPattern *p, const char *object, int n, FcMatrix **s); -FcResult +FcPublic FcResult FcPatternGetCharSet (const FcPattern *p, const char *object, int n, FcCharSet **c); -FcResult +FcPublic FcResult FcPatternGetBool (const FcPattern *p, const char *object, int n, FcBool *b); -FcResult +FcPublic FcResult FcPatternGetLangSet (const FcPattern *p, const char *object, int n, FcLangSet **ls); -FcPattern * +FcPublic FcPattern * FcPatternVaBuild (FcPattern *orig, va_list va); -FcPattern * -FcPatternBuild (FcPattern *orig, ...); +FcPublic FcPattern * +FcPatternBuild (FcPattern *orig, ...) FC_ATTRIBUTE_SENTINEL(0); /* fcstr.c */ -FcChar8 * +FcPublic FcChar8 * FcStrCopy (const FcChar8 *s); -FcChar8 * +FcPublic FcChar8 * FcStrCopyFilename (const FcChar8 *s); -#define FcIsUpper(c) (('A' <= (c) && (c) <= 'Z')) -#define FcIsLower(c) (('a' <= (c) && (c) <= 'z')) -#define FcToLower(c) (FcIsUpper(c) ? (c) - 'A' + 'a' : (c)) +FcPublic FcChar8 * +FcStrPlus (const FcChar8 *s1, const FcChar8 *s2); -int +FcPublic void +FcStrFree (FcChar8 *s); + +/* These are ASCII only, suitable only for pattern element names */ +#define FcIsUpper(c) ((0101 <= (c) && (c) <= 0132)) +#define FcIsLower(c) ((0141 <= (c) && (c) <= 0172)) +#define FcToLower(c) (FcIsUpper(c) ? (c) - 0101 + 0141 : (c)) + +FcPublic FcChar8 * +FcStrDowncase (const FcChar8 *s); + +FcPublic int FcStrCmpIgnoreCase (const FcChar8 *s1, const FcChar8 *s2); -int +FcPublic int FcStrCmp (const FcChar8 *s1, const FcChar8 *s2); -int +FcPublic const FcChar8 * +FcStrStrIgnoreCase (const FcChar8 *s1, const FcChar8 *s2); + +FcPublic const FcChar8 * +FcStrStr (const FcChar8 *s1, const FcChar8 *s2); + +FcPublic int FcUtf8ToUcs4 (const FcChar8 *src_orig, FcChar32 *dst, int len); -FcBool +FcPublic FcBool FcUtf8Len (const FcChar8 *string, int len, int *nchar, @@ -753,63 +864,78 @@ FcUtf8Len (const FcChar8 *string, #define FC_UTF8_MAX_LEN 6 -int +FcPublic int FcUcs4ToUtf8 (FcChar32 ucs4, FcChar8 dest[FC_UTF8_MAX_LEN]); -int +FcPublic int FcUtf16ToUcs4 (const FcChar8 *src_orig, FcEndian endian, FcChar32 *dst, int len); /* in bytes */ -FcBool +FcPublic FcBool FcUtf16Len (const FcChar8 *string, FcEndian endian, int len, /* in bytes */ int *nchar, int *wchar); -FcChar8 * +FcPublic FcChar8 * FcStrDirname (const FcChar8 *file); -FcChar8 * +FcPublic FcChar8 * FcStrBasename (const FcChar8 *file); -FcStrSet * +FcPublic FcStrSet * FcStrSetCreate (void); -FcBool +FcPublic FcBool FcStrSetMember (FcStrSet *set, const FcChar8 *s); -FcBool +FcPublic FcBool FcStrSetEqual (FcStrSet *sa, FcStrSet *sb); -FcBool +FcPublic FcBool FcStrSetAdd (FcStrSet *set, const FcChar8 *s); -FcBool +FcPublic FcBool FcStrSetAddFilename (FcStrSet *set, const FcChar8 *s); -FcBool +FcPublic FcBool FcStrSetDel (FcStrSet *set, const FcChar8 *s); -void +FcPublic void FcStrSetDestroy (FcStrSet *set); -FcStrList * +FcPublic FcStrList * FcStrListCreate (FcStrSet *set); -FcChar8 * +FcPublic FcChar8 * FcStrListNext (FcStrList *list); -void +FcPublic void FcStrListDone (FcStrList *list); /* fcxml.c */ -FcBool +FcPublic FcBool FcConfigParseAndLoad (FcConfig *config, const FcChar8 *file, FcBool complain); _FCFUNCPROTOEND +#undef FC_ATTRIBUTE_SENTINEL + + +#ifndef _FCINT_H_ + +/* + * Deprecated functions are placed here to help users fix their code without + * digging through documentation + */ + +#define FcConfigGetRescanInverval FcConfigGetRescanInverval_REPLACE_BY_FcConfigGetRescanInterval +#define FcConfigSetRescanInverval FcConfigSetRescanInverval_REPLACE_BY_FcConfigSetRescanInterval + +#endif + #endif /* _FONTCONFIG_H_ */ diff --git a/jdk/src/solaris/native/sun/awt/fontpath.c b/jdk/src/solaris/native/sun/awt/fontpath.c index d863d265b58..8f9f3d9a894 100644 --- a/jdk/src/solaris/native/sun/awt/fontpath.c +++ b/jdk/src/solaris/native/sun/awt/fontpath.c @@ -735,6 +735,25 @@ typedef FcPattern* (*FcFontMatchFuncType)(FcConfig *config, typedef FcFontSet* (*FcFontSetCreateFuncType)(); typedef FcBool (*FcFontSetAddFuncType)(FcFontSet *s, FcPattern *font); +typedef FcResult (*FcPatternGetCharSetFuncType)(FcPattern *p, + const char *object, + int n, + FcCharSet **c); +typedef FcFontSet* (*FcFontSortFuncType)(FcConfig *config, + FcPattern *p, + FcBool trim, + FcCharSet **csp, + FcResult *result); +typedef FcCharSet* (*FcCharSetUnionFuncType)(const FcCharSet *a, + const FcCharSet *b); +typedef FcChar32 (*FcCharSetSubtractCountFuncType)(const FcCharSet *a, + const FcCharSet *b); + +typedef int (*FcGetVersionFuncType)(); + +typedef FcStrList* (*FcConfigGetCacheDirsFuncType)(FcConfig *config); +typedef FcChar8* (*FcStrListNextFuncType)(FcStrList *list); +typedef FcChar8* (*FcStrListDoneFuncType)(FcStrList *list); static char **getFontConfigLocations() { @@ -955,10 +974,35 @@ Java_sun_font_FontManager_getFontConfigAASettings } } +JNIEXPORT jint JNICALL +Java_sun_font_FontManager_getFontConfigVersion + (JNIEnv *env, jclass obj) { + + void* libfontconfig; + FcGetVersionFuncType FcGetVersion; + int version = 0; + + if ((libfontconfig = openFontConfig()) == NULL) { + return 0; + } + + FcGetVersion = (FcGetVersionFuncType)dlsym(libfontconfig, "FcGetVersion"); + + if (FcGetVersion == NULL) { + closeFontConfig(libfontconfig, JNI_FALSE); + return 0; + } + version = (*FcGetVersion)(); + closeFontConfig(libfontconfig, JNI_FALSE); + + return version; +} + JNIEXPORT void JNICALL Java_sun_font_FontManager_getFontConfig -(JNIEnv *env, jclass obj, jstring localeStr, jobjectArray fontInfoArray) { +(JNIEnv *env, jclass obj, jstring localeStr, jobject fcInfoObj, + jobjectArray fcCompFontArray, jboolean includeFallbacks) { FcNameParseFuncType FcNameParse; FcPatternAddStringFuncType FcPatternAddString; @@ -967,33 +1011,70 @@ Java_sun_font_FontManager_getFontConfig FcFontMatchFuncType FcFontMatch; FcPatternGetStringFuncType FcPatternGetString; FcPatternDestroyFuncType FcPatternDestroy; + FcPatternGetCharSetFuncType FcPatternGetCharSet; + FcFontSortFuncType FcFontSort; + FcFontSetDestroyFuncType FcFontSetDestroy; + FcCharSetUnionFuncType FcCharSetUnion; + FcCharSetSubtractCountFuncType FcCharSetSubtractCount; + FcGetVersionFuncType FcGetVersion; + FcConfigGetCacheDirsFuncType FcConfigGetCacheDirs; + FcStrListNextFuncType FcStrListNext; + FcStrListDoneFuncType FcStrListDone; int i, arrlen; - jobject fontInfoObj; + jobject fcCompFontObj; jstring fcNameStr, jstr; const char *locale, *fcName; - FcPattern *pattern, *matchPattern; + FcPattern *pattern; FcResult result; void* libfontconfig; - jfieldID fcNameID, familyNameID, fontFileID; + jfieldID fcNameID, fcFirstFontID, fcAllFontsID, fcVersionID, fcCacheDirsID; + jfieldID familyNameID, styleNameID, fullNameID, fontFileID; + jmethodID fcFontCons; + char* debugMinGlyphsStr = getenv("J2D_DEBUG_MIN_GLYPHS"); - jclass fontInfoArrayClass = - (*env)->FindClass(env, "[Lsun/font/FontManager$FontConfigInfo;"); - jclass fontInfoClass = + jclass fcInfoClass = (*env)->FindClass(env, "sun/font/FontManager$FontConfigInfo"); + jclass fcCompFontClass = + (*env)->FindClass(env, "sun/font/FontManager$FcCompFont"); + jclass fcFontClass = + (*env)->FindClass(env, "sun/font/FontManager$FontConfigFont"); - if (fontInfoArray == NULL || fontInfoClass == NULL) { + if (fcInfoObj == NULL || fcCompFontArray == NULL || fcInfoClass == NULL || + fcCompFontClass == NULL || fcFontClass == NULL) { return; } - fcNameID = (*env)->GetFieldID(env, fontInfoClass, + fcVersionID = (*env)->GetFieldID(env, fcInfoClass, "fcVersion", "I"); + + fcCacheDirsID = (*env)->GetFieldID(env, fcInfoClass, "cacheDirs", + "[Ljava/lang/String;"); + + fcNameID = (*env)->GetFieldID(env, fcCompFontClass, "fcName", "Ljava/lang/String;"); - familyNameID = (*env)->GetFieldID(env, fontInfoClass, + fcFirstFontID = + (*env)->GetFieldID(env, fcCompFontClass, "firstFont", + "Lsun/font/FontManager$FontConfigFont;"); + + fcAllFontsID = + (*env)->GetFieldID(env, fcCompFontClass, "allFonts", + "[Lsun/font/FontManager$FontConfigFont;"); + + fcFontCons = (*env)->GetMethodID(env, fcFontClass, "", "()V"); + + familyNameID = (*env)->GetFieldID(env, fcFontClass, "familyName", "Ljava/lang/String;"); - fontFileID = (*env)->GetFieldID(env, fontInfoClass, + styleNameID = (*env)->GetFieldID(env, fcFontClass, + "styleStr", "Ljava/lang/String;"); + fullNameID = (*env)->GetFieldID(env, fcFontClass, + "fullName", "Ljava/lang/String;"); + fontFileID = (*env)->GetFieldID(env, fcFontClass, "fontFile", "Ljava/lang/String;"); - if (fcNameID == NULL || familyNameID == NULL || fontFileID == NULL) { + if (fcVersionID == NULL || fcCacheDirsID == NULL || fcNameID == NULL || + fcFirstFontID == NULL || fcAllFontsID == NULL || fcFontCons == NULL || + familyNameID == NULL || styleNameID == NULL || fullNameID == NULL || + fontFileID == NULL) { return; } @@ -1013,6 +1094,19 @@ Java_sun_font_FontManager_getFontConfig (FcPatternGetStringFuncType)dlsym(libfontconfig, "FcPatternGetString"); FcPatternDestroy = (FcPatternDestroyFuncType)dlsym(libfontconfig, "FcPatternDestroy"); + FcPatternGetCharSet = + (FcPatternGetCharSetFuncType)dlsym(libfontconfig, + "FcPatternGetCharSet"); + FcFontSort = + (FcFontSortFuncType)dlsym(libfontconfig, "FcFontSort"); + FcFontSetDestroy = + (FcFontSetDestroyFuncType)dlsym(libfontconfig, "FcFontSetDestroy"); + FcCharSetUnion = + (FcCharSetUnionFuncType)dlsym(libfontconfig, "FcCharSetUnion"); + FcCharSetSubtractCount = + (FcCharSetSubtractCountFuncType)dlsym(libfontconfig, + "FcCharSetSubtractCount"); + FcGetVersion = (FcGetVersionFuncType)dlsym(libfontconfig, "FcGetVersion"); if (FcNameParse == NULL || FcPatternAddString == NULL || @@ -1020,23 +1114,77 @@ Java_sun_font_FontManager_getFontConfig FcDefaultSubstitute == NULL || FcFontMatch == NULL || FcPatternGetString == NULL || - FcPatternDestroy == NULL) { /* problem with the library: return. */ + FcPatternDestroy == NULL || + FcPatternGetCharSet == NULL || + FcFontSetDestroy == NULL || + FcCharSetUnion == NULL || + FcGetVersion == NULL || + FcCharSetSubtractCount == NULL) {/* problem with the library: return.*/ closeFontConfig(libfontconfig, JNI_FALSE); return; } + (*env)->SetIntField(env, fcInfoObj, fcVersionID, (*FcGetVersion)()); + + /* Optionally get the cache dir locations. This isn't + * available until v 2.4.x, but this is OK since on those later versions + * we can check the time stamps on the cache dirs to see if we + * are out of date. There are a couple of assumptions here. First + * that the time stamp on the directory changes when the contents are + * updated. Secondly that the locations don't change. The latter is + * most likely if a new version of fontconfig is installed, but we also + * invalidate the cache if we detect that. Arguably even that is "rare", + * and most likely is tied to an OS upgrade which gets a new file anyway. + */ + FcConfigGetCacheDirs = + (FcConfigGetCacheDirsFuncType)dlsym(libfontconfig, + "FcConfigGetCacheDirs"); + FcStrListNext = + (FcStrListNextFuncType)dlsym(libfontconfig, "FcStrListNext"); + FcStrListDone = + (FcStrListDoneFuncType)dlsym(libfontconfig, "FcStrListDone"); + if (FcStrListNext != NULL && FcStrListDone != NULL && + FcConfigGetCacheDirs != NULL) { + + FcStrList* cacheDirs; + FcChar8* cacheDir; + int cnt = 0; + jobject cacheDirArray = + (*env)->GetObjectField(env, fcInfoObj, fcCacheDirsID); + int max = (*env)->GetArrayLength(env, cacheDirArray); + + cacheDirs = (*FcConfigGetCacheDirs)(NULL); + if (cacheDirs != NULL) { + while ((cnt < max) && (cacheDir = (*FcStrListNext)(cacheDirs))) { + jstr = (*env)->NewStringUTF(env, (const char*)cacheDir); + (*env)->SetObjectArrayElement(env, cacheDirArray, cnt++, jstr); + } + (*FcStrListDone)(cacheDirs); + } + } + locale = (*env)->GetStringUTFChars(env, localeStr, 0); - arrlen = (*env)->GetArrayLength(env, fontInfoArray); + arrlen = (*env)->GetArrayLength(env, fcCompFontArray); for (i=0; iGetObjectArrayElement(env, fontInfoArray, i); + FcFontSet* fontset; + int fn, j, fontCount, nfonts, minGlyphs; + FcChar8 **family, **styleStr, **fullname, **file; + jarray fcFontArr; + + fcCompFontObj = (*env)->GetObjectArrayElement(env, fcCompFontArray, i); fcNameStr = - (jstring)((*env)->GetObjectField(env, fontInfoObj, fcNameID)); + (jstring)((*env)->GetObjectField(env, fcCompFontObj, fcNameID)); fcName = (*env)->GetStringUTFChars(env, fcNameStr, 0); if (fcName == NULL) { continue; } pattern = (*FcNameParse)((FcChar8 *)fcName); + if (pattern == NULL) { + closeFontConfig(libfontconfig, JNI_FALSE); + return; + } + /* locale may not usually be necessary as fontconfig appears to apply * this anyway based on the user's environment. However we want * to use the value of the JDK startup locale so this should take @@ -1047,25 +1195,134 @@ Java_sun_font_FontManager_getFontConfig } (*FcConfigSubstitute)(NULL, pattern, FcMatchPattern); (*FcDefaultSubstitute)(pattern); - matchPattern = (*FcFontMatch)(NULL, pattern, &result); - if (matchPattern) { - FcChar8 *file, *family; + fontset = (*FcFontSort)(NULL, pattern, FcTrue, NULL, &result); + if (fontset == NULL) { + closeFontConfig(libfontconfig, JNI_FALSE); + return; + } - (*FcPatternGetString)(matchPattern, FC_FILE, 0, &file); - (*FcPatternGetString)(matchPattern, FC_FAMILY, 0, &family); + /* fontconfig returned us "nfonts". If we are just getting the + * first font, we set nfont to zero. Otherwise we use "nfonts". + * Next create separate C arrrays of length nfonts for family file etc. + * Inspect the returned fonts and the ones we like (adds enough glyphs) + * are added to the arrays and we increment 'fontCount'. + */ + if (includeFallbacks) { + nfonts = fontset->nfont; + } else { + nfonts = 1; + } + family = (FcChar8**)calloc(nfonts, sizeof(FcChar8*)); + styleStr = (FcChar8**)calloc(nfonts, sizeof(FcChar8*)); + fullname = (FcChar8**)calloc(nfonts, sizeof(FcChar8*)); + file = (FcChar8**)calloc(nfonts, sizeof(FcChar8*)); + if (family == NULL || styleStr == NULL || + fullname == NULL || file == NULL) { + closeFontConfig(libfontconfig, JNI_FALSE); + return; + } + fontCount = 0; + minGlyphs = 20; + if (debugMinGlyphsStr != NULL) { + int val = minGlyphs; + sscanf(debugMinGlyphsStr, "%5d", &val); + if (val >= 0 && val <= 65536) { + minGlyphs = val; + } + } + for (j=0; jfonts[j]; + FcChar8 *fontformat; + FcCharSet *unionCharset, *charset; - if (file != NULL) { - jstr = (*env)->NewStringUTF(env, (const char*)file); - ((*env)->SetObjectField(env, fontInfoObj, fontFileID, jstr)); + fontformat = NULL; + (*FcPatternGetString)(fontPattern, FC_FONTFORMAT, 0, &fontformat); + if (fontformat != NULL && strcmp((char*)fontformat, "TrueType") + != 0) { + continue; } - if (family != NULL) { - jstr = (*env)->NewStringUTF(env, (const char*)family); - ((*env)->SetObjectField(env, fontInfoObj, familyNameID, jstr)); + result = (*FcPatternGetCharSet)(fontPattern, + FC_CHARSET, 0, &charset); + if (result != FcResultMatch) { + closeFontConfig(libfontconfig, JNI_FALSE); + return; + } + + /* We don't want 20 or 30 fonts, so once we hit 10 fonts, + * then require that they really be adding value. Too many + * adversely affects load time for minimal value-add. + * This is still likely far more than we've had in the past. + */ + if (nfonts==10) { + minGlyphs = 50; + } + if (j == 0) { + unionCharset = charset; + } else { + if ((*FcCharSetSubtractCount)(charset, unionCharset) + > minGlyphs) { + unionCharset = (* FcCharSetUnion)(unionCharset, charset); + } else { + continue; + } + } + + fontCount++; // found a font we will use. + (*FcPatternGetString)(fontPattern, FC_FILE, 0, &file[j]); + (*FcPatternGetString)(fontPattern, FC_FAMILY, 0, &family[j]); + (*FcPatternGetString)(fontPattern, FC_STYLE, 0, &styleStr[j]); + (*FcPatternGetString)(fontPattern, FC_FULLNAME, 0, &fullname[j]); + } + + /* Once we get here 'fontCount' is the number of returned fonts + * we actually want to use, so we create 'fcFontArr' of that length. + * The non-null entries of "family[]" etc are those fonts. + * Then loop again over all nfonts adding just those non-null ones + * to 'fcFontArr'. If its null (we didn't want the font) + * then we don't enter the main body. + * So we should never get more than 'fontCount' entries. + */ + if (includeFallbacks) { + fcFontArr = + (*env)->NewObjectArray(env, fontCount, fcFontClass, NULL); + (*env)->SetObjectField(env,fcCompFontObj, fcAllFontsID, fcFontArr); + } + fn=0; + + for (j=0;jNewObject(env, fcFontClass, fcFontCons); + jstr = (*env)->NewStringUTF(env, (const char*)family[j]); + (*env)->SetObjectField(env, fcFont, familyNameID, jstr); + if (file[j] != NULL) { + jstr = (*env)->NewStringUTF(env, (const char*)file[j]); + (*env)->SetObjectField(env, fcFont, fontFileID, jstr); + } + if (styleStr[j] != NULL) { + jstr = (*env)->NewStringUTF(env, (const char*)styleStr[j]); + (*env)->SetObjectField(env, fcFont, styleNameID, jstr); + } + if (fullname[j] != NULL) { + jstr = (*env)->NewStringUTF(env, (const char*)fullname[j]); + (*env)->SetObjectField(env, fcFont, fullNameID, jstr); + } + if (fn==0) { + (*env)->SetObjectField(env, fcCompFontObj, + fcFirstFontID, fcFont); + } + if (includeFallbacks) { + (*env)->SetObjectArrayElement(env, fcFontArr, fn++,fcFont); + } } - (*FcPatternDestroy)(matchPattern); } (*env)->ReleaseStringUTFChars (env, fcNameStr, (const char*)fcName); + (*FcFontSetDestroy)(fontset); (*FcPatternDestroy)(pattern); + free(family); + free(styleStr); + free(fullname); + free(file); } /* release resources and close the ".so" */ diff --git a/jdk/src/windows/classes/sun/awt/Win32GraphicsEnvironment.java b/jdk/src/windows/classes/sun/awt/Win32GraphicsEnvironment.java index c3820c22a5f..8828b2f0867 100644 --- a/jdk/src/windows/classes/sun/awt/Win32GraphicsEnvironment.java +++ b/jdk/src/windows/classes/sun/awt/Win32GraphicsEnvironment.java @@ -338,7 +338,9 @@ public class Win32GraphicsEnvironment // Implements SunGraphicsEnvironment.createFontConfiguration. protected FontConfiguration createFontConfiguration() { - return new WFontConfiguration(this); + FontConfiguration fc = new WFontConfiguration(this); + fc.init(); + return fc; } public FontConfiguration createFontConfiguration(boolean preferLocaleFonts, From 5917bb24f3bbccf99e1e1811a1f44c89a7bb9d7d Mon Sep 17 00:00:00 2001 From: Steven Loomis Date: Tue, 17 Jun 2008 18:38:20 -0700 Subject: [PATCH 003/105] 6711377: test/java/awt/font/TextLayout/VisibleAdvance.java missing GPL Reviewed-by: igor, prr --- .../awt/font/TextLayout/VisibleAdvance.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/jdk/test/java/awt/font/TextLayout/VisibleAdvance.java b/jdk/test/java/awt/font/TextLayout/VisibleAdvance.java index 446d16dba15..224b2386125 100644 --- a/jdk/test/java/awt/font/TextLayout/VisibleAdvance.java +++ b/jdk/test/java/awt/font/TextLayout/VisibleAdvance.java @@ -1,3 +1,27 @@ +/* + * 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 + * 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.awt.*; import java.awt.font.*; import java.awt.geom.*; From 36d2406edc14eaf7d7fedfe0933e6770778bcd51 Mon Sep 17 00:00:00 2001 From: Jennifer Godinez Date: Mon, 23 Jun 2008 13:00:19 -0700 Subject: [PATCH 004/105] 6708509: print dialog is not displayed when default paper is custom Reviewed-by: tdv, prr --- .../native/sun/windows/awt_PrintJob.cpp | 4 +- .../awt/print/PrinterJob/PrintAWTImage.java | 90 ++++++++++++++++++ jdk/test/java/awt/print/PrinterJob/duke.gif | Bin 0 -> 1929 bytes 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 jdk/test/java/awt/print/PrinterJob/PrintAWTImage.java create mode 100644 jdk/test/java/awt/print/PrinterJob/duke.gif diff --git a/jdk/src/windows/native/sun/windows/awt_PrintJob.cpp b/jdk/src/windows/native/sun/windows/awt_PrintJob.cpp index 5d15e72540a..0ecd0388ac0 100644 --- a/jdk/src/windows/native/sun/windows/awt_PrintJob.cpp +++ b/jdk/src/windows/native/sun/windows/awt_PrintJob.cpp @@ -632,7 +632,9 @@ Java_sun_awt_windows_WPrinterJob_getDefaultPage(JNIEnv *env, jobject self, return ; } - if (pDevMode->dmFields & DM_PAPERSIZE) { + if ((pDevMode->dmFields & DM_PAPERSIZE) || + (pDevMode->dmFields & DM_PAPERWIDTH) || + (pDevMode->dmFields & DM_PAPERLENGTH)) { POINT paperSize; RECT margins; jint orientation = PAGEFORMAT_PORTRAIT; diff --git a/jdk/test/java/awt/print/PrinterJob/PrintAWTImage.java b/jdk/test/java/awt/print/PrinterJob/PrintAWTImage.java new file mode 100644 index 00000000000..0cb3b4eb86b --- /dev/null +++ b/jdk/test/java/awt/print/PrinterJob/PrintAWTImage.java @@ -0,0 +1,90 @@ +/* + * Copyright 1999-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 + * @bug 4257262 6708509 + * @summary Image should be sent to printer. +* @run main/manual PrintAWTImage + */ + +import java.awt.*; +import java.awt.event.*; +import java.awt.print.*; + + +public class PrintAWTImage extends Frame + implements ActionListener, Printable { + + public Image imgJava; + + + public static void main(String args[]) { + PrintAWTImage f = new PrintAWTImage(); + f.show(); + } + + public PrintAWTImage() { + + Button printButton = new Button("Print"); + setLayout(new FlowLayout()); + add(printButton); + printButton.addActionListener(this); + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + + pack(); + } + + public void actionPerformed(ActionEvent e) { + + PrinterJob pj = PrinterJob.getPrinterJob(); + + if (pj != null && pj.printDialog()) { + pj.setPrintable(this); + try { + pj.print(); + } catch (PrinterException pe) { + } finally { + System.err.println("PRINT RETURNED"); + } + } + } + + + public int print(Graphics g, PageFormat pgFmt, int pgIndex) { + if (pgIndex > 0) + return Printable.NO_SUCH_PAGE; + + Graphics2D g2d = (Graphics2D)g; + g2d.translate(pgFmt.getImageableX(), pgFmt.getImageableY()); + Image imgJava = Toolkit.getDefaultToolkit().getImage("duke.gif"); + g2d.drawImage(imgJava, 0, 0, this); + + return Printable.PAGE_EXISTS; + } + +} diff --git a/jdk/test/java/awt/print/PrinterJob/duke.gif b/jdk/test/java/awt/print/PrinterJob/duke.gif new file mode 100644 index 0000000000000000000000000000000000000000..ed32e0ff79b05c07b82863ce6fb07fa9898adaa2 GIT binary patch literal 1929 zcmWlYe^AtB8pi`HvM4_?{J2I$8$dLc1#@!R2zwgXMWdj^k;9xr+bDW&4{JlE8WpDj z7F-cwwK{H<)?L&#SJz$!;hJ{%BY2FYf)Wp^xl?aq!5Xcdi$c#hV~>m9_n-Hl=Xsy+ z=li^?*Q~;pZ+R1N1J40KRkeWM7ew3~jLM24A{CM-A}~TzqzYpq&od0GC=z71!w_=b z-==B0rt2t*nA}((5YSLs6a*Z@X__WqiSjTW6oLo{5km&|K1mGAimYjhs#wwZtvV8SV~7LCFpgub+-TTAk%UQb0dE_cj+pc?!+0o?qG$?% zVFD)%!w7Z;g)ndE8Uk6Aky=+kLaUQ{UW`XS?Nn*s@SQ{VmFgGdkV{&&98EcEQ5hjc@H$`e)fX zj@&GdchxpMUo|-A^M4iBP3(#Ib53Ap?5{nGT7SBA_V!o!TTzL5R~FUWe)4X?@iTd8 z1;TcF^rQLj?4p0uy?@ikb2eUSXdHVa_jIn=@W%a<6~57D>am6&Z!{lzc=@ZbuGB8` zpU38H8d~@82Da!+qdYG5ls&Cx?~|oPMnbqTHMw%I*KlV~?fc{rSwe29?Om}fsknG# z@n5IwY=4Mx>>0WJLG>=yJX^WbHA30iQ$H!X)3<4K zBe1|sf3NKKTS;)mg{$k(2eDJG^u5=&x{@M!V>EWgzRA((>}?o{WQBehp1mIHU!BGG zYz5_6B(+KIVdCVoum2ItM&gXZd+SB^vQTN=a zeYbbah=i-xCho2{4Pazv_i%2mH`EkM{r8XYDLbdY@(a7Ud}$%!$QrTN_DqwNXA9~g zTGKxKyfto7NDp;5A3O5zgb(hyxjN@OAG!(zy^*Ug4!yjF=Y*8aHA@ovB1({&a4;sR zTf1CVC{>Pgy`m$lG;P1$pC_6F7u%iP+qz0q4{lXT`i9g-ThiYgO^GXC`f?JNo*|@p zr{b%U-tSKw99q0|YJa9{Va?`H{IaNICo>p5lGEY*+IDR4bfIUwq~CTRuC_mGWA%~W zea{@eKJ(Iq^7MvdsPsR%&vt$@4i&s?bPptz#y#!FcRZEaMS0WFTyXMCUEfsNxnJ_9 zPwpt`Er4O>``2G{7=4r1GCSTO8#0xw+{<^L4X(K8y1wKj72KLrYD}Y7SJuY7y==wf z;UkI5?(v?h+4r;vR{P*U`ul~=D@U7K5$eV8c!%rX-38vE>azU80UrhFXCv#d`(ylZS4+i2a^vI91MTIxCx%9gd2&N&D9RC&xcpx8#f=GZv%9;F z#?CEVT%UV$nk;L%RJA+d=f8ZB@U*Xz-TZbG?HKKT(VJZMBH!)$#qRuwbFc%Aljqha zoNBs8od~V$_^vux0ZSk!iP!hI($t35SxY8`FV{pxCjpU}Ova2VIg1&>V)CvvMb_ Date: Wed, 25 Jun 2008 16:33:59 -0700 Subject: [PATCH 005/105] 6614556: null location for MonitorContendedEnterEvent Reviewed-by: jjh --- jdk/src/share/classes/com/sun/tools/jdi/EventSetImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/jdk/src/share/classes/com/sun/tools/jdi/EventSetImpl.java b/jdk/src/share/classes/com/sun/tools/jdi/EventSetImpl.java index 1a2c61c7bc9..82a7c78085a 100644 --- a/jdk/src/share/classes/com/sun/tools/jdi/EventSetImpl.java +++ b/jdk/src/share/classes/com/sun/tools/jdi/EventSetImpl.java @@ -208,8 +208,9 @@ public class EventSetImpl extends ArrayList implements EventSet { } public String toString() { - return eventName() + "@" + location().toString() + - " in thread " + thread().name(); + return eventName() + "@" + + ((location() == null) ? " null" : location().toString()) + + " in thread " + thread().name(); } } From 19fc7593d1504862e6d813750578d0d1f46587a7 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Collet Date: Tue, 1 Jul 2008 13:29:36 +0200 Subject: [PATCH 006/105] 6713809: FTP fails from multi-homed system Binds the data socket to the same address as the control socket Reviewed-by: michaelm --- jdk/src/share/classes/sun/net/ftp/FtpClient.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/jdk/src/share/classes/sun/net/ftp/FtpClient.java b/jdk/src/share/classes/sun/net/ftp/FtpClient.java index 9a7c99d517d..a93489f4b60 100644 --- a/jdk/src/share/classes/sun/net/ftp/FtpClient.java +++ b/jdk/src/share/classes/sun/net/ftp/FtpClient.java @@ -352,6 +352,9 @@ public class FtpClient extends TransferProtocolClient { s = new Socket(Proxy.NO_PROXY); } else s = new Socket(); + // Bind the socket to the same address as the control channel. This + // is needed in case of multi-homed systems. + s.bind(new InetSocketAddress(serverSocket.getLocalAddress(),0)); if (connectTimeout >= 0) { s.connect(dest, connectTimeout); } else { @@ -417,8 +420,10 @@ public class FtpClient extends TransferProtocolClient { // since we can't accept a connection through SOCKS (yet) // throw an exception throw new FtpProtocolException("Passive mode failed"); - } else - portSocket = new ServerSocket(0, 1); + } + // Bind the ServerSocket to the same address as the control channel + // This is needed for multi-homed systems + portSocket = new ServerSocket(0, 1, serverSocket.getLocalAddress()); try { myAddress = portSocket.getInetAddress(); if (myAddress.isAnyLocalAddress()) From 3fce795f6c2f6f7562445e89abe2e7c11db432b4 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Collet Date: Tue, 1 Jul 2008 13:38:59 +0200 Subject: [PATCH 007/105] 6656849: NullPointerException thrown while de-serializing IPV6 Address Check for existence of interface name earlier in code Reviewed-by: michaelm --- .../share/classes/java/net/Inet6Address.java | 60 +++++++++++------- .../net/Inet6Address/serialize/Readme.txt | 6 ++ .../net/Inet6Address/serialize/Serialize.java | 22 +++++-- .../Inet6Address/serialize/serial-bge0.ser | Bin 0 -> 266 bytes 4 files changed, 57 insertions(+), 31 deletions(-) create mode 100644 jdk/test/java/net/Inet6Address/serialize/Readme.txt create mode 100644 jdk/test/java/net/Inet6Address/serialize/serial-bge0.ser diff --git a/jdk/src/share/classes/java/net/Inet6Address.java b/jdk/src/share/classes/java/net/Inet6Address.java index 299d55e5f06..0778f5a02fe 100644 --- a/jdk/src/share/classes/java/net/Inet6Address.java +++ b/jdk/src/share/classes/java/net/Inet6Address.java @@ -25,12 +25,9 @@ package java.net; -import java.security.AccessController; import java.io.ObjectInputStream; import java.io.IOException; -import java.io.ObjectStreamException; import java.io.InvalidObjectException; -import sun.security.action.*; import java.util.Enumeration; /** @@ -358,13 +355,13 @@ class Inet6Address extends InetAddress { } private int deriveNumericScope (NetworkInterface ifc) throws UnknownHostException { - Enumeration addresses = ifc.getInetAddresses(); + Enumeration addresses = ifc.getInetAddresses(); while (addresses.hasMoreElements()) { - InetAddress address = (InetAddress)addresses.nextElement(); - if (!(address instanceof Inet6Address)) { + InetAddress addr = addresses.nextElement(); + if (!(addr instanceof Inet6Address)) { continue; } - Inet6Address ia6_addr = (Inet6Address)address; + Inet6Address ia6_addr = (Inet6Address)addr; /* check if site or link local prefixes match */ if (!differentLocalAddressTypes(ia6_addr)){ /* type not the same, so carry on searching */ @@ -377,22 +374,22 @@ class Inet6Address extends InetAddress { } private int deriveNumericScope (String ifname) throws UnknownHostException { - Enumeration en; + Enumeration en; try { en = NetworkInterface.getNetworkInterfaces(); } catch (SocketException e) { throw new UnknownHostException ("could not enumerate local network interfaces"); } while (en.hasMoreElements()) { - NetworkInterface ifc = (NetworkInterface)en.nextElement(); + NetworkInterface ifc = en.nextElement(); if (ifc.getName().equals (ifname)) { Enumeration addresses = ifc.getInetAddresses(); while (addresses.hasMoreElements()) { - InetAddress address = (InetAddress)addresses.nextElement(); - if (!(address instanceof Inet6Address)) { + InetAddress addr = (InetAddress)addresses.nextElement(); + if (!(addr instanceof Inet6Address)) { continue; } - Inet6Address ia6_addr = (Inet6Address)address; + Inet6Address ia6_addr = (Inet6Address)addr; /* check if site or link local prefixes match */ if (!differentLocalAddressTypes(ia6_addr)){ /* type not the same, so carry on searching */ @@ -420,21 +417,22 @@ class Inet6Address extends InetAddress { if (ifname != null && !"".equals (ifname)) { try { scope_ifname = NetworkInterface.getByName(ifname); - try { - scope_id = deriveNumericScope (scope_ifname); - } catch (UnknownHostException e) { - // should not happen - assert false; + if (scope_ifname == null) { + /* the interface does not exist on this system, so we clear + * the scope information completely */ + scope_id_set = false; + scope_ifname_set = false; + scope_id = 0; + } else { + try { + scope_id = deriveNumericScope (scope_ifname); + } catch (UnknownHostException e) { + // should not happen + assert false; + } } } catch (SocketException e) {} - if (scope_ifname == null) { - /* the interface does not exist on this system, so we clear - * the scope information completely */ - scope_id_set = false; - scope_ifname_set = false; - scope_id = 0; - } } /* if ifname was not supplied, then the numeric info is used */ @@ -460,6 +458,7 @@ class Inet6Address extends InetAddress { * an IP multicast address * @since JDK1.1 */ + @Override public boolean isMulticastAddress() { return ((ipaddress[0] & 0xff) == 0xff); } @@ -470,6 +469,7 @@ class Inet6Address extends InetAddress { * a wildcard address. * @since 1.4 */ + @Override public boolean isAnyLocalAddress() { byte test = 0x00; for (int i = 0; i < INADDRSZ; i++) { @@ -485,6 +485,7 @@ class Inet6Address extends InetAddress { * a loopback address; or false otherwise. * @since 1.4 */ + @Override public boolean isLoopbackAddress() { byte test = 0x00; for (int i = 0; i < 15; i++) { @@ -500,6 +501,7 @@ class Inet6Address extends InetAddress { * a link local address; or false if address is not a link local unicast address. * @since 1.4 */ + @Override public boolean isLinkLocalAddress() { return ((ipaddress[0] & 0xff) == 0xfe && (ipaddress[1] & 0xc0) == 0x80); @@ -512,6 +514,7 @@ class Inet6Address extends InetAddress { * a site local address; or false if address is not a site local unicast address. * @since 1.4 */ + @Override public boolean isSiteLocalAddress() { return ((ipaddress[0] & 0xff) == 0xfe && (ipaddress[1] & 0xc0) == 0xc0); @@ -525,6 +528,7 @@ class Inet6Address extends InetAddress { * of global scope or it is not a multicast address * @since 1.4 */ + @Override public boolean isMCGlobal() { return ((ipaddress[0] & 0xff) == 0xff && (ipaddress[1] & 0x0f) == 0x0e); @@ -538,6 +542,7 @@ class Inet6Address extends InetAddress { * of node-local scope or it is not a multicast address * @since 1.4 */ + @Override public boolean isMCNodeLocal() { return ((ipaddress[0] & 0xff) == 0xff && (ipaddress[1] & 0x0f) == 0x01); @@ -551,6 +556,7 @@ class Inet6Address extends InetAddress { * of link-local scope or it is not a multicast address * @since 1.4 */ + @Override public boolean isMCLinkLocal() { return ((ipaddress[0] & 0xff) == 0xff && (ipaddress[1] & 0x0f) == 0x02); @@ -564,6 +570,7 @@ class Inet6Address extends InetAddress { * of site-local scope or it is not a multicast address * @since 1.4 */ + @Override public boolean isMCSiteLocal() { return ((ipaddress[0] & 0xff) == 0xff && (ipaddress[1] & 0x0f) == 0x05); @@ -578,6 +585,7 @@ class Inet6Address extends InetAddress { * or it is not a multicast address * @since 1.4 */ + @Override public boolean isMCOrgLocal() { return ((ipaddress[0] & 0xff) == 0xff && (ipaddress[1] & 0x0f) == 0x08); @@ -590,6 +598,7 @@ class Inet6Address extends InetAddress { * * @return the raw IP address of this object. */ + @Override public byte[] getAddress() { return ipaddress.clone(); } @@ -624,6 +633,7 @@ class Inet6Address extends InetAddress { * * @return the raw IP address in a string format. */ + @Override public String getHostAddress() { String s = numericToTextFormat(ipaddress); if (scope_ifname_set) { /* must check this first */ @@ -639,6 +649,7 @@ class Inet6Address extends InetAddress { * * @return a hash code value for this IP address. */ + @Override public int hashCode() { if (ipaddress != null) { @@ -677,6 +688,7 @@ class Inet6Address extends InetAddress { * false otherwise. * @see java.net.InetAddress#getAddress() */ + @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof Inet6Address)) diff --git a/jdk/test/java/net/Inet6Address/serialize/Readme.txt b/jdk/test/java/net/Inet6Address/serialize/Readme.txt new file mode 100644 index 00000000000..4335bbd206f --- /dev/null +++ b/jdk/test/java/net/Inet6Address/serialize/Readme.txt @@ -0,0 +1,6 @@ +This test uses 2 binary data files that were each created by serializing an Inet6Address instance. +In both cases this has to do with the tricky issue of scopes in serialized addresses. + +serial1.4.2.ser: Was created by serializing an Inet6Address (::1) with J2SE 1.4.2 and is used to check for backward compatibility. + +serial-bge0.ser: Was created on a Sparc workstation because it has an uncommon interface name ('bge0') which is useful for the test. diff --git a/jdk/test/java/net/Inet6Address/serialize/Serialize.java b/jdk/test/java/net/Inet6Address/serialize/Serialize.java index d97149b79a1..17b5500576c 100644 --- a/jdk/test/java/net/Inet6Address/serialize/Serialize.java +++ b/jdk/test/java/net/Inet6Address/serialize/Serialize.java @@ -24,7 +24,9 @@ /** * @test * @bug 4921029 + * @bug 6656849 * @summary java.net.Inet6Address fails to be serialized with IPv6 support + * @summary NullPointerException thrown while de-serializing IPV6 Address. */ import java.net.*; @@ -76,11 +78,20 @@ public class Serialize { System.out.println(nobj); - // create an address with an unlikely numeric scope_id - if (!test ((Inet6Address)InetAddress.getByName ("fe80::1%99"))) { - throw new RuntimeException ("test failed on fe80::1%99"); - } + // create an address with an unlikely numeric scope_id + if (!test ((Inet6Address)InetAddress.getByName ("fe80::1%99"))) { + throw new RuntimeException ("test failed on fe80::1%99"); + } + // Deserialize an Inet6 address with a named interface + file = new File (System.getProperty("test.src"), "serial-bge0.ser"); + ois = new ObjectInputStream(new FileInputStream(file)); + try { + nobj = (Inet6Address) ois.readObject(); + } catch (NullPointerException e) { + throw new RuntimeException("6656849 Not fixed: NullPointer when deserializing"); + } + System.out.println(nobj); System.out.println("All tests passed"); } @@ -97,8 +108,5 @@ public class Serialize { } else { return false; } - - } - } diff --git a/jdk/test/java/net/Inet6Address/serialize/serial-bge0.ser b/jdk/test/java/net/Inet6Address/serialize/serial-bge0.ser new file mode 100644 index 0000000000000000000000000000000000000000..4382d7d1e8fb56bdcba2d44c6f5b67931cf16f9b GIT binary patch literal 266 zcmZ4UmVvdnh(R Date: Tue, 1 Jul 2008 09:23:00 -0700 Subject: [PATCH 008/105] 2157677: ClassPrepareRequest.addSourceNameFilter() does not behave as documented Add proper handling of JVMTI errors. Reviewed-by: tbell --- jdk/src/share/back/eventFilter.c | 15 ++++---- .../com/sun/jdi/SourceNameFilterTest.java | 35 +++++++++++++++---- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/jdk/src/share/back/eventFilter.c b/jdk/src/share/back/eventFilter.c index edddeba808c..955cf8813b4 100644 --- a/jdk/src/share/back/eventFilter.c +++ b/jdk/src/share/back/eventFilter.c @@ -492,14 +492,17 @@ eventFilterRestricted_passesFilter(JNIEnv *env, char *sourceName = 0; jvmtiError error = JVMTI_FUNC_PTR(gdata->jvmti,GetSourceFileName) (gdata->jvmti, clazz, &sourceName); - if (error == JVMTI_ERROR_NONE) { - if (sourceName == 0 || !patternStringMatch(sourceName, desiredNamePattern)) { - /* We have no match */ - jvmtiDeallocate(sourceName); - return JNI_FALSE; - } + if (error == JVMTI_ERROR_NONE && + sourceName != 0 && + patternStringMatch(sourceName, desiredNamePattern)) { + // got a hit - report the event + jvmtiDeallocate(sourceName); + break; } + // We have no match, we have no source file name, + // or we got a JVM TI error. Don't report the event. jvmtiDeallocate(sourceName); + return JNI_FALSE; } break; } diff --git a/jdk/test/com/sun/jdi/SourceNameFilterTest.java b/jdk/test/com/sun/jdi/SourceNameFilterTest.java index 1565a8886a8..cb8260646f0 100644 --- a/jdk/test/com/sun/jdi/SourceNameFilterTest.java +++ b/jdk/test/com/sun/jdi/SourceNameFilterTest.java @@ -23,7 +23,7 @@ /** * @test - * @bug 4836939 + * @bug 4836939 6646613 * @summary JDI add addSourceNameFilter to ClassPrepareRequest * * @author jjh @@ -31,7 +31,11 @@ * @run build TestScaffold VMConnection TargetListener TargetAdapter * @run compile -g SourceNameFilterTest.java * @run main SourceNameFilterTest + * @run compile -g:none SourceNameFilterTest.java + * @run main SourceNameFilterTest */ +// The compile -g:none suppresses the lineNumber table to trigger bug 6646613. + import com.sun.jdi.*; import com.sun.jdi.event.*; import com.sun.jdi.request.*; @@ -84,7 +88,6 @@ public class SourceNameFilterTest extends TestScaffold { boolean gotEvent1 = false; boolean gotEvent2 = false; boolean gotEvent3 = false; - ClassPrepareRequest cpReq; boolean shouldResume = false; SourceNameFilterTest (String args[]) { @@ -151,6 +154,18 @@ public class SourceNameFilterTest extends TestScaffold { */ BreakpointEvent bpe = startToMain("SourceNameFilterTarg"); targetClass = bpe.location().declaringType(); + boolean noSourceName = false; + try { + targetClass.sourceName(); + } catch (AbsentInformationException ee) { + noSourceName = true; + } + if (noSourceName) { + println("-- Running with no source names"); + } else { + println("-- Running with source names"); + } + mainThread = bpe.thread(); EventRequestManager erm = vm().eventRequestManager(); addListener(this); @@ -175,7 +190,9 @@ public class SourceNameFilterTest extends TestScaffold { /* * This should cause us to get a class prepare event for - * LoadedLater3 + * LoadedLater3 except in the case where -g:none + * was used to compile so that there is no LineNumberTable + * and therefore, no source name for the class. */ cpReq = erm.createClassPrepareRequest(); cpReq.addSourceNameFilter("SourceNameFilterTest.java"); @@ -186,17 +203,21 @@ public class SourceNameFilterTest extends TestScaffold { if (!gotEvent1) { failure("failure: Did not get a class prepare request " + - "for Loadedlater1"); + "for LoadedLater1"); } if (gotEvent2) { failure("failure: Did get a class prepare request " + - "for Loadedlater2"); + "for LoadedLater2"); } - if (!gotEvent3) { + if (gotEvent3 && noSourceName) { + failure("failure: Did get a class prepare request " + + "for LoadedLater3"); + } + else if (!gotEvent3 && !noSourceName) { failure("failure: Did not get a class prepare request " + - "for Loadedlater3"); + "for LoadedLater3"); } /* From 696e17c0ba6b44d02c806a89ff537f09f4b7f925 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Thu, 3 Jul 2008 18:02:47 -0700 Subject: [PATCH 009/105] 6684714: Optimize EA Connection Graph build performance Switch on EA by default, optimize Connection Graph construction Reviewed-by: rasbold, never --- hotspot/src/share/vm/compiler/oopMap.cpp | 21 +- hotspot/src/share/vm/compiler/oopMap.hpp | 8 +- hotspot/src/share/vm/opto/bytecodeInfo.cpp | 2 +- hotspot/src/share/vm/opto/c2_globals.hpp | 2 +- hotspot/src/share/vm/opto/compile.cpp | 16 +- hotspot/src/share/vm/opto/escape.cpp | 474 +++++++++++---------- hotspot/src/share/vm/opto/escape.hpp | 52 ++- 7 files changed, 290 insertions(+), 285 deletions(-) diff --git a/hotspot/src/share/vm/compiler/oopMap.cpp b/hotspot/src/share/vm/compiler/oopMap.cpp index 2984d647ea8..78bef1836c8 100644 --- a/hotspot/src/share/vm/compiler/oopMap.cpp +++ b/hotspot/src/share/vm/compiler/oopMap.cpp @@ -188,10 +188,6 @@ void OopMap::set_derived_oop(VMReg reg, VMReg derived_from_local_register ) { } } -void OopMap::set_stack_obj(VMReg reg) { - set_xxx(reg, OopMapValue::stack_obj, VMRegImpl::Bad()); -} - // OopMapSet OopMapSet::OopMapSet() { @@ -399,8 +395,7 @@ void OopMapSet::all_do(const frame *fr, const RegisterMap *reg_map, if ( loc != NULL ) { if ( omv.type() == OopMapValue::oop_value ) { #ifdef ASSERT - if (COMPILER2_PRESENT(!DoEscapeAnalysis &&) - (((uintptr_t)loc & (sizeof(*loc)-1)) != 0) || + if ((((uintptr_t)loc & (sizeof(*loc)-1)) != 0) || !Universe::heap()->is_in_or_null(*loc)) { tty->print_cr("# Found non oop pointer. Dumping state at failure"); // try to dump out some helpful debugging information @@ -431,17 +426,6 @@ void OopMapSet::all_do(const frame *fr, const RegisterMap *reg_map, } } } - -#ifdef COMPILER2 - if (DoEscapeAnalysis) { - for (OopMapStream oms(map, OopMapValue::stack_obj); !oms.is_done(); oms.next()) { - omv = oms.current(); - assert(omv.is_stack_loc(), "should refer to stack location"); - oop loc = (oop) fr->oopmapreg_to_location(omv.reg(),reg_map); - oop_fn->do_oop(&loc); - } - } -#endif // COMPILER2 } @@ -540,9 +524,6 @@ void print_register_type(OopMapValue::oop_types x, VMReg optional, st->print("Derived_oop_" ); optional->print_on(st); break; - case OopMapValue::stack_obj: - st->print("Stack"); - break; default: ShouldNotReachHere(); } diff --git a/hotspot/src/share/vm/compiler/oopMap.hpp b/hotspot/src/share/vm/compiler/oopMap.hpp index ac05d570c04..bce97de176c 100644 --- a/hotspot/src/share/vm/compiler/oopMap.hpp +++ b/hotspot/src/share/vm/compiler/oopMap.hpp @@ -46,7 +46,7 @@ private: public: // Constants - enum { type_bits = 6, + enum { type_bits = 5, register_bits = BitsPerShort - type_bits }; enum { type_shift = 0, @@ -63,8 +63,7 @@ public: value_value = 2, narrowoop_value = 4, callee_saved_value = 8, - derived_oop_value= 16, - stack_obj = 32 }; + derived_oop_value= 16 }; // Constructors OopMapValue () { set_value(0); set_content_reg(VMRegImpl::Bad()); } @@ -93,14 +92,12 @@ public: bool is_narrowoop() { return mask_bits(value(), type_mask_in_place) == narrowoop_value; } bool is_callee_saved() { return mask_bits(value(), type_mask_in_place) == callee_saved_value; } bool is_derived_oop() { return mask_bits(value(), type_mask_in_place) == derived_oop_value; } - bool is_stack_obj() { return mask_bits(value(), type_mask_in_place) == stack_obj; } void set_oop() { set_value((value() & register_mask_in_place) | oop_value); } void set_value() { set_value((value() & register_mask_in_place) | value_value); } void set_narrowoop() { set_value((value() & register_mask_in_place) | narrowoop_value); } void set_callee_saved() { set_value((value() & register_mask_in_place) | callee_saved_value); } void set_derived_oop() { set_value((value() & register_mask_in_place) | derived_oop_value); } - void set_stack_obj() { set_value((value() & register_mask_in_place) | stack_obj); } VMReg reg() const { return VMRegImpl::as_VMReg(mask_bits(value(), register_mask_in_place) >> register_shift); } oop_types type() const { return (oop_types)mask_bits(value(), type_mask_in_place); } @@ -180,7 +177,6 @@ class OopMap: public ResourceObj { void set_dead ( VMReg local); void set_callee_saved( VMReg local, VMReg caller_machine_register ); void set_derived_oop ( VMReg local, VMReg derived_from_local_register ); - void set_stack_obj( VMReg local); void set_xxx(VMReg reg, OopMapValue::oop_types x, VMReg optional); int heap_size() const; diff --git a/hotspot/src/share/vm/opto/bytecodeInfo.cpp b/hotspot/src/share/vm/opto/bytecodeInfo.cpp index 1b12ee877ad..b1fe31aed2e 100644 --- a/hotspot/src/share/vm/opto/bytecodeInfo.cpp +++ b/hotspot/src/share/vm/opto/bytecodeInfo.cpp @@ -83,7 +83,7 @@ static bool is_init_with_ea(ciMethod* callee_method, ciMethod* caller_method, Compile* C) { // True when EA is ON and a java constructor is called or // a super constructor is called from an inlined java constructor. - return DoEscapeAnalysis && EliminateAllocations && + return C->do_escape_analysis() && EliminateAllocations && ( callee_method->is_initializer() || (caller_method->is_initializer() && caller_method != C->method() && diff --git a/hotspot/src/share/vm/opto/c2_globals.hpp b/hotspot/src/share/vm/opto/c2_globals.hpp index 691914e2b0a..7cdf24bdc23 100644 --- a/hotspot/src/share/vm/opto/c2_globals.hpp +++ b/hotspot/src/share/vm/opto/c2_globals.hpp @@ -373,7 +373,7 @@ product(intx, AutoBoxCacheMax, 128, \ "Sets max value cached by the java.lang.Integer autobox cache") \ \ - product(bool, DoEscapeAnalysis, false, \ + product(bool, DoEscapeAnalysis, true, \ "Perform escape analysis") \ \ notproduct(bool, PrintEscapeAnalysis, false, \ diff --git a/hotspot/src/share/vm/opto/compile.cpp b/hotspot/src/share/vm/opto/compile.cpp index 019ca50ae59..f193d5d2d11 100644 --- a/hotspot/src/share/vm/opto/compile.cpp +++ b/hotspot/src/share/vm/opto/compile.cpp @@ -583,18 +583,22 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr NOT_PRODUCT( verify_graph_edges(); ) // Perform escape analysis - if (_do_escape_analysis) - _congraph = new ConnectionGraph(this); - if (_congraph != NULL) { - NOT_PRODUCT( TracePhase t2("escapeAnalysis", &_t_escapeAnalysis, TimeCompiler); ) - _congraph->compute_escape(); - if (failing()) return; + if (_do_escape_analysis && ConnectionGraph::has_candidates(this)) { + TracePhase t2("escapeAnalysis", &_t_escapeAnalysis, true); + + _congraph = new(comp_arena()) ConnectionGraph(this); + bool has_non_escaping_obj = _congraph->compute_escape(); #ifndef PRODUCT if (PrintEscapeAnalysis) { _congraph->dump(); } #endif + if (!has_non_escaping_obj) { + _congraph = NULL; + } + + if (failing()) return; } // Now optimize Optimize(); diff --git a/hotspot/src/share/vm/opto/escape.cpp b/hotspot/src/share/vm/opto/escape.cpp index ad6c8826270..7fee3d3d44f 100644 --- a/hotspot/src/share/vm/opto/escape.cpp +++ b/hotspot/src/share/vm/opto/escape.cpp @@ -25,16 +25,6 @@ #include "incls/_precompiled.incl" #include "incls/_escape.cpp.incl" -uint PointsToNode::edge_target(uint e) const { - assert(_edges != NULL && e < (uint)_edges->length(), "valid edge index"); - return (_edges->at(e) >> EdgeShift); -} - -PointsToNode::EdgeType PointsToNode::edge_type(uint e) const { - assert(_edges != NULL && e < (uint)_edges->length(), "valid edge index"); - return (EdgeType) (_edges->at(e) & EdgeMask); -} - void PointsToNode::add_edge(uint targIdx, PointsToNode::EdgeType et) { uint v = (targIdx << EdgeShift) + ((uint) et); if (_edges == NULL) { @@ -87,12 +77,13 @@ void PointsToNode::dump() const { } #endif -ConnectionGraph::ConnectionGraph(Compile * C) : _processed(C->comp_arena()), _node_map(C->comp_arena()) { - _collecting = true; - this->_compile = C; - const PointsToNode &dummy = PointsToNode(); - int sz = C->unique(); - _nodes = new(C->comp_arena()) GrowableArray(C->comp_arena(), sz, sz, dummy); +ConnectionGraph::ConnectionGraph(Compile * C) : + _nodes(C->comp_arena(), C->unique(), C->unique(), PointsToNode()), + _processed(C->comp_arena()), + _collecting(true), + _compile(C), + _node_map(C->comp_arena()) { + _phantom_object = C->top()->_idx; PointsToNode *phn = ptnode_adr(_phantom_object); phn->_node = C->top(); @@ -182,32 +173,36 @@ PointsToNode::EscapeState ConnectionGraph::escape_state(Node *n, PhaseTransform // If we are still collecting or there were no non-escaping allocations // we don't know the answer yet - if (_collecting || !_has_allocations) + if (_collecting) return PointsToNode::UnknownEscape; // if the node was created after the escape computation, return // UnknownEscape - if (idx >= (uint)_nodes->length()) + if (idx >= nodes_size()) return PointsToNode::UnknownEscape; - es = _nodes->at_grow(idx).escape_state(); + es = ptnode_adr(idx)->escape_state(); // if we have already computed a value, return it if (es != PointsToNode::UnknownEscape) return es; + // PointsTo() calls n->uncast() which can return a new ideal node. + if (n->uncast()->_idx >= nodes_size()) + return PointsToNode::UnknownEscape; + // compute max escape state of anything this node could point to VectorSet ptset(Thread::current()->resource_area()); PointsTo(ptset, n, phase); for(VectorSetI i(&ptset); i.test() && es != PointsToNode::GlobalEscape; ++i) { uint pt = i.elem; - PointsToNode::EscapeState pes = _nodes->adr_at(pt)->escape_state(); + PointsToNode::EscapeState pes = ptnode_adr(pt)->escape_state(); if (pes > es) es = pes; } // cache the computed escape state assert(es != PointsToNode::UnknownEscape, "should have computed an escape state"); - _nodes->adr_at(idx)->set_escape_state(es); + ptnode_adr(idx)->set_escape_state(es); return es; } @@ -220,49 +215,51 @@ void ConnectionGraph::PointsTo(VectorSet &ptset, Node * n, PhaseTransform *phase #endif n = n->uncast(); - PointsToNode npt = _nodes->at_grow(n->_idx); + PointsToNode* npt = ptnode_adr(n->_idx); // If we have a JavaObject, return just that object - if (npt.node_type() == PointsToNode::JavaObject) { + if (npt->node_type() == PointsToNode::JavaObject) { ptset.set(n->_idx); return; } #ifdef ASSERT - if (npt._node == NULL) { + if (npt->_node == NULL) { if (orig_n != n) orig_n->dump(); n->dump(); - assert(npt._node != NULL, "unregistered node"); + assert(npt->_node != NULL, "unregistered node"); } #endif worklist.push(n->_idx); while(worklist.length() > 0) { int ni = worklist.pop(); - PointsToNode pn = _nodes->at_grow(ni); - if (!visited.test_set(ni)) { - // ensure that all inputs of a Phi have been processed - assert(!_collecting || !pn._node->is_Phi() || _processed.test(ni),""); + if (visited.test_set(ni)) + continue; - int edges_processed = 0; - for (uint e = 0; e < pn.edge_count(); e++) { - uint etgt = pn.edge_target(e); - PointsToNode::EdgeType et = pn.edge_type(e); - if (et == PointsToNode::PointsToEdge) { - ptset.set(etgt); - edges_processed++; - } else if (et == PointsToNode::DeferredEdge) { - worklist.push(etgt); - edges_processed++; - } else { - assert(false,"neither PointsToEdge or DeferredEdge"); - } - } - if (edges_processed == 0) { - // no deferred or pointsto edges found. Assume the value was set - // outside this method. Add the phantom object to the pointsto set. - ptset.set(_phantom_object); + PointsToNode* pn = ptnode_adr(ni); + // ensure that all inputs of a Phi have been processed + assert(!_collecting || !pn->_node->is_Phi() || _processed.test(ni),""); + + int edges_processed = 0; + uint e_cnt = pn->edge_count(); + for (uint e = 0; e < e_cnt; e++) { + uint etgt = pn->edge_target(e); + PointsToNode::EdgeType et = pn->edge_type(e); + if (et == PointsToNode::PointsToEdge) { + ptset.set(etgt); + edges_processed++; + } else if (et == PointsToNode::DeferredEdge) { + worklist.push(etgt); + edges_processed++; + } else { + assert(false,"neither PointsToEdge or DeferredEdge"); } } + if (edges_processed == 0) { + // no deferred or pointsto edges found. Assume the value was set + // outside this method. Add the phantom object to the pointsto set. + ptset.set(_phantom_object); + } } } @@ -272,11 +269,11 @@ void ConnectionGraph::remove_deferred(uint ni, GrowableArray* deferred_edg deferred_edges->clear(); visited->Clear(); - uint i = 0; + visited->set(ni); PointsToNode *ptn = ptnode_adr(ni); // Mark current edges as visited and move deferred edges to separate array. - while (i < ptn->edge_count()) { + for (uint i = 0; i < ptn->edge_count(); ) { uint t = ptn->edge_target(i); #ifdef ASSERT assert(!visited->test_set(t), "expecting no duplications"); @@ -293,24 +290,23 @@ void ConnectionGraph::remove_deferred(uint ni, GrowableArray* deferred_edg for (int next = 0; next < deferred_edges->length(); ++next) { uint t = deferred_edges->at(next); PointsToNode *ptt = ptnode_adr(t); - for (uint j = 0; j < ptt->edge_count(); j++) { - uint n1 = ptt->edge_target(j); - if (visited->test_set(n1)) + uint e_cnt = ptt->edge_count(); + for (uint e = 0; e < e_cnt; e++) { + uint etgt = ptt->edge_target(e); + if (visited->test_set(etgt)) continue; - switch(ptt->edge_type(j)) { - case PointsToNode::PointsToEdge: - add_pointsto_edge(ni, n1); - if(n1 == _phantom_object) { - // Special case - field set outside (globally escaping). - ptn->set_escape_state(PointsToNode::GlobalEscape); - } - break; - case PointsToNode::DeferredEdge: - deferred_edges->append(n1); - break; - case PointsToNode::FieldEdge: - assert(false, "invalid connection graph"); - break; + + PointsToNode::EdgeType et = ptt->edge_type(e); + if (et == PointsToNode::PointsToEdge) { + add_pointsto_edge(ni, etgt); + if(etgt == _phantom_object) { + // Special case - field set outside (globally escaping). + ptn->set_escape_state(PointsToNode::GlobalEscape); + } + } else if (et == PointsToNode::DeferredEdge) { + deferred_edges->append(etgt); + } else { + assert(false,"invalid connection graph"); } } } @@ -322,15 +318,15 @@ void ConnectionGraph::remove_deferred(uint ni, GrowableArray* deferred_edg // a pointsto edge is added if it is a JavaObject void ConnectionGraph::add_edge_from_fields(uint adr_i, uint to_i, int offs) { - PointsToNode an = _nodes->at_grow(adr_i); - PointsToNode to = _nodes->at_grow(to_i); - bool deferred = (to.node_type() == PointsToNode::LocalVar); + PointsToNode* an = ptnode_adr(adr_i); + PointsToNode* to = ptnode_adr(to_i); + bool deferred = (to->node_type() == PointsToNode::LocalVar); - for (uint fe = 0; fe < an.edge_count(); fe++) { - assert(an.edge_type(fe) == PointsToNode::FieldEdge, "expecting a field edge"); - int fi = an.edge_target(fe); - PointsToNode pf = _nodes->at_grow(fi); - int po = pf.offset(); + for (uint fe = 0; fe < an->edge_count(); fe++) { + assert(an->edge_type(fe) == PointsToNode::FieldEdge, "expecting a field edge"); + int fi = an->edge_target(fe); + PointsToNode* pf = ptnode_adr(fi); + int po = pf->offset(); if (po == offs || po == Type::OffsetBot || offs == Type::OffsetBot) { if (deferred) add_deferred_edge(fi, to_i); @@ -343,13 +339,13 @@ void ConnectionGraph::add_edge_from_fields(uint adr_i, uint to_i, int offs) { // Add a deferred edge from node given by "from_i" to any field of adr_i // whose offset matches "offset". void ConnectionGraph::add_deferred_edge_to_fields(uint from_i, uint adr_i, int offs) { - PointsToNode an = _nodes->at_grow(adr_i); - for (uint fe = 0; fe < an.edge_count(); fe++) { - assert(an.edge_type(fe) == PointsToNode::FieldEdge, "expecting a field edge"); - int fi = an.edge_target(fe); - PointsToNode pf = _nodes->at_grow(fi); - int po = pf.offset(); - if (pf.edge_count() == 0) { + PointsToNode* an = ptnode_adr(adr_i); + for (uint fe = 0; fe < an->edge_count(); fe++) { + assert(an->edge_type(fe) == PointsToNode::FieldEdge, "expecting a field edge"); + int fi = an->edge_target(fe); + PointsToNode* pf = ptnode_adr(fi); + int po = pf->offset(); + if (pf->edge_count() == 0) { // we have not seen any stores to this field, assume it was set outside this method add_pointsto_edge(fi, _phantom_object); } @@ -835,6 +831,11 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) // Phase 1: Process possible allocations from alloc_worklist. // Create instance types for the CheckCastPP for allocations where possible. + // + // (Note: don't forget to change the order of the second AddP node on + // the alloc_worklist if the order of the worklist processing is changed, + // see the comment in find_second_addp().) + // while (alloc_worklist.length() != 0) { Node *n = alloc_worklist.pop(); uint ni = n->_idx; @@ -842,7 +843,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) if (n->is_Call()) { CallNode *alloc = n->as_Call(); // copy escape information to call node - PointsToNode* ptn = _nodes->adr_at(alloc->_idx); + PointsToNode* ptn = ptnode_adr(alloc->_idx); PointsToNode::EscapeState es = escape_state(alloc, igvn); // We have an allocation or call which returns a Java object, // see if it is unescaped. @@ -899,7 +900,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) // First, put on the worklist all Field edges from Connection Graph // which is more accurate then putting immediate users from Ideal Graph. for (uint e = 0; e < ptn->edge_count(); e++) { - Node *use = _nodes->adr_at(ptn->edge_target(e))->_node; + Node *use = ptnode_adr(ptn->edge_target(e))->_node; assert(ptn->edge_type(e) == PointsToNode::FieldEdge && use->is_AddP(), "only AddP nodes are Field edges in CG"); if (use->outcnt() > 0) { // Don't process dead nodes @@ -1062,7 +1063,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) } if (mem != n->in(MemNode::Memory)) { set_map(n->_idx, mem); - _nodes->adr_at(n->_idx)->_node = n; + ptnode_adr(n->_idx)->_node = n; } if (n->is_Load()) { continue; // don't push users @@ -1223,10 +1224,10 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) // Update the memory inputs of MemNodes with the value we computed // in Phase 2. - for (int i = 0; i < _nodes->length(); i++) { + for (uint i = 0; i < nodes_size(); i++) { Node *nmem = get_map(i); if (nmem != NULL) { - Node *n = _nodes->adr_at(i)->_node; + Node *n = ptnode_adr(i)->_node; if (n != NULL && n->is_Mem()) { igvn->hash_delete(n); n->set_req(MemNode::Memory, nmem); @@ -1237,28 +1238,48 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) } } -void ConnectionGraph::compute_escape() { +bool ConnectionGraph::has_candidates(Compile *C) { + // EA brings benefits only when the code has allocations and/or locks which + // are represented by ideal Macro nodes. + int cnt = C->macro_count(); + for( int i=0; i < cnt; i++ ) { + Node *n = C->macro_node(i); + if ( n->is_Allocate() ) + return true; + if( n->is_Lock() ) { + Node* obj = n->as_Lock()->obj_node()->uncast(); + if( !(obj->is_Parm() || obj->is_Con()) ) + return true; + } + } + return false; +} + +bool ConnectionGraph::compute_escape() { + Compile* C = _compile; // 1. Populate Connection Graph (CG) with Ideal nodes. Unique_Node_List worklist_init; - worklist_init.map(_compile->unique(), NULL); // preallocate space + worklist_init.map(C->unique(), NULL); // preallocate space // Initialize worklist - if (_compile->root() != NULL) { - worklist_init.push(_compile->root()); + if (C->root() != NULL) { + worklist_init.push(C->root()); } GrowableArray cg_worklist; - PhaseGVN* igvn = _compile->initial_gvn(); + PhaseGVN* igvn = C->initial_gvn(); bool has_allocations = false; // Push all useful nodes onto CG list and set their type. for( uint next = 0; next < worklist_init.size(); ++next ) { Node* n = worklist_init.at(next); record_for_escape_analysis(n, igvn); - if (n->is_Call() && - _nodes->adr_at(n->_idx)->node_type() == PointsToNode::JavaObject) { + // Only allocations and java static calls results are checked + // for an escape status. See process_call_result() below. + if (n->is_Allocate() || n->is_CallStaticJava() && + ptnode_adr(n->_idx)->node_type() == PointsToNode::JavaObject) { has_allocations = true; } if(n->is_AddP()) @@ -1269,24 +1290,23 @@ void ConnectionGraph::compute_escape() { } } - if (has_allocations) { - _has_allocations = true; - } else { - _has_allocations = false; + if (!has_allocations) { _collecting = false; - return; // Nothing to do. + return false; // Nothing to do. } // 2. First pass to create simple CG edges (doesn't require to walk CG). - for( uint next = 0; next < _delayed_worklist.size(); ++next ) { + uint delayed_size = _delayed_worklist.size(); + for( uint next = 0; next < delayed_size; ++next ) { Node* n = _delayed_worklist.at(next); build_connection_graph(n, igvn); } // 3. Pass to create fields edges (Allocate -F-> AddP). - for( int next = 0; next < cg_worklist.length(); ++next ) { + uint cg_length = cg_worklist.length(); + for( uint next = 0; next < cg_length; ++next ) { int ni = cg_worklist.at(next); - build_connection_graph(_nodes->adr_at(ni)->_node, igvn); + build_connection_graph(ptnode_adr(ni)->_node, igvn); } cg_worklist.clear(); @@ -1294,8 +1314,8 @@ void ConnectionGraph::compute_escape() { // 4. Build Connection Graph which need // to walk the connection graph. - for (uint ni = 0; ni < (uint)_nodes->length(); ni++) { - PointsToNode* ptn = _nodes->adr_at(ni); + for (uint ni = 0; ni < nodes_size(); ni++) { + PointsToNode* ptn = ptnode_adr(ni); Node *n = ptn->_node; if (n != NULL) { // Call, AddP, LoadP, StoreP build_connection_graph(n, igvn); @@ -1305,20 +1325,19 @@ void ConnectionGraph::compute_escape() { } VectorSet ptset(Thread::current()->resource_area()); - GrowableArray alloc_worklist; - GrowableArray worklist; GrowableArray deferred_edges; VectorSet visited(Thread::current()->resource_area()); - // remove deferred edges from the graph and collect - // information we will need for type splitting - for( int next = 0; next < cg_worklist.length(); ++next ) { + // 5. Remove deferred edges from the graph and collect + // information needed for type splitting. + cg_length = cg_worklist.length(); + for( uint next = 0; next < cg_length; ++next ) { int ni = cg_worklist.at(next); - PointsToNode* ptn = _nodes->adr_at(ni); + PointsToNode* ptn = ptnode_adr(ni); PointsToNode::NodeType nt = ptn->node_type(); - Node *n = ptn->_node; if (nt == PointsToNode::LocalVar || nt == PointsToNode::Field) { remove_deferred(ni, &deferred_edges, &visited); + Node *n = ptn->_node; if (n->is_AddP()) { // If this AddP computes an address which may point to more that one // object or more then one field (array's element), nothing the address @@ -1329,116 +1348,123 @@ void ConnectionGraph::compute_escape() { if (ptset.Size() > 1 || (ptset.Size() != 0 && ptn->offset() == Type::OffsetBot)) { for( VectorSetI j(&ptset); j.test(); ++j ) { - uint pt = j.elem; - ptnode_adr(pt)->_scalar_replaceable = false; + ptnode_adr(j.elem)->_scalar_replaceable = false; } } } - } else if (nt == PointsToNode::JavaObject && n->is_Call()) { - // Push call on alloc_worlist (alocations are calls) - // for processing by split_unique_types(). - alloc_worklist.append(n); } } + // 6. Propagate escape states. + GrowableArray worklist; + bool has_non_escaping_obj = false; + // push all GlobalEscape nodes on the worklist - for( int next = 0; next < cg_worklist.length(); ++next ) { + for( uint next = 0; next < cg_length; ++next ) { int nk = cg_worklist.at(next); - if (_nodes->adr_at(nk)->escape_state() == PointsToNode::GlobalEscape) - worklist.append(nk); + if (ptnode_adr(nk)->escape_state() == PointsToNode::GlobalEscape) + worklist.push(nk); } - // mark all node reachable from GlobalEscape nodes + // mark all nodes reachable from GlobalEscape nodes while(worklist.length() > 0) { - PointsToNode n = _nodes->at(worklist.pop()); - for (uint ei = 0; ei < n.edge_count(); ei++) { - uint npi = n.edge_target(ei); + PointsToNode* ptn = ptnode_adr(worklist.pop()); + uint e_cnt = ptn->edge_count(); + for (uint ei = 0; ei < e_cnt; ei++) { + uint npi = ptn->edge_target(ei); PointsToNode *np = ptnode_adr(npi); if (np->escape_state() < PointsToNode::GlobalEscape) { np->set_escape_state(PointsToNode::GlobalEscape); - worklist.append_if_missing(npi); + worklist.push(npi); } } } // push all ArgEscape nodes on the worklist - for( int next = 0; next < cg_worklist.length(); ++next ) { + for( uint next = 0; next < cg_length; ++next ) { int nk = cg_worklist.at(next); - if (_nodes->adr_at(nk)->escape_state() == PointsToNode::ArgEscape) + if (ptnode_adr(nk)->escape_state() == PointsToNode::ArgEscape) worklist.push(nk); } - // mark all node reachable from ArgEscape nodes + // mark all nodes reachable from ArgEscape nodes while(worklist.length() > 0) { - PointsToNode n = _nodes->at(worklist.pop()); - for (uint ei = 0; ei < n.edge_count(); ei++) { - uint npi = n.edge_target(ei); + PointsToNode* ptn = ptnode_adr(worklist.pop()); + if (ptn->node_type() == PointsToNode::JavaObject) + has_non_escaping_obj = true; // Non GlobalEscape + uint e_cnt = ptn->edge_count(); + for (uint ei = 0; ei < e_cnt; ei++) { + uint npi = ptn->edge_target(ei); PointsToNode *np = ptnode_adr(npi); if (np->escape_state() < PointsToNode::ArgEscape) { np->set_escape_state(PointsToNode::ArgEscape); - worklist.append_if_missing(npi); + worklist.push(npi); } } } + GrowableArray alloc_worklist; + // push all NoEscape nodes on the worklist - for( int next = 0; next < cg_worklist.length(); ++next ) { + for( uint next = 0; next < cg_length; ++next ) { int nk = cg_worklist.at(next); - if (_nodes->adr_at(nk)->escape_state() == PointsToNode::NoEscape) + if (ptnode_adr(nk)->escape_state() == PointsToNode::NoEscape) worklist.push(nk); } - // mark all node reachable from NoEscape nodes + // mark all nodes reachable from NoEscape nodes while(worklist.length() > 0) { - PointsToNode n = _nodes->at(worklist.pop()); - for (uint ei = 0; ei < n.edge_count(); ei++) { - uint npi = n.edge_target(ei); + PointsToNode* ptn = ptnode_adr(worklist.pop()); + if (ptn->node_type() == PointsToNode::JavaObject) + has_non_escaping_obj = true; // Non GlobalEscape + Node* n = ptn->_node; + if (n->is_Allocate() && ptn->_scalar_replaceable ) { + // Push scalar replaceable alocations on alloc_worklist + // for processing in split_unique_types(). + alloc_worklist.append(n); + } + uint e_cnt = ptn->edge_count(); + for (uint ei = 0; ei < e_cnt; ei++) { + uint npi = ptn->edge_target(ei); PointsToNode *np = ptnode_adr(npi); if (np->escape_state() < PointsToNode::NoEscape) { np->set_escape_state(PointsToNode::NoEscape); - worklist.append_if_missing(npi); + worklist.push(npi); } } } _collecting = false; + assert(C->unique() == nodes_size(), "there should be no new ideal nodes during ConnectionGraph build"); - has_allocations = false; // Are there scalar replaceable allocations? + bool has_scalar_replaceable_candidates = alloc_worklist.length() > 0; + if ( has_scalar_replaceable_candidates && + C->AliasLevel() >= 3 && EliminateAllocations ) { - for( int next = 0; next < alloc_worklist.length(); ++next ) { - Node* n = alloc_worklist.at(next); - uint ni = n->_idx; - PointsToNode* ptn = _nodes->adr_at(ni); - PointsToNode::EscapeState es = ptn->escape_state(); - if (ptn->escape_state() == PointsToNode::NoEscape && - ptn->_scalar_replaceable) { - has_allocations = true; - break; - } - } - if (!has_allocations) { - return; // Nothing to do. - } - - if(_compile->AliasLevel() >= 3 && EliminateAllocations) { // Now use the escape information to create unique types for - // unescaped objects + // scalar replaceable objects. split_unique_types(alloc_worklist); - if (_compile->failing()) return; + + if (C->failing()) return false; // Clean up after split unique types. ResourceMark rm; - PhaseRemoveUseless pru(_compile->initial_gvn(), _compile->for_igvn()); + PhaseRemoveUseless pru(C->initial_gvn(), C->for_igvn()); + + C->print_method("After Escape Analysis", 2); #ifdef ASSERT - } else if (PrintEscapeAnalysis || PrintEliminateAllocations) { + } else if (Verbose && (PrintEscapeAnalysis || PrintEliminateAllocations)) { tty->print("=== No allocations eliminated for "); - C()->method()->print_short_name(); + C->method()->print_short_name(); if(!EliminateAllocations) { tty->print(" since EliminateAllocations is off ==="); - } else if(_compile->AliasLevel() < 3) { + } else if(!has_scalar_replaceable_candidates) { + tty->print(" since there are no scalar replaceable candidates ==="); + } else if(C->AliasLevel() < 3) { tty->print(" since AliasLevel < 3 ==="); } tty->cr(); #endif } + return has_non_escaping_obj; } void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *phase) { @@ -1538,7 +1564,7 @@ void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *pha } } if (copy_dependencies) - call_analyzer->copy_dependencies(C()->dependencies()); + call_analyzer->copy_dependencies(_compile->dependencies()); break; } } @@ -1561,7 +1587,6 @@ void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *pha for( VectorSetI j(&ptset); j.test(); ++j ) { uint pt = j.elem; set_escape_state(pt, PointsToNode::GlobalEscape); - PointsToNode *ptadr = ptnode_adr(pt); } } } @@ -1569,9 +1594,10 @@ void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *pha } } void ConnectionGraph::process_call_result(ProjNode *resproj, PhaseTransform *phase) { - PointsToNode *ptadr = ptnode_adr(resproj->_idx); + CallNode *call = resproj->in(0)->as_Call(); + uint call_idx = call->_idx; + uint resproj_idx = resproj->_idx; - CallNode *call = resproj->in(0)->as_Call(); switch (call->Opcode()) { case Op_Allocate: { @@ -1587,7 +1613,6 @@ void ConnectionGraph::process_call_result(ProjNode *resproj, PhaseTransform *pha ciKlass* cik = kt->klass(); ciInstanceKlass* ciik = cik->as_instance_klass(); - PointsToNode *ptadr = ptnode_adr(call->_idx); PointsToNode::EscapeState es; uint edge_to; if (cik->is_subclass_of(_compile->env()->Thread_klass()) || ciik->has_finalizer()) { @@ -1595,25 +1620,24 @@ void ConnectionGraph::process_call_result(ProjNode *resproj, PhaseTransform *pha edge_to = _phantom_object; // Could not be worse } else { es = PointsToNode::NoEscape; - edge_to = call->_idx; + edge_to = call_idx; } - set_escape_state(call->_idx, es); - add_pointsto_edge(resproj->_idx, edge_to); - _processed.set(resproj->_idx); + set_escape_state(call_idx, es); + add_pointsto_edge(resproj_idx, edge_to); + _processed.set(resproj_idx); break; } case Op_AllocateArray: { - PointsToNode *ptadr = ptnode_adr(call->_idx); int length = call->in(AllocateNode::ALength)->find_int_con(-1); if (length < 0 || length > EliminateAllocationArraySizeLimit) { // Not scalar replaceable if the length is not constant or too big. - ptadr->_scalar_replaceable = false; + ptnode_adr(call_idx)->_scalar_replaceable = false; } - set_escape_state(call->_idx, PointsToNode::NoEscape); - add_pointsto_edge(resproj->_idx, call->_idx); - _processed.set(resproj->_idx); + set_escape_state(call_idx, PointsToNode::NoEscape); + add_pointsto_edge(resproj_idx, call_idx); + _processed.set(resproj_idx); break; } @@ -1631,19 +1655,17 @@ void ConnectionGraph::process_call_result(ProjNode *resproj, PhaseTransform *pha // Note: we use isa_ptr() instead of isa_oopptr() here because the // _multianewarray functions return a TypeRawPtr. if (ret_type == NULL || ret_type->isa_ptr() == NULL) { - _processed.set(resproj->_idx); + _processed.set(resproj_idx); break; // doesn't return a pointer type } ciMethod *meth = call->as_CallJava()->method(); const TypeTuple * d = call->tf()->domain(); if (meth == NULL) { // not a Java method, assume global escape - set_escape_state(call->_idx, PointsToNode::GlobalEscape); - if (resproj != NULL) - add_pointsto_edge(resproj->_idx, _phantom_object); + set_escape_state(call_idx, PointsToNode::GlobalEscape); + add_pointsto_edge(resproj_idx, _phantom_object); } else { BCEscapeAnalyzer *call_analyzer = meth->get_bcea(); - VectorSet ptset(Thread::current()->resource_area()); bool copy_dependencies = false; if (call_analyzer->is_return_allocated()) { @@ -1651,13 +1673,12 @@ void ConnectionGraph::process_call_result(ProjNode *resproj, PhaseTransform *pha // update dependency information. // Mark it as NoEscape so that objects referenced by // it's fields will be marked as NoEscape at least. - set_escape_state(call->_idx, PointsToNode::NoEscape); - if (resproj != NULL) - add_pointsto_edge(resproj->_idx, call->_idx); + set_escape_state(call_idx, PointsToNode::NoEscape); + add_pointsto_edge(resproj_idx, call_idx); copy_dependencies = true; - } else if (call_analyzer->is_return_local() && resproj != NULL) { + } else if (call_analyzer->is_return_local()) { // determine whether any arguments are returned - set_escape_state(call->_idx, PointsToNode::NoEscape); + set_escape_state(call_idx, PointsToNode::NoEscape); for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { const Type* at = d->field_at(i); @@ -1665,36 +1686,35 @@ void ConnectionGraph::process_call_result(ProjNode *resproj, PhaseTransform *pha Node *arg = call->in(i)->uncast(); if (call_analyzer->is_arg_returned(i - TypeFunc::Parms)) { - PointsToNode *arg_esp = _nodes->adr_at(arg->_idx); + PointsToNode *arg_esp = ptnode_adr(arg->_idx); if (arg_esp->node_type() == PointsToNode::UnknownType) done = false; else if (arg_esp->node_type() == PointsToNode::JavaObject) - add_pointsto_edge(resproj->_idx, arg->_idx); + add_pointsto_edge(resproj_idx, arg->_idx); else - add_deferred_edge(resproj->_idx, arg->_idx); + add_deferred_edge(resproj_idx, arg->_idx); arg_esp->_hidden_alias = true; } } } copy_dependencies = true; } else { - set_escape_state(call->_idx, PointsToNode::GlobalEscape); - if (resproj != NULL) - add_pointsto_edge(resproj->_idx, _phantom_object); + set_escape_state(call_idx, PointsToNode::GlobalEscape); + add_pointsto_edge(resproj_idx, _phantom_object); for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { const Type* at = d->field_at(i); if (at->isa_oopptr() != NULL) { Node *arg = call->in(i)->uncast(); - PointsToNode *arg_esp = _nodes->adr_at(arg->_idx); + PointsToNode *arg_esp = ptnode_adr(arg->_idx); arg_esp->_hidden_alias = true; } } } if (copy_dependencies) - call_analyzer->copy_dependencies(C()->dependencies()); + call_analyzer->copy_dependencies(_compile->dependencies()); } if (done) - _processed.set(resproj->_idx); + _processed.set(resproj_idx); break; } @@ -1709,13 +1729,11 @@ void ConnectionGraph::process_call_result(ProjNode *resproj, PhaseTransform *pha // Note: we use isa_ptr() instead of isa_oopptr() here because the // _multianewarray functions return a TypeRawPtr. if (ret_type->isa_ptr() != NULL) { - PointsToNode *ptadr = ptnode_adr(call->_idx); - set_escape_state(call->_idx, PointsToNode::GlobalEscape); - if (resproj != NULL) - add_pointsto_edge(resproj->_idx, _phantom_object); + set_escape_state(call_idx, PointsToNode::GlobalEscape); + add_pointsto_edge(resproj_idx, _phantom_object); } } - _processed.set(resproj->_idx); + _processed.set(resproj_idx); } } } @@ -1743,7 +1761,7 @@ void ConnectionGraph::record_for_escape_analysis(Node *n, PhaseTransform *phase) // Check if a call returns an object. const TypeTuple *r = n->as_Call()->tf()->range(); - if (r->cnt() > TypeFunc::Parms && + if (n->is_CallStaticJava() && r->cnt() > TypeFunc::Parms && n->as_Call()->proj_out(TypeFunc::Parms) != NULL) { // Note: use isa_ptr() instead of isa_oopptr() here because // the _multianewarray functions return a TypeRawPtr. @@ -1776,7 +1794,7 @@ void ConnectionGraph::record_for_escape_analysis(Node *n, PhaseTransform *phase) { add_node(n, PointsToNode::LocalVar, PointsToNode::UnknownEscape, false); int ti = n->in(1)->_idx; - PointsToNode::NodeType nt = _nodes->adr_at(ti)->node_type(); + PointsToNode::NodeType nt = ptnode_adr(ti)->node_type(); if (nt == PointsToNode::UnknownType) { _delayed_worklist.push(n); // Process it later. break; @@ -1866,7 +1884,7 @@ void ConnectionGraph::record_for_escape_analysis(Node *n, PhaseTransform *phase) if (in->is_top() || in == n) continue; // ignore top or inputs which go back this node int ti = in->_idx; - PointsToNode::NodeType nt = _nodes->adr_at(ti)->node_type(); + PointsToNode::NodeType nt = ptnode_adr(ti)->node_type(); if (nt == PointsToNode::UnknownType) { break; } else if (nt == PointsToNode::JavaObject) { @@ -1904,7 +1922,7 @@ void ConnectionGraph::record_for_escape_analysis(Node *n, PhaseTransform *phase) // Treat Return value as LocalVar with GlobalEscape escape state. add_node(n, PointsToNode::LocalVar, PointsToNode::GlobalEscape, false); int ti = n->in(TypeFunc::Parms)->_idx; - PointsToNode::NodeType nt = _nodes->adr_at(ti)->node_type(); + PointsToNode::NodeType nt = ptnode_adr(ti)->node_type(); if (nt == PointsToNode::UnknownType) { _delayed_worklist.push(n); // Process it later. break; @@ -1968,17 +1986,17 @@ void ConnectionGraph::record_for_escape_analysis(Node *n, PhaseTransform *phase) } void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) { + uint n_idx = n->_idx; + // Don't set processed bit for AddP, LoadP, StoreP since // they may need more then one pass to process. - if (_processed.test(n->_idx)) + if (_processed.test(n_idx)) return; // No need to redefine node's state. - PointsToNode *ptadr = ptnode_adr(n->_idx); - if (n->is_Call()) { CallNode *call = n->as_Call(); process_call_arguments(call, phase); - _processed.set(n->_idx); + _processed.set(n_idx); return; } @@ -1991,7 +2009,7 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) { PointsTo(ptset, base, phase); for( VectorSetI i(&ptset); i.test(); ++i ) { uint pt = i.elem; - add_field_edge(pt, n->_idx, address_offset(n, phase)); + add_field_edge(pt, n_idx, address_offset(n, phase)); } break; } @@ -2006,12 +2024,12 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) { case Op_DecodeN: { int ti = n->in(1)->_idx; - if (_nodes->adr_at(ti)->node_type() == PointsToNode::JavaObject) { - add_pointsto_edge(n->_idx, ti); + if (ptnode_adr(ti)->node_type() == PointsToNode::JavaObject) { + add_pointsto_edge(n_idx, ti); } else { - add_deferred_edge(n->_idx, ti); + add_deferred_edge(n_idx, ti); } - _processed.set(n->_idx); + _processed.set(n_idx); break; } case Op_ConP: @@ -2060,7 +2078,7 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) { int offset = address_offset(adr, phase); for( VectorSetI i(&ptset); i.test(); ++i ) { uint pt = i.elem; - add_deferred_edge_to_fields(n->_idx, pt, offset); + add_deferred_edge_to_fields(n_idx, pt, offset); } break; } @@ -2083,13 +2101,13 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) { if (in->is_top() || in == n) continue; // ignore top or inputs which go back this node int ti = in->_idx; - if (_nodes->adr_at(in->_idx)->node_type() == PointsToNode::JavaObject) { - add_pointsto_edge(n->_idx, ti); + if (ptnode_adr(in->_idx)->node_type() == PointsToNode::JavaObject) { + add_pointsto_edge(n_idx, ti); } else { - add_deferred_edge(n->_idx, ti); + add_deferred_edge(n_idx, ti); } } - _processed.set(n->_idx); + _processed.set(n_idx); break; } case Op_Proj: @@ -2097,7 +2115,7 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) { // we are only interested in the result projection from a call if (n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->is_Call() ) { process_call_result(n->as_Proj(), phase); - assert(_processed.test(n->_idx), "all call results should be processed"); + assert(_processed.test(n_idx), "all call results should be processed"); } else { assert(false, "Op_Proj"); } @@ -2112,12 +2130,12 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) { } #endif int ti = n->in(TypeFunc::Parms)->_idx; - if (_nodes->adr_at(ti)->node_type() == PointsToNode::JavaObject) { - add_pointsto_edge(n->_idx, ti); + if (ptnode_adr(ti)->node_type() == PointsToNode::JavaObject) { + add_pointsto_edge(n_idx, ti); } else { - add_deferred_edge(n->_idx, ti); + add_deferred_edge(n_idx, ti); } - _processed.set(n->_idx); + _processed.set(n_idx); break; } case Op_StoreP: @@ -2162,9 +2180,9 @@ void ConnectionGraph::dump() { PhaseGVN *igvn = _compile->initial_gvn(); bool first = true; - uint size = (uint)_nodes->length(); + uint size = nodes_size(); for (uint ni = 0; ni < size; ni++) { - PointsToNode *ptn = _nodes->adr_at(ni); + PointsToNode *ptn = ptnode_adr(ni); PointsToNode::NodeType ptn_type = ptn->node_type(); if (ptn_type != PointsToNode::JavaObject || ptn->_node == NULL) @@ -2174,7 +2192,7 @@ void ConnectionGraph::dump() { if (first) { tty->cr(); tty->print("======== Connection graph for "); - C()->method()->print_short_name(); + _compile->method()->print_short_name(); tty->cr(); first = false; } @@ -2182,12 +2200,12 @@ void ConnectionGraph::dump() { ptn->dump(); // Print all locals which reference this allocation for (uint li = ni; li < size; li++) { - PointsToNode *ptn_loc = _nodes->adr_at(li); + PointsToNode *ptn_loc = ptnode_adr(li); PointsToNode::NodeType ptn_loc_type = ptn_loc->node_type(); if ( ptn_loc_type == PointsToNode::LocalVar && ptn_loc->_node != NULL && ptn_loc->edge_count() == 1 && ptn_loc->edge_target(0) == ni ) { tty->print("%6d LocalVar [[%d]]", li, ni); - _nodes->adr_at(li)->_node->dump(); + ptnode_adr(li)->_node->dump(); } } if (Verbose) { @@ -2195,7 +2213,7 @@ void ConnectionGraph::dump() { for (uint i = 0; i < ptn->edge_count(); i++) { uint ei = ptn->edge_target(i); tty->print("%6d Field [[%d]]", ei, ni); - _nodes->adr_at(ei)->_node->dump(); + ptnode_adr(ei)->_node->dump(); } } tty->cr(); diff --git a/hotspot/src/share/vm/opto/escape.hpp b/hotspot/src/share/vm/opto/escape.hpp index 1d2c83511e7..5d9259569ef 100644 --- a/hotspot/src/share/vm/opto/escape.hpp +++ b/hotspot/src/share/vm/opto/escape.hpp @@ -178,14 +178,24 @@ public: // count of outgoing edges uint edge_count() const { return (_edges == NULL) ? 0 : _edges->length(); } + // node index of target of outgoing edge "e" - uint edge_target(uint e) const; + uint edge_target(uint e) const { + assert(_edges != NULL, "valid edge index"); + return (_edges->at(e) >> EdgeShift); + } // type of outgoing edge "e" - EdgeType edge_type(uint e) const; + EdgeType edge_type(uint e) const { + assert(_edges != NULL, "valid edge index"); + return (EdgeType) (_edges->at(e) & EdgeMask); + } + // add a edge of the specified type pointing to the specified target void add_edge(uint targIdx, EdgeType et); + // remove an edge of the specified type pointing to the specified target void remove_edge(uint targIdx, EdgeType et); + #ifndef PRODUCT void dump() const; #endif @@ -194,7 +204,7 @@ public: class ConnectionGraph: public ResourceObj { private: - GrowableArray* _nodes; // Connection graph nodes indexed + GrowableArray _nodes; // Connection graph nodes indexed // by ideal node index. Unique_Node_List _delayed_worklist; // Nodes to be processed before @@ -207,9 +217,6 @@ private: // is still being collected. If false, // no new nodes will be processed. - bool _has_allocations; // Indicates whether method has any - // non-escaping allocations. - uint _phantom_object; // Index of globally escaping object // that pointer values loaded from // a field which has not been set @@ -217,14 +224,13 @@ private: Compile * _compile; // Compile object for current compilation - // address of an element in _nodes. Used when the element is to be modified - PointsToNode *ptnode_adr(uint idx) { - if ((uint)_nodes->length() <= idx) { - // expand _nodes array - PointsToNode dummy = _nodes->at_grow(idx); - } - return _nodes->adr_at(idx); + // Address of an element in _nodes. Used when the element is to be modified + PointsToNode *ptnode_adr(uint idx) const { + // There should be no new ideal nodes during ConnectionGraph build, + // growableArray::adr_at() will throw assert otherwise. + return _nodes.adr_at(idx); } + uint nodes_size() const { return _nodes.length(); } // Add node to ConnectionGraph. void add_node(Node *n, PointsToNode::NodeType nt, PointsToNode::EscapeState es, bool done); @@ -307,30 +313,30 @@ private: // Set the escape state of a node void set_escape_state(uint ni, PointsToNode::EscapeState es); - // Get Compile object for current compilation. - Compile *C() const { return _compile; } - public: ConnectionGraph(Compile *C); + // Check for non-escaping candidates + static bool has_candidates(Compile *C); + // Compute the escape information - void compute_escape(); + bool compute_escape(); // escape state of a node PointsToNode::EscapeState escape_state(Node *n, PhaseTransform *phase); // other information we have collected bool is_scalar_replaceable(Node *n) { - if (_collecting) + if (_collecting || (n->_idx >= nodes_size())) return false; - PointsToNode ptn = _nodes->at_grow(n->_idx); - return ptn.escape_state() == PointsToNode::NoEscape && ptn._scalar_replaceable; + PointsToNode* ptn = ptnode_adr(n->_idx); + return ptn->escape_state() == PointsToNode::NoEscape && ptn->_scalar_replaceable; } bool hidden_alias(Node *n) { - if (_collecting) + if (_collecting || (n->_idx >= nodes_size())) return true; - PointsToNode ptn = _nodes->at_grow(n->_idx); - return (ptn.escape_state() != PointsToNode::NoEscape) || ptn._hidden_alias; + PointsToNode* ptn = ptnode_adr(n->_idx); + return (ptn->escape_state() != PointsToNode::NoEscape) || ptn->_hidden_alias; } #ifndef PRODUCT From 5967d518b5efbfe387b3d510eee178329b341346 Mon Sep 17 00:00:00 2001 From: Eamonn McManus Date: Fri, 4 Jul 2008 18:55:37 +0200 Subject: [PATCH 010/105] 6601652: MXBeans: no IllegalArgumentException in the ex. chain for SortedSet/Map with a non-null comparator() Forward-port this bug fix from JDK 6 Reviewed-by: dfuchs, lmalvent --- .../DefaultMXBeanMappingFactory.java | 4 +- .../mxbean/ComparatorExceptionTest.java | 90 +++++++++++++++++++ .../javax/management/mxbean/MXBeanTest.java | 37 +++++--- .../mxbean/SameObjectTwoNamesTest.java | 76 ++++++++++++++++ 4 files changed, 194 insertions(+), 13 deletions(-) create mode 100644 jdk/test/javax/management/mxbean/ComparatorExceptionTest.java create mode 100644 jdk/test/javax/management/mxbean/SameObjectTwoNamesTest.java diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java index 1a58621458a..0e15a464f31 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java @@ -686,7 +686,7 @@ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory { final String msg = "Cannot convert SortedSet with non-null comparator: " + comparator; - throw new OpenDataException(msg); + throw openDataException(msg, new IllegalArgumentException(msg)); } } final Object[] openArray = (Object[]) @@ -800,7 +800,7 @@ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory { final String msg = "Cannot convert SortedMap with non-null comparator: " + comparator; - throw new OpenDataException(msg); + throw openDataException(msg, new IllegalArgumentException(msg)); } } final TabularType tabularType = (TabularType) getOpenType(); diff --git a/jdk/test/javax/management/mxbean/ComparatorExceptionTest.java b/jdk/test/javax/management/mxbean/ComparatorExceptionTest.java new file mode 100644 index 00000000000..cd88a161b02 --- /dev/null +++ b/jdk/test/javax/management/mxbean/ComparatorExceptionTest.java @@ -0,0 +1,90 @@ +/* + * 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 6601652 + * @summary Test exception when SortedMap or SortedSet has non-null Comparator + * @author Eamonn McManus + */ + +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectName; + +public class ComparatorExceptionTest { + public static interface TestMXBean { + public SortedSet getSortedSet(); + public SortedMap getSortedMap(); + } + + public static class TestImpl implements TestMXBean { + public SortedSet getSortedSet() { + return new TreeSet(String.CASE_INSENSITIVE_ORDER); + } + + public SortedMap getSortedMap() { + return new TreeMap(String.CASE_INSENSITIVE_ORDER); + } + } + + private static String failure; + + private static void fail(String why) { + failure = "FAILED: " + why; + System.out.println(failure); + } + + public static void main(String[] args) throws Exception { + MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + ObjectName name = new ObjectName("a:b=c"); + mbs.registerMBean(new TestImpl(), name); + + for (String attr : new String[] {"SortedSet", "SortedMap"}) { + try { + Object value = mbs.getAttribute(name, attr); + fail("get " + attr + " did not throw exception"); + } catch (Exception e) { + Throwable t = e; + while (!(t instanceof IllegalArgumentException)) { + if (t == null) + break; + t = t.getCause(); + } + if (t != null) + System.out.println("Correct exception for " + attr); + else { + fail("get " + attr + " got wrong exception"); + e.printStackTrace(System.out); + } + } + } + + if (failure != null) + throw new Exception(failure); + } +} diff --git a/jdk/test/javax/management/mxbean/MXBeanTest.java b/jdk/test/javax/management/mxbean/MXBeanTest.java index 9abcf57e450..9415b39faef 100644 --- a/jdk/test/javax/management/mxbean/MXBeanTest.java +++ b/jdk/test/javax/management/mxbean/MXBeanTest.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6175517 6278707 6318827 6305746 6392303 + * @bug 6175517 6278707 6318827 6305746 6392303 6600709 * @summary General MXBean test. * @author Eamonn McManus * @run clean MXBeanTest MerlinMXBean TigerMXBean @@ -40,7 +40,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; -import javax.management.Attribute; +import java.util.Map; +import java.util.SortedMap; import javax.management.JMX; import javax.management.MBeanAttributeInfo; import javax.management.MBeanInfo; @@ -55,10 +56,6 @@ import javax.management.StandardMBean; import javax.management.openmbean.ArrayType; import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataInvocationHandler; -import javax.management.openmbean.OpenMBeanAttributeInfo; -import javax.management.openmbean.OpenMBeanInfo; -import javax.management.openmbean.OpenMBeanOperationInfo; -import javax.management.openmbean.OpenMBeanParameterInfo; import javax.management.openmbean.OpenType; import javax.management.openmbean.SimpleType; import javax.management.openmbean.TabularData; @@ -81,10 +78,8 @@ public class MXBeanTest { if (failures == 0) System.out.println("Test passed"); - else { - System.out.println("TEST FAILURES: " + failures); - System.exit(1); - } + else + throw new Exception("TEST FAILURES: " + failures); } private static int failures = 0; @@ -561,6 +556,11 @@ public class MXBeanTest { return false; return deepEqual(o1, o2, namedMXBeans); } + if (o1 instanceof Map) { + if (!(o2 instanceof Map)) + return false; + return equalMap((Map) o1, (Map) o2, namedMXBeans); + } if (o1 instanceof CompositeData && o2 instanceof CompositeData) { return compositeDataEqual((CompositeData) o1, (CompositeData) o2, namedMXBeans); @@ -600,6 +600,21 @@ public class MXBeanTest { return true; } + private static boolean equalMap(Map m1, Map m2, + NamedMXBeans namedMXBeans) { + if (m1.size() != m2.size()) + return false; + if ((m1 instanceof SortedMap) != (m2 instanceof SortedMap)) + return false; + for (Object k1 : m1.keySet()) { + if (!m2.containsKey(k1)) + return false; + if (!equal(m1.get(k1), m2.get(k1), namedMXBeans)) + return false; + } + return true; + } + // This is needed to work around a bug (5095277) // in CompositeDataSupport.equals private static boolean compositeDataEqual(CompositeData cd1, @@ -655,7 +670,7 @@ public class MXBeanTest { /* I wanted to call this method toString(Object), but oddly enough this meant that I couldn't call it from the inner class MXBeanImplInvocationHandler, because the inherited Object.toString() - prevented that. Surprising behaviour. */ + prevented that. */ static String string(Object o) { if (o == null) return "null"; diff --git a/jdk/test/javax/management/mxbean/SameObjectTwoNamesTest.java b/jdk/test/javax/management/mxbean/SameObjectTwoNamesTest.java new file mode 100644 index 00000000000..53cc9bb3b3d --- /dev/null +++ b/jdk/test/javax/management/mxbean/SameObjectTwoNamesTest.java @@ -0,0 +1,76 @@ +/* + * 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 SameObjectTwoNamesTest.java + * @bug 6283873 + * @summary Check that registering the same MXBean under two different + * names produces an exception + * @author Alexander Shusherov + * @author Eamonn McManus + * @run main SameObjectTwoNamesTest + * @run main/othervm -Djmx.mxbean.multiname=true SameObjectTwoNamesTest + */ + +import javax.management.InstanceAlreadyExistsException; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectName; + +public class SameObjectTwoNamesTest { + + public static void main(String[] args) throws Exception { + boolean expectException = + (System.getProperty("jmx.mxbean.multiname") == null); + try { + ObjectName objectName1 = new ObjectName("test:index=1"); + ObjectName objectName2 = new ObjectName("test:index=2"); + MBeanServer mbs = MBeanServerFactory.createMBeanServer(); + MXBC_SimpleClass01 mxBeanObject = new MXBC_SimpleClass01(); + + mbs.registerMBean(mxBeanObject, objectName1); + + mbs.registerMBean(mxBeanObject, objectName2); + + if (expectException) { + throw new Exception("TEST FAILED: " + + "InstanceAlreadyExistsException was not thrown"); + } else + System.out.println("Correctly got no exception with compat property"); + } catch (InstanceAlreadyExistsException e) { + if (expectException) { + System.out.println("Got expected InstanceAlreadyExistsException:"); + e.printStackTrace(System.out); + } else { + throw new Exception( + "TEST FAILED: Got exception even though compat property set", e); + } + } + System.out.println("TEST PASSED"); + } + + public interface MXBC_Simple01MXBean {} + + public static class MXBC_SimpleClass01 implements MXBC_Simple01MXBean {} + +} From fdd75a4bbbd318b9cd0fe268845221709461e710 Mon Sep 17 00:00:00 2001 From: Jonathan Gibbons Date: Tue, 8 Jul 2008 16:59:27 -0700 Subject: [PATCH 011/105] 6715251: javap should be consistent with javac and return 2 if given no arguments Reviewed-by: ksrini --- .../com/sun/tools/javap/JavapTask.java | 43 +++++++---- langtools/test/tools/javap/T4876942.java | 4 +- langtools/test/tools/javap/T6715251.java | 74 +++++++++++++++++++ 3 files changed, 106 insertions(+), 15 deletions(-) create mode 100644 langtools/test/tools/javap/T6715251.java diff --git a/langtools/src/share/classes/com/sun/tools/javap/JavapTask.java b/langtools/src/share/classes/com/sun/tools/javap/JavapTask.java index a9031371a52..4da923e8846 100644 --- a/langtools/src/share/classes/com/sun/tools/javap/JavapTask.java +++ b/langtools/src/share/classes/com/sun/tools/javap/JavapTask.java @@ -306,14 +306,32 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask { }; } + /** Result codes. + */ + static final int + EXIT_OK = 0, // Compilation completed with no errors. + EXIT_ERROR = 1, // Completed but reported errors. + EXIT_CMDERR = 2, // Bad command-line arguments + EXIT_SYSERR = 3, // System error or resource exhaustion. + EXIT_ABNORMAL = 4; // Compiler terminated abnormally + int run(String[] args) { try { handleOptions(args); + + // the following gives consistent behavior with javac + if (classes == null || classes.size() == 0) { + if (options.help || options.version || options.fullVersion) + return EXIT_OK; + else + return EXIT_CMDERR; + } + boolean ok = run(); - return ok ? 0 : 1; + return ok ? EXIT_OK : EXIT_ERROR; } catch (BadArgs e) { diagnosticListener.report(createDiagnostic(e.key, e.args)); - return 1; + return EXIT_CMDERR; } catch (InternalError e) { Object[] e_args; if (e.getCause() == null) @@ -324,7 +342,7 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask { System.arraycopy(e.args, 0, e_args, 1, e.args.length); } diagnosticListener.report(createDiagnostic("err.internal.error", e_args)); - return 1; + return EXIT_ABNORMAL; } finally { log.flush(); } @@ -349,8 +367,7 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask { fileManager = getDefaultFileManager(diagnosticListener, log); Iterator iter = args.iterator(); - if (!iter.hasNext()) - options.help = true; + boolean noArgs = !iter.hasNext(); while (iter.hasNext()) { String arg = iter.next(); @@ -370,9 +387,15 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask { ((JavapFileManager) fileManager).setIgnoreSymbolFile(true); if ((classes == null || classes.size() == 0) && - !(options.help || options.version || options.fullVersion)) { + !(noArgs || options.help || options.version || options.fullVersion)) { throw new BadArgs("err.no.classes.specified"); } + + if (noArgs || options.help) + showHelp(); + + if (options.version || options.fullVersion) + showVersion(options.fullVersion); } private void handleOption(String name, Iterator rest) throws BadArgs { @@ -405,14 +428,8 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask { } public boolean run() { - if (options.help) - showHelp(); - - if (options.version || options.fullVersion) - showVersion(options.fullVersion); - if (classes == null || classes.size() == 0) - return true; + return false; context.put(PrintWriter.class, log); ClassWriter classWriter = ClassWriter.instance(context); diff --git a/langtools/test/tools/javap/T4876942.java b/langtools/test/tools/javap/T4876942.java index 49a4d71589f..186a5e075ee 100644 --- a/langtools/test/tools/javap/T4876942.java +++ b/langtools/test/tools/javap/T4876942.java @@ -23,7 +23,7 @@ /* * @test - * @bug 4876942 + * @bug 4876942 6715251 * @summary javap invoked without args does not print help screen */ @@ -48,7 +48,7 @@ public class T4876942 { PrintWriter out = new PrintWriter(sw); //sun.tools.javap.Main.entry(args); int rc = com.sun.tools.javap.Main.run(args, out); - if (rc != 0) + if (rc != (args.length == 0 ? 2 : 0)) throw new Error("javap failed. rc=" + rc); out.close(); return sw.toString(); diff --git a/langtools/test/tools/javap/T6715251.java b/langtools/test/tools/javap/T6715251.java new file mode 100644 index 00000000000..357a83c3190 --- /dev/null +++ b/langtools/test/tools/javap/T6715251.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. + * + * 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.*; +import java.util.*; + +/* + * @test + * @bug 6715251 + * @summary javap should be consistent with javac and return 2 if given no arguments + */ + +public class T6715251 { + public static void main(String... args) throws Exception { + new T6715251().run(); + } + + void run() throws Exception { + String testClasses = System.getProperty("test.classes", "."); + + test(2); + test(0, "-help"); + test(0, "-version"); + test(0, "-fullversion"); + test(0, "-classpath", testClasses, "T6715251"); + + if (errors > 0) + throw new Exception(errors + " errors received"); + } + + void test(int expect, String ... args) { + int rc = javap(args); + if (rc != expect) + error("bad result: expected: " + expect + ", found " + rc + "\n" + + log); + + } + + int javap(String... args) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + int rc = com.sun.tools.javap.Main.run(args, pw); + log = sw.toString(); + return rc; + } + + void error(String msg) { + System.err.println(msg); + errors++; + } + + String log; + int errors; +} \ No newline at end of file From 23aea10d3edf801424e9eeaf9def15d10a5d30a4 Mon Sep 17 00:00:00 2001 From: Jonathan Gibbons Date: Tue, 8 Jul 2008 17:14:22 -0700 Subject: [PATCH 012/105] 6715757: javap does not print "extends java.lang.Object" Reviewed-by: ksrini --- .../classes/com/sun/tools/classfile/Type.java | 13 ++----------- .../classes/com/sun/tools/javap/ClassWriter.java | 14 +++++--------- langtools/test/tools/javap/4870651/T4870651.java | 9 ++++++--- langtools/test/tools/javap/T4880663.java | 5 +++-- 4 files changed, 16 insertions(+), 25 deletions(-) diff --git a/langtools/src/share/classes/com/sun/tools/classfile/Type.java b/langtools/src/share/classes/com/sun/tools/classfile/Type.java index aaf6cb81a46..98fd099b341 100644 --- a/langtools/src/share/classes/com/sun/tools/classfile/Type.java +++ b/langtools/src/share/classes/com/sun/tools/classfile/Type.java @@ -36,10 +36,6 @@ import java.util.List; public class Type { protected Type() { } - public boolean isObject() { - return false; - } - protected static void append(StringBuilder sb, String prefix, List types, String suffix) { sb.append(prefix); String sep = ""; @@ -66,11 +62,6 @@ public class Type { return name; } - @Override - public boolean isObject() { - return name.equals("java.lang.Object"); - } - public final String name; } @@ -129,7 +120,7 @@ public class Type { public String toString() { StringBuilder sb = new StringBuilder(); appendIfNotEmpty(sb, "<", typeArgTypes, ">"); - if (superclassType != null && !superclassType.isObject()) { + if (superclassType != null) { sb.append(" extends "); sb.append(superclassType); } @@ -188,7 +179,7 @@ public class Type { StringBuilder sb = new StringBuilder(); sb.append(name); String sep = " extends "; - if (classBound != null && !classBound.isObject()) { + if (classBound != null) { sb.append(sep); sb.append(classBound); sep = " & "; diff --git a/langtools/src/share/classes/com/sun/tools/javap/ClassWriter.java b/langtools/src/share/classes/com/sun/tools/javap/ClassWriter.java index c39dbe52d9f..d1c5707a8d8 100644 --- a/langtools/src/share/classes/com/sun/tools/javap/ClassWriter.java +++ b/langtools/src/share/classes/com/sun/tools/javap/ClassWriter.java @@ -104,14 +104,10 @@ public class ClassWriter extends BasicWriter { Signature_attribute sigAttr = getSignature(cf.attributes); if (sigAttr == null) { // use info from class file header - if (classFile.isClass()) { - if (classFile.super_class != 0 ) { - String sn = getJavaSuperclassName(cf); - if (!sn.equals("java.lang.Object") || options.compat) { // BUG XXXXXXXX - print(" extends "); - print(sn); - } - } + if (classFile.isClass() && classFile.super_class != 0 ) { + String sn = getJavaSuperclassName(cf); + print(" extends "); + print(sn); } for (int i = 0; i < classFile.interfaces.length; i++) { print(i == 0 ? (classFile.isClass() ? " implements " : " extends ") : ","); @@ -124,7 +120,7 @@ public class ClassWriter extends BasicWriter { // FieldType and a ClassSignatureType that only contains a superclass type. if (t instanceof Type.ClassSigType) print(t); - else if (!t.isObject()) { + else { print(" extends "); print(t); } diff --git a/langtools/test/tools/javap/4870651/T4870651.java b/langtools/test/tools/javap/4870651/T4870651.java index a2ab5605304..874d1382bda 100644 --- a/langtools/test/tools/javap/4870651/T4870651.java +++ b/langtools/test/tools/javap/4870651/T4870651.java @@ -23,8 +23,9 @@ /* * @test - * @bug 4870651 - * @summary javap should recognize generics, varargs, enum + * @bug 4870651 6715757 + * @summary javap should recognize generics, varargs, enum; + * javap prints "extends java.lang.Object" * @build T4870651 Test * @run main T4870651 */ @@ -38,7 +39,9 @@ public class T4870651 { public void run() throws IOException { verify("Test", - "class Test, U extends java.lang.Comparable>", + "class Test, " + + "U extends java.lang.Comparable>", "v1(java.lang.String...)"); verify("Test$Enum", diff --git a/langtools/test/tools/javap/T4880663.java b/langtools/test/tools/javap/T4880663.java index 1f76c4212fc..f828274732a 100644 --- a/langtools/test/tools/javap/T4880663.java +++ b/langtools/test/tools/javap/T4880663.java @@ -23,8 +23,9 @@ /* * @test - * @bug 4880663 + * @bug 4880663 6715757 * @summary javap could output whitespace between class name and opening brace + * javap prints "extends java.lang.Object" */ @@ -38,7 +39,7 @@ public class T4880663 { public void run() throws IOException { File javaFile = writeTestFile(); File classFile = compileTestFile(javaFile); - verify(classFile, "class Test {"); + verify(classFile, "class Test extends java.lang.Object {"); if (errors > 0) throw new Error(errors + " found."); From a2ef1138ade2fa5369afd701d18698393669c143 Mon Sep 17 00:00:00 2001 From: Jonathan Gibbons Date: Tue, 8 Jul 2008 17:25:50 -0700 Subject: [PATCH 013/105] 6715753: unknown option error can be a little more helpful Reviewed-by: ksrini --- .../com/sun/tools/javap/JavapTask.java | 5 +- .../tools/javap/resources/javap.properties | 4 ++ langtools/test/tools/javap/T6715753.java | 50 +++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 langtools/test/tools/javap/T6715753.java diff --git a/langtools/src/share/classes/com/sun/tools/javap/JavapTask.java b/langtools/src/share/classes/com/sun/tools/javap/JavapTask.java index 4da923e8846..a040701bcb3 100644 --- a/langtools/src/share/classes/com/sun/tools/javap/JavapTask.java +++ b/langtools/src/share/classes/com/sun/tools/javap/JavapTask.java @@ -298,7 +298,7 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask { return new DiagnosticListener () { public void report(Diagnostic diagnostic) { if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { - pw.print(getMessage("err.prefix")); + pw.print(getMessage("err.prefix")); pw.print(" "); } pw.println(diagnostic.getMessage(null)); @@ -331,6 +331,9 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask { return ok ? EXIT_OK : EXIT_ERROR; } catch (BadArgs e) { diagnosticListener.report(createDiagnostic(e.key, e.args)); + if (e.showUsage) { + log.println(getMessage("main.usage.summary", progname)); + } return EXIT_CMDERR; } catch (InternalError e) { Object[] e_args; diff --git a/langtools/src/share/classes/com/sun/tools/javap/resources/javap.properties b/langtools/src/share/classes/com/sun/tools/javap/resources/javap.properties index b8d8adbae12..1243af32cbd 100644 --- a/langtools/src/share/classes/com/sun/tools/javap/resources/javap.properties +++ b/langtools/src/share/classes/com/sun/tools/javap/resources/javap.properties @@ -16,6 +16,10 @@ err.unknown.option=unknown option: {0} err.verify.not.supported=-verify not supported err.Xold.not.supported.here=-Xold must be given as the first option +main.usage.summary=\ +Usage: {0} \n\ +use -help for a list of possible options + main.usage=\ Usage: {0} \n\ where possible options include: diff --git a/langtools/test/tools/javap/T6715753.java b/langtools/test/tools/javap/T6715753.java new file mode 100644 index 00000000000..b331b5f98e1 --- /dev/null +++ b/langtools/test/tools/javap/T6715753.java @@ -0,0 +1,50 @@ +/* + * 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.*; + +/* + * @test + * @bug 6715753 + * @summary Use javap to inquire about a specific inner class + */ + +public class T6715753 { + public static void main(String... args) throws Exception { + new T6715753().run(); + } + + void run() throws Exception { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + String[] args = { "-notAnOption" }; + int rc = com.sun.tools.javap.Main.run(args, pw); + String log = sw.toString(); + if (rc == 0 + || log.indexOf("-notAnOption") == -1 + || log.indexOf("javap") == -1) { // locale-independent indication of usage message + System.err.println("rc: " + rc + ", log=\n" + log); + throw new Exception("test failed"); + } + } +} \ No newline at end of file From d1e933393158c87cf5aceea09f7bd7971d8d1b3f Mon Sep 17 00:00:00 2001 From: Jonathan Gibbons Date: Tue, 8 Jul 2008 17:53:03 -0700 Subject: [PATCH 014/105] 6716452: (classfile) need a method to get the index of an attribute Reviewed-by: ksrini --- .../com/sun/tools/classfile/Attributes.java | 13 ++ langtools/test/tools/javap/T6716452.java | 113 ++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 langtools/test/tools/javap/T6716452.java diff --git a/langtools/src/share/classes/com/sun/tools/classfile/Attributes.java b/langtools/src/share/classes/com/sun/tools/classfile/Attributes.java index 7891aec9cdc..d3ea27f7337 100644 --- a/langtools/src/share/classes/com/sun/tools/classfile/Attributes.java +++ b/langtools/src/share/classes/com/sun/tools/classfile/Attributes.java @@ -78,6 +78,19 @@ public class Attributes implements Iterable { return map.get(name); } + public int getIndex(ConstantPool constant_pool, String name) { + for (int i = 0; i < attrs.length; i++) { + Attribute attr = attrs[i]; + try { + if (attr != null && attr.getName(constant_pool).equals(name)) + return i; + } catch (ConstantPoolException e) { + // ignore invalid entries + } + } + return -1; + } + public int size() { return attrs.length; } diff --git a/langtools/test/tools/javap/T6716452.java b/langtools/test/tools/javap/T6716452.java new file mode 100644 index 00000000000..32a2620b8f3 --- /dev/null +++ b/langtools/test/tools/javap/T6716452.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 6716452 + * @summary need a method to get an index of an attribute + */ + +import java.io.*; +import com.sun.tools.classfile.*; + +public class T6716452 { + public static void main(String[] args) throws Exception { + new T6716452().run(); + } + + public void run() throws Exception { + File javaFile = writeTestFile(); + File classFile = compileTestFile(javaFile); + + ClassFile cf = ClassFile.read(classFile); + for (Method m: cf.methods) { + test(cf, m); + } + + if (errors > 0) + throw new Exception(errors + " errors found"); + } + + void test(ClassFile cf, Method m) { + test(cf, m, Attribute.Code, Code_attribute.class); + test(cf, m, Attribute.Exceptions, Exceptions_attribute.class); + } + + // test the result of Attributes.getIndex according to expectations + // encoded in the method's name + void test(ClassFile cf, Method m, String name, Class c) { + int index = m.attributes.getIndex(cf.constant_pool, name); + try { + String m_name = m.getName(cf.constant_pool); + System.err.println("Method " + m_name + " name:" + name + " index:" + index + " class: " + c); + boolean expect = (m_name.equals("") && name.equals("Code")) + || (m_name.indexOf(name) != -1); + boolean found = (index != -1); + if (expect) { + if (found) { + Attribute attr = m.attributes.get(index); + if (!c.isAssignableFrom(attr.getClass())) { + error(m + ": unexpected attribute found," + + " expected " + c.getName() + + " found " + attr.getClass().getName()); + } + } else { + error(m + ": expected attribute " + name + " not found"); + } + } else { + if (found) { + error(m + ": unexpected attribute " + name); + } + } + } catch (ConstantPoolException e) { + error(m + ": " + e); + } + } + + File writeTestFile() throws IOException { + File f = new File("Test.java"); + PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(f))); + out.println("abstract class Test { "); + out.println(" abstract void m();"); + out.println(" void m_Code() { }"); + out.println(" abstract void m_Exceptions() throws Exception;"); + out.println(" void m_Code_Exceptions() throws Exception { }"); + out.println("}"); + out.close(); + return f; + } + + File compileTestFile(File f) { + int rc = com.sun.tools.javac.Main.compile(new String[] { "-g", f.getPath() }); + if (rc != 0) + throw new Error("compilation failed. rc=" + rc); + String path = f.getPath(); + return new File(path.substring(0, path.length() - 5) + ".class"); + } + + void error(String msg) { + System.err.println("error: " + msg); + errors++; + } + + int errors; +} From 02ef4bbc8c8000ce4e212f7d739baeff7beec5d6 Mon Sep 17 00:00:00 2001 From: Jonathan Gibbons Date: Tue, 8 Jul 2008 18:06:19 -0700 Subject: [PATCH 015/105] 4501661: disallow mixing -public, -private, and -protected options at the same time Reviewed-by: ksrini --- .../com/sun/tools/javap/JavapTask.java | 17 +++ .../classes/com/sun/tools/javap/Options.java | 3 + .../tools/javap/resources/javap.properties | 1 + langtools/test/tools/javap/T4501661.java | 126 ++++++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 langtools/test/tools/javap/T4501661.java diff --git a/langtools/src/share/classes/com/sun/tools/javap/JavapTask.java b/langtools/src/share/classes/com/sun/tools/javap/JavapTask.java index a040701bcb3..1fc2886385c 100644 --- a/langtools/src/share/classes/com/sun/tools/javap/JavapTask.java +++ b/langtools/src/share/classes/com/sun/tools/javap/JavapTask.java @@ -140,24 +140,31 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask { new Option(false, "-public") { void process(JavapTask task, String opt, String arg) { + task.options.accessOptions.add(opt); task.options.showAccess = AccessFlags.ACC_PUBLIC; } }, new Option(false, "-protected") { void process(JavapTask task, String opt, String arg) { + task.options.accessOptions.add(opt); task.options.showAccess = AccessFlags.ACC_PROTECTED; } }, new Option(false, "-package") { void process(JavapTask task, String opt, String arg) { + task.options.accessOptions.add(opt); task.options.showAccess = 0; } }, new Option(false, "-p", "-private") { void process(JavapTask task, String opt, String arg) { + if (!task.options.accessOptions.contains("-p") && + !task.options.accessOptions.contains("-private")) { + task.options.accessOptions.add(opt); + } task.options.showAccess = AccessFlags.ACC_PRIVATE; } }, @@ -386,6 +393,16 @@ public class JavapTask implements DisassemblerTool.DisassemblerTask { throw new BadArgs("err.unknown.option", arg).showUsage(true); } + if (!options.compat && options.accessOptions.size() > 1) { + StringBuilder sb = new StringBuilder(); + for (String opt: options.accessOptions) { + if (sb.length() > 0) + sb.append(" "); + sb.append(opt); + } + throw new BadArgs("err.incompatible.options", sb); + } + if (options.ignoreSymbolFile && fileManager instanceof JavapFileManager) ((JavapFileManager) fileManager).setIgnoreSymbolFile(true); diff --git a/langtools/src/share/classes/com/sun/tools/javap/Options.java b/langtools/src/share/classes/com/sun/tools/javap/Options.java index 33be38c2db1..fcbfc0211d8 100644 --- a/langtools/src/share/classes/com/sun/tools/javap/Options.java +++ b/langtools/src/share/classes/com/sun/tools/javap/Options.java @@ -25,6 +25,8 @@ package com.sun.tools.javap; +import java.util.HashSet; +import java.util.Set; import com.sun.tools.classfile.AccessFlags; /* @@ -74,6 +76,7 @@ public class Options { public boolean showFlags; public boolean showLineAndLocalVariableTables; public int showAccess; + public Set accessOptions = new HashSet(); public boolean showDisassembled; public boolean showInternalSignatures; public boolean showAllAttrs; diff --git a/langtools/src/share/classes/com/sun/tools/javap/resources/javap.properties b/langtools/src/share/classes/com/sun/tools/javap/resources/javap.properties index 1243af32cbd..8f50abb94bd 100644 --- a/langtools/src/share/classes/com/sun/tools/javap/resources/javap.properties +++ b/langtools/src/share/classes/com/sun/tools/javap/resources/javap.properties @@ -7,6 +7,7 @@ err.crash=A serious internal error has occurred: {0}\nPlease file a bug report, err.end.of.file=unexpected end of file while reading {0} err.file.not.found=file not found: {0} err.h.not.supported=-h is no longer available - use the 'javah' program +err.incompatible.options=bad combination of options: {0} err.internal.error=internal error: {0} {1} {2} err.ioerror=IO error reading {0}: {1} err.missing.arg=no value given for {0} diff --git a/langtools/test/tools/javap/T4501661.java b/langtools/test/tools/javap/T4501661.java new file mode 100644 index 00000000000..3659db1ddeb --- /dev/null +++ b/langtools/test/tools/javap/T4501661.java @@ -0,0 +1,126 @@ +/* + * 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.*; +import java.util.*; + +/* + * @test + * @bug 4501661 + * @summary disallow mixing -public, -private, and -protected + */ +public class T4501661 { + public static void main(String... args) throws Exception { + new T4501661().run(); + } + + void run() throws Exception { + File javaFile = writeTestFile(); + File classFile = compileTestFile(javaFile); + boolean[] values = { false, true }; + for (boolean priv: values) { + for (boolean prot: values) { + for (boolean publ: values) { + test(priv, prot, publ, classFile); + } + } + } + + if (errors > 0) + throw new Exception(errors + " errors found"); + } + + void test(boolean priv, boolean prot, boolean publ, File classFile) { + List args = new ArrayList(); + if (priv) + args.add("-private"); + if (prot) + args.add("-protected"); + if (publ) + args.add("-public"); + boolean expectOK = (args.size() <= 1); + args.add(classFile.getPath()); + String out = javap(args, expectOK); + if (out == null) + return; + if (!priv && !prot && !publ) + checkNone(out, "private"); + if (prot) + checkNone(out, "private", "package"); + if (publ) + checkNone(out, "private", "package", "protected"); + } + + File writeTestFile() throws IOException { + File f = new File("Test.java"); + PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(f))); + out.println("abstract class Test { "); + out.println(" public void public_m() { }"); + out.println(" protected void protected_m() { }"); + out.println(" private void private_m() { }"); + out.println(" void package_m() { }"); + out.println("}"); + out.close(); + return f; + } + + File compileTestFile(File f) { + int rc = com.sun.tools.javac.Main.compile(new String[] { "-g", f.getPath() }); + if (rc != 0) + throw new Error("compilation failed. rc=" + rc); + String path = f.getPath(); + return new File(path.substring(0, path.length() - 5) + ".class"); + } + + String javap(List args, boolean expectOK) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + int rc = com.sun.tools.javap.Main.run(args.toArray(new String[args.size()]), pw); + System.err.println(args); + System.err.println(sw); + if (expectOK) { + if (rc == 0) + return sw.toString(); + else + error("javap failed unexpectedly; rc=" + rc + "\n" + sw); + } else { + if (rc == 0) + error("javap succeeded unexpectedly"); + } + return null; + } + + void checkNone(String log, String... words) { + for (String word: words) { + if (log.indexOf(word) != -1) + error("\"" + word + "\" unexpectedly found in output"); + } + } + + void error(String msg) { + System.err.println("error: " + msg); + errors++; + } + + int errors; +} From 9ac27197dfebf5f24f62dbf807d5917070d94f2e Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Wed, 9 Jul 2008 12:03:16 +0800 Subject: [PATCH 016/105] 6480981: keytool should be able to import certificates from remote SSL servers Reviewed-by: vinnie, wetmore --- .../classes/sun/security/tools/KeyTool.java | 129 ++++++++++++++---- .../classes/sun/security/util/Resources.java | 8 +- .../sun/security/tools/keytool/PrintSSL.java | 55 ++++++++ .../sun/security/tools/keytool/printssl.sh | 58 ++++++++ 4 files changed, 218 insertions(+), 32 deletions(-) create mode 100644 jdk/test/sun/security/tools/keytool/PrintSSL.java create mode 100644 jdk/test/sun/security/tools/keytool/printssl.sh diff --git a/jdk/src/share/classes/sun/security/tools/KeyTool.java b/jdk/src/share/classes/sun/security/tools/KeyTool.java index 41cfa2dc9ee..68fb2fa953e 100644 --- a/jdk/src/share/classes/sun/security/tools/KeyTool.java +++ b/jdk/src/share/classes/sun/security/tools/KeyTool.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 @@ -26,35 +26,23 @@ package sun.security.tools; import java.io.*; -import java.math.BigInteger; -import java.security.GeneralSecurityException; -import java.security.InvalidParameterException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.security.Key; import java.security.PublicKey; import java.security.PrivateKey; import java.security.Security; import java.security.Signature; -import java.security.SignatureException; import java.security.UnrecoverableEntryException; import java.security.UnrecoverableKeyException; import java.security.Principal; import java.security.Provider; import java.security.Identity; -import java.security.Signer; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; -import java.security.interfaces.DSAParams; -import java.security.interfaces.DSAPrivateKey; -import java.security.interfaces.DSAPublicKey; -import java.security.interfaces.RSAPrivateCrtKey; -import java.security.interfaces.RSAPrivateKey; -import java.security.interfaces.RSAPublicKey; import java.text.Collator; import java.text.MessageFormat; import java.util.*; @@ -62,7 +50,6 @@ import java.lang.reflect.Constructor; import java.net.URL; import java.net.URLClassLoader; -import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import sun.security.util.ObjectIdentifier; import sun.security.pkcs.PKCS10; @@ -72,11 +59,16 @@ import sun.security.provider.SystemIdentity; import sun.security.provider.X509Factory; import sun.security.util.DerOutputStream; import sun.security.util.Password; -import sun.security.util.Resources; import sun.security.util.PathList; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; import sun.security.x509.*; import static java.security.KeyStore.*; @@ -132,6 +124,7 @@ public final class KeyTool { private String ksfname = null; private File ksfile = null; private InputStream ksStream = null; // keystore stream + private String sslserver = null; private KeyStore keyStore = null; private boolean token = false; private boolean nullStream = false; @@ -347,6 +340,9 @@ public final class KeyTool { } else if (collator.compare(flags, "-file") == 0) { if (++i == args.length) errorNeedArgument(flags); filename = args[i]; + } else if (collator.compare(flags, "-sslserver") == 0) { + if (++i == args.length) errorNeedArgument(flags); + sslserver = args[i]; } else if (collator.compare(flags, "-srckeystore") == 0) { if (++i == args.length) errorNeedArgument(flags); srcksfname = args[i]; @@ -924,17 +920,7 @@ public final class KeyTool { doPrintEntries(out); } } else if (command == PRINTCERT) { - InputStream inStream = System.in; - if (filename != null) { - inStream = new FileInputStream(filename); - } - try { - doPrintCert(inStream, out); - } finally { - if (inStream != System.in) { - inStream.close(); - } - } + doPrintCert(out); } else if (command == SELFCERT) { doSelfCert(alias, dname, sigAlgName); kssave = true; @@ -1744,7 +1730,7 @@ public final class KeyTool { * Reads a certificate (or certificate chain) and prints its contents in * a human readbable format. */ - private void doPrintCert(InputStream in, PrintStream out) + private void printCertFromStream(InputStream in, PrintStream out) throws Exception { Collection c = null; @@ -1770,13 +1756,98 @@ public final class KeyTool { Object[] source = {new Integer(i + 1)}; out.println(form.format(source)); } - printX509Cert(x509Cert, out); + if (rfc) dumpCert(x509Cert, out); + else printX509Cert(x509Cert, out); if (i < (certs.length-1)) { out.println(); } } } + private void doPrintCert(final PrintStream out) throws Exception { + if (sslserver != null) { + SSLContext sc = SSLContext.getInstance("SSL"); + final boolean[] certPrinted = new boolean[1]; + sc.init(null, new TrustManager[] { + new X509TrustManager() { + + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } + + public void checkClientTrusted( + java.security.cert.X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted( + java.security.cert.X509Certificate[] certs, String authType) { + for (int i=0; i 0) { + certPrinted[0] = true; + } + } + } + }, null); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + HttpsURLConnection.setDefaultHostnameVerifier( + new HostnameVerifier() { + public boolean verify(String hostname, SSLSession session) { + return true; + } + }); + // HTTPS instead of raw SSL, so that -Dhttps.proxyHost and + // -Dhttps.proxyPort can be used. Since we only go through + // the handshake process, an HTTPS server is not needed. + // This program should be able to deal with any SSL-based + // network service. + Exception ex = null; + try { + new URL("https://" + sslserver).openConnection().connect(); + } catch (Exception e) { + ex = e; + } + // If the certs are not printed out, we consider it an error even + // if the URL connection is successful. + if (!certPrinted[0]) { + Exception e = new Exception( + rb.getString("No certificate from the SSL server")); + if (ex != null) { + e.initCause(ex); + } + throw e; + } + } else { + InputStream inStream = System.in; + if (filename != null) { + inStream = new FileInputStream(filename); + } + try { + printCertFromStream(inStream, out); + } finally { + if (inStream != System.in) { + inStream.close(); + } + } + } + } /** * Creates a self-signed certificate, and stores it as a single-element * certificate chain. @@ -3127,7 +3198,7 @@ public final class KeyTool { System.err.println(); System.err.println(rb.getString - ("-printcert [-v] [-file ]")); + ("-printcert [-v] [-rfc] [-file | -sslserver ]")); System.err.println(); System.err.println(rb.getString diff --git a/jdk/src/share/classes/sun/security/util/Resources.java b/jdk/src/share/classes/sun/security/util/Resources.java index 7f765ee368b..0f5982183ad 100644 --- a/jdk/src/share/classes/sun/security/util/Resources.java +++ b/jdk/src/share/classes/sun/security/util/Resources.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 @@ -386,8 +386,10 @@ public class Resources extends java.util.ListResourceBundle { {"\t [-alias ]", "\t [-alias ]"}, /** rest is same as -certreq starting from -keystore **/ - {"-printcert [-v] [-file ]", - "-printcert [-v] [-file ]"}, + {"-printcert [-v] [-rfc] [-file | -sslserver ]", + "-printcert [-v] [-rfc] [-file | -sslserver ]"}, + {"No certificate from the SSL server", + "No certificate from the SSL server"}, //{"-selfcert [-v] [-protected]", // "-selfcert [-v] [-protected]"}, diff --git a/jdk/test/sun/security/tools/keytool/PrintSSL.java b/jdk/test/sun/security/tools/keytool/PrintSSL.java new file mode 100644 index 00000000000..7e9ce7b9534 --- /dev/null +++ b/jdk/test/sun/security/tools/keytool/PrintSSL.java @@ -0,0 +1,55 @@ +/* + * 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. + */ + +// Read printssl.sh, this Java program starts an SSL server + +import java.net.ServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; + +public class PrintSSL { + public static void main(String[] args) throws Exception { + System.setProperty("javax.net.ssl.keyStorePassword", "passphrase"); + System.setProperty("javax.net.ssl.keyStore", + System.getProperty("test.src", "./") + "/../../ssl/etc/keystore"); + SSLServerSocketFactory sslssf = + (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + final ServerSocket server = sslssf.createServerSocket(0); + System.out.println(server.getLocalPort()); + System.out.flush(); + Thread t = new Thread() { + public void run() { + try { + Thread.sleep(30000); + server.close(); + } catch (Exception e) { + ; + } + throw new RuntimeException("Timeout"); + } + }; + t.setDaemon(true); + t.start(); + ((SSLSocket)server.accept()).startHandshake(); + } +} diff --git a/jdk/test/sun/security/tools/keytool/printssl.sh b/jdk/test/sun/security/tools/keytool/printssl.sh new file mode 100644 index 00000000000..9fc19cd9b20 --- /dev/null +++ b/jdk/test/sun/security/tools/keytool/printssl.sh @@ -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 6480981 +# @summary keytool should be able to import certificates from remote SSL servers + +if [ "${TESTSRC}" = "" ] ; then + TESTSRC="." +fi +if [ "${TESTJAVA}" = "" ] ; then + echo "TESTJAVA not set. Test cannot execute." + echo "FAILED!!!" + exit 1 +fi + +# set platform-dependent variables +OS=`uname -s` +case "$OS" in + SunOS | Linux ) + FS="/" + ;; + Windows_* ) + FS="\\" + ;; + * ) + echo "Unrecognized operating system!" + exit 1; + ;; +esac + +${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}PrintSSL.java || exit 10 +${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC PrintSSL | ( read PORT; ${TESTJAVA}${FS}bin${FS}keytool -printcert -sslserver localhost:$PORT ) +status=$? + +rm PrintSSL*.class + +exit $status From 4333dd3520136a8d9644f928699bc115308abaf3 Mon Sep 17 00:00:00 2001 From: Eamonn McManus Date: Wed, 9 Jul 2008 10:36:07 +0200 Subject: [PATCH 017/105] 6323980: Annotations to simplify MBean development Reviewed-by: jfdenise, dfuchs --- .../DefaultMBeanServerInterceptor.java | 519 +++++++---- .../sun/jmx/mbeanserver/DynamicMBean2.java | 14 +- .../com/sun/jmx/mbeanserver/Introspector.java | 229 +++-- .../sun/jmx/mbeanserver/MBeanAnalyzer.java | 29 +- .../sun/jmx/mbeanserver/MBeanInjector.java | 291 ++++++ .../jmx/mbeanserver/MBeanIntrospector.java | 161 +++- .../com/sun/jmx/mbeanserver/MBeanSupport.java | 6 +- .../jmx/mbeanserver/MXBeanIntrospector.java | 32 +- .../sun/jmx/mbeanserver/MXBeanSupport.java | 4 +- .../sun/jmx/mbeanserver/NotifySupport.java | 186 ++++ .../com/sun/jmx/mbeanserver/Repository.java | 161 +++- .../StandardMBeanIntrospector.java | 31 +- .../jmx/mbeanserver/StandardMBeanSupport.java | 43 +- .../classes/com/sun/jmx/mbeanserver/Util.java | 5 + .../javax/management/BinaryRelQueryExp.java | 1 + .../classes/javax/management/Description.java | 180 ++++ .../classes/javax/management/Descriptor.java | 23 +- .../javax/management/DescriptorFields.java | 137 +++ .../javax/management/DescriptorKey.java | 52 +- .../javax/management/DynamicWrapperMBean.java | 62 ++ .../classes/javax/management/Impact.java | 105 +++ .../share/classes/javax/management/JMX.java | 26 + .../share/classes/javax/management/MBean.java | 68 ++ .../javax/management/MBeanOperationInfo.java | 40 +- .../javax/management/MBeanRegistration.java | 104 ++- .../classes/javax/management/MBeanServer.java | 11 +- .../management/MBeanServerConnection.java | 6 + .../classes/javax/management/MXBean.java | 31 +- .../javax/management/ManagedAttribute.java | 64 ++ .../javax/management/ManagedOperation.java | 67 ++ .../classes/javax/management/NotQueryExp.java | 1 + .../NotificationBroadcasterSupport.java | 3 +- .../javax/management/NotificationInfo.java | 117 +++ .../javax/management/NotificationInfos.java | 72 ++ .../javax/management/SendNotification.java | 38 + .../management/StandardEmitterMBean.java | 198 ++++- .../javax/management/StandardMBean.java | 145 ++- .../modelmbean/RequiredModelMBean.java | 68 +- .../javax/management/monitor/package.html | 19 +- .../classes/javax/management/package.html | 415 +++++---- .../Introspector/AnnotatedMBeanTest.java | 337 +++++++ .../AnnotatedNotificationInfoTest.java | 271 ++++++ .../Introspector/MBeanDescriptionTest.java | 830 ++++++++++++++++++ .../Introspector/ParameterNameTest.java | 116 +++ .../Introspector/ResourceInjectionTest.java | 656 ++++++++++++++ .../management/Introspector/annot/Name.java | 32 + 46 files changed, 5419 insertions(+), 587 deletions(-) create mode 100644 jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java create mode 100644 jdk/src/share/classes/com/sun/jmx/mbeanserver/NotifySupport.java create mode 100644 jdk/src/share/classes/javax/management/Description.java create mode 100644 jdk/src/share/classes/javax/management/DescriptorFields.java create mode 100644 jdk/src/share/classes/javax/management/DynamicWrapperMBean.java create mode 100644 jdk/src/share/classes/javax/management/Impact.java create mode 100644 jdk/src/share/classes/javax/management/MBean.java create mode 100644 jdk/src/share/classes/javax/management/ManagedAttribute.java create mode 100644 jdk/src/share/classes/javax/management/ManagedOperation.java create mode 100644 jdk/src/share/classes/javax/management/NotificationInfo.java create mode 100644 jdk/src/share/classes/javax/management/NotificationInfos.java create mode 100644 jdk/src/share/classes/javax/management/SendNotification.java create mode 100644 jdk/test/javax/management/Introspector/AnnotatedMBeanTest.java create mode 100644 jdk/test/javax/management/Introspector/AnnotatedNotificationInfoTest.java create mode 100644 jdk/test/javax/management/Introspector/MBeanDescriptionTest.java create mode 100644 jdk/test/javax/management/Introspector/ParameterNameTest.java create mode 100644 jdk/test/javax/management/Introspector/ResourceInjectionTest.java create mode 100644 jdk/test/javax/management/Introspector/annot/Name.java diff --git a/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java b/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java index b968ba9aa1e..4d47f012f4e 100644 --- a/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java +++ b/jdk/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java @@ -84,8 +84,13 @@ 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; /** * This is the default class for MBean manipulation on the agent side. It @@ -433,36 +438,26 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { if (instance instanceof MBeanRegistration) preDeregisterInvoke((MBeanRegistration) instance); - repository.remove(name); - // may throw InstanceNotFoundException + final Object resource = getResource(instance); - /** - * Checks if the unregistered MBean is a ClassLoader - * If so, it removes the MBean from the default loader repository. - */ + // Unregisters the MBean from the repository. + // Returns the resource context that was used. + // The returned context does nothing for regular MBeans. + // For ClassLoader MBeans and JMXNamespace (and JMXDomain) + // MBeans - the context makes it possible to unregister these + // objects from the appropriate framework artifacts, such as + // the CLR or the dispatcher, from within the repository lock. + // In case of success, we also need to call context.done() at the + // end of this method. + // + final ResourceContext context = + unregisterFromRepository(resource, instance, name); - Object resource = getResource(instance); - if (resource instanceof ClassLoader - && resource != server.getClass().getClassLoader()) { - final ModifiableClassLoaderRepository clr = - instantiator.getClassLoaderRepository(); - if (clr != null) clr.removeClassLoader(name); - } - - // --------------------- - // Send deletion event - // --------------------- - if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { - MBEANSERVER_LOGGER.logp(Level.FINER, - DefaultMBeanServerInterceptor.class.getName(), - "unregisterMBean", "Send delete notification of object " + - name.getCanonicalName()); - } - sendNotification(MBeanServerNotification.UNREGISTRATION_NOTIFICATION, - name); if (instance instanceof MBeanRegistration) postDeregisterInvoke((MBeanRegistration) instance); + + context.done(); } public ObjectInstance getObjectInstance(ObjectName name) @@ -939,15 +934,22 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } ObjectName logicalName = name; + logicalName = preRegister(mbean, server, name); + + // preRegister returned successfully, so from this point on we + // must call postRegister(false) if there is any problem. + boolean registered = false; + boolean registerFailed = false; + ResourceContext context = null; + + try { + mbean = injectResources(mbean, server, logicalName); - if (mbean instanceof MBeanRegistration) { - MBeanRegistration reg = (MBeanRegistration) mbean; - logicalName = preRegisterInvoke(reg, name, server); if (mbean instanceof DynamicMBean2) { try { ((DynamicMBean2) mbean).preRegister2(server, logicalName); + registerFailed = true; // until we succeed } catch (Exception e) { - postRegisterInvoke(reg, false, false); if (e instanceof RuntimeException) throw (RuntimeException) e; if (e instanceof InstanceAlreadyExistsException) @@ -960,86 +962,102 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { logicalName = ObjectName.getInstance(nonDefaultDomain(logicalName)); } - } - checkMBeanPermission(classname, null, logicalName, "registerMBean"); + checkMBeanPermission(classname, null, logicalName, "registerMBean"); - final ObjectInstance result; - if (logicalName!=null) { - result = new ObjectInstance(logicalName, classname); - internal_addObject(mbean, logicalName); - } else { - if (mbean instanceof MBeanRegistration) - postRegisterInvoke((MBeanRegistration) mbean, false, true); - final RuntimeException wrapped = - new IllegalArgumentException("No object name specified"); - throw new RuntimeOperationsException(wrapped, - "Exception occurred trying to register the MBean"); - } - - if (mbean instanceof MBeanRegistration) - postRegisterInvoke((MBeanRegistration) mbean, true, false); - - /** - * Checks if the newly registered MBean is a ClassLoader - * If so, tell the ClassLoaderRepository (CLR) about it. We do - * this even if the object is a PrivateClassLoader. In that - * case, the CLR remembers the loader for use when it is - * explicitly named (e.g. as the loader in createMBean) but - * does not add it to the list that is consulted by - * ClassLoaderRepository.loadClass. - */ - final Object resource = getResource(mbean); - if (resource instanceof ClassLoader) { - final ModifiableClassLoaderRepository clr = - instantiator.getClassLoaderRepository(); - if (clr == null) { + if (logicalName == null) { final RuntimeException wrapped = - new IllegalArgumentException( - "Dynamic addition of class loaders is not supported"); + new IllegalArgumentException("No object name specified"); throw new RuntimeOperationsException(wrapped, - "Exception occurred trying to register the MBean as a class loader"); + "Exception occurred trying to register the MBean"); } - clr.addClassLoader(logicalName, (ClassLoader) resource); + + final Object resource = getResource(mbean); + + // Register the MBean with the repository. + // Returns the resource context that was used. + // The returned context does nothing for regular MBeans. + // For ClassLoader MBeans and JMXNamespace (and JMXDomain) + // MBeans - the context makes it possible to register these + // objects with the appropriate framework artifacts, such as + // the CLR or the dispatcher, from within the repository lock. + // In case of success, we also need to call context.done() at the + // end of this method. + // + context = registerWithRepository(resource, mbean, logicalName); + + registerFailed = false; + registered = true; + } finally { + postRegister(mbean, registered, registerFailed); } - return result; + context.done(); + return new ObjectInstance(logicalName, classname); } - private static ObjectName preRegisterInvoke(MBeanRegistration moi, - ObjectName name, - MBeanServer mbs) - throws InstanceAlreadyExistsException, MBeanRegistrationException { - - final ObjectName newName; - + private static void throwMBeanRegistrationException(Throwable t, String where) + throws MBeanRegistrationException { try { - newName = moi.preRegister(mbs, name); + throw t; } catch (RuntimeException e) { - throw new RuntimeMBeanException(e, - "RuntimeException thrown in preRegister method"); + throw new RuntimeMBeanException( + e, "RuntimeException thrown " + where); } catch (Error er) { - throw new RuntimeErrorException(er, - "Error thrown in preRegister method"); + throw new RuntimeErrorException(er, "Error thrown " + where); } catch (MBeanRegistrationException r) { throw r; } catch (Exception ex) { - throw new MBeanRegistrationException(ex, - "Exception thrown in preRegister method"); + throw new MBeanRegistrationException(ex, "Exception thrown " + where); + } catch (Throwable t1) { + throw new RuntimeException(t); // neither Error nor Exception?? + } + } + + private static ObjectName preRegister( + DynamicMBean mbean, MBeanServer mbs, ObjectName name) + throws InstanceAlreadyExistsException, MBeanRegistrationException { + + ObjectName newName = null; + + try { + if (mbean instanceof MBeanRegistration) + newName = ((MBeanRegistration) mbean).preRegister(mbs, name); + } catch (Throwable t) { + throwMBeanRegistrationException(t, "in preRegister method"); } if (newName != null) return newName; else return name; } - private static void postRegisterInvoke(MBeanRegistration moi, - boolean registrationDone, - boolean registerFailed) { - - if (registerFailed && moi instanceof DynamicMBean2) - ((DynamicMBean2) moi).registerFailed(); + private static DynamicMBean injectResources( + DynamicMBean mbean, MBeanServer mbs, ObjectName name) + throws MBeanRegistrationException { try { - moi.postRegister(registrationDone); + Object resource = getResource(mbean); + MBeanInjector.inject(resource, mbs, name); + if (MBeanInjector.injectsSendNotification(resource)) { + NotificationBroadcasterSupport nbs = + new NotificationBroadcasterSupport(); + MBeanInjector.injectSendNotification(resource, nbs); + mbean = NotifySupport.wrap(mbean, nbs); + } + return mbean; + } catch (Throwable t) { + throwMBeanRegistrationException(t, "injecting @Resources"); + return null; // not reached + } + } + + private static void postRegister( + DynamicMBean mbean, boolean registrationDone, boolean registerFailed) { + + if (registerFailed && mbean instanceof DynamicMBean2) + ((DynamicMBean2) mbean).registerFailed(); + try { + if (mbean instanceof MBeanRegistration) + ((MBeanRegistration) mbean).postRegister(registrationDone); } catch (RuntimeException e) { throw new RuntimeMBeanException(e, "RuntimeException thrown in postRegister method"); @@ -1053,17 +1071,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { throws MBeanRegistrationException { try { moi.preDeregister(); - } catch (RuntimeException e) { - throw new RuntimeMBeanException(e, - "RuntimeException thrown in preDeregister method"); - } catch (Error er) { - throw new RuntimeErrorException(er, - "Error thrown in preDeregister method"); - } catch (MBeanRegistrationException t) { - throw t; - } catch (Exception ex) { - throw new MBeanRegistrationException(ex, - "Exception thrown in preDeregister method"); + } catch (Throwable t) { + throwMBeanRegistrationException(t, "in preDeregister method"); } } @@ -1104,12 +1113,19 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } private static Object getResource(DynamicMBean mbean) { - if (mbean instanceof DynamicMBean2) - return ((DynamicMBean2) mbean).getResource(); + if (mbean instanceof DynamicWrapperMBean) + return ((DynamicWrapperMBean) mbean).getWrappedObject(); else return mbean; } + private static ClassLoader getResourceLoader(DynamicMBean mbean) { + if (mbean instanceof DynamicWrapperMBean) + return ((DynamicWrapperMBean) mbean).getWrappedClassLoader(); + else + return mbean.getClass().getClassLoader(); + } + private ObjectName nonDefaultDomain(ObjectName name) { if (name == null || name.getDomain().length() > 0) return name; @@ -1123,14 +1139,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { if one is supplied where it shouldn't be). */ final String completeName = domain + name; - try { - return new ObjectName(completeName); - } catch (MalformedObjectNameException e) { - final String msg = - "Unexpected default domain problem: " + completeName + ": " + - e; - throw EnvHelp.initCause(new IllegalArgumentException(msg), e); - } + return Util.newObjectName(completeName); } public String getDefaultDomain() { @@ -1211,7 +1220,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } NotificationListener listenerWrapper = - getListenerWrapper(listener, name, broadcaster, true); + getListenerWrapper(listener, name, instance, true); broadcaster.addNotificationListener(listenerWrapper, filter, handback); } @@ -1335,7 +1344,6 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { DynamicMBean instance = getMBean(name); checkMBeanPermission(instance, null, name, "removeNotificationListener"); - Object resource = getResource(instance); /* We could simplify the code by assigning broadcaster after assigning listenerWrapper, but that would change the error @@ -1348,7 +1356,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { getNotificationBroadcaster(name, instance, reqClass); NotificationListener listenerWrapper = - getListenerWrapper(listener, name, resource, false); + getListenerWrapper(listener, name, instance, false); if (listenerWrapper == null) throw new ListenerNotFoundException("Unknown listener"); @@ -1366,8 +1374,10 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { private static T getNotificationBroadcaster(ObjectName name, Object instance, Class reqClass) { - if (instance instanceof DynamicMBean2) - instance = ((DynamicMBean2) instance).getResource(); + if (reqClass.isInstance(instance)) + return reqClass.cast(instance); + if (instance instanceof DynamicWrapperMBean) + instance = ((DynamicWrapperMBean) instance).getWrappedObject(); if (reqClass.isInstance(instance)) return reqClass.cast(instance); final RuntimeException exc = @@ -1415,24 +1425,31 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { checkMBeanPermission(instance, null, name, "isInstanceOf"); try { - if (instance instanceof DynamicMBean2) { - Object resource = ((DynamicMBean2) instance).getResource(); - ClassLoader loader = resource.getClass().getClassLoader(); - Class c = Class.forName(className, false, loader); - return c.isInstance(resource); - } + Object resource = getResource(instance); - final String cn = getClassName(instance); - if (cn.equals(className)) + final String resourceClassName = + (resource instanceof DynamicMBean) ? + getClassName((DynamicMBean) resource) : + resource.getClass().getName(); + + if (resourceClassName.equals(className)) return true; - final ClassLoader cl = instance.getClass().getClassLoader(); + final ClassLoader cl = getResourceLoader(instance); final Class classNameClass = Class.forName(className, false, cl); - if (classNameClass.isInstance(instance)) + if (classNameClass.isInstance(resource)) return true; - final Class instanceClass = Class.forName(cn, false, cl); - return classNameClass.isAssignableFrom(instanceClass); + // Ensure that isInstanceOf(NotificationEmitter) is true when + // the MBean is a NotificationEmitter by virtue of a @Resource + // annotation specifying a SendNotification resource. + // This is a hack. + if (instance instanceof NotificationBroadcaster && + classNameClass.isAssignableFrom(NotificationEmitter.class)) + return true; + + final Class resourceClass = Class.forName(resourceClassName, false, cl); + return classNameClass.isAssignableFrom(resourceClass); } catch (Exception x) { /* Could be SecurityException or ClassNotFoundException */ if (MBEANSERVER_LOGGER.isLoggable(Level.FINEST)) { @@ -1457,7 +1474,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { DynamicMBean instance = getMBean(mbeanName); checkMBeanPermission(instance, null, mbeanName, "getClassLoaderFor"); - return getResource(instance).getClass().getClassLoader(); + return getResourceLoader(instance); } /** @@ -1488,40 +1505,6 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { return (ClassLoader) resource; } - /** - * Adds a MBean in the repository - */ - private void internal_addObject(DynamicMBean object, ObjectName logicalName) - throws InstanceAlreadyExistsException { - - // ------------------------------ - // ------------------------------ - - // Let the repository do the work. - - try { - repository.addMBean(object, logicalName); - } catch (InstanceAlreadyExistsException e) { - if (object instanceof MBeanRegistration) { - postRegisterInvoke((MBeanRegistration) object, false, true); - } - throw e; - } - - // --------------------- - // Send create event - // --------------------- - if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { - MBEANSERVER_LOGGER.logp(Level.FINER, - DefaultMBeanServerInterceptor.class.getName(), - "addObject", "Send create notification of object " + - logicalName.getCanonicalName()); - } - - sendNotification(MBeanServerNotification.REGISTRATION_NOTIFICATION, - logicalName ) ; - } - /** * Sends an MBeanServerNotifications with the specified type for the * MBean with the specified ObjectName @@ -1712,9 +1695,10 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { */ private NotificationListener getListenerWrapper(NotificationListener l, ObjectName name, - Object mbean, + DynamicMBean mbean, boolean create) { - ListenerWrapper wrapper = new ListenerWrapper(l, name, mbean); + Object resource = getResource(mbean); + ListenerWrapper wrapper = new ListenerWrapper(l, name, resource); synchronized (listenerWrappers) { WeakReference ref = listenerWrappers.get(wrapper); if (ref != null) { @@ -1758,6 +1742,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { listener.handleNotification(notification, handback); } + @Override public boolean equals(Object o) { if (!(o instanceof ListenerWrapper)) return false; @@ -1774,6 +1759,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { */ } + @Override public int hashCode() { return (System.identityHashCode(listener) ^ System.identityHashCode(mbean)); @@ -1851,4 +1837,213 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor { } } + // ------------------------------------------------------------------ + // + // Dealing with registration of special MBeans in the repository. + // + // ------------------------------------------------------------------ + + /** + * A RegistrationContext that makes it possible to perform additional + * post registration actions (or post unregistration actions) outside + * of the repository lock, once postRegister (or postDeregister) has + * been called. + * The method {@code done()} will be called in registerMBean or + * unregisterMBean, at the end. + */ + private static interface ResourceContext extends RegistrationContext { + public void done(); + /** An empty ResourceContext which does nothing **/ + public static final ResourceContext NONE = new ResourceContext() { + public void done() {} + public void registering() {} + public void unregistered() {} + }; + } + + /** + * Adds a MBean in the repository, + * sends MBeanServerNotification.REGISTRATION_NOTIFICATION, + * returns ResourceContext for special resources such as ClassLoaders + * or JMXNamespaces. For regular MBean this method returns + * ResourceContext.NONE. + * @return a ResourceContext for special resources such as ClassLoaders + * or JMXNamespaces. + */ + private ResourceContext registerWithRepository( + final Object resource, + final DynamicMBean object, + final ObjectName logicalName) + throws InstanceAlreadyExistsException, + MBeanRegistrationException { + + // Creates a registration context, if needed. + // + final ResourceContext context = + makeResourceContextFor(resource, logicalName); + + + repository.addMBean(object, logicalName, context); + // May throw InstanceAlreadyExistsException + + // --------------------- + // Send create event + // --------------------- + if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { + MBEANSERVER_LOGGER.logp(Level.FINER, + DefaultMBeanServerInterceptor.class.getName(), + "addObject", "Send create notification of object " + + logicalName.getCanonicalName()); + } + + sendNotification( + MBeanServerNotification.REGISTRATION_NOTIFICATION, + logicalName); + + return context; + } + + /** + * Removes a MBean in the repository, + * sends MBeanServerNotification.UNREGISTRATION_NOTIFICATION, + * returns ResourceContext for special resources such as ClassLoaders + * or JMXNamespaces, or null. For regular MBean this method returns + * ResourceContext.NONE. + * + * @return a ResourceContext for special resources such as ClassLoaders + * or JMXNamespaces. + */ + private ResourceContext unregisterFromRepository( + final Object resource, + final DynamicMBean object, + final ObjectName logicalName) + throws InstanceNotFoundException { + + // Creates a registration context, if needed. + // + final ResourceContext context = + makeResourceContextFor(resource, logicalName); + + + repository.remove(logicalName, context); + + // --------------------- + // Send deletion event + // --------------------- + if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { + MBEANSERVER_LOGGER.logp(Level.FINER, + DefaultMBeanServerInterceptor.class.getName(), + "unregisterMBean", "Send delete notification of object " + + logicalName.getCanonicalName()); + } + + sendNotification(MBeanServerNotification.UNREGISTRATION_NOTIFICATION, + logicalName); + return context; + } + + /** + * Registers a ClassLoader with the CLR. + * This method is called by the ResourceContext from within the + * repository lock. + * @param loader The ClassLoader. + * @param logicalName The ClassLoader MBean ObjectName. + */ + private void addClassLoader(ClassLoader loader, + final ObjectName logicalName) { + /** + * Called when the newly registered MBean is a ClassLoader + * If so, tell the ClassLoaderRepository (CLR) about it. We do + * this even if the loader is a PrivateClassLoader. In that + * case, the CLR remembers the loader for use when it is + * explicitly named (e.g. as the loader in createMBean) but + * does not add it to the list that is consulted by + * ClassLoaderRepository.loadClass. + */ + final ModifiableClassLoaderRepository clr = + instantiator.getClassLoaderRepository(); + if (clr == null) { + final RuntimeException wrapped = + new IllegalArgumentException( + "Dynamic addition of class loaders" + + " is not supported"); + throw new RuntimeOperationsException(wrapped, + "Exception occurred trying to register" + + " the MBean as a class loader"); + } + clr.addClassLoader(logicalName, loader); + } + + /** + * Unregisters a ClassLoader from the CLR. + * This method is called by the ResourceContext from within the + * repository lock. + * @param loader The ClassLoader. + * @param logicalName The ClassLoader MBean ObjectName. + */ + private void removeClassLoader(ClassLoader loader, + final ObjectName logicalName) { + /** + * Removes the MBean from the default loader repository. + */ + if (loader != server.getClass().getClassLoader()) { + final ModifiableClassLoaderRepository clr = + instantiator.getClassLoaderRepository(); + if (clr != null) { + clr.removeClassLoader(logicalName); + } + } + } + + /** + * Creates a ResourceContext for a ClassLoader MBean. + * The resource context makes it possible to add the ClassLoader to + * (ResourceContext.registering) or resp. remove the ClassLoader from + * (ResourceContext.unregistered) the CLR + * when the associated MBean is added to or resp. removed from the + * repository. + * + * @param loader The ClassLoader MBean being registered or + * unregistered. + * @param logicalName The name of the ClassLoader MBean. + * @return a ResourceContext that takes in charge the addition or removal + * of the loader to or from the CLR. + */ + private ResourceContext createClassLoaderContext( + final ClassLoader loader, + final ObjectName logicalName) { + return new ResourceContext() { + + public void registering() { + addClassLoader(loader, logicalName); + } + + public void unregistered() { + removeClassLoader(loader, logicalName); + } + + public void done() { + } + }; + } + + /** + * Creates a ResourceContext for the given resource. + * If the resource does not need a ResourceContext, returns + * ResourceContext.NONE. + * At this time, only JMXNamespaces and ClassLoaders need a + * ResourceContext. + * + * @param resource The resource being registered or unregistered. + * @param logicalName The name of the associated MBean. + * @return + */ + private ResourceContext makeResourceContextFor(Object resource, + ObjectName logicalName) { + if (resource instanceof ClassLoader) { + return createClassLoaderContext((ClassLoader) resource, + logicalName); + } + return ResourceContext.NONE; + } } diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/DynamicMBean2.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/DynamicMBean2.java index 49d49ce4c1f..270f7ad77a6 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/DynamicMBean2.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/DynamicMBean2.java @@ -25,7 +25,7 @@ package com.sun.jmx.mbeanserver; -import javax.management.DynamicMBean; +import javax.management.DynamicWrapperMBean; import javax.management.MBeanServer; import javax.management.ObjectName; @@ -35,17 +35,7 @@ import javax.management.ObjectName; * * @since 1.6 */ -public interface DynamicMBean2 extends DynamicMBean { - /** - * The resource corresponding to this MBean. This is the object whose - * class name should be reflected by the MBean's - * getMBeanInfo().getClassName() for example. For a "plain" - * DynamicMBean it will be "this". For an MBean that wraps another - * object, like javax.management.StandardMBean, it will be the wrapped - * object. - */ - public Object getResource(); - +public interface DynamicMBean2 extends DynamicWrapperMBean { /** * The name of this MBean's class, as used by permission checks. * This is typically equal to getResource().getClass().getName(). diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/Introspector.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/Introspector.java index 10826cc36f1..fb5d19125a6 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/Introspector.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/Introspector.java @@ -25,23 +25,39 @@ package com.sun.jmx.mbeanserver; +import com.sun.jmx.remote.util.EnvHelp; +import java.beans.BeanInfo; +import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Array; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; +import javax.management.AttributeNotFoundException; +import javax.management.Description; import javax.management.Descriptor; +import javax.management.DescriptorFields; import javax.management.DescriptorKey; import javax.management.DynamicMBean; import javax.management.ImmutableDescriptor; +import javax.management.MBean; import javax.management.MBeanInfo; +import javax.management.MXBean; import javax.management.NotCompliantMBeanException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.MXBeanMappingFactory; +import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; +import com.sun.jmx.mbeanserver.Util; import com.sun.jmx.remote.util.EnvHelp; import java.beans.BeanInfo; import java.beans.PropertyDescriptor; @@ -133,8 +149,12 @@ public class Introspector { } } - public static void checkCompliance(Class mbeanClass) - throws NotCompliantMBeanException { + public static void checkCompliance(Class mbeanClass) + throws NotCompliantMBeanException { + + // Check that @Resource is used correctly (if it used). + MBeanInjector.validate(mbeanClass); + // Is DynamicMBean? // if (DynamicMBean.class.isAssignableFrom(mbeanClass)) @@ -157,21 +177,39 @@ public class Introspector { } catch (NotCompliantMBeanException e) { mxbeanException = e; } + // Is @MBean or @MXBean class? + // In fact we find @MBean or @MXBean as a hacky variant of + // getStandardMBeanInterface or getMXBeanInterface. If we get here + // then nothing worked. final String msg = "MBean class " + mbeanClass.getName() + " does not implement " + - "DynamicMBean, neither follows the Standard MBean conventions (" + - mbeanException.toString() + ") nor the MXBean conventions (" + - mxbeanException.toString() + ")"; + "DynamicMBean; does not follow the Standard MBean conventions (" + + mbeanException.toString() + "); does not follow the MXBean conventions (" + + mxbeanException.toString() + "); and does not have or inherit the @" + + MBean.class.getSimpleName() + " or @" + MXBean.class.getSimpleName() + + " annotation"; throw new NotCompliantMBeanException(msg); } + /** + *

Make a DynamicMBean out of the existing MBean object. The object + * may already be a DynamicMBean, or it may be a Standard MBean or + * MXBean, possibly defined using {@code @MBean} or {@code @MXBean}.

+ * @param mbean the object to convert to a DynamicMBean. + * @param a type parameter defined for implementation convenience + * (which would have to be removed if this method were part of the public + * API). + * @return the converted DynamicMBean. + * @throws NotCompliantMBeanException if {@code mbean} is not a compliant + * MBean object, including the case where it is null. + */ public static DynamicMBean makeDynamicMBean(T mbean) throws NotCompliantMBeanException { if (mbean == null) throw new NotCompliantMBeanException("Null MBean object"); if (mbean instanceof DynamicMBean) return (DynamicMBean) mbean; - final Class mbeanClass = mbean.getClass(); + final Class mbeanClass = mbean.getClass(); Class c = null; try { c = Util.cast(getStandardMBeanInterface(mbeanClass)); @@ -270,7 +308,7 @@ public class Introspector { * Return null if the MBean is a DynamicMBean, * or if no MBean interface is found. */ - public static Class getMBeanInterface(Class baseClass) { + public static Class getMBeanInterface(Class baseClass) { // Check if the given class implements the MBean interface // or the Dynamic MBean interface if (isDynamic(baseClass)) return null; @@ -291,10 +329,12 @@ public class Introspector { * @throws NotCompliantMBeanException The specified class is * not a JMX compliant Standard MBean. */ - public static Class getStandardMBeanInterface(Class baseClass) - throws NotCompliantMBeanException { - Class current = baseClass; - Class mbeanInterface = null; + public static Class getStandardMBeanInterface(Class baseClass) + throws NotCompliantMBeanException { + if (baseClass.isAnnotationPresent(MBean.class)) + return baseClass; + Class current = baseClass; + Class mbeanInterface = null; while (current != null) { mbeanInterface = findMBeanInterface(current, current.getName()); @@ -321,8 +361,10 @@ public class Introspector { * @throws NotCompliantMBeanException The specified class is * not a JMX compliant MXBean. */ - public static Class getMXBeanInterface(Class baseClass) + public static Class getMXBeanInterface(Class baseClass) throws NotCompliantMBeanException { + if (hasMXBeanAnnotation(baseClass)) + return baseClass; try { return MXBeanSupport.findMXBeanInterface(baseClass); } catch (Exception e) { @@ -345,19 +387,24 @@ public class Introspector { * ------------------------------------------ */ + static boolean hasMXBeanAnnotation(Class c) { + MXBean m = c.getAnnotation(MXBean.class); + return (m != null && m.value()); + } /** * Try to find the MBean interface corresponding to the class aName * - i.e. aNameMBean, from within aClass and its superclasses. **/ - private static Class findMBeanInterface(Class aClass, String aName) { - Class current = aClass; + private static Class findMBeanInterface( + Class aClass, String aName) { + Class current = aClass; while (current != null) { - final Class[] interfaces = current.getInterfaces(); + final Class[] interfaces = current.getInterfaces(); final int len = interfaces.length; for (int i=0;i inter = Util.cast(interfaces[i]); + inter = implementsMBean(inter, aName); if (inter != null) return inter; } current = current.getSuperclass(); @@ -365,6 +412,48 @@ public class Introspector { return null; } + public static String descriptionForElement(AnnotatedElement elmt) { + if (elmt == null) + return null; + Description d = elmt.getAnnotation(Description.class); + if (d == null) + return null; + return d.value(); + } + + public static String descriptionForParameter( + Annotation[] parameterAnnotations) { + for (Annotation a : parameterAnnotations) { + if (a instanceof Description) + return ((Description) a).value(); + } + return null; + } + + public static String nameForParameter( + Annotation[] parameterAnnotations) { + for (Annotation a : parameterAnnotations) { + Class ac = a.annotationType(); + // You'd really have to go out of your way to have more than + // one @Name annotation, so we don't check for that. + if (ac.getSimpleName().equals("Name")) { + try { + Method value = ac.getMethod("value"); + if (value.getReturnType() == String.class && + value.getParameterTypes().length == 0) { + return (String) value.invoke(a); + } + } catch (Exception e) { + MBEANSERVER_LOGGER.log( + Level.WARNING, + "Unexpected exception getting @" + ac.getName(), + e); + } + } + } + return null; + } + public static Descriptor descriptorForElement(final AnnotatedElement elmt) { if (elmt == null) return ImmutableDescriptor.EMPTY_DESCRIPTOR; @@ -372,41 +461,18 @@ public class Introspector { return descriptorForAnnotations(annots); } + public static Descriptor descriptorForAnnotation(Annotation annot) { + return descriptorForAnnotations(new Annotation[] {annot}); + } + public static Descriptor descriptorForAnnotations(Annotation[] annots) { if (annots.length == 0) return ImmutableDescriptor.EMPTY_DESCRIPTOR; Map descriptorMap = new HashMap(); for (Annotation a : annots) { - Class c = a.annotationType(); - Method[] elements = c.getMethods(); - for (Method element : elements) { - DescriptorKey key = element.getAnnotation(DescriptorKey.class); - if (key != null) { - String name = key.value(); - Object value; - try { - value = element.invoke(a); - } catch (RuntimeException e) { - // we don't expect this - except for possibly - // security exceptions? - // RuntimeExceptions shouldn't be "UndeclaredThrowable". - // anyway... - // - throw e; - } catch (Exception e) { - // we don't expect this - throw new UndeclaredThrowableException(e); - } - value = annotationToField(value); - Object oldValue = descriptorMap.put(name, value); - if (oldValue != null && !equals(oldValue, value)) { - final String msg = - "Inconsistent values for descriptor field " + name + - " from annotations: " + value + " :: " + oldValue; - throw new IllegalArgumentException(msg); - } - } - } + if (a instanceof DescriptorFields) + addDescriptorFieldsToMap(descriptorMap, (DescriptorFields) a); + addAnnotationFieldsToMap(descriptorMap, a); } if (descriptorMap.isEmpty()) @@ -415,6 +481,62 @@ public class Introspector { return new ImmutableDescriptor(descriptorMap); } + private static void addDescriptorFieldsToMap( + Map descriptorMap, DescriptorFields df) { + for (String field : df.value()) { + int eq = field.indexOf('='); + if (eq < 0) { + throw new IllegalArgumentException( + "@DescriptorFields string must contain '=': " + + field); + } + String name = field.substring(0, eq); + String value = field.substring(eq + 1); + addToMap(descriptorMap, name, value); + } + } + + private static void addAnnotationFieldsToMap( + Map descriptorMap, Annotation a) { + Class c = a.annotationType(); + Method[] elements = c.getMethods(); + for (Method element : elements) { + DescriptorKey key = element.getAnnotation(DescriptorKey.class); + if (key != null) { + String name = key.value(); + Object value; + try { + value = element.invoke(a); + } catch (RuntimeException e) { + // we don't expect this - except for possibly + // security exceptions? + // RuntimeExceptions shouldn't be "UndeclaredThrowable". + // anyway... + throw e; + } catch (Exception e) { + // we don't expect this + throw new UndeclaredThrowableException(e); + } + if (!key.omitIfDefault() || + !equals(value, element.getDefaultValue())) { + value = annotationToField(value); + addToMap(descriptorMap, name, value); + } + } + } + } + + private static void addToMap( + Map descriptorMap, String name, Object value) { + Object oldValue = descriptorMap.put(name, value); + if (oldValue != null && !equals(oldValue, value)) { + final String msg = + "Inconsistent values for descriptor field " + name + + " from annotations: " + value + " :: " + oldValue; + throw new IllegalArgumentException(msg); + } + } + /** * Throws a NotCompliantMBeanException or a SecurityException. * @param notCompliant the class which was under examination @@ -473,8 +595,13 @@ public class Introspector { // The only other possibility is that the value is another // annotation, or that the language has evolved since this code // was written. We don't allow for either of those currently. + // If it is indeed another annotation, then x will be a proxy + // with an unhelpful name like $Proxy2. So we extract the + // proxy's interface to use that in the exception message. + if (Proxy.isProxyClass(c)) + c = c.getInterfaces()[0]; // array "can't be empty" throw new IllegalArgumentException("Illegal type for annotation " + - "element: " + x.getClass().getName()); + "element using @DescriptorKey: " + c.getName()); } // This must be consistent with the check for duplicate field values in @@ -490,15 +617,15 @@ public class Introspector { * @param c The interface to be tested * @param clName The name of the class implementing this interface */ - private static Class implementsMBean(Class c, String clName) { + private static Class implementsMBean(Class c, String clName) { String clMBeanName = clName + "MBean"; if (c.getName().equals(clMBeanName)) { return c; } - Class[] interfaces = c.getInterfaces(); + Class[] interfaces = c.getInterfaces(); for (int i = 0;i < interfaces.length; i++) { if (interfaces[i].getName().equals(clMBeanName)) - return interfaces[i]; + return Util.cast(interfaces[i]); } return null; diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanAnalyzer.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanAnalyzer.java index 58328e1ce8a..0661a94a404 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanAnalyzer.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanAnalyzer.java @@ -33,6 +33,10 @@ import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Set; +import javax.management.MBean; +import javax.management.MXBean; +import javax.management.ManagedAttribute; +import javax.management.ManagedOperation; import javax.management.NotCompliantMBeanException; /** @@ -125,18 +129,26 @@ class MBeanAnalyzer { for (Method m : methods) { final String name = m.getName(); final int nParams = m.getParameterTypes().length; + final boolean managedOp = m.isAnnotationPresent(ManagedOperation.class); + final boolean managedAttr = m.isAnnotationPresent(ManagedAttribute.class); + if (managedOp && managedAttr) { + throw new NotCompliantMBeanException("Method " + name + + " has both @ManagedOperation and @ManagedAttribute"); + } final M cm = introspector.mFrom(m); String attrName = ""; - if (name.startsWith("get")) - attrName = name.substring(3); - else if (name.startsWith("is") - && m.getReturnType() == boolean.class) - attrName = name.substring(2); + if (!managedOp) { + 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 && nParams == 0 - && m.getReturnType() != void.class) { + && m.getReturnType() != void.class && !managedOp) { // It's a getter // Check we don't have both isX and getX AttrMethods am = attrMap.get(attrName); @@ -153,7 +165,7 @@ class MBeanAnalyzer { attrMap.put(attrName, am); } else if (name.startsWith("set") && name.length() > 3 && nParams == 1 && - m.getReturnType() == void.class) { + m.getReturnType() == void.class && !managedOp) { // It's a setter attrName = name.substring(3); AttrMethods am = attrMap.get(attrName); @@ -166,6 +178,9 @@ class MBeanAnalyzer { } am.setter = cm; attrMap.put(attrName, am); + } else if (managedAttr) { + throw new NotCompliantMBeanException("Method " + name + + " has @ManagedAttribute but is not a valid getter or setter"); } else { // It's an operation List cms = opMap.get(name); diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java new file mode 100644 index 00000000000..4831134f6af --- /dev/null +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java @@ -0,0 +1,291 @@ +/* + * 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.mbeanserver; + +import java.lang.ref.WeakReference; +import java.security.PrivilegedAction; +import java.util.Map; +import java.util.WeakHashMap; +import javax.annotation.Resource; +import javax.management.MBeanServer; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectName; + +import static com.sun.jmx.mbeanserver.Util.newMap; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.security.AccessController; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.management.SendNotification; + +public class MBeanInjector { + private static Class[] injectedClasses = { + MBeanServer.class, ObjectName.class, SendNotification.class, + }; + + public static void inject(Object mbean, MBeanServer mbs, ObjectName name) + throws Exception { + ClassInjector injector = injectorForClass(mbean.getClass()); + injector.inject(mbean, MBeanServer.class, mbs); + injector.inject(mbean, ObjectName.class, name); + } + + public static boolean injectsSendNotification(Object mbean) + throws NotCompliantMBeanException { + ClassInjector injector = injectorForClass(mbean.getClass()); + return injector.injects(SendNotification.class); + } + + public static void injectSendNotification(Object mbean, SendNotification sn) + throws Exception { + ClassInjector injector = injectorForClass(mbean.getClass()); + injector.inject(mbean, SendNotification.class, sn); + } + + public static void validate(Class c) throws NotCompliantMBeanException { + injectorForClass(c); + } + + private static class ClassInjector { + private Map, List> fields; + private Map, List> methods; + + ClassInjector(Class c) throws NotCompliantMBeanException { + fields = newMap(); + methods = newMap(); + + Class sup = c.getSuperclass(); + ClassInjector supInjector; + if (sup == null) { + supInjector = null; + } else { + supInjector = injectorForClass(sup); + fields.putAll(supInjector.fields); + methods.putAll(supInjector.methods); + } + + addMembers(c); + eliminateOverriddenMethods(); + + // If we haven't added any new fields or methods to what we + // inherited, then we can share the parent's maps. + if (supInjector != null) { + if (fields.equals(supInjector.fields)) + fields = supInjector.fields; + if (methods.equals(supInjector.methods)) + methods = supInjector.methods; + } + } + + boolean injects(Class c) { + return (fields.get(c) != null || methods.get(c) != null); + } + + void inject(Object instance, Class type, T resource) + throws Exception { + List fs = fields.get(type); + if (fs != null) { + for (Field f : fs) + f.set(instance, resource); + } + List ms = methods.get(type); + if (ms != null) { + for (Method m : ms) { + try { + m.invoke(instance, resource); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof Error) + throw (Error) cause; + else + throw (Exception) cause; + } + } + } + } + + private void eliminateOverriddenMethods() { + /* Covariant overriding is unlikely, but it is possible that the + * parent has a @Resource method that we override with another + * @Resource method. We don't want to invoke both methods, + * because polymorphism means we would actually invoke the same + * method twice. + */ + for (Map.Entry, List> entry : methods.entrySet()) { + List list = entry.getValue(); + list = MBeanAnalyzer.eliminateCovariantMethods(list); + entry.setValue(list); + } + } + + /* + * Find Fields or Methods within the given Class that we can inject + * resource references into. Suppose we want to know if a Field can get + * a reference to an ObjectName. We'll accept fields like this: + * + * @Resource + * private transient ObjectName name; + * + * or like this: + * + * @Resource(type = ObjectName.class) + * private transient Object name; + * + * but not like this: + * + * @Resource + * private transient Object name; + * + * (Plain @Resource is equivalent to @Resource(type = Object.class).) + * + * We don't want to inject into everything that might possibly accept + * an ObjectName reference, because examples like the last one above + * could also accept an MBeanServer reference or any other sort of + * 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 + * 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 + * meet the same criteria with respect to other types such as MBeanServer. + * + * The same logic applies mutatis mutandis to Methods such as this: + * + * @Resource + * private void setObjectName1(ObjectName name) + * @Resource(type = Object.class) + * private void setObjectName2(Object name) + */ + private void addMembers(final Class c) + throws NotCompliantMBeanException { + AccessibleObject[][] memberArrays = + AccessController.doPrivileged( + new PrivilegedAction() { + public AccessibleObject[][] run() { + return new AccessibleObject[][] { + c.getDeclaredFields(), c.getDeclaredMethods() + }; + } + }); + for (AccessibleObject[] members : memberArrays) { + for (final AccessibleObject member : members) { + Resource res = member.getAnnotation(Resource.class); + if (res == null) + continue; + + final Field field; + final Method method; + final Class memberType; + final int modifiers; + if (member instanceof Field) { + field = (Field) member; + memberType = field.getType(); + modifiers = field.getModifiers(); + method = null; + } else { + field = null; + method = (Method) member; + Class[] paramTypes = method.getParameterTypes(); + if (paramTypes.length != 1) { + throw new NotCompliantMBeanException( + "@Resource method must have exactly 1 " + + "parameter: " + method); + } + if (method.getReturnType() != void.class) { + throw new NotCompliantMBeanException( + "@Resource method must return void: " + + method); + } + memberType = paramTypes[0]; + modifiers = method.getModifiers(); + } + + if (Modifier.isStatic(modifiers)) { + throw new NotCompliantMBeanException( + "@Resource method or field cannot be static: " + + member); + } + + for (Class injectedClass : injectedClasses) { + Class[] types = {memberType, res.type()}; + boolean accept = false; + for (int i = 0; i < 2; i++) { + if (types[i] == injectedClass && + types[1 - i].isAssignableFrom(injectedClass)) { + accept = true; + break; + } + } + if (accept) { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + member.setAccessible(true); + return null; + } + }); + addToMap(fields, injectedClass, field); + addToMap(methods, injectedClass, method); + } + } + } + } + } + + private static void addToMap(Map> map, K key, V value) { + if (value == null) + return; + List list = map.get(key); + if (list == null) + list = Collections.singletonList(value); + else { + if (list.size() == 1) + list = new ArrayList(list); + list.add(value); + } + map.put(key, list); + } + } + + private static synchronized ClassInjector injectorForClass(Class c) + throws NotCompliantMBeanException { + WeakReference wr = injectorMap.get(c); + ClassInjector ci = (wr == null) ? null : wr.get(); + if (ci == null) { + ci = new ClassInjector(c); + injectorMap.put(c, new WeakReference(ci)); + } + return ci; + } + + private static Map, WeakReference> injectorMap = + new WeakHashMap, WeakReference>(); +} diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java index ab14aad19f4..55b7a6655e8 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanIntrospector.java @@ -36,20 +36,28 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.WeakHashMap; +import javax.management.Description; import javax.management.Descriptor; import javax.management.ImmutableDescriptor; import javax.management.IntrospectionException; import javax.management.InvalidAttributeValueException; +import javax.management.MBean; 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.MXBean; +import javax.management.ManagedAttribute; +import javax.management.ManagedOperation; import javax.management.NotCompliantMBeanException; import javax.management.NotificationBroadcaster; +import javax.management.NotificationInfo; +import javax.management.NotificationInfos; import javax.management.ReflectionException; /** @@ -153,6 +161,25 @@ abstract class MBeanIntrospector { abstract MBeanAttributeInfo getMBeanAttributeInfo(String attributeName, M getter, M setter) throws IntrospectionException; + final String getAttributeDescription( + String attributeName, String defaultDescription, + Method getter, Method setter) throws IntrospectionException { + String g = Introspector.descriptionForElement(getter); + String s = Introspector.descriptionForElement(setter); + if (g == null) { + if (s == null) + return defaultDescription; + else + return s; + } else if (s == null || g.equals(s)) { + return g; + } else { + throw new IntrospectionException( + "Inconsistent @Description on getter and setter for " + + "attribute " + attributeName); + } + } + /** * Construct an MBeanOperationInfo for the given operation based on * the M it was derived from. @@ -184,8 +211,12 @@ abstract class MBeanIntrospector { } void checkCompliance(Class mbeanType) throws NotCompliantMBeanException { - if (!mbeanType.isInterface()) { - throw new NotCompliantMBeanException("Not an interface: " + + if (!mbeanType.isInterface() && + !mbeanType.isAnnotationPresent(MBean.class) && + !Introspector.hasMXBeanAnnotation(mbeanType)) { + throw new NotCompliantMBeanException("Not an interface and " + + "does not have @" + MBean.class.getSimpleName() + + " or @" + MXBean.class.getSimpleName() + " annotation: " + mbeanType.getName()); } } @@ -194,7 +225,12 @@ abstract class MBeanIntrospector { * Get the methods to be analyzed to build the MBean interface. */ List getMethods(final Class mbeanType) throws Exception { - return Arrays.asList(mbeanType.getMethods()); + if (mbeanType.isInterface()) + return Arrays.asList(mbeanType.getMethods()); + + final List methods = newList(); + getAnnotatedMethods(mbeanType, methods); + return methods; } final PerInterface getPerInterface(Class mbeanInterface) @@ -232,8 +268,11 @@ abstract class MBeanIntrospector { MBeanAnalyzer analyzer) throws IntrospectionException { final MBeanInfoMaker maker = new MBeanInfoMaker(); analyzer.visit(maker); - final String description = + final String defaultDescription = "Information on the management interface of the MBean"; + String description = Introspector.descriptionForElement(mbeanInterface); + if (description == null) + description = defaultDescription; return maker.makeMBeanInfo(mbeanInterface, description); } @@ -407,7 +446,15 @@ abstract class MBeanIntrospector { throws NotCompliantMBeanException { MBeanInfo mbi = getClassMBeanInfo(resource.getClass(), perInterface); - MBeanNotificationInfo[] notifs = findNotifications(resource); + MBeanNotificationInfo[] notifs; + try { + notifs = findNotifications(resource); + } catch (RuntimeException e) { + NotCompliantMBeanException x = + new NotCompliantMBeanException(e.getMessage()); + x.initCause(e); + throw x; + } Descriptor d = getSpecificMBeanDescriptor(); boolean anyNotifs = (notifs != null && notifs.length > 0); if (!anyNotifs && ImmutableDescriptor.EMPTY_DESCRIPTOR.equals(d)) @@ -460,13 +507,43 @@ abstract class MBeanIntrospector { } } + /* + * Add to "methods" every public method that has the @ManagedAttribute + * or @ManagedOperation annotation, in the given class or any of + * its superclasses or superinterfaces. + * + * We always add superclass or superinterface methods first, so that + * the stable sort used by eliminateCovariantMethods will put the + * method from the most-derived class last. This means that we will + * see the version of the @ManagedAttribute (or ...Operation) annotation + * from that method, which might have a different description or whatever. + */ + private static void getAnnotatedMethods(Class c, List methods) + throws Exception { + Class sup = c.getSuperclass(); + if (sup != null) + getAnnotatedMethods(sup, methods); + Class[] intfs = c.getInterfaces(); + for (Class intf : intfs) + getAnnotatedMethods(intf, methods); + for (Method m : c.getMethods()) { + // We are careful not to add m if it is inherited from a parent + // class or interface, because duplicate methods lead to nasty + // behaviour in eliminateCovariantMethods. + if (m.getDeclaringClass() == c && + (m.isAnnotationPresent(ManagedAttribute.class) || + m.isAnnotationPresent(ManagedOperation.class))) + methods.add(m); + } + } + static MBeanNotificationInfo[] findNotifications(Object moi) { if (!(moi instanceof NotificationBroadcaster)) return null; MBeanNotificationInfo[] mbn = ((NotificationBroadcaster) moi).getNotificationInfo(); if (mbn == null || mbn.length == 0) - return null; + return findNotificationsFromAnnotations(moi.getClass()); MBeanNotificationInfo[] result = new MBeanNotificationInfo[mbn.length]; for (int i = 0; i < mbn.length; i++) { @@ -478,11 +555,81 @@ abstract class MBeanIntrospector { return result; } + private static MBeanNotificationInfo[] findNotificationsFromAnnotations( + Class mbeanClass) { + Class c = getAnnotatedNotificationInfoClass(mbeanClass); + if (c == null) + return null; + NotificationInfo ni = c.getAnnotation(NotificationInfo.class); + NotificationInfos nis = c.getAnnotation(NotificationInfos.class); + List list = newList(); + if (ni != null) + list.add(ni); + if (nis != null) + list.addAll(Arrays.asList(nis.value())); + if (list.isEmpty()) + return null; + List mbnis = newList(); + for (NotificationInfo x : list) { + // The Descriptor includes any fields explicitly specified by + // x.descriptorFields(), plus any fields from the contained + // @Description annotation. + Descriptor d = new ImmutableDescriptor(x.descriptorFields()); + d = ImmutableDescriptor.union( + d, Introspector.descriptorForAnnotation(x.description())); + MBeanNotificationInfo mbni = new MBeanNotificationInfo( + x.types(), x.notificationClass().getName(), + x.description().value(), d); + mbnis.add(mbni); + } + return mbnis.toArray(new MBeanNotificationInfo[mbnis.size()]); + } + + private static final Map, WeakReference>> + annotatedNotificationInfoClasses = newWeakHashMap(); + + private static Class getAnnotatedNotificationInfoClass(Class baseClass) { + synchronized (annotatedNotificationInfoClasses) { + WeakReference> wr = + annotatedNotificationInfoClasses.get(baseClass); + if (wr != null) + return wr.get(); + Class c = null; + if (baseClass.isAnnotationPresent(NotificationInfo.class) || + baseClass.isAnnotationPresent(NotificationInfos.class)) { + c = baseClass; + } else { + Class[] intfs = baseClass.getInterfaces(); + for (Class intf : intfs) { + Class c1 = getAnnotatedNotificationInfoClass(intf); + if (c1 != null) { + if (c != null) { + throw new IllegalArgumentException( + "Class " + baseClass.getName() + " inherits " + + "@NotificationInfo(s) from both " + + c.getName() + " and " + c1.getName()); + } + c = c1; + } + } + } + // Record the result of the search. If no @NotificationInfo(s) + // were found, c is null, and we store a WeakReference(null). + // This prevents us from having to search again and fail again. + annotatedNotificationInfoClasses.put(baseClass, + new WeakReference>(c)); + return c; + } + } + private static MBeanConstructorInfo[] findConstructors(Class c) { Constructor[] cons = c.getConstructors(); MBeanConstructorInfo[] mbc = new MBeanConstructorInfo[cons.length]; for (int i = 0; i < cons.length; i++) { - final String descr = "Public constructor of the MBean"; + String descr = "Public constructor of the MBean"; + Description d = cons[i].getAnnotation(Description.class); + if (d != null) + descr = d.value(); mbc[i] = new MBeanConstructorInfo(descr, cons[i]); } return mbc; diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java index d69f6e77161..33df5764998 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java @@ -263,10 +263,14 @@ public abstract class MBeanSupport return resource.getClass().getName(); } - public final Object getResource() { + public final Object getWrappedObject() { return resource; } + public final ClassLoader getWrappedClassLoader() { + return resource.getClass().getClassLoader(); + } + public final Class getMBeanInterface() { return perInterface.getMBeanInterface(); } diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanIntrospector.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanIntrospector.java index e3b037d29d8..622c765b4ae 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanIntrospector.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanIntrospector.java @@ -35,6 +35,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Map; import java.util.WeakHashMap; +import javax.management.Description; import javax.management.Descriptor; import javax.management.ImmutableDescriptor; import javax.management.IntrospectionException; @@ -43,6 +44,7 @@ import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; +import javax.management.ManagedOperation; import javax.management.NotCompliantMBeanException; import javax.management.openmbean.MXBeanMappingFactory; import javax.management.openmbean.OpenMBeanAttributeInfoSupport; @@ -180,7 +182,10 @@ class MXBeanIntrospector extends MBeanIntrospector { final boolean isWritable = (setter != null); final boolean isIs = isReadable && getName(getter).startsWith("is"); - final String description = attributeName; + final String description = getAttributeDescription( + attributeName, attributeName, + getter == null ? null : getter.getMethod(), + setter == null ? null : setter.getMethod()); final OpenType openType; final Type originalType; @@ -229,13 +234,17 @@ class MXBeanIntrospector extends MBeanIntrospector { MBeanOperationInfo getMBeanOperationInfo(String operationName, ConvertingMethod operation) { final Method method = operation.getMethod(); - final String description = operationName; + String description = operationName; /* Ideally this would be an empty string, but - OMBOperationInfo constructor forbids that. Also, we - could consult an annotation to get a useful - description. */ + OMBOperationInfo constructor forbids that. */ + Description d = method.getAnnotation(Description.class); + if (d != null) + description = d.value(); - final int impact = MBeanOperationInfo.UNKNOWN; + int impact = MBeanOperationInfo.UNKNOWN; + ManagedOperation annot = method.getAnnotation(ManagedOperation.class); + if (annot != null) + impact = annot.impact().getCode(); final OpenType returnType = operation.getOpenReturnType(); final Type originalReturnType = operation.getGenericReturnType(); @@ -247,8 +256,15 @@ class MXBeanIntrospector extends MBeanIntrospector { boolean openParameterTypes = true; Annotation[][] annots = method.getParameterAnnotations(); for (int i = 0; i < paramTypes.length; i++) { - final String paramName = "p" + i; - final String paramDescription = paramName; + String paramName = Introspector.nameForParameter(annots[i]); + if (paramName == null) + paramName = "p" + i; + + String paramDescription = + Introspector.descriptionForParameter(annots[i]); + if (paramDescription == null) + paramDescription = paramName; + final OpenType openType = paramTypes[i]; final Type originalType = originalParamTypes[i]; Descriptor descriptor = diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanSupport.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanSupport.java index cb8587450b8..34295938e32 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanSupport.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/MXBeanSupport.java @@ -161,7 +161,7 @@ public class MXBeanSupport extends MBeanSupport { synchronized (lock) { this.mxbeanLookup = MXBeanLookup.Plain.lookupFor(server); - this.mxbeanLookup.addReference(name, getResource()); + this.mxbeanLookup.addReference(name, getWrappedObject()); this.objectName = name; } } @@ -170,7 +170,7 @@ public class MXBeanSupport extends MBeanSupport { public void unregister() { synchronized (lock) { if (mxbeanLookup != null) { - if (mxbeanLookup.removeReference(objectName, getResource())) + if (mxbeanLookup.removeReference(objectName, getWrappedObject())) objectName = null; } // XXX: need to revisit the whole register/unregister logic in diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/NotifySupport.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/NotifySupport.java new file mode 100644 index 00000000000..94227370ba2 --- /dev/null +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/NotifySupport.java @@ -0,0 +1,186 @@ +/* + * 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.mbeanserver; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.DynamicMBean; +import javax.management.DynamicWrapperMBean; +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.MBeanServer; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.ReflectionException; + +/** + * Create wrappers for DynamicMBean that implement NotificationEmitter + * and SendNotification. + */ +public class NotifySupport + implements DynamicMBean2, NotificationEmitter, MBeanRegistration { + + private final DynamicMBean mbean; + private final NotificationBroadcasterSupport nbs; + + public static DynamicMBean wrap( + DynamicMBean mbean, NotificationBroadcasterSupport nbs) { + return new NotifySupport(mbean, nbs); + } + + private NotifySupport(DynamicMBean mbean, NotificationBroadcasterSupport nbs) { + this.mbean = mbean; + this.nbs = nbs; + } + + public static NotificationBroadcasterSupport getNB(DynamicMBean mbean) { + if (mbean instanceof NotifySupport) + return ((NotifySupport) mbean).nbs; + else + return null; + } + + public String getClassName() { + if (mbean instanceof DynamicMBean2) + return ((DynamicMBean2) mbean).getClassName(); + Object w = mbean; + if (w instanceof DynamicWrapperMBean) + w = ((DynamicWrapperMBean) w).getWrappedObject(); + return w.getClass().getName(); + } + + public void preRegister2(MBeanServer mbs, ObjectName name) throws Exception { + if (mbean instanceof DynamicMBean2) + ((DynamicMBean2) mbean).preRegister2(mbs, name); + } + + public void registerFailed() { + if (mbean instanceof DynamicMBean2) + ((DynamicMBean2) mbean).registerFailed(); + } + + public Object getWrappedObject() { + if (mbean instanceof DynamicWrapperMBean) + return ((DynamicWrapperMBean) mbean).getWrappedObject(); + else + return mbean; + } + + public ClassLoader getWrappedClassLoader() { + if (mbean instanceof DynamicWrapperMBean) + return ((DynamicWrapperMBean) mbean).getWrappedClassLoader(); + else + return mbean.getClass().getClassLoader(); + } + + 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 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 AttributeList getAttributes(String[] attributes) { + return mbean.getAttributes(attributes); + } + + public void removeNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object handback) throws ListenerNotFoundException { + nbs.removeNotificationListener(listener, filter, handback); + } + + public void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException { + nbs.removeNotificationListener(listener); + } + + public MBeanNotificationInfo[] getNotificationInfo() { + return nbs.getNotificationInfo(); + } + + public void addNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object handback) { + nbs.addNotificationListener(listener, filter, handback); + } + + public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { + if (mbr() != null) + return mbr().preRegister(server, name); + else + return name; + } + + public void postRegister(Boolean registrationDone) { + if (mbr() != null) + mbr().postRegister(registrationDone); + } + + public void preDeregister() throws Exception { + if (mbr() != null) + mbr().preDeregister(); + } + + public void postDeregister() { + if (mbr() != null) + mbr().postDeregister(); + } + + private MBeanRegistration mbr() { + if (mbean instanceof MBeanRegistration) + return (MBeanRegistration) mbean; + else + return null; + } +} diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/Repository.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/Repository.java index 075ce819937..5186fa39984 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/Repository.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/Repository.java @@ -29,6 +29,7 @@ import com.sun.jmx.defaults.ServiceName; import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -39,7 +40,6 @@ import java.util.Set; import javax.management.DynamicMBean; import javax.management.InstanceAlreadyExistsException; import javax.management.InstanceNotFoundException; -import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.QueryExp; import javax.management.RuntimeOperationsException; @@ -52,6 +52,27 @@ import javax.management.RuntimeOperationsException; */ public class Repository { + /** + * An interface that allows the caller to get some control + * over the registration. + * @see #addMBean + * @see #remove + */ + public interface RegistrationContext { + /** + * Called by {@link #addMBean}. + * Can throw a RuntimeOperationsException to cancel the + * registration. + */ + public void registering(); + + /** + * Called by {@link #remove}. + * Any exception thrown by this method will be ignored. + */ + public void unregistered(); + } + // Private fields --------------------------------------------> /** @@ -115,7 +136,6 @@ public class Repository { /** * Builds a new ObjectNamePattern object from an ObjectName pattern * constituents. - * @param domain pattern.getDomain(). * @param propertyListPattern pattern.isPropertyListPattern(). * @param propertyValuePattern pattern.isPropertyValuePattern(). * @param canonicalProps pattern.getCanonicalKeyPropertyListString(). @@ -216,16 +236,6 @@ public class Repository { } } - private void addNewDomMoi(final DynamicMBean object, final String dom, - final ObjectName name) { - final Map moiTb = - new HashMap(); - moiTb.put(name.getCanonicalKeyPropertyListString(), - new NamedObject(name, object)); - domainTb.put(dom, moiTb); - nbElements++; - } - /** 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 @@ -306,6 +316,50 @@ public class Repository { } } + private void addNewDomMoi(final DynamicMBean object, + final String dom, + final ObjectName name, + final RegistrationContext context) { + final Map moiTb = + new HashMap(); + final String key = name.getCanonicalKeyPropertyListString(); + addMoiToTb(object,name,key,moiTb,context); + domainTb.put(dom, moiTb); + nbElements++; + } + + private void registering(RegistrationContext context) { + if (context == null) return; + try { + context.registering(); + } catch (RuntimeOperationsException x) { + throw x; + } catch (RuntimeException x) { + throw new RuntimeOperationsException(x); + } + } + + private void unregistering(RegistrationContext context, ObjectName name) { + if (context == null) return; + try { + context.unregistered(); + } catch (Exception x) { + // shouldn't come here... + MBEANSERVER_LOGGER.log(Level.FINE, + "Unexpected exception while unregistering "+name, + x); + } + } + + private void addMoiToTb(final DynamicMBean object, + final ObjectName name, + final String key, + final Map moiTb, + final RegistrationContext context) { + registering(context); + moiTb.put(key,new NamedObject(name, object)); + } + /** * Retrieves the named object contained in repository * from the given objectname. @@ -355,12 +409,12 @@ public class Repository { domainTb = new HashMap>(5); if (domain != null && domain.length() != 0) - this.domain = domain; + this.domain = domain.intern(); // we use == domain later on... else this.domain = ServiceName.DOMAIN; - // Creates an new hastable for the default domain - domainTb.put(this.domain.intern(), new HashMap()); + // Creates a new hashtable for the default domain + domainTb.put(this.domain, new HashMap()); } /** @@ -395,10 +449,21 @@ public class Repository { /** * Stores an MBean associated with its object name in the repository. * - * @param object MBean to be stored in the repository. - * @param name MBean object name. + * @param object MBean to be stored in the repository. + * @param name MBean object name. + * @param context A registration context. If non null, the repository + * will call {@link RegistrationContext#registering() + * context.registering()} from within the repository + * lock, when it has determined that the {@code object} + * can be stored in the repository with that {@code name}. + * If {@link RegistrationContext#registering() + * context.registering()} throws an exception, the + * operation is abandonned, the MBean is not added to the + * repository, and a {@link RuntimeOperationsException} + * is thrown. */ - public void addMBean(final DynamicMBean object, ObjectName name) + public void addMBean(final DynamicMBean object, ObjectName name, + final RegistrationContext context) throws InstanceAlreadyExistsException { if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) { @@ -431,7 +496,7 @@ public class Repository { lock.writeLock().lock(); try { - // Domain cannot be JMImplementation if entry does not exists + // Domain cannot be JMImplementation if entry does not exist if ( !to_default_domain && dom.equals("JMImplementation") && domainTb.containsKey("JMImplementation")) { @@ -440,21 +505,21 @@ public class Repository { "Repository: domain name cannot be JMImplementation")); } - // If domain not already exists, add it to the hash table + // If domain does not already exist, add it to the hash table final Map moiTb = domainTb.get(dom); if (moiTb == null) { - addNewDomMoi(object, dom, name); + addNewDomMoi(object, dom, name, context); return; - } - - // Add instance if not already present - String cstr = name.getCanonicalKeyPropertyListString(); - NamedObject elmt= moiTb.get(cstr); - if (elmt != null) { - throw new InstanceAlreadyExistsException(name.toString()); } else { - nbElements++; - moiTb.put(cstr, new NamedObject(name, object)); + // Add instance if not already present + String cstr = name.getCanonicalKeyPropertyListString(); + NamedObject elmt= moiTb.get(cstr); + if (elmt != null) { + throw new InstanceAlreadyExistsException(name.toString()); + } else { + nbElements++; + addMoiToTb(object,name,cstr,moiTb,context); + } } } finally { @@ -533,7 +598,7 @@ public class Repository { // ":*", ":[key=value],*" : names in defaultDomain // "domain:*", "domain:[key=value],*" : names in the specified domain - // Surely one of the most frequent case ... query on the whole world + // Surely one of the most frequent cases ... query on the whole world ObjectName name; if (pattern == null || pattern.getCanonicalName().length() == 0 || @@ -546,8 +611,7 @@ public class Repository { // If pattern is not a pattern, retrieve this mbean ! if (!name.isPattern()) { - final NamedObject no; - no = retrieveNamedObject(name); + final NamedObject no = retrieveNamedObject(name); if (no != null) result.add(no); return result; } @@ -577,12 +641,22 @@ public class Repository { return result; } + if (!name.isDomainPattern()) { + final Map moiTb = domainTb.get(name.getDomain()); + if (moiTb == null) return Collections.emptySet(); + if (allNames) + result.addAll(moiTb.values()); + else + addAllMatching(moiTb, result, namePattern); + return result; + } + // Pattern matching in the domain name (*, ?) char[] dom2Match = name.getDomain().toCharArray(); - for (String domain : domainTb.keySet()) { - char[] theDom = domain.toCharArray(); + for (String dom : domainTb.keySet()) { + char[] theDom = dom.toCharArray(); if (wildmatch(theDom, dom2Match)) { - final Map moiTb = domainTb.get(domain); + final Map moiTb = domainTb.get(dom); if (allNames) result.addAll(moiTb.values()); else @@ -599,11 +673,21 @@ public class Repository { * Removes an MBean from the repository. * * @param name name of the MBean to remove. + * @param context A registration context. If non null, the repository + * will call {@link RegistrationContext#unregistered() + * context.unregistered()} from within the repository + * lock, just after the mbean associated with + * {@code name} is removed from the repository. + * If {@link RegistrationContext#unregistered() + * context.unregistered()} is not expected to throw any + * exception. If it does, the exception is logged + * and swallowed. * * @exception InstanceNotFoundException The MBean does not exist in * the repository. */ - public void remove(final ObjectName name) + public void remove(final ObjectName name, + final RegistrationContext context) throws InstanceNotFoundException { // Debugging stuff @@ -645,6 +729,9 @@ public class Repository { if (dom == domain) domainTb.put(domain, new HashMap()); } + + unregistering(context,name); + } finally { lock.writeLock().unlock(); } diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanIntrospector.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanIntrospector.java index 2237a5192cd..aca58c32f11 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanIntrospector.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanIntrospector.java @@ -35,6 +35,7 @@ import javax.management.IntrospectionException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanOperationInfo; +import javax.management.ManagedOperation; import javax.management.NotCompliantMBeanException; import javax.management.NotificationBroadcaster; import javax.management.NotificationBroadcasterSupport; @@ -118,22 +119,32 @@ class StandardMBeanIntrospector extends MBeanIntrospector { @Override MBeanAttributeInfo getMBeanAttributeInfo(String attributeName, - Method getter, Method setter) { + Method getter, Method setter) throws IntrospectionException { - final String description = "Attribute exposed for management"; - try { - return new MBeanAttributeInfo(attributeName, description, - getter, setter); - } catch (IntrospectionException e) { - throw new RuntimeException(e); // should not happen - } + String description = getAttributeDescription( + attributeName, "Attribute exposed for management", + getter, setter); + return new MBeanAttributeInfo(attributeName, description, + getter, setter); } @Override MBeanOperationInfo getMBeanOperationInfo(String operationName, Method operation) { - final String description = "Operation exposed for management"; - return new MBeanOperationInfo(description, operation); + final String defaultDescription = "Operation exposed for management"; + String description = Introspector.descriptionForElement(operation); + if (description == null) + description = defaultDescription; + + int impact = MBeanOperationInfo.UNKNOWN; + ManagedOperation annot = operation.getAnnotation(ManagedOperation.class); + if (annot != null) + impact = annot.impact().getCode(); + + MBeanOperationInfo mboi = new MBeanOperationInfo(description, operation); + return new MBeanOperationInfo( + mboi.getName(), mboi.getDescription(), mboi.getSignature(), + mboi.getReturnType(), impact, mboi.getDescriptor()); } @Override diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanSupport.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanSupport.java index 131c5341b16..448a5b6be86 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanSupport.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/StandardMBeanSupport.java @@ -41,26 +41,24 @@ import javax.management.openmbean.MXBeanMappingFactory; public class StandardMBeanSupport extends MBeanSupport { /** -

Construct a Standard MBean that wraps the given resource using the - given Standard MBean interface.

- - @param resource the underlying resource for the new MBean. - - @param mbeanInterface the interface to be used to determine - the MBean's management interface. - - @param a type parameter that allows the compiler to check - that {@code resource} implements {@code mbeanInterface}, - provided that {@code mbeanInterface} is a class constant like - {@code SomeMBean.class}. - - @throws IllegalArgumentException if {@code resource} is null or - if it does not implement the class {@code mbeanInterface} or if - that class is not a valid Standard MBean interface. - */ - public StandardMBeanSupport(T resource, Class mbeanInterface) + *

Construct a Standard MBean that wraps the given resource using the + * given Standard MBean interface.

+ * + * @param resource the underlying resource for the new MBean. + * @param mbeanInterfaceType the class or interface to be used to determine + * the MBean's management interface. An interface if this is a + * classic Standard MBean; a class if this is a {@code @ManagedResource}. + * @param a type parameter that allows the compiler to check + * that {@code resource} implements {@code mbeanInterfaceType}, + * provided that {@code mbeanInterfaceType} is a class constant like + * {@code SomeMBean.class}. + * @throws IllegalArgumentException if {@code resource} is null or + * if it does not implement the class {@code mbeanInterfaceType} or if + * that class is not a valid Standard MBean interface. + */ + public StandardMBeanSupport(T resource, Class mbeanInterfaceType) throws NotCompliantMBeanException { - super(resource, mbeanInterface, (MXBeanMappingFactory) null); + super(resource, mbeanInterfaceType, (MXBeanMappingFactory) null); } @Override @@ -86,13 +84,14 @@ public class StandardMBeanSupport extends MBeanSupport { @Override public MBeanInfo getMBeanInfo() { MBeanInfo mbi = super.getMBeanInfo(); - Class resourceClass = getResource().getClass(); - if (StandardMBeanIntrospector.isDefinitelyImmutableInfo(resourceClass)) + Class resourceClass = getWrappedObject().getClass(); + if (!getMBeanInterface().isInterface() || + StandardMBeanIntrospector.isDefinitelyImmutableInfo(resourceClass)) return mbi; return new MBeanInfo(mbi.getClassName(), mbi.getDescription(), mbi.getAttributes(), mbi.getConstructors(), mbi.getOperations(), - MBeanIntrospector.findNotifications(getResource()), + MBeanIntrospector.findNotifications(getWrappedObject()), mbi.getDescriptor()); } } diff --git a/jdk/src/share/classes/com/sun/jmx/mbeanserver/Util.java b/jdk/src/share/classes/com/sun/jmx/mbeanserver/Util.java index f2dbe60bbdf..b7d31ed0c9e 100644 --- a/jdk/src/share/classes/com/sun/jmx/mbeanserver/Util.java +++ b/jdk/src/share/classes/com/sun/jmx/mbeanserver/Util.java @@ -38,6 +38,7 @@ import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; +import java.util.WeakHashMap; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; @@ -71,6 +72,10 @@ public class Util { return new LinkedHashMap(); } + static WeakHashMap newWeakHashMap() { + return new WeakHashMap(); + } + static Set newSet() { return new HashSet(); } diff --git a/jdk/src/share/classes/javax/management/BinaryRelQueryExp.java b/jdk/src/share/classes/javax/management/BinaryRelQueryExp.java index a624850c5d5..7874b8e2d6c 100644 --- a/jdk/src/share/classes/javax/management/BinaryRelQueryExp.java +++ b/jdk/src/share/classes/javax/management/BinaryRelQueryExp.java @@ -192,6 +192,7 @@ class BinaryRelQueryExp extends QueryEval implements QueryExp { return "(" + exp1 + ") " + relOpString() + " (" + exp2 + ")"; } + @Override String toQueryString() { return exp1 + " " + relOpString() + " " + exp2; } diff --git a/jdk/src/share/classes/javax/management/Description.java b/jdk/src/share/classes/javax/management/Description.java new file mode 100644 index 00000000000..a0bf96d9b1d --- /dev/null +++ b/jdk/src/share/classes/javax/management/Description.java @@ -0,0 +1,180 @@ +/* + * 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; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.ResourceBundle; + +/** + *

The textual description of an MBean or part of an MBean. This + * description is intended to be displayed to users to help them + * understand what the MBean does. Ultimately it will be the value of + * the {@code getDescription()} method of an {@link MBeanInfo}, {@link + * MBeanAttributeInfo}, or similar.

+ * + *

This annotation applies to Standard MBean interfaces and to + * MXBean interfaces, as well as to MBean classes defined using the + * {@link MBean @MBean} or {@link MXBean @MXBean} annotations. For + * example, a Standard MBean might be defined like this:

+ * + *
+ * {@code @Description}("Application configuration")
+ * public interface ConfigurationMBean {
+ *     {@code @Description}("Cache size in bytes")
+ *     public int getCacheSize();
+ *     public void setCacheSize(int size);
+ *
+ *     {@code @Description}("Last time the configuration was changed, " +
+ *                  "in milliseconds since 1 Jan 1970")
+ *     public long getLastChangedTime();
+ *
+ *     {@code @Description}("Save the configuration to a file")
+ *     public void save(
+ *         {@code @Description}("Optional name of the file, or null for the default name")
+ *         String fileName);
+ * }
+ * 
+ * + *

The {@code MBeanInfo} for this MBean will have a {@link + * MBeanInfo#getDescription() getDescription()} that is {@code + * "Application configuration"}. It will contain an {@code + * MBeanAttributeInfo} for the {@code CacheSize} attribute that is + * defined by the methods {@code getCacheSize} and {@code + * setCacheSize}, and another {@code MBeanAttributeInfo} for {@code + * LastChangedTime}. The {@link MBeanAttributeInfo#getDescription() + * getDescription()} for {@code CacheSize} will be {@code "Cache size + * in bytes"}. Notice that there is no need to add a + * {@code @Description} to both {@code getCacheSize} and {@code + * setCacheSize} - either alone will do. But if you do add a + * {@code @Description} to both, it must be the same.

+ * + *

The {@code MBeanInfo} will also contain an {@link + * MBeanOperationInfo} where {@link + * MBeanOperationInfo#getDescription() getDescription()} is {@code + * "Save the configuration to a file"}. This {@code + * MBeanOperationInfo} will contain an {@link MBeanParameterInfo} + * where {@link MBeanParameterInfo#getDescription() getDescription()} + * is {@code "Optional name of the file, or null for the default + * name"}.

+ * + *

The {@code @Description} annotation can also be applied to the + * public constructors of the implementation class. Continuing the + * above example, the {@code Configuration} class implementing {@code + * ConfigurationMBean} might look like this:

+ * + *
+ * public class Configuration implements ConfigurationMBean {
+ *     {@code @Description}("A Configuration MBean with the default file name")
+ *     public Configuration() {
+ *         this(DEFAULT_FILE_NAME);
+ *     }
+ *
+ *     {@code @Description}("A Configuration MBean with a specified file name")
+ *     public Configuration(
+ *         {@code @Description}("Name of the file the configuration is stored in")
+ *         String fileName) {...}
+ *     ...
+ * }
+ * 
+ * + *

The {@code @Description} annotation also works in MBeans that + * are defined using the {@code @MBean} or {@code @MXBean} annotation + * on classes. Here is an alternative implementation of {@code + * Configuration} that does not use an {@code ConfigurationMBean} + * interface.

+ * + *
+ * {@code @MBean}
+ * {@code @Description}("Application configuration")
+ * public class Configuration {
+ *     {@code @Description}("A Configuration MBean with the default file name")
+ *     public Configuration() {
+ *         this(DEFAULT_FILE_NAME);
+ *     }
+ *
+ *     {@code @Description}("A Configuration MBean with a specified file name")
+ *     public Configuration(
+ *         {@code @Description}("Name of the file the configuration is stored in")
+ *         String fileName) {...}
+ *
+ *     {@code @ManagedAttribute}
+ *     {@code @Description}("Cache size in bytes")
+ *     public int getCacheSize() {...}
+ *     {@code @ManagedAttribute}
+ *     public void setCacheSize(int size) {...}
+ *
+ *     {@code @ManagedOperation}
+ *     {@code @Description}("Last time the configuration was changed, " +
+ *                  "in milliseconds since 1 Jan 1970")
+ *     public long getLastChangedTime() {...}
+ *
+ *     {@code @ManagedOperation}
+ *     {@code @Description}("Save the configuration to a file")
+ *     public void save(
+ *         {@code @Description}("Optional name of the file, or null for the default name")
+ *         String fileName) {...}
+ *     ...
+ * }
+ * 
+ */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, + ElementType.TYPE}) +public @interface Description { + /** + *

The description.

+ */ + String value(); + + /** + *

The base name for the {@link ResourceBundle} in which the key given in + * the {@code descriptionResourceKey} field can be found, for example + * {@code "com.example.myapp.MBeanResources"}. If a non-default value + * is supplied for this element, it will appear in the + * {@code Descriptor} for the annotated item.

+ */ + @DescriptorKey( + value = "descriptionResourceBundleBaseName", omitIfDefault = true) + String bundleBaseName() default ""; + + /** + *

A resource key for the description of this element. In + * conjunction with the {@link #bundleBaseName bundleBaseName}, + * this can be used to find a localized version of the description. + * If a non-default value + * is supplied for this element, it will appear in the + * {@code Descriptor} for the annotated item.

+ */ + @DescriptorKey(value = "descriptionResourceKey", omitIfDefault = true) + String key() default ""; +} diff --git a/jdk/src/share/classes/javax/management/Descriptor.java b/jdk/src/share/classes/javax/management/Descriptor.java index 64bb3eccb23..9aa992486a0 100644 --- a/jdk/src/share/classes/javax/management/Descriptor.java +++ b/jdk/src/share/classes/javax/management/Descriptor.java @@ -38,6 +38,7 @@ import java.util.Arrays; import java.util.ResourceBundle; import javax.management.openmbean.CompositeData; +import javax.management.openmbean.MXBeanMappingFactory; import javax.management.openmbean.OpenMBeanAttributeInfoSupport; import javax.management.openmbean.OpenMBeanOperationInfoSupport; import javax.management.openmbean.OpenMBeanParameterInfoSupport; @@ -117,21 +118,19 @@ import javax.management.openmbean.OpenType; * deprecation, for example {@code "1.3 Replaced by the Capacity * attribute"}. * - * descriptionResource
BundleBaseNameStringAny + * + * descriptionResource
BundleBaseNameStringAny * * The base name for the {@link ResourceBundle} in which the key given in * the {@code descriptionResourceKey} field can be found, for example - * {@code "com.example.myapp.MBeanResources"}. The meaning of this - * field is defined by this specification but the field is not set or - * used by the JMX API itself. + * {@code "com.example.myapp.MBeanResources"}. * - * descriptionResourceKeyStringAny + * + * descriptionResourceKeyStringAny * * A resource key for the description of this element. In * conjunction with the {@code descriptionResourceBundleBaseName}, - * this can be used to find a localized version of the description. - * The meaning of this field is defined by this specification but the - * field is not set or used by the JMX API itself. + * this can be used to find a localized version of the description. * * enabledString * MBeanAttributeInfo
MBeanNotificationInfo
MBeanOperationInfo @@ -216,6 +215,14 @@ import javax.management.openmbean.OpenType; * StandardMBean} class will have this field in its MBeanInfo * Descriptor. * + * mxbeanMappingFactoryClass + * String + * MBeanInfo + * + * The name of the {@link MXBeanMappingFactory} class that was used for this + * MXBean, if it was not the {@linkplain MXBeanMappingFactory#DEFAULT default} + * one. + * * openType{@link OpenType} * MBeanAttributeInfo
MBeanOperationInfo
MBeanParameterInfo * diff --git a/jdk/src/share/classes/javax/management/DescriptorFields.java b/jdk/src/share/classes/javax/management/DescriptorFields.java new file mode 100644 index 00000000000..95a4b3a6df1 --- /dev/null +++ b/jdk/src/share/classes/javax/management/DescriptorFields.java @@ -0,0 +1,137 @@ +/* + * 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; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + *

Annotation that adds fields to a {@link Descriptor}. This can be the + * Descriptor for an MBean, or for an attribute, operation, or constructor + * in an MBean, or for a parameter of an operation or constructor.

+ * + *

Consider this Standard MBean interface, for example:

+ * + *
+ * public interface CacheControlMBean {
+ *     @DescriptorFields("units=bytes")
+ *     public long getCacheSize();
+ * }
+ * 
+ * + *

When a Standard MBean is made using this interface, the usual rules + * mean that it will have an attribute called {@code CacheSize} of type + * {@code long}. The {@code DescriptorFields} annotation will ensure + * that the {@link MBeanAttributeInfo} for this attribute will have a + * {@code Descriptor} that has a field called {@code units} with + * corresponding value {@code bytes}.

+ * + *

Similarly, if the interface looks like this:

+ * + *
+ * public interface CacheControlMBean {
+ *     @DescriptorFields({"units=bytes", "since=1.5"})
+ *     public long getCacheSize();
+ * }
+ * 
+ * + *

then the resulting {@code Descriptor} will contain the following + * fields:

+ * + * + * + * + * + *
NameValue
units"bytes"
since"1.5"
+ * + *

The {@code @DescriptorFields} annotation can be applied to:

+ * + *
    + *
  • a Standard MBean or MXBean interface; + *
  • a method in such an interface; + *
  • a parameter of a method in a Standard MBean or MXBean interface + * when that method is an operation (not a getter or setter for an attribute); + *
  • a public constructor in the class that implements a Standard MBean + * or MXBean; + *
  • a parameter in such a constructor. + *
+ * + *

Other uses of the annotation will either fail to compile or be + * ignored.

+ * + *

Interface annotations are checked only on the exact interface + * that defines the management interface of a Standard MBean or an + * MXBean, not on its parent interfaces. Method annotations are + * checked only in the most specific interface in which the method + * appears; in other words, if a child interface overrides a method + * from a parent interface, only {@code @DescriptorFields} annotations in + * the method in the child interface are considered. + * + *

The Descriptor fields contributed in this way must be consistent + * with each other and with any fields contributed by {@link + * DescriptorKey @DescriptorKey} annotations. That is, two + * different annotations, or two members of the same annotation, must + * not define a different value for the same Descriptor field. Fields + * from annotations on a getter method must also be consistent with + * fields from annotations on the corresponding setter method.

+ * + *

The Descriptor resulting from these annotations will be merged + * with any Descriptor fields provided by the implementation, such as + * the {@code + * immutableInfo} field for an MBean. The fields from the annotations + * must be consistent with these fields provided by the implementation.

+ * + *

{@literal @DescriptorFields and @DescriptorKey}

+ * + *

The {@link DescriptorKey @DescriptorKey} annotation provides + * another way to use annotations to define Descriptor fields. + * @DescriptorKey requires more work but is also more + * robust, because there is less risk of mistakes such as misspelling + * the name of the field or giving an invalid value. + * @DescriptorFields is more convenient but includes + * those risks. @DescriptorFields is more + * appropriate for occasional use, but for a Descriptor field that you + * add in many places, you should consider a purpose-built annotation + * using @DescriptorKey. + * + * @since 1.7 + */ +@Documented +@Inherited // for @MBean and @MXBean classes +@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, + ElementType.PARAMETER, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface DescriptorFields { + /** + *

The descriptor fields. Each element of the string looks like + * {@code "name=value"}.

+ */ + public String[] value(); +} diff --git a/jdk/src/share/classes/javax/management/DescriptorKey.java b/jdk/src/share/classes/javax/management/DescriptorKey.java index ad12612db18..9f919490512 100644 --- a/jdk/src/share/classes/javax/management/DescriptorKey.java +++ b/jdk/src/share/classes/javax/management/DescriptorKey.java @@ -33,6 +33,11 @@ import java.lang.annotation.*; * an MBean, or for an attribute, operation, or constructor in an * MBean, or for a parameter of an operation or constructor.

* + *

(The {@link DescriptorFields @DescriptorFields} annotation + * provides another way to add fields to a {@code Descriptor}. See + * the documentation for that annotation for a comparison of the + * two possibilities.)

+ * *

Consider this annotation for example:

* *
@@ -57,7 +62,7 @@ import java.lang.annotation.*;
  * 

When a Standard MBean is made from the {@code CacheControlMBean}, * the usual rules mean that it will have an attribute called * {@code CacheSize} of type {@code long}. The {@code @Units} - * attribute, given the above definition, will ensure that the + * annotation, given the above definition, will ensure that the * {@link MBeanAttributeInfo} for this attribute will have a * {@code Descriptor} that has a field called {@code units} with * corresponding value {@code bytes}.

@@ -125,12 +130,13 @@ import java.lang.annotation.*; * the method in the child interface are considered. * *

The Descriptor fields contributed in this way by different - * annotations on the same program element must be consistent. That - * is, two different annotations, or two members of the same - * annotation, must not define a different value for the same - * Descriptor field. Fields from annotations on a getter method must - * also be consistent with fields from annotations on the - * corresponding setter method.

+ * annotations on the same program element must be consistent with + * each other and with any fields contributed by a {@link + * DescriptorFields @DescriptorFields} annotation. That is, two + * different annotations, or two members of the same annotation, must + * not define a different value for the same Descriptor field. Fields + * from annotations on a getter method must also be consistent with + * fields from annotations on the corresponding setter method.

* *

The Descriptor resulting from these annotations will be merged * with any Descriptor fields provided by the implementation, such as @@ -169,4 +175,36 @@ import java.lang.annotation.*; @Target(ElementType.METHOD) public @interface DescriptorKey { String value(); + + /** + *

Do not include this field in the Descriptor if the annotation + * element has its default value. For example, suppose {@code @Units} is + * defined like this:

+ * + *
+     * @Documented
+     * @Target(ElementType.METHOD)
+     * @Retention(RetentionPolicy.RUNTIME)
+     * public @interface Units {
+     *     @DescriptorKey("units")
+     *     String value();
+     *
+     *     @DescriptorKey(value = "descriptionResourceKey",
+     *                    omitIfDefault = true)
+     *     String resourceKey() default "";
+     *
+     *     @DescriptorKey(value = "descriptionResourceBundleBaseName",
+     *                    omitIfDefault = true)
+     *     String resourceBundleBaseName() default "";
+     * }
+     * 
+ * + *

Then consider a usage such as {@code @Units("bytes")} or + * {@code @Units(value = "bytes", resourceKey = "")}, where the + * {@code resourceKey} and {@code resourceBundleBaseNames} elements + * have their default values. In this case the Descriptor resulting + * from these annotations will not include a {@code descriptionResourceKey} + * or {@code descriptionResourceBundleBaseName} field.

+ */ + boolean omitIfDefault() default false; } diff --git a/jdk/src/share/classes/javax/management/DynamicWrapperMBean.java b/jdk/src/share/classes/javax/management/DynamicWrapperMBean.java new file mode 100644 index 00000000000..4a67a96795a --- /dev/null +++ b/jdk/src/share/classes/javax/management/DynamicWrapperMBean.java @@ -0,0 +1,62 @@ +/* + * Copyright 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. + */ + +package javax.management; + +/** + *

An MBean can implement this interface to affect how the MBeanServer's + * {@link MBeanServer#getClassLoaderFor getClassLoaderFor} and + * {@link MBeanServer#isInstanceOf isInstanceOf} methods behave. + * If these methods should refer to a wrapped object rather than the + * MBean object itself, then the {@link #getWrappedObject} method should + * return that wrapped object.

+ * + * @see MBeanServer#getClassLoaderFor + * @see MBeanServer#isInstanceOf + */ +public interface DynamicWrapperMBean extends DynamicMBean { + /** + *

The resource corresponding to this MBean. This is the object whose + * class name should be reflected by the MBean's + * {@link MBeanServer#getMBeanInfo getMBeanInfo()}.{@link MBeanInfo#getClassName getClassName()} for example. For a "plain" + * DynamicMBean it will be "this". For an MBean that wraps another + * object, in the manner of {@link javax.management.StandardMBean}, it will be the + * wrapped object.

+ * + * @return The resource corresponding to this MBean. + */ + public Object getWrappedObject(); + + /** + *

The {@code ClassLoader} for this MBean, which can be used to + * retrieve resources associated with the MBean for example. Usually, + * it will be + * {@link #getWrappedObject()}.{@code getClass().getClassLoader()}. + * + * @return The {@code ClassLoader} for this MBean. + */ + public ClassLoader getWrappedClassLoader(); +} diff --git a/jdk/src/share/classes/javax/management/Impact.java b/jdk/src/share/classes/javax/management/Impact.java new file mode 100644 index 00000000000..9416df6fac8 --- /dev/null +++ b/jdk/src/share/classes/javax/management/Impact.java @@ -0,0 +1,105 @@ +/* + * 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; + +/** + *

Defines the impact of an MBean operation, in particular whether it + * has an effect on the MBean or simply returns information. This enum + * is used in the {@link ManagedOperation @ManagedOperation} annotation. + * Its {@link #getCode()} method can be used to get an {@code int} suitable + * for use as the {@code impact} parameter in an {@link MBeanOperationInfo} + * constructor.

+ */ +public enum Impact { + /** + * The operation is read-like: it returns information but does not change + * any state. + * @see MBeanOperationInfo#INFO + */ + INFO(MBeanOperationInfo.INFO), + + /** + * The operation is write-like: it has an effect but does not return + * any information from the MBean. + * @see MBeanOperationInfo#ACTION + */ + ACTION(MBeanOperationInfo.ACTION), + + /** + * The operation is both read-like and write-like: it has an effect, + * and it also returns information from the MBean. + * @see MBeanOperationInfo#ACTION_INFO + */ + ACTION_INFO(MBeanOperationInfo.ACTION_INFO), + + /** + * The impact of the operation is unknown or cannot be expressed + * using one of the other values. + * @see MBeanOperationInfo#UNKNOWN + */ + UNKNOWN(MBeanOperationInfo.UNKNOWN); + + private final int code; + + /** + * An instance of this enumeration, with the corresponding {@code int} + * code used by the {@link MBeanOperationInfo} constructors. + * + * @param code the code used by the {@code MBeanOperationInfo} constructors. + */ + Impact(int code) { + this.code = code; + } + + /** + * The equivalent {@code int} code used by the {@link MBeanOperationInfo} + * constructors. + * @return the {@code int} code. + */ + public int getCode() { + return code; + } + + /** + * Return the {@code Impact} value corresponding to the given {@code int} + * code. The {@code code} is the value that would be used in an + * {@code MBeanOperationInfo} constructor. + * + * @param code the {@code int} code. + * + * @return an {@code Impact} value {@code x} such that + * {@code code == x.}{@link #getCode()}, or {@code Impact.UNKNOWN} + * if there is no such value. + */ + public static Impact forCode(int code) { + switch (code) { + case MBeanOperationInfo.ACTION: return ACTION; + case MBeanOperationInfo.INFO: return INFO; + case MBeanOperationInfo.ACTION_INFO: return ACTION_INFO; + default: return UNKNOWN; + } + } +} diff --git a/jdk/src/share/classes/javax/management/JMX.java b/jdk/src/share/classes/javax/management/JMX.java index 91e9f455f60..0d75b8c8743 100644 --- a/jdk/src/share/classes/javax/management/JMX.java +++ b/jdk/src/share/classes/javax/management/JMX.java @@ -26,6 +26,7 @@ package javax.management; import com.sun.jmx.mbeanserver.Introspector; +import com.sun.jmx.mbeanserver.MBeanInjector; import com.sun.jmx.remote.util.ClassLogger; import java.beans.BeanInfo; import java.beans.PropertyDescriptor; @@ -130,6 +131,7 @@ public class JMX { *
* * @see javax.management.JMX.ProxyOptions + * @see javax.management.StandardMBean.Options */ public static class MBeanOptions implements Serializable, Cloneable { private static final long serialVersionUID = -6380842449318177843L; @@ -739,4 +741,28 @@ public class JMX { // exactly the string "MXBean" since that would mean there // was no package name, which is pretty unlikely in practice. } + + /** + *

Test if an MBean can emit notifications. An MBean can emit + * notifications if either it implements {@link NotificationBroadcaster} + * (perhaps through its child interface {@link NotificationEmitter}), or + * it uses resource + * injection to obtain an instance of {@link SendNotification} + * through which it can send notifications.

+ * + * @param mbean an MBean object. + * @return true if the given object is a valid MBean that can emit + * notifications; false if the object is a valid MBean but that + * cannot emit notifications. + * @throws NotCompliantMBeanException if the given object is not + * a valid MBean. + */ + public static boolean isNotificationSource(Object mbean) + throws NotCompliantMBeanException { + if (mbean instanceof NotificationBroadcaster) + return true; + Object resource = (mbean instanceof DynamicWrapperMBean) ? + ((DynamicWrapperMBean) mbean).getWrappedObject() : mbean; + return (MBeanInjector.injectsSendNotification(resource)); + } } diff --git a/jdk/src/share/classes/javax/management/MBean.java b/jdk/src/share/classes/javax/management/MBean.java new file mode 100644 index 00000000000..6837740334c --- /dev/null +++ b/jdk/src/share/classes/javax/management/MBean.java @@ -0,0 +1,68 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + *

Indicates that the annotated class is a Standard MBean. A Standard + * MBean class can be defined as in this example:

+ * + *
+ * {@code @MBean}
+ * public class Configuration {
+ *     {@link ManagedAttribute @ManagedAttribute}
+ *     public int getCacheSize() {...}
+ *     {@code @ManagedAttribute}
+ *     public void setCacheSize(int size);
+ *
+ *     {@code @ManagedAttribute}
+ *     public long getLastChangedTime();
+ *
+ *     {@link ManagedOperation @ManagedOperation}
+ *     public void save();
+ * }
+ * 
+ * + *

The class must be public. Public methods within the class can be + * annotated with {@code @ManagedOperation} to indicate that they are + * MBean operations. Public getter and setter methods within the class + * can be annotated with {@code @ManagedAttribute} to indicate that they define + * MBean attributes.

+ * + *

If the MBean is to be an MXBean rather than a Standard MBean, then + * the {@link MXBean @MXBean} annotation must be used instead of + * {@code @MBean}.

+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +public @interface MBean { +} diff --git a/jdk/src/share/classes/javax/management/MBeanOperationInfo.java b/jdk/src/share/classes/javax/management/MBeanOperationInfo.java index 9bda3ed5319..5863e96ef39 100644 --- a/jdk/src/share/classes/javax/management/MBeanOperationInfo.java +++ b/jdk/src/share/classes/javax/management/MBeanOperationInfo.java @@ -46,25 +46,30 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable { new MBeanOperationInfo[0]; /** - * Indicates that the operation is read-like, - * it basically returns information. + * Indicates that the operation is read-like: + * it returns information but does not change any state. + * @see Impact#INFO */ public static final int INFO = 0; /** - * Indicates that the operation is a write-like, - * and would modify the MBean in some way, typically by writing some value - * or changing a configuration. + * Indicates that the operation is write-like: it has an effect but does + * not return any information from the MBean. + * @see Impact#ACTION */ public static final int ACTION = 1; /** - * Indicates that the operation is both read-like and write-like. + * Indicates that the operation is both read-like and write-like: + * it has an effect, and it also returns information from the MBean. + * @see Impact#ACTION_INFO */ public static final int ACTION_INFO = 2; /** - * Indicates that the operation has an "unknown" nature. + * Indicates that the impact of the operation is unknown or cannot be + * expressed using one of the other values. + * @see Impact#UNKNOWN */ public static final int UNKNOWN = 3; @@ -120,8 +125,9 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable { * describing the parameters(arguments) of the method. This may be * null with the same effect as a zero-length array. * @param type The type of the method's return value. - * @param impact The impact of the method, one of INFO, - * ACTION, ACTION_INFO, UNKNOWN. + * @param impact The impact of the method, one of + * {@link #INFO}, {@link #ACTION}, {@link #ACTION_INFO}, + * {@link #UNKNOWN}. */ public MBeanOperationInfo(String name, String description, @@ -140,8 +146,9 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable { * describing the parameters(arguments) of the method. This may be * null with the same effect as a zero-length array. * @param type The type of the method's return value. - * @param impact The impact of the method, one of INFO, - * ACTION, ACTION_INFO, UNKNOWN. + * @param impact The impact of the method, one of + * {@link #INFO}, {@link #ACTION}, {@link #ACTION_INFO}, + * {@link #UNKNOWN}. * @param descriptor The descriptor for the operation. This may be null * which is equivalent to an empty descriptor. * @@ -319,9 +326,14 @@ public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable { for (int i = 0; i < classes.length; i++) { Descriptor d = Introspector.descriptorForAnnotations(annots[i]); - final String pn = "p" + (i + 1); - params[i] = - new MBeanParameterInfo(pn, classes[i].getName(), "", d); + String description = Introspector.descriptionForParameter(annots[i]); + if (description == null) + description = ""; + String name = Introspector.nameForParameter(annots[i]); + if (name == null) + name = "p" + (i + 1); + params[i] = new MBeanParameterInfo( + name, classes[i].getName(), description, d); } return params; diff --git a/jdk/src/share/classes/javax/management/MBeanRegistration.java b/jdk/src/share/classes/javax/management/MBeanRegistration.java index fbdedc183b7..1ba1c0d827f 100644 --- a/jdk/src/share/classes/javax/management/MBeanRegistration.java +++ b/jdk/src/share/classes/javax/management/MBeanRegistration.java @@ -27,9 +27,101 @@ package javax.management; /** - * Can be implemented by an MBean in order to + *

Can be implemented by an MBean in order to * carry out operations before and after being registered or unregistered from - * the MBean server. + * the MBean Server. An MBean can also implement this interface in order + * to get a reference to the MBean Server and/or its name within that + * MBean Server.

+ * + *

Resource injection

+ * + *

As an alternative to implementing {@code MBeanRegistration}, if all that + * is needed is the MBean Server or ObjectName then an MBean can use + * resource injection.

+ * + *

If a field in the MBean object has type {@link ObjectName} and has + * the {@link javax.annotation.Resource @Resource} annotation, + * then the {@code ObjectName} under which the MBean is registered is + * assigned to that field during registration. Likewise, if a field has type + * {@link MBeanServer} and the @Resource annotation, then it will + * be set to the {@code MBeanServer} in which the MBean is registered.

+ * + *

For example:

+ * + *
+ * public Configuration implements ConfigurationMBean {
+ *     @Resource
+ *     private volatile MBeanServer mbeanServer;
+ *     @Resource
+ *     private volatile ObjectName objectName;
+ *     ...
+ *     void unregisterSelf() throws Exception {
+ *         mbeanServer.unregisterMBean(objectName);
+ *     }
+ * }
+ * 
+ * + *

Resource injection can also be used on fields of type + * {@link SendNotification} to simplify notification sending. Such a field + * will get a reference to an object of type {@code SendNotification} when + * the MBean is registered, and it can use this reference to send notifications. + * For example:

+ * + *
+ * public Configuration implements ConfigurationMBean {
+ *     @Resource
+ *     private volatile SendNotification sender;
+ *     ...
+ *     private void updated() {
+ *         Notification n = new Notification(...);
+ *         sender.sendNotification(n);
+ *     }
+ * }
+ * 
+ * + *

A field to be injected must not be static. It is recommended that + * such fields be declared {@code volatile}.

+ * + *

It is also possible to use the @Resource annotation on + * methods. Such a method must have a {@code void} return type and a single + * argument of the appropriate type, for example {@code ObjectName}.

+ * + *

Any number of fields and methods may have the @Resource + * annotation. All fields and methods with type {@code ObjectName} + * (for example) will receive the same {@code ObjectName} value.

+ * + *

Resource injection is available for all types of MBeans, not just + * Standard MBeans.

+ * + *

If an MBean implements the {@link DynamicWrapperMBean} interface then + * resource injection happens on the object returned by that interface's + * {@link DynamicWrapperMBean#getWrappedObject() getWrappedObject()} method + * rather than on the MBean object itself. + * + *

Resource injection happens after the {@link #preRegister preRegister} + * method is called (if any), and before the MBean is actually registered + * in the MBean Server. If a @Resource method throws + * an exception, the effect is the same as if {@code preRegister} had + * thrown the exception. In particular it will prevent the MBean from being + * registered.

+ * + *

Resource injection can be used on a field or method where the type + * is a parent of the injected type, if the injected type is explicitly + * specified in the @Resource annotation. For example:

+ * + *
+ *     @Resource(type = MBeanServer.class)
+ *     private volatile MBeanServerConnection mbsc;
+ * 
+ * + *

Formally, suppose R is the type in the @Resource + * annotation and T is the type of the method parameter or field. + * Then one of R and T must be a subtype of the other + * (or they must be the same type). Injection happens if this subtype + * is {@code MBeanServer}, {@code ObjectName}, or {@code SendNotification}. + * Otherwise the @Resource annotation is ignored.

+ * + *

Resource injection in MBeans is new in version 2.0 of the JMX API.

* * @since 1.5 */ @@ -38,12 +130,12 @@ public interface MBeanRegistration { /** * Allows the MBean to perform any operations it needs before - * being registered in the MBean server. If the name of the MBean + * 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. + * registered in the MBean Server. * - * @param server The MBean server in which the MBean will be registered. + * @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 @@ -57,7 +149,7 @@ public interface MBeanRegistration { * the returned value. * * @exception java.lang.Exception This exception will be caught by - * the MBean server and re-thrown as an {@link + * the MBean Server and re-thrown as an {@link * MBeanRegistrationException}. */ public ObjectName preRegister(MBeanServer server, diff --git a/jdk/src/share/classes/javax/management/MBeanServer.java b/jdk/src/share/classes/javax/management/MBeanServer.java index 728f71340ba..20818bfaaff 100644 --- a/jdk/src/share/classes/javax/management/MBeanServer.java +++ b/jdk/src/share/classes/javax/management/MBeanServer.java @@ -61,7 +61,7 @@ import javax.management.loading.ClassLoaderRepository; * ObjectName is:
* JMImplementation:type=MBeanServerDelegate.

* - *

An object obtained from the {@link + *

An object obtained from the {@link * MBeanServerFactory#createMBeanServer(String) createMBeanServer} or * {@link MBeanServerFactory#newMBeanServer(String) newMBeanServer} * methods of the {@link MBeanServerFactory} class applies security @@ -661,13 +661,16 @@ public interface MBeanServer extends MBeanServerConnection { ReflectionException; /** - *

Return the {@link java.lang.ClassLoader} that was used for - * loading the class of the named MBean.

+ *

Return the {@link java.lang.ClassLoader} that was used for loading + * the class of the named MBean. If the MBean implements the {@link + * DynamicWrapperMBean} interface, then the returned value is the + * result of the {@link DynamicWrapperMBean#getWrappedClassLoader()} + * method.

* * @param mbeanName The ObjectName of the MBean. * * @return The ClassLoader used for that MBean. If l - * is the MBean's actual ClassLoader, and r is the + * is the value specified by the rules above, and r is the * returned value, then either: * *