Skip to content

Commit

Permalink
Glassfish service name detector (#579)
Browse files Browse the repository at this point in the history
This is a continuation of #562 that introduces the first of several web
app servers for which we will detect the service name.

Because this is the first (GlassFish), it includes some of the
helper/plumbing utilities. Subsequent additions will mostly be the
`AppServer` implementations (and adding them to the delegate list).
  • Loading branch information
breedx-splk committed Nov 15, 2022
1 parent 40fb370 commit 2e8edf3
Show file tree
Hide file tree
Showing 9 changed files with 658 additions and 4 deletions.
8 changes: 6 additions & 2 deletions resource-providers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ to populate the `service.name` resource attribute based on the runtime configura
of an app server. This is useful when a user has not yet specified the `service.name`
resource attribute manually.

It is capable of detecting common scenarios among the following popular application servers:
This `ResourceProvider` supports `.ear` and `.war` archives as well as exploded directory
versions of each. For `.war` files, it attempts to parse the `<web-app>` element
from `WEB-INF/web.xml`. For `.ear` files the `<application>` element of `META-INF/application.xml`.

* tbd (will be filled in as implementations are added)
It is capable of detecting common scenarios among the popular application servers listed below:

* GlassFish
* _remaining are tbd_

## Component owners

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
interface AppServer {

/** Path to directory to be scanned for deployments. */
@Nullable
Path getDeploymentDir() throws Exception;

/**
* Returns a single class that, when present, determines that the given application server is
* active/running.
*/
@Nullable
Class<?> getServerClass();

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.resourceproviders;

import static java.util.logging.Level.FINE;

import com.google.errorprone.annotations.MustBeClosed;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.logging.Logger;
import java.util.stream.Stream;
import javax.annotation.Nullable;

final class AppServerServiceNameDetector implements ServiceNameDetector {

private static final Logger logger =
Logger.getLogger(AppServerServiceNameDetector.class.getName());

private final AppServer appServer;
private final ParseBuddy parseBuddy;
private final DirectoryTool dirTool;

AppServerServiceNameDetector(AppServer appServer) {
this(appServer, new ParseBuddy(appServer), new DirectoryTool());
}

// Exists for testing
AppServerServiceNameDetector(AppServer appServer, ParseBuddy parseBuddy, DirectoryTool dirTool) {
this.appServer = appServer;
this.parseBuddy = parseBuddy;
this.dirTool = dirTool;
}

@Override
@Nullable
public String detect() throws Exception {
if (appServer.getServerClass() == null) {
return null;
}

Path deploymentDir = appServer.getDeploymentDir();
if (deploymentDir == null) {
return null;
}

if (!dirTool.isDirectory(deploymentDir)) {
logger.log(FINE, "Deployment dir '{0}' doesn't exist.", deploymentDir);
return null;
}

logger.log(FINE, "Looking for deployments in '{0}'.", deploymentDir);
try (Stream<Path> stream = dirTool.list(deploymentDir)) {
return stream.map(this::detectName).filter(Objects::nonNull).findFirst().orElse(null);
}
}

@Nullable
private String detectName(Path path) {
if (!appServer.isValidAppName(path)) {
logger.log(FINE, "Skipping '{0}'.", path);
return null;
}

logger.log(FINE, "Attempting service name detection in '{0}'.", path);
String name = path.getFileName().toString();
if (dirTool.isDirectory(path)) {
return parseBuddy.handleExplodedApp(path);
}
if (name.endsWith(".war")) {
return parseBuddy.handlePackagedWar(path);
}
if (appServer.supportsEar() && name.endsWith(".ear")) {
return parseBuddy.handlePackagedEar(path);
}

return null;
}

// Exists for testing
static class DirectoryTool {
boolean isDirectory(Path path) {
return Files.isDirectory(path);
}

@MustBeClosed
Stream<Path> list(Path path) throws IOException {
return Files.list(path);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

package io.opentelemetry.resourceproviders;

import java.net.URL;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;

/**
* This class is just a factory that provides a ServiceNameDetector that knows how to find and parse
Expand All @@ -21,7 +23,30 @@ static ServiceNameDetector create() {
private CommonAppServersServiceNameDetector() {}

private static List<ServiceNameDetector> detectors() {
// TBD: This will contain common app server detector implementations
return Collections.emptyList();
ResourceLocator locator = new ResourceLocatorImpl();
// Additional implementations will be added to this list.
return Collections.singletonList(detectorFor(new GlassfishAppServer(locator)));
}

private static AppServerServiceNameDetector detectorFor(AppServer appServer) {
return new AppServerServiceNameDetector(appServer);
}

private static class ResourceLocatorImpl implements ResourceLocator {

@Override
@Nullable
public Class<?> findClass(String className) {
try {
return Class.forName(className, false, ClassLoader.getSystemClassLoader());
} catch (ClassNotFoundException | LinkageError exception) {
return null;
}
}

@Override
public URL getClassLocation(Class<?> clazz) {
return clazz.getProtectionDomain().getCodeSource().getLocation();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.resourceproviders;

import java.nio.file.Path;
import java.nio.file.Paths;
import javax.annotation.Nullable;

class GlassfishAppServer implements AppServer {

private static final String SERVICE_CLASS_NAME = "com.sun.enterprise.glassfish.bootstrap.ASMain";
private final ResourceLocator locator;

GlassfishAppServer(ResourceLocator locator) {
this.locator = locator;
}

@Nullable
@Override
public Path getDeploymentDir() {
String instanceRoot = System.getProperty("com.sun.aas.instanceRoot");
if (instanceRoot == null) {
return null;
}

// besides autodeploy directory it is possible to deploy applications through admin console and
// asadmin script, to detect those we would need to parse config/domain.xml
return Paths.get(instanceRoot, "autodeploy");
}

@Override
@Nullable
public Class<?> getServerClass() {
return locator.findClass(SERVICE_CLASS_NAME);
}
}
Loading

0 comments on commit 2e8edf3

Please sign in to comment.