Schema Generator for ApacheCayenne classes - java

I'm trying to use SPQR to generate GraphQL schema from a Cayenne generated class.
Cayenne class looks like this
public class MyCayenneClass {
public static final Property<Integer> A_PROPERTY = Property.create("aProperty", Integer.class);
public static final Property<Integer> ANOTHER_PROPERTY = Property.create("anotherProperty", String.class);
public void setAProperty(Integer aProperty) {
writeProperty("aProperty", aProperty);
}
public Integer getAProperty() {
return (Integer)readProperty("aProperty");
}
public void setAnotherProperty(String anotherProperty) {
writeProperty("anotherProperty", anotherProperty);
}
public String getAnotherProperty() {
return (String)readProperty("anotherProperty");
}
}
As the class isn't a simple POJO, SPQR throws an exception and the schema isn't generated.
Error: QUERY_ROOT fields must be an object with field names as keys or a function which returns such an object.
What's the best approach here (without modifying the cayenne class (i.e. annotating a method)?
GraphQLEndPoing.java
#WebServlet(urlPatterns = "/graphql")
public class GraphQLEndpoint extends SimpleGraphQLServlet {
public GraphQLEndpoint() {
super(buildSchema());
}
//This method used SPQR
private static GraphQLSchema buildSchema() {
GraphQLSchema schemaGenerator = new GraphQLSchemaGenerator()
.withOperationsFromSingletons(myRepository) //register the beans
.generate();
return schemaGenerator;
}
private static final MyRepository myRepository;
static {
myRepository= new MyRepository ();
}
}
MyRepository.java
public class MyRepository{
private MyLibService libService;
#GraphQLQuery
public MyCayenneClass find(Integer id) {
List<MyCayenneClass> myList= libService.fetchById(new Integer[] {id});
return myList.get(0);
}
}
*FYI. If I declare the schema. Code will work just fine
schema {
query: Query
}
type Query {
find(id: Int): MyCayenneClass
}
type ConcContract {
id: ID
aProperty: Int
anotherProperty: String
}

From SPQR's perspective, this isn't really different from a POJO, as SPQR cares only about the types.
By default, for all nested classes (MyCayenneClass in your case), everything that looks like a getter will be exposed. For top-level classes (MyRepository in your case), only annotated methods are exposed by default. And at least one top-level method must be exposed, otherwise you have an invalid schema.
The error, as it stands, just means not a single top-level query was discovered. I see the #GraphQLQuery annotation is commented out. Is that intentional? With the default config, this would not expose any query.
You can register a different ResolverBuilder, e.g. PublicResolverBuilder (or your own implementation/extension) if you want to expose un-annotated methods.
E.g.
generator.withOperationsFromSingleton(new MyRepository(), new PublicResolverBuilder())
This would expose all public methods from that class.
Here's a slightly simplified example I tried with v0.9.6 and seems to work as expected (I know you're using a rather old version from the error text).
public class MyRepository {
#GraphQLQuery //not commented out
public MyCayenneClass find(Integer in) {
return new MyCayenneClass();
}
}
// extends CayenneDataObject because I don't know where to get the
// writeProperty and readProperty from
// but shouldn't change anything from SPQR's perspective
public class MyCayenneClass extends CayenneDataObject {
public static final Property<Integer> A_PROPERTY = Property.create("aProperty", Integer.class);
public static final Property<String> ANOTHER_PROPERTY = Property.create("anotherProperty", String.class);
public void setAProperty(Integer aProperty) {
writeProperty("aProperty", aProperty);
}
public Integer getAProperty() {
return (Integer)readProperty("aProperty");
}
public void setAnotherProperty(String anotherProperty) {
writeProperty("anotherProperty", anotherProperty);
}
public String getAnotherProperty() {
return (String)readProperty("anotherProperty");
}
}
There's many more customizations you can apply, depending on what you end up needing, but from the question as it stands, it doesn't seem you need anything extra...
To override the ResolverBuilder used for nested classes, you have 2 options.
1) Register it globally, so all nested types use it:
generator.withNestedResolverBuilders(customBuilder)
2) Or per type:
.withNestedResolverBuildersForType(MyCayenneClass.class, new BeanResolverBuilder())
But this is very rarely needed...

Related

How to implement factory pattern with dagger using annotation

What I have done currently is
Created an abstract class
public interface AbstractRawPathStrategy {
String getRouteKey();
void processRequest();
}
Implemented the classes
public class GetDocumentImpl implements AbstractRawPathStrategy {
#Override
public String getRouteKey() {
return "GET_DOCUMENT";
}
#Override
public void processRequest() {
log.info("Inside get document");
}
}
Created a routing factory
public class RawPathStrategyFactory {
private final Map<String, AbstractRawPathStrategy> dictionary;
#Inject
public RawPathStrategyFactory(final Set<AbstractRawPathStrategy> abstractRawPathStrategySet) {
dictionary = new HashMap<>();
for (AbstractRawPathStrategy abstractRawPathStrategy : abstractRawPathStrategySet) {
dictionary.put(abstractRawPathStrategy.getRouteKey(), abstractRawPathStrategy);
}
}
public AbstractRawPathStrategy getByRouteKey(final String rawPath) {
return dictionary.get(rawPath);
}
}
Instantiated the factory
#Module
public class AppModule {
#Provides
#Singleton
public RawPathStrategyFactory getRouteKeyStrategyFactory() {
Set<AbstractRawPathStrategy> abstractRouteKeyStrategies = new HashSet<>();
abstractRouteKeyStrategies.add(new GetDocumentImpl());
abstractRouteKeyStrategies.add(new GetUserRightsImpl());
return new RawPathStrategyFactory(abstractRouteKeyStrategies);
}
What I want is to go to respective class based on the route key (String). How can this be done without instantiating each class with new in AppModule. Any cleaner way to do this?
Try to use multibindings when create RawPathStrategyFactory
https://dagger.dev/dev-guide/multibindings.html
The easy, boring, way would be to use some static identifier which you'll have to set for each distinct subtype of your abstract class, and subsequent subtype thereof.
The more complicated, albeit fun, way to do this would be to use reflection.

Loading an abstract class based object by YAML file

I want to load object which contains array list of objects based on abstract class from yaml file. And i get this error message:
Exception in thread "LWJGL Application" Cannot create property=arrayListOfAbstractObjects for JavaBean=com.myyaml.test.ImplementationOfExampleClass#7a358cc1
in 'reader', line 1, column 1:
dummyLong: 1
^
java.lang.InstantiationException
in 'reader', line 3, column 3:
- dummyFloat: 444
^
YAML file
dummyLong: 1
arrayListOfAbstractObjects:
- dummyFloat: 444
- dummyDouble: 123
Java classes:
public abstract class ExampleClass {
protected ArrayList<AbstractClass> arrayListOfAbstractObjects;
protected long dummyLong = 111;
public ExampleClass() {
}
public void setArrayListOfAbstractObjects(ArrayList<AbstractClass> arrayListOfAbstractObjects) {
this.arrayListOfAbstractObjects = arrayListOfAbstractObjects;
}
public void setDummyLong(long dummyLong) {
this.dummyLong = dummyLong;
}
}
public class ImplementationOfExampleClass extends ExampleClass {
public ImplementationOfExampleClass() {
}
}
public abstract class AbstractClass {
private int dummyInt = 22;
public AbstractClass() {
}
public void setDummyInt(int dummyInt) {
this.dummyInt = dummyInt;
}
}
public class FirstImplementationOfAbstractClass extends AbstractClass {
float dummyFloat = 111f;
public FirstImplementationOfAbstractClass() {
}
public void setDummyFloat(float dummyFloat) {
this.dummyFloat = dummyFloat;
}
}
public class SecondImplementationOfAbstractClass extends AbstractClass {
double dummyDouble = 333f;
public SecondImplementationOfAbstractClass() {
}
public void setDummyDouble(double dummyDouble) {
this.dummyDouble = dummyDouble;
}
}
My guess is that yaml doesn't know which sort of abstract class implementation to use. FirstImplementationOfAbstractClass or SecondImplementationOfAbstractClass. Is it possible to load an object by yaml with such classes?
This is only possible if you tell the YAML processor which class you want to instantiate on the YAML side. You do this with tags:
dummyLong: 1
arrayListOfAbstractObjects:
- !first
dummyFloat: 444
- !second
dummyDouble: 123
Then, you can instruct your YAML processor to properly process the items based on their tags. E.g. with SnakeYAML, you would do
class MyConstructor extends Constructor {
public MyConstructor() {
this.yamlConstructors.put(new Tag("!first"), new ConstructFirst());
this.yamlConstructors.put(new Tag("!second"), new ConstructSecond());
}
private class ConstructFirst extends AbstractConstruct {
public Object construct(Node node) {
// raw values, as if you would have loaded the content into a generic map.
final Map<Object, Object> values = constructMapping(node);
final FirstImplementationOfAbstractClass ret =
new FirstImplementationOfAbstractClass();
ret.setDummyFloat(Float.parseFloat(values.get("dummyFloat").toString()));
return ret;
}
}
private class ConstructSecond extends AbstractConstruct {
public Object construct(Node node) {
final Map<Object, Object> values = constructMapping(node);
final SecondImplementationOfAbstractClass ret =
new SecondImplementationOfAbstractClass();
ret.setDummyFloat(Double.parseDouble(values.get("dummyFloat").toString()));
return ret;
}
}
}
Note: You can be more intelligent when loading the content, avoiding toString and instead process the node content directly; I use a dumb implementation for easy demonstration.
Then, you use this constructor:
Yaml yaml = new Yaml(new MyConstructor());
ExampleClass loaded = yaml.loadAs(input, ImplementationOfExampleClass.class);
The Node class is kind of YAML file transformed into Java data object. I found under debugger it contains field ArrayList<E> value. Which contains NodeTuple with YAML file fields (e.g. dummyFloat). So I must in constructMapping(node) method convert on my own each field and then set them in e.g. ConstructFirst.construct(Node node) on the constructed object.
EDIT:
So I must in constructMapping(node) method convert on my own each field and then set them in e.g. ConstructFirst.construct(Node node) on the constructed object.
Cast of param node to MappingNode is needed. This method is inherited from BaseConstructor.constructMapping(MappingNode node). Flyx didn't add that cast and I didn't know where to get it from. Thanks for help. Now it works. But i'm still struggle with nested abstract classes. Maybe i'll need help, but I will try to handle myself.
Also this link might be helpful:
Polymorphic collections in SnakeYaml

Why does adding a super-class break my Spring beans?

I have a Spring Boot web application that is working correctly. I noticed that two #Repository beans had a lot in common, so I refactored them using an abstract super class and now my application is broken. I've double-checked and this is the only change that I've made between the working and non-working states. Can anyone see what I've done wrong?
Here's my working code:
public class One { ... }
public class Two { ... }
#Repository
public class RepoOne {
private final ISource<One> sourceOne;
private ICache<One> cache;
#Value("${com.example.lifetime.one}")
private int lifetime;
public RepoOne(ISource<One> sourceOne) {
this.sourceOne = sourceOne;
}
#PostConstruct
public void createCache() {
Duration lifetime = Duration.ofMinutes(this.lifetime);
this.cache = new Cache<>(lifetime, sourceOne);
}
public One get(String key) {
return cache.get(key);
}
}
#Repository
public class RepoTwo {
private final ISource<Two> sourceTwo;
private ICache<Two> cache;
#Value("${com.example.lifetime.two}")
private int lifetime;
public RepoOne(ISource<Two> sourceTwo) {
this.sourceTwo = sourceTwo;
}
#PostConstruct
public void createCache() {
Duration lifetime = Duration.ofMinutes(this.lifetime);
this.cache = new Cache<>(lifetime, sourceTwo);
}
public Two get(String key) {
return cache.get(key);
}
}
#Service
public class RepoService {
private final RepoOne repoOne;
private final RepoTwo repoTwo;
public RepoService(RepoOne repoOne, RepoTwo repoTwo) {
this.repoOne = repoOne;
this.repoTwo = repoTwo;
}
public void doSomething(String key) {
One one = repoOne.get(key);
...
}
}
Here's my re-factored code where I introduced an abstract, generic super-class.
abstract class AbstractRepo<T> {
private final ISource<T> source;
private ICache<T> cache;
AbstractRepo (ISource<T> source) {
this.source = source;
}
#PostConstruct
private void createCache() {
Duration lifetime = Duration.ofMinutes(lifetime());
this.cache = new Cache<>(lifetime, source);
}
protected abstract int lifetime();
public final T get(String key) {
return cache.get(key);
}
}
#Repository
public class RepoOne extends AbstractRepo<One> {
#Value("${com.example.lifetime.one}")
private int lifetime;
public RepoOne(ISource<One> sourceOne) {
super(source);
}
protected int lifetime() { return lifetime; }
}
#Repository
public class RepoTwo extends AbstractRepo<Two> {
#Value("${com.example.lifetime.two}")
private int lifetime;
public RepoTwo(ISource<Two> sourceTwo) {
super(source);
}
protected int lifetime() { return lifetime; }
}
When using the re-factored code I get a NullPointerException in AbstractRepo::get(). I've confirmed via the debugger that cache is null (along with source). However, I also confirmed via the debugger that instances of RepoOne and RepoTwo are created and their createCache() method called. It's as if two instances of each are being created and only the one is initialised. Any thoughts?
It isn't the fact that you introduced a parent class but the fact that you turned the get method into a final method.
A class annotated with #Repository will get automatic exception translation. This automatic exception translation is added through the use of AOP. The default mechanism to apply AOP in Spring is to use proxies and in this case a class based proxy.
What happens is that CgLib creates a proxy for your classes by subclassing it, so that when a method is called an advice can be added. However a final method cannot be overridden in a subclass. Which will lead to the get method being called on the proxy instead of the actual instance.
There are 2 ways of fixing this
Remove the final keyword
Introduce an interface defining the contract for your repositories. This will lead to a JDK Dynamic proxy being created. JDK Dynamic Proxies are interface based and don't need to subclass your actual class (that is only for class based proxies).

Can I have a single instance of Interface

In my Android application I have a class which gives me static string values; something like this:
public class VehicleInfo {
public static String getVehicleEnginePower(boolean isNew) {
return isNew ? "1800CC" : "1600CC";
}
}
Now I have another category, so I will have to pass another Boolean, and I will get the value I need. However, these categories will keep on increasing. So I looked into the Open/Closed principle which looks promising for quick enhancement. To ensure this I will make the VehicleInfo class as an Interface and then I will have other classes implement VehicleInfo.
public interface VehicleInfo {
String getVehicleEnginePower();
}
public class NewVehicle implements VehicleInfo {
#Override
public String getVehicleEnginePower() {
return "1800CC";
}
}
and the other category classes will also be something like this. In this way I will have to add another class for all the new categories.
The question I wanted to ask is: is there a way that I can have single instance of this interface? Because in the whole application flow, a user will only be able to see one category until he switches to another category.
I don't want to instantiate these classes at multiple points. To clarify my question, I want to do something like this at the start of my application:
if (isNew) {
VehicleInfo vehicleInfor = new NewVehicle();
}
And in the whole application, whenever I call VehicleInfo.getVehicleEnginePower, it should always return engine power from the NewVehicle class.
Is something like this possible? Or am I just being silly and I will have to instantiate this interface on multiple points?
Maybe you need a singleton here
public class VehicleInfoManager {
private static VehicleInfoManager INSTANCE = new VehicleInfoManager();
private VehicleInfo currentVehicleInfo;
public static VehicleInfoManager getInstance() {
return INSTANCE;
}
public void setCurrentVehicleInfo(VehicleInfo info) {
this.currentVehicleInfo = info;
}
public String getVehicleEnginePower() {
return this.currentVehicleInfo.getVehicleEnginePower();
}
private VehicleInfoManager() {
// Constructor private by default
}
}
Then you can call it from everywhere like this
VehicleInfoManager.getInstance().getVehicleEnginePower()
//Or set current info like this
VehicleInfoManager.getInstance().setCurrentVehicleInfo(new NewVehicle())
Just be careful as currentVehicleInfo is null by default so you need to handle null pointer cases.
If I understand your question correctly.
My solution to this would be Enum
public enum VehicleEnginePower {
NEW ("1800CC"),
OLD ("1600CC"),
private final String name;
private Modes(String s) {
name = s;
}
public String toString() {
return this.name;
}
}
Then you can do
if (isNew) {
String powerOfEngine = VehicleEnginePower.NEW.toString();
}

Can the compiler verify a generic type of an object through a generic method?

First of all, sorry for the bad title. I don't know how to describe the problem in a few words (maybe not even in many)...
I am refactoring some settings in our system to be more abstract. The current solution has multiple tables in the DB, one for each settings area. In order to add a new setting, you'll need to extend the schema, the hibernate class, all transfer object classes, getters/setters, etc. I felt that this is violating OCP (open-closed principle), thus the refactoring.
I've spent some time coming up with ideas on how to implement such an abstraction. My favourite idea so far is the following:
1 enum for each settings area
1 enum value for each setting
Each setting is a SettingsDefinition<T> class using a generic type
A SettingsService is using static get/set methods with generic types
So for example, a settings area could be:
public enum SettingsABC{
A(new SettingDefinition<Integer>("A", 123)),
B(new SettingDefinition<String>("B", "Hello")),
C(new SettingDefinition<Boolean>("C", false));
private SettingDefinition settingDefinition;
SettingsABC(SettingDefinition settingDefinition) {
this.settingDefinition = settingDefinition;
}
public SettingDefinition getDefinition() {
return settingDefinition;
}
}
Where the SettingDefinition is the following:
public class SettingDefinition<T> {
private String name;
private T defaultValue;
public SettingDefinition(String name, T defaultValue) {
this.name = name;
this.defaultValue = defaultValue;
}
public String getName() {
return name;
}
public T getDefaultValue() {
return defaultValue;
}
}
And the service to get/set the values would be:
public class SettingsService {
public static <T> T getSetting(SettingDefinition setting) {
// hit db to read
// return value
}
public static <T> void setSetting(SettingDefinition setting, T value) {
// hit db to write
}
}
And the consumer would look something like this:
String value = SettingsService.getSetting(SettingsABC.B.getDefinition());
SettingsService.setSetting(SettingsABC.A.getDefinition(), 123);
My problem is that I cannot enforce a compiler type check between the generic type of the SettingDefinition inside SettingsABC and the generic type of get/set methods of the service. So in essence, I can do this:
Integer value = SettingsService.getSetting(SettingsABC.B.getDefinition());
Where B's definition is of type String.
Also, I can do this:
SettingsService.setSetting(SettingsABC.A.getDefinition(), "A");
Where A's definition is an Integer.
Is there any way to use generics to force these two different generic types match?
You can convert the enum to the class:
public final class SettingsABC<T> {
public static final SettingsABC<Integer> A =
new SettingsABC<>(new SettingDefinition<>("A", 123));
public static final SettingsABC<String> B =
new SettingsABC<>(new SettingDefinition<>("B", "Hello"));
public static final SettingsABC<Boolean> C =
new SettingsABC<>(new SettingDefinition<>("C", false));
private final SettingDefinition<T> settingDefinition;
// private constructor, so nobody else would instantiate it
private SettingsABC(SettingDefinition<T> settingDefinition) {
this.settingDefinition = settingDefinition;
}
public SettingDefinition<T> getDefinition() {
return settingDefinition;
}
}
This way individual constants will be typed. Now you can use the type arguments for SettingService as well:
public static <T> T getSetting(SettingDefinition<T> setting) {
...
}
public static <T> void setSetting(SettingDefinition<T> setting, T value) {
...
}
Although it's not an enum anymore, it can be used mostly in the same way. If you need other methods which are usually available in enum, you can mimic them like this:
public String name() {
return settingDefinition.getName();
}
#Override
public String toString() {
return settingDefinition.getName();
}
// and so on

Categories

Resources