001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.servicemix.jbi.nmr.flow.seda;
018
019 import java.util.Map;
020 import java.util.concurrent.ConcurrentHashMap;
021 import java.util.concurrent.atomic.AtomicBoolean;
022
023 import javax.jbi.JBIException;
024 import javax.jbi.management.LifeCycleMBean;
025 import javax.jbi.messaging.ExchangeStatus;
026 import javax.jbi.messaging.MessageExchange;
027 import javax.jbi.messaging.MessagingException;
028 import javax.management.JMException;
029 import javax.management.MBeanAttributeInfo;
030 import javax.management.ObjectName;
031 import javax.transaction.Transaction;
032 import javax.transaction.TransactionManager;
033
034 import org.apache.servicemix.jbi.event.ComponentAdapter;
035 import org.apache.servicemix.jbi.event.ComponentEvent;
036 import org.apache.servicemix.jbi.event.ComponentListener;
037 import org.apache.servicemix.jbi.framework.ComponentNameSpace;
038 import org.apache.servicemix.jbi.management.AttributeInfoHelper;
039 import org.apache.servicemix.jbi.messaging.MessageExchangeImpl;
040 import org.apache.servicemix.jbi.nmr.Broker;
041 import org.apache.servicemix.jbi.nmr.flow.AbstractFlow;
042 import org.apache.servicemix.jbi.servicedesc.AbstractServiceEndpoint;
043
044 /**
045 * The SedaFlow introduces a simple event staging between the internal processes
046 * in the NMR Broker. A Seda flow (the default) is suited for general deployment,
047 * as the additional staging is well suited buffering exchanges between heavily
048 * routed to components (where state may be being used) for example.
049 *
050 * @version $Revision: 665811 $
051 * @org.apache.xbean.XBean element="sedaFlow"
052 */
053 public class SedaFlow extends AbstractFlow {
054
055 protected Map<ComponentNameSpace, SedaQueue> queueMap = new ConcurrentHashMap<ComponentNameSpace, SedaQueue>();
056 protected AtomicBoolean started = new AtomicBoolean(false);
057 protected ComponentListener listener;
058
059 /**
060 * The type of Flow
061 *
062 * @return the type
063 */
064 public String getDescription() {
065 return "seda";
066 }
067
068 /**
069 * Initialize the Region
070 *
071 * @param broker
072 * @throws JBIException
073 */
074 public void init(Broker broker) throws JBIException {
075 super.init(broker);
076 listener = new ComponentAdapter() {
077 public void componentShutDown(ComponentEvent event) {
078 onComponentShutdown(event.getComponent().getComponentNameSpace());
079 }
080 };
081 broker.getContainer().addListener(listener);
082 }
083
084 /**
085 * Check if the flow can support the requested QoS for this exchange
086 * @param me the exchange to check
087 * @return true if this flow can handle the given exchange
088 */
089 public boolean canHandle(MessageExchange me) {
090 if (isPersistent(me)) {
091 return false;
092 }
093 if (isClustered(me)) {
094 return false;
095 }
096 // we have the mirror, so the role is the one for the target component
097 if (!broker.getContainer().isUseNewTransactionModel()
098 && isTransacted(me) && !isSynchronous(me) && me.getStatus() == ExchangeStatus.ACTIVE) {
099 return false;
100 }
101 return true;
102 }
103
104 /**
105 * start the flow
106 *
107 * @throws JBIException
108 */
109 public void start() throws JBIException {
110 if (started.compareAndSet(false, true)) {
111 for (SedaQueue queue : queueMap.values()) {
112 queue.start();
113 }
114 }
115 super.start();
116 }
117
118 /**
119 * stop the flow
120 *
121 * @throws JBIException
122 */
123 public void stop() throws JBIException {
124 if (started.compareAndSet(true, false)) {
125 for (SedaQueue queue : queueMap.values()) {
126 queue.stop();
127 }
128 }
129 super.stop();
130 }
131
132 /**
133 * shutDown the flow
134 *
135 * @throws JBIException
136 */
137 public void shutDown() throws JBIException {
138 broker.getContainer().removeListener(listener);
139 for (SedaQueue queue : queueMap.values()) {
140 queue.shutDown();
141 unregisterQueue(queue);
142 }
143 super.shutDown();
144 }
145
146 /**
147 * Distribute an ExchangePacket
148 *
149 * @param packet
150 * @throws JBIException
151 */
152 protected void doSend(MessageExchangeImpl me) throws JBIException {
153 if (me.getDestinationId() == null) {
154 me.setDestinationId(((AbstractServiceEndpoint) me.getEndpoint()).getComponentNameSpace());
155 }
156 if (isTransacted(me)) {
157 me.setTxState(MessageExchangeImpl.TX_STATE_CONVEYED);
158 }
159 // If the message has been sent synchronously, do not use seda
160 // as it would consume threads from the work manager in a useless
161 // way. This could lead to deadlocks.
162 suspendTx(me);
163 enqueuePacket(me);
164 }
165
166 protected void doRouting(MessageExchangeImpl me) throws MessagingException {
167 resumeTx(me);
168 super.doRouting(me);
169 }
170
171 /**
172 * Put the packet in the queue for later processing.
173 * @param packet
174 * @throws JBIException
175 */
176 protected void enqueuePacket(MessageExchangeImpl me) throws JBIException {
177 ComponentNameSpace cns = me.getDestinationId();
178 SedaQueue queue = queueMap.get(cns);
179 if (queue == null) {
180 queue = createQueue(cns);
181 }
182 try {
183 queue.enqueue(me);
184 } catch (InterruptedException e) {
185 throw new MessagingException(queue + " Failed to enqueue exchange: " + me, e);
186 }
187 }
188
189 protected synchronized SedaQueue createQueue(ComponentNameSpace cns) throws JBIException {
190 SedaQueue queue = queueMap.get(cns);
191 if (queue == null) {
192 queue = new SedaQueue(cns);
193 queue.init(this);
194 registerQueue(cns, queue);
195 if (started.get()) {
196 queue.start();
197 }
198 queueMap.put(cns, queue);
199 }
200 return queue;
201 }
202
203 /**
204 * Process state changes in Components
205 *
206 * @param event
207 */
208 public synchronized void onComponentShutdown(ComponentNameSpace cns) {
209 SedaQueue queue = queueMap.remove(cns);
210 if (queue != null) {
211 try {
212 queue.shutDown();
213 unregisterQueue(queue);
214 } catch (JBIException e) {
215 log.error("Failed to stop SedaQueue: " + queue + ": " + e);
216 if (log.isDebugEnabled()) {
217 log.debug("Failed to stop SedaQueue: " + queue, e);
218 }
219 }
220 }
221 }
222
223 /**
224 * release a queue
225 *
226 * @param queue
227 */
228 public synchronized void release(SedaQueue queue) {
229 if (queue != null) {
230 queueMap.remove(queue.getComponentNameSpace());
231 unregisterQueue(queue);
232 }
233 }
234
235 /**
236 * Get Queue number
237 *
238 * @return number of running Queues
239 */
240 public int getQueueNumber() {
241 return queueMap.size();
242 }
243
244 protected void registerQueue(ComponentNameSpace cns, SedaQueue queue) {
245 try {
246 ObjectName objectName = broker.getContainer().getManagementContext().createObjectName(queue);
247 if (getSubType() != null) {
248 objectName = new ObjectName(objectName + ",subtype=" + getSubType());
249 }
250 queue.setObjectName(objectName);
251 broker.getContainer().getManagementContext().registerMBean(objectName, queue, LifeCycleMBean.class);
252 } catch (JMException e) {
253 log.error("Failed to register SedaQueue: " + queue + " with the ManagementContext: " + e);
254 if (log.isDebugEnabled()) {
255 log.debug("Failed to register SedaQueue: " + queue + " with the ManagementContext", e);
256 }
257 }
258 }
259
260 protected void unregisterQueue(SedaQueue queue) {
261 try {
262 broker.getContainer().getManagementContext().unregisterMBean(queue.getObjectName());
263 } catch (JBIException e) {
264 log.error("Failed to unregister SedaQueue: " + queue + " from the ManagementContext");
265 if (log.isDebugEnabled()) {
266 log.debug("Failed to unregister SedaQueue: " + queue + " with the ManagementContext", e);
267 }
268 }
269 }
270
271 /**
272 * Get an array of MBeanAttributeInfo
273 *
274 * @return array of AttributeInfos
275 * @throws JMException
276 */
277 public MBeanAttributeInfo[] getAttributeInfos() throws JMException {
278 AttributeInfoHelper helper = new AttributeInfoHelper();
279 helper.addAttribute(getObjectToManage(), "queueNumber", "number of running SedaQueues");
280 return AttributeInfoHelper.join(super.getAttributeInfos(), helper.getAttributeInfos());
281 }
282
283 protected void suspendTx(MessageExchangeImpl me) throws MessagingException {
284 if (broker.getContainer().isUseNewTransactionModel()) {
285 return;
286 }
287 try {
288 Transaction oldTx = me.getTransactionContext();
289 if (oldTx != null) {
290 TransactionManager tm = (TransactionManager) getBroker().getContainer().getTransactionManager();
291 if (tm != null) {
292 if (log.isDebugEnabled()) {
293 log.debug("Suspending transaction for " + me.getExchangeId() + " in " + this);
294 }
295 Transaction tx = tm.suspend();
296 if (tx != oldTx) {
297 throw new IllegalStateException("the transaction context set in "
298 + "the messageExchange is not bound to the current thread");
299 }
300 }
301 }
302 } catch (Exception e) {
303 throw new MessagingException(e);
304 }
305 }
306
307 protected void resumeTx(MessageExchangeImpl me) throws MessagingException {
308 if (broker.getContainer().isUseNewTransactionModel()) {
309 return;
310 }
311 try {
312 Transaction oldTx = me.getTransactionContext();
313 if (oldTx != null) {
314 TransactionManager tm = (TransactionManager) getBroker().getContainer().getTransactionManager();
315 if (tm != null) {
316 if (log.isDebugEnabled()) {
317 log.debug("Resuming transaction for " + me.getExchangeId() + " in " + this);
318 }
319 tm.resume(oldTx);
320 }
321 }
322 } catch (Exception e) {
323 throw new MessagingException(e);
324 }
325 }
326
327 }