2021SC@SDUSC【软件工程应用与实践】Cocoon项目9-分析core文件夹(八)

2021SC@SDUSC

分析core文件夹(八)

IncludeTransformer.java(下)

这篇博客中我将继续分析IncludeTransformer.java里的两个内部类class IncludeElementclass IncludeXMLPipe

class IncludeElement

1.属性:

//参数控制递归包括处理。
private boolean recursive;
//参数控制并行(在多个线程)包括处理。
private boolean parallel;
//参数控制并行(在多个线程)包括处理递归。
private boolean recursiveParallel;
//源URI。
private String base;
//include元素的src属性中声明的要包含的源URI。
protected String source;
//指示源内容是否必须解析为XML或包含为文本的标志。
protected boolean parse;
//mime类型提示。
protected String mimeType;
//指示是否剥离根元素的标志。
protected boolean stripRoot;
//缓冲区收集回退内容。
protected SaxBuffer fallback;
//提供给包含的源的参数的Map。
protected Map parameters;
//当前捕获的参数名。
protected String parameter;
//当前参数值(作为StringBuffer)。
protected StringBuffer value;

2.方法:

构造函数:

  • 创建include元素。
  • 传入的参数有:String base, boolean parallel, boolean recursive, boolean recursiveParallel

成员函数:

  • public void process(SaxBuffer buffer)
    • 处理元素进入缓冲区。
    • 这不能被共享缓冲区,因为如果调用回退,它必须被清理。
    • 直接调用process0(buffer, buffer);
  • public void process(ContentHandler contentHandler, LexicalHandler lexicalHandler)
    • 重载上一个成员函数。
    • 将URI加载到提供的处理程序中,处理回退。
    • 如果有回退,就调用process(buffer);,否则调用process0(contentHandler, lexicalHandler);
  • private void process0(ContentHandler contentHandler, LexicalHandler lexicalHandler)
    • 将URI加载到提供的处理程序中。
    • 设置这个线程的环境。
      • 通过判断base是否为空,来决定resolver.resolveURI()的参数。
      • 如果validity不为空,就进行同步处理。
      • 如果parse和recursive均存在,则直接调用toSAX()函数;如果只有parse存在,则创建一个IncludeXMLConsumer对象,设置setIgnoreRootElement,最后调用toSAX()函数;否则,就直接将source转换为utf-8的字符。
      • 最后是一些异常的处理,包括IOException,ProcessingException等。

class IncludeXMLPipe

1.属性:

配置:

//表示这是根管道(由变转换器拥有)还是嵌套管道。
private final boolean root;
//参数控制递归包括处理。
private boolean recursive;
//参数控制并行(在多个线程)包括处理。
private boolean parallel;
//参数控制并行(在多个线程)包括处理递归。
private boolean recursiveParallel;

当前状态:

//XMLConsumers栈。
private final Stack consumers = new Stack();
//include命名空间中嵌套元素的当前深度。
private int depth;
//用于解析包含源的Base URI。
private String base;
//在include元素中声明的要被包含的源。
private IncludeElement element;
//如果启用并行处理,则这个布尔值告诉我们是否已经开始缓冲。
private boolean buffering;
//在打开并行处理时,用于缓冲事件的IncludeBuffer。
//此对象也用作线程计数器线程的锁。
private SaxBuffer buffer;
//包含线程/任务计数器(如果并行执行)。
private int threads;

2.方法:

构造函数:

public IncludeXMLPipe() {
    root = true;
}

public IncludeXMLPipe(ContentHandler contentHandler, LexicalHandler lexicalHandler,boolean recursive, boolean parallel, boolean recursiveParallel) {
    root = false;
    setContentHandler(contentHandler);
    setLexicalHandler(lexicalHandler);
    this.recursive = recursive;
    this.parallel = parallel;
    this.recursiveParallel = recursiveParallel;
}

创建一个新的IncludeXMLPipe实例。

成员函数:

  • public void recycle()
    • 完成处理。
  • private void push(XMLConsumer consumer)
    • 将当前用户推入堆栈,用新的用户替换。
  • private void pop()
    • 从堆栈中取出用户,替换当前用户。
  • public void setDocumentLocator(Locator locator)
    • 设置文档定位器。
    • 能够解析文档基本URI。
  • public void startDocument()
    • 接收XML文档开始的通知。
  • public void endDocument()
    • 接收XML文档结束的通知。
    • 这是该行的结束——处理缓冲事件。
  • public void startElement(String uri, String localName, String qName, Attributes atts)
    • 接收元素开始的通知。
    • 检查名称空间声明。
      • 如果相等,则include命名空间中嵌套元素的当前深度减1。
      • if (INCLUDE_ELEMENT.equals(localName) && depth == 0)
        • 包含不会在这里发生,但当我们关闭这个标签时。
        • 在包含之前进行检查(我们不想要嵌套的东西),记住我们试图包含的源代码。
        • 然后进行处理,默认为“xml”,默认为“text / xml”/,默认为false。
        • 忽略嵌套的内容,最后结束。
      • if (FALLBACK_ELEMENT.equals(localName) && depth == 1)
        • 如果这是一个回退参数,则捕获它的内容。
        • 检查我们是否在正确的上下文中,缓冲区回退,完成。
      • if (PARAMETER_ELEMENT.equals(localName) && depth == 1)
        • 如果这是一个参数,那么确保我们准备好了。
        • 检查我们是否在正确的上下文中。
        • 获取并处理参数名,完成。
      • if (depth < 2)
        • 错误的元素。
  • public void endElement(String uri, String localName, String qName)
    • 接收元素结束的通知。
    • 检查名称空间声明。
      • 如果相等,则include命名空间中嵌套元素的当前深度减1。
      • if (INCLUDE_ELEMENT.equals(localName) && depth == 0)
      • 当我们关闭include元素时,包含将在这里发生。
        • 结束忽略嵌套内容。
        • 获取打开元素和include时发现的源代码。
        • 并行处理检查,然后结束。
      • if (FALLBACK_ELEMENT.equals(localName) && depth == 1)
        • 结束缓冲回退内容,该元素已完成。
      • if (PARAMETER_ELEMENT.equals(localName) && depth == 1)
        • 存储参数名称和值,参数名和值是URL编码的,所以像"&“或”="(有特殊含义)这样的奇怪字符可以完美地传递,完成。
    • 如果不是我们的名称空间,请继续传递事件。
  • public void characters(char[] data, int offset, int length)
    • 接收字符通知。
    • 如果我们有一个要添加的参数值,让我们添加这个块。
  • 线程管理:
    • int incrementThreads()
      • 活动线程递增计数器。
    • void decrementThreads()
      • 活动线程递减计数器。
    • private void waitForThreads()
      • 等待直到没有活跃线程。
      • 如果线程数目大于0,就在日志里进行记录,并等待。

3.其他说明:

一个在内部类里的内部类:

在单独的线程中加载包含源的缓冲区。只有当源完全加载时,才可能对加载的缓冲区进行流处理。如果加载未完成,toSAX方法将阻塞。

private class IncludeBuffer extends SaxBuffer implements Runnable {
    private IncludeElement element;
    private int thread;
    private boolean finished;
    private SAXException e;
    //构造函数
    public IncludeBuffer(IncludeElement element) {
        this.element = element;
        RunnableManager runnable = null;
        try {
            runnable = (RunnableManager) IncludeTransformer.this.manager.lookup(RunnableManager.ROLE);
            runnable.execute(IncludeTransformer.this.threadPool, this);
        } catch (final ServiceException e) {
            //以防我们无法生成一个线程
            throw new CascadingRuntimeException(e.getMessage(), e);
        } finally {
            IncludeTransformer.this.manager.release(runnable);
        }
        //递增活动线程计数器
        this.thread = incrementThreads();
    }
    //将源的内容加载到这个缓冲区中。
    public void run() {
        try {
            if (getLogger().isDebugEnabled()) {
                getLogger().debug("Thread #" + thread + " loading <" + element.source + ">");
            }
            //设置这个线程的环境
            RequestContextHolder.setRequestAttributes(attributes);
            EnvironmentHelper.enterProcessor(processor, environment);
            try {
                element.process(this);

            } catch (SAXException e) {
                this.e = e;

            } finally {
                EnvironmentHelper.leaveProcessor();
                RequestContextHolder.resetRequestAttributes();
            }
        } catch (ProcessingException e) {
            //无法设置线程的环境
            this.e = new SAXException(e);
        } finally {
            synchronized (this) {
                this.finished = true;
                notify();
            }
            //确保活动线程计数器是递减的
            decrementThreads();
        }
        if (getLogger().isDebugEnabled()) {
            if (this.e == null) {
                getLogger().debug("Thread #" + thread + " loaded <" + element.source + ">");
            } else {
                getLogger().debug("Thread #" + thread + " failed to load <" + element.source + ">", this.e);
            }
        }
    }
    //该缓冲区的流内容时,它是完全加载。如果加载未完成,此方法将阻塞。
    public void toSAX(ContentHandler contentHandler)
    throws SAXException {
        synchronized (this) {
            if (!this.finished) {
                try {
                    wait();
                } catch (InterruptedException e) { /* ignored */ }
                // Don't continue waiting if interrupted.
            }
        }
        if (this.e != null) {
            throw this.e;
        }
        super.toSAX(contentHandler);
    }
}

4.关键字synchronized

synchronized是Java中的关键字,被Java原生支持,是一种最基本的同步锁。
它修饰的对象有以下几种:

1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象。
  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象。
  3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象。
  4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

上一篇:ConcurrentHashMap 使用:每个 Key 只调用 1 个方法


下一篇:OpenStack是什么,OpenStack详解