KernelContext.java
001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002  *
003  * Copyright 2000-2011,  Michael Meyling <mime@qedeq.org>.
004  *
005  * "Hilbert II" is free software; you can redistribute
006  * it and/or modify it under the terms of the GNU General Public
007  * License as published by the Free Software Foundation; either
008  * version 2 of the License, or (at your option) any later version.
009  *
010  * This program is distributed in the hope that it will be useful,
011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013  * GNU General Public License for more details.
014  */
015 
016 package org.qedeq.kernel.bo;
017 
018 import java.io.File;
019 import java.io.FileOutputStream;
020 import java.io.IOException;
021 import java.net.URL;
022 import java.net.URLConnection;
023 import java.nio.channels.FileLock;
024 import java.util.Map;
025 
026 import org.qedeq.base.io.IoUtility;
027 import org.qedeq.base.trace.Trace;
028 import org.qedeq.base.utility.StringUtility;
029 import org.qedeq.base.utility.YodaUtility;
030 import org.qedeq.kernel.bo.common.KernelProperties;
031 import org.qedeq.kernel.bo.common.KernelServices;
032 import org.qedeq.kernel.bo.common.KernelState;
033 import org.qedeq.kernel.bo.common.QedeqBo;
034 import org.qedeq.kernel.bo.common.ServiceModule;
035 import org.qedeq.kernel.bo.common.ServiceProcess;
036 import org.qedeq.kernel.bo.log.QedeqLog;
037 import org.qedeq.kernel.se.common.ModuleAddress;
038 import org.qedeq.kernel.se.common.Plugin;
039 import org.qedeq.kernel.se.config.QedeqConfig;
040 
041 
042 /**
043  * This class provides static access methods for basic informations.
044  *
045  @author  Michael Meyling
046  */
047 public final class KernelContext implements KernelProperties, KernelServices {
048 
049     /** Message for non started kernel. */
050     private static final String KERNEL_NOT_STARTED = "Kernel not started";
051 
052     /** Message for non initialized kernel. */
053     private static final String KERNEL_NOT_INITIALIZED = "Kernel not initialized";
054 
055     /** This class. */
056     private static final Class CLASS = KernelContext.class;
057 
058     /** Version of this kernel. */
059     private static final String KERNEL_VERSION = "0.04.02";
060 
061     /** Version dependent directory of this kernel. */
062     private static final String KERNEL_VERSION_DIRECTORY = KERNEL_VERSION.replace('.''_');
063 
064     /** Version code . */
065     private static final String KERNEL_CODE_NAME = "gaffsie";
066 
067     /** Kernel version dedication. */
068     private static final String KERNEL_DEDICATED
069         "dedicated to the one I have chosen among all these stars twenty years ago";
070 
071     /** Descriptive version information of this kernel. */
072     private static final String DESCRIPTIVE_KERNEL_VERSION
073         "Hilbert II - Version " + KERNEL_VERSION + " (" + KERNEL_CODE_NAME + ") ["
074         + getBuildIdFromManifest() "] " + KERNEL_DEDICATED;
075 
076     /** Maximal supported rule version of this kernel. */
077     private static final String MAXIMAL_RULE_VERSION = "1.00.00";
078 
079     /** One and only instance of this class. */
080     private static final KernelContext INSTANCE = new KernelContext();
081 
082     /** Lock file. */
083     private File lockFile;
084 
085     /** Lock file stream. */
086     private FileOutputStream lockStream;
087 
088     /** Initial kernel state. */
089     private final KernelState initialState = new KernelState() {
090 
091         public void init(final ServiceModule moduleServices, final QedeqConfig qedeqConfig)
092                 throws IOException {
093             config = qedeqConfig;
094             Trace.setTraceOn(config.isTraceOn());
095             checkJavaVersion();
096             createAllNecessaryDirectories();
097             checkIfApplicationIsAlreadyRunningAndLockFile();
098             KernelContext.this.services = moduleServices;
099             QedeqLog.getInstance().logMessage("--------------------------------------------------"
100                  "---------------------------------------");
101             QedeqLog.getInstance().logMessage("This is "
102                 + KernelContext.getInstance().getDescriptiveKernelVersion());
103             QedeqLog.getInstance().logMessage("  see \"http://www.qedeq.org\" for more "
104                 "information");
105             QedeqLog.getInstance().logMessage("  supports rules till version "
106                 + KernelContext.getInstance().getMaximalRuleVersion());
107             QedeqLog.getInstance().logMessage("  Java version: "
108                 + StringUtility.alignRight(System.getProperty("java.version""unknown")10));
109             QedeqLog.getInstance().logMessage("  used memory:  "
110                 + StringUtility.alignRight(Runtime.getRuntime().totalMemory()
111                 - Runtime.getRuntime().freeMemory()10));
112             QedeqLog.getInstance().logMessage("  free memory:  "
113                 + StringUtility.alignRight(Runtime.getRuntime().freeMemory()10));
114             QedeqLog.getInstance().logMessage("  total memory: "
115                 + StringUtility.alignRight(Runtime.getRuntime().totalMemory()10));
116             QedeqLog.getInstance().logMessage("  max. memory:  "
117                 + StringUtility.alignRight(Runtime.getRuntime().maxMemory()10));
118             QedeqLog.getInstance().logMessage("  processors/cores: "
119                 + StringUtility.alignRight(Runtime.getRuntime().availableProcessors()6));
120             currentState = initializedState;
121         }
122 
123         public void startup() {
124             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
125         }
126 
127         public void shutdown() {
128             currentState = initialState;
129             // close stream and associated channel
130             IoUtility.close(lockStream);
131             lockStream = null;
132         }
133 
134         public void removeAllModules() {
135             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
136         }
137 
138         public void removeModule(final ModuleAddress address) {
139             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
140         }
141 
142         public void clearLocalBuffer() throws IOException {
143             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
144         }
145 
146         public QedeqBo loadModule(final ModuleAddress address) {
147             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
148         }
149 
150         public boolean loadAllModulesFromQedeq() {
151             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
152         }
153 
154         public boolean loadRequiredModules(final ModuleAddress address) {
155             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
156         }
157 
158         public ModuleAddress[] getAllLoadedModules() {
159             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
160         }
161 
162         public QedeqBo getQedeqBo(final ModuleAddress address) {
163             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
164         }
165 
166         public ModuleAddress getModuleAddress(final URL url) {
167             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
168         }
169 
170         public ModuleAddress getModuleAddress(final String url) {
171             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
172         }
173 
174         public ModuleAddress getModuleAddress(final File file) {
175             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
176         }
177 
178         public String getSource(final ModuleAddress address) {
179             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
180         }
181 
182         public boolean checkModule(final ModuleAddress address) {
183             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
184         }
185 
186         public Plugin[] getPlugins() {
187             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
188         }
189 
190         public Object executePlugin(final String pluginName, final ModuleAddress address,
191                 final Map parameters) {
192             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
193         }
194 
195         public void clearAllPluginResults(final ModuleAddress address) {
196             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
197         }
198 
199         public ServiceProcess[] getServiceProcesses() {
200             throw new IllegalStateException(KERNEL_NOT_INITIALIZED);
201         }
202 
203     };
204 
205     /** Initial kernel state. */
206     private final KernelState initializedState = new KernelState() {
207 
208         public void init(final ServiceModule moduleServices, final QedeqConfig qedeqConfig)
209                 throws IOException {
210             throw new IllegalStateException("Kernel is already initialized");
211         }
212 
213         public void startup() {
214             services.startupServices();
215             currentState = readyState;
216             QedeqLog.getInstance().logMessage("QEDEQ kernel opened.");
217         }
218 
219         public void shutdown() {
220             services.shutdownServices();
221             KernelContext.this.services = null;
222             initialState.shutdown();
223             QedeqLog.getInstance().logMessage("QEDEQ Kernel closed.");
224         }
225 
226         public void removeAllModules() {
227             throw new IllegalStateException(KERNEL_NOT_STARTED);
228         }
229 
230         public void removeModule(final ModuleAddress address) {
231             throw new IllegalStateException(KERNEL_NOT_STARTED);
232         }
233 
234         public void clearLocalBuffer() throws IOException {
235             throw new IllegalStateException(KERNEL_NOT_STARTED);
236         }
237 
238         public QedeqBo loadModule(final ModuleAddress address) {
239             throw new IllegalStateException(KERNEL_NOT_STARTED);
240         }
241 
242         public boolean loadAllModulesFromQedeq() {
243             throw new IllegalStateException(KERNEL_NOT_STARTED);
244         }
245 
246         public boolean loadRequiredModules(final ModuleAddress address) {
247             throw new IllegalStateException(KERNEL_NOT_STARTED);
248         }
249 
250         public ModuleAddress[] getAllLoadedModules() {
251             throw new IllegalStateException(KERNEL_NOT_STARTED);
252         }
253 
254         public QedeqBo getQedeqBo(final ModuleAddress address) {
255             throw new IllegalStateException(KERNEL_NOT_STARTED);
256         }
257 
258         public ModuleAddress getModuleAddress(final URL url) {
259             throw new IllegalStateException(KERNEL_NOT_STARTED);
260         }
261 
262         public ModuleAddress getModuleAddress(final String url) {
263             throw new IllegalStateException(KERNEL_NOT_STARTED);
264         }
265 
266         public ModuleAddress getModuleAddress(final File file) {
267             throw new IllegalStateException(KERNEL_NOT_STARTED);
268         }
269 
270         public String getSource(final ModuleAddress address) {
271             throw new IllegalStateException(KERNEL_NOT_STARTED);
272         }
273 
274         public boolean checkModule(final ModuleAddress address) {
275             throw new IllegalStateException(KERNEL_NOT_STARTED);
276         }
277 
278         public Plugin[] getPlugins() {
279             return services.getPlugins();
280         }
281 
282         public Object executePlugin(final String pluginName, final ModuleAddress address,
283                 final Map parameters) {
284             throw new IllegalStateException(KERNEL_NOT_STARTED);
285         }
286 
287         public void clearAllPluginResults(final ModuleAddress address) {
288             throw new IllegalStateException(KERNEL_NOT_STARTED);
289         }
290 
291         public ServiceProcess[] getServiceProcesses() {
292             throw new IllegalStateException(KERNEL_NOT_STARTED);
293         }
294 
295     };
296 
297     /** State for ready kernel. */
298     private final KernelState readyState = new KernelState() {
299 
300         public void init(final ServiceModule moduleServices, final QedeqConfig qedeqConfig)
301                 throws IOException {
302             // we are already ready
303         }
304 
305         public void startup() {
306             // we are already ready
307         }
308 
309         public void shutdown() {
310             try {
311                 final ModuleAddress[] addresses = services.getAllLoadedModules();
312                 final String[] buffer = new String[addresses.length];
313                 for (int i = 0; i < addresses.length; i++) {
314                     buffer[i= addresses[i].toString();
315                 }
316                 config.setLoadedModules(buffer);
317                 config.store();
318                 QedeqLog.getInstance().logMessage("Current config file successfully saved.");
319             catch (IOException e) {
320                 Trace.trace(CLASS, this, "shutdown()", e);
321                 QedeqLog.getInstance().logMessage("Saving current config file failed.");
322             }
323             initializedState.shutdown();
324         }
325 
326         public void removeAllModules() {
327             services.removeAllModules();
328         }
329 
330         public void removeModule(final ModuleAddress address) {
331             services.removeModule(address);
332         }
333 
334         public void clearLocalBuffer() throws IOException {
335             services.clearLocalBuffer();
336         }
337 
338         public QedeqBo loadModule(final ModuleAddress address) {
339             return services.loadModule(address);
340         }
341 
342         public boolean loadAllModulesFromQedeq() {
343             return services.loadAllModulesFromQedeq();
344         }
345 
346         public boolean loadRequiredModules(final ModuleAddress address) {
347             return services.loadRequiredModules(address);
348         }
349 
350         public ModuleAddress[] getAllLoadedModules() {
351             return services.getAllLoadedModules();
352         }
353 
354         public QedeqBo getQedeqBo(final ModuleAddress address) {
355             return services.getQedeqBo(address);
356         }
357 
358         public ModuleAddress getModuleAddress(final URL urlthrows IOException {
359             return services.getModuleAddress(url);
360         }
361 
362         public ModuleAddress getModuleAddress(final String urlthrows IOException {
363             return services.getModuleAddress(url);
364         }
365 
366         public ModuleAddress getModuleAddress(final File filethrows IOException {
367             return services.getModuleAddress(file);
368         }
369 
370         public String getSource(final ModuleAddress addressthrows IOException {
371             return services.getSource(address);
372         }
373 
374         public boolean checkModule(final ModuleAddress address) {
375             return services.checkModule(address);
376         }
377 
378         public Plugin[] getPlugins() {
379             return services.getPlugins();
380         }
381 
382         public Object executePlugin(final String pluginName, final ModuleAddress address,
383                 final Map parameters) {
384             return services.executePlugin(pluginName, address, parameters);
385         }
386 
387         public void clearAllPluginResults(final ModuleAddress address) {
388             services.clearAllPluginResults(address);
389         }
390 
391         public ServiceProcess[] getServiceProcesses() {
392             return services.getServiceProcesses();
393         }
394 
395     };
396 
397     /** Initial kernel state. */
398     private KernelState currentState = initialState;
399 
400     /** For config access. */
401     private QedeqConfig config;
402 
403     /** This object can service QEDEQ modules. */
404     private ServiceModule services;
405 
406     /**
407      * Constructor.
408      */
409     private KernelContext() {
410         // nothing to do
411     }
412 
413     /**
414      * Get instance of kernel context.
415      *
416      @return  Singleton, which is responsible for the kernel access.
417      */
418     public static final KernelContext getInstance() {
419         return INSTANCE;
420     }
421 
422     /**
423      * Get build information from JAR manifest file. Is also non empty string if no manifest
424      * information is available.
425      *
426      @return  Implementation-version.
427      */
428     private static String getBuildIdFromManifest() {
429         String build = KernelContext.class.getPackage().getImplementationVersion();
430         if (build == null) {
431             build = "no regular build";
432         }
433         return build;
434     }
435 
436     public String getBuildId() {
437         return getBuildIdFromManifest();
438     }
439 
440     public final String getKernelVersion() {
441         return KERNEL_VERSION;
442     }
443 
444     public final String getKernelCodeName() {
445         return KERNEL_CODE_NAME;
446     }
447 
448     public final String getKernelVersionDirectory() {
449         return KERNEL_VERSION_DIRECTORY;
450     }
451 
452     public final String getDescriptiveKernelVersion() {
453         return DESCRIPTIVE_KERNEL_VERSION;
454     }
455 
456     public final String getDedication() {
457         return KERNEL_DEDICATED;
458     }
459 
460     public final String getMaximalRuleVersion() {
461         return MAXIMAL_RULE_VERSION;
462     }
463 
464     public final boolean isRuleVersionSupported(final String ruleVersion) {
465         return MAXIMAL_RULE_VERSION.equals(ruleVersion);
466     }
467 
468     /**
469      * This class ist just for solving the lazy loading problem thread save.
470      * see <a href="http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom">
471      * Initialization_on_demand_holder_idiom</a>.
472      */
473     private static final class LazyHolderTimeoutMethods {
474 
475         /** Lazy initialized constant that knows about the existence of the method
476          <code>URLConnection.setConnectTimeout</code>. This depends on the currently running
477          * JVM. */
478         private static final boolean IS_SET_CONNECTION_TIMEOUT_SUPPORTED = YodaUtility.existsMethod(
479             URLConnection.class, "setConnectTimeout",
480             new Class[] {Integer.TYPE});
481 
482         /** Lazy initialized constant that knows about the existence of the method
483          <code>URLConnection.setReadTimeout</code>. This depends on the currently running
484          * JVM. */
485         private static final boolean IS_SET_READ_TIMEOUT_SUSPPORTED = YodaUtility.existsMethod(
486                 URLConnection.class, "setReadTimeout",
487                 new Class[] {Integer.TYPE});
488 
489         /**
490          * Hidden constructor.
491          */
492         private LazyHolderTimeoutMethods() {
493             // nothing to do
494         }
495 
496     }
497 
498     public boolean isSetConnectionTimeOutSupported() {
499         return LazyHolderTimeoutMethods.IS_SET_CONNECTION_TIMEOUT_SUPPORTED;
500     }
501 
502     public boolean isSetReadTimeoutSupported() {
503         return LazyHolderTimeoutMethods.IS_SET_READ_TIMEOUT_SUSPPORTED;
504     }
505 
506     public QedeqConfig getConfig() {
507         return config;
508     }
509 
510     /**
511      * Init the kernel.
512      *
513      @param   moduleServices  Services for the kernel.
514      @param   qedeqConfig     Configuration for the kernel.
515      @throws  IOException     Initialization failure.
516      */
517     public void init(final ServiceModule moduleServices, final QedeqConfig qedeqConfig)
518             throws IOException {
519         currentState.init(moduleServices, qedeqConfig);
520     }
521 
522     /**
523      * Startup the kernel.
524      */
525     public void startup() {
526         currentState.startup();
527     }
528 
529     /**
530      * Shutdown the kernel.
531      */
532     public void shutdown() {
533         currentState.shutdown();
534     }
535 
536     public void removeAllModules() {
537         currentState.removeAllModules();
538     }
539 
540     public void removeModule(final ModuleAddress address) {
541         currentState.removeModule(address);
542     }
543 
544     public void clearLocalBuffer() throws IOException {
545         currentState.clearLocalBuffer();
546     }
547 
548     public QedeqBo loadModule(final ModuleAddress address) {
549         return currentState.loadModule(address);
550     }
551 
552     public boolean loadAllModulesFromQedeq() {
553         return currentState.loadAllModulesFromQedeq();
554     }
555 
556     public boolean loadRequiredModules(final ModuleAddress address) {
557         return currentState.loadRequiredModules(address);
558     }
559 
560     public ModuleAddress[] getAllLoadedModules() {
561         return currentState.getAllLoadedModules();
562     }
563 
564     public QedeqBo getQedeqBo(final ModuleAddress address) {
565         return currentState.getQedeqBo(address);
566     }
567 
568     public ModuleAddress getModuleAddress(final URL urlthrows IOException {
569         return currentState.getModuleAddress(url);
570     }
571 
572     public ModuleAddress getModuleAddress(final String urlthrows IOException {
573         return currentState.getModuleAddress(url);
574     }
575 
576     public ModuleAddress getModuleAddress(final File filethrows IOException {
577         return currentState.getModuleAddress(file);
578     }
579 
580     public String getSource(final ModuleAddress addressthrows IOException {
581         return currentState.getSource(address);
582     }
583 
584     public boolean checkModule(final ModuleAddress address) {
585         return currentState.checkModule(address);
586     }
587 
588     public Plugin[] getPlugins() {
589         return currentState.getPlugins();
590     }
591 
592     public Object executePlugin(final String pluginName, final ModuleAddress address,
593             final Map parameters) {
594         return currentState.executePlugin(pluginName, address, parameters);
595     }
596 
597     public void clearAllPluginResults(final ModuleAddress address) {
598         currentState.clearAllPluginResults(address);
599     }
600 
601     public ServiceProcess[] getServiceProcesses() {
602         return currentState.getServiceProcesses();
603     }
604 
605     /**
606      * Check java version. We want to be shure that the kernel is run at least with java 1.4.2
607      *
608      @throws  IOException     Application is running below java 1.4.2.
609      */
610     private void checkJavaVersion() throws IOException {
611         final String method = "checkJavaVersion";
612         Trace.info(CLASS, this, method, "running on java version "
613             + System.getProperty("java.version"));
614         final int[] versions = IoUtility.getJavaVersion();
615         if (versions == null) {
616             Trace.fatal(CLASS, this, method, "running java version unknown"null);
617             // we try to continue
618             return;
619         }
620         final StringBuffer version = new StringBuffer();
621         for (int i = 0; i < versions.length; i++) {
622             if (i > 0) {
623                 version.append(".");
624             }
625             version.append(versions[i]);
626         }
627         Trace.paramInfo(CLASS, this, method, "version", version);
628         // >= 1
629         if (versions.length < || versions[01) {
630             throw new IOException("This application requires at least Java 1.4.2 but we got "
631                 + version);
632         }
633         if (versions[0== 1) {         // further checking
634             // >= 1.4
635             if (versions.length < || versions[14) {
636                 throw new IOException("This application requires at least Java 1.4.2 but we got "
637                     + version);
638             }
639             if (versions[1== 4) {     // further checking
640                 // >=1.4.2
641                 if (versions.length < || versions[22) {
642                     throw new IOException(
643                         "This application requires at least Java 1.4.2 but we got "
644                         + version);
645                 }
646             }
647         }
648     }
649 
650     /**
651      * Create all necessary directories for the kernel.
652      *
653      @throws  IOException     Creation was not possible.
654      */
655     void createAllNecessaryDirectories() throws IOException {
656         // log directory
657         final File logFile = new File(config.getBasisDirectory(), config.getLogFile());
658         final File logDir = logFile.getParentFile();
659         if (!logDir.exists() &&  !logDir.mkdirs()) {
660             throw new IOException("can't create directory: " + logDir.getAbsolutePath());
661         }
662         // buffer directory
663         final File bufferDir = config.getBufferDirectory();
664         if (!bufferDir.exists() &&  !bufferDir.mkdirs()) {
665             throw new IOException("can't create directory: " + bufferDir.getAbsolutePath());
666         }
667         // generation directory
668         final File generationDir = config.getGenerationDirectory();
669         if (!generationDir.exists() &&  !generationDir.mkdirs()) {
670             throw new IOException("can't create directory: " + generationDir.getAbsolutePath());
671         }
672     }
673 
674     /**
675      * Checks if the application is already running. To check that we create a file in the
676      * buffer directory, open a stream and write something into it. The stream is not closed
677      * until kernel shutdown.
678      *
679      @throws  IOException     Application is already running.
680      */
681     private void checkIfApplicationIsAlreadyRunningAndLockFile()
682             throws IOException {
683         lockFile = new File(config.getBufferDirectory()"qedeq_lock.lck");
684         FileLock fl = null;
685         try {
686             lockStream = new FileOutputStream(lockFile);
687             lockStream.write("LOCKED".getBytes("UTF8"));
688             lockStream.flush();
689             fl = lockStream.getChannel().tryLock();
690         catch (IOException e) {
691             throw new IOException("It seems the application is already running.\n"
692                 "At least accessing the file \"" + lockFile.getAbsolutePath() "\" failed.");
693         }
694         if (fl == null) {
695             throw new IOException("It seems the application is already running.\n"
696                 "At least locking the file \"" + lockFile.getAbsolutePath() "\" failed.");
697         }
698     }
699 
700 }