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 */
017package org.apache.activemq.plugin;
018
019import org.apache.activemq.broker.BrokerContext;
020import org.apache.activemq.spring.Utils;
021import org.slf4j.Logger;
022import org.slf4j.LoggerFactory;
023import org.springframework.beans.factory.FactoryBean;
024import org.springframework.core.io.Resource;
025import org.w3c.dom.Document;
026import org.w3c.dom.Element;
027import org.w3c.dom.Node;
028import org.w3c.dom.NodeList;
029
030import java.io.IOException;
031import java.net.MalformedURLException;
032import java.util.LinkedList;
033import java.util.List;
034import java.util.Map;
035import java.util.Properties;
036import java.util.regex.Matcher;
037import java.util.regex.Pattern;
038
039public class PropertiesPlaceHolderUtil {
040
041    public static final Logger LOG = LoggerFactory.getLogger(PropertiesPlaceHolderUtil.class);
042
043    static final Pattern pattern = Pattern.compile("\\$\\{([^\\}]+)\\}");
044    final Properties properties;
045
046    public PropertiesPlaceHolderUtil(Properties properties) {
047        this.properties = properties;
048    }
049
050    public void filter(Properties toFilter) {
051        for (Map.Entry<Object, Object> entry : toFilter.entrySet()) {
052            String val = (String) entry.getValue();
053            String newVal = filter(val);
054            if (!val.equals(newVal)) {
055                toFilter.put(entry.getKey(), newVal);
056            }
057        }
058    }
059
060    public String filter(String str) {
061        int start = 0;
062        while (true) {
063            Matcher matcher = pattern.matcher(str);
064            if (!matcher.find(start)) {
065                break;
066            }
067            String group = matcher.group(1);
068            String property = properties.getProperty(group);
069            if (property != null) {
070                str = matcher.replaceFirst(Matcher.quoteReplacement(property));
071            } else {
072                start = matcher.end();
073            }
074        }
075        return replaceBytePostfix(str);
076    }
077
078    static Pattern[] byteMatchers = new Pattern[] {
079            Pattern.compile("^\\s*(\\d+)\\s*(b)?\\s*$", Pattern.CASE_INSENSITIVE),
080            Pattern.compile("^\\s*(\\d+)\\s*k(b)?\\s*$", Pattern.CASE_INSENSITIVE),
081            Pattern.compile("^\\s*(\\d+)\\s*m(b)?\\s*$", Pattern.CASE_INSENSITIVE),
082            Pattern.compile("^\\s*(\\d+)\\s*g(b)?\\s*$", Pattern.CASE_INSENSITIVE)};
083
084    // xbean can Xb, Xkb, Xmb, Xg etc
085    private String replaceBytePostfix(String str) {
086        try {
087            for (int i=0; i< byteMatchers.length; i++) {
088                Matcher matcher = byteMatchers[i].matcher(str);
089                if (matcher.matches()) {
090                    long value = Long.parseLong(matcher.group(1));
091                    for (int j=1; j<=i; j++) {
092                        value *= 1024;
093                    }
094                    return String.valueOf(value);
095                }
096            }
097        } catch (NumberFormatException ignored) {
098            LOG.debug("nfe on: " + str, ignored);
099        }
100        return str;
101    }
102
103    public void mergeProperties(Document doc, Properties initialProperties, BrokerContext brokerContext) {
104        // find resources
105        //        <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
106        //            <property name="locations" || name="properties">
107        //              ...
108        //            </property>
109        //          </bean>
110        LinkedList<String> resources = new LinkedList<String>();
111        LinkedList<String> propertiesClazzes = new LinkedList<String>();
112        NodeList beans = doc.getElementsByTagNameNS("*", "bean");
113        for (int i = 0; i < beans.getLength(); i++) {
114            Node bean = beans.item(0);
115            if (bean.hasAttributes() && bean.getAttributes().getNamedItem("class").getTextContent().contains("PropertyPlaceholderConfigurer")) {
116                if (bean.hasChildNodes()) {
117                    NodeList beanProps = bean.getChildNodes();
118                    for (int j = 0; j < beanProps.getLength(); j++) {
119                        Node beanProp = beanProps.item(j);
120                        if (Node.ELEMENT_NODE == beanProp.getNodeType() && beanProp.hasAttributes() && beanProp.getAttributes().getNamedItem("name") != null) {
121                            String propertyName = beanProp.getAttributes().getNamedItem("name").getTextContent();
122                            if ("locations".equals(propertyName)) {
123
124                                // interested in value or list/value of locations property
125                                Element beanPropElement = (Element) beanProp;
126                                NodeList values = beanPropElement.getElementsByTagNameNS("*", "value");
127                                for (int k = 0; k < values.getLength(); k++) {
128                                    Node value = values.item(k);
129                                    resources.add(value.getFirstChild().getTextContent());
130                                }
131                            } else if ("properties".equals(propertyName)) {
132
133                                // bean or beanFactory
134                                Element beanPropElement = (Element) beanProp;
135                                NodeList values = beanPropElement.getElementsByTagNameNS("*", "bean");
136                                for (int k = 0; k < values.getLength(); k++) {
137                                    Node value = values.item(k);
138                                    if (value.hasAttributes()) {
139                                        Node beanClassTypeNode = value.getAttributes().getNamedItem("class");
140                                        if (beanClassTypeNode != null) {
141                                            propertiesClazzes.add(beanClassTypeNode.getFirstChild().getTextContent());
142                                        }
143                                    }
144                                }
145                            }
146                        }
147                    }
148                }
149            }
150        }
151        for (String value : propertiesClazzes) {
152            try {
153                Object springBean = getClass().getClassLoader().loadClass(value).newInstance();
154                if (springBean instanceof FactoryBean) {
155                    // can't access the factory or created properties from spring context so we got to recreate
156                    initialProperties.putAll((Properties) FactoryBean.class.getMethod("getObject", (Class<?>[]) null).invoke(springBean));
157                }
158            } catch (Throwable e) {
159                LOG.debug("unexpected exception processing properties bean class: " + propertiesClazzes, e);
160            }
161        }
162        List<Resource> propResources = new LinkedList<Resource>();
163        for (String value : resources) {
164            try {
165                if (!value.isEmpty()) {
166                    propResources.add(Utils.resourceFromString(filter(value)));
167                }
168            } catch (MalformedURLException e) {
169                LOG.info("failed to resolve resource: " + value, e);
170            }
171        }
172        for (Resource resource : propResources) {
173            Properties properties = new Properties();
174            try {
175                properties.load(resource.getInputStream());
176            } catch (IOException e) {
177                LOG.info("failed to load properties resource: " + resource, e);
178            }
179            initialProperties.putAll(properties);
180        }
181    }
182
183}