com.linkedin.restli.internal.server.model.RestLiClasspathScanner Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of restli-server Show documentation
Show all versions of restli-server Show documentation
Pegasus is a framework for building robust, scalable service architectures using dynamic discovery and simple asychronous type-checked REST + JSON APIs.
/*
Copyright (c) 2012 LinkedIn Corp.
Licensed 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.
*/
/**
* $Id: $
*/
package com.linkedin.restli.internal.server.model;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import com.linkedin.restli.internal.server.RestLiInternalException;
import com.linkedin.restli.server.ResourceConfigException;
import com.linkedin.restli.server.annotations.RestLiActions;
import com.linkedin.restli.server.annotations.RestLiAssociation;
import com.linkedin.restli.server.annotations.RestLiCollection;
import com.linkedin.restli.server.annotations.RestLiSimpleResource;
/**
* @author Josh Walker
* @version $Revision: $
*
* Scans the resources available to the current classloader
* to find the rest.li annotated classes in the specified
* set of packages.
*
* Inspired by Jersey's package scanning logic.
*/
class RestLiClasspathScanner
{
public static final String CLASS_SUFFIX = ".class";
public static final char PACKAGE_SEPARATOR = '.';
public static final char FILE_SEPARATOR = File.separatorChar;
public static final char UNIX_FILE_SEPARATOR = '/';
public static final String SCHEME_FILE = "file";
public static final String SCHEME_JAR = "jar";
public static final String SCHEME_ZIP = "zip";
public static final char JAR_ENTRY_DELIMITER = '!';
private static final Set> _annotations = buildAnnotations();
private static Set> buildAnnotations()
{
Set> annotations = new HashSet>();
annotations.add(RestLiCollection.class);
annotations.add(RestLiAssociation.class);
annotations.add(RestLiActions.class);
annotations.add(RestLiSimpleResource.class);
return Collections.unmodifiableSet(annotations);
}
private final Set> _matchedClasses;
private final ClassLoader _classLoader;
private final Set _packagePaths;
private final Set _classNames;
public RestLiClasspathScanner(final Set packageNames, final Set classNames, final ClassLoader classLoader)
{
_classLoader = classLoader;
_packagePaths = new HashSet();
//convert package names to paths, to optimize matching against .class paths
for (String packageName : packageNames)
{
_packagePaths.add(nameToPath(packageName));
}
_classNames = classNames;
_matchedClasses = new HashSet>();
}
private String nameToPath(final String name)
{
return name.replace(PACKAGE_SEPARATOR, FILE_SEPARATOR);
}
private String pathToName(final String path)
{
return path.replace(FILE_SEPARATOR, PACKAGE_SEPARATOR);
}
private String toUnixPath(final String path)
{
return path.replace(FILE_SEPARATOR, UNIX_FILE_SEPARATOR);
}
private String toNativePath(final String path)
{
return path.replace(UNIX_FILE_SEPARATOR, FILE_SEPARATOR);
}
public Class> classForName(final String name)
throws ClassNotFoundException
{
return Class.forName(name, false, _classLoader);
}
public Set> getMatchedClasses()
{
return _matchedClasses;
}
public void scanPackages()
{
try
{
for (String p : _packagePaths)
{
Enumeration resources = _classLoader.getResources(toUnixPath(p));
while (resources.hasMoreElements())
{
URI u = resources.nextElement().toURI();
String scheme = u.getScheme().toLowerCase();
if (scheme.equals(SCHEME_JAR) || scheme.equals(SCHEME_ZIP))
{
scanJar(u);
}
else if (scheme.equals(SCHEME_FILE))
{
scanDirectory(new File(u.getPath()));
}
else
{
throw new ResourceConfigException("Unable to scan resource '" + u.toString()
+ "'. URI scheme not supported by scanner.");
}
}
}
}
catch (IOException e)
{
throw new ResourceConfigException("Unable to scan resources", e);
}
catch (URISyntaxException e)
{
throw new ResourceConfigException("Unable to scan resources", e);
}
}
public String scanClasses()
{
final StringBuilder errorBuilder = new StringBuilder();
for (String c : _classNames)
{
try
{
final Class> candidateClass = classForName(c);
for (Annotation a : candidateClass.getAnnotations())
{
if (_annotations.contains(a.annotationType()))
{
_matchedClasses.add(candidateClass);
break;
}
}
}
catch (ClassNotFoundException e)
{
errorBuilder.append(String.format("Failed to load class %s\n", c));
}
}
return errorBuilder.toString();
}
private void scanJar(final URI u) throws IOException
{
String ssp = u.getRawSchemeSpecificPart();
URL jarUrl = new URL(ssp.substring(0, ssp.lastIndexOf(JAR_ENTRY_DELIMITER)));
InputStream in = null;
JarInputStream jarIn = null;
try
{
in = jarUrl.openStream();
jarIn = new JarInputStream(in);
//remove "!/" to get a path like "com/linkedin/util"
String parent = ssp.substring(ssp.lastIndexOf(JAR_ENTRY_DELIMITER) + 2);
for (JarEntry e = jarIn.getNextJarEntry(); e != null; e = jarIn.getNextJarEntry())
{
if (!e.isDirectory() && e.getName().startsWith(parent))
{
checkForMatchingClass(toNativePath(e.getName()));
}
jarIn.closeEntry();
}
}
finally
{
if (jarIn != null)
{
jarIn.close();
}
if (in != null)
{
in.close();
}
}
}
private void scanDirectory(final File root)
{
if (!root.isDirectory())
{
return;
}
for (File child : root.listFiles())
{
if (child.isDirectory())
{
scanDirectory(child);
}
else
{
checkForMatchingClass(child.getAbsolutePath());
}
}
}
public void checkForMatchingClass(final String name)
{
if (name.endsWith(CLASS_SUFFIX))
{
for (String packagePath : _packagePaths)
{
if (name.contains(packagePath))
{
int start = name.lastIndexOf(packagePath);
int end = name.lastIndexOf(CLASS_SUFFIX);
String clazzPath = name.substring(start, end);
String clazzName = pathToName(clazzPath);
try
{
Class> clazz = classForName(clazzName);
for (Annotation a : clazz.getAnnotations())
{
if (_annotations.contains(a.annotationType()))
{
_matchedClasses.add(clazz);
break;
}
}
}
catch (ClassNotFoundException e)
{
throw new RestLiInternalException("Failed to load class while scanning packages", e);
}
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy