org.ops4j.pax.scanner.obr.internal.ObrScanner.java Source code

Java tutorial

Introduction

Here is the source code for org.ops4j.pax.scanner.obr.internal.ObrScanner.java

Source

/*
 * Copyright 2008 Alin Dreghiciu.
 *
 * Licensed  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.ops4j.pax.scanner.obr.internal;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.ops4j.lang.NullArgumentException;
import org.ops4j.net.URLUtils;
import org.ops4j.pax.scanner.MalformedSpecificationException;
import org.ops4j.pax.scanner.ProvisionSpec;
import org.ops4j.pax.scanner.ScannedBundle;
import org.ops4j.pax.scanner.Scanner;
import org.ops4j.pax.scanner.ScannerException;
import org.ops4j.pax.scanner.common.ScannedFileBundle;
import org.ops4j.pax.scanner.common.ScannerConfiguration;
import org.ops4j.pax.scanner.common.ScannerConfigurationImpl;
import org.ops4j.pax.scanner.common.SystemPropertyUtils;
import org.ops4j.util.property.PropertyResolver;

/**
 * A scanner that scans obr specs.
 *
 * @author Alin Dreghiciu
 * @since 0.7.0, February 04, 2008
 */
public class ObrScanner implements Scanner {

    /**
     * Logger.
     */
    private static final Log LOG = LogFactory.getLog(ObrScanner.class);
    /**
     * The starting character for a comment line.
     */
    private static final String COMMENT_SIGN = "#";
    /**
     * Prefix for properties.
     */
    private static final String PROPERTY_PREFIX = "-D";
    /**
     * Regex pattern used to spint property key/value.
     */
    private static final Pattern PROPERTY_PATTERN = Pattern.compile("-D(.*)=(.*)");

    /**
     * PropertyResolver used to resolve properties.
     */
    private PropertyResolver m_propertyResolver;
    /**
     * Filter syntax validator.
     */
    private final FilterValidator m_filterValidator;
    /**
     * OBR Script file. Lazy created on first use.
     */
    private File m_scriptFile;

    /**
     * Creates a new file scanner.
     *
     * @param propertyResolver a propertyResolver; mandatory
     * @param filterValidator  filter syntax validator
     */
    public ObrScanner(final PropertyResolver propertyResolver, final FilterValidator filterValidator) {
        NullArgumentException.validateNotNull(propertyResolver, "Property resolver");
        NullArgumentException.validateNotNull(filterValidator, "Filter syntax validator");

        m_propertyResolver = propertyResolver;
        m_filterValidator = filterValidator;
    }

    /**
     * Reads the bundles from the file specified by the urlSpec.
     * {@inheritDoc}
     */
    public List<ScannedBundle> scan(final ProvisionSpec provisionSpec)
            throws MalformedSpecificationException, ScannerException {
        NullArgumentException.validateNotNull(provisionSpec, "Provision spec");
        LOG.debug("Scanning [" + provisionSpec.getPath() + "]");
        final List<ScannedBundle> scannedBundles = new ArrayList<ScannedBundle>();
        final ScannerConfiguration config = createConfiguration();
        BufferedReader bufferedReader = null;
        BufferedWriter bufferedWriter = null;
        try {
            try {
                bufferedReader = new BufferedReader(new InputStreamReader(
                        URLUtils.prepareInputStream(provisionSpec.getPathAsUrl(), !config.getCertificateCheck())));
                final Integer defaultStartLevel = getDefaultStartLevel(provisionSpec, config);
                final Boolean defaultStart = getDefaultStart(provisionSpec, config);
                final Boolean defaultUpdate = getDefaultUpdate(provisionSpec, config);
                // we always install the obr and pax scanner obr script
                scannedBundles.add(new ScannedFileBundle("mvn:org.apache.felix/org.apache.felix.bundlerepository",
                        defaultStartLevel, defaultStart, defaultUpdate));
                scannedBundles.add(new ScannedFileBundle("mvn:org.ops4j.pax.scanner/pax-scanner-obr-script",
                        defaultStartLevel, defaultStart, defaultUpdate));

                // and we set the script name
                final File scriptFile = createScript();
                System.setProperty("org.ops4j.pax.scanner.obr.script", scriptFile.toURI().toASCIIString());
                // and repositories property
                System.setProperty("obr.repository.url", m_propertyResolver.get("obr.repository.url"));

                bufferedWriter = new BufferedWriter(new FileWriter(scriptFile));
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    if (!"".equals(line.trim()) && !line.trim().startsWith(COMMENT_SIGN)) {
                        if (line.trim().startsWith(PROPERTY_PREFIX)) {
                            final Matcher matcher = PROPERTY_PATTERN.matcher(line.trim());
                            if (!matcher.matches() || matcher.groupCount() != 2) {
                                throw new ScannerException("Invalid property: " + line);
                            }
                            String value = matcher.group(2);
                            value = SystemPropertyUtils.resolvePlaceholders(value);
                            System.setProperty(matcher.group(1), value);
                        } else {
                            line = SystemPropertyUtils.resolvePlaceholders(line);
                            final String obrFilter = createObrFilter(line);
                            bufferedWriter.append(obrFilter);
                            bufferedWriter.newLine();
                        }
                    }
                }
            } finally {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
                if (bufferedWriter != null) {
                    bufferedWriter.flush();
                    bufferedWriter.close();
                }
            }
        } catch (IOException e) {
            throw new ScannerException("Could not parse the provision file", e);
        }
        return scannedBundles;
    }

    /**
     * Creates a script file if the script was not already created before.
     *
     * @return created script file.
     *
     * @throws IOException if script file cannot be created
     */
    private File createScript() throws IOException {
        if (m_scriptFile == null) {
            m_scriptFile = File.createTempFile("pax.scanner.obr.script", ".txt");
            // TODO remove the script file on exit
            //m_scriptFile.deleteOnExit();
            LOG.trace("Created script file [" + m_scriptFile.getCanonicalPath() + "]");
        } else {
            LOG.trace("Using script file [" + m_scriptFile.getCanonicalPath() + "]");
        }
        return m_scriptFile;
    }

    /**
     * Creates an obr filter from a symbolic-name/version by transforming it to
     * (&(symbolicname=symbolic-name)(version=version))
     *
     * @param path obr spec
     *
     * @return an obr filter
     *
     * @throws MalformedURLException if path doesn't conform to expected syntax or contains invalid chars
     */
    private String createObrFilter(final String path) throws MalformedURLException {
        if (path == null || path.trim().length() == 0) {
            throw new MalformedURLException("OBR spec cannot be null or empty");
        }
        final String[] segments = path.split("/");
        if (segments.length > 2) {
            throw new MalformedURLException("OBR spec cannot contain more then one '/'");
        }
        final StringBuilder builder = new StringBuilder();
        // add bundle symbolic name filter
        builder.append("(symbolicname=").append(segments[0]).append(")");
        if (!m_filterValidator.validate(builder.toString())) {
            throw new MalformedURLException("Invalid symbolic name value.");
        }
        // add bundle version filter
        if (segments.length > 1) {
            builder.insert(0, "(&").append("(version=").append(segments[1]).append("))");
            if (!m_filterValidator.validate(builder.toString())) {
                throw new MalformedURLException("Invalid version value.");
            }
        }
        return builder.toString();
    }

    /**
     * Returns the default start level by first looking at the parser and if not set fallback to configuration.
     *
     * @param provisionSpec provision spec
     * @param config        a configuration
     *
     * @return default start level or null if nos set.
     */
    private Integer getDefaultStartLevel(ProvisionSpec provisionSpec, ScannerConfiguration config) {
        Integer startLevel = provisionSpec.getStartLevel();
        if (startLevel == null) {
            startLevel = config.getStartLevel();
        }
        return startLevel;
    }

    /**
     * Returns the default start by first looking at the parser and if not set fallback to configuration.
     *
     * @param provisionSpec provision spec
     * @param config        a configuration
     *
     * @return default start level or null if nos set.
     */
    private Boolean getDefaultStart(final ProvisionSpec provisionSpec, final ScannerConfiguration config) {
        Boolean start = provisionSpec.shouldStart();
        if (start == null) {
            start = config.shouldStart();
        }
        return start;
    }

    /**
     * Returns the default update by first looking at the parser and if not set fallback to configuration.
     *
     * @param provisionSpec provision Spec
     * @param config        a configuration
     *
     * @return default update or null if nos set.
     */
    private Boolean getDefaultUpdate(final ProvisionSpec provisionSpec, final ScannerConfiguration config) {
        Boolean update = provisionSpec.shouldUpdate();
        if (update == null) {
            update = config.shouldUpdate();
        }
        return update;
    }

    /**
     * Sets the propertyResolver to use.
     *
     * @param propertyResolver a propertyResolver
     */
    public void setResolver(final PropertyResolver propertyResolver) {
        NullArgumentException.validateNotNull(propertyResolver, "Property resolver");
        m_propertyResolver = propertyResolver;
    }

    /**
     * Creates a new configuration.
     *
     * @return a configuration
     */
    ScannerConfiguration createConfiguration() {
        return new ScannerConfigurationImpl(m_propertyResolver, org.ops4j.pax.scanner.obr.ServiceConstants.PID);
    }

}