1 | /* This file is part of the project "Hilbert II" - 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 | } |