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    }