Repository: ambari Updated Branches: refs/heads/trunk 2302764c3 -> 591ab4810
AMBARI-11208 - Views : expose extra classpath setting (tbeerbower) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/591ab481 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/591ab481 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/591ab481 Branch: refs/heads/trunk Commit: 591ab48101065da7561115391b747991d4e47d2d Parents: 2302764 Author: tbeerbower <tbeerbo...@hortonworks.com> Authored: Tue May 26 13:01:05 2015 -0400 Committer: tbeerbower <tbeerbo...@hortonworks.com> Committed: Tue May 26 13:01:14 2015 -0400 ---------------------------------------------------------------------- .../ambari/server/view/ViewClassLoader.java | 29 +++++++++++++------- .../ambari/server/view/ViewExtractor.java | 13 +++++---- .../server/view/configuration/ViewConfig.java | 18 ++++++++++++ .../ambari/server/view/ViewClassLoaderTest.java | 16 +++++++---- .../ambari/server/view/ViewRegistryTest.java | 12 ++++---- .../view/configuration/ViewConfigTest.java | 21 ++++++++++++++ ambari-views/src/main/resources/view.xsd | 18 +++++++++++- 7 files changed, 99 insertions(+), 28 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/591ab481/ambari-server/src/main/java/org/apache/ambari/server/view/ViewClassLoader.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewClassLoader.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewClassLoader.java index b7cb594..f339b9a 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewClassLoader.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewClassLoader.java @@ -18,6 +18,7 @@ package org.apache.ambari.server.view; +import org.apache.ambari.server.view.configuration.ViewConfig; import org.eclipse.jetty.webapp.WebAppClassLoader; import org.eclipse.jetty.webapp.WebAppContext; @@ -38,10 +39,11 @@ public class ViewClassLoader extends WebAppClassLoader { * The URLs will be searched in the order specified for classes and resources before searching * the parent class loader. * - * @param urls the URLs from which to load classes and resources + * @param viewConfig the view configuration + * @param urls the URLs from which to load classes and resources */ - public ViewClassLoader(URL[] urls) throws IOException { - this(null, urls); + public ViewClassLoader(ViewConfig viewConfig, URL[] urls) throws IOException { + this(viewConfig, null, urls); } /** @@ -49,11 +51,12 @@ public class ViewClassLoader extends WebAppClassLoader { * The URLs will be searched in the order specified for classes and resources before searching the specified * parent class loader. * - * @param parent the parent class loader - * @param urls the URLs from which to load classes and resources + * @param viewConfig the view configuration + * @param parent the parent class loader + * @param urls the URLs from which to load classes and resources */ - public ViewClassLoader(ClassLoader parent, URL[] urls) throws IOException { - super(parent, getInitContext()); + public ViewClassLoader(ViewConfig viewConfig, ClassLoader parent, URL[] urls) throws IOException { + super(parent, getInitContext(viewConfig)); for (URL url : urls) { addURL(url); @@ -64,9 +67,8 @@ public class ViewClassLoader extends WebAppClassLoader { // ----- helper methods ---------------------------------------------------- // Get a context to initialize the class loader. - private static WebAppContext getInitContext() { - // For now we are using defaults or setting the values for things like parent loader priority and - // system classes. In the future we may allow overrides at the view level. + private static WebAppContext getInitContext(ViewConfig viewConfig) { + WebAppContext webAppContext = new WebAppContext(); // add com.google.inject as system classes to allow for injection in view components using the google annotation @@ -74,6 +76,13 @@ public class ViewClassLoader extends WebAppClassLoader { // add org.slf4j as system classes to avoid linkage errors webAppContext.addSystemClass("org.slf4j."); + // set the class loader settings from the configuration + if (viewConfig != null) { + String extraClasspath = viewConfig.getExtraClasspath(); + if (extraClasspath != null) { + webAppContext.setExtraClasspath(extraClasspath); + } + } return webAppContext; } } http://git-wip-us.apache.org/repos/asf/ambari/blob/591ab481/ambari-server/src/main/java/org/apache/ambari/server/view/ViewExtractor.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewExtractor.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewExtractor.java index 43efc7d..3350726 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewExtractor.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewExtractor.java @@ -19,6 +19,7 @@ package org.apache.ambari.server.view; import org.apache.ambari.server.orm.entities.ViewEntity; +import org.apache.ambari.server.view.configuration.ViewConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,7 +27,6 @@ import javax.inject.Inject; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.net.MalformedURLException; import java.net.URL; import java.util.LinkedList; import java.util.List; @@ -151,7 +151,10 @@ public class ViewExtractor { throw new ExtractionException(msg); } } - return getArchiveClassLoader(archiveDir); + + ViewConfig viewConfig = archiveUtility.getViewConfigFromExtractedArchive(archivePath, false); + + return getArchiveClassLoader(viewConfig, archiveDir); } catch (Exception e) { String msg = "Caught exception trying to extract the view archive " + archivePath + "."; @@ -180,8 +183,8 @@ public class ViewExtractor { // ----- archiveUtility methods ---------------------------------------------------- // get a class loader for the given archive directory - private ClassLoader getArchiveClassLoader(File archiveDir) - throws MalformedURLException, IOException { + private ClassLoader getArchiveClassLoader(ViewConfig viewConfig, File archiveDir) + throws IOException { String archivePath = archiveDir.getAbsolutePath(); List<URL> urlList = new LinkedList<URL>(); @@ -210,7 +213,7 @@ public class ViewExtractor { // include the archive directory urlList.add(archiveDir.toURI().toURL()); - return new ViewClassLoader(urlList.toArray(new URL[urlList.size()])); + return new ViewClassLoader(viewConfig, urlList.toArray(new URL[urlList.size()])); } http://git-wip-us.apache.org/repos/asf/ambari/blob/591ab481/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java b/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java index 6164bb7..f9b898d 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java @@ -27,6 +27,7 @@ import org.apache.commons.lang.StringUtils; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import java.util.Collections; @@ -86,6 +87,13 @@ public class ViewConfig { private boolean system; /** + * The list of extra classpath elements. + */ + @XmlElementWrapper + @XmlElement(name="path") + private List<String> classpath; + + /** * The main view class name. */ @XmlElement(name="view-class") @@ -236,6 +244,16 @@ public class ViewConfig { } /** + * Get the extra classpath as a comma separated path of filenames or URLs pointing to + * directories or jar files. + * + * @return the extra classpath + */ + public String getExtraClasspath() { + return classpath == null ? null : StringUtils.join(classpath, ","); + } + + /** * Get the view class name. * * @return the view class name http://git-wip-us.apache.org/repos/asf/ambari/blob/591ab481/ambari-server/src/test/java/org/apache/ambari/server/view/ViewClassLoaderTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewClassLoaderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewClassLoaderTest.java index 8e22c49..ed701fd 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewClassLoaderTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewClassLoaderTest.java @@ -19,12 +19,14 @@ package org.apache.ambari.server.view; import junit.framework.Assert; +import org.apache.ambari.server.view.configuration.ViewConfig; import org.junit.Test; import java.io.File; import java.net.URL; import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.createNiceMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; @@ -38,17 +40,18 @@ public class ViewClassLoaderTest { public void testGetResource() throws Exception { ClassLoader parentClassLoader = createMock(ClassLoader.class); URL parentResource = new File("parent-resource").toURI().toURL(); + ViewConfig viewConfig = createNiceMock(ViewConfig.class); expect(parentClassLoader.getResource("parent-resource")).andReturn(parentResource).once(); - replay(parentClassLoader); + replay(parentClassLoader, viewConfig); File file = new File("./src/test/resources"); URL testURL = file.toURI().toURL(); URL[] urls = new URL[]{testURL}; - ViewClassLoader classLoader = new ViewClassLoader(parentClassLoader, urls); + ViewClassLoader classLoader = new ViewClassLoader(viewConfig, parentClassLoader, urls); URL url = classLoader.getResource("ambari.properties"); @@ -59,13 +62,14 @@ public class ViewClassLoaderTest { Assert.assertNotNull(url); Assert.assertSame(parentResource, url); - verify(parentClassLoader); + verify(parentClassLoader, viewConfig); } @Test public void testLoadClass() throws Exception { TestClassLoader parentClassLoader = createMock(TestClassLoader.class); Class parentClass = Object.class; + ViewConfig viewConfig = createNiceMock(ViewConfig.class); expect(parentClassLoader.getPackage("org.apache.ambari.server.view")).andReturn(null).anyTimes(); expect(parentClassLoader.loadClass("java.lang.Object")).andReturn(parentClass).anyTimes(); @@ -74,14 +78,14 @@ public class ViewClassLoaderTest { expect(parentClassLoader.loadClass("com.google.inject.AbstractModule")).andReturn(parentClass).once(); expect(parentClassLoader.loadClass("org.slf4j.LoggerFactory")).andReturn(parentClass).once(); - replay(parentClassLoader); + replay(parentClassLoader, viewConfig); File file = new File("./target/test-classes"); URL testURL = file.toURI().toURL(); URL[] urls = new URL[]{testURL}; - ViewClassLoader classLoader = new ViewClassLoader(parentClassLoader, urls); + ViewClassLoader classLoader = new ViewClassLoader(viewConfig, parentClassLoader, urls); Class clazz = classLoader.loadClass("org.apache.ambari.server.view.ViewClassLoaderTest"); @@ -110,7 +114,7 @@ public class ViewClassLoaderTest { Assert.assertNotNull(clazz); Assert.assertSame(parentClass, clazz); - verify(parentClassLoader); + verify(parentClassLoader, viewConfig); } public class TestClassLoader extends ClassLoader { http://git-wip-us.apache.org/repos/asf/ambari/blob/591ab481/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java index a0ee6b3..c66a9ce 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java @@ -493,26 +493,26 @@ public class ViewRegistryTest { jarFiles.put(viewArchive, viewJarFile); // set expectations - expect(configuration.getViewsDir()).andReturn(viewDir); + expect(configuration.getViewsDir()).andReturn(viewDir).anyTimes(); if (System.getProperty("os.name").contains("Windows")) { - expect(viewDir.getAbsolutePath()).andReturn("\\var\\lib\\ambari-server\\resources\\views"); + expect(viewDir.getAbsolutePath()).andReturn("\\var\\lib\\ambari-server\\resources\\views").anyTimes(); } else { - expect(viewDir.getAbsolutePath()).andReturn("/var/lib/ambari-server/resources/views"); + expect(viewDir.getAbsolutePath()).andReturn("/var/lib/ambari-server/resources/views").anyTimes(); } expect(configuration.getViewExtractionThreadPoolCoreSize()).andReturn(2).anyTimes(); expect(configuration.getViewExtractionThreadPoolMaxSize()).andReturn(3).anyTimes(); expect(configuration.getViewExtractionThreadPoolTimeout()).andReturn(10000L).anyTimes(); - expect(viewDir.listFiles()).andReturn(new File[]{viewArchive}); + expect(viewDir.listFiles()).andReturn(new File[]{viewArchive}).anyTimes(); expect(viewArchive.isDirectory()).andReturn(false); if (System.getProperty("os.name").contains("Windows")) { - expect(viewArchive.getAbsolutePath()).andReturn("\\var\\lib\\ambari-server\\resources\\views\\work\\MY_VIEW{1.0.0}"); + expect(viewArchive.getAbsolutePath()).andReturn("\\var\\lib\\ambari-server\\resources\\views\\work\\MY_VIEW{1.0.0}").anyTimes(); } else { - expect(viewArchive.getAbsolutePath()).andReturn("/var/lib/ambari-server/resources/views/work/MY_VIEW{1.0.0}"); + expect(viewArchive.getAbsolutePath()).andReturn("/var/lib/ambari-server/resources/views/work/MY_VIEW{1.0.0}").anyTimes(); } expect(archiveDir.exists()).andReturn(false); http://git-wip-us.apache.org/repos/asf/ambari/blob/591ab481/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java index beb8bde..2391ad6 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java @@ -165,6 +165,16 @@ public class ViewConfigTest { " <max-ambari-version>2.0.0</max-ambari-version>\n" + "</view>"; + private static String EXTRA_CLASSPATH_XML = "<view>\n" + + " <name>MY_VIEW</name>\n" + + " <label>My View!</label>\n" + + " <version>1.0.0</version>\n" + + " <classpath>" + + " <path>/var/lib/</path>" + + " <path>/tmp/foo.jar</path>" + + " </classpath>\n" + + "</view>"; + @Test public void testGetName() throws Exception { ViewConfig config = getConfig(); @@ -281,6 +291,17 @@ public class ViewConfigTest { } @Test + public void testGetExtraClasspath() throws Exception { + ViewConfig config = getConfig(system_xml); + + Assert.assertNull(config.getExtraClasspath()); + + config = getConfig(EXTRA_CLASSPATH_XML); + + Assert.assertEquals("/var/lib/,/tmp/foo.jar", config.getExtraClasspath()); + } + + @Test public void testGetMinAmbariVersion() throws Exception { ViewConfig config = getConfig(); Assert.assertNull(config.getMinAmbariVersion()); http://git-wip-us.apache.org/repos/asf/ambari/blob/591ab481/ambari-views/src/main/resources/view.xsd ---------------------------------------------------------------------- diff --git a/ambari-views/src/main/resources/view.xsd b/ambari-views/src/main/resources/view.xsd index 37737c1..46adf27 100644 --- a/ambari-views/src/main/resources/view.xsd +++ b/ambari-views/src/main/resources/view.xsd @@ -250,7 +250,18 @@ </xs:extension> </xs:complexContent> </xs:complexType> - + <xs:complexType name="PathType"> + <xs:annotation> + <xs:documentation>Defines a single classpath.</xs:documentation> + </xs:annotation> + <xs:sequence> + <xs:element type="xs:string" name="path" minOccurs="0" maxOccurs="unbounded"> + <xs:annotation> + <xs:documentation>The path of a filename or URL pointing to a directory or jar file. A directory path should end with '/'.</xs:documentation> + </xs:annotation> + </xs:element> + </xs:sequence> + </xs:complexType> <xs:element name="view"> <xs:annotation> <xs:documentation>Defines a view.</xs:documentation> @@ -306,6 +317,11 @@ <xs:documentation>Indicates whether or not this is a system view.</xs:documentation> </xs:annotation> </xs:element> + <xs:element type="PathType" name="classpath" minOccurs="0" maxOccurs="1"> + <xs:annotation> + <xs:documentation>Extra class path entries for this view. A directory path should end with '/'.</xs:documentation> + </xs:annotation> + </xs:element> <xs:element type="xs:string" name="view-class" minOccurs="0" maxOccurs="1"> <xs:annotation> <xs:documentation>The View class to receive framework events.</xs:documentation>