/* * Copyright 2013-4 Red Hat Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.overlord.rtgov.ui.provider.switchyard; import static com.google.common.base.Strings.nullToEmpty; import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; import java.io.ByteArrayOutputStream; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.management.AttributeList; import javax.management.MBeanServerConnection; import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; import org.overlord.rtgov.active.collection.ActiveCollection; import org.overlord.rtgov.active.collection.ActiveCollectionManager; import org.overlord.rtgov.active.collection.ActiveCollectionManagerAccessor; import org.overlord.rtgov.active.collection.ActiveMap; import org.overlord.rtgov.analytics.service.ServiceDefinition; import org.overlord.rtgov.analytics.situation.Situation; import org.overlord.rtgov.common.util.RTGovProperties; import org.overlord.rtgov.service.dependency.ServiceDependencyBuilder; import org.overlord.rtgov.service.dependency.ServiceGraph; import org.overlord.rtgov.service.dependency.layout.ServiceGraphLayoutImpl; import org.overlord.rtgov.service.dependency.svg.SVGServiceGraphGenerator; import org.overlord.rtgov.ui.client.model.BindingBean; import org.overlord.rtgov.ui.client.model.QName; import org.overlord.rtgov.ui.client.model.ReferenceBean; import org.overlord.rtgov.ui.client.model.ReferenceSummaryBean; import org.overlord.rtgov.ui.client.model.ServiceBean; import org.overlord.rtgov.ui.client.model.ServiceSummaryBean; import org.overlord.rtgov.ui.client.model.ServicesFilterBean; import org.overlord.rtgov.ui.client.model.UiException; import org.overlord.rtgov.ui.provider.AbstractServicesProvider; import org.overlord.rtgov.ui.provider.ResubmitActionProvider; import com.google.common.base.Optional; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; /** * This class provides a SwitchYard implementation of the ServicesProvider * interface obtaining its information via JMX. * */ public class SwitchYardServicesProvider extends AbstractServicesProvider { protected static final String BINDING_TYPE_SCA = "sca"; private static final Logger LOG=Logger.getLogger(SwitchYardServicesProvider.class.getName()); private static volatile Messages i18n = new Messages(); private static final String PROVIDER_NAME = "switchyard"; // Properties private static final String SWITCHYARD_RESUBMIT_HANDLER_SERVER_URLS = "SwitchYardServicesProvider.serverURLs"; private static final String SWITCHYARD_JMX_URL = "SwitchYardServicesProvider.jmxURL"; private static final String SWITCHYARD_JMX_USERNAME = "SwitchYardServicesProvider.jmxUsername"; private static final String SWITCHYARD_JMX_PASSWORD = "SwitchYardServicesProvider.jmxPassword"; protected static final String DEFAULT_REMOTE_INVOKER_URL = "http://localhost:8080/switchyard-remote"; private String _serverURLs=null; private java.util.List<String> _urlList=new java.util.ArrayList<String>(); private MBeanServerConnection _mbeanServerConnection; private String _jmxUrl=null; private String _jmxUsername=null; private String _jmxPassword=null; private static final char ESCAPE_CHAR = '\\'; private static final char SEPARATOR_CHAR = ':'; /** * The constructor. */ public SwitchYardServicesProvider() { _serverURLs = RTGovProperties.getProperties().getProperty(SWITCHYARD_RESUBMIT_HANDLER_SERVER_URLS); _jmxUrl = RTGovProperties.getProperties().getProperty(SWITCHYARD_JMX_URL); _jmxUsername = RTGovProperties.getProperties().getProperty(SWITCHYARD_JMX_USERNAME); _jmxPassword = RTGovProperties.getProperties().getProperty(SWITCHYARD_JMX_PASSWORD); registerAction(ResubmitActionProvider.class, new SwitchYardResubmitActionProvider(this)); } /** * {@inheritDoc} */ public String getName() { return PROVIDER_NAME; } /** * {@inheritDoc} */ public boolean isServiceKnown(String service) { try { MBeanServerConnection mBeanServerConnection = getMBeanServerConnection(); mBeanServerConnection.getAttributes(new ObjectName( "org.switchyard.admin:type=Service,name=\"" + service + "\""), new String[] { "Bindings" }); } catch (Exception e) { return (false); } return (true); } protected Map<String, BindingBean> getReferenceBindings(String service) throws Exception { return getBindings("Reference", service); } protected Map<String, BindingBean> getServiceBindings(String service) throws Exception { return getBindings("Service", service); } protected Map<String, BindingBean> getBindings(String type, String service) throws Exception { Map<String, BindingBean> result = Maps.newHashMapWithExpectedSize(2); MBeanServerConnection mBeanServerConnection = getMBeanServerConnection(); AttributeList attributeList = mBeanServerConnection.getAttributes(new ObjectName("org.switchyard.admin:type=" + type + ",name=\"" + service + "\""), new String[] { "Bindings" }); ObjectName[] bindings = (ObjectName[]) getAttributeValue(attributeList.get(0)); if (bindings != null && bindings.length > 0) { for (int i = 0; i < bindings.length; i++) { ObjectName objectName = bindings[i]; attributeList = mBeanServerConnection.getAttributes(objectName, new String[] { "Type", "State" }); BindingBean bindingBean = new BindingBean(); bindingBean.setType(nullToEmpty((String) getAttributeValue(attributeList.get(0))).toUpperCase()); bindingBean.setState(nullToEmpty((String) getAttributeValue(attributeList.get(1))).toUpperCase()); result.put(bindingBean.getType(), bindingBean); } } return result; } /** * This method sets the comma separated list of SwitchYard server URLs. * * @param urls The server URLs */ public void setServerURLs(String urls) { synchronized (_urlList) { _serverURLs = urls; _urlList.clear(); } } /** * This method returns the comma separated list of SwitchYard server URLs. * * @return The server URLs */ public String getServerURLs() { return (_serverURLs); } /** * This method returns a list of URLs to use for a particular invocation. * If multiple URLs are available, the list will round robin to balance the * load - however if one URL fails, then the next one in the list will be * tried until successful or end of list reached. * * @return The list of URLs */ protected java.util.List<String> getURLList() { java.util.List<String> ret=null; synchronized (_urlList) { if (_urlList.size() == 0) { if (getServerURLs() != null && getServerURLs().trim().length() > 0) { String[] urls=getServerURLs().split("[, ]"); for (int i=0; i < urls.length; i++) { String url=urls[i].trim(); if (url.length() > 0) { _urlList.add(url); } } } else { _urlList.add(DEFAULT_REMOTE_INVOKER_URL); } } if (_urlList.size() == 1) { // Only one entry in the list, so just return it ret = _urlList; } else { ret = new java.util.ArrayList<String>(_urlList); Collections.rotate(_urlList, -1); } } return (ret); } /** * This method sets the JMX server URL. * * @param url The JMX server URL */ public void setJMXURL(String url) { _jmxUrl = url; } /** * This method returns the JMX server URL. * * @return The JMX server URL */ public String getJMXURL() { return (_jmxUrl); } /** * This method sets the JMX Username. * * @param username The JMX Username */ public void setJMXUsername(String username) { _jmxUsername = username; } /** * This method returns the JMX username. * * @return The JMX username */ public String getJMXUsername() { return (_jmxUsername); } /** * This method sets the JMX Password. * * @param password The JMX Password */ public void setJMXPassword(String password) { _jmxPassword = password; } /** * This method returns the JMX Password. * * @return The JMX Password */ public String getJMXPassword() { return (_jmxPassword); } /** * This method returns the mbean server connection. * * @return The MBean server connection */ protected synchronized MBeanServerConnection getMBeanServerConnection() throws UiException { if (_mbeanServerConnection == null) { if (LOG.isLoggable(Level.FINEST)) { LOG.finest("Creating JMX connector....."); } if (getJMXURL() == null) { if (LOG.isLoggable(Level.FINEST)) { LOG.finest("Creating JMX connector by accessing platform bean server"); } _mbeanServerConnection = ManagementFactory.getPlatformMBeanServer(); } else { try { JMXServiceURL url = new JMXServiceURL(getJMXURL()); java.util.Map<String, String[]> env = new java.util.HashMap<String, String[]>(); if (_jmxUsername != null && _jmxPassword != null) { if (LOG.isLoggable(Level.FINEST)) { LOG.finest("Creating JMX connector for user '"+_jmxUsername+"'"); } String[] creds = new String[2]; creds[0] = _jmxUsername; creds[1] = _jmxPassword; env.put(JMXConnector.CREDENTIALS, creds); } else if (LOG.isLoggable(Level.FINEST)) { LOG.finest("Creating JMX connector with no user credentials"); } JMXConnector jmxc = JMXConnectorFactory.connect(url, env); _mbeanServerConnection = jmxc.getMBeanServerConnection(); } catch (Exception e) { throw new UiException(i18n.format("SwitchYardServicesProvider.JMXConnectionFailed"), e); } if (LOG.isLoggable(Level.FINEST)) { LOG.finest("Created JMX connector: "+_mbeanServerConnection); } } } return (_mbeanServerConnection); } /** * {@inheritDoc} */ public List<QName> getApplicationNames() throws UiException { final List<QName> apps = new ArrayList<QName>(); try { MBeanServerConnection con=getMBeanServerConnection(); java.util.Set<ObjectInstance> results= con.queryMBeans(new ObjectName("org.switchyard.admin:type=Application,name=*"), null); for (ObjectInstance result : results) { java.util.Map<String,String> map=result.getObjectName().getKeyPropertyList(); if (map.containsKey("name")) { String name=result.getObjectName().getKeyProperty("name"); apps.add(parseQName(stripQuotes(name))); } } } catch (Exception e) { throw new UiException(i18n.format("SwitchYardServicesProvider.AppNamesFailed"), e); } return apps; } /** * {@inheritDoc} */ public java.util.List<ServiceSummaryBean> findServices(final ServicesFilterBean filters) throws UiException { final ArrayList<ServiceSummaryBean> services = new ArrayList<ServiceSummaryBean>(); try { MBeanServerConnection con=getMBeanServerConnection(); java.util.Set<ObjectInstance> results= con.queryMBeans(new ObjectName("org.switchyard.admin:type=Service,name=*"), null); // TODO: Request all attributes in one operation for (ObjectInstance result : results) { java.util.Map<String, String> map = result.getObjectName().getKeyPropertyList(); if (map.containsKey("name")) { AttributeList attrs = con.getAttributes(result.getObjectName(), new String[] { "Name", "Application", "Interface" }); String name = (String) getAttributeValue(attrs.get(0)); if (!isSet(filters.getServiceName()) || filters.getServiceName().equals(name)) { ObjectName app = (ObjectName) getAttributeValue(attrs.get(1)); String appName = stripQuotes(app.getKeyProperty("name")); if (!isSet(filters.getApplicationName()) || filters.getApplicationName().equals(appName)) { ServiceSummaryBean ssb = new ServiceSummaryBean(); ssb.setName(name); ssb.setApplication(appName); ssb.setIface((String) getAttributeValue(attrs.get(2))); ssb.setBindings(Sets.newHashSet(getServiceBindings(name).values())); ssb.setServiceId(generateId(appName, name)); services.add(ssb); } } } } } catch (Exception e) { throw new UiException(i18n.format("SwitchYardServicesProvider.GetServicesFailed"), e); } return services; } protected Object getAttributeValue(Object attr) { if (attr instanceof javax.management.Attribute) { return (((javax.management.Attribute)attr).getValue()); } return (attr); } protected String stripQuotes(String text) { if (text.length() >= 2 && text.charAt(0) == '\"' && text.charAt(text.length()-1) == '\"') { return (text.substring(1, text.length()-1)); } return (text); } /** * This method returns the list of references associated with the supplied application * and service. * * @param applicationName The application * @param serviceName The service name * @return The list of references * @throws UiException Failed to get the references */ protected List<ReferenceSummaryBean> getReferences(final String applicationName, final String serviceName) throws UiException { final List<ReferenceSummaryBean> references = new ArrayList<ReferenceSummaryBean>(); try { MBeanServerConnection con=getMBeanServerConnection(); // TODO: Request all attributes in one operation java.util.Set<ObjectInstance> results= con.queryMBeans(new ObjectName("org.switchyard.admin:type=Reference,name=*"), null); for (ObjectInstance result : results) { AttributeList attrs=con.getAttributes(result.getObjectName(), new String[]{"Name", "Application", "Interface"}); String name=(String)getAttributeValue(attrs.get(0)); ReferenceSummaryBean rsb=new ReferenceSummaryBean(); rsb.setName(name); ObjectName app=(ObjectName)getAttributeValue(attrs.get(1)); String appName=stripQuotes(app.getKeyProperty("name")); if (isSet(applicationName) || applicationName.equals(appName)) { rsb.setApplication(appName); rsb.setBindings(Sets.newHashSet(getReferenceBindings(name).values())); rsb.setIface((String)getAttributeValue(attrs.get(2))); rsb.setReferenceId(generateId(appName, name)); references.add(rsb); } } } catch (Exception e) { throw new UiException(i18n.format("SwitchYardServicesProvider.GetReferencesFailed", applicationName, serviceName), e); } return references; } /** * {@inheritDoc} */ public ServiceBean getService(final String uuid) throws UiException { final ServiceBean serviceResult = new ServiceBean(); final List<String> ids = parseId(uuid); if (ids.size() == 2) { final String applicationName = ids.get(0); final String serviceName = ids.get(1); // TODO: Request all attributes in one operation try { MBeanServerConnection con=getMBeanServerConnection(); ObjectInstance instance=con.getObjectInstance( new ObjectName("org.switchyard.admin:type=Service,name=\""+serviceName+"\"")); serviceResult.setName(parseQName(serviceName)); serviceResult.setApplication(parseQName(applicationName)); AttributeList attrs=con.getAttributes(instance.getObjectName(), new String[]{"Interface"}); serviceResult.setServiceInterface((String)getAttributeValue(attrs.get(0))); serviceResult.setServiceId(uuid); serviceResult.setReferences(getReferences(applicationName, serviceName)); //ObjectName app=(ObjectName)con.getAttribute(result.getObjectName(), "Application"); //String appName=stripQuotes(app.getKeyProperty("name")); serviceResult.setServiceGraph(buildGraph(serviceName)); } catch (Exception e) { throw new UiException(i18n.format("SwitchYardServicesProvider.GetServiceFailed", applicationName, serviceName), e); } } return serviceResult; } private String buildGraph(String serviceName) throws Exception { ActiveCollectionManager activeCollectionManager = ActiveCollectionManagerAccessor.getActiveCollectionManager(); ActiveCollection activeCollection = activeCollectionManager.getActiveCollection("ServiceDefinitions"); ActiveCollection activeSituations = activeCollectionManager.getActiveCollection("Situations"); Set<ServiceDefinition> serviceDefinitions = Sets.newHashSet(); List<Situation> situations = Lists.newArrayList(); for (Object entry : Optional.<Iterable<?>> fromNullable(activeCollection).or(emptySet())) { if (entry instanceof ActiveMap.Entry && ((ActiveMap.Entry) entry).getValue() instanceof ServiceDefinition) { serviceDefinitions.add((ServiceDefinition) ((ActiveMap.Entry) entry).getValue()); } } for (Object obj : Optional.<Iterable<?>> fromNullable(activeSituations).or(emptyList())) { if (obj instanceof Situation) { situations.add((Situation) obj); } } ServiceGraph serviceGraph = ServiceDependencyBuilder.buildGraph(serviceDefinitions, situations, serviceName); if (serviceGraph == null) { throw new Exception("Failed to generate service dependency overview"); } serviceGraph.setDescription("Generated: " + new Date()); ServiceGraphLayoutImpl serviceGraphLayout = new ServiceGraphLayoutImpl(); serviceGraphLayout.layout(serviceGraph); SVGServiceGraphGenerator serviceGraphGenerator = new SVGServiceGraphGenerator(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); serviceGraphGenerator.generate(serviceGraph, 0, byteArrayOutputStream); byteArrayOutputStream.close(); return new String(byteArrayOutputStream.toByteArray()); } /** * {@inheritDoc} */ public ReferenceBean getReference(final String uuid) throws UiException { final ReferenceBean referenceResult = new ReferenceBean(); final List<String> ids = parseId(uuid); if (ids.size() == 2) { final String applicationName = ids.get(0); final String referenceName = ids.get(1); // TODO: Request all attributes in one operation try { MBeanServerConnection con=getMBeanServerConnection(); ObjectInstance instance=con.getObjectInstance( new ObjectName("org.switchyard.admin:type=Reference,name=\""+referenceName+"\"")); referenceResult.setName(parseQName(referenceName)); referenceResult.setApplication(parseQName(applicationName)); AttributeList attrs=con.getAttributes(instance.getObjectName(), new String[]{"Interface"}); referenceResult.setReferenceInterface((String)getAttributeValue(attrs.get(0))); referenceResult.setReferenceId(uuid); } catch (Exception e) { throw new UiException(i18n.format("SwitchYardServicesProvider.GetReferenceFailed", applicationName, referenceName), e); } } return referenceResult; } private static QName parseQName(final String value) { final javax.xml.namespace.QName qname = javax.xml.namespace.QName.valueOf(value); return new QName(qname.getNamespaceURI(), qname.getLocalPart()); } private static boolean isSet(final String name) { return ((name != null) && (name.trim().length() > 0)); } public static String generateId(final String application, final String name) { return escape(application) + ':' + escape(name); } private static List<String> parseId(final String id) { if (id == null) { return null; } final List<String> ids = new ArrayList<String>(); final StringBuilder unescaped = new StringBuilder(); final int length = id.length(); for(int count = 0 ; count < length ; count++) { final char ch = id.charAt(count); switch (ch) { case ESCAPE_CHAR: count++; if (count < length) { unescaped.append(id.charAt(count)); } break; case SEPARATOR_CHAR: ids.add(unescaped.toString()); unescaped.setLength(0); break; default: unescaped.append(ch); } } ids.add(unescaped.toString()); return ids; } private static String escape(final String val) { if (val == null) { return null; } final StringBuilder escaped = new StringBuilder(); final int length = val.length(); for(int count = 0 ; count < length ; count++) { final char ch = val.charAt(count); switch (ch) { case ESCAPE_CHAR: case SEPARATOR_CHAR: escaped.append(ESCAPE_CHAR); default: escaped.append(ch); } } return escaped.toString(); } }