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:52:02 UTC
[ambari] branch branch-2.6 updated: AMBARI-24231 : Adding
additional jars in classpath of ambari views (nitirajrathore) (#2160)
This is an automated email from the ASF dual-hosted git repository.
nitiraj pushed a commit to branch branch-2.6
in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/branch-2.6 by this push:
new 64cc76b AMBARI-24231 : Adding additional jars in classpath of ambari views (nitirajrathore) (#2160)
64cc76b is described below
commit 64cc76bfc7c83707f395d8487ea983dc8fddb267
Author: nitirajrathore <ni...@gmail.com>
AuthorDate: Fri Aug 31 09:21:59 2018 +0530
AMBARI-24231 : Adding additional jars in classpath of ambari views (nitirajrathore) (#2160)
* 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 873138d..80b2c74 100644
--- a/ambari-server/conf/unix/ambari.properties
+++ b/ambari-server/conf/unix/ambari.properties
@@ -130,6 +130,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
mpacks.staging.path=$ROOT/var/lib/ambari-server/resources/mpacks
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 a7961bc..352b9ae 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
@@ -2449,6 +2449,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.
*/
@@ -3815,6 +3824,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 0dabf90..07a42d5 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
@@ -27,6 +27,7 @@ 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;
@@ -63,11 +64,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();
@@ -158,7 +160,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 + ".";
@@ -187,7 +189,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();
@@ -200,9 +202,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) {
@@ -213,11 +240,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 9d461e5..7d23451 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
@@ -28,6 +28,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;
@@ -1803,7 +1804,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);
@@ -1843,6 +1846,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() + ".");
@@ -2111,7 +2127,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 != null && classLoader instanceof ViewClassLoader) {
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 02ec916..12964cf 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
@@ -35,8 +35,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;
@@ -79,6 +81,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}");
@@ -131,14 +140,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 6a17d26..dc65d06 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
@@ -1493,6 +1493,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);
@@ -1589,7 +1590,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,