Package org.apache.ace.it.repositoryadmin

Source Code of org.apache.ace.it.repositoryadmin.TemplateProcessorTest$MockArtifactPreprocessor

/*
* 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.it.repositoryadmin;

import static org.apache.ace.client.repository.stateful.StatefulTargetObject.TOPIC_ADDED;
import static org.apache.ace.client.repository.stateful.StatefulTargetObject.TOPIC_STATUS_CHANGED;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Callable;

import org.apache.ace.client.repository.RepositoryAdminLoginContext;
import org.apache.ace.client.repository.helper.ArtifactHelper;
import org.apache.ace.client.repository.helper.ArtifactPreprocessor;
import org.apache.ace.client.repository.helper.PropertyResolver;
import org.apache.ace.client.repository.helper.bundle.BundleHelper;
import org.apache.ace.client.repository.object.Artifact2FeatureAssociation;
import org.apache.ace.client.repository.object.ArtifactObject;
import org.apache.ace.client.repository.object.DeploymentArtifact;
import org.apache.ace.client.repository.object.DeploymentVersionObject;
import org.apache.ace.client.repository.object.DistributionObject;
import org.apache.ace.client.repository.object.FeatureObject;
import org.apache.ace.client.repository.object.TargetObject;
import org.apache.ace.client.repository.stateful.StatefulTargetObject;
import org.apache.ace.client.repository.stateful.StatefulTargetObject.StoreState;
import org.apache.felix.dm.Component;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.useradmin.User;

/**
* Test cases for the template processing functionality.
*/
public class TemplateProcessorTest extends BaseRepositoryAdminTest {

  static class MockArtifactHelper implements ArtifactHelper {
      private final String m_mimetype;
      private final ArtifactPreprocessor m_preprocessor;
 
      MockArtifactHelper(String mimetype) {
          this(mimetype, null);
      }
 
      MockArtifactHelper(String mimetype, ArtifactPreprocessor preprocessor) {
          m_mimetype = mimetype;
          m_preprocessor = preprocessor;
      }
 
      public boolean canUse(ArtifactObject object) {
          return object.getMimetype().equals(m_mimetype);
      }
 
      public Map<String, String> checkAttributes(Map<String, String> attributes) {
          return attributes;
      }
 
      public <TYPE extends ArtifactObject> String getAssociationFilter(TYPE obj, Map<String, String> properties) {
          return ("(" + ArtifactObject.KEY_URL + "=" + obj.getURL() + ")");
      }
 
      public <TYPE extends ArtifactObject> int getCardinality(TYPE obj, Map<String, String> properties) {
          return 1;
      }
 
      public Comparator<ArtifactObject> getComparator() {
          return null;
      }
 
      public String[] getDefiningKeys() {
          return new String[] { ArtifactObject.KEY_URL };
      }
 
      public String[] getMandatoryAttributes() {
          return new String[] { ArtifactObject.KEY_URL };
      }
 
      public ArtifactPreprocessor getPreprocessor() {
          return m_preprocessor;
      }
  };
 
  static class MockArtifactPreprocessor implements ArtifactPreprocessor {
      private PropertyResolver m_props;
 
      public boolean needsNewVersion(String url, PropertyResolver props, String targetID, String fromVersion) {
          return false;
      }
 
      public String preprocess(String url, PropertyResolver props, String targetID, String version, URL obrBase)
          throws IOException {
          m_props = props;
          return url;
      }
 
      PropertyResolver getProps() {
          return m_props;
      }
  }

    public void testStatefulApprovalWithArtifacts() throws Exception {
        setupRepository();
       
        // some setup: we need a helper.
        ArtifactHelper myHelper = new MockArtifactHelper("mymime");

        Properties serviceProps = new Properties();
        serviceProps.put(ArtifactHelper.KEY_MIMETYPE, "mymime");

        Component myHelperService = m_dependencyManager.createComponent()
            .setInterface(ArtifactHelper.class.getName(), serviceProps)
            .setImplementation(myHelper);

        m_dependencyManager.add(myHelperService);

        // Empty tag map to be reused througout test
        final Map<String, String> tags = new HashMap<String, String>();

        // First, create a bundle and two artifacts, but do not provide a processor for the artifacts.
        ArtifactObject b1 = createBasicBundleObject("bundle1");
        Map<String, String> attr = new HashMap<String, String>();
        attr.put(ArtifactObject.KEY_URL, "http://myobject");
        attr.put(ArtifactObject.KEY_PROCESSOR_PID, "my.processor.pid");
        attr.put(ArtifactHelper.KEY_MIMETYPE, "mymime");

        ArtifactObject a1 = m_artifactRepository.create(attr, tags);

        attr = new HashMap<String, String>();
        attr.put(ArtifactObject.KEY_URL, "http://myotherobject");
        attr.put(ArtifactObject.KEY_PROCESSOR_PID, "my.processor.pid");
        attr.put(ArtifactObject.KEY_RESOURCE_ID, "mymime");
        attr.put(ArtifactHelper.KEY_MIMETYPE, "mymime");

        ArtifactObject a2 = m_artifactRepository.create(attr, tags);

        FeatureObject g = createBasicFeatureObject("feature");
        DistributionObject l = createBasicDistributionObject("distribution");

        attr = new HashMap<String, String>();
        attr.put(TargetObject.KEY_ID, "myTarget");

        StatefulTargetObject sgo = m_statefulTargetRepository.preregister(attr, tags);

        m_artifact2featureRepository.create(b1, g);
        m_artifact2featureRepository.create(a1, g);
        m_artifact2featureRepository.create(a2, g);
        m_feature2distributionRepository.create(g, l);
        m_distribution2targetRepository.create(l, sgo.getTargetObject());

        sgo.approve();
       
        runAndWaitForEvent(new Callable<Void>() {
            public Void call() throws Exception {
                m_repositoryAdmin.commit();
                return null;
            }
        }, false, TOPIC_STATUS_CHANGED);
       
        assertEquals("Store state for target should still be new, because the resource processor is missing.", StoreState.New, sgo.getStoreState());
       
        // Now, add a processor for the artifact.
        attr = new HashMap<String, String>();
        attr.put(ArtifactObject.KEY_URL, "http://myprocessor");
        attr.put(BundleHelper.KEY_RESOURCE_PROCESSOR_PID, "my.processor.pid");
        attr.put(BundleHelper.KEY_SYMBOLICNAME, "my.processor.bundle");
        attr.put(BundleHelper.KEY_VERSION, "1.0.0");
        attr.put(ArtifactHelper.KEY_MIMETYPE, BundleHelper.MIMETYPE);

        ArtifactObject b2 = m_artifactRepository.create(attr, tags);

        sgo.approve();
       
        runAndWaitForEvent(new Callable<Void>() {
            public Void call() throws Exception {
                m_repositoryAdmin.commit();
                return null;
            }
        }, false, DeploymentVersionObject.TOPIC_ADDED, TOPIC_STATUS_CHANGED);       

        DeploymentVersionObject dep = m_deploymentVersionRepository.getMostRecentDeploymentVersion(sgo.getID());

        DeploymentArtifact[] toDeploy = dep.getDeploymentArtifacts();

        assertEquals("We expect to find four artifacts to deploy;", 4, toDeploy.length);
        DeploymentArtifact bundle1 = toDeploy[0];
        assertEquals(b1.getURL(), bundle1.getUrl());
       
        DeploymentArtifact bundle2 = toDeploy[1];
        assertEquals(b2.getURL(), bundle2.getUrl());
        assertEquals("true", bundle2.getDirective(DeploymentArtifact.DIRECTIVE_ISCUSTOMIZER));
       
        DeploymentArtifact artifact1 = toDeploy[2];
        assertEquals(a1.getURL(), artifact1.getUrl());
        assertEquals("my.processor.pid", artifact1.getDirective(DeploymentArtifact.DIRECTIVE_KEY_PROCESSORID));
       
        DeploymentArtifact artifact2 = toDeploy[3];
        assertEquals(a2.getURL(), artifact2.getUrl());
        assertEquals("my.processor.pid", artifact2.getDirective(DeploymentArtifact.DIRECTIVE_KEY_PROCESSORID));
        assertEquals(a2.getResourceId(), artifact2.getDirective(DeploymentArtifact.DIRECTIVE_KEY_RESOURCE_ID));
       
        // Now, add a new version of the processor (ACE-373)
        assertFalse("There should be no changes.", sgo.needsApprove());

        attr = new HashMap<String, String>();
        attr.put(ArtifactObject.KEY_URL, "http://myprocessor/v2");
        attr.put(BundleHelper.KEY_RESOURCE_PROCESSOR_PID, "my.processor.pid");
        attr.put(BundleHelper.KEY_SYMBOLICNAME, "my.processor.bundle");
        attr.put(BundleHelper.KEY_VERSION, "2.0.0");
        attr.put(ArtifactHelper.KEY_MIMETYPE, BundleHelper.MIMETYPE);

        ArtifactObject b3 = m_artifactRepository.create(attr, tags);

        assertTrue("By adding a resource processor, we should have triggered a change that needs to be approved.", sgo.needsApprove());

        sgo.approve();

        runAndWaitForEvent(new Callable<Void>() {
            public Void call() throws Exception {
                m_repositoryAdmin.commit();
                return null;
            }
        }, false, DeploymentVersionObject.TOPIC_ADDED, TOPIC_STATUS_CHANGED);

        dep = m_deploymentVersionRepository.getMostRecentDeploymentVersion(sgo.getID());

        toDeploy = dep.getDeploymentArtifacts();

        assertEquals("We expect to find four artifacts to deploy;", 4, toDeploy.length);
        boolean foundBundle = false;
        boolean foundProcessor = false;
        boolean foundArtifact1 = false;
        boolean foundArtifact2 = false;
        for (DeploymentArtifact a : toDeploy) {
            String url = a.getUrl();
            if (url.equals(b1.getURL())) {
                foundBundle = true;
            }
            else if (url.equals(b3.getURL())) {
                assertEquals("true", a.getDirective(DeploymentArtifact.DIRECTIVE_ISCUSTOMIZER));
                foundProcessor = true;
            }
            else if (url.equals(a1.getURL())) {
                assertEquals("my.processor.pid", a.getDirective(DeploymentArtifact.DIRECTIVE_KEY_PROCESSORID));
                foundArtifact1 = true;
            }
            else if (url.equals(a2.getURL())) {
                assertEquals("my.processor.pid", a.getDirective(DeploymentArtifact.DIRECTIVE_KEY_PROCESSORID));
                assertEquals(a2.getResourceId(), a.getDirective(DeploymentArtifact.DIRECTIVE_KEY_RESOURCE_ID));
                foundArtifact2 = true;
            }
        }
        assertTrue("Could not find bundle in deployment", foundBundle);
        assertTrue("Could not find processor in deployment", foundProcessor);
        assertTrue("Could not find artifact 1 in deployment", foundArtifact1);
        assertTrue("Could not find artifact 2 in deployment", foundArtifact2);

        // Now, let's add a new resource processor that is *older* than the one we already have.
        // Nothing should change.

        assertFalse("There should be no changes.", sgo.needsApprove());

        attr = new HashMap<String, String>();
        attr.put(ArtifactObject.KEY_URL, "http://myprocessor/v1.5");
        attr.put(BundleHelper.KEY_RESOURCE_PROCESSOR_PID, "my.processor.pid");
        attr.put(BundleHelper.KEY_SYMBOLICNAME, "my.processor.bundle");
        attr.put(BundleHelper.KEY_VERSION, "1.5.0");
        attr.put(ArtifactHelper.KEY_MIMETYPE, BundleHelper.MIMETYPE);

        m_artifactRepository.create(attr, tags);

        assertFalse("By adding an older resource processor, we should not have triggered a change.", sgo.needsApprove());

        cleanUp();

        m_dependencyManager.remove(myHelperService);
    }

    private void setupRepository() throws IOException, InterruptedException, InvalidSyntaxException {
        User user = new MockUser();

        startRepositoryService();

        addRepository("storeInstance", "apache", "store", true);
        addRepository("targetInstance", "apache", "target", true);
        addRepository("deploymentInstance", "apache", "deployment", true);

        RepositoryAdminLoginContext loginContext = m_repositoryAdmin.createLoginContext(user);
        loginContext
            .add(loginContext.createShopRepositoryContext()
                .setLocation(m_endpoint).setCustomer("apache").setName("store").setWriteable())
            .add(loginContext.createTargetRepositoryContext()
                .setLocation(m_endpoint).setCustomer("apache").setName("target").setWriteable())
            .add(loginContext.createDeploymentRepositoryContext()
                .setLocation(m_endpoint).setCustomer("apache").setName("deployment").setWriteable());

        m_repositoryAdmin.login(loginContext);
        m_repositoryAdmin.checkout();
    }

    /**
     * Tests the full template mechanism, from importing templatable artifacts, to creating deployment
     * versions with it. It uses the configuration (autoconf) helper, which uses a VelocityBased preprocessor.
     */
    public void testTemplateProcessing() throws Exception {
        setupRepository();
       
        addObr("/obr", "store");

        // create some template things
        String xmlHeader =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?><metatype:MetaData xmlns:metatype=\"http://www.osgi.org/xmlns/metatype/v1.0.0\">";
        String xmlFooter = "</metatype:MetaData>";

        String noTemplate = "<Attribute content=\"http://someURL\"/>";
        String noTemplateProcessed = "<Attribute content=\"http://someURL\"/>";
        final File noTemplateFile = createFileWithContents("template", ".xml", xmlHeader + noTemplate + xmlFooter);

        String simpleTemplate = "<Attribute content=\"http://$context.name\"/>";
        String simpleTemplateProcessed = "<Attribute content=\"http://mydistribution\"/>";
        File simpleTemplateFile = createFileWithContents("template", ".xml", xmlHeader + simpleTemplate + xmlFooter);

        // create some tree from artifacts to a target
        FeatureObject go = runAndWaitForEvent(new Callable<FeatureObject>() {
            public FeatureObject call() throws Exception {
                ArtifactObject b1 = createBasicBundleObject("myBundle");
                createBasicBundleObject("myProcessor", "1.0.0", "org.osgi.deployment.rp.autoconf");
                FeatureObject go = createBasicFeatureObject("myfeature");
                DistributionObject lo = createBasicDistributionObject("mydistribution");
                TargetObject gwo = createBasicTargetObject("templatetarget2");
                m_artifact2featureRepository.create(b1, go);
                // note that we do not associate b2: this is a resource processor, so it will be packed
                // implicitly. It should not be available to a preprocessor either.
                m_feature2distributionRepository.create(go, lo);
                m_distribution2targetRepository.create(lo, gwo);
                return go;
            }
        }, false, TOPIC_ADDED);

        ArtifactObject a1 = m_artifactRepository.importArtifact(noTemplateFile.toURI().toURL(), true);
        Artifact2FeatureAssociation a2g = m_artifact2featureRepository.create(a1, go);

        final StatefulTargetObject sgo = findStatefulTarget("templatetarget2");

        // create a deploymentversion
        assertTrue("With the new assignments, the SGO should need approval.", sgo.needsApprove());
       
        sgo.approve();

        runAndWaitForEvent(new Callable<Void>() {
            public Void call() throws Exception {
                m_repositoryAdmin.commit();
                return null;
            }
        }, false, DeploymentVersionObject.TOPIC_ADDED, TOPIC_STATUS_CHANGED)
       
        // find the deployment version
        DeploymentVersionObject dvo = m_deploymentVersionRepository.getMostRecentDeploymentVersion("templatetarget2");
        String inFile = tryGetStringFromURL(findXmlUrlInDeploymentObject(dvo), 10, 100);

        assertEquals(xmlHeader + noTemplateProcessed + xmlFooter, inFile);

        // try the simple template
        m_artifact2featureRepository.remove(a2g);
        a1 = m_artifactRepository.importArtifact(simpleTemplateFile.toURI().toURL(), true);
        a2g = m_artifact2featureRepository.create(a1, go);

        sgo.approve();
       
        runAndWaitForEvent(new Callable<Void>() {
            public Void call() throws Exception {
                m_repositoryAdmin.commit();
                return null;
            }
        }, false, DeploymentVersionObject.TOPIC_ADDED, TOPIC_STATUS_CHANGED)


        // find the deployment version
        dvo = m_deploymentVersionRepository.getMostRecentDeploymentVersion("templatetarget2");
        // sleep for a while, to allow the OBR to process the file.
        Thread.sleep(250);

        inFile = tryGetStringFromURL(findXmlUrlInDeploymentObject(dvo), 10, 100);

        assertEquals(xmlHeader + simpleTemplateProcessed + xmlFooter, inFile);

        deleteObr("/obr");
    }

    /**
     * Tests the template processing mechanism: given a custom processor, do the correct calls go out?
     */
    public void testTemplateProcessingInfrastructure() throws Exception {
        setupRepository();
       
        // create a preprocessor
        MockArtifactPreprocessor preprocessor = new MockArtifactPreprocessor();

        // create a helper
        MockArtifactHelper helper = new MockArtifactHelper("mymime", preprocessor);

        // register preprocessor and helper
        Properties serviceProps = new Properties();
        serviceProps.put(ArtifactHelper.KEY_MIMETYPE, "mymime");

        Component helperService = m_dependencyManager.createComponent()
            .setInterface(ArtifactHelper.class.getName(), serviceProps)
            .setImplementation(helper);

        m_dependencyManager.add(helperService);
       
        String targetId = "templatetarget";

        createBasicBundleObject("myProcessor", "1.0.0", "myProcessor.pid");
        final ArtifactObject b1 = createBasicBundleObject("myBundle");
        final ArtifactObject a1 = createBasicArtifactObject("myArtifact", "mymime", "myProcessor.pid");
        final FeatureObject go = createBasicFeatureObject("myfeature");
        final DistributionObject lo = createBasicDistributionObject("mydistribution");
    final TargetObject gwo = createBasicTargetObject(targetId);

        // create some tree from artifacts to a target
        runAndWaitForEvent(new Callable<Object>() {
            public Object call() throws Exception {
                m_artifact2featureRepository.create(b1, go);
                // note that we do not associate b2: this is a resource processor, so it will be packed
                // implicitly. It should not be available to a preprocessor either.
                m_artifact2featureRepository.create(a1, go);
                m_feature2distributionRepository.create(go, lo);
                m_distribution2targetRepository.create(lo, gwo);
                return null;
            }
        }, false, TOPIC_STATUS_CHANGED);

        StatefulTargetObject sgo = findStatefulTarget(targetId);
        assertNotNull("Failed to find our target in the repository?!", sgo);

        // wait until needsApprove is true; depending on timing, this could have happened before or after the TOPIC_ADDED.
        int attempts = 0;
        while (!sgo.needsApprove() && (attempts++ < 10)) {
            Thread.sleep(100);
        }
       
        assertTrue("With the new assignments, the SGO should need approval.", sgo.needsApprove());
        // create a deploymentversion
        sgo.approve();
       
       
        runAndWaitForEvent(new Callable<Void>() {
            public Void call() throws Exception {
                m_repositoryAdmin.commit();
                return null;
            }
        }, false, DeploymentVersionObject.TOPIC_ADDED, TOPIC_STATUS_CHANGED)

        // the preprocessor now has gotten its properties; inspect these
        PropertyResolver target = preprocessor.getProps();
        assertTrue("The property resolver should be able to resolve 'id'.", target.get("id").startsWith(targetId));
        assertTrue("The property resolver should be able to resolve 'name'.", target.get("name").startsWith("mydistribution"));
        assertNull("The property resolver should not be able to resolve 'someunknownproperty'.", target.get("someunknownproperty"));

        cleanUp(); // we need to do this before the helper goes away

        m_dependencyManager.remove(helperService);
    }

    private String tryGetStringFromURL(URL url, int tries, int interval) throws Exception {
        while (true) {
            try {
                List<String> result = getResponse(url);
               
                StringBuilder sb = new StringBuilder();
                for (String line : result) {
                  if (sb.length() > 0) {
                    sb.append('\n');
                  }
                  sb.append(line);
                }
               
                return sb.toString();
            }
            catch (IOException ioe) {
                if (--tries == 0) {
                    throw ioe;
                } else {
                  Thread.sleep(interval);
                }
            }
        }
    }

    /**
     * The following code is borrowed from RepositoryTest.java, and is used to instantiate and
     * use repository servlets.
     */
    private StatefulTargetObject findStatefulTarget(String targetID) throws Exception {
      int count = 10;
      while (count-- > 0) {
          for (StatefulTargetObject sgo : m_statefulTargetRepository.get()) {
              if (sgo.getID().equals(targetID)) {
                  return sgo;
              }
          }
          Thread.sleep(100);
      }
        return null;
    }

    /**
     * Creates a temporary file with the given name and extension, and stores the given
     * contents in it.
     */
    private File createFileWithContents(String name, String extension, String contents) throws IOException {
        File file = File.createTempFile(name, extension);
        file.deleteOnExit();
        Writer w = new OutputStreamWriter(new FileOutputStream(file));
        w.write(contents);
        w.close();
        return file;
    }

    /**
     * Helper method for testTemplateProcessing; finds the URL of the first deploymentartifact
     * with 'xml' in its url.
     */
    private URL findXmlUrlInDeploymentObject(DeploymentVersionObject dvo) throws MalformedURLException {
        DeploymentArtifact[] artifacts = dvo.getDeploymentArtifacts();
        for (DeploymentArtifact da : artifacts) {
            if (da.getUrl().contains("xml")) {
                return new URL(da.getUrl());
            }
        }
        return null;
    }
}
TOP

Related Classes of org.apache.ace.it.repositoryadmin.TemplateProcessorTest$MockArtifactPreprocessor

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.