2021SC@SDUSC
The main content of this code analysis is the content in the core folder under the sitemap impl folder. This core folder may be the core code to implement the site map. Due to the large content, I will divide it into two blogs for analysis. This blog is mainly based on the analysis of some code in the container folder in the core folder, the avalon folder in the spring folder.
AvalonBeanPostProcessor.java
This is a Spring BeanPostProcessor with added support for Avalon lifecycle interface.
There are some simple methods, such as setSettings(), setResourceLoader(), setLocation(), setBeanFactory(), setConfigurationInfo(), setContext(), to set some values.
Here are some important methods:
1.postProcessAfterInitialization():
After any bean initializes the callback, apply this BeanPostProcessor to the given new bean instance. The bean will already be populated with property values. The returned bean instance may be the wrapper of the original bean instance.
For FactoryBean, this callback function will be called on both the FactoryBean instance and the object created by FactoryBean. The post processor can check the corresponding FactoryBean instance to determine whether to apply to the FactoryBean, the created object, or both.
Compared with all other BeanPostProcessor callbacks, this callback will also be called after a short circuit triggered by the method.
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { try { ContainerUtil.start(bean); } catch (Exception e) { throw new BeanInitializationException("Unable to start bean " + beanName, e); } return bean; }
2.postProcessBeforeInitialization():
Apply this BeanPostProcessor to the given new bean instance before any bean initializes the callback. The bean will already be populated with property values. The returned bean instance may be the wrapper of the original bean instance
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { final ComponentInfo info = (ComponentInfo) this.configurationInfo.getComponents().get(beanName); try { if (info == null) { // no info so we just return the bean and don't apply any // lifecycle interfaces return bean; } if (bean instanceof LogEnabled) { ContainerUtil.enableLogging(bean, new CLLoggerWrapper(LoggerUtils.getChildLogger(beanFactory, info.getLoggerCategory()))); } else if (bean instanceof AbstractLogEnabled && info.getLoggerCategory() != null) { ((AbstractLogEnabled) bean).setLogger(LoggerUtils.getChildLogger(beanFactory, info.getLoggerCategory())); } ContainerUtil.contextualize(bean, this.context); ContainerUtil.service(bean, (ServiceManager) this.beanFactory.getBean(ServiceManager.class.getName())); Configuration config = info.getProcessedConfiguration(); if (config == null) { config = info.getConfiguration(); if (config == null) { config = EMPTY_CONFIG; } else { config = AvalonUtils.replaceProperties(config, this.settings); } info.setProcessedConfiguration(config); } if (bean instanceof Configurable) { ContainerUtil.configure(bean, config); } else if (bean instanceof Parameterizable) { Parameters p = info.getParameters(); if (p == null) { p = Parameters.fromConfiguration(config); info.setParameters(p); } ContainerUtil.parameterize(bean, p); } ContainerUtil.initialize(bean); } catch (Exception e) { throw new BeanCreationException("Unable to initialize Avalon component with role " + beanName, e); } return bean; }
3.postProcessBeforeDestruction():
Apply this BeanPostProcessor to a given bean instance before it is destroyed. You can call a custom destroy callback. This callback applies only to singleton beans (including internal beans) in the factory.
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException { try { ContainerUtil.stop(bean); } catch (Exception e) { throw new BeanInitializationException("Unable to stop bean " + beanName, e); } ContainerUtil.dispose(bean); }
AvalonContextFactoryBean.java
This factory bean sets the Avalon Context object. It is part of the Spring bridge for Avalon integration.
There are three important attributes:
//Avalon context protected ServletContext servletContext; //servlet context protected Settings settings; //set up protected Context context;
There is a method that needs attention - init():
//Create Avalon context object. protected void init()throws Exception { if ( this.settings == null ) { throw new IllegalArgumentException("Settings object is missing."); } //Create a new Avalon context final DefaultContext appContext = new ComponentContext(); //Add environment context and configuration appContext.put(Constants.CONTEXT_ENVIRONMENT_CONTEXT, new HttpContext(this.servletContext)); //Add Avalon context property contained in settings appContext.put(Constants.CONTEXT_WORK_DIR, new File(this.settings.getWorkDirectory())); appContext.put(Constants.CONTEXT_CACHE_DIR, new File(this.settings.getCacheDirectory())); appContext.put(Constants.CONTEXT_DEFAULT_ENCODING, this.settings.getFormEncoding()); this.context = appContext; }
AvalonUtils.java
This class contains some practical methods for dealing with Avalon.
1.replaceProperties():
Replace all properties in the configuration object.
public static Configuration replaceProperties(Configuration tree, Settings settings) throws ConfigurationException { if (tree == null || settings == null) { return tree; } //First copy the tree final DefaultConfiguration root = new DefaultConfiguration(tree, true); //Then call replaceProperties method, replacing properties _replaceProperties(root, settings); return root; }
2._replaceProperties():
Recursively replace the properties of the configuration object.
protected static void _replaceProperties(DefaultConfiguration config, Settings settings) throws ConfigurationException { final String[] names = config.getAttributeNames(); for (int i = 0; i < names.length; i++) { final String value = config.getAttribute(names[i]); config.setAttribute(names[i], PropertyHelper.replace(value, settings)); } final String value = config.getValue(null); if (value != null) { config.setValue(PropertyHelper.replace(value, settings)); } final Configuration[] children = config.getChildren(); for (int m = 0; m < children.length; m++) { _replaceProperties((DefaultConfiguration) children[m], settings); } }
ComponentContext.java
This is the context implementation of the Cocoon component. It extends the DefaultContext by obtaining special processing of objects from the object model and other application information. This class inherits from DefaultContext
Overloaded constructors, one with a final Context parent parameter and one without. The functions are to create a context with a specified parent and a context without a parent node.
There is a unique method, get() -- to retrieve the item from the context:
public Object get( final Object key ) throws ContextException { if ( ContextHelper.CONTEXT_OBJECT_MODEL.equals(key)) { final Environment env = EnvironmentHelper.getCurrentEnvironment(); if ( env == null ) { throw new ContextException("Unable to locate " + key + " (No environment available)"); } return env.getObjectModel(); } else if ( ContextHelper.CONTEXT_SITEMAP_SERVICE_MANAGER.equals(key)) { final ServiceManager manager = EnvironmentHelper.getSitemapServiceManager(); if ( manager == null ) { throw new ContextException("Unable to locate " + key + " (No environment available)"); } return manager; } if ( key instanceof String ) { String stringKey = (String)key; if ( stringKey.startsWith(OBJECT_MODEL_KEY_PREFIX) ) { final Environment env = EnvironmentHelper.getCurrentEnvironment(); if ( env == null ) { throw new ContextException("Unable to locate " + key + " (No environment available)"); } final Map objectModel = env.getObjectModel(); String objectKey = stringKey.substring(OBJECT_MODEL_KEY_PREFIX.length()); Object o = objectModel.get( objectKey ); if ( o == null ) { final String message = "Unable to locate " + key; throw new ContextException( message ); } return o; } } return super.get( key ); }
ComponentInfo.java
Meta information about Avalon based components. This simple bean stores most of the information of the components defined in the Avalon based configuration file, such as the configuration of the component, its recorder, etc.
There are some properties that need attention in this class:
Attribute name | Attribute description |
---|---|
MODEL_POOLED | Each time a component of this type is requested, a new instance is created. |
MODEL_PRIMITIVE | Only components of this type exist in each container. |
MODEL_SINGLETON | The container creates a component pool for this type and services the requests in the pool. |
MODEL_UNKNOWN | The model of the component is unknown. Reflection is later used to determine the model. |
There is a method called copy that needs attention. As the name suggests, this method is used to create a new component with the same information as the current configuration.
public ComponentInfo copy() { final ComponentInfo info = new ComponentInfo(); //The model of the component. info.model = this.model; //An optional method that is called by the container during initialization. info.initMethodName = this.initMethodName; //An optional method that is called by the container when it is destroyed. info.destroyMethodName = this.destroyMethodName; //An optional method called by the container when the component is put back into the pool. info.poolInMethodName = this.poolInMethodName; //An optional method that is called by the container when the component gets from the pool. info.poolOutMethodName = this.poolOutMethodName; //Class name of the component. info.componentClassName = this.componentClassName; //Component configuration. info.configuration = this.configuration; //The configuration of the component is used as a parameter info.parameters = this.parameters; //Optional logger category (relative to the category of the container). info.loggerCategory = this.loggerCategory; //The role of the component. info.role = this.role; //Alias for the component role. info.alias = this.alias; //Default component of selector info.defaultValue = this.defaultValue; info.lazyInit = this.lazyInit; return info; }
ConfigurationReader.java
This component reads the Avalon style configuration file and returns all contained components and their configurations.
Some important attributes:
- Logger -- logger
- resolver -- the parser reads the configuration file
- configInfo -- configuration information
- componentConfigs - all component configurations
- isRootContext -- the context used to determine whether it is a root
Some important methods:
1.configureRoles() -- read the configuration object and create role, shorthand and class name mappings.
protected final void configureRoles( final Configuration configuration ) throws ConfigurationException { final Configuration[] roles = configuration.getChildren(); for (int i = 0; i < roles.length; i++) { final Configuration role = roles[i]; if ("alias".equals(role.getName())) { final String roleName = role.getAttribute("role"); final String shorthand = role.getAttribute("shorthand"); this.configInfo.getShorthands().put(shorthand, roleName); continue; } if (!"role".equals(role.getName())) { throw new ConfigurationException("Unexpected '" + role.getName() + "' element at " + role.getLocation()); } final String roleName = role.getAttribute("name"); final String shorthand = role.getAttribute("shorthand", null); final String defaultClassName = role.getAttribute("default-class", null); if (shorthand != null) { // Store the shorthand and check that its consistent with any previous one Object previous = this.configInfo.getShorthands().put(shorthand, roleName); if (previous != null && !previous.equals(roleName)) { throw new ConfigurationException("Shorthand '" + shorthand + "' already used for role " + previous + ": inconsistent declaration at " + role.getLocation()); } } if (defaultClassName != null) { ComponentInfo info = this.configInfo.getRole(roleName); if (info == null) { // Create a new info and store it info = new ComponentInfo(); info.setComponentClassName(defaultClassName); info.fill(role); info.setRole(roleName); info.setConfiguration(role); info.setAlias(shorthand); this.configInfo.addRole(roleName, info); } else { // Check that it's consistent with the existing info if (!defaultClassName.equals(info.getComponentClassName())) { throw new ConfigurationException("Invalid redeclaration: default class already set to " + info.getComponentClassName() + " for role " + roleName + " at " + role.getLocation()); } //FIXME: should check also other ServiceInfo members } } final Configuration[] keys = role.getChildren("hint"); if (keys.length > 0) { Map keyMap = (Map) this.configInfo.getKeyClassNames().get(roleName); if (keyMap == null) { keyMap = new HashMap(); this.configInfo.getKeyClassNames().put(roleName, keyMap); } for (int j = 0; j < keys.length; j++) { Configuration key = keys[j]; final String shortHand = key.getAttribute("shorthand").trim(); final String className = key.getAttribute("class").trim(); ComponentInfo info = (ComponentInfo) keyMap.get(shortHand); if (info == null) { info = new ComponentInfo(); info.setComponentClassName(className); info.fill(key); info.setConfiguration(key); info.setAlias(shortHand); keyMap.put(shortHand, info); } else { // Check that it's consistent with the existing info if (!className.equals(info.getComponentClassName())) { throw new ConfigurationException("Invalid redeclaration: class already set to " + info.getComponentClassName() + " for hint " + shortHand + " at " + key.getLocation()); } //FIXME: should check also other ServiceInfo members } } } } }
2.convertUrl() -- convert an Avalon URL (using the possible cocoon protocol) into a spring url.
protected String convertUrl(String url) { if (url == null) { return null; } if (url.startsWith("context:")) { return url.substring(10); } if (url.startsWith("resource:")) { return "classpath:" + url.substring(10); } return url; }
3.getInputSource() -- construct an input source from a given resource and initialize the system Id
protected InputSource getInputSource(Resource rsrc) throws IOException { final InputSource is = new InputSource(rsrc.getInputStream()); is.setSystemId(getUrl(rsrc)); return is; }
4.handleBeanInclude() -- handle spring bean configuration
protected void handleBeanInclude(final String contextURI, final Configuration includeStatement) throws ConfigurationException { final String includeURI = includeStatement.getAttribute("src", null); String directoryURI = null; if (includeURI == null) { // check for directories directoryURI = includeStatement.getAttribute("dir", null); } if (includeURI == null && directoryURI == null) { throw new ConfigurationException( "Include statement must either have a 'src' or 'dir' attribute, at " + includeStatement.getLocation()); } if (includeURI != null) { try { Resource src = this.resolver.getResource(getUrl(includeURI, contextURI)); this.configInfo.addImport(getUrl(src)); } catch (Exception e) { throw new ConfigurationException("Cannot load '" + includeURI + "' at " + includeStatement.getLocation(), e); } } else { // test if directory exists Resource dirResource = this.resolver.getResource(this.getUrl(directoryURI, contextURI)); if ( dirResource.exists() ) { final String pattern = includeStatement.getAttribute("pattern", null); try { Resource[] resources = this.resolver.getResources(this.getUrl(directoryURI + '/' + pattern, contextURI)); if ( resources != null ) { Arrays.sort(resources, ResourceUtils.getResourceComparator()); for(int i=0; i < resources.length; i++) { this.configInfo.addImport(getUrl(resources[i])); } } } catch (IOException ioe) { throw new ConfigurationException("Unable to read configurations from " + directoryURI); } } else { if (!includeStatement.getAttributeAsBoolean("optional", false)) { throw new ConfigurationException("Directory '" + directoryURI + "' does not exist (" + includeStatement.getLocation() + ")."); } } } }
5.handleInclude() -- processing avalon configuration
protected void handleInclude(final String contextURI, final Set loadedURIs, final Configuration includeStatement) throws ConfigurationException { final String includeURI = includeStatement.getAttribute("src", null); String directoryURI = null; if (includeURI == null) { // check for directories directoryURI = includeStatement.getAttribute("dir", null); } if (includeURI == null && directoryURI == null) { throw new ConfigurationException("Include statement must either have a 'src' or 'dir' attribute, at " + includeStatement.getLocation()); } if (includeURI != null) { try { Resource src = this.resolver.getResource(getUrl(includeURI, contextURI)); loadURI(src, loadedURIs, includeStatement); } catch (Exception e) { throw new ConfigurationException("Cannot load '" + includeURI + "' at " + includeStatement.getLocation(), e); } } else { boolean load = true; // test if directory exists (only if not classpath protocol is used) if (!ResourceUtils.isClasspathUri(directoryURI)) { Resource dirResource = this.resolver.getResource(this.getUrl(directoryURI, contextURI)); if (!dirResource.exists()) { if (!includeStatement.getAttributeAsBoolean("optional", false)) { throw new ConfigurationException("Directory '" + directoryURI + "' does not exist (" + includeStatement.getLocation() + ")."); } load = false; } } if (load) { final String pattern = includeStatement.getAttribute("pattern", null); try { Resource[] resources = this.resolver.getResources(this.getUrl(directoryURI + '/' + pattern, contextURI)); if (resources != null) { Arrays.sort(resources, ResourceUtils.getResourceComparator()); for (int i = 0; i < resources.length; i++) { loadURI(resources[i], loadedURIs, includeStatement); } } } catch (Exception e) { throw new ConfigurationException("Cannot load from directory '" + directoryURI + "' at " + includeStatement.getLocation(), e); } } } }
Other documents
Name of the class | Class description |
---|---|
AvalonNamespaceHandler | Spring namespace handler for the cocoon avalon namespace |
AvalonServiceManager | The function of this bean is similar to Avalon ServiceManager |
AvalonServiceSelector | The bean behaves like Avalon servicesselector. |
AvalonSitemapContextFactoryBean | This factory bean creates a context for the site map. |
BridgeElementParser | This is the main implementation of Avalon spring bridge. |
ConfigurationInfo | This bean stores information about the full Avalon style configuration. |
ConfigurationInfoFactoryBean | This spring factory bean adds configuration information to the bean factory. |
In this blog, I only analyzed some contents in this folder, and the rest will be given in the next blog.