I use for debugging reasons the java.lang.reflect.Proxy stuff to have a generic way to implement all possible interfaces... but this seems to be difficult to get it working with proguard. Any suggestions?
THX
-Marco
public class DebugLogListenerFactory {
public static IAirplaneListenerAll createStreamHandle(ICAirplane plane) {
DebugLogListenerHandler handler = new DebugLogListenerHandler(plane);
IAirplaneListenerAll proxy = (IAirplaneListenerAll) Proxy
.newProxyInstance(IAirplaneListenerAll.class.getClassLoader(),
new Class[] { IAirplaneListenerAll.class }, handler);
plane.addListener(proxy);
return proxy;
}
private static class DebugLogListenerHandler implements InvocationHandler {
private final Level levDef = Level.FINE;
public DebugLogListenerHandler() {
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("invoked" + method);
String methodName = method.getName();
String msg = methodName + ": ";
if (args != null) {
boolean first = true;
for (Object o : args) {
if (first) {
first = false;
} else {
msg += " ,";
}
msg += o.toString();
}
}
CDebug.getLog().log(levDef, msg);
return null;
}
}
}
The easiest solution is probably to avoid shrinking/optimizing/obfuscating the interface and its methods:
-keep interface some.package.IAirplaneListenerAll {
<methods>;
}
You might allow shrinking:
-keep,allowshrinking interface some.package.IAirplaneListenerAll {
<methods>;
}
If the InvocationHandler can deal with obfuscated method names, you might also allow obfuscation:
-keep,allowshrinking,allowobfuscation interface some.package.IAirplaneListenerAll {
<methods>;
}
Related
According to documentation, I can use something like this in exceptionExpression: #Retryable(exceptionExpression="message.contains('this can be retried')")
But I want to get response body and check message inside it (from RestClientResponseException), something similar to this: exceptionExpression = "getResponseBodyAsString().contains('important message')"
I tried like that but it doesn't work. So, is it possible to do something similar and check info from responseBody?
Edit: Adding whole #Retryable annotation parameters with Gary Russell's suggestion:
#Retryable(value = HttpClientErrorException.class, exceptionExpression = "#{#root instanceof T(org.springframework.web.client.HttpClientErrorException) AND responseBodyAsString.contains('important message')}")
I'm using actual RestClientResponseException subclass that I'm catching but is still not triggering retry.
With the current release, the expression incorrectly requires static template markers; they will not be needed in 1.3.
#Retryable(exceptionExpression = "#{responseBodyAsString.contains('foo')}")
However, you can't use this expression if there are include or exclude properties so the expression should check the type:
#Retryable(exceptionExpression =
"#{#root instanceof T(org.springframework.web.client.RestClientResponseException) "
+ "AND responseBodyAsString.contains('foo')}")
EDIT
#SpringBootApplication
#EnableRetry
public class So61488237Application {
public static void main(String[] args) {
SpringApplication.run(So61488237Application.class, args).close();
}
#Bean
public ApplicationRunner runner(Foo foo) {
return args -> {
try {
foo.test(1, "foo.");
}
catch (Exception e) {
}
};
}
}
#Component
class Foo {
#Retryable(exceptionExpression =
"#{#root instanceof T(org.springframework.web.client.RestClientException) "
+ "AND responseBodyAsString.contains('foo')}")
public void test(int val, String str) {
System.out.println(val + ":" + str);
throw new RestClientResponseException("foo", 500, "bar", new HttpHeaders(), "foo".getBytes(),
StandardCharsets.UTF_8);
}
}
1:foo.
1:foo.
1:foo.
I've implemented the following approach, which in my opinion is much more convenient.
#Retryable(value = WebClientException.class,
exceptionExpression = RetryCheckerService.EXPRESSION,
maxAttempts = 5,
backoff = #Backoff(delay = 500))
public List<ResultDto> getSomeResource () {}
Here the RetryCheckerService encapsulates all needed logic.
#Service
public class RetryCheckerService {
public static final String EXPRESSION = "#retryCheckerService.shouldRetry(#root)";
public boolean shouldRetry(WebClientException ex) {
if (ex instanceof WebClientResponseException responseException) {
return responseException.getStatusCode().is5xxServerError()
|| responseException.getStatusCode().equals(HttpStatus.NOT_FOUND);
}
if (ex instanceof WebClientRequestException requestException) {
String message = requestException.getMessage();
if (message == null) {
return false;
}
return message.contains("HttpConnectionOverHTTP");
}
return false;
}
}
I'm providing a value via a conditional Bean. If the Condition is met everything is fine but if the condition is not met (hence the bean is not present) my code fails. is there some way to check if the bean is defined before hand. in SpEL ?
I tried something like
#{someBean? someBean.myValue:null} but it does not work.
See this answer for why this works...
#SpringBootApplication
public class So56189689Application {
public static void main(String[] args) {
SpringApplication.run(So56189689Application.class, args);
}
#Value("#{containsObject('foo') ? getObject('foo').foo : null}")
String foo;
#Bean
public ApplicationRunner runner() {
return args -> System.out.println(foo);
}
// #Bean
// public Foo foo() {
// return new Foo();
// }
public static class Foo {
private String foo = "bar";
public String getFoo() {
return this.foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
}
}
EDIT
The #root object of the SpEL Expression is a BeanExpressionContext, you can invoke containsObject() and getObject() methods on that context.
Here's the code from the BeanExpressionContext:
public boolean containsObject(String key) {
return (this.beanFactory.containsBean(key) ||
(this.scope != null && this.scope.resolveContextualObject(key) != null));
}
public Object getObject(String key) {
if (this.beanFactory.containsBean(key)) {
return this.beanFactory.getBean(key);
}
else if (this.scope != null){
return this.scope.resolveContextualObject(key);
}
else {
return null;
}
}
Okay, so I'm trying to make a toggleable feature, whether they have it enabled/disabled is stored in the 'data.yml'. The issue I have with this is that the file does get updated (Asin, it does change from true to false and vice versa) but it doesn't actually apply the changes in-game.
Method for reloading the file:
public static void reloadConfig(File file, FileConfiguration conf) {
try {
conf.save(file);
} catch (Exception e) {
}
conf = YamlConfiguration.loadConfiguration(file);
}
Toggle command:
if (args[0].equalsIgnoreCase("toggle")) {
File file = new File("plugins/StatTrack", "data.yml");
if (file.exists()) {
FileConfiguration conf = YamlConfiguration.loadConfiguration(file);
if (conf.getBoolean("Users." + player.getName() + ".OreTracker") == true) {
conf.set("Users." + player.getName() + ".OreTracker", false);
try {
Main.reloadConfig(file, conf);
Main.message(player, "&cDisabled&f the Ore&8-&fTracker");
return true;
} catch (Exception e) {
Main.message(player, "&cSome fatal error occored");
return true;
}
} else if (conf.getBoolean("Users." + player.getName() + ".OreTracker") == false) {
conf.set("Users." + player.getName() + ".OreTracker", true);
try {
Main.reloadConfig(file, conf);
Main.message(player, "&aEnabled&f the Ore&8-&fTracker");
return true;
} catch (Exception e) {
Main.message(player, "&cSome fatal error occored");
return true;
}
}
}
}
If you need any more code or have any questions I'll happily supply the code/answer.
Thanks in advance.
The problem is that the plugin is saving the config in the reload method. I also wouldn't recommend using a static method in this case unless the class of the method is a Singleton.
So let's create a new class being a Singleton. The Singleton pattern describes a class which has only one instance accessible through static methods.
public class PluginConfig {
private static PluginConfig instance; // Static (global) reference to the instance
File confFile;
YamlConfiguration conf;
public PluginConfig(File confFile) {
this.confFile = confFile;
loadConfig();
}
public static YamlConfiguration getConfig() {
return instance.conf;
}
public static void loadConfig() {
instance.conf = YamlConfiguration.loadConfiguration(confFile);
}
// Extra method for another implementation, if potentially needed in the future
public static void reloadConfig() {
loadConfig();
}
}
Using that class you can access the config from everywhere with PluginConfig.getConfig()
I'm trying to add a new field to request's body, in a Zuul Pre-filter.
I'm using one of the Neflix's Zuul sample projects from here, and my filter's implementation is very similar to UppercaseRequestEntityFilter from this sample.
I was able to apply a transformation such as uppercase, or even to completely modify the request, the only inconvenient is that I'm not able to modify the content of body's request that has a length more than the original length of the body's request.
This is my filter's implementation:
#Component
public class MyRequestEntityFilter extends ZuulFilter {
public String filterType() {
return "pre";
}
public int filterOrder() {
return 10;
}
public boolean shouldFilter() {
RequestContext context = getCurrentContext();
return true;
}
public Object run() {
try {
RequestContext context = getCurrentContext();
InputStream in = (InputStream) context.get("requestEntity");
if (in == null) {
in = context.getRequest().getInputStream();
}
String body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
body = body.replaceFirst("qqq", "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq");
// body = body.toUpperCase();
context.set("requestEntity", new ServletInputStreamWrapper(body.getBytes("UTF-8")));
}
catch (IOException e) {
rethrowRuntimeException(e);
}
return null;
}
}
This is the request that I'm doing:
This is the response that I'm receiving:
I was able to obtain what I wanted, using the implementation of PrefixRequestEntityFilter, from sample-zuul-examples:
#Component
public class MyRequestEntityFilter extends ZuulFilter {
public String filterType() {
return "pre";
}
public int filterOrder() {
return 10;
}
public boolean shouldFilter() {
RequestContext context = getCurrentContext();
return true;
}
public Object run() {
try {
RequestContext context = getCurrentContext();
InputStream in = (InputStream) context.get("requestEntity");
if (in == null) {
in = context.getRequest().getInputStream();
}
String body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
body = body.replaceFirst("qqq", "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq");
byte[] bytes = body.getBytes("UTF-8");
context.setRequest(new HttpServletRequestWrapper(getCurrentContext().getRequest()) {
#Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStreamWrapper(bytes);
}
#Override
public int getContentLength() {
return bytes.length;
}
#Override
public long getContentLengthLong() {
return bytes.length;
}
});
}
catch (IOException e) {
rethrowRuntimeException(e);
}
return null;
}
}
My aspect:
[Serializable]
class FlowController : OnMethodBoundaryAspect
{
[ThreadStatic]
private static bool logging;
public override void OnEntry(MethodExecutionArgs args)
{
if (logging)
return;
try
{
logging = true;
if (ProgramState.State() == false)
{
args.ReturnValue = ""; // WHAT DO I SET HERE?
args.FlowBehavior = FlowBehavior.Return;
}
}
finally
{
logging = false;
}
}
}
Basically the ProgramState.State() method checks if the program is running(true),paused(loops while isPaused == true), stopped(false), this should control the if methods can run or not(basically a start pause/resume stop thing)
But sometimes i get nullreferences when returning from the method.
i am interested in knowing how can i set the return type to the default return type of the method.
It is tested with PostSharp 6.0.29
Before use it please check required null controls.
If method is async Task
public override void OnException(MethodExecutionArgs args)
{
var methodReturnType = ((System.Reflection.MethodInfo)args.Method).ReturnType;
var runtime = methodReturnType.GetRuntimeFields().FirstOrDefault(f => f.Name.Equals("m_result"));
//Only if return type has parameterless constructture (should be check before create)
var returnValue = Activator.CreateInstance(runtime.FieldType);
args.ReturnValue = returnValue;
}
And if method is not async
public override void OnException(MethodExecutionArgs args)
{
var methodReturnType = ((System.Reflection.MethodInfo)args.Method).ReturnType;
//Only if return type has parameterless constructture (should be check before create)
var returnValue = Activator.CreateInstance(methodReturnType);
args.ReturnValue = returnValue;
}
You can make your aspect class generic with the generic parameter representing the method return type. Then you need to create a method-level attribute that is also an aspect provider. The attribute will be applied to the user code and in turn it can provide the correct instance of the generic aspect.
[Serializable]
[MulticastAttributeUsage( MulticastTargets.Method )]
public class FlowControllerAttribute : MethodLevelAspect, IAspectProvider
{
public IEnumerable<AspectInstance> ProvideAspects(object targetElement)
{
MethodInfo method = (MethodInfo) targetElement;
Type returnType = method.ReturnType == typeof(void)
? typeof(object)
: method.ReturnType;
IAspect aspect = (IAspect) Activator.CreateInstance(typeof(FlowControllerAspect<>).MakeGenericType(returnType));
yield return new AspectInstance(targetElement, aspect);
}
}
[Serializable]
public class FlowControllerAspect<T> : IOnMethodBoundaryAspect
{
public void RuntimeInitialize(MethodBase method)
{
}
public void OnEntry(MethodExecutionArgs args)
{
args.ReturnValue = default(T);
args.FlowBehavior = FlowBehavior.Return;
}
public void OnExit(MethodExecutionArgs args)
{
}
public void OnSuccess(MethodExecutionArgs args)
{
}
public void OnException(MethodExecutionArgs args)
{
}
}
// Usage:
[FlowController]
public int Method()
{
// ...
}