1 /*
2  * Copyright (c) 2000, 2021, 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 
26 package javax.management.relation;
27 
28 import static com.sun.jmx.mbeanserver.Util.cast;
29 import static com.sun.jmx.defaults.JmxProperties.RELATION_LOGGER;
30 import com.sun.jmx.mbeanserver.GetPropertyAction;
31 
32 import java.io.IOException;
33 import java.io.ObjectInputStream;
34 import java.io.ObjectOutputStream;
35 import java.io.ObjectStreamField;
36 import java.security.AccessController;
37 
38 import java.util.List;
39 import java.util.Vector;
40 
41 import javax.management.MBeanServerNotification;
42 
43 import javax.management.Notification;
44 import javax.management.NotificationFilterSupport;
45 import javax.management.ObjectName;
46 
47 import java.util.List;
48 import java.lang.System.Logger.Level;
49 import java.util.Vector;
50 
51 /**
52  * Filter for {@link MBeanServerNotification}.
53  * This filter filters MBeanServerNotification notifications by
54  * selecting the ObjectNames of interest and the operations (registration,
55  * unregistration, both) of interest (corresponding to notification
56  * types).
57  *
58  * <p>The <b>serialVersionUID</b> of this class is <code>2605900539589789736L</code>.
59  *
60  * @since 1.5
61  */
62 @SuppressWarnings("serial")  // serialVersionUID must be constant
63 public class MBeanServerNotificationFilter extends NotificationFilterSupport {
64 
65     // Serialization compatibility stuff:
66     // Two serial forms are supported in this class. The selected form depends
67     // on system property "jmx.serial.form":
68     //  - "1.0" for JMX 1.0
69     //  - any other value for JMX 1.1 and higher
70     //
71     // Serial version for old serial form
72     private static final long oldSerialVersionUID = 6001782699077323605L;
73     //
74     // Serial version for new serial form
75     private static final long newSerialVersionUID = 2605900539589789736L;
76     //
77     // Serializable fields in old serial form
78     private static final ObjectStreamField[] oldSerialPersistentFields =
79     {
80       new ObjectStreamField("mySelectObjNameList", Vector.class),
81       new ObjectStreamField("myDeselectObjNameList", Vector.class)
82     };
83     //
84     // Serializable fields in new serial form
85     private static final ObjectStreamField[] newSerialPersistentFields =
86     {
87       new ObjectStreamField("selectedNames", List.class),
88       new ObjectStreamField("deselectedNames", List.class)
89     };
90     //
91     // Actual serial version and serial form
92     private static final long serialVersionUID;
93     /**
94      * @serialField selectedNames List List of {@link ObjectName}s of interest
95      *         <ul>
96      *         <li><code>null</code> means that all {@link ObjectName}s are implicitly selected
97      *         (check for explicit deselections)</li>
98      *         <li>Empty vector means that no {@link ObjectName} is explicitly selected</li>
99      *         </ul>
100      * @serialField deselectedNames List List of {@link ObjectName}s with no interest
101      *         <ul>
102      *         <li><code>null</code> means that all {@link ObjectName}s are implicitly deselected
103      *         (check for explicit selections))</li>
104      *         <li>Empty vector means that no {@link ObjectName} is explicitly deselected</li>
105      *         </ul>
106      */
107     private static final ObjectStreamField[] serialPersistentFields;
108     private static boolean compat = false;
109     static {
110         try {
111             GetPropertyAction act = new GetPropertyAction("jmx.serial.form");
112             @SuppressWarnings("removal")
113             String form = AccessController.doPrivileged(act);
114             compat = (form != null && form.equals("1.0"));
115         } catch (Exception e) {
116             // OK : Too bad, no compat with 1.0
117         }
118         if (compat) {
119             serialPersistentFields = oldSerialPersistentFields;
120             serialVersionUID = oldSerialVersionUID;
121         } else {
122             serialPersistentFields = newSerialPersistentFields;
123             serialVersionUID = newSerialVersionUID;
124         }
125     }
126     //
127     // END Serialization compatibility stuff
128 
129     //
130     // Private members
131     //
132 
133     /**
134      * @serial List of {@link ObjectName}s of interest
135      *         <ul>
136      *         <li><code>null</code> means that all {@link ObjectName}s are implicitly selected
137      *         (check for explicit deselections)</li>
138      *         <li>Empty vector means that no {@link ObjectName} is explicitly selected</li>
139      *         </ul>
140      */
141     private List<ObjectName> selectedNames = new Vector<ObjectName>();
142 
143     /**
144      * @serial List of {@link ObjectName}s with no interest
145      *         <ul>
146      *         <li><code>null</code> means that all {@link ObjectName}s are implicitly deselected
147      *         (check for explicit selections))</li>
148      *         <li>Empty vector means that no {@link ObjectName} is explicitly deselected</li>
149      *         </ul>
150      */
151     private List<ObjectName> deselectedNames = null;
152 
153     //
154     // Constructor
155     //
156 
157     /**
158      * Creates a filter selecting all MBeanServerNotification notifications for
159      * all ObjectNames.
160      */
MBeanServerNotificationFilter()161     public MBeanServerNotificationFilter() {
162 
163         super();
164         RELATION_LOGGER.log(Level.TRACE, "ENTRY");
165 
166         enableType(MBeanServerNotification.REGISTRATION_NOTIFICATION);
167         enableType(MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
168 
169         RELATION_LOGGER.log(Level.TRACE, "RETURN");
170         return;
171     }
172 
173     //
174     // Accessors
175     //
176 
177     /**
178      * Disables any MBeanServerNotification (all ObjectNames are
179      * deselected).
180      */
disableAllObjectNames()181     public synchronized void disableAllObjectNames() {
182 
183         RELATION_LOGGER.log(Level.TRACE, "ENTRY");
184 
185         selectedNames = new Vector<ObjectName>();
186         deselectedNames = null;
187 
188         RELATION_LOGGER.log(Level.TRACE, "RETURN");
189         return;
190     }
191 
192     /**
193      * Disables MBeanServerNotifications concerning given ObjectName.
194      *
195      * @param objectName  ObjectName no longer of interest
196      *
197      * @exception IllegalArgumentException  if the given ObjectName is null
198      */
disableObjectName(ObjectName objectName)199     public synchronized void disableObjectName(ObjectName objectName)
200         throws IllegalArgumentException {
201 
202         if (objectName == null) {
203             String excMsg = "Invalid parameter.";
204             throw new IllegalArgumentException(excMsg);
205         }
206 
207         RELATION_LOGGER.log(Level.TRACE, "ENTRY {0}" + objectName);
208 
209         // Removes from selected ObjectNames, if present
210         if (selectedNames != null) {
211             if (selectedNames.size() != 0) {
212                 selectedNames.remove(objectName);
213             }
214         }
215 
216         // Adds it in deselected ObjectNames
217         if (deselectedNames != null) {
218             // If all are deselected, no need to do anything :)
219             if (!(deselectedNames.contains(objectName))) {
220                 // ObjectName was not already deselected
221                 deselectedNames.add(objectName);
222             }
223         }
224 
225         RELATION_LOGGER.log(Level.TRACE, "RETURN");
226         return;
227     }
228 
229     /**
230      * Enables all MBeanServerNotifications (all ObjectNames are selected).
231      */
enableAllObjectNames()232     public synchronized void enableAllObjectNames() {
233 
234         RELATION_LOGGER.log(Level.TRACE, "ENTRY");
235 
236         selectedNames = null;
237         deselectedNames = new Vector<ObjectName>();
238 
239         RELATION_LOGGER.log(Level.TRACE, "RETURN");
240         return;
241     }
242 
243     /**
244      * Enables MBeanServerNotifications concerning given ObjectName.
245      *
246      * @param objectName  ObjectName of interest
247      *
248      * @exception IllegalArgumentException  if the given ObjectName is null
249      */
enableObjectName(ObjectName objectName)250     public synchronized void enableObjectName(ObjectName objectName)
251         throws IllegalArgumentException {
252 
253         if (objectName == null) {
254             String excMsg = "Invalid parameter.";
255             throw new IllegalArgumentException(excMsg);
256         }
257 
258         RELATION_LOGGER.log(Level.TRACE, "ENTRY {0}", objectName);
259 
260         // Removes from deselected ObjectNames, if present
261         if (deselectedNames != null) {
262             if (deselectedNames.size() != 0) {
263                 deselectedNames.remove(objectName);
264             }
265         }
266 
267         // Adds it in selected ObjectNames
268         if (selectedNames != null) {
269             // If all are selected, no need to do anything :)
270             if (!(selectedNames.contains(objectName))) {
271                 // ObjectName was not already selected
272                 selectedNames.add(objectName);
273             }
274         }
275 
276         RELATION_LOGGER.log(Level.TRACE, "RETURN");
277         return;
278     }
279 
280     /**
281      * Gets all the ObjectNames enabled.
282      *
283      * @return Vector of ObjectNames:
284      * <P>- null means all ObjectNames are implicitly selected, except the
285      * ObjectNames explicitly deselected
286      * <P>- empty means all ObjectNames are deselected, i.e. no ObjectName
287      * selected.
288      */
getEnabledObjectNames()289     public synchronized Vector<ObjectName> getEnabledObjectNames() {
290         if (selectedNames != null) {
291             return new Vector<ObjectName>(selectedNames);
292         } else {
293             return null;
294         }
295     }
296 
297     /**
298      * Gets all the ObjectNames disabled.
299      *
300      * @return Vector of ObjectNames:
301      * <P>- null means all ObjectNames are implicitly deselected, except the
302      * ObjectNames explicitly selected
303      * <P>- empty means all ObjectNames are selected, i.e. no ObjectName
304      * deselected.
305      */
getDisabledObjectNames()306     public synchronized Vector<ObjectName> getDisabledObjectNames() {
307         if (deselectedNames != null) {
308             return new Vector<ObjectName>(deselectedNames);
309         } else {
310             return null;
311         }
312     }
313 
314     //
315     // NotificationFilter interface
316     //
317 
318     /**
319      * Invoked before sending the specified notification to the listener.
320      * <P>If:
321      * <P>- the ObjectName of the concerned MBean is selected (explicitly OR
322      * (implicitly and not explicitly deselected))
323      * <P>AND
324      * <P>- the type of the operation (registration or unregistration) is
325      * selected
326      * <P>then the notification is sent to the listener.
327      *
328      * @param notif  The notification to be sent.
329      *
330      * @return true if the notification has to be sent to the listener, false
331      * otherwise.
332      *
333      * @exception IllegalArgumentException  if null parameter
334      */
isNotificationEnabled(Notification notif)335     public synchronized boolean isNotificationEnabled(Notification notif)
336         throws IllegalArgumentException {
337 
338         if (notif == null) {
339             String excMsg = "Invalid parameter.";
340             throw new IllegalArgumentException(excMsg);
341         }
342 
343         RELATION_LOGGER.log(Level.TRACE, "ENTRY {0}", notif);
344 
345         // Checks the type first
346         String ntfType = notif.getType();
347         Vector<String> enabledTypes = getEnabledTypes();
348         if (!(enabledTypes.contains(ntfType))) {
349             RELATION_LOGGER.log(Level.TRACE,
350                     "Type not selected, exiting");
351             return false;
352         }
353 
354         // We have a MBeanServerNotification: downcasts it
355         MBeanServerNotification mbsNtf = (MBeanServerNotification)notif;
356 
357         // Checks the ObjectName
358         ObjectName objName = mbsNtf.getMBeanName();
359         // Is it selected?
360         boolean isSelectedFlg = false;
361         if (selectedNames != null) {
362             // Not all are implicitly selected:
363             // checks for explicit selection
364             if (selectedNames.size() == 0) {
365                 // All are explicitly not selected
366                 RELATION_LOGGER.log(Level.TRACE,
367                         "No ObjectNames selected, exiting");
368                 return false;
369             }
370 
371             isSelectedFlg = selectedNames.contains(objName);
372             if (!isSelectedFlg) {
373                 // Not in the explicit selected list
374                 RELATION_LOGGER.log(Level.TRACE,
375                         "ObjectName not in selected list, exiting");
376                 return false;
377             }
378         }
379 
380         if (!isSelectedFlg) {
381             // Not explicitly selected: is it deselected?
382 
383             if (deselectedNames == null) {
384                 // All are implicitly deselected and it is not explicitly
385                 // selected
386                 RELATION_LOGGER.log(Level.TRACE,
387                         "ObjectName not selected, and all " +
388                         "names deselected, exiting");
389                 return false;
390 
391             } else if (deselectedNames.contains(objName)) {
392                 // Explicitly deselected
393                 RELATION_LOGGER.log(Level.TRACE,
394                         "ObjectName explicitly not selected, exiting");
395                 return false;
396             }
397         }
398 
399         RELATION_LOGGER.log(Level.TRACE,
400                 "ObjectName selected, exiting");
401         return true;
402     }
403 
404 
405     /**
406      * Deserializes an {@link MBeanServerNotificationFilter} from an {@link ObjectInputStream}.
407      */
readObject(ObjectInputStream in)408     private void readObject(ObjectInputStream in)
409             throws IOException, ClassNotFoundException {
410       if (compat)
411       {
412         // Read an object serialized in the old serial form
413         //
414         ObjectInputStream.GetField fields = in.readFields();
415         selectedNames = cast(fields.get("mySelectObjNameList", null));
416         if (fields.defaulted("mySelectObjNameList"))
417         {
418           throw new NullPointerException("mySelectObjNameList");
419         }
420         deselectedNames = cast(fields.get("myDeselectObjNameList", null));
421         if (fields.defaulted("myDeselectObjNameList"))
422         {
423           throw new NullPointerException("myDeselectObjNameList");
424         }
425       }
426       else
427       {
428         // Read an object serialized in the new serial form
429         //
430         in.defaultReadObject();
431       }
432     }
433 
434 
435     /**
436      * Serializes an {@link MBeanServerNotificationFilter} to an {@link ObjectOutputStream}.
437      */
writeObject(ObjectOutputStream out)438     private void writeObject(ObjectOutputStream out)
439             throws IOException {
440       if (compat)
441       {
442         // Serializes this instance in the old serial form
443         //
444         ObjectOutputStream.PutField fields = out.putFields();
445         fields.put("mySelectObjNameList", selectedNames);
446         fields.put("myDeselectObjNameList", deselectedNames);
447         out.writeFields();
448       }
449       else
450       {
451         // Serializes this instance in the new serial form
452         //
453         out.defaultWriteObject();
454       }
455     }
456 }
457