]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.detached/src/main/java/org/argeo/slc/detached/DetachedExecutionServerImpl.java
Allow to refresh a comma separated list of bundles
[gpl/argeo-slc.git] / runtime / org.argeo.slc.detached / src / main / java / org / argeo / slc / detached / DetachedExecutionServerImpl.java
1 package org.argeo.slc.detached;
2
3 import java.io.PrintWriter;
4 import java.io.StringWriter;
5 import java.util.List;
6 import java.util.Vector;
7
8 import org.apache.commons.io.IOUtils;
9 import org.apache.commons.logging.Log;
10 import org.apache.commons.logging.LogFactory;
11 import org.argeo.slc.detached.admin.CloseSession;
12 import org.argeo.slc.detached.admin.OpenSession;
13 import org.osgi.framework.BundleContext;
14 import org.osgi.framework.ServiceReference;
15 import org.springframework.context.ApplicationContext;
16 import org.springframework.osgi.context.BundleContextAware;
17
18 /** Default implementation of a detached server. */
19 public class DetachedExecutionServerImpl implements DetachedExecutionServer,
20 BundleContextAware {
21 private final static Log log = LogFactory
22 .getLog(DetachedExecutionServerImpl.class);
23
24 private final DetachedContextImpl detachedContext;
25 private final List sessions;
26
27 private int skipCount = 1;// start skipCount at 1 since the first step is
28 // always an open session
29
30 private BundleContext bundleContext;
31
32 public DetachedExecutionServerImpl() {
33 detachedContext = new DetachedContextImpl();
34 sessions = new Vector();
35 }
36
37 public synchronized DetachedAnswer executeRequest(DetachedRequest request) {
38 log.info("Received request " + request);
39
40 DetachedAnswer answer = null;
41 try {
42 // Find action
43 ServiceReference[] refs = bundleContext.getAllServiceReferences(
44 ApplicationContext.class.getName(), null);
45 Object obj = null;
46 for (int i = 0; i < refs.length; i++) {
47 ApplicationContext appContext = (ApplicationContext) bundleContext
48 .getService(refs[i]);
49 try {
50 obj = appContext.getBean(request.getRef());
51 } catch (Exception e) {
52 // silent
53 if (log.isTraceEnabled())
54 log.trace("Could not find ref " + request.getRef(), e);
55 }
56 if (obj != null) {
57 break;
58 }
59 }
60
61 if (obj == null)
62 throw new DetachedException("Could not find action with ref "
63 + request.getRef());
64
65 // Execute actions
66 if (obj instanceof DetachedStep) {
67 answer = processStep((DetachedStep) obj, request);
68
69 } else if (obj instanceof DetachedAdminCommand) {
70 answer = processAdminCommand((DetachedAdminCommand) obj,
71 request);
72 }
73
74 if (answer == null) {
75 throw new DetachedException("Unknown action type "
76 + obj.getClass() + " for action with ref "
77 + request.getRef());
78 }
79 } catch (Exception e) {
80 answer = new DetachedAnswer(request);
81 answer.setStatus(DetachedAnswer.ERROR);
82 StringWriter writer = new StringWriter();
83 e.printStackTrace(new PrintWriter(writer));
84 answer.setLog(writer.toString());
85 IOUtils.closeQuietly(writer);
86 }
87
88 // Case where current session is unexpectly null
89 if (getCurrentSession() == null) {
90 log
91 .error("CURRENT SESSION IS NULL."
92 + " Detached status is inconsistent dumping sessions history:");
93 log.error(dumpSessionsHistory(request, answer));
94 if (answer != null) {
95 answer.setStatus(DetachedAnswer.ERROR);
96 answer
97 .addToLog("CURRENT SESSION IS NULL."
98 + " Detached status is inconsistent, see detached log for more details.");
99 return answer;
100 } else {
101 throw new DetachedException(
102 "Answer is null. Cannot return it. See log for more details.");
103 }
104
105 }
106
107 getCurrentSession().getRequests().add(request);
108 getCurrentSession().getAnswers().add(answer);
109 log.info("Sent answer " + answer);
110 return answer;
111 }
112
113 protected synchronized DetachedAnswer processStep(DetachedStep obj,
114 DetachedRequest request) {
115 DetachedAnswer answer;
116 if (getCurrentSession() == null)
117 throw new DetachedException("No open session.");
118
119 StringBuffer skippedLog = new StringBuffer();
120 boolean execute = true;
121 if (getPreviousSession() != null && !getPreviousSession().isClosed()) {
122 if (getCurrentSession().getDoItAgainPolicy().equals(
123 DetachedSession.SKIP_UNTIL_ERROR)) {
124 // Skip execution of already successful steps
125 if (getPreviousSession().getAnswers().size() > skipCount) {
126 DetachedAnswer previousAnswer = (DetachedAnswer) getPreviousSession()
127 .getAnswers().get(skipCount);
128 DetachedRequest previousRequest = (DetachedRequest) getPreviousSession()
129 .getRequests().get(skipCount);
130 // Check paths
131 if (!previousRequest.getPath().equals(request.getPath())) {
132 String msg = "New request is not consistent with previous path. previousPath="
133 + previousRequest.getPath()
134 + ", newPath="
135 + request.getPath() + "\n";
136 skippedLog.append(msg);
137 log.warn(msg);
138 }
139
140 if (previousAnswer.getStatus() != DetachedAnswer.ERROR) {
141 execute = false;
142 String msg = "Skipped path " + request.getPath()
143 + " (skipCount=" + skipCount + ")";
144 skippedLog.append(msg);
145 log.info(msg);
146 skipCount++;
147 } else {
148 log
149 .info("Path "
150 + request.getPath()
151 + " was previously in error, executing it again."
152 + " (skipCount=" + skipCount
153 + "). Reset skip count to 1");
154 skipCount = 1;
155 }
156 } else {
157 // went further as skip count, doing nothing.
158 }
159 }
160 }
161
162 if (execute) {
163 DetachedStep step = (DetachedStep) obj;
164 // Actually execute the step
165 answer = step.execute(detachedContext, request);
166 } else {
167 answer = new DetachedAnswer(request);
168 answer.setStatus(DetachedAnswer.SKIPPED);
169 answer.setLog(skippedLog.toString());
170 }
171 return answer;
172 }
173
174 protected synchronized DetachedAnswer processAdminCommand(
175 DetachedAdminCommand obj, DetachedRequest request) {
176 DetachedAnswer answer;
177 if (obj instanceof OpenSession) {
178 if (getCurrentSession() != null) {
179 // TODO: better understand why there is sometimes two open
180 // sessions sent.
181 log.warn("There is already an open session #"
182 + getCurrentSession().getUuid() + ". Closing it...");
183 DetachedAnswer answerT = new DetachedAnswer(
184 request,
185 "Session #"
186 + getCurrentSession().getUuid()
187 + " forcibly closed. THIS ANSWER WAS NOT SENT BACK.");
188 answerT.setStatus(DetachedAnswer.CLOSED_SESSION);
189 getCurrentSession().getAnswers().add(answerT);
190 }
191 sessions.add(((OpenSession) obj).execute(request, bundleContext));
192 answer = new DetachedAnswer(request, "Session #"
193 + getCurrentSession().getUuid() + " open.");
194 } else if (obj instanceof CloseSession) {
195 if (getCurrentSession() == null)
196 throw new DetachedException("There is no open session to close");
197 answer = new DetachedAnswer(request, "Session #"
198 + getCurrentSession().getUuid() + " closed.");
199 answer.setStatus(DetachedAnswer.CLOSED_SESSION);
200 } else {
201 answer = null;
202 }
203 return answer;
204 }
205
206 /**
207 * Returns the current session based on the list of previous sessions.
208 *
209 * @return the current session or null if there is no session yet defined or
210 * if the last registered session is null or in error.
211 */
212 protected synchronized final DetachedSession getCurrentSession() {
213 if (sessions.size() == 0) {
214 return null;
215 } else {
216 DetachedSession session = (DetachedSession) sessions.get(sessions
217 .size() - 1);
218 List answers = session.getAnswers();
219 if (answers.size() > 0) {
220 DetachedAnswer lastAnswer = (DetachedAnswer) answers
221 .get(answers.size() - 1);
222 if (lastAnswer.getStatus() == DetachedAnswer.ERROR
223 || lastAnswer.getStatus() == DetachedAnswer.CLOSED_SESSION)
224 return null;
225 }
226 return session;
227 }
228 }
229
230 protected synchronized String dumpSessionsHistory(
231 DetachedRequest requestCurrent, DetachedAnswer answerCurrent) {
232 StringBuffer buf = new StringBuffer("## SESSIONS HISTORY DUMP\n");
233 buf.append("# CURRENT\n");
234 buf.append("Current session: ").append(getCurrentSession())
235 .append('\n');
236 buf.append("Current request: ").append(requestCurrent).append('\n');
237 buf.append("Current answer: ").append(answerCurrent).append('\n');
238 buf.append("Skip count: ").append(skipCount).append('\n');
239
240 buf.append("# SESSIONS\n");
241 for (int i = 0; i < sessions.size(); i++) {
242 DetachedSession session = (DetachedSession) sessions.get(i);
243 buf.append(i).append(". ").append(session).append('\n');
244 List requests = session.getRequests();
245 List answers = session.getAnswers();
246 for (int j = 0; j < requests.size(); j++) {
247 DetachedRequest request = (DetachedRequest) requests.get(j);
248 buf.append('\t').append(j).append(". ").append(request).append(
249 '\n');
250 if (answers.size() > j) {
251 DetachedAnswer answer = (DetachedAnswer) answers.get(j);
252 buf.append('\t').append(j).append(". ").append(answer)
253 .append('\n');
254 }
255 }
256 }
257
258 buf.append("# DETACHED CONTEXT\n");
259 buf.append(detachedContext).append('\n');
260
261 return buf.toString();
262 }
263
264 protected synchronized final DetachedSession getPreviousSession() {
265 if (sessions.size() < 2)
266 return null;
267 else
268 return (DetachedSession) sessions.get(sessions.size() - 2);
269 }
270
271 public void setBundleContext(BundleContext bundleContext) {
272 this.bundleContext = bundleContext;
273 }
274
275 }