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).
Related
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.
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...
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();
}
Let's say I have a Strategy interface :
public interface Strategy {
void perform();
}
And a template method to implement it :
public abstract class AbstractStrategy implements Strategy {
#Override
public void perform() {
String firstInfo = doStuff();
String secondInfo = firstDelegationToImplementor(firstInfo);
String thirdInfo = processSecondInfo(secondInfo);
String fourthInfo = secondDelegationToImplementor(thirdInfo);
finalProcessing(fourthInfo);
}
private void finalProcessing(String fourthInfo) {
//TODO automatically generated method body, provide implementation.
}
protected abstract String secondDelegationToImplementor(String thirdInfo);
protected abstract String firstDelegationToImplementor(String firstInfo);
private String processSecondInfo(String secondInfo) {
return "thirdResult";
}
private String doStuff() {
return "firstResult";
}
}
And I have a concrete subclass of that :
public class ConcreteStrategy extends AbstractStrategy {
private String firstInfo;
#Override
protected String secondDelegationToImplementor(String thirdInfo) {
return someMoreProcessing(firstInfo, thirdInfo);
}
private String someMoreProcessing(String firstInfo, String thirdInfo) {
return null;
}
private String someProcessing(String firstInfo) {
return null;
}
#Override
protected String firstDelegationToImplementor(String firstInfo) {
this.firstInfo = firstInfo;
return someProcessing(firstInfo);
}
}
But due to the fact that it needs to remember some intermediate result in between the method calls it is not stateless. Stateless classes have several advantages, they are automatically thread safe for instance.
So the question is : how can I make ConcreteStrategy stateless, while taking advantage of the template method?
(edit) Clarification : the published methods of both the interface and the template method class cannot change.
(note, I have solved this question already and will answer it myself, but I'll give others a chance to solve it)
Ok here's the answer I have come up with when I faced this :
public class StatelessConcreteStrategy implements Strategy {
#Override
public void perform() {
new ConcreteStrategy().perform();
}
}
StatelessConcreteStrategy is stateless. It has all the benefits any other stateless class has, and by delegating the perform() to a new ConcreteStrategy instance, it gets to use the template method pattern, and is able to 'remember' any data it wants to in between method calls.
In fact you'll most likely want to inline ConcreteStrategy to an inner or even anonymous inner class.
How I can modify this class to follow DIP (Dependency Inversion Principle) in order to remove the two ArrayList dependencies in the constructor? How should the interfaces be?
One thing that confuses me is that the new references points to an ArrayList<type> not just the constructor of a class. And I don't know how to handle that situation...
package mvc.controllers;
import java.util.ArrayList;
import mvc.models.AbstractModel;
import mvc.views.AbstractViewPanel;
public abstract class AbstractController {
private ArrayList<AbstractViewPanel> registeredViews;
private ArrayList<AbstractModel> registeredModels;
public AbstractController() {
registeredViews = new ArrayList<AbstractViewPanel>();
registeredModels = new ArrayList<AbstractModel>();
}
public void addModel(AbstractModel model) {
registeredModels.add(model);
model.addPropertyChangeListener(this);
}
public void removeModel(AbstractModel model) {
registeredModels.remove(model);
model.removePropertyChangeListener(this);
}
public void addView(AbstractViewPanel view) {
registeredViews.add(view);
}
public void removeView(AbstractViewPanel view) {
registeredViews.remove(view);
}
...
}
To do it in Dependency Inversion -way, you could do one of the following:
Dependencies to the lists are given in the constructor:
public abstract class AbstractController {
private List<AbstractViewPanel> registeredViews;
private List<AbstractModel> registeredModels;
public AbstractController(List<AbstractViewPanel> registeredViews, List<AbstractModel> registeredModels) {
this.registeredViews = registeredViews;
this.registeredModels = registeredModels;
}
Add mutators (setters) for the lists:
public abstract class AbstractController {
private List<AbstractViewPanel> registeredViews;
private List<AbstractModel> registeredModels;
public AbstractController() {
}
public void setRegisteredViews(List<AbstractViewPanel> views) {
this.registeredViews = views;
}
public void setRegisteredModels(List<AbstractModel> models) {
this.registeredModels = models;
}
Btw, I changed the ArrayLists to Lists. There's no need to introduce a dependency on the List implementation.
It's not really clear what dependency you're trying to remove, but if you want to remove the explicit ArrayList instantiation, you could use constructor injection:
private final List<AbstractViewPanel> registeredViews;
private final List<AbstractModel> registeredModels;
public AbstractController(final List<AbstractViewPanel> registeredViews,
final List<AbstractModel> registeredModels) {
this.registeredViews = registeredViews;
this.registeredModels= registeredModels;
}
I wouldn't inject a List into an object, and thus break encapsulation, just for the sake of unit testing it by mocking the list. The List is not an external dependency. It's part of the internals of the class.
If you want to unit-test this class, test that the method who uses the objects stored in the list do actually use them as they should. You should also test that the propertyChange method is called when you change a property of a model that you've added to the controller.
Or you could add a getView() and a getModels() method (potentially protected), to test that the addition worked as expected.