2021SC@SDUSC [application and practice of software engineering] Cocoon code analysis


This should be my last blog on software engineering application and practice. The main analysis content this time is the content in the sitemap impl folder, including three folders: processing, selection and util. In addition to the previous content, I will also summarize the code I analyzed before.


Simulation Implementation of ProcessInfoProvider interface. This class contains a series of get and set operations, which are related to ObjectModel, Request, Response and ServletContext respectively. Then there are three internal classes - stubbrequest

  • StubRequest
    • This is a stub implementation of HttpServletRequest. The HttpServletRequest interface is implemented.
  • StubResponse
    • This is a stub implementation of HttpServletResponse. The httpservletresponseset interface is implemented.
  • StubSession
    • The HttpSession interface is implemented.


The default implementation of the process information provider.

There are two ways to focus:


After obtaining the current object model, you can get the Request, Response and other contents based on this method.

protected Map getCurrentObjectModel() {
    final Environment env = EnvironmentHelper.getCurrentEnvironment();
    if ( env == null ) {
        throw new IllegalStateException("Unable to locate current environment.");
    return env.getObjectModel();


Set dependency to servlet context

public void setServletContext(ServletContext context) {
    this.servletContext = context;


The abstract class AbstractRegexpSelector defines a simple selector that operates on a configured regular expression pattern.

The configuration of AbstractRegexpSelector is very simple: first, you must configure the mode for selection:

   <map:selectors default="...">
     <map:selector name="..." src="org.apache.cocoon.selection....">
       <pattern name="empty">^$</pattern>
       <pattern name="number">^[0-9]+$</pattern>
       <pattern name="string">^.+$</pattern>

Each configured pattern can then be referenced in the pipeline section of the site map:

   <map:match ...>
     <map:select type="browser">
       <map:when test="empty">...</map:when>
       <map:when test="number">...</map:when>
       <map:when test="string">...</map:when>

In this class, there is an attribute called pattern, which is used for the mapping of regular expression programs. The constructor of this class is used to create a new instance of AbstractRegexpSelector.

There are three important methods in this class:


Select the pipe segment based on the previously configured mode

public boolean select(String patternName, Object selectorContext) {
    //Check what the context selection returns
    if (selectorContext == null) return(false);
    //Check that we have actually configured a pattern
    REProgram pattern = (REProgram) this.patterns.get(patternName);
    if (pattern == null) {
        if (this.getLogger().isWarnEnabled()) {
            this.getLogger().warn("The specified pattern name \"" + patternName + "\" was not configured in this instance");
    //pattern matching
    return(new RE(pattern).match(selectorContext.toString()));


Configure this instance to resolve all regular expression patterns

public void configure(Configuration configuration)
throws ConfigurationException {
    Configuration patterns[] = configuration.getChildren("pattern");
    for (int x = 0; x < patterns.length; x++) {
        String name = patterns[x].getAttribute("name");
        String pattern = patterns[x].getValue();
        this.patterns.put(name, this.compile(pattern));


Compiling mode in REProgram

protected REProgram compile(String pattern)
throws ConfigurationException {
    if (pattern == null) {
        throw new ConfigurationException("Null pattern");
    if (pattern.length() == 0) {
        pattern = "^$";
        if (this.getLogger().isWarnEnabled()) {
            this.getLogger().warn("The empty pattern string was rewritten to "+ "'^$' to match for empty strings.  If you "+ "intended to match all strings, please " + "change your pattern to '.*'");
    try {
        RECompiler compiler = new RECompiler();
        REProgram program = compiler.compile(pattern);
        return program;
    } catch (RESyntaxException rse) {
        getLogger().debug("Failed to compile the pattern '" + pattern + "'", rse);
        throw new ConfigurationException(rse.getMessage(), rse);


Abstract SwitchSelector class.

There is one method that needs attention:


The selector performs a pattern test on some objects in the Map model and uses the returned Boolean value to signal success

public boolean select(String expr, Map objectModel, Parameters params) {
    return select(expr, getSelectorContext(objectModel, params));


An abstract class that is used to select a selector for a value when it matches certain patterns associated with a selection expression.

Known implementations of this abstract class include browserselector, and hostselector comes from the cocoon sitemap components Maven module.

There are two possible ways to focus:


Check whether value is a substring of one of the patterns associated with expression

protected boolean checkPatterns(String expression, String value) {
    if (value == null) {
        getLogger().debug("No value given -- failing.");
        return false;
    //Gets the mode of the expression
    String[] patterns = (String[])this.strings.get(expression);
    if (patterns == null) {
        getLogger().warn("No configuration for expression '" + expression + "' -- failing.");
        return false;
    for (int i = 0; i < patterns.length; i++) {
        if (value.indexOf(patterns[i]) != -1) {
            getLogger().debug(expression + " selected value " + value);
            return true;
    return false;


Sets the association from the expression to the schema list

protected void configure(Configuration conf, String confName, String nameAttr, String valueAttr)
  throws ConfigurationException {
    Configuration confs[] = conf.getChildren(confName);
    Map configMap = new HashMap();
	//Create a list of strings for each name
    for (int i = 0; i < confs.length; i++) {
        String name = confs[i].getAttribute(nameAttr);
        String value = confs[i].getAttribute(valueAttr);
		//Gets a list of values for the name
        List nameList = (List)configMap.get(name);
        if (nameList == null) {
            nameList = new ArrayList();
            configMap.put(name, nameList);
		//Add current value
	//Convert lists to arrays for faster lookup
    Iterator entries = configMap.entrySet().iterator();
    while(entries.hasNext()) {
        Map.Entry entry = (Map.Entry)entries.next();
        List nameList = (List)entry.getValue();
        entry.setValue(nameList.toArray(new String[nameList.size()]));
    this.strings = configMap;


Create JXPath AbstractFactory for DOM elements

There are two methods to analyze:


If the factory cannot create the requested object, false is returned.

public boolean createObject(JXPathContext context,Pointer pointer,Object parent,String name,int index) 
	//JXPath automatically creates attributes if the element already exists, but does not call this method if the element does not exit
    addDOMElement((Node) parent, index, name);
    return true;


Add DOM element

private void addDOMElement(Node parent, int index, String tag) {
    int pos = tag.indexOf(':');
    String prefix = null;
    if (pos != -1) {
        prefix = tag.substring(0, pos);
    String uri = null;
    Node child = parent.getFirstChild();
    int count = 0;
    while (child != null) {
        if (child.getNodeName().equals(tag)) {
        child = child.getNextSibling();
    Document doc = parent.getOwnerDocument();  
    if (doc != null) {
        uri = getNamespaceURI((Element)parent, prefix);
    } else {
        if (parent instanceof Document) {
            doc = (Document)parent;
            if (prefix != null) {
                throw new RuntimeException("Cannot map non-null prefix " + "when creating a document element");    
        } else { 
            //Should not occur (must be a DocumentType object)
            throw new RuntimeException("Node of class " + parent.getClass().getName() + " has null owner document " + "but is not a Document"); 
	//Continue inserting new elements until the index value is 1
    while (count <= index) {
        Node newElement = doc.createElementNS(uri, tag);


In all blogs, the code I analyzed involves four large folders: core, sitemap impl, thread API and thread impl.

Folder nameFolder role
coreThis module is some kind of wrapper that puts all dependencies together to make it easier to use Cocoon as a web application framework. At present, the complete documentation of all core modules can be found in the Cocoon kernel.
sitemap-implTree handler implementation of site map with dependencies. The Spring package supports Avalon. Abstract base classes and support classes to make it easier to write site map components.
thread-apiInterfaces for thread factories and pools and performing background tasks.
thread-implImplementation of thread pool and factory components. Implementation of components that perform background tasks.

Over the past semester, I have benefited a lot from these 16 code analysis articles. I began to understand the cocoon project from the overall framework, and then gradually went deep into each attribute and method. I learned a lot of knowledge, including the four major features of object-oriented, polymorphism, inheritance, encapsulation, abstraction, DOM, SAX and so on.

Keywords: Java

Added by manichean on Sat, 25 Dec 2021 19:18:38 +0200