DefaultInternalKernelServices.java
0001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
0002  *
0003  * Copyright 2000-2011,  Michael Meyling <mime@qedeq.org>.
0004  *
0005  * "Hilbert II" is free software; you can redistribute
0006  * it and/or modify it under the terms of the GNU General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2 of the License, or (at your option) any later version.
0009  *
0010  * This program is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0013  * GNU General Public License for more details.
0014  */
0015 
0016 package org.qedeq.kernel.bo.service;
0017 
0018 import java.io.File;
0019 import java.io.FileFilter;
0020 import java.io.FileNotFoundException;
0021 import java.io.FileOutputStream;
0022 import java.io.IOException;
0023 import java.io.InputStream;
0024 import java.io.Reader;
0025 import java.io.UnsupportedEncodingException;
0026 import java.lang.reflect.InvocationTargetException;
0027 import java.net.HttpURLConnection;
0028 import java.net.MalformedURLException;
0029 import java.net.URL;
0030 import java.net.URLConnection;
0031 import java.net.URLEncoder;
0032 import java.util.ArrayList;
0033 import java.util.List;
0034 import java.util.Map;
0035 
0036 import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
0037 import org.apache.commons.httpclient.HttpClient;
0038 import org.apache.commons.httpclient.HttpStatus;
0039 import org.apache.commons.httpclient.methods.GetMethod;
0040 import org.apache.commons.httpclient.params.HttpMethodParams;
0041 import org.qedeq.base.io.IoUtility;
0042 import org.qedeq.base.io.SourceArea;
0043 import org.qedeq.base.io.TextInput;
0044 import org.qedeq.base.trace.Trace;
0045 import org.qedeq.base.utility.StringUtility;
0046 import org.qedeq.base.utility.YodaUtility;
0047 import org.qedeq.kernel.bo.KernelContext;
0048 import org.qedeq.kernel.bo.common.KernelProperties;
0049 import org.qedeq.kernel.bo.common.QedeqBo;
0050 import org.qedeq.kernel.bo.common.ServiceModule;
0051 import org.qedeq.kernel.bo.common.ServiceProcess;
0052 import org.qedeq.kernel.bo.log.QedeqLog;
0053 import org.qedeq.kernel.bo.module.InternalKernelServices;
0054 import org.qedeq.kernel.bo.module.KernelQedeqBo;
0055 import org.qedeq.kernel.bo.module.QedeqFileDao;
0056 import org.qedeq.kernel.bo.service.logic.FormalProofCheckerPlugin;
0057 import org.qedeq.kernel.bo.service.logic.SimpleProofFinderPlugin;
0058 import org.qedeq.kernel.bo.service.logic.WellFormedCheckerPlugin;
0059 import org.qedeq.kernel.se.base.module.Qedeq;
0060 import org.qedeq.kernel.se.base.module.Specification;
0061 import org.qedeq.kernel.se.common.DefaultModuleAddress;
0062 import org.qedeq.kernel.se.common.DefaultSourceFileExceptionList;
0063 import org.qedeq.kernel.se.common.LoadingState;
0064 import org.qedeq.kernel.se.common.ModuleAddress;
0065 import org.qedeq.kernel.se.common.ModuleDataException;
0066 import org.qedeq.kernel.se.common.Plugin;
0067 import org.qedeq.kernel.se.common.SourceFileException;
0068 import org.qedeq.kernel.se.common.SourceFileExceptionList;
0069 import org.qedeq.kernel.se.config.QedeqConfig;
0070 import org.qedeq.kernel.se.dto.module.QedeqVo;
0071 
0072 
0073 /**
0074  * This class provides a default implementation for the QEDEQ module services.
0075  *
0076  @author  Michael Meyling
0077  */
0078 public class DefaultInternalKernelServices implements ServiceModule, InternalKernelServices, Plugin {
0079 
0080     /** This class. */
0081     private static final Class CLASS = DefaultInternalKernelServices.class;
0082 
0083     /** For synchronized waiting. */
0084     private static final Object MONITOR = new Object();
0085 
0086     /** Number of method calls. */
0087     private volatile int processCounter = 0;
0088 
0089     /** Collection of already known QEDEQ modules. */
0090     private KernelQedeqBoStorage modules;
0091 
0092     /** Kernel properties access. */
0093     private final KernelProperties kernel;
0094 
0095     /** This instance nows how to load a module from the file system. */
0096     private final QedeqFileDao qedeqFileDao;
0097 
0098     /** This instance manages plugins. */
0099     private final PluginManager pluginManager;
0100 
0101     /** This instance manages service processes. */
0102     private final ServiceProcessManager processManager;
0103 
0104     /** Validate module dependencies and status. */
0105     private boolean validate = true;
0106 
0107     /**
0108      * Constructor.
0109      *
0110      @param kernel For kernel access.
0111      @param loader For loading QEDEQ modules.
0112      */
0113     public DefaultInternalKernelServices(final KernelProperties kernel, final QedeqFileDao loader) {
0114         this.kernel = kernel;
0115         this.qedeqFileDao = loader;
0116         processManager = new ServiceProcessManager();
0117         pluginManager = new PluginManager(processManager);
0118         pluginManager.addPlugin("org.qedeq.kernel.bo.service.unicode.Qedeq2UnicodeTextPlugin");
0119         pluginManager.addPlugin("org.qedeq.kernel.bo.service.latex.Qedeq2LatexPlugin");
0120         pluginManager.addPlugin("org.qedeq.kernel.bo.service.unicode.Qedeq2Utf8Plugin");
0121 //        pluginManager.addPlugin("org.qedeq.kernel.bo.service.heuristic.HeuristicCheckerPlugin");
0122         pluginManager.addPlugin(FormalProofCheckerPlugin.class.getName());
0123         pluginManager.addPlugin("org.qedeq.kernel.bo.service.heuristic.DynamicHeuristicCheckerPlugin");
0124         pluginManager.addPlugin(SimpleProofFinderPlugin.class.getName());
0125 
0126 //        pluginManager.addPlugin(MultiProofFinderPlugin.class.getName());
0127         loader.setServices(this);
0128     }
0129 
0130     public void startupServices() {
0131         modules = new KernelQedeqBoStorage();
0132         if (kernel.getConfig().isAutoReloadLastSessionChecked()) {
0133             autoReloadLastSessionChecked();
0134         }
0135     }
0136 
0137     public void shutdownServices() {
0138         processManager.terminateAndRemoveAllServiceProcesses();
0139         modules.removeAllModules();
0140         modules = null;
0141         // clear thread interrupt flag because we might have interrupted ourself
0142         Thread.interrupted();
0143     }
0144 
0145     /**
0146      * If configured load all QEDEQ modules that where successfully loaded the last time.
0147      */
0148     public void autoReloadLastSessionChecked() {
0149         if (kernel.getConfig().isAutoReloadLastSessionChecked()) {
0150             final Thread thread = new Thread() {
0151                 public void run() {
0152                     final String method = "autoReloadLastSessionChecked.thread.run()";
0153                     try {
0154                         Trace.begin(CLASS, this, method);
0155                         QedeqLog.getInstance().logMessage(
0156                             "Trying to load previously successfully loaded modules.");
0157                         final int number = kernel.getConfig().getPreviouslyCheckedModules().length;
0158                         if (loadPreviouslySuccessfullyLoadedModules()) {
0159                             QedeqLog.getInstance().logMessage(
0160                                 "Loading of " + number + " previously successfully loaded module"
0161                                     (number != "s" """ successfully done.");
0162                         else {
0163                             QedeqLog.getInstance().logMessage(
0164                                 "Loading of all previously successfully checked modules failed. "
0165                                     + number + " module" (number != "s" "")
0166                                     " were tried.");
0167                         }
0168                     catch (Exception e) {
0169                         Trace.trace(CLASS, this, method, e);
0170                     finally {
0171                         Trace.end(CLASS, this, method);
0172                     }
0173                 }
0174             };
0175             thread.setDaemon(true);
0176             thread.start();
0177         }
0178     }
0179 
0180     public void removeAllModules() {
0181         do {
0182             synchronized (this) {
0183                 if (processCounter == 0) {
0184                     getModules().removeAllModules();
0185                     return;
0186                 }
0187             }
0188             // we must wait for the other processes to stop (so that processCounter == 0)
0189             synchronized (MONITOR) {
0190                 try {
0191                     MONITOR.wait(10000);
0192                 catch (InterruptedException e) {
0193                 }
0194             }
0195         while (true);
0196 
0197     }
0198 
0199     /**
0200      * Remove a QEDEQ module from memory.
0201      *
0202      @param address Remove module identified by this address.
0203      */
0204     public void removeModule(final ModuleAddress address) {
0205         final QedeqBo prop = getQedeqBo(address);
0206         if (prop != null) {
0207             removeModule(getModules().getKernelQedeqBo(this, address));
0208             if (validate) {
0209                 modules.validateDependencies();
0210             }
0211         }
0212     }
0213 
0214     /**
0215      * Remove a QEDEQ module from memory. This method must block all other methods and if this
0216      * method runs no other is allowed to run
0217      *
0218      @param prop Remove module identified by this property.
0219      */
0220     private void removeModule(final DefaultKernelQedeqBo prop) {
0221         do {
0222             synchronized (this) {
0223                 if (processCounter == 0) { // no other method is allowed to run
0224                     // TODO mime 20080319: one could call prop.setLoadingProgressState(
0225                     // LoadingState.STATE_DELETED) alone but that would
0226                     // miss to inform the KernelQedeqBoPool. How do we inform the pool?
0227                     // must the StateManager have a reference to it?
0228                     prop.delete();
0229                     getModules().removeModule(prop);
0230                     return;
0231                 }
0232             }
0233             // we must wait for the other processes to stop (so that processCounter == 0)
0234             synchronized (MONITOR) {
0235                 try {
0236                     MONITOR.wait(10000);
0237                 catch (InterruptedException e) {
0238                 }
0239             }
0240         while (true);
0241 
0242     }
0243 
0244     /**
0245      * Clear local file buffer and all loaded QEDEQ modules.
0246      *
0247      @throws IOException Deletion of all buffered file was not successful.
0248      */
0249     public void clearLocalBuffer() throws IOException {
0250         removeAllModules();
0251         final File bufferDir = getBufferDirectory().getCanonicalFile();
0252         if (bufferDir.exists() && !IoUtility.deleteDir(bufferDir, new FileFilter() {
0253                     public boolean accept(final File pathname) {
0254                         return pathname.getName().endsWith(".xml");
0255                     }
0256                 })) {
0257             throw new IOException("buffer could not be deleted: " + bufferDir);
0258         }
0259     }
0260 
0261     public QedeqBo loadModule(final ModuleAddress address) {
0262         final String method = "loadModule(ModuleAddress)";
0263         processInc();
0264         final DefaultKernelQedeqBo prop = getModules().getKernelQedeqBo(this, address);
0265         try {
0266             synchronized (prop) {
0267                 if (prop.isLoaded()) {
0268                     return prop;
0269                 }
0270                 QedeqLog.getInstance().logRequest("Load module \"" + IoUtility.easyUrl(address.getUrl()) "\"");
0271                 if (prop.getModuleAddress().isFileAddress()) {
0272                     loadLocalModule(prop);
0273                 else {
0274                     // search in local file buffer
0275                     try {
0276                         getCanonicalReadableFile(prop);
0277                     catch (ModuleFileNotFoundException e) { // file not found
0278                         // we will continue by creating a local copy
0279                         saveQedeqFromWebToBuffer(prop);
0280                     }
0281                     loadBufferedModule(prop);
0282                 }
0283                 QedeqLog.getInstance().logSuccessfulReply(
0284                     "Module \"" + prop.getModuleAddress().getFileName()
0285                         "\" was successfully loaded.");
0286             }
0287         catch (SourceFileExceptionList e) {
0288             Trace.trace(CLASS, this, method, e);
0289             QedeqLog.getInstance().logFailureState("Loading of module failed!", IoUtility.easyUrl(address.getUrl()),
0290                 e.toString());
0291         catch (final RuntimeException e) {
0292             Trace.fatal(CLASS, this, method, "unexpected problem", e);
0293             QedeqLog.getInstance().logFailureReply("Loading failed", e.getMessage());
0294         finally {
0295             processDec();
0296         }
0297         return prop;
0298     }
0299 
0300     /**
0301      * Load buffered QEDEQ module file.
0302      *
0303      @param prop Load this.
0304      @throws SourceFileExceptionList Loading or QEDEQ module failed.
0305      */
0306     private void loadBufferedModule(final DefaultKernelQedeqBo prop)
0307             throws SourceFileExceptionList {
0308         prop.setLoadingProgressState(LoadingState.STATE_LOADING_FROM_BUFFER);
0309         final File localFile;
0310         try {
0311             localFile = getCanonicalReadableFile(prop);
0312         catch (ModuleFileNotFoundException e) {
0313             final SourceFileExceptionList sfl = createSourceFileExceptionList(
0314                 ServiceErrors.LOADING_FROM_FILE_BUFFER_FAILED_CODE,
0315                 ServiceErrors.LOADING_FROM_FILE_BUFFER_FAILED_TEXT,
0316                 prop.getUrl(), e);
0317             prop.setLoadingFailureState(LoadingState.STATE_LOADING_FROM_BUFFER_FAILED, sfl);
0318             throw sfl;
0319         }
0320 
0321         prop.setQedeqFileDao(getQedeqFileDao())// remember loader for this module
0322         final Qedeq qedeq;
0323         try {
0324             qedeq = getQedeqFileDao().loadQedeq(prop, localFile);
0325         catch (SourceFileExceptionList sfl) {
0326             prop.setLoadingFailureState(LoadingState.STATE_LOADING_FROM_BUFFER_FAILED, sfl);
0327             throw sfl;
0328         }
0329         setCopiedQedeq(prop, qedeq);
0330     }
0331 
0332     /**
0333      * Load QEDEQ module file with file loader.
0334      *
0335      @param prop Load this.
0336      @throws SourceFileExceptionList Loading or copying QEDEQ module failed.
0337      */
0338     private void loadLocalModule(final DefaultKernelQedeqBo propthrows SourceFileExceptionList {
0339         prop.setLoadingProgressState(LoadingState.STATE_LOADING_FROM_LOCAL_FILE);
0340         final File localFile;
0341         try {
0342             localFile = getCanonicalReadableFile(prop);
0343         catch (ModuleFileNotFoundException e) {
0344             final SourceFileExceptionList sfl = createSourceFileExceptionList(
0345                 ServiceErrors.LOADING_FROM_LOCAL_FILE_FAILED_CODE,
0346                 ServiceErrors.LOADING_FROM_LOCAL_FILE_FAILED_TEXT,
0347                 prop.getUrl(), e);
0348             prop.setLoadingFailureState(LoadingState.STATE_LOADING_FROM_LOCAL_FILE_FAILED, sfl);
0349             throw sfl;
0350         }
0351         prop.setQedeqFileDao(getQedeqFileDao())// remember loader for this module
0352 
0353         final Qedeq qedeq;
0354         try {
0355             qedeq = getQedeqFileDao().loadQedeq(prop, localFile);
0356         catch (SourceFileExceptionList sfl) {
0357             prop.setLoadingFailureState(LoadingState.STATE_LOADING_FROM_LOCAL_FILE_FAILED, sfl);
0358             throw sfl;
0359         }
0360         setCopiedQedeq(prop, qedeq);
0361     }
0362 
0363     private void setCopiedQedeq(final DefaultKernelQedeqBo prop, final Qedeq qedeq)
0364             throws SourceFileExceptionList {
0365         final String method = "setCopiedQedeq(DefaultKernelQedeqBo, Qedeq)";
0366         prop.setLoadingProgressState(LoadingState.STATE_LOADING_INTO_MEMORY);
0367         QedeqVo vo = null;
0368         try {
0369             vo = QedeqVoBuilder.createQedeq(prop.getModuleAddress(), qedeq);
0370         catch (RuntimeException e) {
0371             Trace.fatal(CLASS, this, method, "looks like a programming error", e);
0372             final SourceFileExceptionList xl = createSourceFileExceptionList(
0373                 ServiceErrors.RUNTIME_ERROR_CODE,
0374                 ServiceErrors.RUNTIME_ERROR_TEXT,
0375                 prop.getModuleAddress().getUrl(), e);
0376             prop.setLoadingFailureState(LoadingState.STATE_LOADING_INTO_MEMORY_FAILED, xl);
0377             throw xl;
0378         catch (ModuleDataException e) {
0379             if (e.getCause() != null) {
0380                 Trace.fatal(CLASS, this, method, "looks like a programming error", e.getCause());
0381             else {
0382                 Trace.fatal(CLASS, this, method, "looks like a programming error", e);
0383             }
0384             final SourceFileExceptionList xl = prop.createSourceFileExceptionList(this, e, qedeq);
0385             prop.setLoadingFailureState(LoadingState.STATE_LOADING_INTO_MEMORY_FAILED, xl);
0386             throw xl;
0387         }
0388         prop.setQedeqVo(vo);
0389         // TODO 20110213 m31: perhaps we need a new state, pre loaded? So when we put more
0390         // label testing into the moduleLabelCreator, we still can launch some plugins
0391         // On the other side: Label checking is only possible, if all referenced modules can
0392         // be loaded.
0393         //
0394         // Correct labels are necessary for many plugins (e.g. LaTeX and UTF-8 generation).
0395         // So a label checker must be run before that.
0396         // It might be a good idea to put it into the formal logic checker.
0397         // We could make a FormalChecker plugin. This starts loading required modules, checks
0398         // the labels and checks if the formulas are correctly written.
0399         // So we get some sub status (for every check) and an overall status (all checks
0400         // green). Later on the formal proof checker can be integrated too.
0401         // This should be the extended load status.
0402         final ModuleLabelsCreator moduleNodesCreator = new ModuleLabelsCreator(this, prop);
0403         try {
0404             moduleNodesCreator.createLabels();
0405             prop.setLoaded(vo, moduleNodesCreator.getLabels(), moduleNodesCreator.getConverter(),
0406                 moduleNodesCreator.getTextConverter());
0407         catch (SourceFileExceptionList sfl) {
0408             prop.setLoadingFailureState(LoadingState.STATE_LOADING_INTO_MEMORY_FAILED, sfl);
0409             throw sfl;
0410         }
0411     }
0412 
0413     /**
0414      * Check if file exists and is readable. Checks the local buffer file for a buffered module or
0415      * the module file address directly. Returns canonical file path.
0416      *
0417      @param prop Check for this file.
0418      @return Canonical file path.
0419      @throws ModuleFileNotFoundException File doesn't exist or is not readable.
0420      */
0421     private File getCanonicalReadableFile(final QedeqBo propthrows ModuleFileNotFoundException {
0422         final String method = "getCanonicalReadableFile(File)";
0423         final File localFile = getLocalFilePath(prop.getModuleAddress());
0424         final File file;
0425         try {
0426             file = localFile.getCanonicalFile();
0427         catch (IOException e) {
0428             Trace.trace(CLASS, this, method, e);
0429             throw new ModuleFileNotFoundException("file path not correct: " + localFile);
0430         }
0431         if (!file.canRead()) {
0432             Trace.trace(CLASS, this, method, "file not readable=" + file);
0433             throw new ModuleFileNotFoundException("file not readable: " + file);
0434         }
0435         return file;
0436     }
0437 
0438     /**
0439      * Load specified QEDEQ module from QEDEQ parent module.
0440      *
0441      @param parent Parent module address.
0442      @param spec Specification for another QEDEQ module.
0443      @return Loaded module.
0444      @throws SourceFileExceptionList Loading failed.
0445      */
0446     public KernelQedeqBo loadModule(final ModuleAddress parent, final Specification spec)
0447             throws SourceFileExceptionList {
0448 
0449         final String method = "loadModule(Module, Specification)";
0450         Trace.begin(CLASS, this, method);
0451         Trace.trace(CLASS, this, method, spec);
0452         processInc();
0453         DefaultKernelQedeqBo prop = null// currently tried module
0454         try {
0455             final ModuleAddress[] modulePaths;
0456             try {
0457                 modulePaths = parent.getModulePaths(spec);
0458             catch (IOException e) {
0459                 Trace.fatal(CLASS, this, method, "getting module path failed", e);  // TODO 20110308 m31: make constant
0460                 throw createSourceFileExceptionList(
0461                     ServiceErrors.LOADING_FROM_FILE_BUFFER_FAILED_CODE,
0462                     ServiceErrors.LOADING_FROM_FILE_BUFFER_FAILED_TEXT,
0463                     parent.getUrl(), e);
0464             }
0465 
0466             // now we iterate over the possible module addresses
0467             for (int i = 0; i < modulePaths.length; i++) {
0468                 prop = getModules().getKernelQedeqBo(this, modulePaths[i]);
0469                 Trace.trace(CLASS, this, method, "synchronizing at prop=" + prop);
0470                 synchronized (prop) {
0471                     if (prop.isLoaded()) {
0472                         return (prop);
0473                     }
0474                     try {
0475                         if (prop.getModuleAddress().isFileAddress()) {
0476                             loadLocalModule(prop);
0477                         else {
0478                             // search in local file buffer
0479                             try {
0480                                 getCanonicalReadableFile(prop);
0481                             catch (ModuleFileNotFoundException e) { // file not found
0482                                 // we will continue by creating a local copy
0483                                 saveQedeqFromWebToBuffer(prop);
0484                             }
0485                             loadBufferedModule(prop);
0486                         }
0487                         // success!
0488                         return prop;
0489                     catch (SourceFileExceptionList e) {
0490                         Trace.trace(CLASS, this, method, e);
0491                         if (i + < modulePaths.length) {
0492                             QedeqLog.getInstance().logMessage("trying alternate path");
0493                             // we continue with the next path
0494                         else {
0495                             // we surrender
0496                             throw e;
0497                         }
0498                     }
0499                 }
0500             }
0501             return prop; // never called, only here to soothe the compiler
0502         catch (final RuntimeException e) {
0503             Trace.fatal(CLASS, this, method, "unexpected problem", e);
0504             QedeqLog.getInstance().logFailureReply("Loading failed", e.getMessage());
0505             throw e;
0506         finally {
0507             processDec();
0508             Trace.end(CLASS, this, method);
0509         }
0510     }
0511 
0512     public ModuleAddress[] getAllLoadedModules() {
0513         return getModules().getAllLoadedModules();
0514     }
0515 
0516     public boolean loadRequiredModules(final ModuleAddress address) {
0517         final DefaultKernelQedeqBo prop = (DefaultKernelQedeqBoloadModule(address);
0518         if (prop.hasBasicFailures()) {
0519             return false;
0520         }
0521         return LoadRequiredModules.loadRequired(this, prop);
0522     }
0523 
0524     /**
0525      * Load all previously checked QEDEQ modules.
0526      *
0527      @return Successfully reloaded all modules.
0528      */
0529     public boolean loadPreviouslySuccessfullyLoadedModules() {
0530         processInc();
0531         try {
0532             final String[] list = kernel.getConfig().getPreviouslyCheckedModules();
0533             boolean errors = false;
0534             for (int i = 0; i < list.length; i++) {
0535                 try {
0536                     final ModuleAddress address = getModuleAddress(list[i]);
0537                     final QedeqBo prop = loadModule(address);
0538                     if (prop.hasErrors()) {
0539                         errors = true;
0540                     }
0541                 catch (IOException e) {
0542                     Trace.fatal(CLASS, this, "loadPreviouslySuccessfullyLoadedModules",
0543                         "internal error: " "saved URLs are malformed", e);
0544                     errors = true;
0545                 }
0546             }
0547             return !errors;
0548         finally {
0549             processDec();
0550         }
0551     }
0552 
0553     // LATER mime 20070326: dynamic loading from web page directory
0554     public boolean loadAllModulesFromQedeq() {
0555         processInc();
0556         try {
0557             final String prefix = "http://www.qedeq.org/" + kernel.getKernelVersionDirectory() "/";
0558             final String[] list = new String[] {
0559                 prefix + "doc/math/qedeq_logic_v1.xml",
0560                 prefix + "doc/math/qedeq_set_theory_v1.xml",
0561                 prefix + "doc/project/qedeq_basic_concept.xml",
0562                 prefix + "doc/project/qedeq_logic_language.xml",
0563                 prefix + "sample/qedeq_sample1.xml",
0564                 prefix + "sample/qedeq_sample2.xml",
0565                 prefix + "sample/qedeq_sample3.xml",
0566                 prefix + "sample/qedeq_sample4.xml",
0567                 prefix + "sample/qedeq_error_sample_00.xml",
0568                 prefix + "sample/qedeq_error_sample_01.xml",
0569                 prefix + "sample/qedeq_error_sample_02.xml",
0570                 prefix + "sample/qedeq_error_sample_03.xml",
0571                 prefix + "sample/qedeq_error_sample_04.xml",
0572                 prefix + "sample/qedeq_error_sample_05.xml",
0573                 prefix + "sample/qedeq_error_sample_12.xml",
0574                 prefix + "sample/qedeq_error_sample_13.xml",
0575                 prefix + "sample/qedeq_error_sample_14.xml",
0576                 prefix + "sample/qedeq_error_sample_15.xml",
0577                 prefix + "sample/qedeq_error_sample_16.xml",
0578                 prefix + "sample/qedeq_error_sample_17.xml",
0579                 prefix + "sample/qedeq_error_sample_18.xml"};
0580             boolean errors = false;
0581             for (int i = 0; i < list.length; i++) {
0582                 try {
0583                     final ModuleAddress address = getModuleAddress(list[i]);
0584                     final QedeqBo prop = loadModule(address);
0585                     if (prop.hasErrors()) {
0586                         errors = true;
0587                     }
0588                 catch (IOException e) {
0589                     Trace.fatal(CLASS, this, "loadPreviouslySuccessfullyLoadedModules",
0590                         "internal error: " "saved URLs are malformed", e);
0591                     errors = true;
0592                 }
0593             }
0594             return !errors;
0595         finally {
0596             processDec();
0597         }
0598     }
0599 
0600     /**
0601      * Make local copy of a module if it is no file address.
0602      *
0603      @param   prop    Module properties.
0604      @throws  SourceFileExceptionList Address was malformed or the file can not be found.
0605      */
0606     private void saveQedeqFromWebToBuffer(final DefaultKernelQedeqBo prop)
0607             throws SourceFileExceptionList {
0608         final String method = "saveQedeqFromWebToBuffer(DefaultKernelQedeqBo)";
0609         Trace.begin(CLASS, this, method);
0610 
0611         // if we are not web started and running under Java 1.4 we use apache commons
0612         // httpclient library (so we can set timeouts)
0613         if (!KernelContext.getInstance().isSetConnectionTimeOutSupported()
0614                 && !IoUtility.isWebStarted()) {
0615             saveQedeqFromWebToBufferApache(prop);
0616             Trace.end(CLASS, this, method);
0617             return;
0618         }
0619 
0620         // set proxy properties according to kernel configuration (if not webstarted)
0621         if (!IoUtility.isWebStarted()) {
0622             if (kernel.getConfig().getHttpProxyHost() != null) {
0623                 System.setProperty("http.proxyHost", kernel.getConfig().getHttpProxyHost());
0624             }
0625             if (kernel.getConfig().getHttpProxyPort() != null) {
0626                 System.setProperty("http.proxyPort", kernel.getConfig().getHttpProxyPort());
0627             }
0628             if (kernel.getConfig().getHttpNonProxyHosts() != null) {
0629                 System.setProperty("http.nonProxyHosts", kernel.getConfig().getHttpNonProxyHosts());
0630             }
0631         }
0632 
0633         if (prop.getModuleAddress().isFileAddress()) { // this is already a local file
0634             Trace.fatal(CLASS, this, method, "tried to make a local copy for a local module"null);
0635             Trace.end(CLASS, this, method);
0636             return;
0637         }
0638         prop.setLoadingProgressState(LoadingState.STATE_LOADING_FROM_WEB);
0639 
0640         FileOutputStream out = null;
0641         InputStream in = null;
0642         final File f = getLocalFilePath(prop.getModuleAddress());
0643         try {
0644             final URLConnection connection = new URL(prop.getUrl()).openConnection();
0645 
0646             if (connection instanceof HttpURLConnection) {
0647                 final HttpURLConnection httpConnection = (HttpURLConnectionconnection;
0648                 // if we are running at least under Java 1.5 the following code should be executed
0649                 if (KernelContext.getInstance().isSetConnectionTimeOutSupported()) {
0650                     try {
0651                         YodaUtility.executeMethod(httpConnection, "setConnectTimeout",
0652                             new Class[] {Integer.TYPE}new Object[] {new Integer(
0653                             kernel.getConfig().getConnectTimeout())});
0654                     catch (NoSuchMethodException e) {
0655                         Trace.fatal(CLASS, this, method,
0656                             "URLConnection.setConnectTimeout was previously found", e);
0657                     catch (InvocationTargetException e) {
0658                         Trace.fatal(CLASS, this, method,
0659                             "URLConnection.setConnectTimeout throwed an error", e);
0660                     }
0661                 }
0662                 // if we are running at least under Java 1.5 the following code should be executed
0663                 if (KernelContext.getInstance().isSetReadTimeoutSupported()) {
0664                     try {
0665                         YodaUtility.executeMethod(httpConnection, "setReadTimeout",
0666                             new Class[] {Integer.TYPE}new Object[] {new Integer(
0667                             kernel.getConfig().getReadTimeout())});
0668                     catch (NoSuchMethodException e) {
0669                         Trace.fatal(CLASS, this, method,
0670                             "URLConnection.setReadTimeout was previously found", e);
0671                     catch (InvocationTargetException e) {
0672                         Trace.fatal(CLASS, this, method,
0673                             "URLConnection.setReadTimeout throwed an error", e);
0674                     }
0675                 }
0676                 int responseCode = httpConnection.getResponseCode();
0677                 if (responseCode == 200) {
0678                     in = httpConnection.getInputStream();
0679                 else {
0680                     in = httpConnection.getErrorStream();
0681                     final String errorText = IoUtility.loadStreamWithoutException(in, 1000);
0682                     throw new IOException("Response code from HTTP server was " + responseCode
0683                         (errorText.length() "\nResponse  text from HTTP server was:\n"
0684                         + errorText : ""));
0685                 }
0686             else {
0687                 Trace.paramInfo(CLASS, this, method, "connection.getClass", connection.getClass()
0688                     .toString());
0689                 in = connection.getInputStream();
0690             }
0691 
0692             if (!prop.getUrl().equals(connection.getURL().toString())) {
0693                 throw new FileNotFoundException("\"" + prop.getUrl() "\" was substituted by "
0694                     "\"" + connection.getURL() "\" from server");
0695             }
0696             final int maximum = connection.getContentLength();
0697             IoUtility.createNecessaryDirectories(f);
0698             out = new FileOutputStream(f);
0699             final byte[] buffer = new byte[4096];
0700             int bytesRead; // bytes read during one buffer read
0701             int position = 0// current reading position within the whole document
0702             // continue writing
0703             while ((bytesRead = in.read(buffer)) != -1) {
0704                 position += bytesRead;
0705                 out.write(buffer, 0, bytesRead);
0706                 if (maximum > 0) {
0707                     long completeness = (long) (position * 100 / maximum);
0708                     if (completeness < 0) {
0709                         completeness = 0;
0710                     }
0711                     if (completeness > 100) {
0712                         completeness = 100;
0713                     }
0714                     prop.setLoadingCompleteness((intcompleteness);
0715                 }
0716             }
0717             prop.setLoadingCompleteness(100);
0718         catch (IOException e) {
0719             Trace.trace(CLASS, this, method, e);
0720             IoUtility.close(out);
0721             out = null;
0722             try {
0723                 f.delete();
0724             catch (Exception ex) {
0725                 Trace.trace(CLASS, this, method, ex);
0726             }
0727             final SourceFileExceptionList sfl = createSourceFileExceptionList(
0728                 ServiceErrors.LOADING_FROM_WEB_FAILED_CODE,
0729                 ServiceErrors.LOADING_FROM_WEB_FAILED_TEXT,
0730                 prop.getUrl(), e);
0731             prop.setLoadingFailureState(LoadingState.STATE_LOADING_FROM_WEB_FAILED, sfl);
0732             Trace.trace(CLASS, this, method, "Couldn't access " + prop.getUrl());
0733             throw sfl;
0734         finally {
0735             IoUtility.close(out);
0736             IoUtility.close(in);
0737             Trace.end(CLASS, this, method);
0738         }
0739     }
0740 
0741     /**
0742      * Make local copy of a http accessable module if it is no file address.
0743      * This method uses apaches HttpClient, but it dosn't work under webstart with proxy
0744      * configuration. If we don't use this method, the apache commons-httpclient
0745      * library can be removed
0746      *
0747      @param   prop    Module properties.
0748      @throws  SourceFileExceptionList Address was malformed or the file can not be found.
0749      */
0750     private void saveQedeqFromWebToBufferApache(final DefaultKernelQedeqBo prop)
0751             throws SourceFileExceptionList {
0752         final String method = "saveQedeqFromWebToBufferOld(DefaultKernelQedeqBo)";
0753         Trace.begin(CLASS, this, method);
0754 
0755         if (prop.getModuleAddress().isFileAddress()) { // this is already a local file
0756             Trace.fatal(CLASS, this, method, "tried to make a local copy for a local module"null);
0757             Trace.end(CLASS, this, method);
0758             return;
0759         }
0760         prop.setLoadingProgressState(LoadingState.STATE_LOADING_FROM_WEB);
0761 
0762         final File f = getLocalFilePath(prop.getModuleAddress());
0763         // Create an instance of HttpClient.
0764         HttpClient client = new HttpClient();
0765 
0766         // set proxy properties according to kernel configuration (if not webstarted)
0767         if (!IoUtility.isWebStarted() && kernel.getConfig().getHttpProxyHost() != null) {
0768             final String pHost = kernel.getConfig().getHttpProxyHost();
0769             int pPort = 80;
0770             if (kernel.getConfig().getHttpProxyPort() != null) {
0771                 try {
0772                     pPort = Integer.parseInt(kernel.getConfig().getHttpProxyPort());
0773                 catch (RuntimeException e) {
0774                     Trace.fatal(CLASS, this, method, "proxy port not numeric: "
0775                         + kernel.getConfig().getHttpProxyPort(), e);
0776                 }
0777             }
0778             if (pHost.length() 0) {
0779                 client.getHostConfiguration().setProxy(pHost, pPort);
0780             }
0781         }
0782 
0783         // Create a method instance.
0784         GetMethod httpMethod = new GetMethod(prop.getUrl());
0785 
0786         try {
0787             // Provide custom retry handler is necessary
0788             httpMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
0789                     new DefaultHttpMethodRetryHandler(3false));
0790 
0791             httpMethod.getParams().setSoTimeout(kernel.getConfig().getConnectTimeout());
0792             // Throws IOException on TimeOut.
0793 
0794             int statusCode = client.executeMethod(httpMethod);
0795 
0796             if (statusCode != HttpStatus.SC_OK) {
0797                 throw new FileNotFoundException("Problems loading: " + prop.getUrl() "\n"
0798                     + httpMethod.getStatusLine());
0799             }
0800 
0801             // Read the response body.
0802             byte[] responseBody = httpMethod.getResponseBody();
0803             IoUtility.createNecessaryDirectories(f);
0804             IoUtility.saveFileBinary(f, responseBody);
0805             prop.setLoadingCompleteness(100);
0806         catch (IOException e) {
0807             Trace.trace(CLASS, this, method, e);
0808             try {
0809                 f.delete();
0810             catch (Exception ex) {
0811                 Trace.trace(CLASS, this, method, ex);
0812             }
0813             final SourceFileExceptionList sfl = createSourceFileExceptionList(
0814                 ServiceErrors.LOADING_FROM_WEB_FAILED_CODE,
0815                 ServiceErrors.LOADING_FROM_WEB_FAILED_TEXT,
0816                 prop.getUrl(), e);
0817             prop.setLoadingFailureState(LoadingState.STATE_LOADING_FROM_WEB_FAILED, sfl);
0818             Trace.trace(CLASS, this, method, "Couldn't access " + prop.getUrl());
0819             throw sfl;
0820         finally {
0821             // Release the connection.
0822             httpMethod.releaseConnection();
0823             Trace.end(CLASS, this, method);
0824         }
0825     }
0826 
0827     public final File getLocalFilePath(final ModuleAddress address) {
0828         final String method = "getLocalFilePath(ModuleAddress)";
0829         URL url;
0830         try {
0831             url = new URL(address.getUrl());
0832         catch (MalformedURLException e) {
0833             Trace.fatal(CLASS, this, method, "Could not get local file path.", e);
0834             return null;
0835         }
0836         Trace.param(CLASS, this, method, "protocol", url.getProtocol());
0837         Trace.param(CLASS, this, method, "host", url.getHost());
0838         Trace.param(CLASS, this, method, "port", url.getPort());
0839         Trace.param(CLASS, this, method, "path", url.getPath());
0840         Trace.param(CLASS, this, method, "file", url.getFile());
0841         if (address.isFileAddress()) {
0842             try {
0843                 return IoUtility.toFile(url.getFile());
0844             catch (IllegalArgumentException e) {
0845                 // should not occur because check for validy must be done in constructor of address
0846                 Trace.fatal(CLASS, this, method, "Loading failed of local file with URL=" + url, e);
0847                 throw new RuntimeException(e);
0848             }
0849         }
0850         StringBuffer file = new StringBuffer(url.getFile());
0851         StringUtility.replace(file, "_""_1")// remember all '_'
0852         StringUtility.replace(file, "/""_2")// preserve all '/'
0853         String encoded = file.toString()// fallback file name
0854         try {
0855             encoded = URLEncoder.encode(file.toString()"UTF-8");
0856         catch (UnsupportedEncodingException e) {
0857             // should not occur
0858             Trace.trace(CLASS, method, e);
0859         }
0860         file.setLength(0);
0861         file.append(encoded);
0862         StringUtility.replace(file, "#""##")// escape all '#'
0863         StringUtility.replace(file, "_2""#")// from '/' into '#'
0864         StringUtility.replace(file, "_1""_")// from '_' into '_'
0865         // mime 2010-06-25: use if we throw no RuntimException
0866         // StringBuffer adr = new StringBuffer(url.toExternalForm());
0867         final StringBuffer adr;
0868         try {
0869             adr = new StringBuffer(new URL(url.getProtocol(), url.getHost(), url.getPort(), file
0870                 .toString()).toExternalForm());
0871         catch (MalformedURLException e) {
0872             Trace.fatal(CLASS, this, method, "unexpected", e);
0873             throw new RuntimeException(e);
0874         }
0875         // escape characters:
0876         StringUtility.replace(adr, "://""_")// before host
0877         StringUtility.replace(adr, ":""_")// before protocol
0878         return new File(getBufferDirectory(), adr.toString());
0879     }
0880 
0881     /**
0882      * Increment intern process counter.
0883      */
0884     private synchronized void processInc() {
0885         this.processCounter++;
0886     }
0887 
0888     /**
0889      * Decrement intern process counter.
0890      */
0891     private synchronized void processDec() {
0892         this.processCounter--;
0893     }
0894 
0895     public File getBufferDirectory() {
0896         return kernel.getConfig().getBufferDirectory();
0897     }
0898 
0899     public File getGenerationDirectory() {
0900         return kernel.getConfig().getGenerationDirectory();
0901     }
0902 
0903     public KernelQedeqBo getKernelQedeqBo(final ModuleAddress address) {
0904         return getModules().getKernelQedeqBo(this, address);
0905     }
0906 
0907     public QedeqBo getQedeqBo(final ModuleAddress address) {
0908         return getModules().getKernelQedeqBo(this, address);
0909     }
0910 
0911     public ModuleAddress getModuleAddress(final URL urlthrows IOException {
0912         return new DefaultModuleAddress(url);
0913     }
0914 
0915     public ModuleAddress getModuleAddress(final String urlthrows IOException {
0916         return new DefaultModuleAddress(url);
0917     }
0918 
0919     public ModuleAddress getModuleAddress(final File filethrows IOException {
0920         return new DefaultModuleAddress(file);
0921     }
0922 
0923     public String getSource(final ModuleAddress addressthrows IOException {
0924         final KernelQedeqBo bo = getKernelQedeqBo(address);
0925         if (bo.getLoadingState().equals(LoadingState.STATE_UNDEFINED)
0926             || bo.getLoadingState().equals(LoadingState.STATE_LOADING_FROM_WEB)
0927             || bo.getLoadingState().equals(LoadingState.STATE_LOADING_FROM_WEB_FAILED)) {
0928             return null;
0929         }
0930         final StringBuffer buffer = new StringBuffer();
0931         final Reader reader = getQedeqFileDao().getModuleReader(bo);
0932         try {
0933             IoUtility.loadReader(reader, buffer);
0934         finally {
0935             IoUtility.close(reader);
0936         }
0937         return buffer.toString();
0938     }
0939 
0940     public boolean checkModule(final ModuleAddress address) {
0941         final String method = "checkModule(ModuleAddress)";
0942         final DefaultKernelQedeqBo prop = modules.getKernelQedeqBo(this, address);
0943         // did we check this already?
0944         if (prop.isChecked()) {
0945             return true// everything is OK
0946         }
0947         try {
0948             final WellFormedCheckerPlugin checker = new WellFormedCheckerPlugin();
0949             checker.createExecutor(prop, null).executePlugin();
0950         catch (final RuntimeException e) {
0951             final String msg = "Check of logical correctness failed for \"" + IoUtility.easyUrl(address.getUrl())
0952                 "\"";
0953             Trace.fatal(CLASS, this, method, msg, e);
0954             QedeqLog.getInstance().logFailureReply(msg, e.getMessage());
0955             throw e;
0956         finally {
0957             if (validate) {
0958                 modules.validateDependencies();
0959             }
0960         }
0961         return prop.isChecked();
0962     }
0963 
0964     /**
0965      * Add plugin to services.
0966      *
0967      @param   pluginClass Plugin class to instantiate.
0968      @throws  RuntimeException    Addition failed.
0969      */
0970     public void addPlugin(final String pluginClass) {
0971         pluginManager.addPlugin(pluginClass);
0972     }
0973 
0974     public Plugin[] getPlugins() {
0975         return pluginManager.getPlugins();
0976     }
0977 
0978     public Object executePlugin(final String id, final ModuleAddress address,
0979             final Map parameters) {
0980         return pluginManager.executePlugin(id, getKernelQedeqBo(address), parameters);
0981     }
0982 
0983     public void clearAllPluginResults(final ModuleAddress address) {
0984         pluginManager.clearAllPluginResults(getKernelQedeqBo(address));
0985     }
0986 
0987     public ServiceProcess[] getServiceProcesses() {
0988         return processManager.getServiceProcesses();
0989     }
0990 
0991     /**
0992      * Get all loaded QEDEQ modules.
0993      *
0994      @return All QEDEQ modules.
0995      */
0996     private KernelQedeqBoStorage getModules() {
0997         return modules;
0998     }
0999 
1000     public SourceFileExceptionList createSourceFileExceptionList(final int code,
1001             final String message, final String address, final IOException e) {
1002         return new DefaultSourceFileExceptionList(new SourceFileException(this,
1003             code, message, e, new SourceArea(address)null));
1004     }
1005 
1006     public SourceFileExceptionList createSourceFileExceptionList(final int code,
1007             final String message, final String address, final RuntimeException e) {
1008         return new DefaultSourceFileExceptionList(new SourceFileException(this,
1009             code, message, e, new SourceArea(address)null));
1010     }
1011 
1012     public SourceFileExceptionList createSourceFileExceptionList(final int code,
1013             final String message, final String address, final Exception e) {
1014         return new DefaultSourceFileExceptionList(new SourceFileException(this,
1015             code, message, e, new SourceArea(address)null));
1016     }
1017 
1018     /**
1019      * Get description of source file exception list.
1020      *
1021      @param address Get description for this module exceptions.
1022      @return Error description and location.
1023      */
1024     public String[] getSourceFileExceptionList(final ModuleAddress address) {
1025         final List list = new ArrayList();
1026         final KernelQedeqBo bo = getKernelQedeqBo(address);
1027         final SourceFileExceptionList sfl = bo.getErrors();
1028         if (sfl.size() 0) {
1029             final StringBuffer buffer = new StringBuffer();
1030             do {
1031                 Reader reader = null;
1032                 try {
1033                     reader = getQedeqFileDao().getModuleReader(bo);
1034                     IoUtility.loadReader(reader, buffer);
1035                 catch (IOException e) {
1036                     IoUtility.close(reader);
1037                     for (int i = 0; i < sfl.size(); i++) {
1038                         list.add(sfl.get(i).getDescription());
1039                     }
1040                     break// out of do while
1041                 }
1042                 final TextInput input = new TextInput(buffer);
1043                 try {
1044                     input.setPosition(0);
1045                     final StringBuffer buf = new StringBuffer();
1046                     for (int i = 0; i < sfl.size(); i++) {
1047                         buf.setLength(0);
1048                         final SourceFileException sf = sfl.get(i);
1049                         buf.append(sf.getDescription());
1050                         try {
1051                             if (sf.getSourceArea() != null
1052                                 && sf.getSourceArea().getStartPosition() != null) {
1053                                 buf.append("\n");
1054                                 input.setRow(sf.getSourceArea().getStartPosition().getRow());
1055                                 buf.append(StringUtility.replace(input.getLine()"\t"" "));
1056                                 buf.append("\n");
1057                                 final StringBuffer whitespace = StringUtility.getSpaces(sf
1058                                     .getSourceArea().getStartPosition().getColumn() 1);
1059                                 buffer.append(whitespace);
1060                                 buffer.append("^");
1061                             }
1062                         catch (Exception e) {
1063                             Trace.trace(CLASS, this, "getSourceFileExceptionList(ModuleAddress)", e);
1064                         }
1065                         list.add(buf.toString());
1066                     }
1067                 finally {
1068                     IoUtility.close(input);
1069                 }
1070                 break// out of do while
1071             while (true);
1072         }
1073         return (String[]) list.toArray(new String[list.size()]);
1074     }
1075 
1076     public String getPluginId() {
1077         return CLASS.getName();
1078     }
1079 
1080     public String getPluginActionName() {
1081         return "Basis";
1082     }
1083     public QedeqFileDao getQedeqFileDao() {
1084         return qedeqFileDao;
1085     }
1086 
1087     public String getPluginDescription() {
1088         return "provides basic services for loading QEDEQ modules";
1089     }
1090 
1091     public QedeqConfig getConfig() {
1092         return kernel.getConfig();
1093     }
1094 
1095     public String getKernelVersionDirectory() {
1096         return kernel.getKernelVersionDirectory();
1097     }
1098 
1099     public String getBuildId() {
1100         return kernel.getBuildId();
1101     }
1102 
1103     public String getDedication() {
1104         return kernel.getDedication();
1105     }
1106 
1107     public String getDescriptiveKernelVersion() {
1108         return kernel.getDescriptiveKernelVersion();
1109     }
1110 
1111     public String getKernelCodeName() {
1112         return kernel.getKernelCodeName();
1113     }
1114 
1115     public String getKernelVersion() {
1116         return kernel.getKernelVersion();
1117     }
1118 
1119     public String getMaximalRuleVersion() {
1120         return kernel.getMaximalRuleVersion();
1121     }
1122 
1123     public boolean isRuleVersionSupported(final String ruleVersion) {
1124         return kernel.isRuleVersionSupported(ruleVersion);
1125     }
1126 
1127     public boolean isSetConnectionTimeOutSupported() {
1128         return kernel.isSetConnectionTimeOutSupported();
1129     }
1130 
1131     public boolean isSetReadTimeoutSupported() {
1132         return kernel.isSetReadTimeoutSupported();
1133     }
1134 
1135 }