001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002 *
003 * Copyright 2000-2014, 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.internal;
017
018 import java.util.ArrayList;
019 import java.util.List;
020
021 import org.qedeq.base.io.Parameters;
022 import org.qedeq.base.trace.Trace;
023 import org.qedeq.kernel.bo.KernelContext;
024 import org.qedeq.kernel.bo.common.ModuleServiceResult;
025 import org.qedeq.kernel.bo.common.QedeqBo;
026 import org.qedeq.kernel.bo.common.ServiceJob;
027 import org.qedeq.kernel.bo.job.InternalModuleServiceCallImpl;
028 import org.qedeq.kernel.bo.job.InternalServiceJobImpl;
029 import org.qedeq.kernel.bo.log.QedeqLog;
030 import org.qedeq.kernel.bo.module.InternalModuleServiceCall;
031 import org.qedeq.kernel.bo.module.InternalServiceJob;
032 import org.qedeq.kernel.bo.module.KernelQedeqBo;
033 import org.qedeq.kernel.bo.module.ModuleArbiter;
034 import org.qedeq.kernel.bo.service.basis.ModuleServiceExecutor;
035 import org.qedeq.kernel.bo.service.basis.ModuleServicePlugin;
036 import org.qedeq.kernel.bo.service.basis.ModuleServicePluginExecutor;
037 import org.qedeq.kernel.se.common.ModuleService;
038 import org.qedeq.kernel.se.common.Service;
039 import org.qedeq.kernel.se.visitor.InterruptException;
040
041 /**
042 * Manage all known processes.
043 */
044 public class ServiceProcessManager {
045
046 /** This class. */
047 private static final Class CLASS = ServiceProcessManager.class;
048
049 /** Stores all running processes. */
050 private final List processes = new ArrayList();
051
052 /** Stores some finished processes. FIXME 20130408 m31: use! */
053 private final List finished = new ArrayList();
054
055 /** Stores all calls. */
056 private final List calls = new ArrayList();
057
058 /** Manage all known plugins. */
059 private final PluginManager pluginManager;
060
061 /** Manage synchronized module access. */
062 private final ModuleArbiter arbiter;
063
064 /**
065 * Constructor.
066 *
067 * @param pluginManager Collects process information.
068 * @param arbiter For module access synchronization.
069 */
070 public ServiceProcessManager(final PluginManager pluginManager, final ModuleArbiter arbiter) {
071 this.pluginManager = pluginManager;
072 this.arbiter = arbiter;
073 }
074
075
076 /**
077 * Get all service processes.
078 *
079 * @return All service processes.
080 */
081 public synchronized ServiceJob[] getServiceProcesses() {
082 return (ServiceJob[]) processes.toArray(new ServiceJob[] {});
083 }
084
085 /**
086 * Get all running service processes. But remember a running process might currently
087 * be blocked.
088 *
089 * @return All service running processes.
090 */
091 public synchronized ServiceJob[] getRunningServiceProcesses() {
092 final ArrayList result = new ArrayList(processes);
093 for (int i = 0; i < result.size(); ) {
094 if (!((ServiceJob) result.get(i)).isRunning()) {
095 result.remove(i);
096 } else {
097 i++;
098 }
099 }
100 return (ServiceJob[]) result.toArray(new ServiceJob[] {});
101 }
102
103 /**
104 * Create service call. Might block further execution, because an exclusive access to given module is given.
105 *
106 * @param service The service that runs in current thread.
107 * @param qedeq QEDEQ module for service.
108 * @param configParameters Config parameters for the service.
109 * @param parameters Parameter for this service call.
110 * @param process We run in this process.
111 * @return Created service call. Never <code>null</code> (if no {@link InterruptException} occurred).
112 * @throws InterruptException User canceled call.
113 */
114 public InternalModuleServiceCallImpl createServiceCall(final Service service,
115 final QedeqBo qedeq, final Parameters configParameters, final Parameters parameters,
116 final InternalServiceJob process) throws InterruptException {
117 if (!process.isRunning()) { // should not occur
118 throw new RuntimeException("Service process is not running any more.");
119 }
120 if (!process.getThread().isAlive()) {
121 throw new RuntimeException("thread is already dead");
122 }
123 final InternalModuleServiceCallImpl call = new InternalModuleServiceCallImpl(service, qedeq, configParameters,
124 parameters, process, process.getModuleServiceCall());
125 synchronized (this) {
126 calls.add(call);
127 }
128 process.setInternalServiceCall(call);
129 arbiter.lockRequiredModule(call);
130 return call;
131 }
132
133 /**
134 * End service call by unlocking previously locked module.
135 *
136 * @param call End this call, which should be finished, interrupted or halted before.
137 */
138 public void endServiceCall(final InternalModuleServiceCall call) {
139 arbiter.unlockRequiredModule(call);
140 }
141
142 /**
143 * Remove all service processes. All processes are also terminated via interruption.
144 */
145 public synchronized void terminateAndRemoveAllServiceProcesses() {
146 terminateAllServiceProcesses();
147 processes.clear();
148 finished.clear();
149 calls.clear();
150 }
151
152 /**
153 * Terminate all service processes.
154 */
155 public synchronized void terminateAllServiceProcesses() {
156 for (int i = 0; i < processes.size(); i++) {
157 final ServiceJob proc = (ServiceJob) processes.get(i);
158 proc.interrupt();
159 }
160 }
161
162 public synchronized InternalServiceJobImpl createServiceProcess(final String action) {
163 final InternalServiceJobImpl process = new InternalServiceJobImpl(arbiter, action);
164 processes.add(process);
165 return process;
166 }
167
168 public ModuleServiceResult executeService(final ModuleService service, final ModuleServiceExecutor executor,
169 final QedeqBo qedeq, final InternalServiceJob process) throws InterruptException {
170 final String method = "executePlugin(String, KernelQedeqBo, Object)";
171 if (process == null) {
172 throw new NullPointerException("ServiceProcess must not be null");
173 }
174 final Parameters configParameters = KernelContext.getInstance().getConfig().getServiceEntries(service);
175 InternalModuleServiceCallImpl call = null;
176 try {
177 call = createServiceCall(service, qedeq, configParameters, Parameters.EMPTY, process);
178 executor.executeService(call);
179 return call.getServiceResult();
180 } catch (final RuntimeException e) {
181 final String msg = service.getServiceAction() + " failed with a runtime exception.";
182 Trace.fatal(CLASS, this, method, msg, e);
183 QedeqLog.getInstance().logFailureReply(msg, qedeq.getUrl(), e.getMessage());
184 if (call != null) {
185 call.finishError(msg + " " + e.getMessage());
186 }
187 process.setFailureState();
188 return call != null ? call.getServiceResult() : null;
189 } catch (final InterruptException e) {
190 final String msg = service.getServiceAction() + " was canceled by user.";
191 QedeqLog.getInstance().logFailureReply(msg, qedeq.getUrl(), e.getMessage());
192 if (call != null) {
193 call.interrupt();
194 }
195 process.setInterruptedState();
196 throw e;
197 } finally {
198 endServiceCall(call);
199 }
200 }
201
202 /**
203 * Execute a plugin on an QEDEQ module.
204 *
205 * @param id Plugin to use.
206 * @param qedeq QEDEQ module to work on.
207 * @param data Process parameters.
208 * @param process Process. Must not be <code>null</code>..
209 * @return Plugin Specific result object. Might be <code>null</code>.
210 * @throws InterruptException User interrupt occurred.
211 * @throws RuntimeException Plugin unknown or process is not running any more.
212 */
213 public Object executePlugin(final String id, final KernelQedeqBo qedeq, final Object data,
214 final InternalServiceJob process) throws InterruptException {
215 final String method = "executePlugin(String, KernelQedeqBo, Object, InternalServiceJob)";
216 final ModuleServicePlugin plugin = pluginManager.getPlugin(id);
217 if (plugin == null) {
218 final String message = "Kernel does not know about plugin: ";
219 final RuntimeException e = new RuntimeException(message + id);
220 Trace.fatal(CLASS, this, method, message + id,
221 e);
222 throw e;
223 }
224 final Parameters configParameters = KernelContext.getInstance().getConfig().getServiceEntries(plugin);
225 if (!process.isRunning()) {
226 // TODO 20140124 m31: but if it was interrupted we want to throw a InterrruptException
227 final String message = "Process " + process.getId() + " was already finished: "
228 + process.getExecutionActionDescription();
229 final RuntimeException e = new RuntimeException(message + id);
230 Trace.fatal(CLASS, this, method, message + id,
231 e);
232 throw e;
233 }
234 InternalModuleServiceCallImpl call = null;
235 try {
236 call = createServiceCall(plugin, qedeq, configParameters, Parameters.EMPTY,
237 process);
238 final ModuleServicePluginExecutor exe = plugin.createExecutor(qedeq, configParameters);
239 call.setServiceCompleteness(exe);
240 final Object result = exe.executePlugin(call, data);
241 if (exe.getInterrupted()) {
242 call.interrupt();
243 throw new InterruptException(qedeq.getModuleAddress().createModuleContext());
244 } else {
245 call.finishOk();
246 process.setInternalServiceCall((InternalModuleServiceCall) call.getParentServiceCall());
247 }
248 return result;
249 } catch (final RuntimeException e) {
250 final String msg = plugin.getServiceAction() + " failed with a runtime exception.";
251 Trace.fatal(CLASS, this, method, msg, e);
252 QedeqLog.getInstance().logFailureReply(msg, qedeq.getUrl(), e.getMessage());
253 if (call != null) {
254 call.finishError(msg + ": " + e.getMessage());
255 }
256 return null;
257 } catch (final InterruptException e) {
258 final String msg = plugin.getServiceAction() + " was canceled by user.";
259 QedeqLog.getInstance().logFailureReply(msg, qedeq.getUrl(), e.getMessage());
260 if (call != null) {
261 call.interrupt();
262 }
263 throw e;
264 } finally {
265 endServiceCall(call);
266 }
267 }
268
269 /**
270 * Create a service job for executing a plugin.
271 *
272 * @param id Plugin to use.
273 * @return Process.
274 * @throws RuntimeException Plugin unknown.
275 */
276 public InternalServiceJob createServiceJob(final String id) {
277 final ModuleServicePlugin plugin = pluginManager.getPlugin(id);
278 if (plugin == null) {
279 final String message = "Kernel does not know about plugin: ";
280 final RuntimeException e = new RuntimeException(message + id);
281 Trace.fatal(CLASS, this, "createServiceJob", message + id, e);
282 throw e;
283 }
284 return createServiceProcess(plugin.getServiceAction());
285 }
286
287 }
|