Can my #InitiatingFlow be a Generic class? - corda

Is it possible to define an #InitiatingFlow as a generic class?:
#InitiatingFlow
public class FungibleStateIssueSend<T extends Oil> extends FlowLogic<SignedTransaction> {...}
...If I try I get a compile error on the #InitiatedBy annotation in the receiving flow:
#InitiatedBy(FungibleStateIssueSend.class)
public class FungibleStateIssueReceive extends FlowLogic<Void> {...}
Error:
Incompatible types. Found: 'java.lang.Class<com.template.flows.FungibleStateIssueSend>', required: 'java.lang.Class<? extends net.corda.core.flows.FlowLogic<?>>'
Here is how I was going to use it:
// Define the delivery unit (bbl, m3 or tonne) and corresponding API of the oil in that unit:
WtiBbl wtiBbl = new WtiBbl(new BigDecimal("4.5"));
// Set Amount of units for delivery:
Amount<WtiBbl> amountWtiBbl = new Amount<>(1_000L, wtiBbl);
// Create a FungibleState so the delivery can be divided between different shippers:
OilFungibleState<WtiBbl> juneProduction = new OilFungibleState<>(Month.JUNE, Collections.singletonList(partyA), amountWtiBbl);
// Issue the FungibleState onto the ledger...
FungibleStateIssueSend<WtiBbl> fungibleStateIssueSend = new FungibleStateIssueSend<>(juneProduction);
CordaFuture<SignedTransaction> future = partyANode.startFlow(fungibleStateIssueSend);
mockNetwork.runNetwork();
SignedTransaction signedTransaction = future.get();
I can work around the limitation. I just want to understand if this is a Corda thing or a Java thing?

It feels like its more of a Java thing.
For example, if I use a wild card for the class type it works fine with generic typed parameter.
#Target(ElementType.TYPE_USE)
#Retention(RetentionPolicy.RUNTIME)
#interface TestAnnotation{
public Class<?> value();
}
So I can use the annotation #TestAnnotation(Initiator.class) in a class, while Initiator could be defined as:
public static class Initiator<T> extends FlowLogic<SignedTransaction>
But when we bound the wildcard to something below, the compiler complains.
#Target(ElementType.TYPE_USE)
#Retention(RetentionPolicy.RUNTIME)
#interface TestAnnotation{
public Class<? extends FlowLogic<?>> value();
}
But if we remove the wildcard from the FlowLogic it works. Not sure why the compiler complains because of that wildcard.

Related

Xamarin Android binding does not implement interface issue

I've a java binding for android which somewhat works bar the new feature I'm trying to integrate with. Only now I have realised that the intended callback is not happening. Here are the classes (decompiled to java) in question:
At the top level we have
public interface MyPackage {
MyPackage.Companion Companion = MyPackage.Companion.$$INSTANCE;
public static final class Companion {
#Nullable
private static MyEventHandler myEventHandler;
// $FF: synthetic field
static final MyPackage.Companion $$INSTANCE;
#Nullable
public final MyEventHandler getMyEventHandler() {
return myEventHandler;
}
public final void setMyEventHandler(#Nullable MyEventHandler var1) {
myEventHandler = var1;
}
private Companion() {
}
static {
MyPackage.Companion var0 = new MyPackage.Companion();
$$INSTANCE = var0;
}
}
}
MyEventHandler class:
public abstract class MyEventHandler {
public abstract void handleEvent(#NotNull String var1, #NotNull Properties var2);
}
Properties class:
import java.util.Map;
public class Properties extends r {
public Properties() {
}
Properties(Map<String, Object> var1) {
super(var1);
}
public Properties a(String var1, Object var2) {
super.b(var1, var2);
return this;
}
}
and the problematic r class:
public class r implements Map<String, Object> {
private final Map<String, Object> a;
various implementations...
}
So I noticed the issue when I couldnt override the HandleEvent method at the integration level and started looking at the Binding logs and found:
Warning=>
BINDINGSGENERATOR: Warning BG8801: Invalid parameter type MyPackage...Properties in method HandleEvent in managed type MyPackage.MyEventHandler. (BG8801)
And in build logs:
message BG0000: warning BG8102: Class MyPackage....Properties has unknown base type MyPackage....r.
warning BG8801: Invalid parameter type MyPackage...Properties in method HandleEvent in managed type MyPackage.MyEventHandler.
As it was obvious r is an obfuscated class I need to make chagnes to my Metadata so I went ahead and popped in:
<attr path="/api/package[#name='MyPackage']/class[#name='r']" name="obfuscated">false</attr>
Which resulted in the R being generated but now I get the 5 following compile error:
Error CS0535: 'R' does not implement interface member 'IMap.EntrySet()' (CS0535)
Error CS0738: 'R' does not implement interface member 'IMap.KeySet()'. 'R.KeySet()' cannot implement 'IMap.KeySet()' because it does not have the matching return type of 'ICollection'. (CS0738)
Error CS0535: 'R' does not implement interface member 'IMap.Put(Object?, Object?)' (CS0535)
Error CS0535: 'R' does not implement interface member 'IMap.PutAll(IDictionary?)' (CS0535)
Error CS0738: 'R' does not implement interface member 'IMap.Values()'. 'R.Values()' cannot implement 'IMap.Values()' because it does not have the matching return type of 'ICollection'. (CS0738)
I tried to make a managed return using
<attr path="/api/package[#name='MyPackage']/class[#name='r']/method[#name='entrySet' and count(parameter)=0]" name="managedReturn">Java.Util.IMap</attr>
With same number of compile error as above. Then I tried removing the node using:
<remove-node path="/api/package[#name='MyPackage']/class[#name='r']/method[#name='entrySet']"/>
Still no luck. :(
What am I missing here? Any pointers/suggestions will be appreciated!
It seems like you are trying to expose a Map to C# and as you stated, Java Generics are not handled very well.
In a very popular social network you received an answer from #mattleibow. I do not take credit for his answer but I went to check nonetheless and it seems fine.
If you look at the description of the Java.Lang.HashMap type
https://learn.microsoft.com/en-us/dotnet/api/java.util.hashmap?view=xamarin-android-sdk-9 it's a good candidate for you to expose.
You can also try with the corresponding interface for better safety https://learn.microsoft.com/en-us/dotnet/api/java.util.imap?view=xamarin-android-sdk-9
If it works you will still have to cast the types yourself.
Please answer to the comment to say that problem is solved for the sake of future generations arriving on this post :D
Credit is not mine so don't give it to me :-)
John,
I got arround fixing it by providing implementation of the the said methods in a partial class. Basically added a new file called R.cs under Additions folder as follows:
namespace YourNameSpace
{
public partial class R
{
public void PutAll(System.Collections.IDictionary p0)
{
PutAll(p0);
}
public Java.Lang.Object Put(Java.Lang.Object key, Java.Lang.Object value)
{
return Put(key, value);
}
public System.Collections.ICollection EntrySet()
{
return EntrySet();
}
public System.Collections.ICollection KeySet()
{
return KeySet();
}
public System.Collections.ICollection Values()
{
return Values();
}
}
}
I couldn't get it to work by adding XML transformation, but I think there was some tooling issue.

Mapper decorator not getting compiled

Mapper decorator for my mapper is not getting compiled.Mapper is getting compiled, but not the decorator. Because, during the build I'm getting the type conversion error, even though I'm doing it in the mapper decorator. Is there anything more to add?
mapper code:
#Mapper
#DecoratedWith(OneMapperDecorator.class)
public interface OneMapper {
public TwoObject convertToTwoObject(OneObject one);
}
decorator code:
public abstract class OneMapperDecorator implements OneMapper {
private final OneMapper delegate;
public OneMapperDecorator (OneMapper delegate) {
this.delegate = delegate;
}
#Override
public TwoObject convertToTwoObject(OneObject one)
{
TwoObject two=delegate.convertToTwoObject(one);
two.setTotalFare(new BigDecimal(one.getPrice()));//string to bigdecimal conversion
return two;
}
}
The decorator is meant to augment the mapping not to replace it. MapStruct has no way of knowing that you are mapping your totalFare in the decorator. You have 2 options:
Define a custom mapping method
In your OneMapper you can add a default method that would perform the mapping (as the error says.
#Mapper
#DecoratedWith(OneMapperDecorator.class)
public interface OneMapper {
#Mapping(target = "totalFare", source = "price");
TwoObject convertToTwoObject(OneObject one);
default BigDecimal map(String value) {
return value == null ? null : new BigDecimal(value);
}
}
Ignore the mapping
In case you want to do the mapping in your decorator then you need to tell MapStruct to not map it.
#Mapper
#DecoratedWith(OneMapperDecorator.class)
public interface OneMapper {
#Mapping(target = "totalFare", ignore = true);
TwoObject convertToTwoObject(OneObject one);
}
One advise from me if you are using your delegate only to map extra fields I would either add custom methods or use #AfterMapping and #BeforeMapping to handle that.

Minecraft modding block constructer error

I'm making a mod, and I am getting an error(no duh) and I have tried searching it up but I want an answer specific to my problem because I am not very good at this. I am getting this error in my block class.
Implicit super constructor Block() is undefined for default constructor. Must define an explicit constructor
and I don't know how to fix it. Please Help its for a project.
block class:
package GDMCrocknrollkid.fandomcraft;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
public class BlockCbBlock extends Block {
protected BlockCbBlock(Material material) {
super(material);
}
}
mod class:
package GDMCrocknrollkid.fandomcraft;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.GameRegistry;
#Mod(modid = "fc", name = "Fandomcraft", version = "1.0")
public class fandomcraft {
public static Item itemCbIngot;
public static Block blockCbBlock;
#EventHandler
public void preInit(FMLPreInitializationEvent event){
//Item/Block initialization and registering
//Config Handling
itemCbIngot = new ItemCbIngot().setUnlocalizedName("ItemCbIngot").setTextureName("fc:itemCbIngot"); //item.itemCbIngot.name
blockCbBlock = new BlockCbBlock(Material.iron);
GameRegistry.registerItem(itemCbIngot, itemCbIngot.getUnlocalizedName().substring(5));
}
#EventHandler
public void init(FMLInitializationEvent event){
//Proxy, TileEntity, entity, GUI and Packet Registering
}
#EventHandler
public void postInit(FMLPostInitializationEvent event) {
}
}
This error pertains to all of java, not just minecraft forge. Check this for some more reference. There are a couple possible reasons for this error. It is most likely 1, but 2 and 3 can be a contributing factor to the error.
Your BlockCbBlock Class declares a constructor that is not the default, no-argument constructor that the compiler would otherwise provide (that is, if the Block class doesn't have a constructor) and, if in fact the Block class is using the default constructor, then you can't call super() on the arguements because the Block class uses a constructor with no arguments. Because of this, if you wanted to modify the Block constructor, it would be safier and easier to create a custom construcotr inside of the BlockCbBlock class itself.
You are trying to inherit the constructor of Block, but you have declared it as protected, when the constructor in your class should be public to match the inherited .
If you're using Eclipse, it can give this error when you have your project setup incorrectly (system configuration mismatch)
Probably not directly realted to this specific error, but a possible cause of other errors in the near future; you are using the annotation #EventHandler, but you have not actually declared the forge event handler.
You don't actually register the block for some reason. Even if you're using the block as a recipe item, you still need to register it
To fix potential problems 1, 2, and 4, try this (obtained from here):
package GDMCrocknrollkid.fandomcraft;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
private final String name = "BlockCbBlock";
public class BlockCbBlock extends Block {
public BlockCbBlock() {
super(Material.iron);
GameRegistry.registerBlock(this, name);
setUnlocalizedName(Reference.MODID + "_" + name);
setCreativeTab(CreativeTabs.tabBlock);
}
public String getName() {
return name;
}
}
This way, you'll declare its UnlocalizedName, Material, and CreativeTab ahead of time. This method might be unnecessary, but its a good precaution to help prevent the error. Now, all you have to do is declare it like this:
//You need to make your own EventHandler class. Search online for that.
FCEventHandler handler = new FCEventHandler();
#EventHandler
public void preInit(FMLPreInitializationEvent event){
//Config Handling
//event handler registry
FMLCommonHandler.instance().bus().register(handler);
MinecraftForge.EVENT_BUS.register(handler);
//the same thing can be similarly done with this if you wish
itemCbIngot = new ItemCbIngot().setUnlocalizedName("ItemCbIngot").setTextureName("fc:itemCbIngot");
blockCbBlock = new BlockCbBlock();
GameRegistry.registerItem(itemCbIngot, itemCbIngot.getUnlocalizedName().substring(5));
}

Finding all Classes beloning to a superclass using Java 7

I'm looking for a way in java to find all classes that belongs to a certain superclass, and within that class refer to a static string with a known name (using reflection?);
public class Bar
extends ObjectInstance
{
public final static String Name = "Foo";
// more
}
From the example; there are n-occurences of classes that extend from ObjectInstance, and from all, i need the value of Name. The classes i am refering to are generated so i know for sure that there is a Name element that i can refer to, but i have no access to the generation sourcedata.
Perhaps the same question as How do you find all subclasses of a given class in Java?
This backs up my initial feeling that this can only be done like IDEs do it: by scanning everything down the tree, building your relationships as you go.
No Way.
One failing solution:
publc abstract class ObjectInstance {
public abstring String name();
private static Map<String, Class<? extends ObjectInstance> klasses =
new HashMap<>();
protected ObjectInstance() {
classes.put(name(), getClass());
}
Only collects instantiated classes! So fails.
With the idea to have the name provided by a function with return "foo";.
Collecting the class.
There are two unsatisfactory solutions:
The other way around: use the name as a factory pattern:
enum Name {
FOO(Bar.class),
BAZ(Baz.class),
QUX(Qux.class),
BAR(Foo.class);
public final Class<ObjectInstance> klass;
private Name(Class<ObjectInstance> klass) {
this.klass = klass;
}
}
Maybe as factory to create instances too.
Using a class annotation, and have a compile time scanning:
#ObjectInstanceName("foo")
public class Bar extends ObjectInstance {
}
How to apply this to your case: experiment.
There would be a more fitting solution of using your own ClassLoader, but that is far too over-engineered.

CDI Injection Within A Constructor

I've got my application setup to use CDI and all is going well. Now I'm creating a new bean that extends a class from a 3rd party library. I attempted to create something like the below example:
#Named("myNewClass")
#ConversationScoped
public class MyNewClass extends ThirdPartyClass {
#Inject
private ApplicationConfig applicationConfig;
#Override
public void doStuff() {
// In this code, applicationConfig will be null.
}
}
When doStuff is called, applicationConfig was always null. I added a no args constructor & a method tagged with #PostConstruct to try and see what was going on. The constructor gets called then the doStuff method. As doStuff is being called at construction time I cannot use the #Inject annotations at this point.
So my question is how do I get a hold of applicationConfig at this point?
I've been tinkering with BeanManager (this is in a function I call with ApplicationConfig.class as a parameter):
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
BeanManager beanManager = (BeanManager) envCtx.lookup("BeanManager");
Bean myBean = beanManager.getBeans(clazz).iterator().next();
return beanManager.getReference(myBean, clazz, beanManager.createCreationalContext(myBean));
Which works but it's creating a new ApplicationConfig instance. I want to get the one that I know already exists on my ConversationScope.
A little info: I'm using Seam 3.0, Weld Servlet 1.1.1 and this is running on Tomcat 6.
You can annotate a constructor with #Inject then any parameters of the constructor become injection points which the BeanManager will resolve. It's certainly not the desired way of doing it, but if it works for you, go for it.

Resources