1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.services.binding;
23
24 import java.util.HashMap;
25 import java.beans.PropertyEditorManager;
26 import java.beans.PropertyEditor;
27 import javax.management.Attribute;
28 import javax.management.MBeanServer;
29 import javax.management.ObjectName;
30 import javax.management.MBeanInfo;
31 import javax.management.MBeanAttributeInfo;
32
33 import org.w3c.dom.Element;
34 import org.w3c.dom.NodeList;
35
36 import org.jboss.logging.Logger;
37 import org.jboss.metadata.MetaData;
38 import org.jboss.util.StringPropertyReplacer;
39 import org.jboss.util.Classes;
40 import org.jboss.deployment.DeploymentException;
41
42 /** An implementation of the ServicesConfigDelegate that expects a delegate-config
43 element of the form:
44 <delegate-config portName="portAttrName" hostName="hostAttrName">
45 <attribute name="mbeanAttrName">host-port-expr</attribute>
46 ...
47 </delegate-config>
48 where the portAttrName is the attribute name of the mbean service
49 to which the (int port) value should be applied and the hostAttrName
50 is the attribute name of the mbean service to which the (String virtualHost)
51 value should be applied.
52
53 Any mbeanAttrName attribute reference has the corresponding value replaced
54 with any ${host} and ${port} references with the associated host and port
55 bindings.
56
57 @author Scott.Stark@jboss.org
58 @version $Revision: 37459 $
59 */
60 public class AttributeMappingDelegate
61 implements ServicesConfigDelegate
62 {
63 private static Logger log = Logger.getLogger(AttributeMappingDelegate.class);
64
65 /** Take the given config and map it onto the service specified in the
66 config using JMX via the given server.
67 @param config, the service name and its config bindings
68 @param server, the JMX server to use to apply the config
69 */
70 public void applyConfig(ServiceConfig config, MBeanServer server)
71 throws Exception
72 {
73 Element delegateConfig = (Element) config.getServiceConfigDelegateConfig();
74 if( delegateConfig == null )
75 throw new IllegalArgumentException("ServiceConfig.ServiceConfigDelegateConfig is null");
76 // Check for a port and host name
77 String portAttrName = delegateConfig.getAttribute("portName");
78 if( portAttrName.length() == 0 )
79 portAttrName = null;
80 String hostAttrName = delegateConfig.getAttribute("hostName");
81 if( hostAttrName.length() == 0 )
82 hostAttrName = null;
83
84 // Check for any arbitrary attributes
85 NodeList attributes = delegateConfig.getElementsByTagName("attribute");
86
87 // Only the first binding is used as only one (host,port) pair is mapped
88 ServiceBinding[] bindings = config.getBindings();
89 if( bindings != null && bindings.length > 0 )
90 {
91 // Build a mapping of the attribute names to their type name
92 ObjectName serviceName = new ObjectName(config.getServiceName());
93 MBeanInfo info = server.getMBeanInfo(serviceName);
94 MBeanAttributeInfo[] attrInfo = info.getAttributes();
95 HashMap attrTypeMap = new HashMap();
96 for(int a = 0; a < attrInfo.length; a ++)
97 {
98 MBeanAttributeInfo attr = attrInfo[a];
99 attrTypeMap.put(attr.getName(), attr.getType());
100 }
101
102 int port = bindings[0].getPort();
103 String host = bindings[0].getHostName();
104 // Apply the port setting override if the port name was given
105 if( portAttrName != null )
106 {
107 Attribute portAttr = new Attribute(portAttrName, new Integer(port));
108 log.debug("setPort, name='"+portAttrName+"' value="+port);
109 server.setAttribute(serviceName, portAttr);
110 }
111 // Apply the host setting override if the port name was given
112 if( hostAttrName != null )
113 {
114 Attribute hostAttr = createAtribute(port, host, attrTypeMap,
115 hostAttrName, host);
116 log.debug("setHost, name='"+hostAttrName+"' value="+host);
117 server.setAttribute(serviceName, hostAttr);
118 }
119
120 /* Apply any other host/port based attributes with replacement of
121 the ${host} and ${port} strings.
122 */
123 for(int a = 0; a < attributes.getLength(); a ++)
124 {
125 Element attr = (Element) attributes.item(a);
126 String name = attr.getAttribute("name");
127 if( name.length() == 0 )
128 throw new IllegalArgumentException("attribute element #"+a+" has no name attribute");
129 String attrExp = MetaData.getElementContent(attr);
130 Attribute theAttr = createAtribute(port, host, attrTypeMap,
131 name, attrExp);
132 server.setAttribute(serviceName, theAttr);
133 }
134 }
135 else
136 {
137 /**
138 * Apply attributes even if not using port or host
139 */
140 for(int a = 0; a < attributes.getLength(); a ++)
141 {
142 Element attr = (Element) attributes.item(a);
143 String name = attr.getAttribute("name");
144 if( name.length() == 0 )
145 throw new IllegalArgumentException("attribute element #"+a+" has no name attribute");
146 String attrExp = MetaData.getElementContent(attr);
147 Attribute attribute = new Attribute(name, attrExp);
148 ObjectName serviceName = new ObjectName(config.getServiceName());
149 server.setAttribute(serviceName, attribute);
150 }
151 }
152 }
153
154 /** Create a JMX Attribute with the correct type value object. This
155 * converts the given attrExp into an Attribute for attrName with
156 * replacement of any ${host} ${port} references in the attrExp
157 * replaced with the given port/host values.
158 * @param port The binding port value
159 * @param host The binding host value
160 * @param attrTypeMap the name to type map for the service attributes
161 * @param attrName the name of the attribute to create
162 * @param attrExp the string exp for the attribute value
163 * @return the JMX attribute instance
164 * @throws Exception thrown on an invalid attribute name or inability
165 * to find a valid property editor
166 */
167 private Attribute createAtribute(int port, String host,
168 HashMap attrTypeMap, String attrName, String attrExp)
169 throws Exception
170 {
171 String attrText = replaceHostAndPort(attrExp, host, ""+port);
172 String typeName = (String) attrTypeMap.get(attrName);
173 if( typeName == null )
174 {
175 throw new DeploymentException("No such attribute: " + attrName);
176 }
177 // Convert the type
178 Class attrType = Classes.loadClass(typeName);
179 PropertyEditor editor = PropertyEditorManager.findEditor(attrType);
180 if( editor == null )
181 {
182 String msg = "No property editor for attribute: " + attrName +
183 "; type=" + typeName;
184 throw new DeploymentException(msg);
185 }
186 editor.setAsText(attrText);
187 Object attrValue = editor.getValue();
188 log.debug("setAttribute, name='"+attrName+"', text="+attrText
189 +", value="+attrValue);
190 Attribute theAttr = new Attribute(attrName, attrValue);
191 return theAttr;
192 }
193
194 /** Loop over text and replace any ${host} and ${port} strings. If there are
195 * any ${x} system property references in the resulting replacement string
196 * these will be replaced with the corresponding System.getProperty("x")
197 * value if one exists.
198 * @param text the text exp with optional ${host} ${port} references
199 * @param host the binding host value
200 * @param port the binding port value
201 */
202 private String replaceHostAndPort(String text, String host, String port)
203 {
204 if( text == null )
205 return null;
206
207 StringBuffer replacement = new StringBuffer(text);
208 if( host == null )
209 host = "localhost";
210 // Simple looping should be replaced with regex package
211 String test = replacement.toString();
212 int index;
213 while( (index = test.indexOf("${host}")) >= 0 )
214 {
215 replacement.replace(index, index+7, host);
216 test = replacement.toString();
217 }
218 while( (index = test.indexOf("${port}")) >= 0 )
219 {
220 replacement.replace(index, index+7, port);
221 test = replacement.toString();
222 }
223 return StringPropertyReplacer.replaceProperties(replacement.toString());
224 }
225 }