1/*
2 * Copyright (c) 2004, 2012, 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 sun.management;
27
28import java.lang.management.ThreadInfo;
29import java.lang.management.MonitorInfo;
30import java.lang.management.LockInfo;
31import javax.management.openmbean.CompositeType;
32import javax.management.openmbean.CompositeData;
33import javax.management.openmbean.CompositeDataSupport;
34import javax.management.openmbean.OpenDataException;
35
36/**
37 * A CompositeData for ThreadInfo for the local management support.
38 * This class avoids the performance penalty paid to the
39 * construction of a CompositeData use in the local case.
40 */
41public class ThreadInfoCompositeData extends LazyCompositeData {
42    private final ThreadInfo threadInfo;
43    private final CompositeData cdata;
44    private final boolean currentVersion;
45    private final boolean hasV6;
46
47    private ThreadInfoCompositeData(ThreadInfo ti) {
48        this.threadInfo = ti;
49        this.currentVersion = true;
50        this.cdata = null;
51        this.hasV6 = true;
52    }
53
54    private ThreadInfoCompositeData(CompositeData cd) {
55        this.threadInfo = null;
56        this.currentVersion = ThreadInfoCompositeData.isCurrentVersion(cd);
57        this.cdata = cd;
58        this.hasV6 = ThreadInfoCompositeData.hasV6(cd);
59    }
60
61    public ThreadInfo getThreadInfo() {
62        return threadInfo;
63    }
64
65    public boolean hasV6() {
66        return hasV6;
67    }
68
69    public boolean isCurrentVersion() {
70        return currentVersion;
71    }
72
73    public static ThreadInfoCompositeData getInstance(CompositeData cd) {
74        validateCompositeData(cd);
75        return new ThreadInfoCompositeData(cd);
76    }
77
78    public static CompositeData toCompositeData(ThreadInfo ti) {
79        ThreadInfoCompositeData ticd = new ThreadInfoCompositeData(ti);
80        return ticd.getCompositeData();
81    }
82
83    protected CompositeData getCompositeData() {
84        // Convert StackTraceElement[] to CompositeData[]
85        StackTraceElement[] stackTrace = threadInfo.getStackTrace();
86        CompositeData[] stackTraceData =
87            new CompositeData[stackTrace.length];
88        for (int i = 0; i < stackTrace.length; i++) {
89            StackTraceElement ste = stackTrace[i];
90            stackTraceData[i] = StackTraceElementCompositeData.toCompositeData(ste);
91        }
92
93        // Convert MonitorInfo[] and LockInfo[] to CompositeData[]
94        CompositeData lockInfoData =
95            LockInfoCompositeData.toCompositeData(threadInfo.getLockInfo());
96
97        // Convert LockInfo[] and MonitorInfo[] to CompositeData[]
98        LockInfo[] lockedSyncs = threadInfo.getLockedSynchronizers();
99        CompositeData[] lockedSyncsData =
100            new CompositeData[lockedSyncs.length];
101        for (int i = 0; i < lockedSyncs.length; i++) {
102            LockInfo li = lockedSyncs[i];
103            lockedSyncsData[i] = LockInfoCompositeData.toCompositeData(li);
104        }
105
106        MonitorInfo[] lockedMonitors = threadInfo.getLockedMonitors();
107        CompositeData[] lockedMonitorsData =
108            new CompositeData[lockedMonitors.length];
109        for (int i = 0; i < lockedMonitors.length; i++) {
110            MonitorInfo mi = lockedMonitors[i];
111            lockedMonitorsData[i] = MonitorInfoCompositeData.toCompositeData(mi);
112        }
113
114        // CONTENTS OF THIS ARRAY MUST BE SYNCHRONIZED WITH
115        // threadInfoItemNames!
116        final Object[] threadInfoItemValues = {
117            threadInfo.getThreadId(),
118            threadInfo.getThreadName(),
119            threadInfo.getThreadState().name(),
120            threadInfo.getBlockedTime(),
121            threadInfo.getBlockedCount(),
122            threadInfo.getWaitedTime(),
123            threadInfo.getWaitedCount(),
124            lockInfoData,
125            threadInfo.getLockName(),
126            threadInfo.getLockOwnerId(),
127            threadInfo.getLockOwnerName(),
128            stackTraceData,
129                threadInfo.isSuspended(),
130                threadInfo.isInNative(),
131            lockedMonitorsData,
132            lockedSyncsData,
133            threadInfo.isDaemon(),
134            threadInfo.getPriority(),
135        };
136
137        try {
138            return new CompositeDataSupport(threadInfoCompositeType,
139                                            threadInfoItemNames,
140                                            threadInfoItemValues);
141        } catch (OpenDataException e) {
142            // Should never reach here
143            throw new AssertionError(e);
144        }
145    }
146
147    // Attribute names
148    private static final String THREAD_ID       = "threadId";
149    private static final String THREAD_NAME     = "threadName";
150    private static final String THREAD_STATE    = "threadState";
151    private static final String BLOCKED_TIME    = "blockedTime";
152    private static final String BLOCKED_COUNT   = "blockedCount";
153    private static final String WAITED_TIME     = "waitedTime";
154    private static final String WAITED_COUNT    = "waitedCount";
155    private static final String LOCK_INFO       = "lockInfo";
156    private static final String LOCK_NAME       = "lockName";
157    private static final String LOCK_OWNER_ID   = "lockOwnerId";
158    private static final String LOCK_OWNER_NAME = "lockOwnerName";
159    private static final String STACK_TRACE     = "stackTrace";
160    private static final String SUSPENDED       = "suspended";
161    private static final String IN_NATIVE       = "inNative";
162    private static final String DAEMON          = "daemon";
163    private static final String PRIORITY        = "priority";
164    private static final String LOCKED_MONITORS = "lockedMonitors";
165    private static final String LOCKED_SYNCS    = "lockedSynchronizers";
166
167    private static final String[] threadInfoItemNames = {
168        THREAD_ID,
169        THREAD_NAME,
170        THREAD_STATE,
171        BLOCKED_TIME,
172        BLOCKED_COUNT,
173        WAITED_TIME,
174        WAITED_COUNT,
175        LOCK_INFO,
176        LOCK_NAME,
177        LOCK_OWNER_ID,
178        LOCK_OWNER_NAME,
179        STACK_TRACE,
180        SUSPENDED,
181        IN_NATIVE,
182        LOCKED_MONITORS,
183        LOCKED_SYNCS,
184        DAEMON,
185        PRIORITY,
186    };
187
188    // New attributes added in 6.0 ThreadInfo
189    private static final String[] threadInfoV6Attributes = {
190        LOCK_INFO,
191        LOCKED_MONITORS,
192        LOCKED_SYNCS,
193    };
194
195    private static final String[] threadInfoV9Attributes = {
196        DAEMON,
197        PRIORITY,
198    };
199
200    // Current version of ThreadInfo
201    private static final CompositeType threadInfoCompositeType;
202    // Previous version of ThreadInfo
203    private static final CompositeType threadInfoV6CompositeType;
204    // Previous-previous version of ThreadInfo
205    private static final CompositeType threadInfoV5CompositeType;
206    private static final CompositeType lockInfoCompositeType;
207    static {
208        try {
209            threadInfoCompositeType = (CompositeType)
210                MappedMXBeanType.toOpenType(ThreadInfo.class);
211            // Form a CompositeType for JDK 5.0 ThreadInfo version
212
213            threadInfoV5CompositeType =
214                TypeVersionMapper.getInstance().getVersionedCompositeType(
215                    threadInfoCompositeType, TypeVersionMapper.V5
216                );
217
218            threadInfoV6CompositeType =
219                TypeVersionMapper.getInstance().getVersionedCompositeType(
220                    threadInfoCompositeType, TypeVersionMapper.V6
221                );
222        } catch (OpenDataException e) {
223            // Should never reach here
224            throw new AssertionError(e);
225        }
226
227        // Each CompositeData object has its CompositeType associated
228        // with it.  So we can get the CompositeType representing LockInfo
229        // from a mapped CompositeData for any LockInfo object.
230        // Thus we construct a random LockInfo object and pass it
231        // to LockInfoCompositeData to do the conversion.
232        Object o = new Object();
233        LockInfo li = new LockInfo(o.getClass().getName(),
234                                   System.identityHashCode(o));
235        CompositeData cd = LockInfoCompositeData.toCompositeData(li);
236        lockInfoCompositeType = cd.getCompositeType();
237    }
238
239    static boolean isV5Attribute(String itemName) {
240        for (String n : threadInfoV6Attributes) {
241            if (itemName.equals(n)) {
242                return false;
243            }
244        }
245        for (String n : threadInfoV9Attributes) {
246            if (itemName.equals(n)) {
247                return false;
248            }
249        }
250        return true;
251    }
252
253    static boolean isV6Attribute(String itemName) {
254        for (String n : threadInfoV9Attributes) {
255            if (itemName.equals(n)) {
256                return false;
257            }
258        }
259        return true;
260    }
261
262    public static boolean isCurrentVersion(CompositeData cd) {
263        if (cd == null) {
264            throw new NullPointerException("Null CompositeData");
265        }
266
267        return isTypeMatched(threadInfoCompositeType, cd.getCompositeType());
268    }
269
270    private static boolean hasV6(CompositeData cd) {
271        if (cd == null) {
272            throw new NullPointerException("Null CompositeData");
273        }
274
275        return isTypeMatched(threadInfoCompositeType, cd.getCompositeType()) ||
276               isTypeMatched(threadInfoV6CompositeType, cd.getCompositeType());
277     }
278
279    public long threadId() {
280        return getLong(cdata, THREAD_ID);
281    }
282
283    public String threadName() {
284        // The ThreadName item cannot be null so we check that
285        // it is present with a non-null value.
286        String name = getString(cdata, THREAD_NAME);
287        if (name == null) {
288            throw new IllegalArgumentException("Invalid composite data: " +
289                "Attribute " + THREAD_NAME + " has null value");
290        }
291        return name;
292    }
293
294    public Thread.State threadState() {
295        return Thread.State.valueOf(getString(cdata, THREAD_STATE));
296    }
297
298    public long blockedTime() {
299        return getLong(cdata, BLOCKED_TIME);
300    }
301
302    public long blockedCount() {
303        return getLong(cdata, BLOCKED_COUNT);
304    }
305
306    public long waitedTime() {
307        return getLong(cdata, WAITED_TIME);
308    }
309
310    public long waitedCount() {
311        return getLong(cdata, WAITED_COUNT);
312    }
313
314    public String lockName() {
315        // The LockName and LockOwnerName can legitimately be null,
316        // we don't bother to check the value
317        return getString(cdata, LOCK_NAME);
318    }
319
320    public long lockOwnerId() {
321        return getLong(cdata, LOCK_OWNER_ID);
322    }
323
324    public String lockOwnerName() {
325        return getString(cdata, LOCK_OWNER_NAME);
326    }
327
328    public boolean suspended() {
329        return getBoolean(cdata, SUSPENDED);
330    }
331
332    public boolean inNative() {
333        return getBoolean(cdata, IN_NATIVE);
334    }
335
336    public boolean isDaemon() {
337        return getBoolean(cdata, DAEMON);
338    }
339
340    public int getPriority(){
341        return getInt(cdata, PRIORITY);
342    }
343
344    public StackTraceElement[] stackTrace() {
345        CompositeData[] stackTraceData =
346            (CompositeData[]) cdata.get(STACK_TRACE);
347
348        // The StackTrace item cannot be null, but if it is we will get
349        // a NullPointerException when we ask for its length.
350        StackTraceElement[] stackTrace =
351            new StackTraceElement[stackTraceData.length];
352        for (int i = 0; i < stackTraceData.length; i++) {
353            CompositeData cdi = stackTraceData[i];
354            stackTrace[i] = StackTraceElementCompositeData.from(cdi);
355        }
356        return stackTrace;
357    }
358
359    // 6.0 new attributes
360    public LockInfo lockInfo() {
361        CompositeData lockInfoData = (CompositeData) cdata.get(LOCK_INFO);
362        return LockInfo.from(lockInfoData);
363    }
364
365    public MonitorInfo[] lockedMonitors() {
366        CompositeData[] lockedMonitorsData =
367            (CompositeData[]) cdata.get(LOCKED_MONITORS);
368
369        // The LockedMonitors item cannot be null, but if it is we will get
370        // a NullPointerException when we ask for its length.
371        MonitorInfo[] monitors =
372            new MonitorInfo[lockedMonitorsData.length];
373        for (int i = 0; i < lockedMonitorsData.length; i++) {
374            CompositeData cdi = lockedMonitorsData[i];
375            monitors[i] = MonitorInfo.from(cdi);
376        }
377        return monitors;
378    }
379
380    public LockInfo[] lockedSynchronizers() {
381        CompositeData[] lockedSyncsData =
382            (CompositeData[]) cdata.get(LOCKED_SYNCS);
383
384        // The LockedSynchronizers item cannot be null, but if it is we will
385        // get a NullPointerException when we ask for its length.
386        LockInfo[] locks = new LockInfo[lockedSyncsData.length];
387        for (int i = 0; i < lockedSyncsData.length; i++) {
388            CompositeData cdi = lockedSyncsData[i];
389            locks[i] = LockInfo.from(cdi);
390        }
391        return locks;
392    }
393
394    /** Validate if the input CompositeData has the expected
395     * CompositeType (i.e. contain all attributes with expected
396     * names and types).
397     */
398    public static void validateCompositeData(CompositeData cd) {
399        if (cd == null) {
400            throw new NullPointerException("Null CompositeData");
401        }
402
403        CompositeType type = cd.getCompositeType();
404        boolean currentVersion = true;
405        if (!isTypeMatched(threadInfoCompositeType, type)) {
406            currentVersion = false;
407            // check if cd is an older version
408            if (!isTypeMatched(threadInfoV5CompositeType, type) &&
409                !isTypeMatched(threadInfoV6CompositeType, type)) {
410                throw new IllegalArgumentException(
411                    "Unexpected composite type for ThreadInfo");
412            }
413        }
414
415        CompositeData[] stackTraceData =
416            (CompositeData[]) cd.get(STACK_TRACE);
417        if (stackTraceData == null) {
418            throw new IllegalArgumentException(
419                "StackTraceElement[] is missing");
420        }
421        if (stackTraceData.length > 0) {
422            StackTraceElementCompositeData.validateCompositeData(stackTraceData[0]);
423        }
424
425        // validate v6 attributes
426        if (currentVersion) {
427            CompositeData li = (CompositeData) cd.get(LOCK_INFO);
428            if (li != null) {
429                if (!isTypeMatched(lockInfoCompositeType,
430                                   li.getCompositeType())) {
431                    throw new IllegalArgumentException(
432                        "Unexpected composite type for \"" +
433                        LOCK_INFO + "\" attribute.");
434                }
435            }
436
437            CompositeData[] lms = (CompositeData[]) cd.get(LOCKED_MONITORS);
438            if (lms == null) {
439                throw new IllegalArgumentException("MonitorInfo[] is null");
440            }
441            if (lms.length > 0) {
442                MonitorInfoCompositeData.validateCompositeData(lms[0]);
443            }
444
445            CompositeData[] lsyncs = (CompositeData[]) cd.get(LOCKED_SYNCS);
446            if (lsyncs == null) {
447                throw new IllegalArgumentException("LockInfo[] is null");
448            }
449            if (lsyncs.length > 0) {
450                if (!isTypeMatched(lockInfoCompositeType,
451                                   lsyncs[0].getCompositeType())) {
452                    throw new IllegalArgumentException(
453                        "Unexpected composite type for \"" +
454                        LOCKED_SYNCS + "\" attribute.");
455                }
456            }
457
458        }
459    }
460
461    private static final long serialVersionUID = 2464378539119753175L;
462}
463