/*
 * Copyright (c) 1998, 2017, 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * 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.
 */

package com.sun.tools.jdi;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.sun.jdi.Field;
import com.sun.jdi.Location;
import com.sun.jdi.NativeMethodException;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.request.AccessWatchpointRequest;
import com.sun.jdi.request.BreakpointRequest;
import com.sun.jdi.request.ClassPrepareRequest;
import com.sun.jdi.request.ClassUnloadRequest;
import com.sun.jdi.request.DuplicateRequestException;
import com.sun.jdi.request.EventRequest;
import com.sun.jdi.request.EventRequestManager;
import com.sun.jdi.request.ExceptionRequest;
import com.sun.jdi.request.InvalidRequestStateException;
import com.sun.jdi.request.MethodEntryRequest;
import com.sun.jdi.request.MethodExitRequest;
import com.sun.jdi.request.ModificationWatchpointRequest;
import com.sun.jdi.request.MonitorContendedEnterRequest;
import com.sun.jdi.request.MonitorContendedEnteredRequest;
import com.sun.jdi.request.MonitorWaitRequest;
import com.sun.jdi.request.MonitorWaitedRequest;
import com.sun.jdi.request.StepRequest;
import com.sun.jdi.request.ThreadDeathRequest;
import com.sun.jdi.request.ThreadStartRequest;
import com.sun.jdi.request.VMDeathRequest;
import com.sun.jdi.request.WatchpointRequest;

This interface is used to create and remove Breakpoints, Watchpoints, etc. It include implementations of all the request interfaces..
/** * This interface is used to create and remove Breakpoints, Watchpoints, * etc. * It include implementations of all the request interfaces.. */
// Warnings from List filters and List[] requestLists is hard to fix. // Remove SuppressWarning when we fix the warnings from List filters // and List[] requestLists. The generic array is not supported. @SuppressWarnings({"unchecked", "rawtypes"}) class EventRequestManagerImpl extends MirrorImpl implements EventRequestManager { private final List<? extends EventRequest>[] requestLists; private static int methodExitEventCmd = 0; static int JDWPtoJDISuspendPolicy(byte jdwpPolicy) { switch(jdwpPolicy) { case JDWP.SuspendPolicy.ALL: return EventRequest.SUSPEND_ALL; case JDWP.SuspendPolicy.EVENT_THREAD: return EventRequest.SUSPEND_EVENT_THREAD; case JDWP.SuspendPolicy.NONE: return EventRequest.SUSPEND_NONE; default: throw new IllegalArgumentException("Illegal policy constant: " + jdwpPolicy); } } static byte JDItoJDWPSuspendPolicy(int jdiPolicy) { switch(jdiPolicy) { case EventRequest.SUSPEND_ALL: return JDWP.SuspendPolicy.ALL; case EventRequest.SUSPEND_EVENT_THREAD: return JDWP.SuspendPolicy.EVENT_THREAD; case EventRequest.SUSPEND_NONE: return JDWP.SuspendPolicy.NONE; default: throw new IllegalArgumentException("Illegal policy constant: " + jdiPolicy); } } /* * Override superclass back to default equality */ public boolean equals(Object obj) { return this == obj; } public int hashCode() { return System.identityHashCode(this); } private abstract class EventRequestImpl extends MirrorImpl implements EventRequest { int id; /* * This list is not protected by a synchronized wrapper. All * access/modification should be protected by synchronizing on * the enclosing instance of EventRequestImpl. */ List<Object> filters = new ArrayList<>(); boolean isEnabled = false; boolean deleted = false; byte suspendPolicy = JDWP.SuspendPolicy.ALL; private Map<Object, Object> clientProperties = null; EventRequestImpl() { super(EventRequestManagerImpl.this.vm); } /* * Override superclass back to default equality */ public boolean equals(Object obj) { return this == obj; } public int hashCode() { return System.identityHashCode(this); } abstract int eventCmd(); InvalidRequestStateException invalidState() { return new InvalidRequestStateException(toString()); } String state() { return deleted? " (deleted)" : (isEnabled()? " (enabled)" : " (disabled)"); }
Returns:all the event request of this kind
/** * @return all the event request of this kind */
List requestList() { return EventRequestManagerImpl.this.requestList(eventCmd()); }
delete the event request
/** * delete the event request */
void delete() { if (!deleted) { requestList().remove(this); disable(); /* must do BEFORE delete */ deleted = true; } } public boolean isEnabled() { return isEnabled; } public void enable() { setEnabled(true); } public void disable() { setEnabled(false); } public synchronized void setEnabled(boolean val) { if (deleted) { throw invalidState(); } else { if (val != isEnabled) { if (isEnabled) { clear(); } else { set(); } } } } public synchronized void addCountFilter(int count) { if (isEnabled() || deleted) { throw invalidState(); } if (count < 1) { throw new IllegalArgumentException("count is less than one"); } filters.add(JDWP.EventRequest.Set.Modifier.Count.create(count)); } public void setSuspendPolicy(int policy) { if (isEnabled() || deleted) { throw invalidState(); } suspendPolicy = JDItoJDWPSuspendPolicy(policy); } public int suspendPolicy() { return JDWPtoJDISuspendPolicy(suspendPolicy); }
set (enable) the event request
/** * set (enable) the event request */
synchronized void set() { JDWP.EventRequest.Set.Modifier[] mods = filters.toArray( new JDWP.EventRequest.Set.Modifier[filters.size()]); try { id = JDWP.EventRequest.Set.process(vm, (byte)eventCmd(), suspendPolicy, mods).requestID; } catch (JDWPException exc) { throw exc.toJDIException(); } isEnabled = true; } synchronized void clear() { try { JDWP.EventRequest.Clear.process(vm, (byte)eventCmd(), id); } catch (JDWPException exc) { throw exc.toJDIException(); } isEnabled = false; }
See Also:
Returns:a small Map
/** * @return a small Map * @see #putProperty * @see #getProperty */
private Map<Object, Object> getProperties() { if (clientProperties == null) { clientProperties = new HashMap<>(2); } return clientProperties; }
Returns the value of the property with the specified key. Only properties added with putProperty will return a non-null value.
See Also:
Returns:the value of this property or null
/** * Returns the value of the property with the specified key. Only * properties added with <code>putProperty</code> will return * a non-null value. * * @return the value of this property or null * @see #putProperty */
public final Object getProperty(Object key) { if (clientProperties == null) { return null; } else { return getProperties().get(key); } }
Add an arbitrary key/value "property" to this component.
See Also:
  • getProperty
/** * Add an arbitrary key/value "property" to this component. * * @see #getProperty */
public final void putProperty(Object key, Object value) { if (value != null) { getProperties().put(key, value); } else { getProperties().remove(key); } } } abstract class ThreadVisibleEventRequestImpl extends EventRequestImpl { public synchronized void addThreadFilter(ThreadReference thread) { validateMirror(thread); if (isEnabled() || deleted) { throw invalidState(); } filters.add(JDWP.EventRequest.Set.Modifier.ThreadOnly .create((ThreadReferenceImpl)thread)); } } abstract class ClassVisibleEventRequestImpl extends ThreadVisibleEventRequestImpl { public synchronized void addClassFilter(ReferenceType clazz) { validateMirror(clazz); if (isEnabled() || deleted) { throw invalidState(); } filters.add(JDWP.EventRequest.Set.Modifier.ClassOnly .create((ReferenceTypeImpl)clazz)); } public synchronized void addClassFilter(String classPattern) { if (isEnabled() || deleted) { throw invalidState(); } if (classPattern == null) { throw new NullPointerException(); } filters.add(JDWP.EventRequest.Set.Modifier.ClassMatch .create(classPattern)); } public synchronized void addClassExclusionFilter(String classPattern) { if (isEnabled() || deleted) { throw invalidState(); } if (classPattern == null) { throw new NullPointerException(); } filters.add(JDWP.EventRequest.Set.Modifier.ClassExclude .create(classPattern)); } public synchronized void addInstanceFilter(ObjectReference instance) { validateMirror(instance); if (isEnabled() || deleted) { throw invalidState(); } if (!vm.canUseInstanceFilters()) { throw new UnsupportedOperationException( "target does not support instance filters"); } filters.add(JDWP.EventRequest.Set.Modifier.InstanceOnly .create((ObjectReferenceImpl)instance)); } } class BreakpointRequestImpl extends ClassVisibleEventRequestImpl implements BreakpointRequest { private final Location location; BreakpointRequestImpl(Location location) { this.location = location; filters.add(0,JDWP.EventRequest.Set.Modifier.LocationOnly .create(location)); requestList().add(this); } public Location location() { return location; } int eventCmd() { return JDWP.EventKind.BREAKPOINT; } public String toString() { return "breakpoint request " + location() + state(); } } class ClassPrepareRequestImpl extends ClassVisibleEventRequestImpl implements ClassPrepareRequest { ClassPrepareRequestImpl() { requestList().add(this); } int eventCmd() { return JDWP.EventKind.CLASS_PREPARE; } public synchronized void addSourceNameFilter(String sourceNamePattern) { if (isEnabled() || deleted) { throw invalidState(); } if (!vm.canUseSourceNameFilters()) { throw new UnsupportedOperationException( "target does not support source name filters"); } if (sourceNamePattern == null) { throw new NullPointerException(); } filters.add(JDWP.EventRequest.Set.Modifier.SourceNameMatch .create(sourceNamePattern)); } public String toString() { return "class prepare request " + state(); } } class ClassUnloadRequestImpl extends ClassVisibleEventRequestImpl implements ClassUnloadRequest { ClassUnloadRequestImpl() { requestList().add(this); } int eventCmd() { return JDWP.EventKind.CLASS_UNLOAD; } public String toString() { return "class unload request " + state(); } } class ExceptionRequestImpl extends ClassVisibleEventRequestImpl implements ExceptionRequest { ReferenceType exception = null; boolean caught = true; boolean uncaught = true; ExceptionRequestImpl(ReferenceType refType, boolean notifyCaught, boolean notifyUncaught) { exception = refType; caught = notifyCaught; uncaught = notifyUncaught; { ReferenceTypeImpl exc; if (exception == null) { exc = new ClassTypeImpl(vm, 0); } else { exc = (ReferenceTypeImpl)exception; } filters.add(JDWP.EventRequest.Set.Modifier.ExceptionOnly. create(exc, caught, uncaught)); } requestList().add(this); } public ReferenceType exception() { return exception; } public boolean notifyCaught() { return caught; } public boolean notifyUncaught() { return uncaught; } int eventCmd() { return JDWP.EventKind.EXCEPTION; } public String toString() { return "exception request " + exception() + state(); } } class MethodEntryRequestImpl extends ClassVisibleEventRequestImpl implements MethodEntryRequest { MethodEntryRequestImpl() { requestList().add(this); } int eventCmd() { return JDWP.EventKind.METHOD_ENTRY; } public String toString() { return "method entry request " + state(); } } class MethodExitRequestImpl extends ClassVisibleEventRequestImpl implements MethodExitRequest { MethodExitRequestImpl() { if (methodExitEventCmd == 0) { /* * If we can get return values, then we always get them. * Thus, for JDI MethodExitRequests, we always use the * same JDWP EventKind. Here we decide which to use and * save it so that it will be used for all future * MethodExitRequests. * * This call to canGetMethodReturnValues can't * be done in the EventRequestManager ctor because that is too early. */ if (vm.canGetMethodReturnValues()) { methodExitEventCmd = JDWP.EventKind.METHOD_EXIT_WITH_RETURN_VALUE; } else { methodExitEventCmd = JDWP.EventKind.METHOD_EXIT; } } requestList().add(this); } int eventCmd() { return EventRequestManagerImpl.methodExitEventCmd; } public String toString() { return "method exit request " + state(); } } class MonitorContendedEnterRequestImpl extends ClassVisibleEventRequestImpl implements MonitorContendedEnterRequest { MonitorContendedEnterRequestImpl() { requestList().add(this); } int eventCmd() { return JDWP.EventKind.MONITOR_CONTENDED_ENTER; } public String toString() { return "monitor contended enter request " + state(); } } class MonitorContendedEnteredRequestImpl extends ClassVisibleEventRequestImpl implements MonitorContendedEnteredRequest { MonitorContendedEnteredRequestImpl() { requestList().add(this); } int eventCmd() { return JDWP.EventKind.MONITOR_CONTENDED_ENTERED; } public String toString() { return "monitor contended entered request " + state(); } } class MonitorWaitRequestImpl extends ClassVisibleEventRequestImpl implements MonitorWaitRequest { MonitorWaitRequestImpl() { requestList().add(this); } int eventCmd() { return JDWP.EventKind.MONITOR_WAIT; } public String toString() { return "monitor wait request " + state(); } } class MonitorWaitedRequestImpl extends ClassVisibleEventRequestImpl implements MonitorWaitedRequest { MonitorWaitedRequestImpl() { requestList().add(this); } int eventCmd() { return JDWP.EventKind.MONITOR_WAITED; } public String toString() { return "monitor waited request " + state(); } } class StepRequestImpl extends ClassVisibleEventRequestImpl implements StepRequest { ThreadReferenceImpl thread; int size; int depth; StepRequestImpl(ThreadReference thread, int size, int depth) { this.thread = (ThreadReferenceImpl)thread; this.size = size; this.depth = depth; /* * Translate size and depth to corresponding JDWP values. */ int jdwpSize; switch (size) { case STEP_MIN: jdwpSize = JDWP.StepSize.MIN; break; case STEP_LINE: jdwpSize = JDWP.StepSize.LINE; break; default: throw new IllegalArgumentException("Invalid step size"); } int jdwpDepth; switch (depth) { case STEP_INTO: jdwpDepth = JDWP.StepDepth.INTO; break; case STEP_OVER: jdwpDepth = JDWP.StepDepth.OVER; break; case STEP_OUT: jdwpDepth = JDWP.StepDepth.OUT; break; default: throw new IllegalArgumentException("Invalid step depth"); } /* * Make sure this isn't a duplicate */ List<StepRequest> requests = stepRequests(); Iterator<StepRequest> iter = requests.iterator(); while (iter.hasNext()) { StepRequest request = iter.next(); if ((request != this) && request.isEnabled() && request.thread().equals(thread)) { throw new DuplicateRequestException( "Only one step request allowed per thread"); } } filters.add(JDWP.EventRequest.Set.Modifier.Step. create(this.thread, jdwpSize, jdwpDepth)); requestList().add(this); } public int depth() { return depth; } public int size() { return size; } public ThreadReference thread() { return thread; } int eventCmd() { return JDWP.EventKind.SINGLE_STEP; } public String toString() { return "step request " + thread() + state(); } } class ThreadDeathRequestImpl extends ThreadVisibleEventRequestImpl implements ThreadDeathRequest { ThreadDeathRequestImpl() { requestList().add(this); } int eventCmd() { return JDWP.EventKind.THREAD_DEATH; } public String toString() { return "thread death request " + state(); } } class ThreadStartRequestImpl extends ThreadVisibleEventRequestImpl implements ThreadStartRequest { ThreadStartRequestImpl() { requestList().add(this); } int eventCmd() { return JDWP.EventKind.THREAD_START; } public String toString() { return "thread start request " + state(); } } abstract class WatchpointRequestImpl extends ClassVisibleEventRequestImpl implements WatchpointRequest { final Field field; WatchpointRequestImpl(Field field) { this.field = field; filters.add(0, JDWP.EventRequest.Set.Modifier.FieldOnly.create( (ReferenceTypeImpl)field.declaringType(), ((FieldImpl)field).ref())); } public Field field() { return field; } } class AccessWatchpointRequestImpl extends WatchpointRequestImpl implements AccessWatchpointRequest { AccessWatchpointRequestImpl(Field field) { super(field); requestList().add(this); } int eventCmd() { return JDWP.EventKind.FIELD_ACCESS; } public String toString() { return "access watchpoint request " + field + state(); } } class ModificationWatchpointRequestImpl extends WatchpointRequestImpl implements ModificationWatchpointRequest { ModificationWatchpointRequestImpl(Field field) { super(field); requestList().add(this); } int eventCmd() { return JDWP.EventKind.FIELD_MODIFICATION; } public String toString() { return "modification watchpoint request " + field + state(); } } class VMDeathRequestImpl extends EventRequestImpl implements VMDeathRequest { VMDeathRequestImpl() { requestList().add(this); } int eventCmd() { return JDWP.EventKind.VM_DEATH; } public String toString() { return "VM death request " + state(); } }
Constructor.
/** * Constructor. */
EventRequestManagerImpl(VirtualMachine vm) { super(vm); java.lang.reflect.Field[] ekinds = JDWP.EventKind.class.getDeclaredFields(); int highest = 0; for (int i = 0; i < ekinds.length; ++i) { int val; try { val = ekinds[i].getInt(null); } catch (IllegalAccessException exc) { throw new RuntimeException("Got: " + exc); } if (val > highest) { highest = val; } } requestLists = new List[highest+1]; for (int i=0; i <= highest; i++) { requestLists[i] = Collections.synchronizedList(new ArrayList<>()); } } public ClassPrepareRequest createClassPrepareRequest() { return new ClassPrepareRequestImpl(); } public ClassUnloadRequest createClassUnloadRequest() { return new ClassUnloadRequestImpl(); } public ExceptionRequest createExceptionRequest(ReferenceType refType, boolean notifyCaught, boolean notifyUncaught) { validateMirrorOrNull(refType); return new ExceptionRequestImpl(refType, notifyCaught, notifyUncaught); } public StepRequest createStepRequest(ThreadReference thread, int size, int depth) { validateMirror(thread); return new StepRequestImpl(thread, size, depth); } public ThreadDeathRequest createThreadDeathRequest() { return new ThreadDeathRequestImpl(); } public ThreadStartRequest createThreadStartRequest() { return new ThreadStartRequestImpl(); } public MethodEntryRequest createMethodEntryRequest() { return new MethodEntryRequestImpl(); } public MethodExitRequest createMethodExitRequest() { return new MethodExitRequestImpl(); } public MonitorContendedEnterRequest createMonitorContendedEnterRequest() { if (!vm.canRequestMonitorEvents()) { throw new UnsupportedOperationException( "target VM does not support requesting Monitor events"); } return new MonitorContendedEnterRequestImpl(); } public MonitorContendedEnteredRequest createMonitorContendedEnteredRequest() { if (!vm.canRequestMonitorEvents()) { throw new UnsupportedOperationException( "target VM does not support requesting Monitor events"); } return new MonitorContendedEnteredRequestImpl(); } public MonitorWaitRequest createMonitorWaitRequest() { if (!vm.canRequestMonitorEvents()) { throw new UnsupportedOperationException( "target VM does not support requesting Monitor events"); } return new MonitorWaitRequestImpl(); } public MonitorWaitedRequest createMonitorWaitedRequest() { if (!vm.canRequestMonitorEvents()) { throw new UnsupportedOperationException( "target VM does not support requesting Monitor events"); } return new MonitorWaitedRequestImpl(); } public BreakpointRequest createBreakpointRequest(Location location) { validateMirror(location); if (location.codeIndex() == -1) { throw new NativeMethodException("Cannot set breakpoints on native methods"); } return new BreakpointRequestImpl(location); } public AccessWatchpointRequest createAccessWatchpointRequest(Field field) { validateMirror(field); if (!vm.canWatchFieldAccess()) { throw new UnsupportedOperationException( "target VM does not support access watchpoints"); } return new AccessWatchpointRequestImpl(field); } public ModificationWatchpointRequest createModificationWatchpointRequest(Field field) { validateMirror(field); if (!vm.canWatchFieldModification()) { throw new UnsupportedOperationException( "target VM does not support modification watchpoints"); } return new ModificationWatchpointRequestImpl(field); } public VMDeathRequest createVMDeathRequest() { if (!vm.canRequestVMDeathEvent()) { throw new UnsupportedOperationException( "target VM does not support requesting VM death events"); } return new VMDeathRequestImpl(); } public void deleteEventRequest(EventRequest eventRequest) { validateMirror(eventRequest); ((EventRequestImpl)eventRequest).delete(); } public void deleteEventRequests(List<? extends EventRequest> eventRequests) { validateMirrors(eventRequests); // copy the eventRequests to avoid ConcurrentModificationException Iterator<? extends EventRequest> iter = (new ArrayList<>(eventRequests)).iterator(); while (iter.hasNext()) { ((EventRequestImpl)iter.next()).delete(); } } public void deleteAllBreakpoints() { requestList(JDWP.EventKind.BREAKPOINT).clear(); try { JDWP.EventRequest.ClearAllBreakpoints.process(vm); } catch (JDWPException exc) { throw exc.toJDIException(); } } public List<StepRequest> stepRequests() { return (List<StepRequest>)unmodifiableRequestList(JDWP.EventKind.SINGLE_STEP); } public List<ClassPrepareRequest> classPrepareRequests() { return (List<ClassPrepareRequest>)unmodifiableRequestList(JDWP.EventKind.CLASS_PREPARE); } public List<ClassUnloadRequest> classUnloadRequests() { return (List<ClassUnloadRequest>)unmodifiableRequestList(JDWP.EventKind.CLASS_UNLOAD); } public List<ThreadStartRequest> threadStartRequests() { return (List<ThreadStartRequest>)unmodifiableRequestList(JDWP.EventKind.THREAD_START); } public List<ThreadDeathRequest> threadDeathRequests() { return (List<ThreadDeathRequest>)unmodifiableRequestList(JDWP.EventKind.THREAD_DEATH); } public List<ExceptionRequest> exceptionRequests() { return (List<ExceptionRequest>)unmodifiableRequestList(JDWP.EventKind.EXCEPTION); } public List<BreakpointRequest> breakpointRequests() { return (List<BreakpointRequest>)unmodifiableRequestList(JDWP.EventKind.BREAKPOINT); } public List<AccessWatchpointRequest> accessWatchpointRequests() { return (List<AccessWatchpointRequest>)unmodifiableRequestList(JDWP.EventKind.FIELD_ACCESS); } public List<ModificationWatchpointRequest> modificationWatchpointRequests() { return (List<ModificationWatchpointRequest>)unmodifiableRequestList(JDWP.EventKind.FIELD_MODIFICATION); } public List<MethodEntryRequest> methodEntryRequests() { return (List<MethodEntryRequest>)unmodifiableRequestList(JDWP.EventKind.METHOD_ENTRY); } public List<MethodExitRequest> methodExitRequests() { return (List<MethodExitRequest>)unmodifiableRequestList(EventRequestManagerImpl.methodExitEventCmd); } public List<MonitorContendedEnterRequest> monitorContendedEnterRequests() { return (List<MonitorContendedEnterRequest>)unmodifiableRequestList(JDWP.EventKind.MONITOR_CONTENDED_ENTER); } public List<MonitorContendedEnteredRequest> monitorContendedEnteredRequests() { return (List<MonitorContendedEnteredRequest>)unmodifiableRequestList(JDWP.EventKind.MONITOR_CONTENDED_ENTERED); } public List<MonitorWaitRequest> monitorWaitRequests() { return (List<MonitorWaitRequest>)unmodifiableRequestList(JDWP.EventKind.MONITOR_WAIT); } public List<MonitorWaitedRequest> monitorWaitedRequests() { return (List<MonitorWaitedRequest>)unmodifiableRequestList(JDWP.EventKind.MONITOR_WAITED); } public List<VMDeathRequest> vmDeathRequests() { return (List<VMDeathRequest>)unmodifiableRequestList(JDWP.EventKind.VM_DEATH); } List<? extends EventRequest> unmodifiableRequestList(int eventCmd) { // No need of explicit synchronization for requestList here. // It is taken care internally by SynchronizedList class. return Collections.unmodifiableList(new ArrayList<>(requestList(eventCmd))); } EventRequest request(int eventCmd, int requestId) { List<? extends EventRequest> rl = requestList(eventCmd); synchronized(rl) { // Refer Collections.synchronizedList javadoc. Iterator<? extends EventRequest> itr = rl.iterator(); while (itr.hasNext()){ EventRequestImpl er = (EventRequestImpl)itr.next(); if (er.id == requestId) return er; } } return null; } private List<? extends EventRequest> requestList(int eventCmd) { return requestLists[eventCmd]; } }