The scenario is that before persisting a Log entity class, its property, String description should be checked if it contains at least a word found in the IllegalWord entity class. Here is the mapping of the two entity classes:
// Log.java
#Entity
public class Log {
#GeneratedValue(strategy=GenerationType.SEQUENCE)
#Id
private Long id;
#NotContainingIllegalWords
private String description;
}
// IllegalWord.java
#Entity
public class IllegalWord {
#GeneratedValue(strategy=GenerationType.SEQUENCE)
#Id
private Long id;
private String word;
}
Since I will be performing a select * to the IllegalWord entity class, I created a repository class for it:
// IllegalWordRepository.java
#Repository
public interface IllegalWordRepository extends CrudRepository<IllegalWord, Long> {}
And then created the ConstraintValidator validator class that will be used by NotContainingIllegalWords annotation, that in turn, will be use to annotate the String description field of Log entity class:
// NotContainingIllegalWordsValidator.java
public class NotContainingIllegalWordsValidator implements ConstraintValidator<NotContainingIllegalWords, Object> {
private static final Logger log = LoggerFactory.getLogger(NotContainingIllegalWordsValidator.class);
#Autowired
private IllegalWordRepository illegalWordRepository;
public void initialize(NotContainingIllegalWords constraintAnnotation) {}
public boolean isValid(String value, ConstraintValidatorContext cxt) {
log.debug("illegalWordRepository is null? " + (illegalWordRepository == null));
// Returns "illegalWordRepository is null? true"
// It is not injected even with the #Autowired annotation.
boolean valid = true;
Collection<IllegalWord> illegalWords = illegalWordRepository.findAll();
// Encounters a NullPointerException here.
// valid = ...loop through illegalWords collection and match regex (or whatever optimal approach)
// with #param value to check if it contains the illegal word.
return valid;
}
I thought it will be as straight-forward like that. But the statement illegalWordRepository.findAll() throws an error because the illegalWordRepository variable is null. Notice that I tried to check if it is null in the preceding statement.
I assumed that I have something wrong coded within the repository class so I attempted to used #Autowired private IllegalWordRepository illegalWordRepository inside a #Service annotated class and suprisingly it is injected there properly (e.i. not null):
// IllegalWordService.java
#Service
public class IllegalWordService {
private static final Logger log = LoggerFactory.getLogger(IllegalWordService.class);
#Autowired
private IllegalWordRepository illegalWordRepository;
public IllegalWord generate(String word) {
log.debug("illegalWordRepository is null? " + (illegalWordRepository == null));
// Returns "illegalWordRepository is null? false"
IllegalWord illegalWord = new IllegalWord();
illegalWord.setWord(word);
illegalWordRepository.save(illegalWord);
// Didn't encounter a NullPointerException here.
return illegalWord;
}
}
Therefore, I guess nothing is wrong with the IllegalWordRepository repository class. It's just that it is not injected in NotContainingIllegalWordsValidator validator class as I intended it to be with the #Autowired annotation (if that is how #Autowired annotation was intended to function even, I am sorry I am new in Spring Framework.).
If there is a proper approach on how to perform a #Entity query inside a ConstraintValidator instance, please tell me.
Related unanswered SO question: Inject Repository inside ConstraintValidator with Spring 4 and message interpolation configuration
Failed Attempt:
I tried to annotate the NotContainingIllegalWordsValidator class with #Configurable annotation, like so:
#Configurable(autowire=Autowire.BY_NAME, preConstruction=true)
public class NotContainingIllegalWordsValidator implements ConstraintValidator<NotContainingIllegalWords, Object> {
but the illegalWordRepository property remains null.
Since Your validator is not initialized by Spring, you can't inject anything into it. You'd have to access the ApplicationContext through a static variable.
#SpringBootApplication
public class MyApplication {
private static ApplicationContext applicationContext;
public static void main(final String[] args) {
applicationContext = SpringApplication.run(MyApplication.class, args);
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
And in your ConstraintValidator:
public class NotContainingIllegalWordsValidator implements ConstraintValidator<NotContainingIllegalWords, Object> {
public boolean isValid(String value, ConstraintValidatorContext cxt) {
ApplicationContext applicationContext = MyApplication.getApplicationContext();
IllegalWordRepository illegalWordRepository = applicationContext.getBean(IllegalWordRepository.class);
...
}
}
From my answer to a similar question:
The minimum setup for #Autowired to work properly in ConstraintValidator implementation is to have this bean in a Spring #Configuration:
#Bean
public Validator defaultValidator() {
return new LocalValidatorFactoryBean();
}
This is the demo project
Related
Short version: I have a singleton class that should only be initialized once per program execution. The goal of this class is to fetch constant data from the database, which lies in its own table. I've currently got the class set up like this:
public class MyService {
private static final MyService INSTANCE = new MyService();
// Evaluates to null
#PersistenceContext(unitName = "mydb")
private EntityManager myEntityManager;
private int myDbConstant;
private MyEnum myEnum;
public static getInstance() {
return INSTANCE;
}
public int getDbConstant() {
return myDbConstant;
}
public MyEnum getMyEnum() {
return myEnum;
}
private MyService() {
readValues();
}
// This method fetches the constant values from the database
private void readValues() {
List<MyEntity> constantEntities = myEntityManager.createNamedQuery(MyEntity.FIND_ALL).getResultList(); // NullPointerException
// ...
}
}
However, this always seems to result in a NullPointerException just as the entity manager tries to fetch the entities. I'm not really sure what could be causing this. My project is in an EJB container so my understanding is I'm not supposed to create the EM myself.
Is it possible that the new MyService(); is the problem? If so, how would I work around it while maintaining the singleton nature of this class?
Is your MyService class annotated with appropriate annotations like #Singletone or #Stareless ?
I am trying to extend existing code which auto-wires a number of components which share the same base class and are only referred to by their base class, ei:
#Configuration
public class NavigationHotKeyBindConfigs {
private static final Logger logger = LogManager.getLogger(NavigationHotKeyBindConfigs.class);
#Autowired
private HotKeyConfig hotKeyConfig;
#Autowired
private ANavigation upNavigation;
#Autowired
private ANavigation downNavigation;
#Autowired
private ANavigation centerOnSelection;
#Autowired
private ANavigation handVerify;
public abstract class ANavigation {
#Autowired
protected ScrollSchedulerConfigurer scrollSchedulerConfigurer;
public abstract void execute(DetectionView DetectionView);
}
#Component
public class DownNavigation extends ANavigation{
private static final Logger logger = LogManager.getLogger(DownNavigation.class);
public void execute(DetectionView detectionView){
logger.info(String.format("Received: Navigation key %s", ENavigationKey.DOWN.name()));
scrollSchedulerConfigurer.stop();
detectionView.selectNextDetection();
}
}
#Component
public class UpNavigation extends ANavigation{
private static final Logger logger = LogManager.getLogger(UpNavigation.class);
public void execute(DetectionView detectionView){
logger.info(String.format("Received: Navigation key %s", ENavigationKey.Up.name()));
scrollSchedulerConfigurer.stop();
detectionView.selectPrevDetection();
}
}
I don't understand how spring is ever supposed to match up upNavigation/downNavigation with the UpNavigation/DownNavigation components respectively. Was this just bad design from the original author or is there a subtlety I'm missing?
This lack of understanding is also causing some issues as the centerOnSelection and handVerify objects end up NULL after autowiring though they follow the same pattern in every way I can see. Interestingly, this last behavior is different when run in the intellij IDE vs when run out of a .jar
You can do it like this ...
Let's say we have two beans which have the same base class or interface
#Component("fooFormatter")
public class FooFormatter implements Formatter {
public String format() {
return "foo";
}
}
#Component("barFormatter")
public class BarFormatter implements Formatter {
public String format() {
return "bar";
}
}
Then we inject them like this ...
public class FooService {
#Autowired
#Qualifier("fooFormatter")
private Formatter formatter;
}
From the point of view of clarity and maintainability this seems to be the best solution
#Autowired first looks at the type of the variable and then the name of the variable to match the correct bean. By default, the name of a class marked with #Component is its short name, e.g. upNavigation for the UpNavigation class.
#Autowired
private ANavigation upNavigation;
is the same as
#Autowired
#Qualifier("upNavigation")
private ANavigation upNavigation;
And
#Component
public class UpNavigation
is the same as
#Component("upNavigation")
public class UpNavigation
Since the names match, Spring is able to find the corresponding bean.
I am trying to #Autowire a #Configuration class inside a #Service class. basically my #Configuration class contains mapping to my custom .properties file. When i try to autowire my configuration class inside my service class, BeanCreationException occurs. I am not sure what happen. Just followed the guide on creating Property classes from spring. There must be something i missed out.
Also, when i try to autowire #Configuration class to another #Configuration class, it runs smoothly
Currently, i know that, prop is always null because when i remove prop.getUploadFileLocation() call, everything will be fine. There must be something wrong during autowiring.
Here is my Service class
#Service
public class ImageService {
public static Logger logger = Logger.getLogger(ImageService.class.getName());
#Autowired
MyProperties prop;
private final String FILE_UPLOAD_LOCATION = prop.getUploadFileLocation() +"uploads/images/";
public void upload(String base64ImageFIle) throws IOException {
logger.info(FILE_UPLOAD_LOCATION);
}
}
Here is my Configuration class
#Data
#Configuration
#ConfigurationProperties (prefix = "my")
public class MyProperties {
private String resourceLocation;
private String resourceUrl;
public String getUploadFileLocation() {
return getResourceLocation().replace("file:///", "");
}
public String getBaseResourceUrl() {
return getResourceUrl().replace("**", "");
}
}
And here is where i can successfully use MyProperties
#Configuration
public class StaticResourceConfiguration implements WebMvcConfigurer {
#Autowired
MyProperties prop;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(prop.getResourceUrl())
.addResourceLocations(prop.getResourceLocation());
}
}
The issue is that you are trying to use an autowired field to set the value in an inline field assignment.
That means
private final String FILE_UPLOAD_LOCATION = prop.getUploadFileLocation() +"uploads/images/";
is executed before the prop is autowired, meaning it will always be null
The way to mitigate this would be to use constructor injection instead.
#Service
public class ImageService {
//Fine since you are using static method
public static Logger logger = Logger.getLogger(ImageService.class.getName());
//Not needed if you are only using it to set FILE_UPLOAD_LOCATION
//Allows field to be final
private final MyProperties prop;
//Still final
private final String FILE_UPLOAD_LOCATION;
//No need for #Autowired since implicit on component constructors
ImageService(MyProperties prop){
//Again not needed if you aren't going to use anywhere else in the class
this.prop = prop;
FILE_UPLOAD_LOCATION = prop.getUploadFileLocation() +"uploads/images/";
}
public void upload(String base64ImageFIle) throws IOException {
logger.info(FILE_UPLOAD_LOCATION);
}
}
See this question for why constructor is preferred over #autowired in general
If you need MyProperties bean to be created before StaticResourceConfiguration bean, you can put #ConditionalOnBean(MyProperties.class) as following. Spring will make sure MyProperties is there before processing StaticResourceConfiguration.
#Configuration
#ConditionalOnBean(MyProperties.class)
public class StaticResourceConfiguration implements WebMvcConfigurer {
I am trying to use #ConfigurationProperties to load key-value pairs from application.properties file.
application.properties
soap.action.segalRead=Segal/SegalRead
soap.action.mantilUpdate=Mantil/MantilUpdate
SoapUri.java
#ConfigurationProperties(prefix = "soap.action")
public class SoapUri {
#NotNull
private String segalRead;
#NotNull
private String mantilUpdate;
//getters and setters
}
SoapUriTests.java
#RunWith(SpringRunner.class)
#SpringBootTest
public class SoapUriTests {
#Autowired
private SoapUri soapUri;
#Test
public void testSoapUri_returnsSoapAction() {
assertThat(soapUri.getSegalRead()).isEqualTo("Segal/SegalRead");
assertThat(soapUri.getMantilUpdate()).isEqualTo("Mantil/MantilUpdate");
}
}
Above unit test works great.
However, I need to use SoapUri in real code.
Consider following code:
public class MantilUpdateReadVO extends RequestClientVO {
#Autowired
private SoapUri soapUri;
public MantilUpdateReadVO(final MantilUpdate mantilUpdate) {
super(mantilUpdate, soapUri.getMantilUpdate(), MantilUpdateResponse.class);
}
}
public class RequestClientVO {
private Object readRequest;
private String serviceName;
private Class<?> unmarshalTargetclass;
public MwsRequestClientVO(Object readRequest, String serviceName, Class<?> unmarshalTargetclass) {
super();
this.readRequest = readRequest;
this.serviceName = serviceName;
this.unmarshalTargetclass = unmarshalTargetclass;
}
//getters and setters
}
Above complains about: "Cannot refer to an instance field soapUri while explicitly invoking a constructor"
Does anyone know a workaround for injecting segalRead and mantilUpdate in constructor of super()
You are using field-injection, which is not a good idea. See Oliver Gierke's Why Field Injection is Evil for details.
The field cannot be injected until after the instance is constructed; so, you cannot use an injected field during construction.
Change the code like this:
#Autowired
public MantilUpdateReadVO(final SoapUri soapUri, final MantilUpdate mantilUpdate) {
super(mantilUpdate, soapUri.getMantilUpdate(), MantilUpdateResponse.class);
}
You also need to ensure MantilUpdateReadVO is a Spring Bean; might need to add #Component.
Good luck!
I have a test class which loads a test spring application context, now I want to create a junit rule which will setup some test data in mongo db. For this I created a rule class.
public class MongoRule<T> extends ExternalResource {
private MongoOperations mongoOperations;
private final String collectionName;
private final String file;
public MongoRule(MongoOperations mongoOperations, String file, String collectionName) {
this.mongoOperations = mongoOperations;
this.file = file;
this.collectionName = collectionName;
}
#Override
protected void before() throws Throwable {
String entitiesStr = FileUtils.getFileAsString(file);
List<T> entities = new ObjectMapper().readValue(entitiesStr, new TypeReference<List<T>>() {
});
entities.forEach((t) -> {
mongoOperations.save(t, collectionName);
});
}
}
Now I am using this rule inside my test class and passing the mongoOperations bean.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringTestConfiguration.class)
public class TransactionResourceTest {
#Autowired
private ITransactionResource transactionResource;
#Autowired
private MongoOperations mongoOperations;
#Rule
public MongoRule<PaymentInstrument> paymentInstrumentMongoRule
= new MongoRule(mongoOperations, "paymentInstrument.js", "paymentInstrument");
....
}
The problem is that Rule is getting executed before application context gets loaded, so mongoOperations reference is passed as null. Is there a way to make rules run after the context is loaded?
As far as I know what you are trying to achieve is not possible in such straight forward way because:
the rule is instantiated prior Spring's Application Context.
SpringJUnit4ClassRunner will not attempt to inject anything on the rule's instance.
There is an alternative described here: https://blog.jayway.com/2014/12/07/junit-rule-spring-caches/ but I think it would fall short in terms of what can be loaded into mongodb.
In order to achieve what you want to achieve, you would probably require a test execution listener that would inject whatever dependencies you require on your rule object.
Here's a solution, using some abstract super class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringTestConfiguration.class)
public abstract class AbstractTransactionResourceTest<T> {
#Autowired
private ITransactionResource transactionResource;
#Autowired
private MongoOperations mongoOperations;
#Before
public void setUpDb() {
String entitiesStr = FileUtils.getFileAsString(entityName() + ".js");
List<T> entities = new ObjectMapper().readValue(entitiesStr, new TypeReference<List<T>>() {});
entities.forEach((t) -> {
mongoOperations.save(t, entityName());
});
}
protected abstract String entityName();
}
then
public class TransactionResourceTest extends AbstractTransactionResourceTest<PaymentInstrument> {
#Override
protected String entityName() {
return "paymentInstrument";
};
// ...
}