1 //
2 // ========================================================================
3 // Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
8 //
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
11 //
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
14 //
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
17 //
18
19
20 package org.eclipse.jetty.spring;
21
22 import java.net.URL;
23 import java.util.Arrays;
24 import java.util.Map;
25 import java.util.ServiceLoader;
26
27 import org.eclipse.jetty.util.log.Log;
28 import org.eclipse.jetty.util.log.Logger;
29 import org.eclipse.jetty.xml.ConfigurationProcessor;
30 import org.eclipse.jetty.xml.ConfigurationProcessorFactory;
31 import org.eclipse.jetty.xml.XmlConfiguration;
32 import org.eclipse.jetty.xml.XmlParser;
33 import org.springframework.beans.factory.xml.XmlBeanFactory;
34 import org.springframework.core.io.ByteArrayResource;
35 import org.springframework.core.io.Resource;
36 import org.springframework.core.io.UrlResource;
37
38 /**
39 * Spring ConfigurationProcessor
40 * <p/>
41 * A {@link ConfigurationProcessor} that uses a spring XML file to emulate the {@link XmlConfiguration} format.
42 * <p/>
43 * {@link XmlConfiguration} expects a primary object that is either passed in to a call to {@link #configure(Object)}
44 * or that is constructed by a call to {@link #configure()}. This processor looks for a bean definition
45 * with an id, name or alias of "Main" as uses that as the primary bean.
46 * <p/>
47 * The objects mapped by {@link XmlConfiguration#getIdMap()} are set as singletons before any configuration calls
48 * and if the spring configuration file contains a definition for the singleton id, the the singleton is updated
49 * with a call to {@link XmlBeanFactory#configureBean(Object, String)}.
50 * <p/>
51 * The property map obtained via {@link XmlConfiguration#getProperties()} is set as a singleton called "properties"
52 * and values can be accessed by somewhat verbose
53 * usage of {@link org.springframework.beans.factory.config.MethodInvokingFactoryBean}.
54 * <p/>
55 * This processor is returned by the {@link SpringConfigurationProcessorFactory} for any XML document whos first
56 * element is "beans". The factory is discovered by a {@link ServiceLoader} for {@link ConfigurationProcessorFactory}.
57 */
58 public class SpringConfigurationProcessor implements ConfigurationProcessor
59 {
60 private static final Logger LOG = Log.getLogger(SpringConfigurationProcessor.class);
61
62 private Map<String, Object> _idMap;
63 private Map<String, String> _propertyMap;
64 private XmlBeanFactory _beanFactory;
65 private String _main;
66
67 public void init(URL url, XmlParser.Node config, Map<String, Object> idMap, Map<String, String> properties)
68 {
69 try
70 {
71 _idMap = idMap;
72 _propertyMap = properties;
73
74 Resource resource = url != null
75 ? new UrlResource(url)
76 : new ByteArrayResource(("" +
77 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
78 "<!DOCTYPE beans PUBLIC \"-//SPRING//DTD BEAN//EN\" \"http://www.springframework.org/dtd/spring-beans.dtd\">" +
79 config).getBytes("UTF-8"));
80
81 _beanFactory = new XmlBeanFactory(resource);
82 }
83 catch (Exception e)
84 {
85 throw new RuntimeException(e);
86 }
87 }
88
89 public Object configure(Object obj) throws Exception
90 {
91 doConfigure();
92 return _beanFactory.configureBean(obj, _main);
93 }
94
95 /**
96 * Return a configured bean. If a bean has the id or alias of "Main", then it is returned, otherwise the first bean in the file is returned.
97 *
98 * @see org.eclipse.jetty.xml.ConfigurationProcessor#configure()
99 */
100 public Object configure() throws Exception
101 {
102 doConfigure();
103 return _beanFactory.getBean(_main);
104 }
105
106 private void doConfigure()
107 {
108 _beanFactory.registerSingleton("properties", _propertyMap);
109
110 // Look for the main bean;
111 for (String bean : _beanFactory.getBeanDefinitionNames())
112 {
113 LOG.debug("{} - {}", bean, Arrays.asList(_beanFactory.getAliases(bean)));
114 String[] aliases = _beanFactory.getAliases(bean);
115 if ("Main".equals(bean) || aliases != null && Arrays.asList(aliases).contains("Main"))
116 {
117 _main = bean;
118 break;
119 }
120 }
121 if (_main == null)
122 _main = _beanFactory.getBeanDefinitionNames()[0];
123
124 // Register id beans as singletons
125 LOG.debug("idMap {}", _idMap);
126 for (String id : _idMap.keySet())
127 {
128 LOG.debug("register {}", id);
129 _beanFactory.registerSingleton(id, _idMap.get(id));
130 }
131
132 // Apply configuration to existing singletons
133 for (String id : _idMap.keySet())
134 {
135 if (_beanFactory.containsBeanDefinition(id))
136 {
137 LOG.debug("reconfigure {}", id);
138 _beanFactory.configureBean(_idMap.get(id), id);
139 }
140 }
141
142 // Extract id's for next time.
143 for (String id : _beanFactory.getSingletonNames())
144 _idMap.put(id, _beanFactory.getBean(id));
145 }
146 }