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