1 /*
   2  * Copyright (c) 2006, 2015, 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  *
  31  * @run clean TooManyFooTest
  32  * @run build TooManyFooTest
  33  * @run main TooManyFooTest
  34  */
  35 
  36 import java.lang.management.ManagementFactory;
  37 import java.lang.reflect.Method;
  38 import java.util.Arrays;
  39 import java.util.HashMap;
  40 import java.util.HashSet;
  41 import java.util.Map;
  42 import java.util.Set;
  43 import java.util.logging.Logger;
  44 import javax.management.Descriptor;
  45 import javax.management.MBeanInfo;
  46 import javax.management.MBeanOperationInfo;
  47 import javax.management.MBeanServer;
  48 import javax.management.ObjectName;
  49 import javax.management.StandardMBean;
  50 import javax.management.openmbean.OpenMBeanOperationInfo;
  51 
  52 /**
  53  * Class TooManyFooTest
  54  * @author Sun Microsystems, 2005 - All rights reserved.
  55  */
  56 public class TooManyFooTest {
  57 
  58     /**
  59      * A logger for this class.
  60      **/
  61     private static final Logger LOG =
  62             Logger.getLogger(TooManyFooTest.class.getName());
  63 
  64     public static class NumberHolder {
  65         public Integer getNumber() { return 0;}
  66         public void setNumber(Integer n) {};
  67     }
  68     public static class MyNumberHolder extends NumberHolder {
  69 
  70     }
  71     public interface Parent1 {
  72         public int foo(); // Both in Parent1 and Parent2
  73         public Integer barfoo(); // Subtype in Parent1, Super type in Parent2
  74         public Long    foobar(); // Subtype in Parent1 & MBean, Super type in
  75                                  // Parent2
  76         public Number  toofoo(); // Subtype in Parent1, Super type in Parent2
  77                                  // Concrete type in MBean
  78         public Object toofoofoo(); // Super type in Parent1, Subtype in Parent2,
  79         public NumberHolder toobarbar(); // toofoofoo reversed
  80     }
  81 
  82     public interface Parent2 {
  83         public int foo(); // Both in Parent1 and Parent2
  84         public Number barfoo();
  85         public Number foobar();
  86         public Object toofoo();
  87         public NumberHolder  toofoofoo();
  88         public Object toobarbar();
  89     }
  90 
  91     public interface ChildMBean extends Parent1, Parent2 {
  92         public Long foobar();
  93         public Long toofoo();
  94     }
  95 
  96     public interface ChildMXBean extends Parent1, Parent2 {
  97         public Long foobar();
  98         public Long toofoo();
  99     }
 100 
 101     public interface ChildMixMXBean extends ChildMBean, ChildMXBean {
 102     }
 103 
 104     public static class Child implements ChildMBean {
 105         public int foo() {return 0;}
 106         public Long foobar() {return 0L;}
 107         public Long toofoo() {return 0L;}
 108         public Integer barfoo() {return 0;}
 109         public MyNumberHolder toofoofoo() { return null;}
 110         public MyNumberHolder toobarbar() { return null;}
 111     }
 112 
 113     public static class ChildMix implements ChildMXBean {
 114         public int foo() {return 0;}
 115         public Long foobar() {return 0L;}
 116         public Long toofoo() {return 0L;}
 117         public Integer barfoo() {return 0;}
 118         public MyNumberHolder toofoofoo() { return null;}
 119         public MyNumberHolder toobarbar() { return null;}
 120     }
 121 
 122     public static class ChildMixMix extends Child implements ChildMixMXBean {
 123     }
 124 
 125 
 126     /** Creates a new instance of TooManyFooTest */
 127     public TooManyFooTest() {
 128     }
 129 
 130     private static final int OPCOUNT;
 131     private static final Map<String,String> EXPECTED_TYPES;
 132     private static final String[][] type_array = {
 133         { "foo", int.class.getName() },
 134         { "foobar", Long.class.getName()},
 135         { "toofoo", Long.class.getName()},
 136         { "barfoo", Integer.class.getName()},
 137         { "toofoofoo", NumberHolder.class.getName()},
 138         { "toobarbar", NumberHolder.class.getName()},
 139     };
 140     static {
 141         try {
 142             final Set<String> declared = new HashSet<String>();
 143             for (Method m:Child.class.getDeclaredMethods()) {
 144                 declared.add(m.getName()+Arrays.asList(m.getParameterTypes()));
 145             }
 146             final Set<String> exposed = new HashSet<String>();
 147             for (Method m:ChildMBean.class.getMethods()) {
 148                 exposed.add(m.getName()+Arrays.asList(m.getParameterTypes()));
 149             }
 150             declared.retainAll(exposed);
 151             OPCOUNT = declared.size();
 152             EXPECTED_TYPES = new HashMap<String,String>();
 153             for (String[] st:type_array) {
 154                 EXPECTED_TYPES.put(st[0],st[1]);
 155             }
 156         } catch (Exception x) {
 157             throw new ExceptionInInitializerError(x);
 158         }
 159     }
 160 
 161     private static void test(Object child, String name, boolean mxbean)
 162         throws Exception {
 163         final ObjectName childName =
 164                 new ObjectName("test:type=Child,name="+name);
 165         final MBeanServer server =
 166                 ManagementFactory.getPlatformMBeanServer();
 167         server.registerMBean(child,childName);
 168         try {
 169             final MBeanInfo info = server.getMBeanInfo(childName);
 170             System.out.println(name+": " + info.getDescriptor());
 171             final int len = info.getOperations().length;
 172             if (len == OPCOUNT) {
 173                 System.out.println(name+": OK, only "+OPCOUNT+
 174                         " operations here...");
 175             } else {
 176                 final String qual = (len>OPCOUNT)?"many":"few";
 177                 System.err.println(name+": Too "+qual+" foos! Found "+
 178                         len+", expected "+OPCOUNT);
 179                 for (MBeanOperationInfo op : info.getOperations()) {
 180                     System.err.println("public "+op.getReturnType()+" "+
 181                             op.getName()+"();");
 182                 }
 183                 throw new RuntimeException("Too " + qual +
 184                         " foos for "+name);
 185             }
 186 
 187             final Descriptor d = info.getDescriptor();
 188             final String mxstr = String.valueOf(d.getFieldValue("mxbean"));
 189             final boolean mxb =
 190                     (mxstr==null)?false:Boolean.valueOf(mxstr).booleanValue();
 191             System.out.println(name+": mxbean="+mxb);
 192             if (mxbean && !mxb)
 193                 throw new AssertionError("MXBean is not OpenMBean?");
 194 
 195             for (MBeanOperationInfo mboi : info.getOperations()) {
 196 
 197                 // Sanity check
 198                 if (mxbean && !mboi.getName().equals("foo")) {
 199                     // The spec doesn't guarantee that the MBeanOperationInfo
 200                     // of an MXBean will be an OpenMBeanOperationInfo, and in
 201                     // some circumstances in our implementation it will not.
 202                     // However, in thsi tests, for all methods but foo(),
 203                     // it should.
 204                     //
 205                     if (!(mboi instanceof OpenMBeanOperationInfo))
 206                         throw new AssertionError("Operation "+mboi.getName()+
 207                                 "() is not Open?");
 208                 }
 209 
 210                 final String exp = EXPECTED_TYPES.get(mboi.getName());
 211 
 212                 // For MXBeans, we need to compare 'exp' with the original
 213                 // type - because mboi.getReturnType() returns the OpenType
 214                 //
 215                 String type = (String)mboi.getDescriptor().
 216                             getFieldValue("originalType");
 217                 if (type == null) type = mboi.getReturnType();
 218                 if (type.equals(exp)) continue;
 219                 System.err.println("Bad return type for "+
 220                         mboi.getName()+"! Found "+type+
 221                         ", expected "+exp);
 222                 throw new RuntimeException("Bad return type for "+
 223                         mboi.getName());
 224             }
 225         } finally {
 226             server.unregisterMBean(childName);
 227         }
 228     }
 229 
 230     public static void main(String[] args) throws Exception {
 231         final Child child = new Child();
 232         test(child,"Child[MBean]",false);
 233         final ChildMix childx = new ChildMix();
 234         test(childx,"ChildMix[MXBean]",true);
 235         final ChildMixMix childmx = new ChildMixMix();
 236         test(childmx,"ChildMixMix[MXBean]",false);
 237         final StandardMBean schild = new StandardMBean(child,ChildMBean.class);
 238         test(schild,"Child[StandarMBean(Child)]",false);
 239         final StandardMBean schildx =
 240                 new StandardMBean(childx,ChildMXBean.class,true);
 241         test(schildx,"ChildMix[StandarMXBean(ChildMix)]",true);
 242         final StandardMBean schildmx =
 243                 new StandardMBean(childmx,ChildMixMXBean.class,true);
 244         test(schildmx,"ChildMixMix[StandarMXBean(ChildMixMix)]",true);
 245     }
 246 
 247 }