I have a multi-project Java project set up using Gradle. Project B is dependent on Project A and imported via Gradle dependencies. Everything compiles just fine except for one class in Project B that extends an abstract class in Project A. This abstract class implements an interface in a library that I'm using. When I go to build the project I get the following error:
ExNihiloAELootTables is not abstract and does not override abstract method getName() in DataProvider (edited)
The abstract super class implements the method in question and so it appears that the abstract super class is being ignored/skipped over. While debugging, I copied the abstract class into the same package as the instance class, which fixed the issue, however other classes from Project A are being used and recognized in Project B without being copied over, so this is not a good fix.
I am at a loss at this point about what the issue may be. Any help would be appreciated.
Here is the project structure:
Main Project
|
+-- build.gradle
+-- settings.gradle
|
+-- Project A
| |
| +-- build.gradle
| \-- Abstract Class
|
+-- Project B
| |
| +-- build.gradle
| \-- Instance Class that extends Abstract Class from A
Project repo and branch for reference: https://github.com/NovaMachina-Mods/ExNihiloSequentia/tree/sub-project-refactoring
(This branch is in the middle of a major refactor and so its pretty ugly right now.)
Edit:
Abstract Class from Project A
package novamachina.exnihilosequentia.datagen.api;
import com.google.common.collect.Multimap;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.mojang.logging.LogUtils;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.data.CachedOutput;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.DataProvider;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.storage.loot.LootPool;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.LootTables;
import net.minecraft.world.level.storage.loot.ValidationContext;
import net.minecraft.world.level.storage.loot.entries.LootItem;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition;
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
import net.minecraftforge.registries.ForgeRegistries;
import novamachina.exnihilosequentia.common.utility.ExNihiloLogger;
public abstract class AbstractLootTableGenerator implements DataProvider {
#Nonnull
private static final Gson GSON =
(new GsonBuilder()).setPrettyPrinting().disableHtmlEscaping().create();
#Nonnull private static final ExNihiloLogger logger = new ExNihiloLogger(LogUtils.getLogger());
#Nonnull protected final Map<ResourceLocation, LootTable> lootTables = new HashMap<>();
#Nonnull private final DataGenerator generator;
#Nonnull private final String modId;
protected AbstractLootTableGenerator(
#Nonnull final DataGenerator generator, #Nonnull final String modId) {
this.generator = generator;
this.modId = modId;
}
#Override
public void run(#Nonnull final CachedOutput cache) {
lootTables.clear();
#Nonnull final Path outFolder = generator.getOutputFolder();
createLootTables();
#Nonnull
final ValidationContext validator =
new ValidationContext(LootContextParamSets.ALL_PARAMS, function -> null, lootTables::get);
lootTables.forEach((name, table) -> LootTables.validate(validator, name, table));
#Nonnull final Multimap<String, String> problems = validator.getProblems();
if (!problems.isEmpty()) {
problems.forEach(
(name, table) -> logger.warn("Found validation problem in " + name + ": " + table));
throw new IllegalStateException("Failed to validate loot tables, see logs");
} else {
lootTables.forEach(
(name, table) -> {
#Nonnull final Path out = getPath(outFolder, name);
try {
DataProvider.saveStable(cache, LootTables.serialize(table), out);
} catch (IOException e) {
logger.error("Couldn't save loot table " + out);
logger.error(Arrays.toString(e.getStackTrace()));
}
});
}
}
#Nonnull
#Override
public String getName() {
return "Loot Tables: " + modId;
}
#Nonnull
protected LootPool.Builder createLootPoolBuilder() {
return LootPool.lootPool().when(ExplosionCondition.survivesExplosion());
}
protected abstract void createLootTables();
protected void register(#Nonnull final Block block, #Nonnull final LootPool.Builder... pools) {
#Nonnull final LootTable.Builder builder = LootTable.lootTable();
for (#Nonnull final LootPool.Builder pool : pools) {
builder.withPool(pool);
}
register(block, builder);
}
protected void registerSelfDrop(#Nonnull final Block block) {
register(block, singleItem(block));
}
#Nonnull
private Path getPath(#Nonnull final Path outFolder, #Nonnull final ResourceLocation name) {
return outFolder.resolve(
"data/" + name.getNamespace() + "/loot_tables/" + name.getPath() + ".json");
}
private void register(#Nonnull final Block block, #Nonnull final LootTable.Builder table) {
#Nullable final ResourceLocation resourceLocation = ForgeRegistries.BLOCKS.getKey(block);
if (resourceLocation == null) {
return;
}
register(resourceLocation, table);
}
private void register(
#Nonnull final ResourceLocation registryName, #Nonnull final LootTable.Builder table) {
if (lootTables.put(
toTableLoc(registryName), table.setParamSet(LootContextParamSets.BLOCK).build())
!= null) {
throw new IllegalStateException("Duplicate loot table: " + table);
}
}
#Nonnull
private LootPool.Builder singleItem(#Nonnull final ItemLike in) {
return createLootPoolBuilder()
.setRolls(ConstantValue.exactly(1))
.add(LootItem.lootTableItem(in));
}
#Nonnull
private ResourceLocation toTableLoc(#Nonnull final ResourceLocation registryName) {
return new ResourceLocation(registryName.getNamespace(), "blocks/" + registryName.getPath());
}
}
Implemented Class from Project B:
package novamachina.exnihiloae.datagen.common;
import net.minecraft.data.DataGenerator;
import novamachina.exnihiloae.common.init.ExNihiloAEBlocks;
import novamachina.exnihiloae.common.utility.ExNihiloAEConstants;
import novamachina.exnihilosequentia.datagen.api.AbstractLootTableGenerator;
public class ExNihiloAELootTables extends AbstractLootTableGenerator {
public ExNihiloAELootTables(DataGenerator generator) {
super(generator, ExNihiloAEConstants.ModIds.EX_NIHILO_AE);
}
#Override
protected void createLootTables() {
registerSelfDrop(ExNihiloAEBlocks.CRUSHED_SKYSTONE.get());
}
}
Related
So, I've been trying to make a small PluginLoader in my library which allows you to load JAR files into a custom ModuleLayer and use the layer to load services using ServiceLoader.load(ModuleLayer, Class<?>).
However, when calling ServiceProvider.load, it internally uses Reflection.getCallerClass to get the, duhh, class calling the code, so it can load the services from it's module.
PluginLoader.java
package com.wexalian.common.plugin;
import com.wexalian.common.collection.wrapper.StreamWrapper;
import com.wexalian.nullability.annotations.Nonnull;
import java.nio.file.Path;
import java.util.ServiceLoader;
import java.util.stream.Stream;
#FunctionalInterface
public interface PluginLoader<T extends IAbstractPlugin> extends StreamWrapper.Iterable<T> {
#Nonnull
#Override
Stream<T> get();
static void init(#Nonnull ServiceLoaderLayerFunction serviceLoaderFunc) {
PluginLoaderImpl.init(serviceLoaderFunc);
}
static void loadPlugins(#Nonnull Path path) {
PluginLoaderImpl.loadPlugins(path);
}
#Nonnull
static <T extends IAbstractPlugin> PluginLoader<T> load(#Nonnull Class<T> pluginClass) {
return load(pluginClass, null);
}
#Nonnull
static <T extends IAbstractPlugin> PluginLoader<T> load(#Nonnull Class<T> pluginClass, ServiceLoaderFallbackFunction fallbackServiceProvider) {
return PluginLoaderImpl.load(pluginClass, fallbackServiceProvider);
}
#FunctionalInterface
interface ServiceLoaderLayerFunction {
#Nonnull
<T> ServiceLoader<T> load(#Nonnull ModuleLayer layer, #Nonnull Class<T> clazz);
#Nonnull
default <T> Stream<T> stream(#Nonnull ModuleLayer layer, #Nonnull Class<T> clazz) {
return load(layer, clazz).stream().map(ServiceLoader.Provider::get);
}
}
#FunctionalInterface
interface ServiceLoaderFallbackFunction {
#Nonnull
<T> ServiceLoader<T> load(#Nonnull Class<T> clazz);
#Nonnull
default <T> Stream<T> stream(#Nonnull Class<T> clazz) {
return load(clazz).stream().map(ServiceLoader.Provider::get);
}
}
}
PluginLoaderImpl.java
package com.wexalian.common.plugin;
import com.wexalian.nullability.annotations.Nonnull;
import java.io.IOException;
import java.lang.module.Configuration;
import java.lang.module.ModuleFinder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
final class PluginLoaderImpl {
private static final Set<ModuleLayer> pluginLayerSet = new HashSet<>();
private static PluginLoader.ServiceLoaderLayerFunction serviceLoaderLayer;
private static ModuleLayer coreLayer;
private static ClassLoader coreLoader;
private static boolean init = false;
private PluginLoaderImpl() {}
static void init(#Nonnull PluginLoader.ServiceLoaderLayerFunction serviceLoaderFunc) {
if (!init) {
serviceLoaderLayer = serviceLoaderFunc;
Class<?> coreClass = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass();
coreLayer = coreClass.getModule().getLayer();
coreLoader = coreClass.getClassLoader();
if (coreLayer == null) {
throw new IllegalStateException("PluginLoaderImpl can only be initialized from a named module!");
}
else init = true;
}
else throw new IllegalStateException("PluginLoaderImpl can only be initialized once!");
}
static void loadPlugins(#Nonnull Path path) {
if (init) {
if (Files.exists(path)) {
try (Stream<Path> paths = Files.list(path)) {
ModuleFinder moduleFinder = ModuleFinder.of(paths.toArray(Path[]::new));
List<String> moduleNames = moduleFinder.findAll().stream().map(ref -> ref.descriptor().name()).toList();
Configuration configuration = coreLayer.configuration().resolveAndBind(moduleFinder, ModuleFinder.of(), moduleNames);
ModuleLayer pluginLayer = coreLayer.defineModulesWithOneLoader(configuration, coreLoader);
pluginLayerSet.add(pluginLayer);
}
catch (IOException e) {
throw new IllegalStateException("Error loading plugins from path " + path, e);
}
}
}
else throw new IllegalStateException("PluginLoaderImpl has to be initialized before you can load plugins!");
}
static <T extends IAbstractPlugin> PluginLoader<T> load(Class<T> clazz, PluginLoader.ServiceLoaderFallbackFunction serviceLoader) {
if (init) {
if (!pluginLayerSet.isEmpty()) {
return () -> pluginLayerSet.stream().flatMap(layer -> serviceLoaderLayer.stream(layer, clazz)).filter(IAbstractPlugin::isEnabled);
}
else {
return () -> serviceLoaderLayer.stream(coreLayer, clazz).filter(IAbstractPlugin::isEnabled);
}
}
else if (serviceLoader != null) {
return () -> serviceLoader.stream(clazz);
}
else throw new IllegalStateException("PluginLoaderImpl has to be initialized before you can load services from plugins!");
}
}
Now my problem is:
I am currently writing a program with some services, and using that library to load JAR files and load them. However, it recognizes the PluginLoader as the caller class, which "does not declare uses", because the library doesn't actually have the service I want.
I have found a work around, which is accepting a Function<ModuleLayer, Class<?>, ServiceProvider<?>, which redirects all the calls to the proper module, but I'd rather not do that everywhere I use my PluginLoader.
Other than this I wouldn't know any other solution, so maybe one of you knows.
Thanks in advance,
Wexalian
When using the ModuleLayer system, you also have to define the uses and provides in your and the various plugin module definitions.
Your module:
uses com.wexalian.common.plugin.IAbstractPlugin;
And in your plugin modules:
provides com.wexalian.common.plugin.IAbstractPlugin with some.plugin.PluginFactory;
See ServiceLoader and ServiceLoader.Provider, this is how the service loader in one module knows about loaders in other modules.
I m trying to use native modules on react native to get the device name and package installer from android , but I got always a null value ?
my code like the following
the result always is null !
// DeviceInfoModule.java
package com.example;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactMethod;
import android.annotation.SuppressLint;
import android.os.Build;
import android.content.Context;
import android.os.AsyncTask;
public class DeviceInfoModule extends ReactContextBaseJavaModule {
public DeviceInfoModule(ReactApplicationContext reactContext) {
super(reactContext);
}
#Override
public String getName() {
return "DeviceInfoModule";
}
#ReactMethod
public void getDeviceName(final Callback callback) {
getDeviceNameHandler(callback);
}
#ReactMethod
public void getDeviceInstaller(final Callback callback) {
getDevicePackager(callback);
}
private void getDeviceNameHandler(final Callback callback) {
#SuppressLint("StaticFieldLeak") AsyncTask<Void,Void,Void> myAsyncTask = new AsyncTask<Void,Void,Void>() {
#SuppressLint("StaticFieldLeak")
#Override
protected Void doInBackground(final Void ... params) {
String device_Name = Build.MANUFACTURER + "-" + Build.MODEL;
callback.invoke( device_Name, device_Name);
return null;
}
};
myAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void getDevicePackager(final Callback callback) {
#SuppressLint("StaticFieldLeak") AsyncTask<Void,Void,Void> myAsyncTask = new AsyncTask<Void,Void,Void>() {
#Override
protected Void doInBackground(final Void ... params) {
Context context = getReactApplicationContext();
String installer = context.getPackageManager().getInstallerPackageName(context.getPackageName());
callback.invoke(null, installer);
return null;
}
};
myAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
and packages are :
// ReactNativePackages.java
package com.example;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import com.carexs.DeviceInfoModule;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ReactNativePackages implements ReactPackage {
#Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
#Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new DeviceInfoModule(reactContext));
return modules;
}
}
in the Main Application class I added the package
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new CxsNativeUtilsPackage());
packages.add(new ReactNativePackages());
return packages;
}
in my app I have the following code
const DeviceInfo = NativeModules.DeviceInfoModule;
DeviceInfo.getDeviceInstaller((err: string, name: string) => {
if (err) {
console.log('error', err);
} else {
console.log('device name', name);
}
});
any help? , thank you.
I found a solution and maybe someone will need it someday , I found that react native methods that I have created is not working maybe because of some depraceted modules or other issue on the code I m not sure exactly , I have changed those methods to new version , for example:
#ReactMethod
getDevicePackager() // (renamed to getDeviceInstaller()) method in DeviceInfoModule is changed to the following
// will be
#ReactMethod(isBlockingSynchronousMethod = true)
public String getInstallerPackageNameSync() {
String packageName = getReactApplicationContext().getPackageName();
String installerPackageName = getReactApplicationContext().getPackageManager().getInstallerPackageName(packageName);
if (installerPackageName == null) {
return "unknown";
}
return installerPackageName;
}
#ReactMethod
public void getDeviceInstaller(Promise p) { p.resolve(getInstallerPackageNameSync()); }
Like spigot/bukkit plugins id like to be able to load jars in a file and load their classes. Ive managed to get this working with java class loader but the class has to extend a runnable for it to work. Id like to have my own custom interface to implement for each plugin(jar). So I could have functions that get ran on plugin load and so on. If anyone knows how to do this please let me know.
Plugin structure
A plugin is a .jar file. The plugin has a plugin.properties file with the properties of the plugin.
It looks like this:
plugin.main=com.example.plugins.ExamplePlugin
plugin.name=Example Plugin
plugin.description=Test 123
plugin.version=1.0
The file contains the main class, the plugin name, a description and the version.
A plugin must have a class that inherits from the abstract plugin class. This counts as the main class.
Code structure
Let's begin with the Plugin-class:
package com.example.plugins;
public abstract class Plugin {
protected PluginProperty property;
public abstract void onEnable();
public abstract void onDisable();
public PluginProperty getProperty() {
return property;
}
public void setProperty(PluginProperty property) {
this.property = property;
}
}
As you may see, I have chosen an abstract class here.
The class consists of two abstract methods (onEnable and onDisable). The plugin also has a PluginProperty object. The equivalent of this class in Spigot would be JavaPlugin.
Let's take a look at the PluginProperty class.
package com.example.plugins;
public class PluginProperty {
private String main;
private String name;
private String description;
private double version;
public PluginProperty(String main, String name, String description, double version) {
this.main = main;
this.name = name;
this.description = description;
this.version = version;
}
public String getMain() {
return main;
}
public void setMain(String main) {
this.main = main;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public double getVersion() {
return version;
}
public void setVersion(double version) {
this.version = version;
}
}
This class has all the necessary properties of a plugin in it. Most of the things here are self-explanatory, but I would still like to discuss main.
The string holds the name of the main plugin class of the plugin. This is basically the same as the main in the plugin.yml in Spigot.
The loader
Here is the PluginLoader-class:
package com.example.plugins;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Properties;
import java.util.zip.ZipException;
public class PluginLoader {
private static PluginProperty loadPluginProperties(File file) throws ZipException, IOException {
URL url = file.toURI().toURL();
String jarURL = "jar:" + url +"!/plugin.properties";
InputStream input;
URL inputURL = new URL(jarURL);
JarURLConnection conn = (JarURLConnection)inputURL.openConnection();
input = conn.getInputStream();
Properties property = new Properties();
property.load(input);
String main = property.getProperty("plugin.main");
String name = property.getProperty("plugin.name");
String description = property.getProperty("description");
double version = Double.parseDouble(property.getProperty("plugin.version"));
return new PluginProperty(main, name, description, version);
}
public static Plugin loadPlugin(File file) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
if(!file.exists()) {
return null;
}
PluginProperty property = loadPluginProperties(file);
URL url = file.toURI().toURL();
String jarURL = "jar:" + url + "!/";
URL urls[] = {new URL(jarURL)};
URLClassLoader ucl = new URLClassLoader(urls);
Plugin plugin = (Plugin) Class.forName(property.getMain(), true, ucl).getDeclaredConstructor().newInstance();
plugin.setProperty(property);
return plugin;
}
}
The private loadPluginProperties method loads the plugin properties and returns the required object. The loadPlugin method loads the main class specified in the properties into an object and returns it.
Examples
I just gave you the basic framework for the plugin system. But how should you use it? Let's start with an example loader.
Here is the Main-class:
package com.example.plugins;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static List<Plugin> plugins = new ArrayList<Plugin>();
public static void main(String[] args) {
File[] pluginFiles = new File("plugins").listFiles();
//Load plugins
for(File f : pluginFiles) {
if(f.isDirectory()) {
continue;
}
if(!f.getName().endsWith(".jar")) {
continue;
}
Plugin p = null;
try {
p = PluginLoader.loadPlugin(f);
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException | IOException e) {
System.err.println("Failed to load plugin!");
e.printStackTrace();
}
Main.plugins.add(p);
}
//Enable plugins
for(Plugin p : plugins) {
p.onEnable();
}
//Disable plugins
for(Plugin p : plugins) {
p.onDisable();
}
}
}
I won't go into much detail here as I think it's pretty self-explanatory. If you have a question, just ask me through the comments.
After exporting the previously written as a JAR, add it to the classpath in a new project. Don't forget to create a plugin.properties file.
This is an example plugin that is compatible with the .properties file specified above:
package com.example.plugins;
public class ExamplePlugin extends Plugin {
#Override
public void onEnable() {
System.out.println("Hello world!");
}
#Override
public void onDisable() {
}
}
When I export this plugin and put it in the plugins folder I get the following output:
Hello world!
The End
It would be recommended to use JSON or YAML, XML etc. instead of the built-in Java property files. This is the basic structure for plugins. Have fun!
My code is exactly the same as the example shown on py4j website:
Implementing Java Interfaces from Python
Except my classes are all in the same src.main.java package
(see below for the code)
Problem:
If I do a gradle fatjar build with ListenerApplication as main, then execute the jar, everything works fine. If I do a gradle fatjar build and instead access the code via a plugin interface, I get the following error:
Py4JError: An error occurred while calling o0.registerListener. Trace:
py4j.Py4JException: Invalid interface name: ExampleListener
at py4j.Protocol.getPythonProxy(Protocol.java:429)
at py4j.Protocol.getObject(Protocol.java:311)
at py4j.commands.AbstractCommand.getArguments(AbstractCommand.java:82)
at py4j.commands.CallCommand.execute(CallCommand.java:77)
at py4j.GatewayConnection.run(GatewayConnection.java:238)
at java.lang.Thread.run(Thread.java:748)
Question: Why does Py4J have problems finding "ExampleListener" when the .jar is run as a plugin and not as an application? I can even add :
public String classtest() throws Exception {
System.out.println("classtest called");
Class<?> py = Class.forName("ExampleListener");
return py.toString();
}
to the ListenerApplication, which will return the correct interface both when run as plugin and as application! The interesting thing is, if I run the program plus plugin from netbeans IDE, everything works fine! Does Netbeans somehow expose the interface, while the application run directly, does not?
Plugin interface
import org.micromanager.MenuPlugin;
import org.micromanager.Studio;
import org.scijava.plugin.Plugin;
import org.scijava.plugin.SciJavaPlugin;
import py4j.GatewayServer;
#Plugin(type = MenuPlugin.class)
public class Py4JPluginInterface implements MenuPlugin, SciJavaPlugin{
private static final String menuName = "Simpletest_gradle";
private static final String tooltipDescription = "py4j gateway";
private static final String version = "0.1";
private static final String copyright = "copyright";
#Override
public String getSubMenu() {
return "Simpletest_gradle";
}
#Override
public void onPluginSelected() {
GatewayServer gatewayServer = new GatewayServer(new ListenerApplication());
gatewayServer.start();
System.out.println("Gateway Started at IP:port = "+gatewayServer.getAddress()+":"+gatewayServer.getPort());
}
#Override
public void setContext(Studio app) {
}
#Override
public String getName() {
return menuName;
}
#Override
public String getHelpText() {
return tooltipDescription;
}
#Override
public String getVersion() {
return version;
}
#Override
public String getCopyright() {
return copyright;
}
}
The interface:
//py4j/examples/ExampleListener.java
package py4j.examples;
public interface ExampleListener {
Object notify(Object source);
}
The application:
package py4j.examples;
import py4j.GatewayServer;
import java.util.ArrayList;
import java.util.List;
public class ListenerApplication {
List<ExampleListener> listeners = new ArrayList<ExampleListener>();
public void registerListener(ExampleListener listener) {
listeners.add(listener);
}
public void notifyAllListeners() {
for (ExampleListener listener: listeners) {
Object returnValue = listener.notify(this);
System.out.println(returnValue);
}
}
#Override
public String toString() {
return "<ListenerApplication> instance";
}
public static void main(String[] args) {
ListenerApplication application = new ListenerApplication();
GatewayServer server = new GatewayServer(application);
server.start(true);
}
}
The python listener
from py4j.java_gateway import JavaGateway, CallbackServerParameters
class PythonListener(object):
def __init__(self, gateway):
self.gateway = gateway
def notify(self, obj):
print("Notified by Java")
print(obj)
gateway.jvm.System.out.println("Hello from python!")
return "A Return Value"
class Java:
implements = ["py4j.examples.ExampleListener"]
if __name__ == "__main__":
gateway = JavaGateway(
callback_server_parameters=CallbackServerParameters())
listener = PythonListener(gateway)
gateway.entry_point.registerListener(listener)
gateway.entry_point.notifyAllListeners()
gateway.shutdown()
For those who are interested, this was a class loader issue, which is apparently common for plugin/OSGI apps.
See the maintainer's response:
https://github.com/bartdag/py4j/issues/339#issuecomment-473655738
I simply added the following to the Java-side ListenerApplication constructor:
RootClassLoadingStrategy rmmClassLoader = new RootClassLoadingStrategy();
ReflectionUtil.setClassLoadingStrategy(rmmClassLoader);
I'm doing an android bridge to native react,
of the following module: https://github.com/PierfrancescoSoffritti/android-youtube-player/
but from the following errors:
warning: Cannot find annotation method 'value()' in type 'OnLifecycleEvent': class file for android.arch.lifecycle.OnLifecycleEvent not found
warning: unknown enum constant Event.ON_DESTROY reason: class file for android.arch.lifecycle.Lifecycle not found
warning: Cannot find annotation method 'value()' in type 'OnLifecycleEvent'
warning: unknown enum constant Event.ON_STOP
error: cannot access LifecycleObserver
youtubePlayerView.initialize(new YouTubePlayerInitListener() {
^
class file for android.arch.lifecycle.LifecycleObserver not found
This is the source code I'm using, I can not extend AppCompatActivity to use
getLifecycle().addObserver(youtubePlayerView);
package com.uiyoutube;
import android.support.annotation.NonNull;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;
import com.pierfrancescosoffritti.androidyoutubeplayer.player.YouTubePlayer;
import com.pierfrancescosoffritti.androidyoutubeplayer.player.YouTubePlayerView;
import com.pierfrancescosoffritti.androidyoutubeplayer.player.listeners.AbstractYouTubePlayerListener;
import com.pierfrancescosoffritti.androidyoutubeplayer.player.listeners.YouTubePlayerInitListener;
public class RNMaterialLetterIcon extends ViewGroupManager<ViewGroup> {
public static final String REACT_CLASS = "RNMaterialLetterIcon";
#Override
public String getName() {
return REACT_CLASS;
}
#Override
protected FrameLayout createViewInstance(final ThemedReactContext reactContext) {
//int randomId;
//Random rand = new Random();
//while (reactContext.getCurrentActivity().findViewById(randomId = rand.nextInt(Integer.MAX_VALUE) + 1) != null)
;
//final int viewId = randomId;
YouTubePlayerView youtubePlayerView = new YouTubePlayerView(reactContext);
//youtubePlayerView.getPlayerUIController().showVideoTitle(true);
//youtubePlayerView.getPlayerUIController().showYouTubeButton(false);
//youtubePlayerView.getPlayerUIController().showFullscreenButton(false);
//AppCompatActivity.getLifecycle().addObserver(youtubePlayerView);
youtubePlayerView.initialize(new YouTubePlayerInitListener() {
String videoId = "vs0-ix6UhwU";
#Override
public void onInitSuccess(#NonNull final YouTubePlayer initializedYouTubePlayer) {
initializedYouTubePlayer.addListener(new AbstractYouTubePlayerListener() {
#Override
public void onReady() {
initializedYouTubePlayer.loadVideo(videoId, 660);
}
#Override
public void onCurrentSecond(float second) {
if (second == 680) initializedYouTubePlayer.seekTo(660);
}
});
}
}, true);
final FrameLayout frameLayout = new FrameLayout(reactContext);
frameLayout.addView(youtubePlayerView);
return frameLayout;
}
}
How can I solve?
You probably need to import the lifecycle library or appcompat.
It is complaining because it doesn't know stuff from the lifecycle lib.