Package org.apache.ace.client.repository.impl

Source Code of org.apache.ace.client.repository.impl.ArtifactRepositoryImpl

/*
* 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.ace.client.repository.impl;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ace.client.repository.RepositoryUtil;
import org.apache.ace.client.repository.helper.ArtifactHelper;
import org.apache.ace.client.repository.helper.ArtifactPreprocessor;
import org.apache.ace.client.repository.helper.ArtifactRecognizer;
import org.apache.ace.client.repository.helper.bundle.BundleHelper;
import org.apache.ace.client.repository.object.ArtifactObject;
import org.apache.ace.client.repository.object.GatewayObject;
import org.apache.ace.client.repository.repository.ArtifactRepository;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;

import com.thoughtworks.xstream.io.HierarchicalStreamReader;

/**
* Implementation class for the ArtifactRepository. For 'what it does', see ArtifactRepository,
* for 'how it works', see ObjectRepositoryImpl.<br>
* <br>
* This class has some extended functionality when compared to <code>ObjectRepositoryImpl</code>,
* <ul>
* <li> it keeps track of all <code>ArtifactHelper</code>s, and serves them to its inhabitants.
* <li> it handles importing of artifacts.
* </ul>
*/
public class ArtifactRepositoryImpl extends ObjectRepositoryImpl<ArtifactObjectImpl, ArtifactObject> implements ArtifactRepository {
    private final static String XML_NODE = "artifacts";
    private volatile BundleContext m_context; /*Injected by dependency manager*/
    private volatile LogService m_log; /*Injected by dependency manager*/
    private Map<String, ArtifactHelper> m_helpers = new HashMap<String, ArtifactHelper>();
    private URL m_obrBase;

    /**
     * Custom comparator which sorts service references by service rank, highest rank first.
     */
    private static Comparator<ServiceReference> SERVICE_RANK_COMPARATOR = new Comparator<ServiceReference>() {
        public int compare(ServiceReference o1, ServiceReference o2) {
            int rank1 = 0;
            int rank2 = 0;
            try {
                Object rankObject1 = o1.getProperty(Constants.SERVICE_RANKING);
                rank1 = (rankObject1 == null) ? 0 : ((Integer) rankObject1).intValue();
            }
            catch (ClassCastException cce) {
                // No problem.
            }
            try {
                Object rankObject2 = o2.getProperty(Constants.SERVICE_RANKING);
                rank1 = (rankObject2 == null) ? 0 : ((Integer) rankObject2).intValue();
            }
            catch (ClassCastException cce) {
                // No problem.
            }

            return rank1 - rank2;
        }
    };


    public ArtifactRepositoryImpl(ChangeNotifier notifier) {
        super(notifier, XML_NODE);
    }

    public List<ArtifactObject> getResourceProcessors() {
        try {
            return super.get(createFilter("(" + BundleHelper.KEY_RESOURCE_PROCESSOR_PID + "=*)"));
        }
        catch (InvalidSyntaxException e) {
            m_log.log(LogService.LOG_ERROR, "getResourceProcessors' filter returned an InvalidSyntaxException.", e);
        }
        return new ArrayList<ArtifactObject>();
    }

    @Override
    public List<ArtifactObject> get(Filter filter) {
        // Note that this excludes any Bundle artifacts which are resource processors.
        try {
            Filter extendedFilter = createFilter("(&" + filter.toString() + "(!(" + BundleHelper.KEY_RESOURCE_PROCESSOR_PID + "=*)))");
            return super.get(extendedFilter);
        }
        catch (InvalidSyntaxException e) {
            m_log.log(LogService.LOG_ERROR, "Extending " + filter.toString() + " resulted in an InvalidSyntaxException.", e);
        }
        return new ArrayList<ArtifactObject>();
    }

    @Override
    public List<ArtifactObject> get() {
        // Note that this excludes any Bundle artifacts which are resource processors.
        try {
            return super.get(createFilter("(!(" + RepositoryUtil.escapeFilterValue(BundleHelper.KEY_RESOURCE_PROCESSOR_PID) + "=*))"));
        }
        catch (InvalidSyntaxException e) {
            m_log.log(LogService.LOG_ERROR, "get's filter returned an InvalidSyntaxException.", e);
        }
        return new ArrayList<ArtifactObject>();
    }

    @Override
    ArtifactObjectImpl createNewInhabitant(Map<String, String> attributes) {
        ArtifactHelper helper = getHelper(attributes.get(ArtifactObject.KEY_MIMETYPE));
        return new ArtifactObjectImpl(helper.checkAttributes(attributes), helper.getMandatoryAttributes(), this, this);
    }

    @Override
    ArtifactObjectImpl createNewInhabitant(Map<String, String> attributes, Map<String, String> tags) {
        ArtifactHelper helper = getHelper(attributes.get(ArtifactObject.KEY_MIMETYPE));
        return new ArtifactObjectImpl(helper.checkAttributes(attributes), helper.getMandatoryAttributes(), tags, this, this);
    }

    @Override
    ArtifactObjectImpl createNewInhabitant(HierarchicalStreamReader reader) {
        return new ArtifactObjectImpl(reader, this, this);
    }

    /**
     * Helper method for this repository's inhabitants, which finds the necessary helpers.
     * @param mimetype The mimetype for which a helper should be found.
     * @return An artifact helper for the given mimetype.
     * @throws IllegalArgumentException when the mimetype is invalid, or no helpers are available.
     */
    ArtifactHelper getHelper(String mimetype) {
        synchronized(m_helpers) {
            if ((mimetype == null) || (mimetype.length() == 0)) {
                throw new IllegalArgumentException("Without a mimetype, we cannot find a helper.");
            }

            ArtifactHelper helper = m_helpers.get(mimetype.toLowerCase());

            if (helper == null) {
                throw new IllegalArgumentException("There are no ArtifactHelpers known for type '" + mimetype + "'.");
            }

            return helper;
        }
    }

    /**
     * Method intended for adding artifact helpers by the bundle's activator.
     */
    void addHelper(String mimetype, ArtifactHelper helper) {
        synchronized(m_helpers) {
            if ((mimetype == null) || (mimetype.length() == 0)) {
                m_log.log(LogService.LOG_WARNING, "An ArtifactHelper has been published without a proper mimetype.");
            }
            else {
                m_helpers.put(mimetype.toLowerCase(), helper);
            }
        }
    }

    /**
     * Method intended for removing artifact helpers by the bundle's activator.
     */
    void removeHelper(String mimetype, ArtifactHelper helper) {
        synchronized(m_helpers) {
            if ((mimetype == null) || (mimetype.length() == 0)) {
                m_log.log(LogService.LOG_WARNING, "An ArtifactHelper is being removed without a proper mimetype.");
            }
            else {
                m_helpers.remove(mimetype.toLowerCase());
            }
        }
    }

    /**
     * Utility function that takes either a URL or a String representing a mimetype,
     * and returns the corresponding <code>ArtifactHelper</code>, <code>ArtifactRecognizer</code>
     * and, if not specified, the mimetype.
     * @param input Either a <code>URL</code> pointing to a physical artifact, or a <code>String</code>
     * representing a mime type.
     * @return A mapping from a class (<code>ArtifactRecognizer</code>, <code>ArtifactHelper</code> or
     * <code>String</code> to an instance of that class as a result.
     */
    protected Map<Class<?>, Object> findRecognizerAndHelper(Object input) throws IllegalArgumentException {
        // check input.
        URL url = null;
        String mimetype = null;
        if (input instanceof URL) {
            url = (URL) input;
        }
        else if (input instanceof String) {
            mimetype = (String) input;
        }
        else {
            throw new IllegalArgumentException("findRecognizer received an unrecognized input.");
        }

        // Get all published ArtifactRecognizers.
        ServiceReference[] refs = null;
        try {
            refs = m_context.getServiceReferences(ArtifactRecognizer.class.getName(), null);
        }
        catch (InvalidSyntaxException e) {
            // We do not pass in a filter, so this should not happen.
            m_log.log(LogService.LOG_WARNING, "A null filter resulted in an InvalidSyntaxException from getServiceReferences.");
        }

        if (refs == null) {
            throw new IllegalArgumentException("There are no artifact recognizers available.");
        }

        // If available, sort the references by service ranking.
        Arrays.sort(refs, SERVICE_RANK_COMPARATOR);

        // Check all referenced services to find one that matches our input.
        ArtifactRecognizer recognizer = null;
        String foundMimetype = null;
        for (ServiceReference ref : refs) {
            ArtifactRecognizer candidate = (ArtifactRecognizer) m_context.getService(ref);
            if (mimetype != null) {
                if (candidate.canHandle(mimetype)) {
                    recognizer = candidate;
                    break;
                }
            }
            else {
                String candidateMime = candidate.recognize(url);
                if (candidateMime != null) {
                    foundMimetype = candidateMime;
                    recognizer = candidate;
                    break;
                }
            }
        }

        if (recognizer == null) {
            throw new IllegalArgumentException("There is no artifact recognizer that recognizes artifact " + ((mimetype != null) ? mimetype : url));
        }

        // Package the results in the map.
        Map<Class<?>, Object> result = new HashMap<Class<?>, Object>();
        result.put(ArtifactRecognizer.class, recognizer);
        if (mimetype == null) {
            result.put(ArtifactHelper.class, getHelper(foundMimetype));
            result.put(String.class, foundMimetype);
        }
        else {
            result.put(ArtifactHelper.class, getHelper(mimetype));
        }

        return result;
    }

    public boolean recognizeArtifact(URL artifact) {
        try {
            Map<Class<?>, Object> fromArtifact = findRecognizerAndHelper(artifact);
            String mimetype = (String) fromArtifact.get(String.class);
            return mimetype != null;
        }
        catch (Exception e) {
            //too bad... Nothing to do now.
            return false;
        }
    }

    public ArtifactObject importArtifact(URL artifact, boolean upload) throws IllegalArgumentException, IOException {
        try {
            if ((artifact == null) || (artifact.toString().length() == 0)) {
                throw new IllegalArgumentException("The URL to import cannot be null or empty.");
            }
            checkURL(artifact);

            Map<Class<?>, Object> fromArtifact = findRecognizerAndHelper(artifact);
            ArtifactRecognizer recognizer = (ArtifactRecognizer) fromArtifact.get(ArtifactRecognizer.class);
            ArtifactHelper helper = (ArtifactHelper) fromArtifact.get(ArtifactHelper.class);
            String mimetype = (String) fromArtifact.get(String.class);

            return importArtifact(artifact, recognizer, helper, mimetype, false, upload);
        }
        catch (IllegalArgumentException iae) {
            m_log.log(LogService.LOG_INFO, "Error importing artifact: " + iae.getMessage());
            throw iae;
        }
        catch (IOException ioe) {
            m_log.log(LogService.LOG_INFO, "Error storing artifact: " + ioe.getMessage());
            throw ioe;
        }
    }

    public ArtifactObject importArtifact(URL artifact, String mimetype, boolean upload) throws IllegalArgumentException, IOException {
        try {
            if ((artifact == null) || (artifact.toString().length() == 0)) {
                throw new IllegalArgumentException("The URL to import cannot be null or empty.");
            }
            if ((mimetype == null) || (mimetype.length() == 0)) {
                throw new IllegalArgumentException("The mimetype of the artifact to import cannot be null or empty.");
            }

            checkURL(artifact);

            Map<Class<?>, Object> fromMimetype = findRecognizerAndHelper(mimetype);
            ArtifactRecognizer recognizer = (ArtifactRecognizer) fromMimetype.get(ArtifactRecognizer.class);
            ArtifactHelper helper = (ArtifactHelper) fromMimetype.get(ArtifactHelper.class);

            return importArtifact(artifact, recognizer, helper, mimetype, true, upload);
        }
        catch (IllegalArgumentException iae) {
            m_log.log(LogService.LOG_INFO, "Error importing artifact: " + iae.getMessage());
            throw iae;
        }
        catch (IOException ioe) {
            m_log.log(LogService.LOG_INFO, "Error storing artifact: " + ioe.getMessage());
            throw ioe;
        }
    }

    private ArtifactObject importArtifact(URL artifact, ArtifactRecognizer recognizer, ArtifactHelper helper, String mimetype, boolean overwrite, boolean upload) throws IOException {
        Map<String, String> attributes = recognizer.extractMetaData(artifact);
        Map<String, String> tags = new HashMap<String, String>();

        helper.checkAttributes(attributes);

        URL location;
        if (upload) {
            location = upload(artifact, mimetype);
        }
        else {
            location = artifact;
        }

        attributes.put(ArtifactObject.KEY_URL, location.toString());
        attributes.put(ArtifactObject.KEY_ARTIFACT_DESCRIPTION, "");
        if (overwrite) {
            attributes.put(ArtifactObject.KEY_MIMETYPE, mimetype);
        }

        return create(attributes, tags);
    }

    /**
     * Helper method which checks a given URL for 'validity', that is, does this URL point
     * to something that can be read.
     * @param artifact A URL pointing to an artifact.
     * @throws IllegalArgumentException when the URL does not point to a valid file.
     */

    private void checkURL(URL artifact) throws IllegalArgumentException {
        // First, check whether we can actually reach something from this URL.
        InputStream is = null;
        try {
            is = artifact.openStream();
        }
        catch (IOException ioe) {
            throw new IllegalArgumentException("Artifact " + artifact.toString() + "does not point to a valid file.");
        }
        finally {
            if (is != null) {
                try {
                    is.close();
                }
                catch (IOException ioe) {
                    // Too bad, nothing to do.
                }
            }
        }

        // Then, check whether the name is legal.
        for (byte b : artifact.toString().substring(artifact.toString().lastIndexOf('/') + 1).getBytes()) {
            if (!(((b >= 'A') && (b <= 'Z')) || ((b >= 'a') && (b <= 'z')) || ((b >= '0') && (b <= '9')) || (b == '.') || (b == '-') || (b == '_'))) {
                throw new IllegalArgumentException("Artifact " + artifact.toString() + "'s name contains an illegal character '" + new String(new byte[] {b}) + "'");
            }
        }

    }

    /**
     * Uploads an artifact to the OBR.
     * @param artifact URL pointing to the local artifact.
     * @param mimetype The mimetype of this artifact.
     * @return The persistent URL of this artifact.
     * @throws IOException for any problem uploading the artifact.
     */
    private URL upload(URL artifact, String mimetype) throws IOException {
        if (m_obrBase == null) {
            throw new IOException("There is no storage available for this artifact.");
        }

        InputStream input = null;
        OutputStream output = null;
        URL url = null;
        try {
            input = artifact.openStream();
            url = new URL(m_obrBase, new File(artifact.getFile()).getName());
            URLConnection connection = url.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setRequestProperty("Content-Type", mimetype);
            output = connection.getOutputStream();
            byte[] buffer = new byte[4 * 1024];
            for (int count = input.read(buffer); count != -1; count = input.read(buffer)) {
                output.write(buffer, 0, count);
            }
            output.close();
            if (connection instanceof HttpURLConnection) {
                int responseCode = ((HttpURLConnection) connection).getResponseCode();
                switch (responseCode) {
                    case HttpURLConnection.HTTP_OK :
                        break;
                    case HttpURLConnection.HTTP_CONFLICT:
                        throw new IOException("Artifact already exists in storage.");
                    case HttpURLConnection.HTTP_INTERNAL_ERROR:
                        throw new IOException("The storage server returned an internal server error.");
                    default:
                        throw new IOException("The storage server returned code " + responseCode + " writing to " + url.toString());
                }
            }
        }
        catch (IOException ioe) {
            throw new IOException("Error importing artifact " + artifact.toString() + ": " + ioe.getMessage());
        }
        finally {
            if (input != null) {
                try {
                    input.close();
                }
                catch (Exception ex) {
                    // Not much we can do
                }
            }
            if (output != null) {
                try {
                    output.close();
                }
                catch (Exception ex) {
                    // Not much we can do
                }
            }
        }

        return url;
    }

    public void setObrBase(URL obrBase) {
        m_obrBase = obrBase;
    }

    public String preprocessArtifact(ArtifactObject artifact, GatewayObject gateway, String gatewayID, String version) throws IOException {
        ArtifactPreprocessor preprocessor = getHelper(artifact.getMimetype()).getPreprocessor();
        if (preprocessor == null) {
            return artifact.getURL();
        }
        else {
            return preprocessor.preprocess(artifact.getURL(), new GatewayPropertyResolver(gateway), gatewayID, version, m_obrBase);
        }
    }

    public boolean needsNewVersion(ArtifactObject artifact, GatewayObject gateway, String gatewayID, String fromVersion) {
        ArtifactPreprocessor preprocessor = getHelper(artifact.getMimetype()).getPreprocessor();
        if (preprocessor == null) {
            return false;
        }
        else {
            return preprocessor.needsNewVersion(artifact.getURL(), new GatewayPropertyResolver(gateway), gatewayID, fromVersion);
        }
    }
   

    public URL getObrBase() {
        return m_obrBase;
    }
}
TOP

Related Classes of org.apache.ace.client.repository.impl.ArtifactRepositoryImpl

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.