1/*
2 * Copyright (c) 1997, 2017, 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.activation;
27
28import java.io.*;
29import java.lang.reflect.InvocationTargetException;
30import java.lang.reflect.Method;
31import java.security.AccessController;
32import java.security.PrivilegedAction;
33
34/**
35 * The CommandInfo class is used by CommandMap implementations to
36 * describe the results of command requests. It provides the requestor
37 * with both the verb requested, as well as an instance of the
38 * bean. There is also a method that will return the name of the
39 * class that implements the command but <i>it is not guaranteed to
40 * return a valid value</i>. The reason for this is to allow CommandMap
41 * implmentations that subclass CommandInfo to provide special
42 * behavior. For example a CommandMap could dynamically generate
43 * JavaBeans. In this case, it might not be possible to create an
44 * object with all the correct state information solely from the class
45 * name.
46 *
47 * @since 1.6
48 */
49
50public class CommandInfo {
51    private String verb;
52    private String className;
53
54    /**
55     * The Constructor for CommandInfo.
56     * @param verb The command verb this CommandInfo decribes.
57     * @param className The command's fully qualified class name.
58     */
59    public CommandInfo(String verb, String className) {
60        this.verb = verb;
61        this.className = className;
62    }
63
64    /**
65     * Return the command verb.
66     *
67     * @return the command verb.
68     */
69    public String getCommandName() {
70        return verb;
71    }
72
73    /**
74     * Return the command's class name. <i>This method MAY return null in
75     * cases where a CommandMap subclassed CommandInfo for its
76     * own purposes.</i> In other words, it might not be possible to
77     * create the correct state in the command by merely knowing
78     * its class name. <b>DO NOT DEPEND ON THIS METHOD RETURNING
79     * A VALID VALUE!</b>
80     *
81     * @return The class name of the command, or <i>null</i>
82     */
83    public String getCommandClass() {
84        return className;
85    }
86
87    /**
88     * Return the instantiated JavaBean component.
89     * <p>
90     * If {@code java.beans.Beans} is visible then it's
91     * {@code java.beans.Beans#instantiate} method is invoked to instantiate
92     * the component as a JavaBeans component.
93     * When {@code java.beans.Beans} is not visible (when {@code java.desktop}
94     * module is not  readable or when the runtime image does not contain the
95     * {@code java.desktop} module) then the command's class is loaded and
96     * instantiated with its public no-args constructor.
97     * <p>
98     * The component class needs to be public.
99     * <p>
100     * If the bean implements the {@code javax.activation.CommandObject}
101     * interface, call its {@code setCommandContext} method.
102     * <p>
103     * If the DataHandler parameter is null, then the bean is
104     * instantiated with no data. NOTE: this may be useful
105     * if for some reason the DataHandler that is passed in
106     * throws IOExceptions when this method attempts to
107     * access its InputStream. It will allow the caller to
108     * retrieve a reference to the bean if it can be
109     * instantiated.
110     * <p>
111     * If the bean does NOT implement the CommandObject interface,
112     * this method will check if it implements the
113     * java.io.Externalizable interface. If it does, the bean's
114     * readExternal method will be called if an InputStream
115     * can be acquired from the DataHandler.
116     *
117     * @param dh        The DataHandler that describes the data to be
118     *                  passed to the command.
119     * @param loader    The ClassLoader to be used to instantiate the bean.
120     * @return The bean
121     * @exception       IOException     for failures reading data
122     * @exception       ClassNotFoundException  if command object class can't
123     *                                          be found
124     * @see java.beans.Beans#instantiate
125     * @see javax.activation.CommandObject
126     */
127    public Object getCommandObject(DataHandler dh, ClassLoader loader)
128                        throws IOException, ClassNotFoundException {
129        Object new_bean = null;
130
131        // try to instantiate the bean
132        new_bean = Beans.instantiate(loader, className);
133
134        // if we got one and it is a CommandObject
135        if (new_bean != null) {
136            if (new_bean instanceof CommandObject) {
137                ((CommandObject)new_bean).setCommandContext(verb, dh);
138            } else if (new_bean instanceof Externalizable) {
139                if (dh != null) {
140                    InputStream is = dh.getInputStream();
141                    if (is != null) {
142                        ((Externalizable)new_bean).readExternal(
143                                               new ObjectInputStream(is));
144                    }
145                }
146            }
147        }
148
149        return new_bean;
150    }
151
152    /**
153     * Helper class to invoke Beans.instantiate reflectively or the equivalent
154     * with core reflection when module java.desktop is not readable.
155     */
156    private static final class Beans {
157        static final Method instantiateMethod;
158
159        static {
160            Method m;
161            try {
162                Class<?> c = Class.forName("java.beans.Beans");
163                m = c.getDeclaredMethod("instantiate", ClassLoader.class, String.class);
164            } catch (ClassNotFoundException e) {
165                m = null;
166            } catch (NoSuchMethodException e) {
167                m = null;
168            }
169            instantiateMethod = m;
170        }
171
172        /**
173         * Equivalent to invoking java.beans.Beans.instantiate(loader, cn)
174         */
175        static Object instantiate(ClassLoader loader, String cn)
176                throws IOException, ClassNotFoundException {
177
178            Exception exception;
179
180            if (instantiateMethod != null) {
181
182                // invoke Beans.instantiate
183                try {
184                    return instantiateMethod.invoke(null, loader, cn);
185                } catch (InvocationTargetException e) {
186                    exception = e;
187                } catch (IllegalAccessException e) {
188                    exception = e;
189                }
190
191            } else {
192
193                SecurityManager security = System.getSecurityManager();
194                if (security != null) {
195                    // if it's ok with the SecurityManager, it's ok with me.
196                    String cname = cn.replace('/', '.');
197                    if (cname.startsWith("[")) {
198                        int b = cname.lastIndexOf('[') + 2;
199                        if (b > 1 && b < cname.length()) {
200                            cname = cname.substring(b);
201                        }
202                    }
203                    int i = cname.lastIndexOf('.');
204                    if (i != -1) {
205                        security.checkPackageAccess(cname.substring(0, i));
206                    }
207                }
208
209                // Beans.instantiate specified to use SCL when loader is null
210                if (loader == null) {
211                    loader = (ClassLoader)
212                        AccessController.doPrivileged(new PrivilegedAction() {
213                            public Object run() {
214                                ClassLoader cl = null;
215                                try {
216                                    cl = ClassLoader.getSystemClassLoader();
217                                } catch (SecurityException ex) { }
218                                return cl;
219                            }
220                        });
221                }
222                Class<?> beanClass = Class.forName(cn, false, loader);
223                try {
224                    return beanClass.getDeclaredConstructor().newInstance();
225                } catch (Exception ex) {
226                    throw new ClassNotFoundException(beanClass + ": " + ex, ex);
227                }
228
229            }
230            return null;
231        }
232    }
233}
234