001: // You can redistribute this software and/or modify it under the terms of
002: // the Ozone Core License version 1 published by ozone-db.org.
003: //
004: // The original code and portions created by SMB are
005: // Copyright (C) 1997-@year@ by SMB GmbH. All rights reserved.
006: //
007: // $Id: Env.java,v 1.5 2002/06/25 11:34:38 mediumnet Exp $
008:
009: package org.ozoneDB.core;
010:
011: import java.io.*;
012: import java.util.*;
013: import java.lang.reflect.*;
014:
015: import org.ozoneDB.DxLib.*;
016: import org.ozoneDB.*;
017: import org.ozoneDB.core.DbRemote.*;
018: import org.ozoneDB.core.dr.*;
019: import org.ozoneDB.core.admin.*;
020: import org.ozoneDB.util.*;
021:
022: /**
023: * Env is the environment of a ozone database server. Currently there
024: * is only one environment allowed per JVM. A server environment can be
025: * initialized by the a Server or by a LocalDatabase.
026: *
027: *
028: * @author <a href="http://www.softwarebuero.de/">SMB</a>
029: * @author <a href="http://www.medium.net/">Medium.net</a>
030: * @version $Revision: 1.5 $Date: 2002/06/25 11:34:38 $
031: */
032: public final class Env {
033:
034: /**
035: Wether ozone should do selfChecks at different code locations.
036: If set to true, diagnostic messages are printed out
037: in case something unusual or bad has been detected.
038: If set to false, ozone will run at full speed.
039:
040: This is a kind of "poor man"s assertion facility as long as
041: ozone should be compileable by javac older than from JDK1.4
042: */
043: public final static boolean selfCheck = true;
044:
045: // constant members ***********************************
046: public final static String VERSION = "@version@";
047: public final static String OS_DIR = "ostab";
048: public final static String STATE_FILE = "state.properties";
049: public final static String CONFIG_FILE = "config.properties";
050: public final static String LOG_FILE = "log";
051: public final static String DATA_DIR = "data" + File.separator;
052: public final static String STATS_DIR = "stats";
053:
054: /**
055: * AdminPort and InvokeServer accepts and admin requests
056: */
057: public final static int ACCEPT_THREAD_PRIORITY = Thread.NORM_PRIORITY + 2;
058:
059: /**
060: * Thread priority of normal transaction.
061: */
062: public final static int TRANSACTION_THREAD_PRIORITY = Thread.NORM_PRIORITY;
063:
064: public final static int TRANSACTION_MUTEX_PRIORITY = TRANSACTION_THREAD_PRIORITY + 1;
065:
066: /**
067: * Thread priority deadlock recognition.
068: */
069: public final static int DEADLOCK_THREAD_PRIORITY = Thread.NORM_PRIORITY;
070:
071: /**
072: * Priority of the server thread (Server.main())
073: */
074: public final static int SERVER_THREAD_PRIORITY = Thread.NORM_PRIORITY + 2;
075:
076: // class members **************************************
077:
078: /**
079: * The one and only ozone environment of this VM.
080: */
081: public static Env theEnv;
082:
083: protected static OzoneSecurityManager securityManager;
084:
085: // instance members ***********************************
086:
087: public String dir;
088:
089: /**
090: * Holds the content of the 'state.properties' file. After
091: * changing the content the state must be written to disk to make
092: * changes persistent.
093: */
094: public Setup state;
095:
096: /**
097: * Holds the content of the 'config.properties' config file.
098: */
099: public Setup config;
100:
101: // public LogWriter logWriter = new LogWriterImpl();
102: public LogWriter logWriter = new LogWriterLog4JImpl();
103:
104: /**
105: * This indicates that we are about to shutdown.
106: */
107: public boolean shuttingdown = false;
108:
109: /*
110: * The total memory of this VM.
111: */
112: protected long totalMemory;
113:
114: /*
115: * The amount of memory that should be kept free.
116: */
117: protected long keepMemory;
118:
119: /**
120: * Interface for the database objects inside the server.
121: */
122: public Database database;
123:
124: private DxBag components;
125:
126: public KeyGenerator keyGenerator;
127:
128: public AdminManager adminManager;
129:
130: public ClassManager classManager;
131:
132: public TransactionManager transactionManager;
133:
134: public StoreManager storeManager;
135:
136: public UserManager userManager;
137:
138: protected LocalClientTracker localClientTracker;
139:
140: protected GarbageCollector garbageCollector;
141:
142: protected InvokeServer invokeServer;
143:
144: protected DeadlockThread deadlockThread;
145:
146: protected DeadlockRecognition dr;
147:
148: // class methods **************************************
149:
150: /**
151: * Returns the environment of the current thread. Useful for objects that
152: * do not store the enviroment itself like ObjectContainer.
153: *
154: *
155: * @return The environment of the current thread or null if called outside
156: * the server.
157: */
158: public static Env currentEnv() {
159: return theEnv;
160: }
161:
162: // instance methods ***********************************
163:
164: /**
165: * Construct a new ozone server environment.
166: *
167: *
168: * @param _dirName Directory of the database.
169: * @param _debugLevel the debug level that should be used,
170: * oveerriding the entry in config.properties. If null then the
171: * config.properties entry will be used.
172: */
173: public Env(String _dirName, String _debugLevel) throws Exception {
174:
175: if (theEnv != null) {
176: throw new Exception(
177: "ozone environment (Env) already initialized for this VM");
178: }
179:
180: try {
181: // give the engine its environment first but don't forget to
182: // reset it if we catch an exception
183: theEnv = this ;
184:
185: dir = new String(_dirName) + File.separator;
186: if (!new File(dir).isDirectory()) {
187: throw new Exception("No database found at '" + dir
188: + "'.");
189: }
190:
191: initSetup();
192: initLogs(_debugLevel);
193:
194: getLogWriter()
195: .newEntry(
196: this ,
197: "Copyright (C) 1997-@year@ The Ozone Database Project",
198: LogWriter.INFO);
199: getLogWriter()
200: .newEntry(
201: this ,
202: "contains libraries from the Apache Software Foundation",
203: LogWriter.INFO);
204: getLogWriter().newEntry(this ,
205: "contains libraries from SUN microsystems",
206: LogWriter.INFO);
207: getLogWriter().newEntry(this ,
208: "contains libraries from the W3C", LogWriter.INFO);
209: getLogWriter().newEntry(this ,
210: "contains libraries from Exoffice, Inc.",
211: LogWriter.INFO);
212: getLogWriter().newEntry(this ,
213: "contains libraries (JavaClass) from Markus Dahm ",
214: LogWriter.INFO);
215: getLogWriter().newEntry(this ,
216: "Copyright (C) under owner's respective terms.",
217: LogWriter.INFO);
218:
219: if (System.getSecurityManager() == null) {
220: securityManager = new OzoneSecurityManager();
221: System.setSecurityManager(securityManager);
222: }
223:
224: calcMemory();
225:
226: components = new DxArrayBag(16);
227:
228: localClientTracker = new LocalClientTracker();
229:
230: garbageCollector = new GarbageCollector(this );
231: components.add(garbageCollector);
232: garbageCollector.startup();
233:
234: keyGenerator = new KeyGenerator(this );
235: components.add(keyGenerator);
236: keyGenerator.startup();
237:
238: database = new Database(this );
239:
240: classManager = new ClassManager(this );
241: components.add(classManager);
242: classManager.startup();
243:
244: // UserManager has to be initialized before the recovery
245: userManager = new UserManager(this );
246: components.add(userManager);
247: userManager.startup();
248:
249: transactionManager = new TransactionManager(this );
250: components.add(transactionManager);
251: transactionManager.startup();
252:
253: // initialize the storeManager
254: String storeClassName = config.stringProperty(Setup.STORE,
255: "org.ozoneDB.core.wizardStore.WizardStore");
256: Class storeClass = Class.forName(storeClassName);
257: if (storeClass == null) {
258: getLogWriter().newEntry(this ,
259: "Store not found: " + storeClassName,
260: LogWriter.ERROR);
261: System.exit(1);
262: }
263: Constructor ctor = storeClass
264: .getConstructor(new Class[] { Env.class });
265: storeManager = (StoreManager) ctor
266: .newInstance(new Object[] { this });
267: storeManager.init(this );
268: components.add(storeManager);
269: storeManager.startup();
270:
271: // initialize adminManager
272: adminManager = new AdminManager(this );
273: components.add(adminManager);
274: adminManager.startup();
275: } catch (Exception e) {
276: theEnv = null;
277: if (logWriter != null) {
278: logWriter.newEntry(this ,
279: "Unable to initialize server.", e,
280: LogWriter.ERROR);
281: } else {
282: System.out.println("Unable to initialize server.");
283: e.printStackTrace();
284: }
285: throw e;
286: }
287: }
288:
289: public void shutdown() {
290: shuttingdown = true;
291: try {
292: logWriter.newEntry(this , "shutdown...", LogWriter.INFO);
293:
294: // if (storageFrame != null) {
295: // storageFrame.dispose();
296: // storageFrame = null;
297: // logWriter.newEntry (this, "ozonometer closed.", LogWriter.INFO);
298: // }
299: if (invokeServer != null) {
300: invokeServer.shutdown();
301: invokeServer = null;
302: }
303: if (deadlockThread != null) {
304: deadlockThread.stop();
305: deadlockThread = null;
306: logWriter
307: .newEntry(this ,
308: "Deadlock recognition stopped.",
309: LogWriter.INFO);
310: }
311:
312: transactionManager.shutdown();
313: transactionManager = null;
314:
315: storeManager.shutdown();
316: storeManager = null;
317:
318: userManager.shutdown();
319: userManager = null;
320:
321: classManager.shutdown();
322: classManager = null;
323:
324: keyGenerator.shutdown();
325: keyGenerator = null;
326:
327: garbageCollector.shutdown();
328: garbageCollector = null;
329:
330: storeSetup();
331:
332: components = null;
333:
334: logWriter.newEntry(this , "Halted.", LogWriter.INFO);
335: theEnv = null;
336: } catch (Exception e) {
337: fatalError(null, "Env.shutdown(): " + e.toString(), e);
338: }
339: }
340:
341: public void startExternalEventProcessing() throws Exception {
342: try {
343: invokeServer = new InvokeServer(this , portNum());
344: invokeServer.startup();
345: invokeServer.accept();
346: logWriter
347: .newEntry(this ,
348: "external event processing started",
349: LogWriter.INFO);
350: } catch (Exception e) {
351: logWriter.newEntry(this , "Client port (" + portNum()
352: + ") or admin port (" + adminPortNum()
353: + ") are already in use.", e, LogWriter.ERROR);
354: throw e;
355: }
356: }
357:
358: public void startDeadlockRecognition() {
359: logWriter.newEntry(this , "deadlock recognition started",
360: LogWriter.INFO);
361: deadlockThread = new DeadlockThread(3000, transactionManager);
362: deadlockThread.setPriority(DEADLOCK_THREAD_PRIORITY);
363: deadlockThread.setDaemon(true);
364: deadlockThread.start();
365: }
366:
367: /**
368: * Initialize the setup (state and config) of this server environment. When
369: * searching specific property look in setup file first, then in the
370: * properties file, then use defaults, then use System properties.
371: */
372: protected void initSetup() throws Exception {
373: Setup defaults = new Setup(this );
374: defaults.fillWithOzoneDefaults();
375:
376: FileInputStream configIn = new FileInputStream(new File(dir,
377: CONFIG_FILE));
378: FileInputStream stateIn = new FileInputStream(new File(dir,
379: STATE_FILE));
380: try {
381: config = new Setup(this , defaults);
382: config.load(configIn);
383: config.addProperties(System.getProperties(), "ozoneDB.");
384: config.print(System.out, "ozoneDB.", " ");
385: // config.addObserver( this );
386:
387: state = new Setup(this );
388: state.load(stateIn);
389: // state.addObserver( this );
390: } finally {
391: configIn.close();
392: stateIn.close();
393: }
394: }
395:
396: public boolean isComponentStateChanged() {
397: boolean hasChanged = false;
398:
399: DxIterator it = components.iterator();
400: while (it.next() != null) {
401: ServerComponent component = (ServerComponent) it.object();
402: if (component.hasChanged()) {
403: hasChanged = true;
404: }
405: }
406: return hasChanged;
407: }
408:
409: /**
410: * Save the setup (state and config) of the current server environment.
411: */
412: protected synchronized void storeSetup() {
413: logWriter.newEntry(this , "storeSetup()... ", LogWriter.DEBUG);
414:
415: try {
416: // having all components updating their properties
417: DxIterator it = components.iterator();
418: while (it.next() != null) {
419: ServerComponent component = (ServerComponent) it
420: .object();
421: if (component.hasChanged()) {
422: logWriter.newEntry(this , " changed component: "
423: + component, LogWriter.DEBUG);
424: component.save();
425: component.clearChanged();
426: }
427: }
428:
429: // actually save properties to disk
430: OutputStream stateOut = new BufferedOutputStream(
431: new FileOutputStream(new File(dir, STATE_FILE)));
432: OutputStream configOut = new BufferedOutputStream(
433: new FileOutputStream(new File(dir, CONFIG_FILE)));
434: try {
435: // save state
436: state.save(stateOut,
437: "Ozone Server State File.\n#Do not edit!");
438:
439: // save config
440: StringBuffer head = new StringBuffer(1024);
441: head.append("Ozone Server Config File.\n");
442: head.append("#\n");
443: head
444: .append("# Do not use comments. This file will be overwritten,\n");
445: head
446: .append("# if the config changes. See the ozone documentation\n");
447: head
448: .append("# for details about the properties and their values.\n");
449: config.save(configOut, head.toString());
450: } finally {
451: stateOut.close();
452: configOut.close();
453: }
454: } catch (Exception e) {
455: fatalError(this , "Unable to store server state.", e);
456: }
457: }
458:
459: /**
460: * Initialize server logging. There are two LogTargets: stdout and file.
461: * Which log messages are written to which target is specified in the
462: * config file. If the server runs in debug mode, debug
463: * messages are written to each target.
464: * If debugLevelName is set to null the setting from the config.properties file is used
465: */
466: protected void initLogs(String debugLevelName) throws Exception {
467:
468: if (debugLevelName == null) {
469: String defaultLevel = OzoneDebugLevel.INFO_STR;
470: debugLevelName = config.getProperty(Setup.LOG_LEVEL,
471: defaultLevel);
472: }
473:
474: int debugLevel = OzoneDebugLevel.toLevel(debugLevelName)
475: .toInt();
476:
477: String fileLog = dir + LOG_FILE;
478: logWriter.addLogTarget("stdout", System.out, debugLevel);
479: logWriter.addLogTarget(fileLog, new FileOutputStream(fileLog),
480: debugLevel);
481: }
482:
483: /**
484: * Fires an error message and exits the VM
485: */
486: public void fatalError(Object sender, String msg, Exception e) {
487: logWriter.newEntry(sender, msg, e, LogWriter.ERROR);
488: // FIXME: shutdown???
489: if (theEnv != null) {
490: theEnv.shutdown();
491: }
492: System.exit(1);
493: }
494:
495: public int dbID() {
496: return config.intProperty(Setup.DB_ID, -1);
497: }
498:
499: public int portNum() {
500: return config.intProperty(Setup.PORT, -1);
501: }
502:
503: public int adminPortNum() {
504: return config.intProperty(Setup.ADMIN_PORT, -1);
505: }
506:
507: /**
508: * Factory method to create or re-use a DR object.
509: */
510: public DeadlockRecognition deadlockRecognition() {
511: if (dr == null) {
512: dr = new EdgeChasing(this );
513: }
514: return dr;
515: }
516:
517: /**
518: * Initialize the internal memory counter so that freeMemory() returns
519: * correct results.
520: */
521: protected void calcMemory() {
522: Runtime rt = Runtime.getRuntime();
523:
524: long totalMemory = config.longProperty(
525: "ozoneDB.vm.totalMemory", -1);
526:
527: /*
528: Computing totalMemory this way is bad.
529: The reason is, that the reserved VM memory is increased to
530: maximum. This is not bad itself (despite the long startup time
531: induced due to swapout required to get to maximum possible memory).
532: It is bad, that after reaching maximum memory, the JavaVM thinks
533: that all that memory once reached is memory to be used. But because
534: this is only virtual memory, the JavaVM thus creates massive
535: swapping on using all the available memory, scattering all memory
536: accesses across the reserved memory area.
537:
538: If the reserved memory area is smaller (as this is usually the case after
539: startup of a Java application), less real RAM is required, less swapping
540: will occur, with the same application running.
541:
542: Because the behaviour is bad, we only revert to it if we do not get
543: any value set by the user for total memory.
544: */
545: if (totalMemory < 0) {
546: logWriter.newEntry(this , "checking memory... ",
547: LogWriter.INFO);
548: try {
549: DxBag bag = new DxArrayBag();
550: for (;;) {
551: bag.add(new byte[100000]);
552: }
553: } catch (OutOfMemoryError e) {
554: totalMemory = rt.totalMemory();
555: }
556: }
557:
558: long absoluteMinimumFreeMemoryRequest = config.longProperty(
559: "ozoneDB.vm.minFreeMemory", -1);
560:
561: if (absoluteMinimumFreeMemoryRequest < 0) {
562: keepMemory = Math.min(4000000L, totalMemory / 10);
563: } else {
564: keepMemory = absoluteMinimumFreeMemoryRequest;
565: }
566:
567: rt.gc();
568:
569: logWriter.newEntry(this , " total: " + totalMemory,
570: LogWriter.INFO);
571: logWriter.newEntry(this , " free : " + rt.freeMemory(),
572: LogWriter.INFO);
573: logWriter.newEntry(this , " keep : " + keepMemory,
574: LogWriter.INFO);
575: }
576:
577: /**
578: * Return the amount of *total* free memory in the system. The results
579: * returned by Runtime.freeMemory() may change overtime and so its
580: * useless for ozone.
581: */
582: public long freeMemory() {
583: Runtime rt = Runtime.getRuntime();
584: long hiddenMemory = totalMemory - rt.totalMemory();
585:
586: // keep xMB free at least
587: return Math.max(rt.freeMemory() + hiddenMemory - keepMemory, 0);
588: }
589:
590: public LogWriter getLogWriter() {
591: return logWriter;
592: }
593:
594: public TransactionManager getTransactionManager() {
595: return transactionManager;
596: }
597:
598: public Setup getState() {
599: return state;
600: }
601:
602: public InvokeServer getInvokeServer() {
603: return invokeServer;
604: }
605:
606: public StoreManager getStoreManager() {
607: return storeManager;
608: }
609:
610: public UserManager getUserManager() {
611: return userManager;
612: }
613:
614: public GarbageCollector getGarbageCollector() {
615: return garbageCollector;
616: }
617:
618: public LocalClientTracker getLocalClientTracker() {
619: return localClientTracker;
620: }
621: }
|