/*
 * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * @test
 * @bug 6204469
 * @summary Check that descriptors have not broken serial interop.
 * @author Eamonn McManus
 *
 * @run clean MBeanInfoInteropTest SerializedInfo
 * @run build MBeanInfoInteropTest SerializedInfo
 * @run main MBeanInfoInteropTest SerializedInfo
 */

/*
 * When run with just a classname argument ("SerializedInfo" above),
 * this reads the serialized objects from that class.
 * When run with the argument "generate" and a classname, say "SerializedInfo",
 * this creates the class SerializedInfo.java.  The idea is to do that on JDK 5.0
 * then run this test on the latest JDK, or vice versa.  The
 * copy included in this source directory was generated on JDK 5.0
 * so that we continue to check forward interop (serialize on JDK 5,
 * deserialize on JDK 6).  There is no easy way to automate backward
 * interop checking.
 */

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import javax.management.Descriptor;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import static javax.management.MBeanOperationInfo.*;
import static javax.management.openmbean.SimpleType.INTEGER;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.modelmbean.DescriptorSupport;
import javax.management.modelmbean.ModelMBeanAttributeInfo;
import javax.management.modelmbean.ModelMBeanConstructorInfo;
import javax.management.modelmbean.ModelMBeanInfoSupport;
import javax.management.modelmbean.ModelMBeanNotificationInfo;
import javax.management.modelmbean.ModelMBeanOperationInfo;
import javax.management.openmbean.OpenMBeanAttributeInfo;
import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
import javax.management.openmbean.OpenMBeanConstructorInfo;
import javax.management.openmbean.OpenMBeanConstructorInfoSupport;
import javax.management.openmbean.OpenMBeanInfoSupport;
import javax.management.openmbean.OpenMBeanOperationInfo;
import javax.management.openmbean.OpenMBeanOperationInfoSupport;
import javax.management.openmbean.OpenMBeanParameterInfo;
import javax.management.openmbean.OpenMBeanParameterInfoSupport;

public class MBeanInfoInteropTest {
    public static void main(String[] args) throws Exception {
        if (args.length == 2 && args[0].equals("generate"))
            generate(args[1]);
        else if (args.length == 1)
            test(args[0]);
        else {
            final String usage =
                "Usage: MBeanInfoInteropTest [generate] ClassName";
            throw new Exception(usage);
        }
    }

    private static void test(String className) throws Exception {
        Class<?> c = Class.forName(className);
        Field f = c.getField("BYTES");
        byte[] bytes = (byte[]) f.get(null);
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bis);
        boolean matched = true;
        for (Serializable s : objects) {
            Object o = ois.readObject();
            if (!o.equals(s)) {
                showMismatch(o, s);
                matched = false;
            }
        }
        if (!matched)
            throw new Exception("Read objects did not match");
        System.out.println("Test passed");
    }

    private static void showMismatch(Object read, Serializable expected)
    throws Exception {
        String name = "<unknown>";
        Field[] fs = MBeanInfoInteropTest.class.getDeclaredFields();
        for (Field f : fs) {
            if (!Modifier.isStatic(f.getModifiers()))
                continue;
            Object x = f.get(null);
            if (x == expected) {
                name = f.getName();
                break;
            }
        }
        System.out.println("Read object mismatch for field " + name);
        System.out.println("...read:     " + read);
        System.out.println("...expected: " + expected);
    }

    private static void generate(String className) throws Exception {
        System.out.println("Generating " + className + ".java");
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        for (Serializable s : objects)
            oos.writeObject(s);
        oos.close();
        byte[] bytes = bos.toByteArray();
        PrintWriter pw = new PrintWriter(className + ".java");
        pw.printf(
            "// Generated by: MBeanInfoInteropTest generate %s\n" +
            "import java.io.*;\n" +
            "public class %s {\n" +
            "public static final byte[] BYTES = {\n", className, className);
        for (int i = 0; i < bytes.length; i++) {
            byte b = bytes[i];
            pw.printf("%d,", b);
            if (i % 16 == 15)
                pw.printf("\n");
        }
        pw.printf("\n");
        pw.printf(
            "};\n" +
            "}\n");
        pw.close();
        System.out.println("...done");
    }

    private static MBeanAttributeInfo mbai;
    private static MBeanParameterInfo mbpi;
    private static MBeanConstructorInfo mbci1, mbci2;
    private static MBeanNotificationInfo mbni1, mbni2;
    private static MBeanOperationInfo mboi1, mboi2;
    private static MBeanInfo mbi1, mbi2;
    private static OpenMBeanAttributeInfoSupport ombai1, ombai2, ombai3, ombai4;
    private static OpenMBeanParameterInfoSupport ombpi1, ombpi2, ombpi3, ombpi4;
    private static OpenMBeanConstructorInfoSupport ombci1, ombci2;
    private static OpenMBeanOperationInfoSupport omboi1, omboi2;
    private static OpenMBeanInfoSupport ombi1, ombi2;
    private static ModelMBeanAttributeInfo mmbai1, mmbai2;
    private static ModelMBeanConstructorInfo mmbci1, mmbci2, mmbci3, mmbci4;
    private static ModelMBeanOperationInfo mmboi1, mmboi2, mmboi3, mmboi4;
    private static ModelMBeanNotificationInfo mmbni1, mmbni2, mmbni3, mmbni4;
    private static ModelMBeanInfoSupport mmbi1, mmbi2, mmbi3, mmbi4;
    private static Serializable[] objects;
    static {
        try {
            init();
        } catch (Exception e) {
            throw new IllegalArgumentException("unexpected", e);
        }
    }
    private static void init() throws Exception {
        mbai =
            new MBeanAttributeInfo("name", "type", "descr", true, false, false);
        mbpi =
            new MBeanParameterInfo("name", "type", "descr");
        MBeanParameterInfo[] params = new MBeanParameterInfo[] {mbpi};
        mbci1 =
            new MBeanConstructorInfo("name", "descr", null);
        mbci2 =
            new MBeanConstructorInfo("name", "descr", params);
        mbni1 =
            new MBeanNotificationInfo(null, "name", "descr");
        mbni2 =
            new MBeanNotificationInfo(new String[] {"type"}, "name", "descr");
        mboi1 =
            new MBeanOperationInfo("name", "descr", null, "type", ACTION);
        mboi2 =
            new MBeanOperationInfo("name", "descr", params, "type", INFO);
        mbi1 =
            new MBeanInfo("class", "descr", null, null, null, null);
        mbi2 =
            new MBeanInfo(
                "class", "descr",
                new MBeanAttributeInfo[] {mbai},
                new MBeanConstructorInfo[] {mbci1, mbci2},
                new MBeanOperationInfo[] {mboi1, mboi2},
                new MBeanNotificationInfo[] {mbni1, mbni2});

        ombai1 =
            new OpenMBeanAttributeInfoSupport("name", "descr", INTEGER,
                                              true, false, false);
        ombai2 =
            new OpenMBeanAttributeInfoSupport("name", "descr", INTEGER,
                                              true, false, false, 5);
        ombai3 =
            new OpenMBeanAttributeInfoSupport("name", "descr", INTEGER,
                                              true, false, false, 5, 1, 6);
        ombai4 =
            new OpenMBeanAttributeInfoSupport("name", "descr", INTEGER,
                                              true, false, false, 5,
                                              new Integer[] {2, 3, 5, 7});
        ombpi1 =
            new OpenMBeanParameterInfoSupport("name1", "descr", INTEGER);
        ombpi2 =
            new OpenMBeanParameterInfoSupport("name2", "descr", INTEGER, 5);
        ombpi3 =
            new OpenMBeanParameterInfoSupport("name3", "descr", INTEGER, 5, 1, 6);
        ombpi4 =
            new OpenMBeanParameterInfoSupport("name4", "descr", INTEGER, 5,
                                              new Integer[] {2, 3, 5, 7});
        OpenMBeanParameterInfo[] oparams = {ombpi1, ombpi2, ombpi3, ombpi4};
        ombci1 =
            new OpenMBeanConstructorInfoSupport("name", "descr", null);
        ombci2 =
            new OpenMBeanConstructorInfoSupport("name", "descr", oparams);
        omboi1 =
            new OpenMBeanOperationInfoSupport("name", "descr", null,
                                              INTEGER, ACTION);
        omboi2 =
            new OpenMBeanOperationInfoSupport("name", "descr", oparams,
                                              INTEGER, ACTION);
        ombi1 =
            new OpenMBeanInfoSupport("class", "descr", null, null, null, null);
        ombi2 =
            new OpenMBeanInfoSupport(
                "class", "descr",
                new OpenMBeanAttributeInfo[] {ombai1, ombai2, ombai3, ombai4},
                new OpenMBeanConstructorInfo[] {ombci1, ombci2},
                new OpenMBeanOperationInfo[] {omboi1, omboi2},
                new MBeanNotificationInfo[] {mbni1, mbni2});

        Descriptor attrd = new DescriptorSupport(new String[] {
            "name=name", "descriptorType=attribute",
        });
        mmbai1 =
            new ModelMBeanAttributeInfo("name", "type", "descr",
                                        true, false, false);
        mmbai2 =
            new ModelMBeanAttributeInfo("name", "type", "descr",
                                        true, false, false, attrd);
        Descriptor constrd = new DescriptorSupport(new String[] {
            "name=name", "descriptorType=operation", "role=constructor",
        });
        mmbci1 =
            new ModelMBeanConstructorInfo("name", "descr", null);
        mmbci2 =
            new ModelMBeanConstructorInfo("name", "descr", null, constrd);
        mmbci3 =
            new ModelMBeanConstructorInfo("name", "descr", params);
        mmbci4 =
            new ModelMBeanConstructorInfo("name", "descr", params, constrd);
        Descriptor operd = new DescriptorSupport(new String[] {
            "name=name", "descriptorType=operation",
        });
        mmboi1 =
            new ModelMBeanOperationInfo("name", "descr", null, "type", ACTION);
        mmboi2 =
            new ModelMBeanOperationInfo("name", "descr", null, "type", ACTION,
                                        operd);
        mmboi3 =
            new ModelMBeanOperationInfo("name", "descr", params, "type", ACTION);
        mmboi4 =
            new ModelMBeanOperationInfo("name", "descr", params, "type", ACTION,
                                        operd);
        Descriptor notifd = new DescriptorSupport(new String[] {
            "name=name", "descriptorType=notification",
        });
        mmbni1 =
            new ModelMBeanNotificationInfo(null, "name", "descr");
        mmbni2 =
            new ModelMBeanNotificationInfo(null, "name", "descr", notifd);
        mmbni3 =
            new ModelMBeanNotificationInfo(new String[] {"type"}, "name", "descr");
        mmbni4 =
            new ModelMBeanNotificationInfo(new String[] {"type"}, "name",
                                           "descr", notifd);
        Descriptor mbeand = new DescriptorSupport(new String[] {
            "name=name", "descriptorType=mbean",
        });
        mmbi1 =
            new ModelMBeanInfoSupport("class", "descr", null, null, null, null);
        mmbi2 =
            new ModelMBeanInfoSupport("class", "descr", null, null, null, null,
                                      mbeand);
        mmbi3 =
            new ModelMBeanInfoSupport(
                "class", "descr",
                new ModelMBeanAttributeInfo[] {mmbai1, mmbai2},
                new ModelMBeanConstructorInfo[] {mmbci1, mmbci2, mmbci3, mmbci4},
                new ModelMBeanOperationInfo[] {mmboi1, mmboi2, mmboi3, mmboi4},
                new ModelMBeanNotificationInfo[] {mmbni1, mmbni2, mmbni3, mmbni4});
        mmbi4 =
            new ModelMBeanInfoSupport(
                "class", "descr",
                new ModelMBeanAttributeInfo[] {mmbai1, mmbai2},
                new ModelMBeanConstructorInfo[] {mmbci1, mmbci2, mmbci3, mmbci4},
                new ModelMBeanOperationInfo[] {mmboi1, mmboi2, mmboi3, mmboi4},
                new ModelMBeanNotificationInfo[] {mmbni1, mmbni2, mmbni3, mmbni4},
                mbeand);

        objects = new Serializable[] {
            mbai, mbpi, mbci1, mbci2, mbni1, mbni2, mboi1, mboi2, mbi1, mbi2,

            ombai1, ombai2, ombai3, ombai4,
            ombpi1, ombpi2, ombpi3, ombpi4,
            ombci1, ombci2,
            omboi1, omboi2,
            ombi1, ombi2,

            mmbai1, mmbai2,
            mmbci1, mmbci2, mmbci3, mmbci4,
            mmboi1, mmboi2, mmboi3, mmboi4,
            mmbni1, mmbni2, mmbni3, mmbni4,
        };
    }
}