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 != 1 ? "s" : "") + " successfully done.");
0162 } else {
0163 QedeqLog.getInstance().logMessage(
0164 "Loading of all previously successfully checked modules failed. "
0165 + number + " module" + (number != 1 ? "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 prop) throws 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 prop) throws 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 + 1 < 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 = (DefaultKernelQedeqBo) loadModule(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 = (HttpURLConnection) connection;
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() > 0 ? "\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((int) completeness);
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(3, false));
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 url) throws IOException {
0912 return new DefaultModuleAddress(url);
0913 }
0914
0915 public ModuleAddress getModuleAddress(final String url) throws IOException {
0916 return new DefaultModuleAddress(url);
0917 }
0918
0919 public ModuleAddress getModuleAddress(final File file) throws IOException {
0920 return new DefaultModuleAddress(file);
0921 }
0922
0923 public String getSource(final ModuleAddress address) throws 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 }
|