spring-boot-starter-aop around annotated interface's method won't advice my aspect - aop

I have the following implementation:
public interface BusinessResource {
#RequiresAuthorization
public ResponseEnvelope getResource(ParamObj param);
}
and
#Component
public class BusinessResourceImpl implements BusinessResource {
#Autowired
public Response getResource(ParamObj param) {
return Response.ok().build();
}
}
and
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class AuthorizerAspect {
protected static final Logger LOGGER =
LoggerFactory.getLogger(AuthorizerAspect.class);
#Autowired
public AuthorizerAspect() {
LOGGER.info("Break point works here..." +
"so spring is creating the aspect as a component...");
}
#Around(value="#annotation(annotation)")
public Object intercept(ProceedingJoinPoint jp,
RequiresAuthorization annotation) throws Throwable {
LOGGER.info("BEGIN");
jp.proceed();
LOGGER.info("END");
}
}
The maven dependencies are properly configured with the spring-boot-starter-aop dependency. So what happens is that AuthorizerAspect won't intercept around the getResource method if the #RequiresAuthorization is used on the declared method of the BusinessResource interface, but if I change the implementation to annotate the same method now in the BusinessResourceImpl class, the aspect will take place.
NOTE: With the annotation in the interface level, the proxy isn't even created, whereas the annotation being placed in the implementation level will create a proxy for the resource.
Question is: Is there a way to advice objects which the annotation is present just on the interface?

May this alternative be useful for those who like me found no direct approach to sort that limitation on Spring AOP through proxies:
public interface BusinessResource {
#RequiresAuthorization
public ResponseEnvelope getResource(ParamObj param);
}
And
#Component
public class BusinessResourceImpl implements BusinessResource {
#Autowired
public Response getResource(ParamObj param) {
return Response.ok().build();
}
}
And
import import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
#Configuration
public class AuthorizerAspect {
protected static final Logger LOGGER =
LoggerFactory.getLogger(AuthorizerAspect.class);
#Autowired
public AuthorizerAspect() {
LOGGER.info("Break point works here..." +
"so spring is creating the aspect as a component...");
}
public Object invoke(MethodInvocation invocation) throws Throwable {
LOGGER.info("BEGIN");
invocation.proceed();
LOGGER.info("END");
}
#Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();
}
#Bean("requiresAuthorizationPointcut")
public AbstractPointcutAdvisor createPointcut() {
return new AbstractPointcutAdvisor() {
private static final long serialVersionUID = 4733447191475535406L;
#Override
public Advice getAdvice() {
return AuthorizerAspect.this;
}
#Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
#Override
public boolean matches(Method method, Class<?> targetClass) {
if (method.isAnnotationPresent(RequiresAuthorization.class)) {
return true;
}
if (method.getDeclaringClass().isInterface()) {
String methodName = method.getName();
try {
Method targetMethod = targetClass.getMethod(methodName, method.getParameterTypes());
return targetMethod != null && targetMethod.isAnnotationPresent(RequiresAuthorization.class);
} catch (NoSuchMethodException |
SecurityException e) {
LOGGER.debug("FAILURE LOG HERE",
e.getMessage());
return false;
}
}
return method.isAnnotationPresent(RequiresAuthorization.class);
}
};
}
};
}
}
So as you'll notice, we're sorting it by using method interceptors.

Related

Using and extending JOOQ generated DAOs with injected DataSource?

I'm new to JOOQ... The following code seems to work in WildFly 22 but I'm not sure if that is the best way to do things. What is the preferred way to inject WF DataSource to JOOQ DAOs (my extended ones)? Is there a way to avoid doing the ".get()." in the service below and just leave #Resource(...) etc. connection related for the MyCompanyDAO to handle internally?
In other words: companyDAO.get().fetchOneById(id) vs. companyDAO.fetchOneById(id)
#Stateless
public class CompanyService extends DefaultCompanyService {
#Inject
private MyCompanyDAO companyDAO;
public Company find(Integer id) {
return companyDAO.get().fetchOneById(id);
}
}
#Stateless
public class MyCompanyDAO extends CompanyDao {
#Inject
private MyConnectionProvider cp;
public CompanyDAO get() { // since cannot use #Resource in dao constructor
this.configuration().set(cp).set(SQLDialect.POSTGRES);
return this;
}
// custom code here
}
public class CompanyDao extends DAOImpl<CompanyRecord, tables.pojos.Company, Integer> {
// jooq generated code here
}
#Stateless
#LocalBean
public class MyConnectionProvider implements ConnectionProvider {
#Resource(lookup = "java:/MyDS")
private DataSource dataSource;
#Override
public Connection acquire() throws DataAccessException {
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new DataAccessException("Could not acquire connection.", e);
}
}
#Override
public void release(Connection connection) throws DataAccessException {
try {
connection.close();
} catch (SQLException e) {
throw new DataAccessException("Could not release connection.", e);
}
}
}
Put initialization logic of MyCompanyDAO inside a #PostConstruct method.
#PostConstruct
public void init() {
this.configuration().set(cp).set(SQLDialect.POSTGRES);
}
This way, you don't need to call get:
#Inject
private MyCompanyDAO companyDAO;
public Company find(Integer id) {
return companyDAO.fetchOneById(id);
}
How about using constructor injection instead? The generated DAO classes offer a constructor that accepts a Configuration precisely for that:
#Stateless
public class MyCompanyDAO extends CompanyDao {
#Inject
public MyCompanyDAO (Configuration configuration) {
super(configuration);
}
}
If for some reason you cannot inject the entire configuration (which I'd recommend), you could still inject the ConnectionProvider:
#Stateless
public class MyCompanyDAO extends CompanyDao {
#Inject
public MyCompanyDAO (MyConnectionProvider cp) {
super(DSL.using(cp, SQLDialect.POSTGRES));
}
}

How to assert/validate the JSON body and properties returned by a Micronaut controller

I am a Micronaut/Java beginner and I am trying to design some tests for my controllers. I could not find many examples online so here is my question.
Below is the controller with 2 #GET requests:
#Controller("/api/v1")
public class MyController {
private final ClientNetworkList clientNetworkList;
private final ClientStatus clientStatus;
public MyController(
ClientNetworkList clientNetworkList,
ClientStatus clientStatus
){
this.ClientNetworkList = clientNetworkList;
this.ClientStatus = clientStatus;
}
#Get(uri = "/networkList", produces = MediaType.APPLICATION_JSON_STREAM)
Flowable<NetworkListPackage> packagesNetworkList() {
return ClientNetworkList.fetchPackages();
}
#Get(uri = "/channels/{stringParm}/status/", produces = MediaType.APPLICATION_JSON_STREAM)
Flowable<ChannelStatusPackage> packagesStatus(stringParm) {
return ClientStatus.fetchPackages(genesis);
}
}
The java object POJOs:
#Introspected
public class NetworkListPackage {
private List<NetworkList> networkList = null;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public List<NetworkList> getNetworkList() {
return networkList;
}
public void setNetworkList(List<NetworkList> networkList) {
this.networkList = networkList;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
public class NetworkList {
private String name;
private Boolean authEnabled;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getAuthEnabled() {
return authEnabled;
}
public void setAuthEnabled(Boolean authEnabled) {
this.authEnabled = authEnabled;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
#Introspected
public class ChannelStatusPackage {
private String chaincodeCount;
private String txCount;
private String latestBlock;
private String peerCount;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public String getChaincodeCount() {
return chaincodeCount;
}
public void setChaincodeCount(String chaincodeCount) {
this.chaincodeCount = chaincodeCount;
}
public String getTxCount() {
return txCount;
}
public void setTxCount(String txCount) {
this.txCount = txCount;
}
public String getLatestBlock() {
return latestBlock;
}
public void setLatestBlock(String latestBlock) {
this.latestBlock = latestBlock;
}
public String getPeerCount() {
return peerCount;
}
public void setPeerCount(String peerCount) {
this.peerCount = peerCount;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
And the potential tests:
#MicronautTest
class MyControllerTest {
#Inject
#Client("/")
RxStreamingHttpClient client;
#Test
public void verifyChannelStatusPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
//when:
HttpRequest request = HttpRequest.GET("/api/v1/channels/{stringParam}/status/");
Flowable<ChannelStatusPackage> channelStatusPackageStream = client.jsonStream(request, ChannelStatusPackage.class);
Iterable<ChannelStatusPackage> channelStatusPackages = channelStatusPackageStream.blockingIterable();
//then:
//How to assert the returned body compared to the POJO?
//How to handle the parameter in the request url?
#Test
public void verifyNetworkListPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
//when:
HttpRequest request = HttpRequest.GET("/api/v1/networkList");
Flowable<NetworkListPackage> networkListPackageStream = client.jsonStream(request, NetworkListPackage.class);
Iterable<NetworkListPackage> networkListPackages = networkListPackageStream.blockingIterable();
//then:
//How to assert the returned body and compared to the POJO?
//How to assert the returned properties ?
}
}
Based on the previous code, how can I test that the returned bodies and properties of the requests matches the POJOs?
What are the usual test to be carried out?
Thank you very much for helping.
Normaly, the basic assertion start by testing the object type, so this should validate your schema.
An other way to test it is to use RestAssured, witch is a bit more readable.
You need to import the fallowing dependencies in you build.gradle
testImplementation("io.rest-assured:rest-assured:4.2.+")
testImplementation("io.rest-assured:json-schema-validator:4.2.+")
You need test annotation processor to enable micronaut injection and junit 5 for the BeforeEach.
The full test dependencies:
testAnnotationProcessor("io.micronaut:micronaut-inject-java")
testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("io.micronaut.test:micronaut-test-junit5")
testImplementation("io.rest-assured:rest-assured:4.2.+")
testImplementation("io.rest-assured:json-schema-validator:4.2.+")
testRuntime("org.junit.jupiter:junit-jupiter-engine")
Then you can wright your tests like that:
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import io.micronaut.http.HttpStatus;
import io.micronaut.runtime.server.EmbeddedServer;
import io.micronaut.test.annotation.MicronautTest;
import io.restassured.RestAssured;
import javax.inject.Inject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
#MicronautTest
class MyControllerTest {
#Inject
private EmbeddedServer embeddedServer;
#BeforeEach
public void setUp() {
RestAssured.port = embeddedServer.getPort();
}
#Test
public void verifyChannelStatusPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
given()
.when()
.pathParam("stringParam", "value")
.get("/api/v1/channels/{stringParam}/status/")
.then()
.statusCode(HttpStatus.OK.getCode())
.body(
"chaincodeCount", equalTo("chaincodeCountValue"),
"txCount", equalTo("txCountValue"),
"latestBlock", equalTo("latestBlockValue"),
"peerCount", equalTo("peerCountValue"),
"additionalProperties.key1", equalTo("additionalPropertyValue1"),
"additionalProperties.key2", equalTo("additionalPropertyValue2")
);
}
#Test
public void verifyNetworkListPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
given()
.when()
.get("/api/v1/networkList")
.then()
.statusCode(HttpStatus.OK.getCode())
.body(
"networkList.name[0]", equalTo("nameValue0"),
"networkList.authEnabled[0]", equalTo("authEnabledValue0"),
"networkList.additionalProperties[0].key1", equalTo("additionalPropertiesValue1"),
"networkList.additionalProperties[0].key2", equalTo("additionalPropertyValue2")
);
}
}
This is not really the way you wanted to do your tests, but I hope it will help.
So I ended up using the "hasItems" matcher or/and the jackson schema matcher.
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import io.micronaut.http.HttpStatus;
import io.micronaut.runtime.server.EmbeddedServer;
import io.micronaut.test.annotation.MicronautTest;
import io.restassured.RestAssured;
import javax.inject.Inject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.hamcrest.Matchers.hasItems;
import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
#MicronautTest
class MyControllerTest {
#Inject
private EmbeddedServer embeddedServer;
#BeforeEach
public void setUp() {
RestAssured.port = embeddedServer.getPort();
}
#Test
public void verifyChannelStatusPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
given()
.when()
.pathParam("stringParam", "value")
.get("/api/v1/channels/{stringParam}/status/")
.then()
.statusCode(HttpStatus.OK.getCode())
.body(matchesJsonSchemaInClasspath("channelsStatus.json"))
.body("keySet()",hasItems(
"chaincodeCount",
"txCount",
"latestBlock",
"peerCount",
);
}
#Test
public void verifyNetworkListPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
given()
.when()
.get("/api/v1/networkList")
.then()
.statusCode(HttpStatus.OK.getCode())
.body(matchesJsonSchemaInClasspath("networkList.json"))
.body("networkList.keySet()",hasItems(
"name",
"authEnabled",
);
}
}
``
Another option is to use jsonPath similar to Spring Boot MockMvc ResultMatcher:
testImplementation 'com.jayway.jsonpath:json-path:2.4.0'
testImplementation 'org.hamcrest:hamcrest:2.2'
Get the response as HttpResponse<String> and then use JsonPath.parse(response.body()) to assert the json path:
#Test
public void verifyChannelStatusPackagesCanBeFetchedWithCompileTimeAutoGeneratedAtClient() {
URI uri = UriBuilder.of("/api/v1/channels/{stringParam}/status/").expand(singletonMap("stringParam", "value"));
HttpResponse<String> response = client.toBlocking().exchange(HttpRequest.GET(uri), String.class);
assertEquals(HttpStatus.OK, response.getStatus());
ReadContext ctx = JsonPath.parse(response.body());
assertThat(ctx.read("$"), isA(Object.class));
assertThat(ctx.read("$.chaincodeCount"), is("chaincodeCountValue"));
}
Example for an endpoint test using Micronaut vs Spring Boot

Jackson-Serialiser: Ignore Field at Serialisation Time

My situation asks for a bit more complex serialisation. I have a class Available (this is a very simplified snippet):
public class Available<T> {
private T value;
private boolean available;
...
}
So a POJO
class Tmp {
private Available<Integer> myInt = Available.of(123);
private Available<Integer> otherInt = Available.clean();
...
}
would normally result in
{"myInt":{available:true,value:123},"otherInt":{available:false,value:null}}
However, I want a serialiser to render the same POJO like this:
{"myInt":123}
What I have now:
public class AvailableSerializer extends JsonSerializer<Available<?>> {
#Override
public void serialize(Available<?> available, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException, JsonProcessingException {
if (available != null && available.isAvailable()) {
jsonGenerator.writeObject(available.getValue());
}
// MISSING: nothing at all should be rendered here for the field
}
#Override
public Class<Available<?>> handledType() {
#SuppressWarnings({ "unchecked", "rawtypes" })
Class<Available<?>> clazz = (Class) Available.class;
return clazz;
}
}
A test
#Test
public void testSerialize() throws Exception {
SimpleModule module = new SimpleModule().addSerializer(new AvailableSerializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
System.out.println(objectMapper.writeValueAsString(new Tmp()));
}
outputs
{"myInt":123,"otherInt"}
Can anyone tell me how to do the "MISSING"-stuff? Or if I'm doing it all wrong, how do I do it then?
The restriction I have is that I don't want the developers to add #Json...-annotations all the time to fields of type Available. So the Tmp-class above is an example of what a typical using class should look like. If that's possible...
Include.NON_DEFAULT
If we assume that your clean method is implemented in this way:
class Available<T> {
public static final Available<Object> EMPTY = clean();
//....
#SuppressWarnings("unchecked")
static <T> Available<T> clean() {
return (Available<T>) EMPTY;
}
}
You can set serialisation inclusion to JsonInclude.Include.NON_DEFAULT value and it should skip values set to EMPTY (default) values. See below example:
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import java.io.IOException;
public class JsonApp {
public static void main(String[] args) throws Exception {
SimpleModule module = new SimpleModule();
module.addSerializer(new AvailableSerializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
System.out.println(objectMapper.writeValueAsString(new Tmp()));
}
}
class AvailableSerializer extends JsonSerializer<Available<?>> {
#Override
public void serialize(Available<?> value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
jsonGenerator.writeObject(value.getValue());
}
#Override
#SuppressWarnings({"unchecked", "rawtypes"})
public Class<Available<?>> handledType() {
return (Class) Available.class;
}
}
Above code prints:
{"myInt":123}
Custom BeanPropertyWriter
If you do not want to use Include.NON_DEFAULT you can write your custom BeanPropertyWriter and skip all values you want. See below example:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
SimpleModule module = new SimpleModule();
module.addSerializer(new AvailableSerializer());
module.setSerializerModifier(new BeanSerializerModifier() {
#Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
List<BeanPropertyWriter> writers = new ArrayList<>(beanProperties.size());
for (BeanPropertyWriter writer : beanProperties) {
if (writer.getType().getRawClass() == Available.class) {
writer = new SkipNotAvailableBeanPropertyWriter(writer);
}
writers.add(writer);
}
return writers;
}
});
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
System.out.println(objectMapper.writeValueAsString(new Tmp()));
}
}
class AvailableSerializer extends JsonSerializer<Available<?>> {
#Override
public void serialize(Available<?> value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
jsonGenerator.writeObject(value.getValue());
}
#Override
#SuppressWarnings({"unchecked", "rawtypes"})
public Class<Available<?>> handledType() {
return (Class) Available.class;
}
}
class SkipNotAvailableBeanPropertyWriter extends BeanPropertyWriter {
SkipNotAvailableBeanPropertyWriter(BeanPropertyWriter base) {
super(base);
}
#Override
public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {
// copier from super.serializeAsField(bean, gen, prov);
final Object value = (_accessorMethod == null) ? _field.get(bean) : _accessorMethod.invoke(bean, (Object[]) null);
if (value == null || value instanceof Available && !((Available) value).isAvailable()) {
return;
}
super.serializeAsField(bean, gen, prov);
}
}
Above code prints:
{"myInt":123}
After Michał Ziober's answer I had to look for something regarding Include.NON_DEFAULT and the default object and ran into this answer explaining Include.NON_EMPTY that Google didn't return in my first research (thanks Google).
So things become easier, it's now:
public class AvailableSerializer extends JsonSerializer<Available<?>> {
#Override
public void serialize(Available<?> available, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException, JsonProcessingException {
jsonGenerator.writeObject(available.getValue());
}
#Override
public Class<Available<?>> handledType() {
#SuppressWarnings({ "unchecked", "rawtypes" })
Class<Available<?>> clazz = (Class) Available.class;
return clazz;
}
#Override
public boolean isEmpty(SerializerProvider provider, Available<?> value) {
return value == null || !value.isAvailable();
}
}
with the test
#Test
public void testSerialize() throws Exception {
SimpleModule module = new SimpleModule().addSerializer(availableSerializer);
objectMapper.registerModule(module);
objectMapper.configOverride(Available.class).setInclude(
// the call comes from JavaDoc of objectMapper.setSerializationInclusion(...)
JsonInclude.Value.construct(JsonInclude.Include.NON_EMPTY, JsonInclude.Include.ALWAYS));
Tmp tmp = new Tmp();
assertThat(objectMapper.writeValueAsString(tmp)).isEqualTo("{\"myInt\":123}");
tmp.otherInt.setValue(123);
assertThat(objectMapper.writeValueAsString(tmp)).isEqualTo("{\"myInt\":123,\"otherInt\":123}");
}
So please, if you upvote my answer please also upvote Michał Ziober's as that's also working with a mildly different approach.

How to manage the life cycle in react native

I know when we need a nativeUI component we need override function
getName()and
createViewInstance(ThemedReactContext context) But when I use a dependence about map. The API need me use the component like this
#Override
protected void onDestroy() {
super.onDestroy();
//在activity执行onDestroy时执行mMapView.onDestroy(),
mMapView.onDestroy();
}
#Override
protected void onResume() {
super.onResume();
//在activity执行onResume时执行mMapView. onResume (),
mMapView.onResume();
}
#Override
protected void onPause() {
super.onPause();
//在activity执行onPause时执行mMapView. onPause (),
mMapView.onPause();
}
}
I override the function getetName()and
createViewInstance(ThemedReactContext context) like this
#Override
public String getName() {
return REACT_CLASS;
}
#Override
public MapView createViewInstance(ThemedReactContext context) {
final ThemedReactContext mapContext = context;
bdMapViewInstance = new MapView(context);
bdMapViewInstance.getMap().setOnMarkerClickListener(new BaiduMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
ShopResponseInfo shopResponseInfo = (ShopResponseInfo) marker.getExtraInfo().getSerializable(INDENTIFY);
if(shopResponseInfo != null){
String id = shopResponseInfo.getShop_id() + "";
String shop_name = shopResponseInfo.getShop_name() + "";
onReceiveNativeEvent(mapContext,bdMapViewInstance,id,shop_name);
}
return true;
}
});
return bdMapViewInstance;
}
Finally There are some performance question in my app. I don't know whether it is affect on my app's performance. And I don't know how to meet the official suggestion. I don't know how to control the life cycle of nativeUI component for android. Very Thanks.
You can listen to activity life cycle like this in your createViewInstance. You probably want to keep track of listeners and remove them appropriately based on you keep track of your instances.
LifecycleEventListener lifecycleEventListener = new LifecycleEventListener() {
#Override
public void onHostResume() {
}
#Override
public void onHostPause() {
}
#Override
public void onHostDestroy() {
}
};
getReactApplicationContext().addLifecycleEventListener(lifecycleEventListener);
An example of TestModule as explained in the docs of RN. PS Don't forget to use implements LifecycleEventListener
package com.testpackage;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.LifecycleEventListener;
import java.util.Map;
import java.util.HashMap;
public class TestModule extends ReactContextBaseJavaModule implements LifecycleEventListener {
public TestModule(ReactApplicationContext reactContext) {
super(reactContext);
reactContext.addLifecycleEventListener(this);
}
#Override
public void onHostResume() {
}
#Override
public void onHostPause() {
}
#Override
public void onHostDestroy() {
}
}

#EventHandler events not working

I am trying to make a simple addition to my plugin so that when someone joins they receive a message that says "Heyyyyyyy". My plugin has a few commands also.
Here's my Main class:
package me.ben.test;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
public class Main extends JavaPlugin implements Listener {
#Override
public void onEnable() {
this.getServer().getPluginManager().registerEvents(new Click(), this);
getLogger().info("The Plugin Has Been Enabled!");
}
#Override
public void onDisable() {
getLogger().info("The Plugin Has Been Disabled!");
}
public boolean onCommand(CommandSender sender, Command cmd, String label,
String[] args) {
if (cmd.getName().equalsIgnoreCase("hello") && sender instanceof Player) {
Player player = (Player) sender;
player.sendMessage("Hello, " + player.getName() + "!");
return true;
} else if (cmd.getName().equalsIgnoreCase("isonline")
&& args.length == 1) {
Player target = Bukkit.getServer().getPlayer(args[0]);
if (target == null) {
sender.sendMessage(ChatColor.AQUA + "Player " + args[0]
+ " is not online.");
return true;
} else if (target != null) {
sender.sendMessage(ChatColor.AQUA + "Player " + args[0]
+ " is online.");
return true;
} else {
return false;
}
}
return false;
}
}
and here is my Click class:
package me.ben.test;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
public class Click extends JavaPlugin implements Listener {
#EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
event.getPlayer().sendMessage("Heyyyyyyy");
}
}
All of the #EventHandler things are not working so I quick made this simple one.
You can have only one class that extends JavaPlugin. Remove extends JavaPlugin from your Click Class - only your main class should extend JavaPlugin.
Check out Bukkit's official plugin tutorial for help on coding Bukkit Plugins.
You are using Listener in your Main class but you are not handling any event there, use it only when you want the class to be able to handler bukkit events.
You can use Listener with your Main class if you want, but you'll need to put the methods that handles events in your main class, but it'll become messy in big projects...
You also don't need to extend JavaPlugin everywhere, just in your main class.
If you want to use your main class:
public class Main extends JavaPlugin implements Listener {
#Override
public void onEnable() {
this.getServer().getPluginManager().registerEvents(this, this);
getLogger().info("The Plugin Has Been Enabled!");
}
#EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
event.getPlayer().sendMessage("Heyyyyyyy");
}
}
If you want to use a separated class to handle events:
public class Main extends JavaPlugin {
#Override
public void onEnable() {
this.getServer().getPluginManager().registerEvents(new Click(), this);
getLogger().info("The Plugin Has Been Enabled!");
}
}
public class Click implements Listener {
#EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
event.getPlayer().sendMessage("Heyyyyyyy");
}
}
Don't forget that you need to create a plugin.yml file correctly otherwise nothing will work.