DefaultInternalKernelServices.java
001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002  *
003  * Copyright 2000-2013,  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.service;
017 
018 import java.io.File;
019 import java.io.FileFilter;
020 import java.io.IOException;
021 import java.io.Reader;
022 import java.io.UnsupportedEncodingException;
023 import java.net.MalformedURLException;
024 import java.net.URL;
025 import java.net.URLEncoder;
026 import java.util.ArrayList;
027 import java.util.List;
028 
029 import org.qedeq.base.io.IoUtility;
030 import org.qedeq.base.io.LoadingListener;
031 import org.qedeq.base.io.Parameters;
032 import org.qedeq.base.io.SourceArea;
033 import org.qedeq.base.io.TextInput;
034 import org.qedeq.base.io.UrlUtility;
035 import org.qedeq.base.trace.Trace;
036 import org.qedeq.base.utility.StringUtility;
037 import org.qedeq.kernel.bo.KernelContext;
038 import org.qedeq.kernel.bo.common.KernelProperties;
039 import org.qedeq.kernel.bo.common.QedeqBo;
040 import org.qedeq.kernel.bo.common.ServiceModule;
041 import org.qedeq.kernel.bo.common.ServiceProcess;
042 import org.qedeq.kernel.bo.log.QedeqLog;
043 import org.qedeq.kernel.bo.module.InternalKernelServices;
044 import org.qedeq.kernel.bo.module.KernelQedeqBo;
045 import org.qedeq.kernel.bo.module.QedeqFileDao;
046 import org.qedeq.kernel.bo.service.logic.FormalProofCheckerPlugin;
047 import org.qedeq.kernel.bo.service.logic.SimpleProofFinderPlugin;
048 import org.qedeq.kernel.bo.service.logic.WellFormedCheckerPlugin;
049 import org.qedeq.kernel.se.base.module.Qedeq;
050 import org.qedeq.kernel.se.base.module.Specification;
051 import org.qedeq.kernel.se.common.DefaultModuleAddress;
052 import org.qedeq.kernel.se.common.LoadingState;
053 import org.qedeq.kernel.se.common.ModuleAddress;
054 import org.qedeq.kernel.se.common.ModuleDataException;
055 import org.qedeq.kernel.se.common.Plugin;
056 import org.qedeq.kernel.se.common.SourceFileException;
057 import org.qedeq.kernel.se.common.SourceFileExceptionList;
058 import org.qedeq.kernel.se.config.QedeqConfig;
059 import org.qedeq.kernel.se.dto.module.QedeqVo;
060 
061 
062 /**
063  * This class provides a default implementation for the QEDEQ module services.
064  *
065  @author  Michael Meyling
066  */
067 public class DefaultInternalKernelServices implements ServiceModule, InternalKernelServices, Plugin {
068 
069     /** This class. */
070     private static final Class CLASS = DefaultInternalKernelServices.class;
071 
072     /** For synchronized waiting. */
073     private static final Object MONITOR = new Object();
074 
075     /** Number of method calls. */
076     private volatile int processCounter = 0;
077 
078     /** Collection of already known QEDEQ modules. */
079     private KernelQedeqBoStorage modules;
080 
081     /** Config access. */
082     private final QedeqConfig config;
083 
084     /** Basic kernel properties. */
085     private final KernelProperties kernel;
086 
087     /** This instance nows how to load a module from the file system. */
088     private final QedeqFileDao qedeqFileDao;
089 
090     /** This instance manages plugins. */
091     private final PluginManager pluginManager;
092 
093     /** This instance manages service processes. */
094     private final ServiceProcessManager processManager;
095 
096     /** Validate module dependencies and status. */
097     private boolean validate = true;
098 
099     /**
100      * Constructor.
101      *
102      @param   config  For config access.
103      @param   kernel  For kernel properties.
104      @param   loader  For loading QEDEQ modules.
105      */
106     public DefaultInternalKernelServices(final QedeqConfig config, final KernelProperties kernel,
107             final QedeqFileDao loader) {
108         this.config = config;
109         this.kernel = kernel;
110         this.qedeqFileDao = loader;
111         processManager = new ServiceProcessManager();
112         pluginManager = new PluginManager(this, processManager);
113         loader.setServices(this);
114 
115 //      pluginManager.addPlugin(MultiProofFinderPlugin.class.getName());
116         pluginManager.addPlugin("org.qedeq.kernel.bo.service.unicode.Qedeq2UnicodeTextPlugin");
117         pluginManager.addPlugin("org.qedeq.kernel.bo.service.latex.Qedeq2LatexPlugin");
118         pluginManager.addPlugin("org.qedeq.kernel.bo.service.unicode.Qedeq2Utf8Plugin");
119 //        pluginManager.addPlugin("org.qedeq.kernel.bo.service.heuristic.HeuristicCheckerPlugin");
120         pluginManager.addPlugin(FormalProofCheckerPlugin.class.getName());
121         pluginManager.addPlugin("org.qedeq.kernel.bo.service.heuristic.DynamicHeuristicCheckerPlugin");
122         pluginManager.addPlugin(SimpleProofFinderPlugin.class.getName());
123 
124     }
125 
126     public void startupServices() {
127         modules = new KernelQedeqBoStorage();
128         if (config.isAutoReloadLastSessionChecked()) {
129             autoReloadLastSessionChecked();
130         }
131     }
132 
133     public void shutdownServices() {
134         processManager.terminateAndRemoveAllServiceProcesses();
135         modules.removeAllModules();
136         modules = null;
137         // clear thread interrupt flag because we might have interrupted ourself
138         Thread.interrupted();
139     }
140 
141     /**
142      * If configured load all QEDEQ modules that where successfully loaded the last time.
143      */
144     public void autoReloadLastSessionChecked() {
145         if (config.isAutoReloadLastSessionChecked()) {
146             final Thread thread = new Thread() {
147                 public void run() {
148                     final String method = "autoReloadLastSessionChecked.thread.run()";
149                     try {
150                         Trace.begin(CLASS, this, method);
151                         QedeqLog.getInstance().logMessage(
152                             "Trying to load previously successfully loaded modules.");
153                         final int number = config.getPreviouslyLoadedModules().length;
154                         if (loadPreviouslySuccessfullyLoadedModules()) {
155                             QedeqLog.getInstance().logMessage(
156                                 "Loading of " + number + " previously successfully loaded module"
157                                     (number != "s" """ successfully done.");
158                         else {
159                             QedeqLog.getInstance().logMessage(
160                                 "Loading of all previously successfully checked modules failed. "
161                                     + number + " module" (number != "s" "")
162                                     " were tried.");
163                         }
164                     catch (Exception e) {
165                         Trace.trace(CLASS, this, method, e);
166                     finally {
167                         Trace.end(CLASS, this, method);
168                     }
169                 }
170             };
171             thread.setDaemon(true);
172             thread.start();
173         }
174     }
175 
176     public void removeAllModules() {
177         do {
178             synchronized (this) {
179                 if (processCounter == 0) {
180                     getModules().removeAllModules();
181                     return;
182                 }
183             }
184             // we must wait for the other processes to stop (so that processCounter == 0)
185             synchronized (MONITOR) {
186                 try {
187                     MONITOR.wait(10000);
188                 catch (InterruptedException e) {
189                 }
190             }
191         while (true);
192 
193     }
194 
195     /**
196      * Remove a QEDEQ module from memory.
197      *
198      @param address Remove module identified by this address.
199      */
200     public void removeModule(final ModuleAddress address) {
201         final QedeqBo prop = getQedeqBo(address);
202         if (prop != null) {
203             QedeqLog.getInstance().logRequest("Removing module", address.getUrl());
204             try {
205                 removeModule(getModules().getKernelQedeqBo(this, address));
206             catch (final RuntimeException e) {
207                 QedeqLog.getInstance().logFailureReply(
208                     "Remove failed", address.getUrl(), e.getMessage());
209             }
210 
211             if (validate) {
212                 modules.validateDependencies();
213             }
214         }
215     }
216 
217     /**
218      * Remove a QEDEQ module from memory. This method must block all other methods and if this
219      * method runs no other is allowed to run
220      *
221      @param prop Remove module identified by this property.
222      */
223     private void removeModule(final DefaultKernelQedeqBo prop) {
224         do {
225             synchronized (this) {
226                 if (processCounter == 0) { // no other method is allowed to run
227                     // TODO mime 20080319: one could call prop.setLoadingProgressState(
228                     // LoadingState.STATE_DELETED) alone but that would
229                     // miss to inform the KernelQedeqBoPool. How do we inform the pool?
230                     // must the StateManager have a reference to it?
231                     prop.delete();
232                     getModules().removeModule(prop);
233                     return;
234                 }
235             }
236             // we must wait for the other processes to stop (so that processCounter == 0)
237             synchronized (MONITOR) {
238                 try {
239                     MONITOR.wait(10000);
240                 catch (InterruptedException e) {
241                 }
242             }
243         while (true);
244 
245     }
246 
247     /**
248      * Clear local file buffer and all loaded QEDEQ modules.
249      *
250      @return  Successful?
251      */
252     public boolean clearLocalBuffer() {
253         final String method = "clearLocalBuffer";
254         try {
255             QedeqLog.getInstance().logMessage(
256                 "Clear local buffer from all QEDEQ files.");
257             removeAllModules();
258             final File bufferDir = getBufferDirectory().getCanonicalFile();
259             if (bufferDir.exists() && !IoUtility.deleteDir(bufferDir, new FileFilter() {
260                         public boolean accept(final File pathname) {
261                             return pathname.getName().endsWith(".xml");
262                         }
263                     })) {
264                 throw new IOException("buffer could not be deleted: " + bufferDir);
265             }
266             QedeqLog.getInstance().logMessage("Local buffer was cleared.");
267             return true;
268         catch (IOException e) {
269             Trace.fatal(CLASS, this, method, "IO access problem", e);
270             QedeqLog.getInstance().logMessage(
271                 "Local buffer not cleared. IO access problem. " + e.getMessage());
272             return false;
273         catch (final RuntimeException e) {
274             Trace.fatal(CLASS, this, method, "unexpected problem", e);
275             QedeqLog.getInstance().logMessage(
276                 "Local buffer not cleared. " + e.getMessage());
277             return false;
278         }
279     }
280 
281     public QedeqBo loadModule(final ModuleAddress address) {
282         final String method = "loadModule(ModuleAddress)";
283         processInc();
284         final DefaultKernelQedeqBo prop = getModules().getKernelQedeqBo(this, address);
285         try {
286             synchronized (prop) {
287                 if (prop.isLoaded()) {
288                     return prop;
289                 }
290                 QedeqLog.getInstance().logRequest("Load module", address.getUrl());
291                 if (prop.getModuleAddress().isFileAddress()) {
292                     loadLocalModule(prop);
293                 else {
294                     // search in local file buffer
295                     try {
296                         getCanonicalReadableFile(prop);
297                     catch (ModuleFileNotFoundException e) { // file not found
298                         // we will continue by creating a local copy
299                         saveQedeqFromWebToBuffer(prop);
300                     }
301                     loadBufferedModule(prop);
302                 }
303                 QedeqLog.getInstance().logSuccessfulReply(
304                     "Successfully loaded", address.getUrl());
305             }
306         catch (SourceFileExceptionList e) {
307             Trace.trace(CLASS, this, method, e);
308             QedeqLog.getInstance().logFailureState("Loading of module failed!", address.getUrl(),
309                 e.toString());
310         catch (final RuntimeException e) {
311             Trace.fatal(CLASS, this, method, "unexpected problem", e);
312             QedeqLog.getInstance().logFailureReply("Loading failed", address.getUrl(), e.getMessage());
313         finally {
314             processDec();
315         }
316         return prop;
317     }
318 
319     /**
320      * Load buffered QEDEQ module file.
321      *
322      @param prop Load this.
323      @throws SourceFileExceptionList Loading or QEDEQ module failed.
324      */
325     private void loadBufferedModule(final DefaultKernelQedeqBo prop)
326             throws SourceFileExceptionList {
327         prop.setLoadingProgressState(LoadingState.STATE_LOADING_FROM_BUFFER);
328         final File localFile;
329         try {
330             localFile = getCanonicalReadableFile(prop);
331         catch (ModuleFileNotFoundException e) {
332             final SourceFileExceptionList sfl = createSourceFileExceptionList(
333                 ServiceErrors.LOADING_FROM_FILE_BUFFER_FAILED_CODE,
334                 ServiceErrors.LOADING_FROM_FILE_BUFFER_FAILED_TEXT,
335                 prop.getUrl(), e);
336             prop.setLoadingFailureState(LoadingState.STATE_LOADING_FROM_BUFFER_FAILED, sfl);
337             throw sfl;
338         }
339 
340         prop.setQedeqFileDao(getQedeqFileDao())// remember loader for this module
341         final Qedeq qedeq;
342         try {
343             qedeq = getQedeqFileDao().loadQedeq(prop, localFile);
344         catch (SourceFileExceptionList sfl) {
345             prop.setLoadingFailureState(LoadingState.STATE_LOADING_FROM_BUFFER_FAILED, sfl);
346             throw sfl;
347         }
348         setCopiedQedeq(prop, qedeq);
349     }
350 
351     /**
352      * Load QEDEQ module file with file loader.
353      *
354      @param prop Load this.
355      @throws SourceFileExceptionList Loading or copying QEDEQ module failed.
356      */
357     private void loadLocalModule(final DefaultKernelQedeqBo propthrows SourceFileExceptionList {
358         prop.setLoadingProgressState(LoadingState.STATE_LOADING_FROM_LOCAL_FILE);
359         final File localFile;
360         try {
361             localFile = getCanonicalReadableFile(prop);
362         catch (ModuleFileNotFoundException e) {
363             final SourceFileExceptionList sfl = createSourceFileExceptionList(
364                 ServiceErrors.LOADING_FROM_LOCAL_FILE_FAILED_CODE,
365                 ServiceErrors.LOADING_FROM_LOCAL_FILE_FAILED_TEXT,
366                 prop.getUrl(), e);
367             prop.setLoadingFailureState(LoadingState.STATE_LOADING_FROM_LOCAL_FILE_FAILED, sfl);
368             throw sfl;
369         }
370         prop.setQedeqFileDao(getQedeqFileDao())// remember loader for this module
371 
372         final Qedeq qedeq;
373         try {
374             qedeq = getQedeqFileDao().loadQedeq(prop, localFile);
375         catch (SourceFileExceptionList sfl) {
376             prop.setLoadingFailureState(LoadingState.STATE_LOADING_FROM_LOCAL_FILE_FAILED, sfl);
377             throw sfl;
378         }
379         setCopiedQedeq(prop, qedeq);
380     }
381 
382     private void setCopiedQedeq(final DefaultKernelQedeqBo prop, final Qedeq qedeq)
383             throws SourceFileExceptionList {
384         final String method = "setCopiedQedeq(DefaultKernelQedeqBo, Qedeq)";
385         prop.setLoadingProgressState(LoadingState.STATE_LOADING_INTO_MEMORY);
386         QedeqVo vo = null;
387         try {
388             vo = QedeqVoBuilder.createQedeq(prop.getModuleAddress(), qedeq);
389         catch (RuntimeException e) {
390             Trace.fatal(CLASS, this, method, "looks like a programming error", e);
391             final SourceFileExceptionList xl = createSourceFileExceptionList(
392                 ServiceErrors.RUNTIME_ERROR_CODE,
393                 ServiceErrors.RUNTIME_ERROR_TEXT,
394                 prop.getModuleAddress().getUrl(), e);
395             prop.setLoadingFailureState(LoadingState.STATE_LOADING_INTO_MEMORY_FAILED, xl);
396             throw xl;
397         catch (ModuleDataException e) {
398             if (e.getCause() != null) {
399                 Trace.fatal(CLASS, this, method, "looks like a programming error", e.getCause());
400             else {
401                 Trace.fatal(CLASS, this, method, "looks like a programming error", e);
402             }
403             final SourceFileExceptionList xl = prop.createSourceFileExceptionList(this, e, qedeq);
404             prop.setLoadingFailureState(LoadingState.STATE_LOADING_INTO_MEMORY_FAILED, xl);
405             throw xl;
406         }
407         prop.setQedeqVo(vo);
408         // TODO 20110213 m31: perhaps we need a new state, pre loaded? So when we put more
409         // label testing into the moduleLabelCreator, we still can launch some plugins
410         // On the other side: Label checking is only possible, if all referenced modules can
411         // be loaded.
412         //
413         // Correct labels are necessary for many plugins (e.g. LaTeX and UTF-8 generation).
414         // So a label checker must be run before that.
415         // It might be a good idea to put it into the formal logic checker.
416         // We could make a FormalChecker plugin. This starts loading required modules, checks
417         // the labels and checks if the formulas are correctly written.
418         // So we get some sub status (for every check) and an overall status (all checks
419         // green). Later on the formal proof checker can be integrated too.
420         // This should be the extended load status.
421         final ModuleLabelsCreator moduleNodesCreator = new ModuleLabelsCreator(this, prop);
422         try {
423             moduleNodesCreator.createLabels();
424             prop.setLoaded(vo, moduleNodesCreator.getLabels(), moduleNodesCreator.getConverter(),
425                 moduleNodesCreator.getTextConverter());
426         catch (SourceFileExceptionList sfl) {
427             prop.setLoadingFailureState(LoadingState.STATE_LOADING_INTO_MEMORY_FAILED, sfl);
428             throw sfl;
429         }
430     }
431 
432     /**
433      * Check if file exists and is readable. Checks the local buffer file for a buffered module or
434      * the module file address directly. Returns canonical file path.
435      *
436      @param prop Check for this file.
437      @return Canonical file path.
438      @throws ModuleFileNotFoundException File doesn't exist or is not readable.
439      */
440     private File getCanonicalReadableFile(final QedeqBo propthrows ModuleFileNotFoundException {
441         final String method = "getCanonicalReadableFile(File)";
442         final File localFile = getLocalFilePath(prop.getModuleAddress());
443         final File file;
444         try {
445             file = localFile.getCanonicalFile();
446         catch (IOException e) {
447             Trace.trace(CLASS, this, method, e);
448             throw new ModuleFileNotFoundException("file path not correct: " + localFile);
449         }
450         if (!file.canRead()) {
451             Trace.trace(CLASS, this, method, "file not readable=" + file);
452             throw new ModuleFileNotFoundException("file not readable: " + file);
453         }
454         return file;
455     }
456 
457     /**
458      * Load specified QEDEQ module from QEDEQ parent module.
459      *
460      @param parent Parent module address.
461      @param spec Specification for another QEDEQ module.
462      @return Loaded module.
463      @throws SourceFileExceptionList Loading failed.
464      */
465     public KernelQedeqBo loadModule(final ModuleAddress parent, final Specification spec)
466             throws SourceFileExceptionList {
467 
468         final String method = "loadModule(Module, Specification)";
469         Trace.begin(CLASS, this, method);
470         Trace.trace(CLASS, this, method, spec);
471         processInc();
472         DefaultKernelQedeqBo prop = null// currently tried module
473         try {
474             final ModuleAddress[] modulePaths;
475             try {
476                 modulePaths = parent.getModulePaths(spec);
477             catch (IOException e) {
478                 Trace.fatal(CLASS, this, method, "getting module path failed", e);  // TODO 20110308 m31: make constant
479                 throw createSourceFileExceptionList(
480                     ServiceErrors.LOADING_FROM_FILE_BUFFER_FAILED_CODE,
481                     ServiceErrors.LOADING_FROM_FILE_BUFFER_FAILED_TEXT,
482                     parent.getUrl(), e);
483             }
484 
485             // now we iterate over the possible module addresses
486             for (int i = 0; i < modulePaths.length; i++) {
487                 prop = getModules().getKernelQedeqBo(this, modulePaths[i]);
488                 Trace.trace(CLASS, this, method, "synchronizing at prop=" + prop);
489                 synchronized (prop) {
490                     if (prop.isLoaded()) {
491                         return (prop);
492                     }
493                     try {
494                         if (prop.getModuleAddress().isFileAddress()) {
495                             loadLocalModule(prop);
496                         else {
497                             // search in local file buffer
498                             try {
499                                 getCanonicalReadableFile(prop);
500                             catch (ModuleFileNotFoundException e) { // file not found
501                                 // we will continue by creating a local copy
502                                 saveQedeqFromWebToBuffer(prop);
503                             }
504                             loadBufferedModule(prop);
505                         }
506                         // success!
507                         return prop;
508                     catch (SourceFileExceptionList e) {
509                         Trace.trace(CLASS, this, method, e);
510                         if (i + < modulePaths.length) {
511                             QedeqLog.getInstance().logMessage("trying alternate path");
512                             // we continue with the next path
513                         else {
514                             // we surrender
515                             throw e;
516                         }
517                     }
518                 }
519             }
520             return prop; // never called, only here to soothe the compiler
521         catch (final RuntimeException e) {
522             Trace.fatal(CLASS, this, method, "unexpected problem", e);
523             QedeqLog.getInstance().logFailureReply("Loading failed", prop.getUrl(), e.getMessage());
524             throw e;
525         finally {
526             processDec();
527             Trace.end(CLASS, this, method);
528         }
529     }
530 
531     public ModuleAddress[] getAllLoadedModules() {
532         return getModules().getAllLoadedModules();
533     }
534 
535     public boolean loadRequiredModules(final ModuleAddress address) {
536         final DefaultKernelQedeqBo prop = (DefaultKernelQedeqBoloadModule(address);
537         if (prop.hasBasicFailures()) {
538             return false;
539         }
540         return LoadRequiredModules.loadRequired(this, prop);
541     }
542 
543     /**
544      * Load all previously checked QEDEQ modules.
545      *
546      @return Successfully reloaded all modules.
547      */
548     public boolean loadPreviouslySuccessfullyLoadedModules() {
549         processInc();
550         try {
551             final String[] list = config.getPreviouslyLoadedModules();
552             boolean errors = false;
553             for (int i = 0; i < list.length; i++) {
554                 try {
555                     final ModuleAddress address = getModuleAddress(list[i]);
556                     final QedeqBo prop = loadModule(address);
557                     if (prop.hasErrors()) {
558                         errors = true;
559                     }
560                 catch (IOException e) {
561                     Trace.fatal(CLASS, this, "loadPreviouslySuccessfullyLoadedModules",
562                         "internal error: " "saved URLs are malformed", e);
563                     errors = true;
564                 }
565             }
566             return !errors;
567         finally {
568             processDec();
569         }
570     }
571 
572     // LATER mime 20070326: dynamic loading from web page directory
573     public boolean loadAllModulesFromQedeq() {
574         processInc();
575         try {
576             final String prefix = "http://www.qedeq.org/" + kernel.getKernelVersionDirectory() "/";
577             final String[] list = new String[] {
578                 prefix + "doc/math/qedeq_logic_v1.xml",
579                 prefix + "doc/math/qedeq_formal_logic_v1.xml",
580                 prefix + "doc/math/qedeq_set_theory_v1.xml",
581                 prefix + "doc/project/qedeq_basic_concept.xml",
582                 prefix + "doc/project/qedeq_logic_language.xml",
583                 prefix + "sample/qedeq_sample1.xml",
584                 prefix + "sample/qedeq_sample2.xml",
585                 prefix + "sample/qedeq_sample3.xml",
586                 prefix + "sample/qedeq_sample4.xml",
587                 prefix + "sample/qedeq_error_sample_00.xml",
588                 prefix + "sample/qedeq_error_sample_01.xml",
589                 prefix + "sample/qedeq_error_sample_02.xml",
590                 prefix + "sample/qedeq_error_sample_03.xml",
591                 prefix + "sample/qedeq_error_sample_04.xml",
592                 prefix + "sample/qedeq_error_sample_05.xml",
593                 prefix + "sample/qedeq_error_sample_12.xml",
594                 prefix + "sample/qedeq_error_sample_13.xml",
595                 prefix + "sample/qedeq_error_sample_14.xml",
596                 prefix + "sample/qedeq_error_sample_15.xml",
597                 prefix + "sample/qedeq_error_sample_16.xml",
598                 prefix + "sample/qedeq_error_sample_17.xml",
599                 prefix + "sample/qedeq_error_sample_18.xml"};
600             boolean errors = false;
601             for (int i = 0; i < list.length; i++) {
602                 try {
603                     final ModuleAddress address = getModuleAddress(list[i]);
604                     final QedeqBo prop = loadModule(address);
605                     if (prop.hasErrors()) {
606                         errors = true;
607                     }
608                 catch (IOException e) {
609                     Trace.fatal(CLASS, this, "loadPreviouslySuccessfullyLoadedModules",
610                         "internal error: " "saved URLs are malformed", e);
611                     errors = true;
612                 }
613             }
614             return !errors;
615         finally {
616             processDec();
617         }
618     }
619 
620     /**
621      * Make local copy of a module if it is no file address.
622      *
623      @param   prop    Module properties.
624      @throws  SourceFileExceptionList Address was malformed or the file can not be found.
625      */
626     private void saveQedeqFromWebToBuffer(final DefaultKernelQedeqBo prop)
627             throws SourceFileExceptionList {
628         final String method = "saveQedeqFromWebToBuffer(DefaultKernelQedeqBo)";
629         Trace.begin(CLASS, this, method);
630 
631         if (prop.getModuleAddress().isFileAddress()) { // this is already a local file
632             Trace.fatal(CLASS, this, method, "tried to make a local copy for a local module"null);
633             Trace.end(CLASS, this, method);
634             return;
635         }
636         prop.setLoadingProgressState(LoadingState.STATE_LOADING_FROM_WEB);
637 
638         final File f = getLocalFilePath(prop.getModuleAddress());
639         try {
640             UrlUtility.saveUrlToFile(prop.getUrl(), f,
641             config.getHttpProxyHost(), config.getHttpProxyPort(), config.getHttpNonProxyHosts(),
642             config.getConnectionTimeout(), config.getReadTimeout()new LoadingListener() {
643                 public void loadingCompletenessChanged(final double completeness) {
644                     prop.setLoadingCompleteness((intcompleteness * 100);
645                 }
646             });
647         catch (IOException e) {
648             Trace.trace(CLASS, this, method, e);
649             try {
650                 f.delete();
651             catch (Exception ex) {
652                 Trace.trace(CLASS, this, method, ex);
653             }
654             final SourceFileExceptionList sfl = createSourceFileExceptionList(
655                 ServiceErrors.LOADING_FROM_WEB_FAILED_CODE,
656                 ServiceErrors.LOADING_FROM_WEB_FAILED_TEXT,
657                 prop.getUrl(), e);
658             prop.setLoadingFailureState(LoadingState.STATE_LOADING_FROM_WEB_FAILED, sfl);
659             Trace.trace(CLASS, this, method, "Couldn't access " + prop.getUrl());
660             throw sfl;
661         finally {
662             Trace.end(CLASS, this, method);
663         }
664     }
665 
666     public final File getLocalFilePath(final ModuleAddress address) {
667         final String method = "getLocalFilePath(ModuleAddress)";
668         URL url;
669         try {
670             url = new URL(address.getUrl());
671         catch (MalformedURLException e) {
672             Trace.fatal(CLASS, this, method, "Could not get local file path.", e);
673             return null;
674         }
675         Trace.param(CLASS, this, method, "protocol", url.getProtocol());
676         Trace.param(CLASS, this, method, "host", url.getHost());
677         Trace.param(CLASS, this, method, "port", url.getPort());
678         Trace.param(CLASS, this, method, "path", url.getPath());
679         Trace.param(CLASS, this, method, "file", url.getFile());
680         if (address.isFileAddress()) {
681             try {
682                 return UrlUtility.transformURLPathToFilePath(url);
683             catch (IllegalArgumentException e) {
684                 // should not occur because check for validy must be done in constructor of address
685                 Trace.fatal(CLASS, this, method, "Loading failed of local file with URL=" + url, e);
686                 throw new RuntimeException(e);
687             }
688         }
689         StringBuffer file = new StringBuffer(url.getFile());
690         StringUtility.replace(file, "_""_1")// remember all '_'
691         StringUtility.replace(file, "/""_2")// preserve all '/'
692         String encoded = file.toString()// fallback file name
693         try {
694             encoded = URLEncoder.encode(file.toString()"UTF-8");
695         catch (UnsupportedEncodingException e) {
696             // should not occur
697             Trace.trace(CLASS, method, e);
698         }
699         file.setLength(0);
700         file.append(encoded);
701         StringUtility.replace(file, "#""##")// escape all '#'
702         StringUtility.replace(file, "_2""#")// from '/' into '#'
703         StringUtility.replace(file, "_1""_")// from '_' into '_'
704         // mime 2010-06-25: use if we throw no RuntimException
705         // StringBuffer adr = new StringBuffer(url.toExternalForm());
706         final StringBuffer adr;
707         try {
708             adr = new StringBuffer(new URL(url.getProtocol(), url.getHost(), url.getPort(), file
709                 .toString()).toExternalForm());
710         catch (MalformedURLException e) {
711             Trace.fatal(CLASS, this, method, "unexpected", e);
712             throw new RuntimeException(e);
713         }
714         // escape characters:
715         StringUtility.replace(adr, "://""_")// before host
716         StringUtility.replace(adr, ":""_")// before protocol
717         return new File(getBufferDirectory(), adr.toString());
718     }
719 
720     /**
721      * Increment intern process counter.
722      */
723     private synchronized void processInc() {
724         this.processCounter++;
725     }
726 
727     /**
728      * Decrement intern process counter.
729      */
730     private synchronized void processDec() {
731         this.processCounter--;
732     }
733 
734     public File getBufferDirectory() {
735         return config.getBufferDirectory();
736     }
737 
738     public File getGenerationDirectory() {
739         return config.getGenerationDirectory();
740     }
741 
742     public KernelQedeqBo getKernelQedeqBo(final ModuleAddress address) {
743         return getModules().getKernelQedeqBo(this, address);
744     }
745 
746     public QedeqBo getQedeqBo(final ModuleAddress address) {
747         return getModules().getKernelQedeqBo(this, address);
748     }
749 
750     public ModuleAddress getModuleAddress(final URL urlthrows IOException {
751         return new DefaultModuleAddress(url);
752     }
753 
754     public ModuleAddress getModuleAddress(final String urlthrows IOException {
755         return new DefaultModuleAddress(url);
756     }
757 
758     public ModuleAddress getModuleAddress(final File filethrows IOException {
759         return new DefaultModuleAddress(file);
760     }
761 
762     public String getSource(final ModuleAddress addressthrows IOException {
763         final KernelQedeqBo bo = getKernelQedeqBo(address);
764         if (bo.getLoadingState().equals(LoadingState.STATE_UNDEFINED)
765             || bo.getLoadingState().equals(LoadingState.STATE_LOADING_FROM_WEB)
766             || bo.getLoadingState().equals(LoadingState.STATE_LOADING_FROM_WEB_FAILED)) {
767             return null;
768         }
769         final StringBuffer buffer = new StringBuffer();
770         final Reader reader = getQedeqFileDao().getModuleReader(bo);
771         try {
772             IoUtility.loadReader(reader, buffer);
773         finally {
774             IoUtility.close(reader);
775         }
776         return buffer.toString();
777     }
778 
779     public boolean checkModule(final ModuleAddress address) {
780         final String method = "checkModule(ModuleAddress)";
781         final DefaultKernelQedeqBo prop = modules.getKernelQedeqBo(this, address);
782         // did we check this already?
783         if (prop.isChecked()) {
784             return true// everything is OK
785         }
786         try {
787             // TODO 20110606 m31: perhaps this should be a real plugin and is managed by the PluginManager?
788             // perhaps we have to make a difference between normal and hidden internal plugins?
789             final WellFormedCheckerPlugin checker = new WellFormedCheckerPlugin();
790             // set default plugin values for not yet set parameters
791             final Parameters parameters = KernelContext.getInstance().getConfig().getPluginEntries(checker);
792             checker.setDefaultValuesForEmptyPluginParameters(parameters);
793             KernelContext.getInstance().getConfig().setPluginKeyValues(checker, parameters);
794             checker.createExecutor(prop, parameters).executePlugin();
795         catch (final RuntimeException e) {
796             final String msg = "Check of logical correctness failed";
797             Trace.fatal(CLASS, this, method, msg, e);
798             QedeqLog.getInstance().logFailureReply(msg, address.getUrl(), e.getMessage());
799             throw e;
800         finally {
801             if (validate) {
802                 modules.validateDependencies();
803             }
804         }
805         return prop.isChecked();
806     }
807 
808     /**
809      * Add plugin to services.
810      *
811      @param   pluginClass Plugin class to instantiate.
812      @throws  RuntimeException    Addition failed.
813      */
814     public void addPlugin(final String pluginClass) {
815         pluginManager.addPlugin(pluginClass);
816     }
817 
818     public Plugin[] getPlugins() {
819         return pluginManager.getPlugins();
820     }
821 
822     public Object executePlugin(final String id, final ModuleAddress address) {
823         return pluginManager.executePlugin(id, getKernelQedeqBo(address));
824     }
825 
826     public void clearAllPluginResults(final ModuleAddress address) {
827         pluginManager.clearAllPluginResults(getKernelQedeqBo(address));
828     }
829 
830     public ServiceProcess[] getServiceProcesses() {
831         return processManager.getServiceProcesses();
832     }
833 
834     public void stopAllPluginExecutions() {
835         processManager.terminateAllServiceProcesses();
836     }
837 
838 
839     /**
840      * Get all loaded QEDEQ modules.
841      *
842      @return All QEDEQ modules.
843      */
844     private KernelQedeqBoStorage getModules() {
845         return modules;
846     }
847 
848     public SourceFileExceptionList createSourceFileExceptionList(final int code,
849             final String message, final String address, final IOException e) {
850         return new SourceFileExceptionList(new SourceFileException(this,
851             code, message, e, new SourceArea(address)null));
852     }
853 
854     public SourceFileExceptionList createSourceFileExceptionList(final int code,
855             final String message, final String address, final RuntimeException e) {
856         return new SourceFileExceptionList(new SourceFileException(this,
857             code, message, e, new SourceArea(address)null));
858     }
859 
860     public SourceFileExceptionList createSourceFileExceptionList(final int code,
861             final String message, final String address, final Exception e) {
862         return new SourceFileExceptionList(new SourceFileException(this,
863             code, message, e, new SourceArea(address)null));
864     }
865 
866     /**
867      * Get description of source file exception list.
868      *
869      @param address Get description for this module exceptions.
870      @return Error description and location.
871      */
872     public String[] getSourceFileExceptionList(final ModuleAddress address) {
873         final List list = new ArrayList();
874         final KernelQedeqBo bo = getKernelQedeqBo(address);
875         final SourceFileExceptionList sfl = bo.getErrors();
876         if (sfl.size() 0) {
877             final StringBuffer buffer = new StringBuffer();
878             do {
879                 Reader reader = null;
880                 try {
881                     reader = getQedeqFileDao().getModuleReader(bo);
882                     IoUtility.loadReader(reader, buffer);
883                 catch (IOException e) {
884                     IoUtility.close(reader);
885                     for (int i = 0; i < sfl.size(); i++) {
886                         list.add(sfl.get(i).getDescription());
887                     }
888                     break// out of do while
889                 }
890                 final TextInput input = new TextInput(buffer);
891                 try {
892                     input.setPosition(0);
893                     final StringBuffer buf = new StringBuffer();
894                     for (int i = 0; i < sfl.size(); i++) {
895                         buf.setLength(0);
896                         final SourceFileException sf = sfl.get(i);
897                         buf.append(sf.getDescription());
898                         try {
899                             if (sf.getSourceArea() != null
900                                 && sf.getSourceArea().getStartPosition() != null) {
901                                 buf.append("\n");
902                                 input.setRow(sf.getSourceArea().getStartPosition().getRow());
903                                 buf.append(StringUtility.replace(input.getLine()"\t"" "));
904                                 buf.append("\n");
905                                 final StringBuffer whitespace = StringUtility.getSpaces(sf
906                                     .getSourceArea().getStartPosition().getColumn() 1);
907                                 buffer.append(whitespace);
908                                 buffer.append("^");
909                             }
910                         catch (Exception e) {
911                             Trace.trace(CLASS, this, "getSourceFileExceptionList(ModuleAddress)", e);
912                         }
913                         list.add(buf.toString());
914                     }
915                 finally {
916                     IoUtility.close(input);
917                 }
918                 break// out of do while
919             while (true);
920         }
921         return (String[]) list.toArray(new String[list.size()]);
922     }
923 
924     public String getPluginId() {
925         return CLASS.getName();
926     }
927 
928     public String getPluginActionName() {
929         return "Basis";
930     }
931     public QedeqFileDao getQedeqFileDao() {
932         return qedeqFileDao;
933     }
934 
935     public String getPluginDescription() {
936         return "provides basic services for loading QEDEQ modules";
937     }
938 
939     public QedeqConfig getConfig() {
940         return config;
941     }
942 
943     public String getKernelVersionDirectory() {
944         return kernel.getKernelVersionDirectory();
945     }
946 
947     public String getBuildId() {
948         return kernel.getBuildId();
949     }
950 
951     public String getDedication() {
952         return kernel.getDedication();
953     }
954 
955     public String getDescriptiveKernelVersion() {
956         return kernel.getDescriptiveKernelVersion();
957     }
958 
959     public String getKernelCodeName() {
960         return kernel.getKernelCodeName();
961     }
962 
963     public String getKernelVersion() {
964         return kernel.getKernelVersion();
965     }
966 
967     public String getMaximalRuleVersion() {
968         return kernel.getMaximalRuleVersion();
969     }
970 
971     public boolean isRuleVersionSupported(final String ruleVersion) {
972         return kernel.isRuleVersionSupported(ruleVersion);
973     }
974 
975     public boolean isSetConnectionTimeOutSupported() {
976         return kernel.isSetConnectionTimeOutSupported();
977     }
978 
979     public boolean isSetReadTimeoutSupported() {
980         return kernel.isSetReadTimeoutSupported();
981     }
982 
983 }