View Javadoc

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.nio.charset.StandardCharsets;
24  import java.util.Arrays;
25  import java.util.Map;
26  import java.util.ServiceLoader;
27  
28  import org.eclipse.jetty.util.log.Log;
29  import org.eclipse.jetty.util.log.Logger;
30  import org.eclipse.jetty.xml.ConfigurationProcessor;
31  import org.eclipse.jetty.xml.ConfigurationProcessorFactory;
32  import org.eclipse.jetty.xml.XmlConfiguration;
33  import org.eclipse.jetty.xml.XmlParser;
34  import org.springframework.beans.BeanWrapper;
35  import org.springframework.beans.PropertyValues;
36  import org.springframework.beans.factory.config.BeanDefinition;
37  import org.springframework.beans.factory.support.DefaultListableBeanFactory;
38  import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
39  import org.springframework.beans.factory.xml.XmlBeanFactory;
40  import org.springframework.core.io.ByteArrayResource;
41  import org.springframework.core.io.Resource;
42  import org.springframework.core.io.UrlResource;
43  
44  /**
45   * Spring ConfigurationProcessor
46   * <p/>
47   * A {@link ConfigurationProcessor} that uses a spring XML file to emulate the {@link XmlConfiguration} format.
48   * <p/>
49   * {@link XmlConfiguration} expects a primary object that is either passed in to a call to {@link #configure(Object)}
50   * or that is constructed by a call to {@link #configure()}. This processor looks for a bean definition
51   * with an id, name or alias of "Main" as uses that as the primary bean.
52   * <p/>
53   * The objects mapped by {@link XmlConfiguration#getIdMap()} are set as singletons before any configuration calls
54   * and if the spring configuration file contains a definition for the singleton id, the the singleton is updated
55   * with a call to {@link XmlBeanFactory#configureBean(Object, String)}.
56   * <p/>
57   * The property map obtained via {@link XmlConfiguration#getProperties()} is set as a singleton called "properties"
58   * and values can be accessed by somewhat verbose
59   * usage of {@link org.springframework.beans.factory.config.MethodInvokingFactoryBean}.
60   * <p/>
61   * This processor is returned by the {@link SpringConfigurationProcessorFactory} for any XML document whos first
62   * element is "beans". The factory is discovered by a {@link ServiceLoader} for {@link ConfigurationProcessorFactory}.
63   */
64  public class SpringConfigurationProcessor implements ConfigurationProcessor
65  {
66      private static final Logger LOG = Log.getLogger(SpringConfigurationProcessor.class);
67  
68      private XmlConfiguration _configuration;
69      private DefaultListableBeanFactory _beanFactory;
70      private String _main;
71  
72      @Override
73      public void init(URL url, XmlParser.Node config, XmlConfiguration configuration)
74      {
75          try
76          {
77              _configuration = configuration;
78  
79              Resource resource = url != null
80                      ? new UrlResource(url)
81                      : new ByteArrayResource(("" +
82                      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
83                      "<!DOCTYPE beans PUBLIC \"-//SPRING//DTD BEAN//EN\" \"http://www.springframework.org/dtd/spring-beans.dtd\">" +
84                      config).getBytes(StandardCharsets.UTF_8));
85  
86              _beanFactory = new DefaultListableBeanFactory()
87              {
88                  @Override
89                  protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs)
90                  {
91                      _configuration.initializeDefaults(bw.getWrappedInstance());
92                      super.applyPropertyValues(beanName, mbd, bw, pvs);
93                  }
94              };
95  
96              new XmlBeanDefinitionReader(_beanFactory).loadBeanDefinitions(resource);
97          }
98          catch (Exception e)
99          {
100             throw new RuntimeException(e);
101         }
102     }
103 
104     @Override
105     public Object configure(Object obj) throws Exception
106     {
107         doConfigure();
108         return _beanFactory.configureBean(obj, _main);
109     }
110 
111     /**
112      * 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.
113      *
114      * @see org.eclipse.jetty.xml.ConfigurationProcessor#configure()
115      */
116     @Override
117     public Object configure() throws Exception
118     {
119         doConfigure();
120         return _beanFactory.getBean(_main);
121     }
122 
123     private void doConfigure()
124     {
125         _beanFactory.registerSingleton("properties", _configuration.getProperties());
126 
127         // Look for the main bean;
128         for (String bean : _beanFactory.getBeanDefinitionNames())
129         {
130             LOG.debug("{} - {}", bean, Arrays.asList(_beanFactory.getAliases(bean)));
131             String[] aliases = _beanFactory.getAliases(bean);
132             if ("Main".equals(bean) || aliases != null && Arrays.asList(aliases).contains("Main"))
133             {
134                 _main = bean;
135                 break;
136             }
137         }
138         if (_main == null)
139             _main = _beanFactory.getBeanDefinitionNames()[0];
140 
141         // Register id beans as singletons
142         Map<String, Object> idMap = _configuration.getIdMap();
143         LOG.debug("idMap {}", idMap);
144         for (String id : idMap.keySet())
145         {
146             LOG.debug("register {}", id);
147             _beanFactory.registerSingleton(id, idMap.get(id));
148         }
149 
150         // Apply configuration to existing singletons
151         for (String id : idMap.keySet())
152         {
153             if (_beanFactory.containsBeanDefinition(id))
154             {
155                 LOG.debug("reconfigure {}", id);
156                 _beanFactory.configureBean(idMap.get(id), id);
157             }
158         }
159 
160         // Extract id's for next time.
161         for (String id : _beanFactory.getSingletonNames())
162             idMap.put(id, _beanFactory.getBean(id));
163     }
164 }