Autowiring Enums in Spring boot - java

In my SpringBoot App I have class:
#Service
public enum LockerRepo {
INSTANCE;
private static List<Locker> lockerList = new ArrayList<>(generateExampleData());
private static List<Locker> generateExampleData() {
List<Locker> exampleList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
if (i <= 30) {
exampleList.add(new Locker(i, Size.S));
} else if (1 > 30 && i <= 70) {
exampleList.add(new Locker(i, Size.M));
} else {
exampleList.add(new Locker(i, Size.L));
}
}
return exampleList;
}
public static LockerRepo getInstance() {
return INSTANCE;
}
}
And I'm getting error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.smartlocker.repository.LockerRepo required a bean of type 'java.lang.String' that could not be found.
Action:
Consider defining a bean of type 'java.lang.String' in your configuration.
I have it autowired in other classes, and also called by getInstance() method. I was trying to add different annotations, and it didn't worked out. Also in test module it works well, and I can access it by getInstance()

As I wrote as comment, Enums cannot be used as Spring beans because they cannot be instantiated using the default constructor.
But if you still have to use the #Service annotation and also #Autowired to inject your class, you use the Singleton Pattern:
#Service
public class LockerRepo {
private static final LockerRepo INSTANCE = new LockerRepo();
private List<Locker> lockerList;
private LockerRepo() {
lockerList = generateExampleData();
}
private List<Locker> generateExampleData() {
List<Locker> exampleList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
if (i <= 30) {
exampleList.add(new Locker(i, Size.S));
} else if (1 > 30 && i <= 70) {
exampleList.add(new Locker(i, Size.M));
} else {
exampleList.add(new Locker(i, Size.L));
}
}
return exampleList;
}
public static LockerRepo getInstance() {
return INSTANCE;
}
// getter for locker list
}
This should work, but it has a disadvantage - it makes your code more difficult to test and you have also tight coupling between your components. So it is generally not recommented as best practice.

Related

Jacoco shows lines are not covered even though IntelliJIdea static code analyser says they are covered

Environment: Java 8 / SpringBoot v2.0.2 / IntelliJIdea (Ultimate Edition - 2020.3.2) /
I have this static method
public static boolean isAvailable(final Price price) {
if (price == null) {
return false;
}
boolean isAvailable = true;
BigDecimal annualPremium = price.getAnnualPremium() != null ? price.getAnnualPremium() : BigDecimal.ZERO;
BigDecimal monthlyPremium = price.getMonthlyPremium() != null ? price.getMonthlyPremium() : BigDecimal.ZERO;
BigDecimal monthlyFirstMonth = price.getMonthlyFirstMonth() != null ? price.getMonthlyFirstMonth() : BigDecimal.ZERO;
if (BigDecimal.ZERO.compareTo(annualPremium) == 0
|| BigDecimal.ZERO.compareTo(monthlyPremium) == 0
|| BigDecimal.ZERO.compareTo(monthlyFirstMonth) == 0) {
isAvailable = false;
}
return isAvailable;
}
Here is my test class,
NOTE: HomeContentsQuote class has a private default constructor. Hence, reflections being used.Have tried accessing the static method using the class name but, that too didn't work.
public class HomeContentsQuoteTest {
HomeContentsQuote homeContentsQuote;
#Mock
private HollardProviderProductValues hollardProviderProductValues;
#Mock
private HomeContentsQuoteTranslator homeContentsQuoteTranslator;
#Mock
private HomeContentsProduct product;
#Before
public void setup() throws Exception{
initMocks(this);
product = mock(HomeContentsProduct.class, new ReturnsMocks());
Constructor<HomeContentsQuote> constructor = HomeContentsQuote.class.getDeclaredConstructor();
constructor.setAccessible(true);
homeContentsQuote = constructor.newInstance();
}
#Test
public void testNullPrice() {
assertFalse(homeContentsQuote.isAvailable(null));
}
#Test
public void testNonZeroPremiums() {
when(hollardProviderProductValues.getAnnualPremium()).thenReturn(Optional.of(new BigDecimal(548.06)));
when(hollardProviderProductValues.getMonthlyPremium()).thenReturn(Optional.of(new BigDecimal(53.36)));
when(hollardProviderProductValues.getAnnualisedMonthlyPremium()).thenReturn(Optional.of(new BigDecimal(53.36)));
when(hollardProviderProductValues.getMonthlyFirstMonth()).thenReturn(Optional.of(new BigDecimal(53.36)));
when(hollardProviderProductValues.getShowMonthlyTotal()).thenReturn(Optional.of(Boolean.TRUE));
when(hollardProviderProductValues.getMonthlyAvailable()).thenReturn(Optional.of(Boolean.TRUE));
when(hollardProviderProductValues.getAnnualAvailable()).thenReturn(Optional.of(Boolean.TRUE));
homeContentsQuoteTranslator = new HomeContentsQuoteTranslator(hollardProviderProductValues, product);
Price price = Price.create(homeContentsQuoteTranslator);
assertNotNull(price);
assertTrue(homeContentsQuote.isAvailable(price));
}
#Test
public void testZeroAnnualPremiums() {
when(hollardProviderProductValues.getAnnualPremium()).thenReturn(Optional.of(new BigDecimal(0.00)));
when(hollardProviderProductValues.getMonthlyPremium()).thenReturn(Optional.of(new BigDecimal(53.36)));
when(hollardProviderProductValues.getAnnualisedMonthlyPremium()).thenReturn(Optional.of(new BigDecimal(53.36)));
when(hollardProviderProductValues.getMonthlyFirstMonth()).thenReturn(Optional.of(new BigDecimal(53.36)));
when(hollardProviderProductValues.getShowMonthlyTotal()).thenReturn(Optional.of(Boolean.TRUE));
when(hollardProviderProductValues.getMonthlyAvailable()).thenReturn(Optional.of(Boolean.TRUE));
when(hollardProviderProductValues.getAnnualAvailable()).thenReturn(Optional.of(Boolean.TRUE));
homeContentsQuoteTranslator = new HomeContentsQuoteTranslator(hollardProviderProductValues, product);
Price price = Price.create(homeContentsQuoteTranslator);
assertNotNull(price);
assertFalse(homeContentsQuote.isAvailable(price));
}
}
When I run mvn clean test jacoco says non of this method's lines are covered. But when I debug I can see clearly that the test code reaches every line of the method being tested. I use IntelliJIdea (Ultimate Edition - 2020.3.2). When I run these tests with coverage it marks that method lines as covered(green).
Please help me figuring this out.

Reassigning a value to an instance variable inside of a method by reassigning the method argument's variable

My class has a scheduled method that runs every half a second and it checks the data to see if any of the devices have timed out. If they are timed out for at least 5 seconds I am throwing an event to my database (this is done by checkSpecs method which I haven't reproduced here. The actual class is quite large so I have attempted to reduce the size while keeping relevant parts).
Now I am adding a new method to the class, checkReconnections which I want to use to throw another event to the database whenever a connection that previously timed out is regained.
Because my class is so large and I am monitoring several devices with this method, I attempted to create a helper method monitorConnectionStatus that accepts two booleans as arguments. My question is concerning this method.
I was under the impression that in passing the instance variables into monitorConnectionStatus when the method is invoked, that method gains access to them and can reassign those variables. This is my intent in order for the method to function as intended. But my IDE is telling me that the value of disconnectionStatus will always be false, which caused me to think, am I wrong in believing the instance variable will be reassigned? Is it possible that my IDE is just wrong in this case?
When I reassign the value of disconnectionStatus is it reassigning the value of the instance variable hasADisconnected or is it just doing it with a local argument variable?
Public Class OffSpecAgent {
private final DataModel dataModel;
private int deviceATimeoutCounter = 0;
private boolean isDeviceATimeout = false;
private boolean hasADisconnected = false;
private int deviceBTimeoutCounter = 0;
private boolean isDeviceBTimeout = false;
private boolean hasBDisconnected = false;
#Scheduled(fixedDelay = 500)
public void offSpecMonitor() {
checkTimeouts();
checkReconnections();
checkSpecs();
}
private void checkTimeouts() {
deviceATimeoutCounter = dataModel.getBoolean(DEVICE_A_TIMEOUT) ? deviceATimeoutCounter + 1 : 0;
isDeviceATimeout = deviceATimeoutCounter >= 10;
deviceBTimeoutCounter = dataModel.getBoolean(DEVICE_B_TIMEOUT) ? deviceBTimeoutCounter + 1 : 0;
isDeviceBTimeout = deviceATimeoutCounter >= 10;
}
private void checkReconnections() {
monitorConnectionStatus(isDeviceATimeout, hasADisconnected);
monitorConnectionStatus(isDeviceBTimeout, hasBDisconnected);
}
private void monitorConnectionStatus(boolean timeoutCondition, boolean disconnectionStatus) {
if (timeoutCondition) {
disconnectionStatus = true;
}
if (disconnectionStatus && !timeoutCondition) {
disconnectionStatus = false;
//throw reconnection event
}
}
}
In java, variables are passed by value into methods, meaning your method monitorConnectionStatus is only aware that it's getting false, false values. You would have to update your method to access the instance variable directly.
private void monitorConnectionStatus() {
if (this.timeoutCondition) {
this.disconnectionStatus = true;
}
if (this.disconnectionStatus && !this.timeoutCondition) {
this.disconnectionStatus = false;
//throw reconnection event
}
}
Note the keyword this is not required.
Also, I just want to add that you are using the term class variable incorrectly. The variables you are referring to are instance variables.
You can read more about that here:
https://www.tutorialspoint.com/What-are-class-variables-instance-variables-and-local-variables-in-Java
I refactored the class and now it looks like this:
Public Class OffSpecAgent {
private final DataModel dataModel;
private static class ConnectionTracker {
boolean timeout, timeoutExceeded, hasDisconnected;
int timeoutCounter = 0;
}
private final ConnectionTracker deviceATracker = new ConnectionTracker();
private final ConnectionTracker deviceBTracker = new ConnectionTracker();
#Scheduled(fixedDelay = 500)
public void offSpecMonitor() {
checkTimeouts();
checkReconnections();
checkSpecs();
}
private void checkTimeouts() {
trackTimeout(plcTracker, dataModel.getBoolean(DEVICE_A_TIMEOUT), 10);
trackTimeout(plcTracker, dataModel.getBoolean(DEVICE_B_TIMEOUT), 20);
}
private void trackTimeout(ConnectionTracker tracker, boolean isTimeout, int maxTimeout){
tracker.timeout = isTimeout;
tracker.timeoutCounter = isTimeout ? tracker.timeoutCounter + 1 : 0;
tracker.timeoutExceeded = tracker.timeoutCounter >= maxTimeout;
}
private void checkReconnections() {
monitorConnectionStatus(deviceATracker);
monitorConnectionStatus(deviceBTracker);
}
private void monitorConnectionStatus(ConnectionTracker tracker) {
if (tracker.timeoutExceeded) {
tracker.hasDisconnected = true;
}
if (tracker.hasDisconnected && !tracker.timeout) {
tracker.hasDisconnected = false;
//throw reconnection event
}
}
}
This seems to be much of an improvement, the tracker object actually makes the code more readable in my opinion, and now I am able to have the desired behavior. Thank you to everyone who responded to my question.

Constructor chaining and preparing arguments before calling this(aguments)

I am making a Yahtzee game. I want to supply a constructor for different cases. Suppose you couldn't be bothered to supply the names of the players that you want to create a new game with, I'd like to just create "Unnamed Player 1", "Unnamed Player 2", etc.
Here is how I am trying to do that:
public class YahtzeeGame {
private List<Player> players = new ArrayList<>();
public YahtzeeGame(String[] playerNames) {
for (String playerName : playerNames) {
players.add(new Player(playerName));
}
}
public YahtzeeGame(int numberOfPlayers) {
String[] playerNames = new String[numberOfPlayers];
for (int i = 0; i < numberOfPlayers; i++) {
playerNames[i] = "Unnamed player " + (i+1);
}
this(playerNames); // ERROR: "Constructor call must be the first statement in a constructor.
}
public YahtzeeGame(String playerName) {
this(new String[] {playerName});
}
public YahtzeeGame() {
this("Unnamed player");
}
}
This doesn't work of course, as per the error written in the comment.
Is there a way around this? Do I need a factory pattern for this?
Yes, there's fairly simple way around it, at least in this case: create a static method which will prepare the constructor argument for you. Call that from the this expression:
public YahtzeeGame(int numberOfPlayers) {
this(getUnnamedPlayers(numberOfPlayers));
}
private static String[] getUnnamedPlayers(int numberOfPlayers) {
String[] playerNames = new String[numberOfPlayers];
for (int i = 0; i < numberOfPlayers; i++) {
playerNames[i] = "Unnamed player " + (i+1);
}
return playerNames;
}
Note that it does have to be static, because you can't call any instance methods on this before the chained constructor, either.

Instantiating an object in run method has no effect

As shown in the code below, when I instantiate an object in the constructor the run method works normally, but if I instantiated the object in the run method I receive no result.
Why that is happening?
code:
private class TTSReg extends TimerTask {
TTSCtrl mTTS = null;
TTSReg() {
this.mTTS = new TTSCtrl(getApplicationContext());
}
#Override
public void run() {
//this.mTTS = new TTSCtrl(getApplicationContext());//if this line was activated, the rest would not work
if ((mVelocity < 1) && (mEngSpeed >= 600)) {
this.mTTS.pronounce(getApplicationContext().getResources().getString(R.string.rule_velocity_1));
}
}
}

How can I create a new expression for use with Thymeleaf and Spring?

Anyone know how to extend an expressions in thymeleaf? (I need a "variety" function in thymeleaf).
Cloning a "Numbers" expression works, but...
When I make my own expression, I have error:
SEVERE: Servlet.service() for servlet [appServlet] in context with path [] threw exception
[Request processing failed;
nested exception is org.thymeleaf.exceptions.TemplateProcessingException:
Exception evaluating SpringEL expression:
"Variety.Variety(review.usefulScore, 'osoba', 'osoby', 'osób')"
(static:/cms/fragments/reviews:49)] with root cause
org.springframework.expression.spel.SpelEvaluationException:
EL1011E:(pos 8): Method call:
Attempted to call method Variety(java.lang.Integer,java.lang.String,java.lang.String,java.lang.String) on null context object
Any suggestions?
Edit (my code):
package org.springframework.expression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public abstract class Variety extends StandardEvaluationContext {
private boolean in_array(int needle, int[] haystack) {
for(int i = 0; i < haystack.length; i++) {
if(haystack[i] == needle) return true;
}
return false;
}
public String Variety(int number, String varietyFor1, String varietyFor234, String varietyForOthers) {
if(number == 1)
return varietyFor1;
if(number % 100 >= 10 && number % 100 <= 20)
return varietyForOthers;
if(in_array(number%10, new int[] {2,3,4}))
return varietyFor234;
return varietyForOthers;
}
}
Edit (forgot, like allways):
I would like to use it like: ${utils.Variety(...)}
The following exception shows that there is no Variety object on the context.
Attempted to call method Variety(java.lang.Integer,java.lang.String,java.lang.String,java.lang.String) on null context object
The expression object must be added via a dialect. If you have added the dialect to your template engine, you will be able to use #utils.variety(...).
public class UtilsDialect implements IExpressionEnhancingDialect {
public static final String UTILS_EXPRESSION_OBJECT_NAME = "utils";
private final Variety utils;
public UtilsDialect (final Variety utils){
this.utils = utils;
}
#Override
public Map<String, Object> getAdditionalExpressionObjects(final IProcessingContext processingContext) {
final Map<String, Object> objects = new HashMap<>();
objects.put(UTILS_EXPRESSION_OBJECT_NAME, variety);
return objects;
}
}
Also make sure you write your methods in mixed case:
public abstract class Variety extends StandardEvaluationContext {
private boolean inArray(...){
...
}
public String variety(...){
...
}
}

Categories