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.framework;
018
019 import java.beans.PropertyChangeEvent;
020 import java.beans.PropertyChangeListener;
021 import java.io.File;
022 import java.io.IOException;
023 import java.io.StringReader;
024 import java.util.ArrayList;
025 import java.util.List;
026 import java.util.Properties;
027
028 import javax.jbi.JBIException;
029 import javax.jbi.management.DeploymentException;
030 import javax.management.JMException;
031 import javax.management.MBeanAttributeInfo;
032 import javax.management.MBeanOperationInfo;
033 import javax.management.ObjectName;
034 import javax.xml.namespace.QName;
035 import javax.xml.parsers.DocumentBuilder;
036 import javax.xml.parsers.DocumentBuilderFactory;
037 import javax.xml.parsers.ParserConfigurationException;
038
039 import org.w3c.dom.Document;
040 import org.w3c.dom.Element;
041 import org.w3c.dom.NodeList;
042
043 import org.xml.sax.InputSource;
044 import org.xml.sax.SAXException;
045
046 import org.apache.commons.logging.Log;
047 import org.apache.commons.logging.LogFactory;
048 import org.apache.servicemix.jbi.container.ServiceAssemblyEnvironment;
049 import org.apache.servicemix.jbi.deployment.Connection;
050 import org.apache.servicemix.jbi.deployment.Consumes;
051 import org.apache.servicemix.jbi.deployment.DescriptorFactory;
052 import org.apache.servicemix.jbi.deployment.ServiceAssembly;
053 import org.apache.servicemix.jbi.deployment.Services;
054 import org.apache.servicemix.jbi.event.ServiceAssemblyEvent;
055 import org.apache.servicemix.jbi.event.ServiceAssemblyListener;
056 import org.apache.servicemix.jbi.management.AttributeInfoHelper;
057 import org.apache.servicemix.jbi.management.MBeanInfoProvider;
058 import org.apache.servicemix.jbi.management.OperationInfoHelper;
059 import org.apache.servicemix.jbi.util.XmlPersistenceSupport;
060
061 /**
062 * ComponentConnector is used internally for message routing
063 *
064 * @version $Revision: 564900 $
065 */
066 public class ServiceAssemblyLifeCycle implements ServiceAssemblyMBean, MBeanInfoProvider {
067
068 private static final Log LOG = LogFactory.getLog(ServiceAssemblyLifeCycle.class);
069
070 private ServiceAssembly serviceAssembly;
071
072 private String currentState = SHUTDOWN;
073
074 private ServiceUnitLifeCycle[] sus;
075
076 private Registry registry;
077
078 private PropertyChangeListener listener;
079
080 private ServiceAssemblyEnvironment env;
081
082 /**
083 * Construct a LifeCycle
084 *
085 * @param sa
086 * @param stateFile
087 */
088 public ServiceAssemblyLifeCycle(ServiceAssembly sa,
089 ServiceAssemblyEnvironment env,
090 Registry registry) {
091 this.serviceAssembly = sa;
092 this.env = env;
093 this.registry = registry;
094 }
095
096 protected void setServiceUnits(ServiceUnitLifeCycle[] serviceUnits) {
097 this.sus = serviceUnits;
098 }
099
100 /**
101 * Start a Service Assembly and put it in the STARTED state.
102 *
103 * @return Result/Status of this operation.
104 * @throws Exception
105 */
106 public String start() throws Exception {
107 return start(true);
108 }
109
110 public synchronized String start(boolean writeState) throws Exception {
111 LOG.info("Starting service assembly: " + getName());
112 // Start connections
113 try {
114 startConnections();
115 } catch (JBIException e) {
116 throw ManagementSupport.failure("start", e.getMessage());
117 }
118 // Start service units
119 List<Element> componentFailures = new ArrayList<Element>();
120 for (int i = 0; i < sus.length; i++) {
121 if (sus[i].isShutDown()) {
122 try {
123 sus[i].init();
124 } catch (DeploymentException e) {
125 componentFailures.add(getComponentFailure(e, "start", sus[i].getComponentName()));
126 }
127 }
128 }
129 for (int i = 0; i < sus.length; i++) {
130 if (sus[i].isStopped()) {
131 try {
132 sus[i].start();
133 } catch (DeploymentException e) {
134 componentFailures.add(getComponentFailure(e, "start", sus[i].getComponentName()));
135 }
136 }
137 }
138 if (componentFailures.size() == 0) {
139 currentState = STARTED;
140 if (writeState) {
141 writeRunningState();
142 }
143 fireEvent(ServiceAssemblyEvent.ASSEMBLY_STARTED);
144 return ManagementSupport.createSuccessMessage("start");
145 } else {
146 throw ManagementSupport.failure("start", componentFailures);
147 }
148 }
149
150 /**
151 * Stops the service assembly and puts it in STOPPED state.
152 *
153 * @return Result/Status of this operation.
154 * @throws Exception
155 */
156 public String stop() throws Exception {
157 return stop(true, false);
158 }
159
160 public synchronized String stop(boolean writeState, boolean forceInit) throws Exception {
161 LOG.info("Stopping service assembly: " + getName());
162 // Stop connections
163 stopConnections();
164 // Stop service units
165 List<Element> componentFailures = new ArrayList<Element>();
166 if (forceInit) {
167 for (int i = 0; i < sus.length; i++) {
168 try {
169 sus[i].init();
170 } catch (DeploymentException e) {
171 componentFailures.add(getComponentFailure(e, "stop", sus[i].getComponentName()));
172 }
173 }
174 }
175 for (int i = 0; i < sus.length; i++) {
176 if (sus[i].isStarted()) {
177 try {
178 sus[i].stop();
179 } catch (DeploymentException e) {
180 componentFailures.add(getComponentFailure(e, "stop", sus[i].getComponentName()));
181 }
182 }
183 }
184 if (componentFailures.size() == 0) {
185 currentState = STOPPED;
186 if (writeState) {
187 writeRunningState();
188 }
189 fireEvent(ServiceAssemblyEvent.ASSEMBLY_STOPPED);
190 return ManagementSupport.createSuccessMessage("stop");
191 } else {
192 throw ManagementSupport.failure("stop", componentFailures);
193 }
194 }
195
196 /**
197 * Shutdown the service assembly and puts it in SHUTDOWN state.
198 *
199 * @return Result/Status of this operation.
200 * @throws Exception
201 */
202 public String shutDown() throws Exception {
203 return shutDown(true);
204 }
205
206 public synchronized String shutDown(boolean writeState) throws Exception {
207 LOG.info("Shutting down service assembly: " + getName());
208 List<Element> componentFailures = new ArrayList<Element>();
209 for (int i = 0; i < sus.length; i++) {
210 if (sus[i].isStarted()) {
211 try {
212 sus[i].stop();
213 } catch (DeploymentException e) {
214 componentFailures.add(getComponentFailure(e, "shutDown", sus[i].getComponentName()));
215 }
216 }
217 }
218 for (int i = 0; i < sus.length; i++) {
219 if (sus[i].isStopped()) {
220 try {
221 sus[i].shutDown();
222 } catch (DeploymentException e) {
223 componentFailures.add(getComponentFailure(e, "shutDown", sus[i].getComponentName()));
224 }
225 }
226 }
227 if (componentFailures.size() == 0) {
228 currentState = SHUTDOWN;
229 if (writeState) {
230 writeRunningState();
231 }
232 fireEvent(ServiceAssemblyEvent.ASSEMBLY_SHUTDOWN);
233 return ManagementSupport.createSuccessMessage("shutDown");
234 } else {
235 throw ManagementSupport.failure("shutDown", componentFailures);
236 }
237 }
238
239 /**
240 * @return the currentState as a String
241 */
242 public String getCurrentState() {
243 return currentState;
244 }
245
246 boolean isShutDown() {
247 return currentState.equals(SHUTDOWN);
248 }
249
250 boolean isStopped() {
251 return currentState.equals(STOPPED);
252 }
253
254 boolean isStarted() {
255 return currentState.equals(STARTED);
256 }
257
258 /**
259 * @return the name of the ServiceAssembly
260 */
261 public String getName() {
262 return serviceAssembly.getIdentification().getName();
263 }
264
265 /**
266 *
267 * @return the description of the ServiceAssembly
268 */
269 public String getDescription() {
270 return serviceAssembly.getIdentification().getDescription();
271 }
272
273 /**
274 * @return the ServiceAssembly
275 */
276 public ServiceAssembly getServiceAssembly() {
277 return serviceAssembly;
278 }
279
280 public String getDescriptor() {
281 File saDir = env.getInstallDir();
282 return DescriptorFactory.getDescriptorAsText(saDir);
283 }
284
285 /**
286 * @return string representation of this
287 */
288 public String toString() {
289 return "ServiceAssemblyLifeCycle[name=" + getName() + ",state=" + getCurrentState() + "]";
290 }
291
292 /**
293 * write the current running state of the Component to disk
294 */
295 void writeRunningState() {
296 try {
297 if (env.getStateFile() != null) {
298 String state = getCurrentState();
299 Properties props = new Properties();
300 props.setProperty("state", state);
301 XmlPersistenceSupport.write(env.getStateFile(), props);
302 }
303 } catch (IOException e) {
304 LOG.error("Failed to write current running state for ServiceAssembly: " + getName(), e);
305 }
306 }
307
308 /**
309 * get the current running state from disk
310 */
311 String getRunningStateFromStore() {
312 try {
313 if (env.getStateFile() != null && env.getStateFile().exists()) {
314 Properties props = (Properties) XmlPersistenceSupport.read(env.getStateFile());
315 return props.getProperty("state", SHUTDOWN);
316 }
317 } catch (Exception e) {
318 LOG.error("Failed to read current running state for ServiceAssembly: " + getName(), e);
319 }
320 return null;
321 }
322
323 /**
324 * Restore this service assembly to its state at shutdown.
325 * @throws Exception
326 */
327 public synchronized void restore() throws Exception {
328 String state = getRunningStateFromStore();
329 if (STARTED.equals(state)) {
330 start(false);
331 } else {
332 stop(false, true);
333 if (SHUTDOWN.equals(state)) {
334 shutDown(false);
335 }
336 }
337 }
338
339 public ServiceUnitLifeCycle[] getDeployedSUs() {
340 return sus;
341 }
342
343 protected void startConnections() throws JBIException {
344 if (serviceAssembly.getConnections() == null
345 || serviceAssembly.getConnections().getConnections() == null) {
346 return;
347 }
348 Connection[] connections = serviceAssembly.getConnections().getConnections();
349 for (int i = 0; i < connections.length; i++) {
350 if (connections[i].getConsumer().getInterfaceName() != null) {
351 QName fromItf = connections[i].getConsumer().getInterfaceName();
352 QName toSvc = connections[i].getProvider().getServiceName();
353 String toEp = connections[i].getProvider().getEndpointName();
354 registry.registerInterfaceConnection(fromItf, toSvc, toEp);
355 } else {
356 QName fromSvc = connections[i].getConsumer().getServiceName();
357 String fromEp = connections[i].getConsumer().getEndpointName();
358 QName toSvc = connections[i].getProvider().getServiceName();
359 String toEp = connections[i].getProvider().getEndpointName();
360 String link = getLinkType(fromSvc, fromEp);
361 registry.registerEndpointConnection(fromSvc, fromEp, toSvc, toEp, link);
362 }
363 }
364 }
365
366 protected String getLinkType(QName svc, String ep) {
367 for (int i = 0; i < sus.length; i++) {
368 Services s = sus[i].getServices();
369 if (s != null && s.getConsumes() != null) {
370 Consumes[] consumes = s.getConsumes();
371 for (int j = 0; j < consumes.length; j++) {
372 if (svc.equals(consumes[j].getServiceName())
373 && ep.equals(consumes[j].getEndpointName())) {
374 return consumes[j].getLinkType();
375 }
376 }
377 }
378 }
379 return null;
380 }
381
382 protected void stopConnections() {
383 if (serviceAssembly.getConnections() == null
384 || serviceAssembly.getConnections().getConnections() == null) {
385 return;
386 }
387 Connection[] connections = serviceAssembly.getConnections().getConnections();
388 for (int i = 0; i < connections.length; i++) {
389 if (connections[i].getConsumer().getInterfaceName() != null) {
390 QName fromItf = connections[i].getConsumer().getInterfaceName();
391 registry.unregisterInterfaceConnection(fromItf);
392 } else {
393 QName fromSvc = connections[i].getConsumer().getServiceName();
394 String fromEp = connections[i].getConsumer().getEndpointName();
395 registry.unregisterEndpointConnection(fromSvc, fromEp);
396 }
397 }
398 }
399
400 protected Element getComponentFailure(Exception exception, String task, String component) {
401 Element result = null;
402 String resultMsg = exception.getMessage();
403 try {
404 Document doc = parse(resultMsg);
405 result = getElement(doc, "component-task-result");
406 } catch (Exception e) {
407 LOG.warn("Could not parse result exception", e);
408 }
409 if (result == null) {
410 result = ManagementSupport.createComponentFailure(
411 task, component,
412 "Unable to parse result string", exception);
413 }
414 return result;
415 }
416
417 protected Document parse(String result) throws ParserConfigurationException, SAXException, IOException {
418 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
419 factory.setNamespaceAware(true);
420 factory.setIgnoringElementContentWhitespace(true);
421 factory.setIgnoringComments(true);
422 DocumentBuilder builder = factory.newDocumentBuilder();
423 return builder.parse(new InputSource(new StringReader(result)));
424 }
425
426 protected Element getElement(Document doc, String name) {
427 NodeList l = doc.getElementsByTagNameNS("http://java.sun.com/xml/ns/jbi/management-message", name);
428 return (Element) l.item(0);
429 }
430
431 public MBeanAttributeInfo[] getAttributeInfos() throws JMException {
432 AttributeInfoHelper helper = new AttributeInfoHelper();
433 helper.addAttribute(getObjectToManage(), "currentState", "current state of the assembly");
434 helper.addAttribute(getObjectToManage(), "name", "name of the assembly");
435 helper.addAttribute(getObjectToManage(), "description", "description of the assembly");
436 helper.addAttribute(getObjectToManage(), "serviceUnits", "list of service units contained in this assembly");
437 return helper.getAttributeInfos();
438 }
439
440 public MBeanOperationInfo[] getOperationInfos() throws JMException {
441 OperationInfoHelper helper = new OperationInfoHelper();
442 helper.addOperation(getObjectToManage(), "start", "start the assembly");
443 helper.addOperation(getObjectToManage(), "stop", "stop the assembly");
444 helper.addOperation(getObjectToManage(), "shutDown", "shutdown the assembly");
445 helper.addOperation(getObjectToManage(), "getDescriptor", "retrieve the jbi descriptor for this assembly");
446 return helper.getOperationInfos();
447 }
448
449 public Object getObjectToManage() {
450 return this;
451 }
452
453 public String getType() {
454 return "ServiceAssembly";
455 }
456
457 public String getSubType() {
458 return null;
459 }
460
461 public void setPropertyChangeListener(PropertyChangeListener l) {
462 this.listener = l;
463 }
464
465 protected void firePropertyChanged(String name, Object oldValue, Object newValue) {
466 PropertyChangeListener l = listener;
467 if (l != null) {
468 PropertyChangeEvent event = new PropertyChangeEvent(this, name, oldValue, newValue);
469 l.propertyChange(event);
470 }
471 }
472
473 public ObjectName[] getServiceUnits() {
474 ObjectName[] names = new ObjectName[sus.length];
475 for (int i = 0; i < names.length; i++) {
476 names[i] = registry.getContainer().getManagementContext().createObjectName(sus[i]);
477 }
478 return names;
479 }
480
481 public ServiceAssemblyEnvironment getEnvironment() {
482 return env;
483 }
484
485 protected void fireEvent(int type) {
486 ServiceAssemblyEvent event = new ServiceAssemblyEvent(this, type);
487 ServiceAssemblyListener[] listeners =
488 (ServiceAssemblyListener[]) registry.getContainer().getListeners(ServiceAssemblyListener.class);
489 for (int i = 0; i < listeners.length; i++) {
490 switch (type) {
491 case ServiceAssemblyEvent.ASSEMBLY_STARTED:
492 listeners[i].assemblyStarted(event);
493 break;
494 case ServiceAssemblyEvent.ASSEMBLY_STOPPED:
495 listeners[i].assemblyStopped(event);
496 break;
497 case ServiceAssemblyEvent.ASSEMBLY_SHUTDOWN:
498 listeners[i].assemblyShutDown(event);
499 break;
500 default:
501 break;
502 }
503 }
504
505 }
506
507 }