/contrib-fort/rules4jbi/src/main/org/openesb/components/rules4jbi/engine/component/Rules4JBIComponent.java
Java | 365 lines | 198 code | 102 blank | 65 comment | 21 complexity | 9607a9a1aa4415eadb596d9e24d953bb MD5 | raw file
1/*
2 * @(#)Rules4JBIComponent.java $Revision: 1.2 $ $Date: 2008/07/14 16:30:26 $
3 *
4 * Copyright (c) 2008 Milan Fort (http://www.milanfort.com/). All rights reserved.
5 *
6 * The contents of this file are subject to the terms of the Common Development
7 * and Distribution License (the "License"). You may not use this file except
8 * in compliance with the License.
9 *
10 * You can obtain a copy of the license at http://www.sun.com/cddl/cddl.html.
11 * See the License for the specific language governing permissions and limitations
12 * under the License.
13 */
14
15package org.openesb.components.rules4jbi.engine.component;
16
17import java.io.File;
18import java.util.concurrent.Callable;
19import java.util.concurrent.CompletionService;
20import java.util.concurrent.ExecutionException;
21import java.util.concurrent.ExecutorService;
22import java.util.concurrent.Executors;
23import java.util.concurrent.Future;
24import java.util.concurrent.TimeUnit;
25
26import javax.jbi.JBIException;
27import javax.jbi.component.Component;
28import javax.jbi.component.ComponentContext;
29import javax.jbi.component.ComponentLifeCycle;
30import javax.jbi.component.ServiceUnitManager;
31import javax.jbi.messaging.ExchangeStatus;
32import javax.jbi.messaging.InOut;
33import javax.jbi.messaging.MessageExchange;
34import javax.jbi.servicedesc.ServiceEndpoint;
35import javax.management.ObjectName;
36
37import org.w3c.dom.Document;
38import org.w3c.dom.DocumentFragment;
39
40import com.google.inject.Guice;
41import com.google.inject.Inject;
42import com.google.inject.Injector;
43import com.google.inject.Stage;
44
45import net.jcip.annotations.NotThreadSafe;
46
47import org.openesb.components.rules4jbi.shared.logging.Logger;
48
49import org.openesb.components.rules4jbi.engine.guice.annotations.Main;
50import org.openesb.components.rules4jbi.engine.guice.annotations.MessageExchangeProcessor;
51import org.openesb.components.rules4jbi.engine.guice.modules.ConstantModule;
52import org.openesb.components.rules4jbi.engine.guice.modules.ExecutorModule;
53import org.openesb.components.rules4jbi.engine.guice.modules.JBIModule;
54import org.openesb.components.rules4jbi.engine.guice.modules.LoggerModule;
55import org.openesb.components.rules4jbi.engine.util.DOMUtils;
56
57import static org.openesb.components.rules4jbi.engine.util.ConcurrencyUtils.shutdownExecutorService;
58
59/**
60 * This class is the entry point of the Rules service engine to the JBI runtime.
61 * It implements both the <code>javax.jbi.component.Component</code> and
62 * <code>javax.jbi.component.ComponentLifeCycle</code> interfaces.
63 *
64 * @author Milan Fort (http://www.milanfort.com/)
65 * @version $Revision: 1.2 $ $Date: 2008/07/14 16:30:26 $
66 *
67 * @see javax.jbi.component.Component
68 * @see javax.jbi.component.ComponentLifeCycle
69 * @since 0.1
70 */
71@NotThreadSafe
72public class Rules4JBIComponent implements Component, ComponentLifeCycle {
73
74 /** Time to wait for tasks to complete after component was stopped; in seconds. */
75 private static final long TASK_COMPLETION_WAIT_TIME = 2;
76
77 @Inject @Main
78 private Logger logger;
79
80 @Inject
81 private Injector injector;
82
83 @Inject
84 private Rules4JBIServiceUnitManager manager;
85
86 @Inject
87 private DeliveryChannelService deliveryChannelService;
88
89 @Inject
90 private ComponentContext componentContext;
91
92 @Inject @Main
93 private ExecutorService mainExecutorService;
94
95 /**
96 * This completition service uses mainExecutorService as its internal executor.
97 *
98 * @see #mainExecutorService
99 */
100 @Inject @MessageExchangeProcessor
101 private CompletionService<InOut> messageExchangeProcessor;
102
103 private ExecutorService dispatcher;
104
105 private ExecutorService collector;
106
107
108 /* ComponentLifeCycle Methods */
109
110 public ObjectName getExtensionMBeanName() {
111 logger.entering(this.getClass(), "getExtensionMBeanName");
112
113 return null;
114 }
115
116 public void init(ComponentContext componentContext) throws JBIException {
117 if (componentContext == null) {
118 throw new JBIException("Null component context received during bootstrap");
119 }
120
121 /*
122 * The JBI spec doesn't guarantee that the same instance will be provided
123 * during the lifecycle of the component. Therefore, we need to re-inject
124 * the dependencie upon every init() method call.
125 */
126// if (this.componentContext != null && this.componentContext != componentContext) {
127// throw new JBIException("Different instance of component context during lifecycle is not supported");
128// }
129
130 boolean configurationValuesLoaded = true;
131 InstallationConfiguration configuration = null;
132
133 try {
134 configuration = InstallationConfiguration.load(
135 new File(componentContext.getWorkspaceRoot(), InstallationConfiguration.CONFIG_FILE_NAME));
136
137 } catch (InvalidInstallationConfigurationException e) {
138
139 configurationValuesLoaded = false;
140 configuration = new InstallationConfiguration();
141 }
142
143 injector = Guice.createInjector(Stage.PRODUCTION,
144 new JBIModule(componentContext),
145 new LoggerModule(componentContext),
146 new ConstantModule(configuration.getMaxServiceUnits()),
147 new ExecutorModule(configuration.getPoolSize()));
148
149 injector.injectMembers(this);
150 manager.injectDependenciesIntoServiceUnits();
151
152 if (logger == null) {
153 throw new JBIException("Unable to properly inject depencencies");
154 }
155
156 logger.entering(this.getClass(), "init");
157
158 logger.info("Initializing component '%s'", componentContext.getComponentName());
159
160 if (!configurationValuesLoaded) {
161 logger.warning("Failed to load configuration file; using default values");
162 }
163
164 logger.config("Thread pool size: %d", configuration.getPoolSize());
165 logger.config("Maximum allowed service units: %d", configuration.getMaxServiceUnits());
166
167 deliveryChannelService.init();
168
169 dispatcher = Executors.newSingleThreadExecutor();
170
171 collector = Executors.newSingleThreadExecutor();
172
173 logger.exiting(this.getClass(), "init");
174 }
175
176 public void start() throws JBIException {
177 logger.entering(this.getClass(), "start");
178
179 deliveryChannelService.start();
180
181 collector.execute(new CollectorTask());
182 dispatcher.execute(new DispatcherTask());
183
184 /*
185 * JBI runtime will start the service units automatically, if appropriate
186 */
187 }
188
189 public void stop() throws JBIException {
190 logger.entering(this.getClass(), "stop");
191
192 deliveryChannelService.stop();
193
194 /*
195 * JBI runtime will stop the service units automatically, if appropriate
196 */
197 }
198
199 public void shutDown() throws JBIException {
200 logger.entering(this.getClass(), "shutDown");
201
202 shutdownExecutorService(logger, "Dispatcher", dispatcher);
203
204 shutdownExecutorService(logger, "Collector", collector);
205
206 shutdownExecutorService(logger, "Main Executor", mainExecutorService);
207
208 deliveryChannelService.shutDown();
209 }
210
211
212 /* Component Methods */
213
214 public ComponentLifeCycle getLifeCycle() {
215 return this;
216 }
217
218 public Document getServiceDescription(ServiceEndpoint endpoint) {
219 logger.entering(this.getClass(), "getServiceDescription", endpoint);
220
221 ServiceUnit serviceUnit = manager.findByServiceEndpoint(endpoint);
222
223 return serviceUnit != null ? serviceUnit.getServiceDescription() : null;
224 }
225
226 public ServiceUnitManager getServiceUnitManager() {
227 logger.entering(this.getClass(), "getServiceUnitManager");
228
229 return manager;
230 }
231
232 public boolean isExchangeWithConsumerOkay(ServiceEndpoint endpoint, MessageExchange exchange) {
233 logger.entering(this.getClass(), "isExchangeWithConsumerOkay");
234
235 /*
236 * TODO: We should check whether we want/are able to handle this exchange
237 */
238
239 return true;
240 }
241
242 public boolean isExchangeWithProviderOkay(ServiceEndpoint endpoint, MessageExchange exchange) {
243 logger.entering(this.getClass(), "isExchangeWithProviderOkay");
244
245 /*
246 * TODO: We should probably return false here. We do not initiate message exchanges,
247 * so this method should never be called on us.
248 */
249
250 return true;
251 }
252
253 public ServiceEndpoint resolveEndpointReference(DocumentFragment epr) {
254 logger.entering(this.getClass(), "resolveEndpointReference", DOMUtils.documentFragmentToString(epr));
255
256 /* We do not support dynamic endpoints */
257 return null;
258 }
259
260
261 /* Tasks */
262
263 /**
264 * Task responsible for retrieving received <code>MessageExchange</code>s
265 * and dispatching them to the appropriate service unit for processing.
266 */
267 private class DispatcherTask implements Runnable {
268
269 public void run() {
270 logger.fine("Starting message dispatcher");
271
272 while (true) {
273 try {
274 MessageExchange messageExchange = deliveryChannelService.receive();
275
276 if (messageExchange instanceof EmptyMessageExchange) {
277 logger.fine("Message dispatcher received terminal message");
278
279 messageExchangeProcessor.submit(new Callable<InOut>() {
280
281 public InOut call() throws Exception {
282
283 /* give the other tasks some time to complete */
284 TimeUnit.SECONDS.sleep(TASK_COMPLETION_WAIT_TIME);
285
286 return new EmptyMessageExchange();
287 }
288 });
289
290 break;
291
292 } else if (messageExchange instanceof InOut) {
293 InOut inOut = (InOut) messageExchange;
294
295 if (ExchangeStatus.ACTIVE.equals(inOut.getStatus())) {
296
297 logger.fine("Searching for service unit for the endpoint: %s", inOut.getEndpoint());
298 ServiceUnit serviceUnit = manager.findByServiceEndpoint(inOut.getEndpoint());
299
300 if (serviceUnit != null) {
301 serviceUnit.process(inOut);
302
303 } else {
304 logger.warning("Could not find providing service unit");
305 }
306
307 } else if (ExchangeStatus.DONE.equals(inOut.getStatus())) {
308 logger.fine("Received message with DONE status");
309
310 } else if (ExchangeStatus.ERROR.equals(inOut.getStatus())) {
311 logger.warning("Received message with ERROR status", inOut.getError());
312 }
313
314 } else {
315 logger.severe("Received incorrect MEP");
316 }
317
318 } catch (InterruptedException e) {
319 logger.fine("Message dispatcher interrupted");
320
321 break;
322 }
323 }
324
325 logger.fine("Message dispatcher terminated");
326 }
327 }
328
329 /**
330 * Task responsible for retrieving completed <code>MessageExchange</code>s
331 * and sending them back to the caller via <code>DeliveryChannelService</code>.
332 */
333 private class CollectorTask implements Runnable {
334
335 public void run() {
336 logger.fine("Starting results collector");
337
338 while (true) {
339 try {
340 Future<InOut> processedMessageExchange = messageExchangeProcessor.take();
341
342 InOut processedMessage = processedMessageExchange.get();
343
344 deliveryChannelService.send(processedMessage);
345
346 if (processedMessage instanceof EmptyMessageExchange) {
347 logger.fine("Results collector received terminal message");
348
349 break;
350 }
351
352 } catch (InterruptedException e) {
353 logger.fine("Results collector interrupted");
354
355 break;
356
357 } catch (ExecutionException e) {
358 logger.warning("Error while executing task", e);
359 }
360 }
361
362 logger.fine("Results collector terminated");
363 }
364 }
365}