1 /*
   2  * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 6398884
  27  * @summary Test that a method inherited from two different interfaces
  28  *          appears only once in MBeanInfo.
  29  * @author dfuchs
  30  * @run clean TooManyFooTest
  31  * @run build TooManyFooTest
  32  * @run main TooManyFooTest
  33  */
  34 
  35 import java.lang.management.ManagementFactory;
  36 import java.lang.reflect.Method;
  37 import java.util.Arrays;
  38 import java.util.HashMap;
  39 import java.util.HashSet;
  40 import java.util.Map;
  41 import java.util.Set;
  42 import java.util.logging.Logger;
  43 import javax.management.Descriptor;
  44 import javax.management.MBeanInfo;
  45 import javax.management.MBeanOperationInfo;
  46 import javax.management.MBeanServer;
  47 import javax.management.ObjectName;
  48 import javax.management.StandardMBean;
  49 import javax.management.openmbean.OpenMBeanOperationInfo;
  50 
  51 /**
  52  * Class TooManyFooTest
  53  * @author Sun Microsystems, 2005 - All rights reserved.
  54  */
  55 public class TooManyFooTest {
  56 
  57     /**
  58      * A logger for this class.
  59      **/
  60     private static final Logger LOG =
  61             Logger.getLogger(TooManyFooTest.class.getName());
  62 
  63     public static class NumberHolder {
  64         public Integer getNumber() { return 0;}
  65         public void setNumber(Integer n) {};
  66     }
  67     public static class MyNumberHolder extends NumberHolder {
  68 
  69     }
  70     public interface Parent1 {
  71         public int foo(); // Both in Parent1 and Parent2
  72         public Integer barfoo(); // Subtype in Parent1, Super type in Parent2
  73         public Long    foobar(); // Subtype in Parent1 & MBean, Super type in
  74                                  // Parent2
  75         public Number  toofoo(); // Subtype in Parent1, Super type in Parent2
  76                                  // Concrete type in MBean
  77         public Object toofoofoo(); // Super type in Parent1, Subtype in Parent2,
  78         public NumberHolder toobarbar(); // toofoofoo reversed
  79     }
  80 
  81     public interface Parent2 {
  82         public int foo(); // Both in Parent1 and Parent2
  83         public Number barfoo();
  84         public Number foobar();
  85         public Object toofoo();
  86         public NumberHolder  toofoofoo();
  87         public Object toobarbar();
  88     }
  89 
  90     public interface ChildMBean extends Parent1, Parent2 {
  91         public Long foobar();
  92         public Long toofoo();
  93     }
  94 
  95     public interface ChildMXBean extends Parent1, Parent2 {
  96         public Long foobar();
  97         public Long toofoo();
  98     }
  99 
 100     public interface ChildMixMXBean extends ChildMBean, ChildMXBean {
 101     }
 102 
 103     public static class Child implements ChildMBean {
 104         public int foo() {return 0;}
 105         public Long foobar() {return 0L;}
 106         public Long toofoo() {return 0L;}
 107         public Integer barfoo() {return 0;}
 108         public MyNumberHolder toofoofoo() { return null;}
 109         public MyNumberHolder toobarbar() { return null;}
 110     }
 111 
 112     public static class ChildMix implements ChildMXBean {
 113         public int foo() {return 0;}
 114         public Long foobar() {return 0L;}
 115         public Long toofoo() {return 0L;}
 116         public Integer barfoo() {return 0;}
 117         public MyNumberHolder toofoofoo() { return null;}
 118         public MyNumberHolder toobarbar() { return null;}
 119     }
 120 
 121     public static class ChildMixMix extends Child implements ChildMixMXBean {
 122     }
 123 
 124 
 125     /** Creates a new instance of TooManyFooTest */
 126     public TooManyFooTest() {
 127     }
 128 
 129     private static final int OPCOUNT;
 130     private static final Map<String,String> EXPECTED_TYPES;
 131     private static final String[][] type_array = {
 132         { "foo", int.class.getName() },
 133         { "foobar", Long.class.getName()},
 134         { "toofoo", Long.class.getName()},
 135         { "barfoo", Integer.class.getName()},
 136         { "toofoofoo", NumberHolder.class.getName()},
 137         { "toobarbar", NumberHolder.class.getName()},
 138     };
 139     static {
 140         try {
 141             final Set<String> declared = new HashSet<String>();
 142             for (Method m:Child.class.getDeclaredMethods()) {
 143                 declared.add(m.getName()+Arrays.asList(m.getParameterTypes()));
 144             }
 145             final Set<String> exposed = new HashSet<String>();
 146             for (Method m:ChildMBean.class.getMethods()) {
 147                 exposed.add(m.getName()+Arrays.asList(m.getParameterTypes()));
 148             }
 149             declared.retainAll(exposed);
 150             OPCOUNT = declared.size();
 151             EXPECTED_TYPES = new HashMap<String,String>();
 152             for (String[] st:type_array) {
 153                 EXPECTED_TYPES.put(st[0],st[1]);
 154             }
 155         } catch (Exception x) {
 156             throw new ExceptionInInitializerError(x);
 157         }
 158     }
 159 
 160     private static void test(Object child, String name, boolean mxbean)
 161         throws Exception {
 162         final ObjectName childName =
 163                 new ObjectName("test:type=Child,name="+name);
 164         final MBeanServer server =
 165                 ManagementFactory.getPlatformMBeanServer();
 166         server.registerMBean(child,childName);
 167         try {
 168             final MBeanInfo info = server.getMBeanInfo(childName);
 169             System.out.println(name+": " + info.getDescriptor());
 170             final int len = info.getOperations().length;
 171             if (len == OPCOUNT) {
 172                 System.out.println(name+": OK, only "+OPCOUNT+
 173                         " operations here...");
 174             } else {
 175                 final String qual = (len>OPCOUNT)?"many":"few";
 176                 System.err.println(name+": Too "+qual+" foos! Found "+
 177                         len+", expected "+OPCOUNT);
 178                 for (MBeanOperationInfo op : info.getOperations()) {
 179                     System.err.println("public "+op.getReturnType()+" "+
 180                             op.getName()+"();");
 181                 }
 182                 throw new RuntimeException("Too " + qual +
 183                         " foos for "+name);
 184             }
 185 
 186             final Descriptor d = info.getDescriptor();
 187             final String mxstr = String.valueOf(d.getFieldValue("mxbean"));
 188             final boolean mxb =
 189                     (mxstr==null)?false:Boolean.valueOf(mxstr).booleanValue();
 190             System.out.println(name+": mxbean="+mxb);
 191             if (mxbean && !mxb)
 192                 throw new AssertionError("MXBean is not OpenMBean?");
 193 
 194             for (MBeanOperationInfo mboi : info.getOperations()) {
 195 
 196                 // Sanity check
 197                 if (mxbean && !mboi.getName().equals("foo")) {
 198                     // The spec doesn't guarantee that the MBeanOperationInfo
 199                     // of an MXBean will be an OpenMBeanOperationInfo, and in
 200                     // some circumstances in our implementation it will not.
 201                     // However, in thsi tests, for all methods but foo(),
 202                     // it should.
 203                     //
 204                     if (!(mboi instanceof OpenMBeanOperationInfo))
 205                         throw new AssertionError("Operation "+mboi.getName()+
 206                                 "() is not Open?");
 207                 }
 208 
 209                 final String exp = EXPECTED_TYPES.get(mboi.getName());
 210 
 211                 // For MXBeans, we need to compare 'exp' with the original
 212                 // type - because mboi.getReturnType() returns the OpenType
 213                 //
 214                 String type = (String)mboi.getDescriptor().
 215                             getFieldValue("originalType");
 216                 if (type == null) type = mboi.getReturnType();
 217                 if (type.equals(exp)) continue;
 218                 System.err.println("Bad return type for "+
 219                         mboi.getName()+"! Found "+type+
 220                         ", expected "+exp);
 221                 throw new RuntimeException("Bad return type for "+
 222                         mboi.getName());
 223             }
 224         } finally {
 225             server.unregisterMBean(childName);
 226         }
 227     }
 228 
 229     public static void main(String[] args) throws Exception {
 230         final Child child = new Child();
 231         test(child,"Child[MBean]",false);
 232         final ChildMix childx = new ChildMix();
 233         test(childx,"ChildMix[MXBean]",true);
 234         final ChildMixMix childmx = new ChildMixMix();
 235         test(childmx,"ChildMixMix[MXBean]",false);
 236         final StandardMBean schild = new StandardMBean(child,ChildMBean.class);
 237         test(schild,"Child[StandarMBean(Child)]",false);
 238         final StandardMBean schildx =
 239                 new StandardMBean(childx,ChildMXBean.class,true);
 240         test(schildx,"ChildMix[StandarMXBean(ChildMix)]",true);
 241         final StandardMBean schildmx =
 242                 new StandardMBean(childmx,ChildMixMXBean.class,true);
 243         test(schildmx,"ChildMixMix[StandarMXBean(ChildMixMix)]",true);
 244     }
 245 
 246 }