You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by li...@apache.org on 2016/10/30 10:44:07 UTC
[26/26] kylin git commit: minor, tool ClasspathScanner
minor, tool ClasspathScanner
Project: http://git-wip-us.apache.org/repos/asf/kylin/repo
Commit: http://git-wip-us.apache.org/repos/asf/kylin/commit/daf359d3
Tree: http://git-wip-us.apache.org/repos/asf/kylin/tree/daf359d3
Diff: http://git-wip-us.apache.org/repos/asf/kylin/diff/daf359d3
Branch: refs/heads/KYLIN-1971
Commit: daf359d38e9339c9c1151e5b90a0449d05841e19
Parents: 635b3b9
Author: Yang Li <li...@apache.org>
Authored: Sun Oct 30 18:40:51 2016 +0800
Committer: Yang Li <li...@apache.org>
Committed: Sun Oct 30 18:40:51 2016 +0800
----------------------------------------------------------------------
.../kylin/common/util/ClasspathScanner.java | 335 +++++++++++++++++++
.../apache/kylin/common/util/StringUtil.java | 16 +
2 files changed, 351 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/kylin/blob/daf359d3/core-common/src/main/java/org/apache/kylin/common/util/ClasspathScanner.java
----------------------------------------------------------------------
diff --git a/core-common/src/main/java/org/apache/kylin/common/util/ClasspathScanner.java b/core-common/src/main/java/org/apache/kylin/common/util/ClasspathScanner.java
new file mode 100644
index 0000000..6eeb53a
--- /dev/null
+++ b/core-common/src/main/java/org/apache/kylin/common/util/ClasspathScanner.java
@@ -0,0 +1,335 @@
+/*
+ * 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.kylin.common.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+
+public class ClasspathScanner {
+
+ public static void main(final String[] args) {
+ ClasspathScanner scanner = new ClasspathScanner();
+
+ System.out.println("Finding " + Arrays.toString(args) + " in:");
+ System.out.println("----------------------------------------------------------------------------");
+ for (File f : scanner.rootResources) {
+ System.out.println(" - " + f.getAbsolutePath());
+ }
+ System.out.println("----------------------------------------------------------------------------");
+
+ scanner.scan("", new ResourceVisitor() {
+ public void accept(File dir, String relativeFileName) {
+ check(dir.getAbsolutePath(), relativeFileName.replace('\\', '/'));
+ }
+
+ public void accept(ZipFile archive, ZipEntry zipEntry) {
+ check(archive.getName(), zipEntry.getName().replace('\\', '/'));
+ }
+
+ private void check(String base, String relativePath) {
+ boolean hit = false;
+ for (int i = 0; i < args.length && !hit; i++)
+ hit = match(args[0], relativePath);
+
+ if (hit) {
+ System.out.println(base + " - " + relativePath);
+ }
+ }
+ });
+ }
+
+ /**
+ * Scan classpath to find resources that has a suffix in name.
+ *
+ * This thread's context class loader is used to define the searching
+ * classpath.
+ *
+ * @param suffix
+ * like ".jsp" for example
+ * @return a string array; each element is a standard resource name relative
+ * to the root of class path, like "young/web/frame.jsp"
+ */
+ public static String[] findResources(final String suffix) {
+ ClasspathScanner scanner = new ClasspathScanner();
+
+ final ArrayList result = new ArrayList();
+
+ scanner.scan(suffix, new ResourceVisitor() {
+ public void accept(File dir, String relativeFileName) {
+ result.add(relativeFileName.replace('\\', '/'));
+ }
+
+ public void accept(ZipFile archive, ZipEntry zipEntry) {
+ result.add(zipEntry.getName().replace('\\', '/'));
+ }
+ });
+
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+ // ============================================================================
+
+ private File[] rootResources;
+
+ public ClasspathScanner() {
+ this(Thread.currentThread().getContextClassLoader(), true);
+ }
+
+ public ClasspathScanner(ClassLoader classLoader, boolean recursive) {
+ this.rootResources = extractRoots(classLoader, recursive);
+ }
+
+ static File[] extractRoots(ClassLoader loader, boolean recursive) {
+ ArrayList<File> roots = new ArrayList();
+
+ do {
+ if (loader instanceof URLClassLoader) {
+ URL[] urls = ((URLClassLoader) loader).getURLs();
+ for (int i = 0; i < urls.length; i++) {
+ // tricky: space is "%20" in URL
+ File f = new File(urls[i].getFile().replace("%20", " "));
+
+ // some generated run script could contain empty path, i.e., foo::bar
+ // try detect and filter them out
+ if (f.isDirectory()) {
+ boolean badSuspect = false;
+ for (File known : roots)
+ if (known.isDirectory() && known.toString().startsWith(f.toString()))
+ badSuspect = true;
+ if (badSuspect)
+ continue;
+ }
+ roots.add(f);
+ }
+ }
+ loader = loader.getParent();
+ } while (loader != null && recursive);
+
+ return (File[]) roots.toArray(new File[roots.size()]);
+ }
+
+ /**
+ * @param rootResources
+ * can be directories or zip/jar archives. All contents inside
+ * will be searched.
+ */
+ public ClasspathScanner(File[] rootResources) {
+ this.rootResources = rootResources;
+ }
+
+ public static interface ResourceVisitor {
+
+ void accept(File dir, String relativeFileName);
+
+ void accept(ZipFile archive, ZipEntry zipEntry);
+ }
+
+ public void scan(String suffix, ResourceVisitor visitor) {
+ for (int i = 0; i < rootResources.length; i++) {
+ if (rootResources[i].exists()) {
+ if (rootResources[i].isDirectory()) {
+ scanDirectory(rootResources[i], suffix, visitor);
+ } else if (rootResources[i].getName().contains(".zip") || rootResources[i].getName().contains(".jar")) {
+ scanArchive(rootResources[i], suffix, visitor);
+ }
+ }
+ }
+ }
+
+ private void scanArchive(File archive, String suffix, ResourceVisitor visitor) {
+ ZipFile zip = null;
+ try {
+ zip = new ZipFile(archive, ZipFile.OPEN_READ);
+ Enumeration enu = zip.entries();
+ while (enu.hasMoreElements()) {
+ ZipEntry entry = (ZipEntry) enu.nextElement();
+ if (entry.getName().endsWith(suffix)) {
+ visitor.accept(zip, entry);
+ }
+ }
+ } catch (ZipException e) {
+ // e.printStackTrace();
+ } catch (IOException e) {
+ // e.printStackTrace();
+ } finally {
+ if (zip != null) {
+ try {
+ zip.close();
+ } catch (IOException e) {
+ // e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private void scanDirectory(File dir, String suffix, ResourceVisitor visitor) {
+ String[] files = scanFiles(dir, new String[] { "*" + suffix });
+ for (int i = 0; i < files.length; i++) {
+ if (files[i].endsWith(suffix)) {
+ visitor.accept(dir, files[i]);
+ }
+ }
+ }
+
+ /**
+ * Search files inside a dir that match certain patterns.
+ * @param dir from where files are searched
+ * @param includes patterns that may contain '*' or '?' wild char
+ * @return an array of included files relative to the dir
+ */
+ public static String[] scanFiles(File dir, String... includes) {
+ return scanFiles(dir, includes, null);
+ }
+
+ /**
+ * Search files inside a dir that match any of the include patterns
+ * and not match the exclude patterns. All files are included if
+ * include patterns is null/empty; no files are excluded if exclude
+ * patterns is null/empty.
+ * @param dir from where files are searched
+ * @param includes patterns that may contain '*' or '?' wild char
+ * @param excludes patterns that may contain '*' or '?' wild char
+ * @return an array of included files relative to the dir
+ */
+ public static String[] scanFiles(File dir, String[] includes, String[] excludes) {
+ // remove tailing '/' in patterns
+ if (includes != null) {
+ for (int i = 0; i < includes.length; i++)
+ includes[i] = StringUtil.trimSuffix(includes[i], "/");
+ }
+ if (excludes != null) {
+ for (int i = 0; i < excludes.length; i++)
+ excludes[i] = StringUtil.trimSuffix(excludes[i], "/");
+ }
+
+ ArrayList result = new ArrayList();
+ ArrayList queue = new ArrayList();
+ queue.add("");
+
+ String dirPath, path;
+ File dirFile, f;
+ File[] files;
+ while (!queue.isEmpty()) {
+ dirPath = (String) queue.remove(queue.size() - 1);
+ dirFile = dirPath.length() == 0 ? dir : new File(dir, dirPath);
+ files = dirFile.listFiles();
+ for (int i = 0; files != null && i < files.length; i++) {
+ f = files[i];
+ path = dirPath + (dirPath.length() == 0 ? "" : "/") + f.getName();
+ if (f.isDirectory()) {
+ // cut off excluded dir early
+ if (scanFiles_isIncluded(path, null, excludes))
+ queue.add(path);
+ } else if (f.isFile()) {
+ if (scanFiles_isIncluded(path, includes, excludes))
+ result.add(path);
+ }
+ }
+ }
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+ private static boolean scanFiles_isIncluded(String path, String[] includes, String[] excludes) {
+ // if null, means include everything
+ if (includes != null && includes.length != 0) {
+ boolean included = false;
+ for (int i = 0; !included && i < includes.length; i++) {
+ if (match(includes[i], path) || match(includes[i] + "/*", path))
+ included = true;
+ }
+ if (!included)
+ return false;
+ }
+ // if null, means exclude nothing
+ if (excludes != null && excludes.length != 0) {
+ for (int i = 0; i < excludes.length; i++) {
+ if (match(excludes[i], path))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Matches a string against a pattern. The pattern contains two special
+ * characters: '*' which means zero or more characters, '?' which means one
+ * and only one character.
+ *
+ * @param pattern
+ * the (non-null) pattern to match against
+ * @param str
+ * the (non-null) string that must be matched against the pattern
+ *
+ * @return <code>true</code> when the string matches against the pattern,
+ * <code>false</code> otherwise.
+ */
+ public static boolean match(String pattern, String str) {
+ int i = 0, j = 0, ii = 0, jj = 0, plen = pattern.length(), slen = str.length();
+ int wordStart, wordEnd, lastPossible;
+ while (i < plen) {
+ // find the next word in pattern
+ wordStart = i;
+ while (wordStart < plen && pattern.charAt(wordStart) == '*')
+ wordStart++;
+ if (wordStart == plen) // all left in pattern is '*'
+ return true;
+ // find the word end
+ wordEnd = wordStart + 1;
+ while (wordEnd < plen && pattern.charAt(wordEnd) != '*')
+ wordEnd++;
+
+ // locate the word in string
+ lastPossible = slen - (wordEnd - wordStart) + 1;
+ for (; j < lastPossible; j++) {
+ // check if word matches at j
+ for (ii = wordStart, jj = j; ii < wordEnd; ii++, jj++)
+ if (pattern.charAt(ii) != '?' && pattern.charAt(ii) != str.charAt(jj))
+ break;
+ if (ii == wordEnd) // matched at j
+ break;
+
+ // if there's no '*' before word, then the string must
+ // match at j, but it didn't
+ if (wordStart == i)
+ return false;
+ }
+
+ // failed to locate the word
+ if (!(j < lastPossible))
+ return false;
+
+ // proceed to the next word
+ i = ii;
+ j = jj;
+ }
+
+ // pattern ended, if string also ended, then it's a match;
+ // otherwise it's a mismatch
+ return j == slen;
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/kylin/blob/daf359d3/core-common/src/main/java/org/apache/kylin/common/util/StringUtil.java
----------------------------------------------------------------------
diff --git a/core-common/src/main/java/org/apache/kylin/common/util/StringUtil.java b/core-common/src/main/java/org/apache/kylin/common/util/StringUtil.java
index b336e4b..bbc9448 100644
--- a/core-common/src/main/java/org/apache/kylin/common/util/StringUtil.java
+++ b/core-common/src/main/java/org/apache/kylin/common/util/StringUtil.java
@@ -49,6 +49,22 @@ public class StringUtil {
return (String[]) whatsLeft.toArray(new String[whatsLeft.size()]);
}
+ /**
+ * Returns a substring by removing the specified suffix. If the given string
+ * does not ends with the suffix, the string is returned without change.
+ *
+ * @param str
+ * @param suffix
+ * @return
+ */
+ public static String trimSuffix(String str, String suffix) {
+ if (str.endsWith(suffix)) {
+ return str.substring(0, str.length() - suffix.length());
+ } else {
+ return str;
+ }
+ }
+
public static String join(Iterable<String> parts, String separator) {
StringBuilder buf = new StringBuilder();
for (String p : parts) {