Tuesday, 23 May 2023

Micronaut: Introduction advice

Introduction advice introduces new behavior to a class.

 

Step 1: Define Introduction advice.

@Introduction
@Bean
@Documented
@Retention(RUNTIME)
@Target({ TYPE, ANNOTATION_TYPE, METHOD })
public @interface AppConfigProperty {
	String value() default "";
}

 

a.   The introduction advice is annotated with Introduction

b.   The Bean annotation is added so that all types annotated with @Stub become beans

 

Step 2: Implement MethodInterceptor interface.

@Singleton
@InterceptorBean(AppConfigProperty.class)
public class AppConfigPropertyIntroudction implements MethodInterceptor<Object, Object> { 
	
	private Environment environment;
	
	public AppConfigPropertyIntroudction(Environment environment) {
		this.environment = environment;
	}

	@Nullable
	@Override
	public Object intercept(MethodInvocationContext<Object, Object> context) {
		// As this annotation has only one property, this is sufficient
		 Optional<Object> valueOpt = context.getValue(AppConfigProperty.class, context.getReturnType().getType());
		 
		 if(valueOpt.isEmpty()) {
			 return null;
		 }
		 
		 Object obj = valueOpt.get();
		 String value = obj.toString();
		 return environment.getProperty(value, String.class, "no_property configured");
	}
}

 

a. The InterceptorBean annotation is used to associate the interceptor with the @Stub annotation

b. The class is annotated with @Singleton and implements the MethodInterceptor interface

c. The value of the @AppConfigProperty annotation is read from the context and an attempt made to convert the value to the return type Otherwise null is returned

 

Step 3: Use @AppConfigProperty in the application.

@AppConfigProperty
public interface AppConfigPropertyExample {

	@AppConfigProperty("runningEnvironment")
	String runningEnvironment();
	
	@AppConfigProperty("appName")
	String applicationName();

}

 

All the abstract methods delegate to the AppConfigPropertyIntroudction class to be implemented.

 

Find the below working application.

 

Step 1: Create new maven project ‘micronaut-introduction-advice’.

 

Step 2: Update pom.xml with maven dependencies.

 

pom.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.sample.app</groupId>
	<artifactId>micronaut-introduction-advice</artifactId>
	<version>0.1</version>
	<packaging>jar</packaging>

	<parent>
		<groupId>io.micronaut</groupId>
		<artifactId>micronaut-parent</artifactId>
		<version>3.7.3</version>
	</parent>

	<properties>
		<packaging>jar</packaging>
		<jdk.version>11</jdk.version>
		<release.version>11</release.version>
		<micronaut.version>3.7.3</micronaut.version>
		<micronaut.runtime>netty</micronaut.runtime>
		<exec.mainClass>com.sample.app.App</exec.mainClass>
	</properties>

	<repositories>
		<repository>
			<id>central</id>
			<url>https://repo.maven.apache.org/maven2</url>
		</repository>
	</repositories>

	<dependencies>
		<dependency>
			<groupId>io.micronaut</groupId>
			<artifactId>micronaut-inject</artifactId>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>io.micronaut</groupId>
			<artifactId>micronaut-validation</artifactId>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>io.micronaut</groupId>
			<artifactId>micronaut-http-client</artifactId>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>io.micronaut</groupId>
			<artifactId>micronaut-http-server-netty</artifactId>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>io.micronaut</groupId>
			<artifactId>micronaut-jackson-databind</artifactId>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>jakarta.annotation</groupId>
			<artifactId>jakarta.annotation-api</artifactId>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>io.micronaut.test</groupId>
			<artifactId>micronaut-test-junit5</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-api</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-engine</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>io.micronaut.build</groupId>
				<artifactId>micronaut-maven-plugin</artifactId>
			</plugin>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<!-- Uncomment to enable incremental compilation -->
					<!-- <useIncrementalCompilation>false</useIncrementalCompilation> -->

					<annotationProcessorPaths
						combine.children="append">
						<path>
							<groupId>io.micronaut</groupId>
							<artifactId>micronaut-http-validation</artifactId>
							<version>${micronaut.version}</version>
						</path>
					</annotationProcessorPaths>

				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

 

Step 3: Create application.yml file under src/main/resources folder.

 

application.yml

 

logger:
  levels:
    io.netty: ERROR
    io.micronaut: ERROR
    
runningEnvironment: local
appName: chat server

Step 4: Define Introduction advice.

 

AppConfigProperty.java

package com.sample.app.advices;

import io.micronaut.aop.Introduction;
import io.micronaut.context.annotation.Bean;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Introduction
@Bean
@Documented
@Retention(RUNTIME)
@Target({ TYPE, ANNOTATION_TYPE, METHOD })
public @interface AppConfigProperty {
	String value() default "";
}

Step 5: Implement MethodInterceptor.

 

AppConfigPropertyIntroudction.java

package com.sample.app.interceptor;

import java.util.Optional;

import com.sample.app.advices.AppConfigProperty;

import io.micronaut.aop.InterceptorBean;
import io.micronaut.aop.MethodInterceptor;
import io.micronaut.aop.MethodInvocationContext;
import io.micronaut.context.env.Environment;
import io.micronaut.core.annotation.Nullable;
import jakarta.inject.Singleton;

@Singleton
@InterceptorBean(AppConfigProperty.class)
public class AppConfigPropertyIntroudction implements MethodInterceptor<Object, Object> { 
	
	private Environment environment;
	
	public AppConfigPropertyIntroudction(Environment environment) {
		this.environment = environment;
	}

	@Nullable
	@Override
	public Object intercept(MethodInvocationContext<Object, Object> context) {
		// As this annotation has only one property, this is sufficient
		 Optional<Object> valueOpt = context.getValue(AppConfigProperty.class, context.getReturnType().getType());
		 
		 if(valueOpt.isEmpty()) {
			 return null;
		 }
		 
		 Object obj = valueOpt.get();
		 String value = obj.toString();
		 return environment.getProperty(value, String.class, "no_property configured");
	}
}

Step 6: Define AppConfigPropertyExample class.

 

AppConfigPropertyExample.java

package com.sample.app.interfaces;

import com.sample.app.advices.AppConfigProperty;

@AppConfigProperty
public interface AppConfigPropertyExample {

	@AppConfigProperty("runningEnvironment")
	String runningEnvironment();
	
	@AppConfigProperty("appName")
	String applicationName();

}

Step 7: Define main application class.

 

App.java

package com.sample.app;

import com.sample.app.interfaces.AppConfigPropertyExample;

import io.micronaut.context.ApplicationContext;

public class App {

	public static void main(String[] args) {

		try (ApplicationContext applicationContext = ApplicationContext.run()) {
			AppConfigPropertyExample appConfigPropertyEx = applicationContext.getBean(AppConfigPropertyExample.class);

			System.out.println(appConfigPropertyEx.runningEnvironment());
			System.out.println(appConfigPropertyEx.applicationName());
		}

	}
}

Total project structure looks like below.



Build the project using mvn package command.

Navigate to the folder where pom.xml is located and execute the command ‘mvn package’.

 

Upon command successful execution, you can see the jar file ‘micronaut-introduction-advice-0.1.jar’ in project target folder.

$ ls ./target/
classes
generated-sources
generated-test-sources
maven-archiver
maven-status
micronaut-introduction-advice-0.1.jar
original-micronaut-introduction-advice-0.1.jar
test-classes

Execute below command to run the application.

java -jar ./target/micronaut-introduction-advice-0.1.jar

You can download the application from this link.


Previous                                                    Next                                                    Home

No comments:

Post a Comment