1
2
3
4
5
6
7
8
9
10 package org.archive.util;
11
12 import java.io.File;
13 import java.util.ArrayList;
14 import java.util.List;
15
16 import javax.management.Attribute;
17 import javax.management.AttributeNotFoundException;
18 import javax.management.InvalidAttributeValueException;
19 import javax.management.MBeanAttributeInfo;
20 import javax.management.MBeanException;
21 import javax.management.MBeanNotificationInfo;
22 import javax.management.MBeanOperationInfo;
23 import javax.management.MBeanParameterInfo;
24
25 import com.sleepycat.je.CheckpointConfig;
26 import com.sleepycat.je.Database;
27 import com.sleepycat.je.DatabaseConfig;
28 import com.sleepycat.je.DatabaseException;
29 import com.sleepycat.je.DatabaseStats;
30 import com.sleepycat.je.DbInternal;
31 import com.sleepycat.je.Environment;
32 import com.sleepycat.je.EnvironmentConfig;
33 import com.sleepycat.je.EnvironmentMutableConfig;
34 import com.sleepycat.je.StatsConfig;
35
36 /***
37 * JEMBeanHelper is a utility class for the MBean implementation which wants to
38 * add management of a JE environment to its capabilities. MBean
39 * implementations can contain a JEMBeanHelper instance to get MBean metadata
40 * for JE and to set attributes, get attributes, and invoke operations.
41 * <p>
42 * com.sleepycat.je.jmx.JEMonitor and
43 * the example program jmx.JEApplicationMBean are two MBean implementations
44 * which provide support different application use cases. See those classes for
45 * examples of how to use JEMBeanHelper.
46 * <p>This class was copied from the bdb je 2.0 jmx examples.
47 */
48
49 public class JEMBeanHelper {
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 public static final String ATT_ENV_HOME = "environmentHome";
65 public static final String ATT_OPEN = "isOpen";
66 public static final String ATT_IS_READ_ONLY = "isReadOnly";
67 public static final String ATT_IS_TRANSACTIONAL = "isTransactional";
68 public static final String ATT_CACHE_SIZE = "cacheSize";
69 public static final String ATT_CACHE_PERCENT = "cachePercent";
70 public static final String ATT_LOCK_TIMEOUT = "lockTimeout";
71 public static final String ATT_IS_SERIALIZABLE = "isSerializableIsolation";
72 public static final String ATT_TXN_TIMEOUT = "transactionTimeout";
73 public static final String ATT_SET_READ_ONLY = "openReadOnly";
74 public static final String ATT_SET_TRANSACTIONAL = "openTransactional";
75 public static final String ATT_SET_SERIALIZABLE =
76 "openSerializableIsolation";
77
78
79 private static final MBeanAttributeInfo [] COMMON_ATTR = {
80
81 new MBeanAttributeInfo(ATT_ENV_HOME,
82 "java.lang.String",
83 "Environment home directory.",
84 true,
85 false,
86 false),
87 new MBeanAttributeInfo(ATT_OPEN,
88 "java.lang.Boolean",
89 "True if this environment is open.",
90 true,
91 false,
92 true)
93 };
94
95
96 private static final MBeanAttributeInfo [] OPEN_ATTR = {
97
98 new MBeanAttributeInfo(ATT_IS_READ_ONLY,
99 "java.lang.Boolean",
100 "True if this environment is read only.",
101 true,
102 false,
103 true),
104 new MBeanAttributeInfo(ATT_IS_TRANSACTIONAL,
105 "java.lang.Boolean",
106 "True if this environment supports transactions.",
107 true,
108 false,
109 true),
110 new MBeanAttributeInfo(ATT_CACHE_SIZE,
111 "java.lang.Long",
112 "Cache size, in bytes.",
113 true,
114 true,
115 false),
116 new MBeanAttributeInfo(ATT_CACHE_PERCENT,
117 "java.lang.Integer",
118 "By default, cache size is (cachePercent * " +
119 "JVM maximum memory. To change the cache size "+
120 "using a percentage of the heap size, set " +
121 "the cache size to 0 and cachePercent to the "+
122 "desired percentage value.",
123 true,
124 true,
125 false),
126 new MBeanAttributeInfo(ATT_LOCK_TIMEOUT,
127 "java.lang.Long",
128 "Lock timeout, in microseconds.",
129 true,
130 false,
131 false),
132 };
133
134
135
136
137
138 private static final MBeanAttributeInfo [] TRANSACTIONAL_ATTR = {
139
140 new MBeanAttributeInfo(ATT_IS_SERIALIZABLE,
141 "java.lang.Boolean",
142 "True if this environment provides " +
143 "Serializable (degree 3) isolation. The " +
144 "default is RepeatableRead isolation.",
145 true,
146 false,
147 true),
148 new MBeanAttributeInfo(ATT_TXN_TIMEOUT,
149 "java.lang.Long",
150 "Transaction timeout, in seconds. A value " +
151 "of 0 means there is no timeout.",
152 true,
153 false,
154 false)
155 };
156
157
158
159
160
161
162 private static final MBeanAttributeInfo [] CREATE_ATTR = {
163
164 new MBeanAttributeInfo(ATT_SET_READ_ONLY,
165 "java.lang.Boolean",
166 "True if this environment should be opened " +
167 "in readonly mode.",
168 true,
169 true,
170 false),
171 new MBeanAttributeInfo(ATT_SET_TRANSACTIONAL,
172 "java.lang.Boolean",
173 "True if this environment should be opened " +
174 "in transactional mode.",
175 true,
176 true,
177 false),
178 new MBeanAttributeInfo(ATT_SET_SERIALIZABLE,
179 "java.lang.Boolean",
180 "True if this environment should be opened " +
181 "with serializableIsolation. The default is "+
182 "false.",
183 true,
184 true,
185 false),
186 };
187
188
189
190
191 static final String OP_CLEAN = "cleanLog";
192 static final String OP_EVICT = "evictMemory";
193 static final String OP_CHECKPOINT = "checkpoint";
194 static final String OP_SYNC = "sync";
195 static final String OP_ENV_STAT = "getEnvironmentStats";
196 static final String OP_ENV_STAT_STR = "getEnvironmentStatsToString";
197 static final String OP_LOCK_STAT = "getLockStats";
198 static final String OP_LOCK_STAT_STR = "getLockStatsToString";
199 static final String OP_TXN_STAT = "getTxnStats";
200 static final String OP_DB_NAMES = "getDatabaseNames";
201 static final String OP_DB_STAT = "getDatabaseStats";
202
203 private static final MBeanOperationInfo OP_CLEAN_INFO =
204 new MBeanOperationInfo(OP_CLEAN,
205 "Remove obsolete environment log files. " +
206 "Zero or more log files will be cleaned as " +
207 "necessary to bring the disk space " +
208 "utilization of the environment above the " +
209 "configured minimum utilization threshold " +
210 "as determined by the setting " +
211 "je.cleaner.minUtilization. Returns the " +
212 "number of files cleaned, that will be " +
213 "deleted at the next qualifying checkpoint.",
214 new MBeanParameterInfo[0],
215 "java.lang.Integer",
216 MBeanOperationInfo.UNKNOWN);
217
218 private static final MBeanOperationInfo OP_EVICT_INFO =
219 new MBeanOperationInfo(OP_EVICT,
220 "Reduce cache usage to the threshold " +
221 "determined by the setting " +
222 "je.evictor.useMemoryFloor. ",
223 new MBeanParameterInfo[0],
224 "void",
225 MBeanOperationInfo.UNKNOWN);
226
227
228 private static final MBeanParameterInfo [] checkpointParams = {
229 new MBeanParameterInfo ("force", "java.lang.Boolean",
230 "If true, force a checkpoint even if " +
231 "there has been no activity since the last " +
232 "checkpoint. Returns true if a checkpoint " +
233 "executed.")
234 };
235
236 private static final MBeanOperationInfo OP_CHECKPOINT_INFO =
237 new MBeanOperationInfo(OP_CHECKPOINT,
238 "Checkpoint the environment.",
239 checkpointParams,
240 "void",
241 MBeanOperationInfo.UNKNOWN);
242
243 private static final MBeanOperationInfo OP_SYNC_INFO =
244 new MBeanOperationInfo(OP_SYNC,
245 "Flush the environment to stable storage.",
246 new MBeanParameterInfo[0],
247 "void",
248 MBeanOperationInfo.UNKNOWN);
249
250 private static final MBeanParameterInfo [] statParams = {
251 new MBeanParameterInfo ("clear", "java.lang.Boolean",
252 "If true, reset statistics after reading."),
253 new MBeanParameterInfo ("fast", "java.lang.Boolean",
254 "If true, only return statistics which do " +
255 "not require expensive computation.")
256
257 };
258
259 private static final MBeanOperationInfo OP_ENV_STAT_INFO =
260 new MBeanOperationInfo(OP_ENV_STAT,
261 "Get environment statistics.",
262 statParams,
263 "com.sleepycat.je.EnvironmentStats",
264 MBeanOperationInfo.INFO);
265
266 private static final MBeanOperationInfo OP_ENV_STAT_STR_INFO =
267 new MBeanOperationInfo(OP_ENV_STAT_STR,
268 "Get environment statistics.",
269 statParams,
270 "java.lang.String",
271 MBeanOperationInfo.INFO);
272
273 private static final MBeanOperationInfo OP_LOCK_STAT_INFO =
274 new MBeanOperationInfo(OP_LOCK_STAT,
275 "Get locking statistics.",
276 statParams,
277 "com.sleepycat.je.LockStats",
278 MBeanOperationInfo.INFO);
279
280 private static final MBeanOperationInfo OP_LOCK_STAT_STR_INFO =
281 new MBeanOperationInfo(OP_LOCK_STAT_STR,
282 "Get locking statistics.",
283 statParams,
284 "java.lang.String",
285 MBeanOperationInfo.INFO);
286
287 private static final MBeanOperationInfo OP_TXN_STAT_INFO =
288 new MBeanOperationInfo(OP_TXN_STAT,
289 "Get transactional statistics.",
290 statParams,
291 "com.sleepycat.je.TransactionStats",
292 MBeanOperationInfo.INFO);
293
294 private static final MBeanOperationInfo OP_DB_NAMES_INFO =
295 new MBeanOperationInfo(OP_DB_NAMES,
296 "Get the names of databases in the environment.",
297 new MBeanParameterInfo[0],
298 "java.lang.String",
299 MBeanOperationInfo.INFO);
300
301 private static final MBeanOperationInfo OP_DB_STAT_INFO =
302 new MBeanOperationInfo(OP_DB_STAT,
303 "Get database statistics.",
304 statParams,
305 "com.sleepycat.je.TransactionStats",
306 MBeanOperationInfo.INFO);
307
308
309
310
311
312
313
314
315
316
317
318
319
320 private File environmentHome;
321
322
323
324
325
326
327 private boolean canConfigure;
328 private EnvironmentConfig openConfig;
329
330
331 private boolean needReset;
332
333
334
335
336
337 private boolean envWasOpen;
338
339 /***
340 * Instantiate a helper, specifying environment home and open capabilities.
341 *
342 * @param environmentHome home directory of the target JE environment.
343 * @param canConfigure If true, the helper will show environment
344 * configuration attributes.
345 */
346 public JEMBeanHelper(EnvironmentConfig config,
347 File environmentHome, boolean canConfigure) {
348
349 if (environmentHome == null) {
350 throw new IllegalArgumentException(
351 "Environment home cannot be null");
352 }
353 this.environmentHome = environmentHome;
354 this.canConfigure = canConfigure;
355 if (canConfigure) {
356 openConfig = config;
357 }
358 }
359
360 /***
361 * Return the target environment directory.
362 * @return the environment directory.
363 */
364 public File getEnvironmentHome() {
365 return environmentHome;
366 }
367
368 /***
369 * If the helper was instantiated with canConfigure==true, it shows
370 * environment configuration attributes. Those attributes are returned
371 * within this EnvironmentConfig object for use in opening environments.
372 *
373 * @return EnvironmentConfig object which saves configuration attributes
374 * recorded through MBean attributes.
375 */
376 public EnvironmentConfig getEnvironmentOpenConfig() {
377 return openConfig;
378 }
379
380 /***
381 * Return an Environment only if the environment has already been opened
382 * in this process. A helper method for MBeans which want to only access
383 * open environments.
384 * @return Environment if already open, null if not open.
385 */
386 public Environment getEnvironmentIfOpen() {
387 if (environmentHome == null) {
388 return null;
389 }
390
391 return DbInternal.getEnvironmentShell(environmentHome);
392 }
393
394 /***
395 * Tell the MBean if the available set of functionality has changed.
396 *
397 * @return true if the MBean should regenerate its JE metadata.
398 */
399 public synchronized boolean getNeedReset() {
400 return needReset;
401 }
402
403 /*********************************************************************/
404
405 /*********************************************************************/
406
407 /***
408 * Get MBean attribute metadata for this environment.
409 * @param targetEnv The target JE environment. May be null if the
410 * environment is not open.
411 * @return list of MBeanAttributeInfo objects describing the available
412 * attributes.
413 */
414 public List<MBeanAttributeInfo> getAttributeList(Environment targetEnv) {
415
416
417 setNeedReset(false);
418
419 ArrayList<MBeanAttributeInfo> attrList
420 = new ArrayList<MBeanAttributeInfo>();
421
422
423 for (int i = 0; i < COMMON_ATTR.length; i++) {
424 attrList.add(COMMON_ATTR[i]);
425 }
426
427 if (targetEnv == null) {
428 if (canConfigure) {
429
430 for (int i = 0; i < CREATE_ATTR.length; i++) {
431 attrList.add(CREATE_ATTR[i]);
432 }
433 }
434 } else {
435
436 for (int i = 0; i < OPEN_ATTR.length; i++) {
437 attrList.add(OPEN_ATTR[i]);
438 }
439
440
441 try {
442 EnvironmentConfig config = targetEnv.getConfig();
443 if (config.getTransactional()) {
444 for (int i = 0; i < TRANSACTIONAL_ATTR.length; i++) {
445 attrList.add(TRANSACTIONAL_ATTR[i]);
446 }
447 }
448 } catch (DatabaseException ignore) {
449
450 }
451 }
452
453 return attrList;
454 }
455
456 /***
457 * Get an attribute value for the given environment. Check
458 * JEMBeanHelper.getNeedReset() after this call because the helper may
459 * detect that the environment has changed and that the MBean metadata
460 * should be reset.
461 *
462 * @param targetEnv The target JE environment. May be null if the
463 * environment is not open.
464 * @param attributeName attribute name.
465 * @return attribute value.
466 */
467 public Object getAttribute(Environment targetEnv,
468 String attributeName)
469 throws AttributeNotFoundException,
470 MBeanException {
471
472
473 if (attributeName == null) {
474 throw new AttributeNotFoundException(
475 "Attribute name cannot be null");
476 }
477
478
479 try {
480 if (attributeName.equals(ATT_ENV_HOME)) {
481 return environmentHome.getCanonicalPath();
482 } else if (attributeName.equals(ATT_OPEN)) {
483 boolean envIsOpen = (targetEnv != null);
484 resetIfOpenStateChanged(envIsOpen);
485 return new Boolean(envIsOpen);
486 } else if (attributeName.equals(ATT_SET_READ_ONLY)) {
487 return new Boolean(openConfig.getReadOnly());
488 } else if (attributeName.equals(ATT_SET_TRANSACTIONAL)) {
489 return new Boolean(openConfig.getTransactional());
490 } else if (attributeName.equals(ATT_SET_SERIALIZABLE)) {
491 return new Boolean(openConfig.getTxnSerializableIsolation());
492 } else {
493
494 if (targetEnv != null) {
495
496 EnvironmentConfig config = targetEnv.getConfig();
497
498 if (attributeName.equals(ATT_IS_READ_ONLY)) {
499 return new Boolean(config.getReadOnly());
500 } else if (attributeName.equals(ATT_IS_TRANSACTIONAL)) {
501 return new Boolean(config.getTransactional());
502 } else if (attributeName.equals(ATT_CACHE_SIZE)) {
503 return new Long(config.getCacheSize());
504 } else if (attributeName.equals(ATT_CACHE_PERCENT)) {
505 return new Integer(config.getCachePercent());
506 } else if (attributeName.equals(ATT_LOCK_TIMEOUT)) {
507 return new Long(config.getLockTimeout());
508 } else if (attributeName.equals(ATT_IS_SERIALIZABLE)) {
509 return new
510 Boolean(config.getTxnSerializableIsolation());
511 } else if (attributeName.equals(ATT_TXN_TIMEOUT)) {
512 return new Long(config.getTxnTimeout());
513 } else {
514 throw new AttributeNotFoundException("attribute " +
515 attributeName +
516 " is not valid.");
517 }
518 }
519 return null;
520 }
521 } catch (Exception e) {
522
523
524
525
526
527 throw new MBeanException(e, e.getMessage());
528 }
529 }
530
531 /***
532 * Set an attribute value for the given environment.
533 *
534 * @param targetEnv The target JE environment. May be null if the
535 * environment is not open.
536 * @param attribute name/value pair
537 */
538 public void setAttribute(Environment targetEnv,
539 Attribute attribute)
540 throws AttributeNotFoundException,
541 InvalidAttributeValueException {
542
543 if (attribute == null) {
544 throw new AttributeNotFoundException("Attribute cannot be null");
545 }
546
547
548 String name = attribute.getName();
549 Object value = attribute.getValue();
550
551 if (name == null) {
552 throw new AttributeNotFoundException(
553 "Attribute name cannot be null");
554 }
555
556 if (value == null) {
557 throw new InvalidAttributeValueException(
558 "Attribute value for attribute " +
559 name + " cannot be null");
560 }
561
562 try {
563 if (name.equals(ATT_SET_READ_ONLY)) {
564 openConfig.setReadOnly(((Boolean) value).booleanValue());
565 } else if (name.equals(ATT_SET_TRANSACTIONAL)) {
566 openConfig.setTransactional(((Boolean) value).booleanValue());
567 } else if (name.equals(ATT_SET_SERIALIZABLE)) {
568 openConfig.setTxnSerializableIsolation(
569 ((Boolean) value).booleanValue());
570 } else {
571
572 if (targetEnv != null) {
573
574 EnvironmentMutableConfig config =
575 targetEnv.getMutableConfig();
576
577 if (name.equals(ATT_CACHE_SIZE)) {
578 config.setCacheSize(((Long) value).longValue());
579 targetEnv.setMutableConfig(config);
580 } else if (name.equals(ATT_CACHE_PERCENT)) {
581 config.setCachePercent(((Integer) value).intValue());
582 targetEnv.setMutableConfig(config);
583 } else {
584 throw new AttributeNotFoundException("attribute " +
585 name +
586 " is not valid.");
587 }
588 } else {
589 throw new AttributeNotFoundException("attribute " +
590 name +
591 " is not valid.");
592 }
593 }
594 } catch (NumberFormatException e) {
595 throw new InvalidAttributeValueException("attribute name=" + name);
596 } catch (DatabaseException e) {
597 throw new InvalidAttributeValueException("attribute name=" + name +
598 e.getMessage());
599 }
600 }
601
602 /*********************************************************************/
603
604 /*********************************************************************/
605
606 /***
607 * Get mbean operation metadata for this environment.
608 *
609 * @param targetEnv The target JE environment. May be null if the
610 * environment is not open.
611 * @return List of MBeanOperationInfo describing available operations.
612 */
613 public List<MBeanOperationInfo> getOperationList(Environment targetEnv) {
614 setNeedReset(false);
615
616 List<MBeanOperationInfo> operationList = new ArrayList<MBeanOperationInfo>();
617
618 if (targetEnv != null) {
619
620
621
622
623 operationList.add(OP_CLEAN_INFO);
624 operationList.add(OP_EVICT_INFO);
625 operationList.add(OP_ENV_STAT_INFO);
626 operationList.add(OP_ENV_STAT_STR_INFO);
627 operationList.add(OP_LOCK_STAT_INFO);
628 operationList.add(OP_LOCK_STAT_STR_INFO);
629 operationList.add(OP_DB_NAMES_INFO);
630 operationList.add(OP_DB_STAT_INFO);
631
632
633 boolean isTransactional = false;
634 try {
635 EnvironmentConfig config = targetEnv.getConfig();
636 isTransactional = config.getTransactional();
637 } catch (DatabaseException e) {
638
639 return new ArrayList<MBeanOperationInfo>();
640 }
641
642 if (isTransactional) {
643 operationList.add(OP_CHECKPOINT_INFO);
644 operationList.add(OP_TXN_STAT_INFO);
645 } else {
646 operationList.add(OP_SYNC_INFO);
647 }
648 }
649
650 return operationList;
651 }
652
653 /***
654 * Invoke an operation for the given environment.
655 *
656 * @param targetEnv The target JE environment. May be null if the
657 * environment is not open.
658 * @param actionName operation name.
659 * @param params operation parameters. May be null.
660 * @param signature operation signature. May be null.
661 * @return the operation result
662 */
663 public Object invoke(Environment targetEnv,
664 String actionName,
665 Object [] params,
666 String [] signature)
667 throws MBeanException {
668
669
670 if (actionName == null) {
671 throw new IllegalArgumentException("actionName cannot be null");
672 }
673
674 try {
675 if (targetEnv != null) {
676 if (actionName.equals(OP_CLEAN)) {
677 int numFiles = targetEnv.cleanLog();
678 return new Integer(numFiles);
679 } else if (actionName.equals(OP_EVICT)) {
680 targetEnv.evictMemory();
681 return null;
682 } else if (actionName.equals(OP_CHECKPOINT)) {
683 CheckpointConfig config = new CheckpointConfig();
684 if ((params != null) && (params.length > 0)) {
685 Boolean force = (Boolean) params[0];
686 config.setForce(force.booleanValue());
687 }
688 targetEnv.checkpoint(config);
689 return null;
690 } else if (actionName.equals(OP_SYNC)) {
691 targetEnv.sync();
692 return null;
693 } else if (actionName.equals(OP_ENV_STAT)) {
694 return targetEnv.getStats(getStatsConfig(params));
695 } else if (actionName.equals(OP_ENV_STAT_STR)) {
696 return targetEnv.getStats(getStatsConfig(params)).toString();
697 } else if (actionName.equals(OP_LOCK_STAT)) {
698 return targetEnv.getLockStats(getStatsConfig(params));
699 } else if (actionName.equals(OP_LOCK_STAT_STR)) {
700 return targetEnv.getLockStats(getStatsConfig(params)).toString();
701 } else if (actionName.equals(OP_TXN_STAT)) {
702 return targetEnv.getTransactionStats(
703 getStatsConfig(params));
704 } else if (actionName.equals(OP_DB_NAMES)) {
705 return targetEnv.getDatabaseNames();
706 } else if (actionName.equals(OP_DB_STAT)) {
707 return getDatabaseStats(targetEnv, params);
708 }
709 }
710
711 return new IllegalArgumentException("actionName: " +
712 actionName +
713 " is not valid");
714 } catch (DatabaseException e) {
715
716
717
718
719
720 throw new MBeanException(e, e.getMessage());
721 }
722 }
723
724 /***
725 * Helper for creating a StatsConfig object to use as an operation
726 * parameter.
727 */
728 private StatsConfig getStatsConfig(Object [] params) {
729 StatsConfig statsConfig = new StatsConfig();
730 if ((params != null) && (params.length > 0) && (params[0] != null)) {
731 Boolean clear = (Boolean) params[0];
732 statsConfig.setClear(clear.booleanValue());
733 }
734 if ((params != null) && (params.length > 1) && (params[1] != null)) {
735 Boolean fast = (Boolean) params[1];
736 statsConfig.setFast(fast.booleanValue());
737 }
738 return statsConfig;
739 }
740
741 /***
742 * Helper to get statistics for a given database.
743 * @param params operation parameters
744 * @return DatabaseStats object
745 */
746 private DatabaseStats getDatabaseStats(Environment targetEnv,
747 Object [] params)
748 throws IllegalArgumentException,
749 DatabaseException {
750
751 if ((params == null) || (params.length < 3)) {
752 return null;
753 }
754 String dbName = (String)params[2];
755
756 Database db = null;
757 try {
758 DatabaseConfig dbConfig = new DatabaseConfig();
759 dbConfig.setReadOnly(true);
760 DbInternal.setUseExistingConfig(dbConfig, true);
761 db = targetEnv.openDatabase(null, dbName, dbConfig);
762 return db.getStats(getStatsConfig(params));
763 } finally {
764 if (db != null) {
765 db.close();
766 }
767 }
768 }
769
770 /*********************************************************************/
771
772
773
774 /***
775 * No notifications are supported.
776 * @return List of MBeanNotificationInfo for available notifications.
777 */
778 public MBeanNotificationInfo []
779 getNotificationInfo(Environment targetEnv) {
780 return null;
781 }
782
783 /*********************************************************************/
784
785
786
787 private synchronized void setNeedReset(boolean reset) {
788 needReset = reset;
789 }
790
791 private synchronized void resetIfOpenStateChanged(boolean isOpen) {
792 if (isOpen != envWasOpen) {
793 setNeedReset(true);
794 envWasOpen = isOpen;
795 }
796 }
797 }
798