Package org.apache.cocoon.components.source.helpers

Source Code of org.apache.cocoon.components.source.helpers.DelaySourceRefresher$RefresherTask

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cocoon.components.source.helpers;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.MalformedURLException;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.avalon.framework.CascadingException;
import org.apache.avalon.framework.CascadingRuntimeException;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.component.WrapperComponentManager;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.SAXConfigurationHandler;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.SourceException;
import org.apache.excalibur.source.SourceResolver;

import org.apache.cocoon.Constants;
import org.apache.cocoon.Processor;
import org.apache.cocoon.components.CocoonComponentManager;
import org.apache.cocoon.components.source.SourceUtil;
import org.apache.cocoon.components.thread.RunnableManager;
import org.apache.cocoon.environment.background.BackgroundEnvironment;
import org.apache.cocoon.util.NetUtils;

/**
* Default implementation of the refresher.
*
* @since 2.1.1
* @version $Id: DelaySourceRefresher.java 485495 2006-12-11 04:44:23Z crossley $
*/
public class DelaySourceRefresher extends AbstractLogEnabled
                                  implements Contextualizable, Serviceable, Configurable,
                                             Disposable, ThreadSafe, SourceRefresher {

    private static final String PARAM_WRITE_FILE          = "write-file";

  private static final String DEFAULT_WRITE_FILE        = "refresher-targets.xml";

    private static final String TAGNAME_TARGET            = "target";
  private static final String ATTR_KEY                  = "key";
  private static final String ATTR_URI                  = "uri";
    private static final String ATTR_INTERVAL             = "interval";


    protected Context context;

    // service dependencies
    protected ServiceManager manager;
    protected SourceResolver resolver;
    protected RunnableManager runnable;

    // the scheduled targets to be persisted and recovered upon restart
    protected Map entries = Collections.synchronizedMap(new HashMap());

    // the cocoon working directory
    protected File workDir;

    /** The source to persist refresher entries into */
    protected File configFile;

    // whether anything changed to the entries since last persisting them
    protected volatile boolean changed;

    protected ConfigurationTask configurationTask;


    // ---------------------------------------------------- Lifecycle

  /* (non-Javadoc)
   * @see Contextualizable#contextualize(Context)
   */
  public void contextualize(Context context) throws ContextException {
        this.context = context;
        this.workDir = (File) context.get(Constants.CONTEXT_WORK_DIR);
  }

    /* (non-Javadoc)
     * @see Serviceable#service(ServiceManager)
     */
    public void service(ServiceManager manager) throws ServiceException {
        this.manager = manager;
        this.resolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE);
        this.runnable = (RunnableManager) this.manager.lookup(RunnableManager.ROLE);
    }

    public void configure(Configuration configuration) throws ConfigurationException {
        Parameters parameters = Parameters.fromConfiguration(configuration);
        long interval = parameters.getParameterAsLong("interval", 0);
        if (interval > 0) {
            String fileName = parameters.getParameter(PARAM_WRITE_FILE, DEFAULT_WRITE_FILE);
            this.configFile = new File(this.workDir, fileName);
            if (this.configFile.exists() && !this.configFile.canWrite()) {
                throw new ConfigurationException("Parameter 'write-source' resolves to not modifiable file: " +
                                                 this.configFile);
            }
            if (!this.configFile.getParentFile().exists() && !this.configFile.getParentFile().mkdirs()) {
                throw new ConfigurationException("Can not create parent directory for: " +
                                                 this.configFile);
            }
            if (getLogger().isDebugEnabled()) {
                getLogger().debug("Write source location: " + this.configFile);
            }

            setupRefreshJobs(readRefreshJobConfiguration());
            startConfigurationTask(interval);
        } else {
          if (getLogger().isInfoEnabled()) {
        getLogger().info("Not writing update targets to file.");
          }
        }

        // Setup any in-line configured tasks
        setupRefreshJobs(configuration);
    }

    /* (non-Javadoc)
     * @see Disposable#dispose()
     */
    public void dispose() {
      stopConfigurationTask();
        if (this.runnable != null) {
            this.manager.release(this.runnable);
            this.runnable = null;
        }
        if (this.resolver != null) {
            this.manager.release(this.resolver);
            this.resolver = null;
        }
        this.manager = null;
    }

    // ---------------------------------------------------- SourceRefresher implementation

    /* (non-Javadoc)
     * @see SourceRefresher#refresh
     */
    public void refresh(String name,
                        String uri,
                        Parameters parameters)
    throws SourceException {
    final long interval = parameters.getParameterAsLong(PARAM_CACHE_INTERVAL, -1);
    if (uri != null && interval > 0) {
            addRefreshSource(name, uri, interval, interval);
    } else {
            removeRefreshSource(name);
        }
    }

    protected void addRefreshSource(String key, String uri, long delay, long interval) {
        RefresherTask task = (RefresherTask) this.entries.get(key);
        if (task == null) {
            // New source added.
            task = new RefresherTask(key, uri, interval);
            task.enableLogging(getLogger());
            this.entries.put(key, task);
            this.runnable.execute(task, interval, interval);
            this.changed = true;
        } else if (task.interval != interval) {
            // Existing source refresh interval updated.
            task.update(uri, interval);
            this.runnable.remove(task);
            this.runnable.execute(task, interval, interval);
            this.changed = true;
        } else {
            // No change.
        }
    }

    protected void removeRefreshSource(String key) {
        RefresherTask task = (RefresherTask) this.entries.get(key);
        if (task != null) {
            this.entries.remove(key);
            this.runnable.remove(task);
            this.changed = true;
        }
    }

    // ---------------------------------------------------- Implementation

    /**
     *
     */
    private Configuration readRefreshJobConfiguration() {
        Source source = null;
        SAXConfigurationHandler b = new SAXConfigurationHandler();
        try {
            if (this.configFile.exists()) {
                source = this.resolver.resolveURI(this.configFile.toURL().toString());
                SourceUtil.toSAX(this.manager, source, source.getMimeType(), b);
            }
        } catch (Exception ignore) {
            getLogger().warn("Unable to read configuration from " + this.configFile);
        } finally {
            if (source != null) {
                this.resolver.release(source);
            }
        }
        return b.getConfiguration();
    }


    /**
   * @param conf
   */
  private void setupRefreshJobs(final Configuration conf) {
        if (conf != null) {
            final Configuration[] children = conf.getChildren(TAGNAME_TARGET);
            if (children != null) {
                for (int i = 0; i < children.length; i++) {
                    try {
                        setupSingleRefreshJob(children[i]);
                    } catch (CascadingException ignore) {
                        if (getLogger().isDebugEnabled()) {
                            getLogger().debug("Setting up refresh job, ignoring exception:", ignore);
                        }
                    }
                }
            }
        }
    }

    /**
   * @param conf
   * @throws ConfigurationException
   */
  private void setupSingleRefreshJob(final Configuration conf) throws ConfigurationException {
    try {
            String key = NetUtils.decode(conf.getAttribute(ATTR_KEY), "utf-8");
            String uri = NetUtils.decode(conf.getAttribute(ATTR_URI), "utf-8");
            long interval = conf.getAttributeAsLong(ATTR_INTERVAL);
            addRefreshSource(key, uri, 10, interval);
        } catch (UnsupportedEncodingException e) {
            /* Won't happen */
        }
  }

    /**
     * @param interval
     */
    protected void startConfigurationTask(long interval) {
        configurationTask = new ConfigurationTask();
        configurationTask.enableLogging(getLogger());
        runnable.execute(configurationTask, interval, interval);
    }

    protected void stopConfigurationTask() {
        if (this.configurationTask != null) {
            this.runnable.remove(this.configurationTask);
            this.configurationTask.run();
            this.configurationTask = null;
        }
    }

    /**
     * Task which writes refresher configuraiton into the source.
     */
    protected class ConfigurationTask extends AbstractLogEnabled
                                      implements Runnable {
        public void run() {
            if (changed) {
                // Reset the flag.
                changed = false;

                boolean success = true;
                Writer writer = null;
                try {
                    writer = new OutputStreamWriter(new FileOutputStream(configFile), "utf-8");
                    writer.write("<targets>\n");

                    try {
                        final Iterator i = entries.values().iterator();
                        while (i.hasNext()) {
                            RefresherTask task = (RefresherTask) i.next();
                            writer.write(task.toXML());
                        }
                    } catch (ConcurrentModificationException e) {
                        // List of targets has been changed, unable to save it completely.
                        // Will re-try writing the list next time.
                        success = false;
                    }

                    writer.write("</targets>\n");
                } catch (IOException e) {
                    // Got I/O exception while writing the list.
                    // Will re-try writing the list next time.
                    success = false;
                    if (getLogger().isDebugEnabled()) {
                        getLogger().debug("Error writing targets to file.", e);
                    }
                } finally {
                    if (writer != null) {
                        try {
                            writer.close();
                        } catch (IOException e) { /* ignored */ }
                    }
                }

                // Set the flag to run next time if failed this time
                if (!success) {
                    changed = true;
                }
            }
        }
    }

    protected class RefresherTask extends AbstractLogEnabled
                                  implements Runnable {
        private String key;
        private String uri;
        private long interval;


        public RefresherTask(String key, String uri, long interval) {
            this.key = key;
            this.uri = uri;
            this.interval = interval;
        }

        public void run() {
            if (this.uri != null) {
                if (getLogger().isDebugEnabled()) {
                    getLogger().debug("Refreshing " + this.uri);
                }

                // Setup Environment
                final BackgroundEnvironment env;
                try {
                    org.apache.cocoon.environment.Context ctx =
                            (org.apache.cocoon.environment.Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);
                    env = new BackgroundEnvironment(getLogger(), ctx);
                } catch (ContextException e) {
                    throw new CascadingRuntimeException("No context found", e);
                } catch (MalformedURLException e) {
                    // Unlikely to happen
                    throw new CascadingRuntimeException("Invalid URL", e);
                }
                Processor processor;
                try {
                    processor = (Processor) manager.lookup(Processor.ROLE);
                } catch (ServiceException e) {
                    throw new CascadingRuntimeException("No processor found", e);
                }

                final Object key = CocoonComponentManager.startProcessing(env);
                CocoonComponentManager.enterEnvironment(env, new WrapperComponentManager(manager), processor);
                try {
                    // Refresh Source
                    Source source = null;
                    try {
                        source = resolver.resolveURI(uri);
                        source.refresh();
                    } catch (IOException e) {
                        getLogger().error("Error refreshing source", e);
                    } finally {
                        if (source != null) {
                            resolver.release(source);
                        }
                    }
                } finally {
                    CocoonComponentManager.leaveEnvironment();
                    CocoonComponentManager.endProcessing(env, key);
                    if (manager != null) {
                        manager.release(processor);
                    }
                }
            }
        }

        public void update(String uri, long interval) {
            this.uri = uri;
            this.interval = interval;
        }

        public String toXML() {
            String key = null;
            String uri = null;
            try {
                key = NetUtils.encode(this.key, "utf-8");
                uri = NetUtils.encode(this.uri, "utf-8");
            } catch (UnsupportedEncodingException e) {
                /* Won't happen */
            }
            StringBuffer s = new StringBuffer();
            s.append('<').append(TAGNAME_TARGET).append(' ');
            s.append(ATTR_KEY).append("=\"").append(key).append("\" ");
            s.append(ATTR_URI).append("=\"").append(uri).append("\" ");
            s.append(ATTR_INTERVAL).append("=\"").append(interval).append("\" />\n");
            return s.toString();
        }
    }
}
TOP

Related Classes of org.apache.cocoon.components.source.helpers.DelaySourceRefresher$RefresherTask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.