Skip to content

Commit

Permalink
Properly handle @ApplicationPath and Application in RESTEasy Classic
Browse files Browse the repository at this point in the history
`@ApplicationPath` is only taken into account for the single
`Application` class that is selected (if any).
  • Loading branch information
geoand committed Oct 30, 2024
1 parent d085393 commit 8eae5d4
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -222,44 +222,43 @@ public void build(
CustomScopeAnnotationsBuildItem scopes) throws Exception {
IndexView index = combinedIndexBuildItem.getIndex();

Collection<AnnotationInstance> applicationPaths = Collections.emptySet();
final Set<String> allowedClasses;
final AnnotationInstance applicationPath;
final Set<String> excludedClasses;
if (resteasyConfig.buildTimeConditionAware) {
excludedClasses = getExcludedClasses(buildTimeConditions);
} else {
excludedClasses = Collections.emptySet();
}
final Set<String> allowedClasses;
final String appClass;
if (resteasyConfig.ignoreApplicationClasses) {
applicationPath = null;
allowedClasses = Collections.emptySet();
appClass = null;
} else {
applicationPaths = index.getAnnotations(ResteasyDotNames.APPLICATION_PATH);
allowedClasses = getAllowedClasses(index);
jaxrsProvidersToRegisterBuildItem = getFilteredJaxrsProvidersToRegisterBuildItem(
jaxrsProvidersToRegisterBuildItem, allowedClasses, excludedClasses);

Collection<ClassInfo> knownApplications = index.getAllKnownSubclasses(ResteasyDotNames.APPLICATION).stream()
Collection<ClassInfo> jakartaRestApplicationClasses = index.getAllKnownSubclasses(ResteasyDotNames.APPLICATION)
.stream()
.filter(ci -> !ci.isAbstract()).collect(
Collectors.toSet());
// getAllowedClasses throws an Exception if multiple Applications are found, so we should only get 1
if (knownApplications.size() == 1) {
appClass = knownApplications.iterator().next().name().toString();
} else {
if (jakartaRestApplicationClasses.size() > 1) {
throw new RuntimeException("More than one Application class: " + jakartaRestApplicationClasses);
}
if (jakartaRestApplicationClasses.isEmpty()) {
applicationPath = null;
allowedClasses = Collections.emptySet();
appClass = null;
} else {
ClassInfo jakartaRestApplicationClass = jakartaRestApplicationClasses.iterator().next();
applicationPath = jakartaRestApplicationClass.annotation(ResteasyDotNames.APPLICATION_PATH);
allowedClasses = getAllowedClasses(jakartaRestApplicationClass);
appClass = jakartaRestApplicationClass.name().toString();
jaxrsProvidersToRegisterBuildItem = getFilteredJaxrsProvidersToRegisterBuildItem(
jaxrsProvidersToRegisterBuildItem, allowedClasses, excludedClasses);
}
}

boolean filterClasses = !allowedClasses.isEmpty() || !excludedClasses.isEmpty();

// currently we only examine the first class that is annotated with @ApplicationPath so best
// fail if the user code has multiple such annotations instead of surprising the user
// at runtime
if (applicationPaths.size() > 1) {
throw createMultipleApplicationsException(applicationPaths);
}

Set<AnnotationInstance> additionalPaths = new HashSet<>();
for (AdditionalJaxRsResourceDefiningAnnotationBuildItem annotation : additionalJaxRsResourceDefiningAnnotations) {
additionalPaths.addAll(beanArchiveIndexBuildItem.getIndex().getAnnotations(annotation.getAnnotationClass()));
Expand All @@ -284,8 +283,7 @@ public void build(

final String rootPath;
final String path;
if (!applicationPaths.isEmpty()) {
AnnotationInstance applicationPath = applicationPaths.iterator().next();
if (applicationPath != null) {
rootPath = "/";
path = Encode.decode(applicationPath.value().asString());
} else {
Expand Down Expand Up @@ -1098,49 +1096,37 @@ private static JaxrsProvidersToRegisterBuildItem getFilteredJaxrsProvidersToRegi
}

/**
* @param index the index to use to find the existing {@link Application}.
* @return the set of classes returned by the methods {@link Application#getClasses()} and
* {@link Application#getSingletons()}.
*/
private static Set<String> getAllowedClasses(IndexView index) {
final Collection<ClassInfo> applications = index.getAllKnownSubclasses(ResteasyDotNames.APPLICATION);
private Set<String> getAllowedClasses(ClassInfo jakartaRestApplicationClass) {
final Set<String> allowedClasses = new HashSet<>();
Application application;
ClassInfo selectedAppClass = null;
for (ClassInfo applicationClassInfo : applications) {
if (Modifier.isAbstract(applicationClassInfo.flags())) {
continue;
}
if (selectedAppClass != null) {
throw new RuntimeException("More than one Application class: " + applications);
}
selectedAppClass = applicationClassInfo;
if (selectedAppClass.annotationsMap().containsKey(ResteasyDotNames.CDI_INJECT)) {
throw new RuntimeException(
"Usage of '@Inject' is not allowed in 'jakarta.ws.rs.core.Application' classes. Offending class is '"
+ selectedAppClass.name() + "'");
}
if (jakartaRestApplicationClass.annotationsMap().containsKey(ResteasyDotNames.CDI_INJECT)) {
throw new RuntimeException(
"Usage of '@Inject' is not allowed in 'jakarta.ws.rs.core.Application' classes. Offending class is '"
+ jakartaRestApplicationClass.name() + "'");
}

String applicationClass = applicationClassInfo.name().toString();
try {
Class<?> appClass = Thread.currentThread().getContextClassLoader().loadClass(applicationClass);
application = (Application) appClass.getConstructor().newInstance();
Set<Class<?>> classes = application.getClasses();
if (!classes.isEmpty()) {
for (Class<?> klass : classes) {
allowedClasses.add(klass.getName());
}
String applicationClass = jakartaRestApplicationClass.name().toString();
try {
Class<?> appClass = Thread.currentThread().getContextClassLoader().loadClass(applicationClass);
application = (Application) appClass.getConstructor().newInstance();
Set<Class<?>> classes = application.getClasses();
if (!classes.isEmpty()) {
for (Class<?> klass : classes) {
allowedClasses.add(klass.getName());
}
classes = application.getSingletons().stream().map(Object::getClass).collect(Collectors.toSet());
if (!classes.isEmpty()) {
for (Class<?> klass : classes) {
allowedClasses.add(klass.getName());
}
}
classes = application.getSingletons().stream().map(Object::getClass).collect(Collectors.toSet());
if (!classes.isEmpty()) {
for (Class<?> klass : classes) {
allowedClasses.add(klass.getName());
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException
| InvocationTargetException e) {
throw new RuntimeException("Unable to handle class: " + applicationClass, e);
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException
| InvocationTargetException e) {
throw new RuntimeException("Unable to handle class: " + applicationClass, e);
}
return allowedClasses;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.HashSet;
import java.util.Set;

import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.container.*;
Expand Down Expand Up @@ -310,6 +311,7 @@ public Set<Object> getSingletons() {
}
}

@ApplicationPath("whatever")
public static abstract class AppTest2 extends Application {

}
Expand Down

0 comments on commit 8eae5d4

Please sign in to comment.