/* * Copyright to the original author or authors. * * 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.rioproject.impl.container; import net.jini.admin.Administrable; import net.jini.admin.JoinAdmin; import net.jini.config.Configuration; import net.jini.config.ConfigurationException; import net.jini.core.discovery.LookupLocator; import net.jini.core.entry.Entry; import net.jini.discovery.LookupDiscovery; import net.jini.lookup.entry.Host; import org.rioproject.admin.ServiceBeanControl; import org.rioproject.admin.ServiceBeanControlException; import org.rioproject.config.Constants; import org.rioproject.entry.OperationalStringEntry; import org.rioproject.impl.client.JiniClient; import org.rioproject.impl.jmx.JMXUtil; import org.rioproject.impl.servicebean.ServiceBeanActivation; import org.rioproject.servicebean.ServiceBeanContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * The ServiceAdvertiser is a utility to help advertise a service with configured attributes. */ public class ServiceAdvertiser { static Logger logger = LoggerFactory.getLogger(ServiceAdvertiser.class.getName()); /** * Advertise a ServiceBean * * @param serviceProxy Proxy to the service * @param context The ServiceBeanContext * @throws ServiceBeanControlException If the service bean cannot be advertised */ public static void advertise(final Object serviceProxy, final ServiceBeanContext context, final boolean forked) throws ServiceBeanControlException { if (serviceProxy == null) throw new IllegalArgumentException("serviceProxy is null"); if (context == null) throw new IllegalArgumentException("context is null"); String hostAddress = context.getComputeResourceManager().getComputeResource().getAddress().getHostAddress(); Configuration config; try { config = context.getConfiguration(); } catch (ConfigurationException e) { throw new ServiceBeanControlException("Unable to obtain configuration for service " + "[" + context.getServiceElement().getName() + "]", e); } String serviceName = context.getServiceElement().getName(); String opStringName = context.getServiceElement().getOperationalStringName(); String[] groups = context.getServiceElement().getServiceBeanConfig().getGroups(); List<LookupLocator> lookupLocators = new ArrayList<LookupLocator>(); if(context.getServiceElement().getServiceBeanConfig().getLocators()!=null) { Collections.addAll(lookupLocators, context.getServiceElement().getServiceBeanConfig().getLocators()); } String globalLocators = System.getProperty(Constants.LOCATOR_PROPERTY_NAME); if(globalLocators!=null) { try { Collections.addAll(lookupLocators, JiniClient.parseLocators(globalLocators)); } catch (MalformedURLException e) { logger.warn("Configured LookupLocators [{}] are malformed", globalLocators, e); } } String componentName = getServiceComponentName(context); final Thread currentThread = Thread.currentThread(); ClassLoader currentClassLoader = currentThread.getContextClassLoader(); try { ClassLoader proxyCL = serviceProxy.getClass().getClassLoader(); currentThread.setContextClassLoader(proxyCL); if (serviceProxy instanceof Administrable) { Administrable admin = (Administrable) serviceProxy; Object adminObject = admin.getAdmin(); if (adminObject instanceof ServiceBeanControl) { ServiceBeanControl controller = (ServiceBeanControl) adminObject; controller.advertise(); /* Additional attributes are ignored here, they are obtained * by the ServiceBeanAdmin.advertise() method */ } else if (adminObject instanceof JoinAdmin) { Entry[] configuredAttributes = getConfiguredAttributes(componentName, config, serviceName, context.getExportCodebase()); JoinAdmin joinAdmin = (JoinAdmin) adminObject; ArrayList<Entry> addList = new ArrayList<Entry>(); logger.trace("OperationalString {}", opStringName); /* Try and add an OperationalStringEntry */ if (opStringName != null && opStringName.length() > 0) { Entry opStringEntry = loadEntry("org.rioproject.entry.OperationalStringEntry", joinAdmin, opStringName, proxyCL); if (opStringEntry != null) { addList.add(opStringEntry); logger.debug("Added OperationalStringEntry [{}] for {}", ((OperationalStringEntry)opStringEntry).name, serviceName); } else { logger.warn("Unable to obtain the OperationalStringEntry for {}", serviceName); } } else { logger.trace("OperationalString name is {}", (opStringName == null ? "[null]" : "[empty string]")); } /* Next, try and add the net.jini.lookup.entry.Host */ Entry hostEntry = loadEntry("net.jini.lookup.entry.Host", joinAdmin, hostAddress, proxyCL); if (hostEntry != null) { addList.add(hostEntry); logger.debug("Added Host [{}] for {}", ((Host)hostEntry).hostName, serviceName); } else { logger.warn("Unable to obtain the Host entry for {}", serviceName); } Entry infoEntry = loadServiceInfo(serviceName, "", proxyCL); if (infoEntry != null) { addList.add(infoEntry); logger.debug("Added ServiceInfo for {}", serviceName); } else { logger.warn("Unable to obtain the Host entry for {}", serviceName); } /* Process the net.jini.lookup.entry.Name attribute */ try { Class<?> nameClass = proxyCL.loadClass("net.jini.lookup.entry.Name"); Constructor cons = nameClass.getConstructor(String.class); Entry name = (Entry) cons.newInstance(serviceName); boolean add = true; /* Check if the service already has a Name, if it does * ensure it is not the same name as the one this * utility is prepared to add */ Entry[] attributes = joinAdmin.getLookupAttributes(); for (Entry attribute : attributes) { if (attribute.getClass().getName().equals(nameClass.getName())) { Field n = attribute.getClass().getDeclaredField("name"); String value = (String) n.get(attribute); if (value.equals(serviceName)) add = false; break; } } if (add) addList.add(name); } catch (Exception e) { logger.warn("Name not found, cannot add a Name Entry", e); } /* If running forked, add JMX connection entries */ if(logger.isTraceEnabled()) logger.trace("Service: {}, forked? {}", serviceName, forked); if(forked) { Collections.addAll(addList, JMXUtil.getJMXConnectionEntries()); } addList.addAll(context.getServiceBeanConfig().getAdditionalEntries()); /* If any additional attributes (including the * OperationalString and Name entry already processed) are * passed in, include them as well */ addList.addAll(Arrays.asList(configuredAttributes)); /* If we have Entry objects to add, add them */ if (!addList.isEmpty()) { Entry[] adds = addList.toArray(new Entry[addList.size()]); addAttributes(adds, joinAdmin); } /* Apply groups to the JoinAdmin */ if (groups == null || groups.length == 0) groups = LookupDiscovery.NO_GROUPS; if (groups != null && groups.length > 0) { if (groups.length == 1 && groups[0].equals("all")) { groups = LookupDiscovery.ALL_GROUPS; } else { for (int i = 0; i < groups.length; i++) { if (groups[i].equals("public")) groups[i] = ""; } } } if (logger.isTraceEnabled()) { StringBuilder buff = new StringBuilder(); if (groups == null || groups.length == 0) { buff.append("LookupDiscovery.NO_GROUPS"); } else { for (int i = 0; i < groups.length; i++) { if (i > 0) buff.append(","); buff.append(groups[i]); } } logger.trace("Setting groups [{}] using JoinAdmin.setLookupGroups", buff.toString()); } joinAdmin.setLookupGroups(groups); if (!lookupLocators.isEmpty()) { if (logger.isTraceEnabled()) { StringBuilder buff = new StringBuilder(); for (LookupLocator lookupLocator : lookupLocators) { if (buff.length()>0 ) buff.append(","); buff.append(lookupLocator.toString()); } logger.trace("Setting locators [{}] using JoinAdmin.setLookupLocators", buff.toString()); } joinAdmin.setLookupLocators(lookupLocators.toArray(new LookupLocator[lookupLocators.size()])); } } else { logger.error("Admin must implement JoinAdmin or ServiceBeanControl to be properly advertised"); } } else { throw new ServiceBeanControlException(String.format("Unable to obtain mechanism to advertise [%s]", serviceName)); } } catch (ServiceBeanControlException e) { /* If we throw a ServiceBeanControlException above, just rethrow it */ throw e; } catch (Throwable t) { logger.warn("Advertising ServiceBean, [{}: {}]", t.getClass().getName(), t.getLocalizedMessage()); throw new ServiceBeanControlException("advertise", t); } finally { currentThread.setContextClassLoader(currentClassLoader); } } /** * Get the configuration component name from a ServiceBeanContext. * * @param context The ServiceBeanContext to use. * @return The configuration component name. */ private static String getServiceComponentName(ServiceBeanContext context) { String serviceBeanComponent; String className = null; if (context.getServiceElement().getComponentBundle() == null) { serviceBeanComponent = (String) context.getInitParameter(ServiceBeanActivation.BOOT_CONFIG_COMPONENT); } else { if (context.getServiceElement().getComponentBundle() != null) className = context.getServiceElement().getComponentBundle().getClassName(); if (className == null) className = context.getServiceElement().getExportBundles()[0].getClassName(); if (className.indexOf(".") > 0) { int index = className.lastIndexOf("."); serviceBeanComponent = className.substring(0, index); } else { serviceBeanComponent = className; } } return serviceBeanComponent; } /** * Get configuration defined attributes * * @param context The ServiceBeanContext. Must not be null. * * @return An array of configured Entry attributes from the ServiceBeanContext */ public static Entry[] getConfiguredAttributes(ServiceBeanContext context) { Configuration config; try { config = context.getConfiguration(); } catch (ConfigurationException e) { logger.warn("Unable to obtain configuration for service [{}]", context.getServiceElement().getName(), e); return new Entry[0]; } ArrayList<Entry> attrList = new ArrayList<Entry>(); Entry[] configuredAttributes = getConfiguredAttributes(getServiceComponentName(context), config, context.getServiceElement().getName(), context.getExportCodebase()); Collections.addAll(attrList, configuredAttributes); return(attrList.toArray(new Entry[attrList.size()])); } /** * Get configuration defined attributes * * @param serviceBeanComponent The configuration component to use when accessing the configuration. * Must not be null. * @param config The Configuration. Must not be null. * @param serviceName The name of the service, used for logging. Must not be null. * @param exportCodebase The codebase the service is using, may be null * * @return An array of configured Entry attributes from the ServiceBeanContext */ private static Entry[] getConfiguredAttributes(String serviceBeanComponent, Configuration config, String serviceName, String exportCodebase) { ArrayList<Entry> attrList = new ArrayList<Entry>(); if(serviceBeanComponent!=null) { try { /* 1. Get any configured ServiceUIs */ Entry[] serviceUIs = (Entry[])config.getEntry(serviceBeanComponent, "serviceUIs", Entry[].class, new Entry[0], exportCodebase == null ? Configuration.NO_DATA : exportCodebase); logger.trace("Obtained [{}] serviceUI declarations for [{}] using component [{}]", serviceUIs.length, serviceName, serviceBeanComponent); attrList.addAll(Arrays.asList(serviceUIs)); } catch (ConfigurationException e) { logger.warn("Getting ServiceUIs for [{}]", serviceName, e); } /* 2. Get any additional attributes */ try { logger.trace("Getting {}.initialAttributes", serviceBeanComponent); Entry[] initialAttributes = (Entry[])config.getEntry(serviceBeanComponent, "initialAttributes", Entry[].class, new Entry[0]); logger.trace("Obtained [{}] initialAttribute declarations for [{}] using component [{}]", initialAttributes.length, serviceName, serviceBeanComponent); attrList.addAll(Arrays.asList(initialAttributes)); } catch (ConfigurationException e) { logger.warn("Getting initialAttributes for [{}]", serviceName, e); } } return(attrList.toArray(new Entry[attrList.size()])); } /* * Add an entry */ private static Entry loadEntry(String entryClassName, JoinAdmin joinAdmin, String value, ClassLoader loader) { Entry entry = null; try { boolean add = true; Class<?> entryClass = loader.loadClass(entryClassName); Constructor cons = entryClass.getConstructor(String.class); Entry newEntry = (Entry)cons.newInstance(value); /* Check if the service already has the Entry, if it does not, add the entry */ Entry[] attributes = joinAdmin.getLookupAttributes(); for (Entry attribute : attributes) { if (attribute.getClass().getName().equals(entryClass.getName())) { add = false; break; } } if(add) entry = newEntry; } catch(Exception e) { logger.warn("{} not found, cannot add {}", entryClassName, entryClassName.toLowerCase(), e); } return entry; } private static Entry loadServiceInfo(String name, String version, ClassLoader loader) { Entry serviceInfo = null; try { Class<?> c = loader.loadClass("org.rioproject.entry.ServiceInfo"); serviceInfo = (Entry)c.newInstance(); Method m = c.getMethod("initialize", String.class, String.class); m.invoke(serviceInfo, name, version); } catch (Exception e) { // This happens if Rio classes are not in classpath. Ignore } return serviceInfo; } /* * Add Attributes using the JoinAdmin */ private static void addAttributes(Entry[] attrs, JoinAdmin joinAdmin) { try { StringBuilder builder = new StringBuilder(); for(Entry a : attrs) { if(builder.length()>0) { builder.append(", "); } builder.append("[").append(a).append("]"); } if(logger.isTraceEnabled()) logger.trace("Adding {}", builder.toString()); joinAdmin.addLookupAttributes(attrs); } catch (Exception e) { logger.warn("Unable to add Entry attributes", e); } } }