View Javadoc

1   /* This file is part of the project "Hilbert II" - http://www.qedeq.org" target="alexandria_uri">http://www.qedeq.org
2    *
3    * Copyright 2000-2014,  Michael Meyling <mime@qedeq.org>.
4    *
5    * "Hilbert II" is free software; you can redistribute
6    * it and/or modify it under the terms of the GNU General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU General Public License for more details.
14   */
15  
16  package org.qedeq.kernel.bo.service.dependency;
17  
18  import java.util.Stack;
19  
20  import org.qedeq.base.io.Parameters;
21  import org.qedeq.base.trace.Trace;
22  import org.qedeq.base.utility.StringUtility;
23  import org.qedeq.kernel.bo.common.ModuleReferenceList;
24  import org.qedeq.kernel.bo.log.QedeqLog;
25  import org.qedeq.kernel.bo.module.InternalModuleServiceCall;
26  import org.qedeq.kernel.bo.module.KernelModuleReferenceList;
27  import org.qedeq.kernel.bo.module.KernelQedeqBo;
28  import org.qedeq.kernel.bo.service.basis.ControlVisitor;
29  import org.qedeq.kernel.bo.service.basis.ModuleServicePluginExecutor;
30  import org.qedeq.kernel.se.common.ModuleDataException;
31  import org.qedeq.kernel.se.common.ModuleService;
32  import org.qedeq.kernel.se.common.SourceFileException;
33  import org.qedeq.kernel.se.common.SourceFileExceptionList;
34  import org.qedeq.kernel.se.state.DependencyState;
35  import org.qedeq.kernel.se.visitor.InterruptException;
36  
37  
38  /**
39   * Load all required QEDEQ modules.
40   *
41   * @author  Michael Meyling
42   */
43  public final class LoadRequiredModulesExecutor extends ControlVisitor implements ModuleServicePluginExecutor {
44  
45      /** This class. */
46      private static final Class CLASS = LoadRequiredModulesExecutor.class;
47  
48      /** Percentage between 0 and 100. */
49      private double percentage;
50  
51      /**
52       * Constructor.
53       *
54       * @param   plugin      Plugin we work for.
55       * @param   prop        Internal QedeqBo.
56       * @param   parameters  Currently ignored.
57       */
58      LoadRequiredModulesExecutor(final ModuleService plugin, final KernelQedeqBo prop,
59              final Parameters parameters) {
60          super(plugin, prop);
61      }
62  
63      public Object executePlugin(final InternalModuleServiceCall call, final Object data) throws InterruptException {
64          percentage = 0;
65          final String method = "executePlugin";
66          if (getKernelQedeqBo().hasLoadedRequiredModules()) {
67              percentage = 100;
68              return Boolean.TRUE; // everything is OK
69          }
70          QedeqLog.getInstance().logRequest(
71              "Loading required modules", getKernelQedeqBo().getUrl());
72          // all QedeqBos currently in state "loading required modules"
73  
74          if (!loadAllRequiredModules(call, getKernelQedeqBo(), true)) {
75              final String msg = "Loading required modules failed";
76              QedeqLog.getInstance().logFailureReply(msg, getKernelQedeqBo().getUrl(),
77                  "Not all required modules could not even be loaded.");
78              return Boolean.FALSE;
79          }
80          percentage = 100;
81          Trace.trace(CLASS, this, method, "loading required modules of " + getKernelQedeqBo().getUrl());
82          getKernelQedeqBo().setDependencyProgressState(DependencyState.STATE_LOADING_REQUIRED_MODULES);
83  
84  
85          final SourceFileExceptionList sfl = new SourceFileExceptionList();
86          if (circlesInRequiredModules(call, getKernelQedeqBo(), sfl)) {
87              final String msg = "Loading required modules failed";
88              QedeqLog.getInstance().logFailureReply(msg, getKernelQedeqBo().getUrl(),
89                  "There were circular dependencies.");
90              return Boolean.FALSE;
91          }
92  
93          if (getKernelQedeqBo().getDependencyState().areAllRequiredLoaded()) {
94              return Boolean.TRUE; // everything is OK, someone else's thread might have corrected errors!
95          }
96  
97          getKernelQedeqBo().getLabels().setModuleReferences(getKernelQedeqBo().getRequiredModules()); // FIXME why here?
98          if (!getKernelQedeqBo().hasBasicFailures() && sfl.size() == 0) {
99              getKernelQedeqBo().setLoadedRequiredModules();
100             QedeqLog.getInstance().logSuccessfulReply(
101                 "Loading required modules successful", getKernelQedeqBo().getUrl());
102             return Boolean.TRUE;
103         }
104         if (sfl.size() != 0) {
105             getKernelQedeqBo().setDependencyFailureState(
106                 DependencyState.STATE_LOADING_REQUIRED_MODULES_FAILED, sfl);
107         } else {
108             getKernelQedeqBo().setDependencyFailureState(
109                 DependencyState.STATE_LOADING_REQUIRED_MODULES_FAILED, getKernelQedeqBo().getErrors());
110         }
111         final String msg = "Loading required modules failed";
112         QedeqLog.getInstance().logFailureReply(msg, getKernelQedeqBo().getUrl(),
113              StringUtility.replace(getKernelQedeqBo().getErrors().getMessage(), "\n", "\n\t"));
114         return  Boolean.FALSE;
115     }
116 
117     private boolean circlesInRequiredModules(final InternalModuleServiceCall call, final KernelQedeqBo bo,
118             final SourceFileExceptionList sfl) {
119         if (bo.hasLoadedRequiredModules()) {
120             return false;
121         }
122         Stack loadingRequiredInProgress = new Stack();
123         Stack labels = new Stack();
124 //        System.out.println("->checking " + bo.getName());
125         loadingRequiredInProgress.push(bo);
126         final KernelModuleReferenceList required = bo.getKernelRequiredModules();
127         final StringBuffer error = new StringBuffer();
128         for (int i = 0; i < required.size(); i++) {
129             final KernelQedeqBo current = required.getKernelQedeqBo(i);
130 //            System.out.println("-->testing " + current.getName());
131             labels.push(required.getLabel(i));
132             if (loadingRequiredInProgress.contains(current)) {
133 //                for (int j = 0; j < loadingRequiredInProgress.size(); j++) {
134 //                    System.out.print("-> " + labels.get(j).toString());
135 //                }
136                 ModuleDataException me = new LoadRequiredModuleException(
137                     DependencyErrors.RECURSIVE_IMPORT_OF_MODULES_IS_FORBIDDEN_CODE,
138                     DependencyErrors.RECURSIVE_IMPORT_OF_MODULES_IS_FORBIDDEN_TEXT + "\""
139                     + required.getLabel(i) + "\"",
140                     required.getModuleContext(i));
141                 sfl.add(createError(me));
142 //                me.printStackTrace(System.out);
143                 labels.pop();
144                 continue;
145             }
146 
147 //            System.out.println("->removing " + bo.getName());
148 //            loadingRequiredInProgress.remove(bo);
149             error.setLength(0);
150             if (!noCirclesInRequiredModules(call, required.getKernelQedeqBo(i), loadingRequiredInProgress, labels,
151                     error)) {
152                 // LATER 20110119 m31: we take only the first error, is that ok?
153                 String text = DependencyErrors.RECURSIVE_IMPORT_OF_MODULES_IS_FORBIDDEN_TEXT + error.toString();
154                 ModuleDataException me = new LoadRequiredModuleException(
155                     DependencyErrors.RECURSIVE_IMPORT_OF_MODULES_IS_FORBIDDEN_CODE,
156                     text, required.getModuleContext(i));
157                 sfl.add(createError(me));
158             }
159             labels.pop();
160         }
161 //        System.out.println("->removing " + bo.getName());
162         loadingRequiredInProgress.pop();
163         if (sfl.size() > 0) {
164             bo.setDependencyFailureState(DependencyState.STATE_LOADING_REQUIRED_MODULES_FAILED, sfl);
165             return true;
166         }
167         return false;
168     }
169 
170     private boolean noCirclesInRequiredModules(final InternalModuleServiceCall call, final KernelQedeqBo bo,
171             final Stack loadingRequiredInProgress, final Stack labels, final StringBuffer error) {
172         if (!bo.hasLoadedImports()) {
173             return false;
174         }
175 //        System.out.println("->checking " + bo.getName());
176         loadingRequiredInProgress.push(bo);
177         final KernelModuleReferenceList required = bo.getKernelRequiredModules();
178         boolean result = true;
179         for (int i = 0; i < required.size(); i++) {
180             final KernelQedeqBo current = required.getKernelQedeqBo(i);
181 //            System.out.println("-->testing " + current.getName() + " (" + required.getLabel(i) + ")");
182             labels.push(required.getLabel(i));
183             if (loadingRequiredInProgress.contains(current)) {
184                 for (int j = 0; j < loadingRequiredInProgress.size(); j++) {
185                     if (j > 0) {
186                         error.append(" -> ");
187                     }
188                     error.append("\"" + labels.get(j).toString() + "\"");
189                 }
190                 result = false;
191 //                System.out.println("## " + error);
192                 labels.pop();
193                 break;
194             }
195 
196             if (!noCirclesInRequiredModules(call, required.getKernelQedeqBo(i), loadingRequiredInProgress, labels,
197                     error)) {
198                 result = false;
199 //                System.out.println("## " + error);
200                 labels.pop();
201                 break;
202             }
203             labels.pop();
204         }
205 //        System.out.println("->removing " + bo.getName());
206         loadingRequiredInProgress.pop();
207         return result;
208     }
209 
210     private boolean loadAllRequiredModules(final InternalModuleServiceCall call, final KernelQedeqBo bo,
211             final boolean first) throws InterruptException {
212         if (bo.hasLoadedImports()) {
213             return true;
214         }
215         getServices().executePlugin(call.getInternalServiceProcess(),
216             LoadDirectlyRequiredModulesPlugin.class.getName(), bo, null);
217         if (!bo.hasLoadedImports()) {
218             return false;
219         }
220         final ModuleReferenceList imports = bo.getRequiredModules();
221         final SourceFileExceptionList sfl = new SourceFileExceptionList();
222         boolean result = true;
223         for (int i = 0; i < imports.size(); i++) {
224             if (!imports.getQedeqBo(i).hasLoadedImports()) {
225                 if (!loadAllRequiredModules(call, (KernelQedeqBo) imports.getQedeqBo(i), false)) {
226                     result = false;
227                     if (first) {
228                         // LATER 20110119 m31: we take only the first error, is that ok?
229                         String text = DependencyErrors.IMPORT_OF_MODULE_FAILED_TEXT + "\""
230                             + imports.getLabel(i) + "\"";
231                         if (imports.getQedeqBo(i).getErrors().size() > 0) {
232                             text += ", " + imports.getQedeqBo(i).getErrors().get(0).getMessage();
233                         }
234                         ModuleDataException me = new LoadRequiredModuleException(
235                             DependencyErrors.IMPORT_OF_MODULE_FAILED_CODE,
236                             text, imports.getModuleContext(i));
237                         sfl.add(createError(me));
238                     }
239                 }
240             }
241         }
242         if (sfl.size() > 0) {
243             bo.setDependencyFailureState(DependencyState.STATE_LOADING_REQUIRED_MODULES_FAILED, sfl);
244         }
245         return result;
246     }
247 
248     public double getVisitPercentage() {
249         return percentage;
250     }
251 
252     public boolean getInterrupted() {
253         return false;
254     }
255 
256     public String getLocationDescription() {
257         return super.getLocationDescription();
258     }
259 
260 
261     /**
262      * Prepare exception for error collection.
263      *
264      * @param   me  Basis exception.
265      * @return  Error.
266      */
267     private SourceFileException createError(final ModuleDataException me) {
268         return getKernelQedeqBo().createSourceFileException(getService(), me);
269     }
270 
271 }