Bukkit - How to reload custom yaml-configuration - yaml

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()

Related

Usage of exceptionExpression in Spring Retry

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;
}
}

How to fix 'No way to dispatch this command to Redis Cluster because keys have different slots' in Spring

I need to use Redis Cluster in Spring. But I'm getting the following error when I use mget or del on a list of keys: 'No way to dispatch this command to Redis Cluster because keys have different slots'. Showing a part of my Component code using JedisCluster.
It works when I use single key operations but not with multiple keys.
/* Component Code */
public class RedisServiceManager {
#Value("${redis.hosts}")
String hosts;
#Autowired
JedisPoolConfig jedisPoolConfig;
private JedisCluster jedisCluster;
#PostConstruct
private void init() {
List<String> redisHosts = Arrays.asList(hosts.split(","));
Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
redisHosts.forEach(redisHost -> {
jedisClusterNode.add(new HostAndPort(redisHost, 6379));
});
jedisCluster = new JedisCluster(jedisClusterNode, jedisPoolConfig);
}
// This works
public String getValueForKey(String key) {
try {
return jedisCluster.get(key);
} catch (Exception e) {
return null;
}
}
// This works
public void delKey(String cacheKey) {
try {
jedisCluster.del(cacheKey);
} catch (Exception e) {
}
}
// This doesn't work
public List<String> getValuesForAllKeys(String... keys) {
try {
return jedisCluster.mget(keys);
} catch (Exception e) {
return new ArrayList<>();
}
}
// This doesn't work
public void delAllKeys(String... keys) {
try {
jedisCluster.del(keys);
} catch (Exception e) {
}
}
}
Can someone help with this?
This is not a bug or an issue, but is the way how redis cluster works. You can find more details in the cluster documentation. But don't worry: there is a "trick": you can use hash as described here

Freemarker removeIntrospectionInfo does not work with DCEVM after model hotswap

I am using Freemarker and DCEVM+HotSwapManager agent. This basically allows me to hotswap classes even when adding/removing methods.
Everything works like charm until Freemarker uses hotswapped class as model. It's throwing freemarker.ext.beans.InvalidPropertyException: No such bean property on me even though reflection shows that the method is there (checked during debug session).
I am using
final Method clearInfoMethod = beanWrapper.getClass().getDeclaredMethod("removeIntrospectionInfo", Class.class);
clearInfoMethod.setAccessible(true);
clearInfoMethod.invoke(clazz);
to clear the cache, but it does not work. I even tried to obtain classCache member field and clear it using reflection but it does not work too.
What am I doing wrong?
I just need to force freemarker to throw away any introspection on model class/classes he has already obtained.
Is there any way?
UPDATE
Example code
Application.java
// Application.java
public class Application
{
public static final String TEMPLATE_PATH = "TemplatePath";
public static final String DEFAULT_TEMPLATE_PATH = "./";
private static Application INSTANCE;
private Configuration freemarkerConfiguration;
private BeansWrapper beanWrapper;
public static void main(String[] args)
{
final Application application = new Application();
INSTANCE = application;
try
{
application.run(args);
}
catch (InterruptedException e)
{
System.out.println("Exiting");
}
catch (IOException e)
{
System.out.println("IO Error");
e.printStackTrace();
}
}
public Configuration getFreemarkerConfiguration()
{
return freemarkerConfiguration;
}
public static Application getInstance()
{
return INSTANCE;
}
private void run(String[] args) throws InterruptedException, IOException
{
final String templatePath = System.getProperty(TEMPLATE_PATH) != null
? System.getProperty(TEMPLATE_PATH)
: DEFAULT_TEMPLATE_PATH;
final Configuration configuration = new Configuration();
freemarkerConfiguration = configuration;
beanWrapper = new BeansWrapper();
beanWrapper.setUseCache(false);
configuration.setObjectWrapper(beanWrapper);
try
{
final File templateDir = new File(templatePath);
configuration.setTemplateLoader(new FileTemplateLoader(templateDir));
}
catch (IOException e)
{
throw new RuntimeException(e);
}
final RunnerImpl runner = new RunnerImpl();
try
{
runner.run(args);
}
catch (RuntimeException e)
{
e.printStackTrace();
}
}
public BeansWrapper getBeanWrapper()
{
return beanWrapper;
}
}
RunnerImpl.java
// RunnerImpl.java
public class RunnerImpl implements Runner
{
#Override
public void run(String[] args) throws InterruptedException
{
long counter = 0;
while(true)
{
++counter;
System.out.printf("Run %d\n", counter);
// Application.getInstance().getFreemarkerConfiguration().setObjectWrapper(new BeansWrapper());
Application.getInstance().getBeanWrapper().clearClassIntrospecitonCache();
final Worker worker = new Worker();
worker.doWork();
Thread.sleep(1000);
}
}
Worker.java
// Worker.java
public class Worker
{
void doWork()
{
final Application application = Application.getInstance();
final Configuration freemarkerConfiguration = application.getFreemarkerConfiguration();
try
{
final Template template = freemarkerConfiguration.getTemplate("test.ftl");
final Model model = new Model();
final PrintWriter printWriter = new PrintWriter(System.out);
printObjectInto(model);
System.out.println("-----TEMPLATE MACRO PROCESSING-----");
template.process(model, printWriter);
System.out.println();
System.out.println("-----END OF PROCESSING------");
System.out.println();
}
catch (IOException e)
{
e.printStackTrace();
}
catch (TemplateException e)
{
e.printStackTrace();
}
}
private void printObjectInto(Object o)
{
final Class<?> aClass = o.getClass();
final Method[] methods = aClass.getDeclaredMethods();
for (final Method method : methods)
{
System.out.println(String.format("Method name: %s, public: %s", method.getName(), Modifier.isPublic(method.getModifiers())));
}
}
}
Model.java
// Model.java
public class Model
{
public String getMessage()
{
return "Hello";
}
public String getAnotherMessage()
{
return "Hello World!";
}
}
This example does not work at all. Even changing BeansWrapper during runtime won't have any effect.
BeansWrapper (and DefaultObjectWrapper's, etc.) introspection cache relies on java.beans.Introspector.getBeanInfo(aClass), not on reflection. (That's because it treats objects as JavaBeans.) java.beans.Introspector has its own internal cache, so it can return stale information, and in that case BeansWrapper will just recreate its own class introspection data based on that stale information. As of java.beans.Introspector's caching, it's in fact correct, as it builds on the assumption that classes in Java are immutable. If something breaks that basic rule, it should ensure that java.beans.Introspector's cache is cleared (and many other caches...), or else it's not just FreeMarker that will break. At JRebel for example they made a lot of effort to clear all kind of caches. I guess DCEVM doesn't have the resources for that. So then, it seems you have to call Introspector.flushCaches() yourself.
Update: For a while (Java 7, maybe 6) java.beans.Introspector has one cache per thread group, so you have call flushCaches() from all thread groups. And this all is actually implementation detail that, in principle, can change any time. And sadly, the JavaDoc of Introspector.flushCaches() doesn't warn you...

how do i import my jar file in drjava?

i have already added the file into extra class path but when i try to call the method "boolean ImageLibary.loadImage(File f)" i get error cannot find symbol..
public class ImageEditing
{
public static void main(String[]args) throws Exception
{
File image = getfile();
boolean i= ImageLibary.loadImage(image); // error occurs here
}
public static File getfile()
{
JFileChooser chooser = new JFileChooser();
try
{
String currentDir = System.getProperty("user.dir");
chooser.setCurrentDirectory(new File(currentDir));
}
catch (Exception e)
{
System.out.println("Warning: Error setting current folder: "+e.getMessage());
}
File file = null;
int returnValue;
returnValue = chooser.showOpenDialog(null);
if(returnValue==JFileChooser.APPROVE_OPTION)
{
file = chooser.getSelectedFile();
}
return file;
}
}

obfuscation with proguard vs. java.lang.reflect.Proxy

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>;
}

Resources