2 * Copyright (C) 2007-2012 Argeo GmbH
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org
.argeo
.slc
.detached
;
18 import java
.util
.Collections
;
19 import java
.util
.HashMap
;
20 import java
.util
.Iterator
;
21 import java
.util
.List
;
24 import org
.apache
.commons
.logging
.Log
;
25 import org
.apache
.commons
.logging
.LogFactory
;
26 import org
.argeo
.slc
.detached
.admin
.CloseSession
;
27 import org
.argeo
.slc
.detached
.admin
.OpenSession
;
28 import org
.osgi
.framework
.BundleContext
;
29 import org
.osgi
.framework
.Constants
;
30 import org
.osgi
.util
.tracker
.ServiceTracker
;
31 import org
.springframework
.beans
.factory
.DisposableBean
;
32 import org
.springframework
.beans
.factory
.InitializingBean
;
33 import org
.springframework
.context
.ApplicationContext
;
34 import org
.springframework
.context
.ApplicationContextAware
;
35 import org
.springframework
.osgi
.context
.BundleContextAware
;
37 /** Default implementation of a detached server. */
38 public class DetachedExecutionServerImpl
implements DetachedExecutionServer
,
39 BundleContextAware
, InitializingBean
, DisposableBean
,
40 ApplicationContextAware
{
41 private final static Log log
= LogFactory
42 .getLog(DetachedExecutionServerImpl
.class);
44 private final DetachedContextImpl detachedContext
;
46 private DetachedSession currentSession
;
49 * Session being replayed, skipping the steps in the current session. If
50 * null, no session is replayed
52 private DetachedSession replayedSession
= null;
54 private BundleContext bundleContext
;
55 private ApplicationContext applicationContext
;
57 private final static String ALL_APP_CONTEXTS_KEY
= "__allApplicationContexts";
59 private Map
/* <String,ServiceTracker> */appContextServiceTrackers
= Collections
60 .synchronizedMap(new HashMap());
62 public DetachedExecutionServerImpl() {
63 detachedContext
= new DetachedContextImpl();
64 currentSession
= new DetachedSession();
65 currentSession
.setUuid(Long
.toString(System
.currentTimeMillis()));
68 public synchronized DetachedAnswer
executeRequest(DetachedRequest request
) {
69 if(log
.isDebugEnabled())
70 log
.debug("Received " + request
);
72 DetachedAnswer answer
= null;
74 Object obj
= retrieveStep(request
);
77 throw new DetachedException("Could not find action with ref "
81 if (obj
instanceof DetachedStep
) {
82 answer
= processStep((DetachedStep
) obj
, request
);
84 } else if (obj
instanceof DetachedAdminCommand
) {
85 answer
= processAdminCommand((DetachedAdminCommand
) obj
,
90 throw new DetachedException("Unknown action type "
91 + obj
.getClass() + " for action with ref "
94 } catch (Exception e
) {
95 answer
= new DetachedAnswer(request
);
96 answer
.setStatus(DetachedAnswer
.ERROR
);
97 log
.error("Error executing request " + request
, e
);
100 currentSession
.getRequests().add(request
);
101 currentSession
.getAnswers().add(answer
);
102 if(log
.isDebugEnabled())
103 log
.debug("Sent " + answer
);
107 protected synchronized Object
retrieveStep(DetachedRequest request
)
110 // Check whether there is a cached object
111 if (request
.getCachedObject() != null) {
112 Object cachedObj
= request
.getCachedObject();
113 if (log
.isTraceEnabled())
114 log
.trace("Use cached object " + cachedObj
+ " for request "
119 // Check its own app context (typically for admin steps)
120 if (applicationContext
.containsBean(request
.getRef())) {
122 Object obj
= applicationContext
.getBean(request
.getRef());
123 if (log
.isTraceEnabled())
124 log
.trace("Retrieve from server app context " + obj
125 + " for request " + request
);
127 } catch (Exception e
) {
128 if (log
.isTraceEnabled())
129 log
.trace("Could not retrieve " + request
.getRef()
130 + " from server app context: " + e
);
134 // Check whether the source bundle is set
135 String bundleName
= request
.getProperties().getProperty(
136 Constants
.BUNDLE_SYMBOLICNAME
);
138 ApplicationContext sourceAppContext
= null;
139 if (bundleName
!= null) {
140 if (!appContextServiceTrackers
.containsKey(bundleName
)) {
141 ServiceTracker nSt
= new ServiceTracker(bundleContext
,
142 bundleContext
.createFilter("(Bundle-SymbolicName="
143 + bundleName
+ ")"), null);
145 appContextServiceTrackers
.put(bundleName
, nSt
);
147 ServiceTracker st
= (ServiceTracker
) appContextServiceTrackers
149 sourceAppContext
= (ApplicationContext
) st
.getService();
150 if (log
.isTraceEnabled())
151 log
.trace("Use source application context from bundle "
156 obj
= sourceAppContext
.getBean(request
.getRef());
157 } catch (Exception e
) {
158 if (log
.isTraceEnabled())
159 log
.trace("Could not retrieve " + request
.getRef()
160 + " from app context of " + bundleName
+ ": " + e
);
165 // no bundle name specified or it failed
166 if (!appContextServiceTrackers
.containsKey(ALL_APP_CONTEXTS_KEY
)) {
167 ServiceTracker nSt
= new ServiceTracker(bundleContext
,
168 ApplicationContext
.class.getName(), null);
170 appContextServiceTrackers
.put(ALL_APP_CONTEXTS_KEY
, nSt
);
172 ServiceTracker st
= (ServiceTracker
) appContextServiceTrackers
173 .get(ALL_APP_CONTEXTS_KEY
);
174 Object
[] arr
= st
.getServices();
175 for (int i
= 0; i
< arr
.length
; i
++) {
176 ApplicationContext appC
= (ApplicationContext
) arr
[i
];
177 if (appC
.containsBean(request
.getRef())) {
178 sourceAppContext
= appC
;
179 if (log
.isTraceEnabled())
181 .trace("Retrieved source application context "
182 + "by scanning all published application contexts.");
184 Object obj
= sourceAppContext
.getBean(request
.getRef());
186 } catch (Exception e
) {
187 if (log
.isTraceEnabled())
188 log
.trace("Could not retrieve " + request
.getRef()
189 + " from app context " + appC
+ ": " + e
);
195 "Cannot find any published application context containing bean "
199 protected synchronized DetachedAnswer
processStep(DetachedStep obj
,
200 DetachedRequest request
) {
201 DetachedAnswer answer
;
203 StringBuffer skippedLog
= new StringBuffer();
204 boolean execute
= true;
206 if (replayedSession
!= null) {
207 // Skip execution of already successful steps
208 int stepIndex
= currentSession
.getExecutedStepCount();
210 if (stepIndex
< replayedSession
.getExecutedStepCount()) {
211 DetachedAnswer previousAnswer
= (DetachedAnswer
) replayedSession
212 .getAnswers().get(stepIndex
);
213 DetachedRequest previousRequest
= (DetachedRequest
) replayedSession
214 .getRequests().get(stepIndex
);
217 if (!previousRequest
.getRef().equals(request
.getRef())) {
218 String msg
= "New request is not consistent with previous ref. previousRef="
219 + previousRequest
.getRef()
221 + request
.getRef() + "\n";
222 skippedLog
.append(msg
);
226 if (previousAnswer
.getStatus() != DetachedAnswer
.ERROR
) {
227 // if no error occurred in the replayedSession,
230 String msg
= "Skipped Step " + request
.getRef()
231 + " (stepIndex=" + stepIndex
+ ")";
232 skippedLog
.append(msg
);
236 // if an error occurred, execute the step and leave
237 // skipUntillError mode (even if replayedSession
239 log
.info("### End of SkipUntilError Mode ###");
240 log
.info("Step " + request
.getRef()
241 + " was previously in error, executing it again."
242 + " (stepIndex=" + stepIndex
+ ").");
243 replayedSession
= null;
246 // went further as skip count, doing nothing.
251 DetachedStep step
= (DetachedStep
) obj
;
252 // Actually execute the step
253 answer
= step
.execute(detachedContext
, request
);
255 answer
= new DetachedAnswer(request
);
256 answer
.setStatus(DetachedAnswer
.SKIPPED
);
257 answer
.setLog(skippedLog
.toString());
262 protected synchronized DetachedAnswer
processAdminCommand(
263 DetachedAdminCommand obj
, DetachedRequest request
) {
264 DetachedAnswer answer
;
265 if (obj
instanceof OpenSession
) {
266 DetachedSession newSession
= ((OpenSession
) obj
).execute(request
,
269 log
.debug("Creating new DetachedSession : " + newSession
);
271 if ((currentSession
!= null) && currentSession
.lastActionIsError()
272 && DetachedSession
.SKIP_UNTIL_ERROR
.equals(newSession
.getDoItAgainPolicy())) {
273 // switch to replay mode
274 log
.info("### Start SkipUntilError Mode ###");
275 replayedSession
= currentSession
;
278 currentSession
= newSession
;
280 answer
= new DetachedAnswer(request
, "Session #"
281 + currentSession
.getUuid() + " open.");
282 } else if (obj
instanceof CloseSession
) {
283 if (currentSession
== null)
284 throw new DetachedException("There is no open session to close");
285 answer
= new DetachedAnswer(request
, "Session #"
286 + currentSession
.getUuid() + " closed.");
287 answer
.setStatus(DetachedAnswer
.CLOSED_SESSION
);
294 protected synchronized String
dumpSessionsHistory(
295 DetachedRequest requestCurrent
, DetachedAnswer answerCurrent
) {
296 StringBuffer buf
= new StringBuffer(
297 "##\n## SESSIONS HISTORY DUMP\n##\n");
298 buf
.append("# CURRENT\n");
299 buf
.append("Current session: ").append(currentSession
)
301 buf
.append("Current request: ").append(requestCurrent
).append('\n');
302 buf
.append("Current answer: ").append(answerCurrent
).append('\n');
304 buf
.append("# CURRENT SESSION\n");
306 List requests
= currentSession
.getRequests();
307 List answers
= currentSession
.getAnswers();
308 for (int j
= 0; j
< requests
.size(); j
++) {
309 DetachedRequest request
= (DetachedRequest
) requests
.get(j
);
310 buf
.append('\t').append(j
).append(". ").append(request
)
312 if (answers
.size() > j
) {
313 DetachedAnswer answer
= (DetachedAnswer
) answers
.get(j
);
314 buf
.append('\t').append(j
).append(". ").append(answer
).append(
319 buf
.append("# DETACHED CONTEXT\n");
320 buf
.append(detachedContext
).append('\n');
322 buf
.append("##\n## END OF SESSIONS HISTORY DUMP\n##\n");
323 return buf
.toString();
326 public void setBundleContext(BundleContext bundleContext
) {
327 this.bundleContext
= bundleContext
;
330 public void afterPropertiesSet() throws Exception
{
331 log
.debug("Detached execution server initialized.");
334 public synchronized void destroy() throws Exception
{
335 Iterator
/* <String> */keys
= appContextServiceTrackers
.keySet()
337 while (keys
.hasNext()) {
338 ServiceTracker st
= (ServiceTracker
) appContextServiceTrackers
342 appContextServiceTrackers
.clear();
344 log
.debug("Detached execution server closed.");
347 public void setApplicationContext(ApplicationContext applicationContext
) {
348 this.applicationContext
= applicationContext
;