You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ni...@apache.org on 2018/08/31 03:51:49 UTC
[ambari] branch branch-2.7 updated: AMBARI-24231 : Adding
additional jars in classpath of ambari views (nitirajrathore) (#2161)
This is an automated email from the ASF dual-hosted git repository.
nitiraj pushed a commit to branch branch-2.7
in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/branch-2.7 by this push:
new 77d348c AMBARI-24231 : Adding additional jars in classpath of ambari views (nitirajrathore) (#2161)
77d348c is described below
commit 77d348c6dd8173b01f3319a61ca757e4fd9d0182
Author: nitirajrathore <ni...@gmail.com>
AuthorDate: Fri Aug 31 09:21:45 2018 +0530
AMBARI-24231 : Adding additional jars in classpath of ambari views (nitirajrathore) (#2161)
* AMBARI-24231 : Adding additional jars in classpath of ambari views (nitirajrathore)
* AMBARI-24231 : (review comments) Adding additional jars in classpath of ambari views (nitirajrathore)
---
ambari-server/conf/unix/ambari.properties | 1 +
.../ambari/server/configuration/Configuration.java | 22 ++++++++++++
.../apache/ambari/server/view/ViewExtractor.java | 40 +++++++++++++++++-----
.../apache/ambari/server/view/ViewRegistry.java | 21 ++++++++++--
.../ambari/server/view/ViewExtractorTest.java | 33 ++++++++++++++++--
.../ambari/server/view/ViewRegistryTest.java | 3 +-
6 files changed, 105 insertions(+), 15 deletions(-)
diff --git a/ambari-server/conf/unix/ambari.properties b/ambari-server/conf/unix/ambari.properties
index ea137fc..4e189a4 100644
--- a/ambari-server/conf/unix/ambari.properties
+++ b/ambari-server/conf/unix/ambari.properties
@@ -124,6 +124,7 @@ views.http.x-xss-protection=1; mode=block
views.http.x-frame-options=SAMEORIGIN
views.http.x-content-type-options=nosniff
views.http.cache-control=no-store
+#views.additional.classpath=<comma separated list of dir and jars>
views.http.pragma=no-cache
views.http.charset=utf-8
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
index e2c3953..09d90e8 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
@@ -2269,6 +2269,15 @@ public class Configuration {
"views.http.cache-control", "no-store");
/**
+ * The value that is additional classpath for the views. It will take comma separated paths. If the individual path is jar
+ * it will be included otherwise if it is a directory then all the files inside it will be included in the classpath. Directories
+ * WILL NOT BE traversed recursively
+ */
+ @Markdown(description = "Additional class path added to each Ambari View. Comma separated jars or directories")
+ public static final ConfigurationProperty<String> VIEWS_ADDITIONAL_CLASSPATH_VALUE = new ConfigurationProperty<>(
+ "views.additional.classpath", "");
+
+ /**
* The value that will be used to set the {@code PRAGMA} HTTP response header.
* HTTP response header for Ambari View requests.
*/
@@ -3747,6 +3756,19 @@ public class Configuration {
}
/**
+ * Get the comma separated additional classpath, that should be added to view's classloader.
+ * <p/>
+ * By default it will be empty. i.e. no additional classpath.
+ * If present it will be comma separated path entries. Each entry can be a file or a directory.
+ * If entry is a file it will be added as it is.
+ * If entry is a directory, all the files inside this directory will be added to the classpath.
+ * @return the view's additional classpath value - null or "" indicates that the value is not set
+ */
+ public String getViewsAdditionalClasspath() {
+ return getProperty(VIEWS_ADDITIONAL_CLASSPATH_VALUE);
+ }
+
+ /**
* Get the value that should be set for the <code>Pragma</code> HTTP response header for Ambari Views.
* <p/>
* By default this will be <code>no-cache</code>. For example:
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 d729942..b9e95eb 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
@@ -20,6 +20,7 @@ package org.apache.ambari.server.view;
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;
@@ -64,11 +65,12 @@ public class ViewExtractor {
* @param viewArchive the view archive file
* @param archiveDir the view archive directory
*
+ * @param viewsAdditionalClasspath: list of additional paths to be added to every view's classpath
* @return the class loader for the archive classes
*
* @throws ExtractionException if the archive can not be extracted
*/
- public ClassLoader extractViewArchive(ViewEntity view, File viewArchive, File archiveDir)
+ public ClassLoader extractViewArchive(ViewEntity view, File viewArchive, File archiveDir, List<File> viewsAdditionalClasspath)
throws ExtractionException {
String archivePath = archiveDir.getAbsolutePath();
@@ -159,7 +161,7 @@ public class ViewExtractor {
ViewConfig viewConfig = archiveUtility.getViewConfigFromExtractedArchive(archivePath, false);
- return getArchiveClassLoader(viewConfig, archiveDir);
+ return getArchiveClassLoader(viewConfig, archiveDir, viewsAdditionalClasspath);
} catch (Exception e) {
String msg = "Caught exception trying to extract the view archive " + archivePath + ".";
@@ -188,7 +190,7 @@ public class ViewExtractor {
// ----- archiveUtility methods ----------------------------------------------------
// get a class loader for the given archive directory
- private ClassLoader getArchiveClassLoader(ViewConfig viewConfig, File archiveDir)
+ private ClassLoader getArchiveClassLoader(ViewConfig viewConfig, File archiveDir, List<File> viewsAdditionalClasspath)
throws IOException {
String archivePath = archiveDir.getAbsolutePath();
@@ -201,9 +203,34 @@ public class ViewExtractor {
urlList.add(classesDir.toURI().toURL());
}
+ // include libs in additional classpath
+ for (File file : viewsAdditionalClasspath) {
+ if (file.isDirectory()) {
+ // add all files inside this dir.
+ addDirToClasspath(urlList, file);
+ } else if (file.isFile()) {
+ urlList.add(file.toURI().toURL());
+ }
+ }
+
// include any libraries in the lib directory
String libPath = archivePath + File.separator + ARCHIVE_LIB_DIR;
- File libDir = archiveUtility.getFile(libPath);
+ File libDir = archiveUtility.getFile(libPath);
+ addDirToClasspath(urlList, libDir);
+
+ // include the archive directory
+ urlList.add(archiveDir.toURI().toURL());
+
+ LOG.trace("classpath for view {} is : {}", viewConfig.getName(), urlList);
+ return new ViewClassLoader(viewConfig, urlList.toArray(new URL[urlList.size()]));
+ }
+
+ /**
+ * Add all the files in libDir to urlList ignoring directories.
+ * @param urlList: the list to which all paths needs to be appended
+ * @param libDir: the path of which all the files needs to be appended to urlList
+ */
+ private void addDirToClasspath(List<URL> urlList, File libDir) throws MalformedURLException {
if (libDir.exists()) {
File[] files = libDir.listFiles();
if (files != null) {
@@ -214,11 +241,6 @@ public class ViewExtractor {
}
}
}
-
- // include the archive directory
- urlList.add(archiveDir.toURI().toURL());
-
- return new ViewClassLoader(viewConfig, urlList.toArray(new URL[urlList.size()]));
}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
index cd871b3..8e573e4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
@@ -29,6 +29,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -1814,7 +1815,9 @@ public class ViewRegistry {
try {
// extract the archive and get the class loader
- ClassLoader cl = extractor.extractViewArchive(viewDefinition, archiveFile, extractedArchiveDirFile);
+ List<File> additionalPaths = getViewsAdditionalClasspath(configuration);
+
+ ClassLoader cl = extractor.extractViewArchive(viewDefinition, archiveFile, extractedArchiveDirFile, additionalPaths);
configureViewLogging(viewDefinition, cl);
@@ -1854,6 +1857,19 @@ public class ViewRegistry {
}
}
+ private static List<File> getViewsAdditionalClasspath(Configuration configuration) {
+ String viewsAdditionalClasspath = configuration.getViewsAdditionalClasspath();
+ List<File> additionalPaths = new LinkedList<>();
+ if(null != viewsAdditionalClasspath && !viewsAdditionalClasspath.trim().isEmpty()) {
+ String[] paths = viewsAdditionalClasspath.trim().split(",");
+ for(String path : paths) {
+ if(null != path && !path.trim().isEmpty())
+ additionalPaths.add(new File(path));
+ }
+ }
+ return additionalPaths;
+ }
+
private void migrateDataFromPreviousVersion(ViewEntity viewDefinition, String serverVersion) {
if (!viewDefinitions.containsKey(viewDefinition.getName())) { // migrate only registered views to avoid recursive calls
LOG.debug("Cancel auto migration of not loaded view: {}.", viewDefinition.getName());
@@ -2110,7 +2126,8 @@ public class ViewRegistry {
if (!systemOnly || viewDefinition.isSystem()) {
ClassLoader classLoader = null;
try {
- classLoader = extractor.extractViewArchive(viewDefinition, archiveFile, extractedArchiveDirFile);
+ List<File> additionalPaths = getViewsAdditionalClasspath(configuration);
+ classLoader = extractor.extractViewArchive(viewDefinition, archiveFile, extractedArchiveDirFile, additionalPaths);
return true;
} finally {
if (classLoader instanceof Closeable) {
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewExtractorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewExtractorTest.java
index 3162b50..d06eca0 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewExtractorTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewExtractorTest.java
@@ -33,8 +33,10 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
@@ -80,6 +82,13 @@ public class ViewExtractorTest {
@Test
public void testExtractViewArchive() throws Exception {
+ File addDirPath = createNiceMock(File.class);
+ File addDirPathFile1 = createNiceMock(File.class);
+ File addDirPathFile2 = createNiceMock(File.class);
+ File addDirPath2 = createNiceMock(File.class);
+ File addFilePath = createNiceMock(File.class);
+ List<File> viewsAdditionalClasspath = Arrays.asList(addDirPath, addDirPath2, addFilePath);
+
ResourceTypeEntity resourceTypeEntity = new ResourceTypeEntity();
resourceTypeEntity.setId(10);
resourceTypeEntity.setName("MY_VIEW{1.0.0}");
@@ -132,14 +141,32 @@ public class ViewExtractorTest {
expect(libDir.listFiles()).andReturn(new File[]{fileEntry});
expect(fileEntry.toURI()).andReturn(new URI("file:./"));
+ expect(addDirPath.isDirectory()).andReturn(true);
+ expect(addDirPath.exists()).andReturn(true);
+ expect(addDirPath.listFiles()).andReturn(new File[]{addDirPathFile1, addDirPathFile2});
+ expect(addDirPathFile1.isDirectory()).andReturn(false);
+ expect(addDirPathFile1.toURI()).andReturn(new URI("file://file1"));
+ expect(addDirPathFile2.isDirectory()).andReturn(false);
+ expect(addDirPathFile2.toURI()).andReturn(new URI("file://file2"));
+
+ expect(addDirPath2.isDirectory()).andReturn(true);
+ expect(addDirPath2.exists()).andReturn(true);
+ expect(addDirPath2.listFiles()).andReturn(new File[]{});
+
+ expect(addFilePath.isDirectory()).andReturn(false);
+ expect(addFilePath.isFile()).andReturn(true);
+ expect(addFilePath.toURI()).andReturn(new URI("file://file3"));
+
replay(extractedArchiveDir, viewArchive, archiveDir, entryFile, classesDir, libDir, metaInfDir, viewJarFile,
- jarEntry, fos, configuration, viewDir, fileEntry, viewDAO);
+ jarEntry, fos, configuration, viewDir, fileEntry, viewDAO,
+ addDirPath, addDirPathFile1, addDirPathFile2, addDirPath2, addFilePath);
ViewExtractor viewExtractor = getViewExtractor(viewDefinition);
- viewExtractor.extractViewArchive(viewDefinition, viewArchive, archiveDir);
+ viewExtractor.extractViewArchive(viewDefinition, viewArchive, archiveDir, viewsAdditionalClasspath);
verify(extractedArchiveDir, viewArchive, archiveDir, entryFile, classesDir, libDir, metaInfDir, viewJarFile,
- jarEntry, fos, configuration, viewDir, fileEntry, viewDAO);
+ jarEntry, fos, configuration, viewDir, fileEntry, viewDAO,
+ addDirPath, addDirPathFile1, addDirPathFile2, addDirPath2, addFilePath);
}
@Test
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 7703aaf..8e7f8b0 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
@@ -1517,6 +1517,7 @@ public class ViewRegistryTest {
File viewDir = createNiceMock(File.class);
File extractedArchiveDir = createNiceMock(File.class);
File viewArchive = createNiceMock(File.class);
+
File archiveDir = createNiceMock(File.class);
File entryFile = createNiceMock(File.class);
File classesDir = createNiceMock(File.class);
@@ -1613,7 +1614,7 @@ public class ViewRegistryTest {
else {
expect(viewExtractor.ensureExtractedArchiveDirectory("/var/lib/ambari-server/resources/views/work")).andReturn(true);
}
- expect(viewExtractor.extractViewArchive(capture(viewEntityCapture), eq(viewArchive), eq(archiveDir))).andReturn(null);
+ expect(viewExtractor.extractViewArchive(capture(viewEntityCapture), eq(viewArchive), eq(archiveDir), anyObject(List.class))).andReturn(null);
// replay mocks
replay(configuration, viewDir, extractedArchiveDir, viewArchive, archiveDir, entryFile, classesDir,