Quick way to check if the REST API is alive – GET details from Manifest file

There might be cases when you want to quickly verify if your REST API, that is deployed either on dev, test or prod environments, is reachable altogether. A common way to do this is by building a generic resource that delivers for example the version of the deployed API. You can trigger a request to this resource manually or, even better, have a Jenkings/Hudson job, which runs a checkup job after deployment. In this post, I will present how to implement such a service that reads the implementation details from the application’s manifest file. The API verified, is the one developed in the Tutorial – REST API design and implementation in Java with Jersey and Spring

Software used

  1. Jersey JAX-RS implementation 2.14
  2. Spring 4.1.4
  3. Maven 3.1.1
  4. JDK 7

REST resource

I have developed two REST resources reading from the Manifest file :

  • /manifest – returns the manifest’s main attributes as a key, value pairs
  • /manifest/implementation-details – returns only the implementation details from the manifest file
@Path("/manifest")
public class ManifestResource {

	@Autowired
	ManifestService manifestService;

	@GET
	@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
	public Response getManifestAttributes() throws FileNotFoundException, IOException{
		Attributes manifestAttributes = manifestService.getManifestAttributes();

		return Response.status(Response.Status.OK)
				.entity(manifestAttributes)
				.build();
	}

	@Path("/implementation-details")
	@GET
	@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
	public Response getVersion() throws FileNotFoundException, IOException{
		ImplementationDetails implementationVersion = manifestService.getImplementationVersion();

		return Response.status(Response.Status.OK)
				.entity(implementationVersion)
				.build();
	}

}

Request

GET http://localhost:8888/demo-rest-jersey-spring/manifest HTTP/1.1
Accept-Encoding: gzip,deflate
Accept: application/json
Host: localhost:8888
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

Response – 200 OK

{
   "Implementation-Title": "DemoRestWS",
   "Implementation-Version": "0.0.1-SNAPSHOT",
   "Implementation-Vendor-Id": "org.codingpedia",
   "Built-By": "ama",
   "Build-Jdk": "1.7.0_40",
   "Manifest-Version": "1.0",
   "Created-By": "Apache Maven 3.1.1",
   "Specification-Title": "DemoRestWS",
   "Specification-Version": "0.0.1-SNAPSHOT"
}

The returned values in case of success (HTTP Status 200 OK) contain different default data related to implementation and specification details. These are automatically generated  the Manifest file with Maven plugin, which I will present in the next section.

Generate Manifest file with Maven

Since the demo application is a web application, I am using the Apache maven war plugin supported by the Apache Maven Archiver to generate a Manifest file:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-war-plugin</artifactId>
	<version>2.5</version>
	<configuration>
		<warName>${project.artifactId}</warName>
		<archive>
			<manifest>
				<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
				<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
			</manifest>
		</archive>					
	</configuration>
	<executions>
		<execution>
			<phase>package</phase>
			<goals>
				<goal>manifest</goal>
			</goals>
			<inherited>true</inherited>
		</execution>
	</executions>				
</plugin>

The addDefaultImplementationEntries and addDefaultSpecificationEntries will generate default implementation, respectively specification details, out of the project properties defined in the pom.xml file:

Implementation-Title: ${project.name}
Implementation-Version: ${project.version}
Implementation-Vendor-Id: ${project.groupId}
Implementation-Vendor: ${project.organization.name}
Implementation-URL: ${project.url}

, respectively:

Specification-Title: ${project.name}
Specification-Version: ${project.version}
Specification-Vendor: ${project.organization.name}

See  Apache Maven Archiver for further details.

Notice that in order to generate the Manifest.mf file also in the file system under webapp/META-INF, you need to bind the manifest goal to an execution phase (e.g. package):

<executions>
	<execution>
		<phase>package</phase>
		<goals>
			<goal>manifest</goal>
		</goals>
		<inherited>true</inherited>
	</execution>
</executions>

Read from Manifest file

Reading from the manifest file occurs in the injected ManifestService class:

public class ManifestService {

	@Autowired
	ServletContext context;

	Attributes getManifestAttributes() throws FileNotFoundException, IOException{
	    InputStream resourceAsStream = context.getResourceAsStream("/META-INF/MANIFEST.MF");
	    Manifest mf = new Manifest();
	    mf.read(resourceAsStream);
	    Attributes atts = mf.getMainAttributes();

	    return atts;	    		
	}

	ImplementationDetails getImplementationVersion() throws FileNotFoundException, IOException{
	    String appServerHome = context.getRealPath("/");
	    File manifestFile = new File(appServerHome, "META-INF/MANIFEST.MF");

	    Manifest mf = new Manifest();

	    mf.read(new FileInputStream(manifestFile));

	    Attributes atts = mf.getMainAttributes();
	    ImplementationDetails response = new ImplementationDetails();
	    response.setImplementationTitle(atts.getValue("Implementation-Title"));
	    response.setImplementationVersion(atts.getValue("Implementation-Version"));
	    response.setImplementationVendorId(atts.getValue("Implementation-Vendor-Id"));

	    return response;		
	}

}

To access the MANIFEST.MF file you need to inject the ServletContext, and call one of its methods

  • ServletContext#getRealPath() – gets the real path corresponding to the given virtual path. The real path returned will be in a form appropriate to the computer and operating system on which the servlet container is running, including the proper path separators. Its biggest problem in this case, if you don’t deploy the .war exploded you won’t have access to the manifest file.
  • Java EE version

    In a JavaEE environment, you would have the ServletContext injected via the @Context annotation:

    public class ManifestResource {
    
    	@Context
    	ServletContext context;
    
    	@GET
    	@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    	public Response getManifestAttributes() throws FileNotFoundException, IOException{
    	    InputStream resourceAsStream = context.getResourceAsStream("/META-INF/MANIFEST.MF");
    	    Manifest mf = new Manifest();
    	    mf.read(resourceAsStream);
    	    Attributes atts = mf.getMainAttributes();
    
    		return Response.status(Response.Status.OK)
    				.entity(atts)
    				.build();	    		
    	}
    	...
    }

    Here you have – a quick way to verify that your REST api is reachable. If you have any suggestions please leave a comment below.

    Resources

    1. Apache Maven
      1. Apache Maven Archiver
      2. Introduction to the Build Lifecycle#Built-in_Lifecycle_Bindings
    2. Oracle docs – Working with Manifest Files: The Basics
    3.  Stackoverflow
      1. How to get Maven Artifact version at runtime?
      2. How to Get Maven Project Version From Java Method as Like at Pom
    Podcastpedia image

    Adrian Matei

    Creator of Podcastpedia.org and Codingpedia.org, computer science engineer, husband, father, curious and passionate about science, computers, software, education, economics, social equity, philosophy - but these are just outside labels and not that important, deep inside we are all just consciousness, right?

    “Image courtesy of Stuart Miles/ FreeDigitalPhotos.net”

    How to configure Nginx in production to serve an Angular app and reverse proxy NodeJS

    Install Nginx on Ubuntu Server, understand configuration files, configure SSL, serve static files, reverse proxy Keycloak and NodeJS servers Continue reading